Compare commits

...

9 Commits

Author SHA1 Message Date
Luca Puderbach
85e0c68a36 Merge branch 'gui' of https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02 into gui 2024-12-01 21:33:18 +01:00
Luca Puderbach
113e1b7aa5 Spieler Bewegungs hochdatum 2024-12-01 21:33:07 +01:00
Johannes Schmelz
cfcc232432 trigger Bankrupt PopUp 2024-12-01 21:30:55 +01:00
Johannes Schmelz
29e9b54cc4 trigger NoMoneyPopUp 2024-12-01 21:24:33 +01:00
Yvonne Schmidt
0c90d1f185 Merge remote-tracking branch 'origin/gui' into gui 2024-12-01 21:19:20 +01:00
Yvonne Schmidt
b5090ea179 fixed overlapping with propertycard 2024-12-01 21:18:57 +01:00
Johannes Schmelz
cfbf20f745 payBail 2024-12-01 20:55:58 +01:00
Johannes Schmelz
dd002746aa trigger winner and looser popup 2024-12-01 20:52:29 +01:00
Johannes Schmelz
11f4560745 du kommst ins Gulag Popup 2024-12-01 20:31:29 +01:00
11 changed files with 231 additions and 97 deletions

View File

@ -1,5 +1,7 @@
package pp.monopoly.client.gui;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
@ -10,7 +12,14 @@ import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.popups.*;
import pp.monopoly.client.gui.popups.BuildingPropertyCard;
import pp.monopoly.client.gui.popups.ConfirmTrade;
import pp.monopoly.client.gui.popups.EventCardPopup;
import pp.monopoly.client.gui.popups.FoodFieldCard;
import pp.monopoly.client.gui.popups.GateFieldCard;
import pp.monopoly.client.gui.popups.LooserPopUp;
import pp.monopoly.client.gui.popups.TimeOut;
import pp.monopoly.client.gui.popups.WinnerPopUp;
import pp.monopoly.game.server.Player;
import pp.monopoly.game.server.PlayerHandler;
import pp.monopoly.model.fields.BuildingProperty;
@ -19,7 +28,6 @@ import pp.monopoly.model.fields.GateField;
import pp.monopoly.notification.EventCardEvent;
import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.PopUpEvent;
import pp.monopoly.notification.Sound;
import pp.monopoly.notification.UpdatePlayerView;
/**
@ -40,9 +48,9 @@ public class TestWorld implements GameEventListener {
*/
public TestWorld(MonopolyApp app) {
this.app = app;
this.playerHandler = app.getGameLogic().getPlayerHandler(); // Hole den PlayerHandler
this.playerHandler = app.getGameLogic().getPlayerHandler();
app.getGameLogic().addListener(this);
cameraController = new CameraController(app.getCamera()); // Übergebe PlayerHandler
cameraController = new CameraController(app.getCamera());
}
/**
@ -86,7 +94,7 @@ public class TestWorld implements GameEventListener {
geom.setLocalTranslation(0, -0.1f, 0);
com.jme3.math.Quaternion rotation = new com.jme3.math.Quaternion();
rotation.fromAngleAxis(com.jme3.math.FastMath.HALF_PI, com.jme3.math.Vector3f.UNIT_Y);
rotation.fromAngleAxis(FastMath.HALF_PI, com.jme3.math.Vector3f.UNIT_Y);
geom.setLocalRotation(rotation);
app.getRootNode().attachChild(geom);
@ -109,7 +117,8 @@ public class TestWorld implements GameEventListener {
);
model.setLocalScale(0.5f);
Vector3f startPosition = calculateFieldPosition(player.getFieldID(), i);
int playerIndexOnField = calculatePlayerIndexOnField(player.getFieldID(), player.getId());
Vector3f startPosition = calculateFieldPosition(player.getFieldID(), playerIndexOnField);
model.setLocalTranslation(startPosition);
model.setName("PlayerFigure_" + player.getId());
@ -178,72 +187,49 @@ public class TestWorld implements GameEventListener {
}
private void movePlayerFigure(Player player) {
int fieldID = player.getFieldID();
int playerIndex = playerHandler.getPlayers().indexOf(player); // Spielerindex holen
Vector3f targetPosition = calculateFieldPosition(fieldID, playerIndex);
int playerIndexOnField = calculatePlayerIndexOnField(player.getFieldID(), player.getId());
String figureName = "PlayerFigure_" + player.getId();
com.jme3.scene.Spatial figure = app.getRootNode().getChild(figureName);
if (figure != null) {
Vector3f startPosition = figure.getLocalTranslation();
Vector3f cornerPosition = calculateCornerPosition(startPosition, targetPosition); // Berechne Eckpunkt
float animationDuration = 3.0f; // Gesamtdauer der Animation
float[] elapsedTime = {0.0f}; // Verstrichene Zeit
// Berechne das aktuelle Feld basierend auf der Position der Figur
int startFieldID = getFieldIDFromPosition(figure.getLocalTranslation());
int targetFieldID = player.getFieldID();
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
app.enqueue(() -> {
app.getRootNode().addControl(new AbstractControl() {
@Override
protected void controlUpdate(float tpf) {
elapsedTime[0] += tpf;
float progress = Math.min(elapsedTime[0] / animationDuration, 1.0f);
// Bewege die Figur nur, wenn das Ziel-Feld unterschiedlich ist
if (startFieldID != targetFieldID) {
// Verzögerung vor Start der Animation (z.B. 3 Sekunden)
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// Berechne den Pfad basierend auf den Feld-IDs
List<Vector3f> pathPoints = calculatePath(startFieldID, targetFieldID, playerIndexOnField);
Vector3f interpolatedPosition;
if (progress < 0.5f) {
// Erste Hälfte der Animation: Bewegung zur Ecke
float localProgress = progress / 0.5f; // Normiere auf [0, 1]
interpolatedPosition = new Vector3f(
FastMath.interpolateLinear(localProgress, startPosition.x, cornerPosition.x),
FastMath.interpolateLinear(localProgress, startPosition.y, cornerPosition.y),
FastMath.interpolateLinear(localProgress, startPosition.z, cornerPosition.z)
);
} else {
// Zweite Hälfte der Animation: Bewegung vom Eckpunkt zum Ziel
float localProgress = (progress - 0.5f) / 0.5f; // Normiere auf [0, 1]
interpolatedPosition = new Vector3f(
FastMath.interpolateLinear(localProgress, cornerPosition.x, targetPosition.x),
FastMath.interpolateLinear(localProgress, cornerPosition.y, targetPosition.y),
FastMath.interpolateLinear(localProgress, cornerPosition.z, targetPosition.z)
);
}
figure.setLocalTranslation(interpolatedPosition);
// Animation beenden, wenn sie fertig ist
if (progress >= 1.0f) {
this.setEnabled(false);
app.getRootNode().removeControl(this);
System.out.println("Spieler " + player.getId() + " hat das Ziel erreicht: " + targetPosition);
}
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
// Keine spezielle Renderlogik notwendig
}
});
});
}
}, (long) 2000); // Verzögerung in Millisekunden
// Starte die Animation entlang des Pfads
animateMovementAlongPath(figure, pathPoints);
}
}, 3000); // Verzögerung von 3000ms (3 Sekunden)
} else {
System.out.println("Figur für Spieler " + player.getId() + " bleibt auf dem gleichen Feld.");
}
} else {
System.err.println("Figur für Spieler " + player.getId() + " nicht gefunden.");
}
}
private int getFieldIDFromPosition(Vector3f position) {
for (int fieldID = 0; fieldID < 40; fieldID++) {
Vector3f fieldPosition = calculateFieldPosition(fieldID, 0);
if (fieldPosition.distance(position) < 0.5f) { // Toleranz für Positionserkennung
return fieldID;
}
}
throw new IllegalArgumentException("Position entspricht keinem gültigen Feld: " + position);
}
/**
* Berechnet den Eckpunkt basierend auf Start- und Zielposition.
*
@ -252,28 +238,120 @@ public class TestWorld implements GameEventListener {
* @return Die Position der Ecke, die passiert werden muss.
*/
private Vector3f calculateCornerPosition(Vector3f startPosition, Vector3f targetPosition) {
if (Math.abs(startPosition.x - targetPosition.x) > Math.abs(startPosition.z - targetPosition.z)) {
// Bewegung entlang der X-Achse zuerst, dann Z-Achse
return new Vector3f(targetPosition.x, 0, startPosition.z);
// Ziel: Immer entlang der Spielfeldkante navigieren
float deltaX = targetPosition.x - startPosition.x;
float deltaZ = targetPosition.z - startPosition.z;
// Überprüfen, ob Bewegung entlang X oder Z-Koordinate zuerst erfolgen soll
if (deltaX != 0 && deltaZ != 0) {
if (Math.abs(deltaX) > Math.abs(deltaZ)) {
// Bewegung entlang X zuerst
return new Vector3f(targetPosition.x, 0, startPosition.z);
} else {
// Bewegung entlang Z zuerst
return new Vector3f(startPosition.x, 0, targetPosition.z);
}
} else {
// Bewegung entlang der Z-Achse zuerst, dann X-Achse
return new Vector3f(startPosition.x, 0, targetPosition.z);
// Bewegung ist bereits entlang einer Achse (keine Ecke erforderlich)
return targetPosition;
}
}
private List<Vector3f> calculatePath(int startFieldID, int targetFieldID, int playerIndex) {
List<Vector3f> pathPoints = new ArrayList<>();
// Bewegung im Uhrzeigersinn
if (startFieldID < targetFieldID) {
for (int i = startFieldID; i <= targetFieldID; i++) {
// Füge Ecken hinzu, falls sie überschritten werden
if (i == 10 || i == 20 || i == 30 || i == 0) {
pathPoints.add(calculateFieldPosition(i, playerIndex));
}
}
} else {
// Bewegung über das Ende des Spielfelds hinaus (z.B. von 39 zu 5)
for (int i = startFieldID; i < 40; i++) {
if (i == 10 || i == 20 || i == 30 || i == 0) {
pathPoints.add(calculateFieldPosition(i, playerIndex));
}
}
for (int i = 0; i <= targetFieldID; i++) {
if (i == 10 || i == 20 || i == 30 || i == 0) {
pathPoints.add(calculateFieldPosition(i, playerIndex));
}
}
}
// Füge das Ziel hinzu
pathPoints.add(calculateFieldPosition(targetFieldID, playerIndex));
return pathPoints;
}
private int calculatePlayerIndexOnField(int fieldID, int playerID) {
List<Player> playersOnField = playerHandler.getPlayers().stream()
.filter(p -> p.getFieldID() == fieldID)
.toList();
for (int i = 0; i < playersOnField.size(); i++) {
if (playersOnField.get(i).getId() == playerID) {
return i;
}
}
return 0;
}
private void animateMovementAlongPath(com.jme3.scene.Spatial figure, List<Vector3f> pathPoints) {
float animationDurationPerSegment = 2.5f; // Langsamere Animation (2.5 Sekunden pro Segment)
int[] currentSegment = {0};
app.enqueue(() -> {
figure.addControl(new AbstractControl() {
private float elapsedTime = 0.0f;
@Override
protected void controlUpdate(float tpf) {
if (currentSegment[0] >= pathPoints.size() - 1) {
this.setEnabled(false); // Animation abgeschlossen
return;
}
elapsedTime += tpf;
float progress = Math.min(elapsedTime / animationDurationPerSegment, 1.0f);
Vector3f start = pathPoints.get(currentSegment[0]);
Vector3f end = pathPoints.get(currentSegment[0] + 1);
Vector3f interpolatedPosition = start.interpolateLocal(end, progress);
figure.setLocalTranslation(interpolatedPosition);
if (progress >= 1.0f) {
elapsedTime = 0.0f;
currentSegment[0]++;
}
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
// Nicht benötigt
}
});
});
}
@Override
public void receivedEvent(PopUpEvent event) {
if (event.msg().equals("Buy")) {
// Erstelle einen Timer, um den Delay umzusetzen
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// GUI-Operationen im jMonkey-Thread ausführen
app.enqueue(() -> {
int field = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
Object fieldObject = app.getGameLogic().getBoardManager().getFieldAtIndex(field);
if (fieldObject instanceof BuildingProperty) {
new BuildingPropertyCard(app).open();
} else if (fieldObject instanceof GateField) {
@ -283,15 +361,19 @@ public class TestWorld implements GameEventListener {
}
});
}
}, 2500); // Verzögerung in Millisekunden
}, 2500);
} else if (event.msg().equals("Winner")) {
new WinnerPopUp(app).open();
} else if (event.msg().equals("Looser")) {
new LooserPopUp(app).open();
} else if (event.msg().equals("timeout")) {
new TimeOut(app).open();
} else if(event.msg().equals("tradeRequest")) {
} else if (event.msg().equals("tradeRequest")) {
new ConfirmTrade(app).open();
} else if (event.msg().equals("goingToJail")) {
new Gulag(app).open();
} else if (event.msg().equals("NoMoneyWarning")) {
new NoMoneyWarning(app).open();
}
}
@ -301,20 +383,14 @@ public class TestWorld implements GameEventListener {
timer.schedule(new TimerTask() {
@Override
public void run() {
app.enqueue(() -> {
app.getGameLogic().playSound(Sound.EVENT_CARD);
new EventCardPopup(app, event.description()).open();
});
app.enqueue(() -> new EventCardPopup(app, event.description()).open());
}
}, 2500); // 5 Sekunden Verzögerung für Event-Popups
}, 2500);
}
@Override
public void receivedEvent(UpdatePlayerView event) {
this.playerHandler = app.getGameLogic().getPlayerHandler();
// Aktualisiere die Position aller Spielerfiguren
for (Player player : playerHandler.getPlayers()) {
movePlayerFigure(player);
}

View File

@ -14,6 +14,7 @@ import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.popups.Bankrupt;
import pp.monopoly.game.server.Player;
import pp.monopoly.game.server.PlayerHandler;
import pp.monopoly.message.client.EndTurn;
@ -185,8 +186,12 @@ public class Toolbar extends Dialog implements GameEventListener {
endTurnButton.setPreferredSize(new Vector3f(150, 50, 0));
endTurnButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.getGameLogic().send(new EndTurn());
receivedEvent(new ButtonStatusEvent(false));
if (app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getAccountBalance() < 0) {
new Bankrupt(app).open();
} else {
app.getGameLogic().send(new EndTurn());
receivedEvent(new ButtonStatusEvent(false));
}
}));
return endTurnButton;
}

View File

@ -64,7 +64,7 @@ public class Bankrupt extends Dialog {
// Beenden-Button
Button quitButton = bankruptContainer.addChild(new Button("Bestätigen", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(source -> close());
quitButton.addClickCommands(source -> ifTopDialog(this::close));
// Zentriere das Popup

View File

@ -68,14 +68,14 @@ public class EventCardPopup extends Dialog {
eventCardContainer.setLocalTranslation(
(app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y) / 2,
8
10
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y+ padding) / 2,
7
9
);
app.getGuiNode().attachChild(eventCardContainer);

View File

@ -261,10 +261,8 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
@Override
public void received(JailEvent msg) {
if (msg.isGoingToJail()) {
playSound(Sound.GULAG);
} else {
System.out.println("NO MORE JAIL");
notifyListeners(new PopUpEvent("goingToJail", msg));
}
}
@ -345,6 +343,10 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
public void received(NotificationMessage msg) {
if (msg.getKeyWord().equals("rent")) {
notifyListeners(new PopUpEvent("rent", msg));
} else if (msg.getKeyWord().equals("jailpay")) {
notifyListeners(new PopUpEvent(msg.getKeyWord(), msg));
} else if(msg.getKeyWord().equals("NoMoneyWarning")) {
notifyListeners(new PopUpEvent("NoMoneyWarning", msg));
}
}
}

View File

@ -332,6 +332,7 @@ public class Player implements FieldVisitor<Void>{
public Void visit(BuildingProperty field) {
if(field.getOwner() == null) {
if (field.getPrice() <= accountBalance) getHandler().getLogic().send(this, new BuyPropertyRequest());
else getHandler().getLogic().send(this, new NotificationMessage("NoMoneyWarning"));
} else if (field.getOwner() != this){
int rent = field.calcRent();
field.getOwner().earnMoney(rent);
@ -494,6 +495,7 @@ public class Player implements FieldVisitor<Void>{
*/
private static int rollDice() {
return random.nextInt(6) + 1;
// return 3;
}
}
@ -612,6 +614,7 @@ public class Player implements FieldVisitor<Void>{
remainingAttempts--;
if (remainingAttempts <= 0) {
handler.getLogic().send(Player.this, new NotificationMessage("jailpay"));
if(getOutOfJailCard == 0) payBail();
} else {
handler.getLogic().send(Player.this, new NotificationMessage("jailtryagain"));
}
@ -622,23 +625,15 @@ public class Player implements FieldVisitor<Void>{
@Override
public void payBail() {
if (accountBalance >= 500) {
pay(500);
handler.getLogic().send(Player.this, new NotificationMessage(""));
state = new ActiveState();
} else {
handler.getLogic().send(Player.this, new NotificationMessage(""));
}
}
@Override
public void useJailCard() {
if (getOutOfJailCard > 0) {
removeJailCard();
handler.getLogic().send(Player.this, new NotificationMessage(""));
state = new ActiveState();
} else {
handler.getLogic().send(Player.this, new NotificationMessage(""));
}
}
}

View File

@ -14,11 +14,13 @@ import pp.monopoly.message.client.AlterProperty;
import pp.monopoly.message.client.BuyPropertyResponse;
import pp.monopoly.message.client.ClientInterpreter;
import pp.monopoly.message.client.EndTurn;
import pp.monopoly.message.client.NotificationAnswer;
import pp.monopoly.message.client.PlayerReady;
import pp.monopoly.message.client.RollDice;
import pp.monopoly.message.client.TradeOffer;
import pp.monopoly.message.client.TradeResponse;
import pp.monopoly.message.client.ViewAssetsRequest;
import pp.monopoly.message.server.GameOver;
import pp.monopoly.message.server.GameStart;
import pp.monopoly.message.server.NextPlayerTurn;
import pp.monopoly.message.server.PlayerStatusUpdate;
@ -181,8 +183,14 @@ public class ServerGameLogic implements ClientInterpreter {
send(next, new NextPlayerTurn());
send(next, new PlayerStatusUpdate(playerHandler));
send(player, new PlayerStatusUpdate(playerHandler));
} else {
send(player, new GameOver(false));
playerHandler.removePlayer(player);
}
}
if(playerHandler.getPlayers().size() == 1) {
send(playerHandler.getPlayerAtIndex(0), new GameOver(true));
}
updateAllPlayers();
}
@ -400,5 +408,18 @@ public class ServerGameLogic implements ClientInterpreter {
}
}
}
updateAllPlayers();
}
@Override
public void received(NotificationAnswer msg, int from) {
if(msg.getKeyword().equals("UseJailCard")) {
playerHandler.getPlayerById(from).useJailCard();
} else if (msg.getKeyword().equals("PayJail")) {
playerHandler.getPlayerById(from).payBail();
}
updateAllPlayers();
}
}

View File

@ -74,4 +74,12 @@ public interface ClientInterpreter {
* @param from the connection ID from which the message was received
*/
void received(AlterProperty msg, int from);
/**
* Processes a received NotificationAnswer.
*
* @param msg the NotificationAnswer to be processed
* @param from the connection ID from which the message was received
*/
void received(NotificationAnswer msg, int from);
}

View File

@ -0,0 +1,25 @@
package pp.monopoly.message.client;
import com.jme3.network.serializing.Serializable;
@Serializable
public class NotificationAnswer extends ClientMessage{
private String keyword;
private NotificationAnswer() {}
public NotificationAnswer(String keyword) {
this.keyword = keyword;
}
public String getKeyword() {
return keyword;
}
@Override
public void accept(ClientInterpreter interpreter, int from) {
interpreter.received(this, from);
}
}

View File

@ -47,7 +47,7 @@ public class DeckHelper{
cards.add(new Card("Du wurdest zur VP gewählt und schmeißt eine Einstandsparty. Zahle 800 EUR", "vp-einstandsparty"));
cards.add(new Card("Du hast eine Party veranstaltet und dick Gewinn gemacht. Ziehe 1500 EUR ein", "party-gewinn"));
cards.add(new Card("Zur falschen Zeit am falschen Ort. Du musst einen Bergmarsch planen und setzt eine Runde aus.", "bergmarsch"));
cards.add(new Card("Dein Jodel eines Eispenis mit Unterhodenbeleuchtung geht viral. Ziehe 1000 EUR ein", "jodel-eispenis"));
cards.add(new Card("Dein Jodel eines Schneepenis mit Unterhodenbeleuchtung geht viral. Ziehe 1000 EUR ein", "jodel-eispenis"));
shuffle();
}

View File

@ -41,6 +41,7 @@ import pp.monopoly.message.client.ViewAssetsRequest;
import pp.monopoly.message.server.BuyPropertyRequest;
import pp.monopoly.message.server.DiceResult;
import pp.monopoly.message.server.EventDrawCard;
import pp.monopoly.message.server.GameOver;
import pp.monopoly.message.server.GameStart;
import pp.monopoly.message.server.JailEvent;
import pp.monopoly.message.server.NextPlayerTurn;
@ -176,6 +177,7 @@ public class MonopolyServer implements MessageListener<HostedConnection>, Connec
Serializer.registerClass(NotificationMessage.class);
Serializer.registerClass(JailEvent.class);
Serializer.registerClass(AlterProperty.class);
Serializer.registerClass(GameOver.class);
}
private void registerListeners() {