refactor Worlds and client States

This commit is contained in:
Johannes Schmelz
2024-12-08 01:18:35 +00:00
parent 2e256f4ff5
commit e102d22f14
172 changed files with 11159 additions and 3133 deletions

View File

@@ -34,13 +34,13 @@ public class MonopolyConfig extends Config {
* The width of the game map in terms of grid units.
*/
@Property("map.width")
private int mapWidth = 12;
private int mapWidth = 10;
/**
* The height of the game map in terms of grid units.
*/
@Property("map.height")
private int mapHeight = 12;
private int mapHeight = 10;
/**
* Creates an instance of {@code MonopolyConfig} with default settings.

View File

@@ -1,8 +1,40 @@
package pp.monopoly.game.client;
public class ActiveState extends ClientState{
import pp.monopoly.game.server.Player;
import pp.monopoly.message.server.NextPlayerTurn;
import pp.monopoly.message.server.ViewAssetsResponse;
/**
* Represents the active client state in the Monopoly game.
* Extends {@link ClientState}.
*/
public class ActiveState extends ClientState {
/**
* Constructs an ActiveState with the specified game logic.
*
* @param logic the client-side game logic associated with this state
* used to manage game interactions and transitions
*/
ActiveState(ClientGameLogic logic) {
super(logic);
}
}
@Override
boolean isTurn() {
return true;
}
@Override
void recivedNextPlayerTurn(NextPlayerTurn msg) {
logic.getBoard().clear();
for (Player player : logic.getPlayerHandler().getPlayers()) {
logic.getBoard().add(player.getFigure());
}
}
@Override
void recivedViewAssetsResponse(ViewAssetsResponse msg) {
}
}

View File

@@ -6,14 +6,17 @@ import java.util.ArrayList;
import java.util.List;
import pp.monopoly.game.server.Player;
import pp.monopoly.game.server.PlayerHandler;
import pp.monopoly.message.client.ClientMessage;
import pp.monopoly.message.server.BuyPropertyResponse;
import pp.monopoly.message.server.BuildInfo;
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;
import pp.monopoly.message.server.NotificationMessage;
import pp.monopoly.message.server.PlayerStatusUpdate;
import pp.monopoly.message.server.ServerInterpreter;
import pp.monopoly.message.server.TimeOutWarning;
@@ -21,15 +24,24 @@ import pp.monopoly.message.server.TradeReply;
import pp.monopoly.message.server.TradeRequest;
import pp.monopoly.message.server.ViewAssetsResponse;
import pp.monopoly.model.Board;
import pp.monopoly.model.IntPoint;
import pp.monopoly.model.Hotel;
import pp.monopoly.model.House;
import pp.monopoly.model.TradeHandler;
import pp.monopoly.model.fields.BoardManager;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.notification.ClientStateEvent;
import pp.monopoly.notification.DiceRollEvent;
import pp.monopoly.notification.ButtonStatusEvent;
import pp.monopoly.notification.EventCardEvent;
import pp.monopoly.notification.GameEvent;
import pp.monopoly.notification.GameEventBroker;
import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.InfoTextEvent;
import pp.monopoly.notification.ItemAddedEvent;
import pp.monopoly.notification.PopUpEvent;
import pp.monopoly.notification.Sound;
import pp.monopoly.notification.SoundEvent;
import pp.monopoly.notification.UpdatePlayerView;
/**
* Controls the client-side game logic for Monopoly.
@@ -47,15 +59,18 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
private final List<GameEventListener> listeners = new ArrayList<>();
/** The game board representing the player's current state. */
private Board board;
private Board board = new Board(10, 10, this);
/** The current state of the client game logic. */
private ClientState state = new LobbyState(this);
private List<Player> players;
private PlayerHandler playerHandler;
private BoardManager boardManager = new BoardManager();
private TradeHandler tradeHandler;
/**
* Constructs a ClientGameLogic with the specified sender object.
*
@@ -94,8 +109,8 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
state.entry();
}
public List<Player> getPlayers() {
return players;
public PlayerHandler getPlayerHandler() {
return playerHandler;
}
/**
@@ -107,13 +122,8 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
return board;
}
/**
* Moves the preview figure to the specified position.
*
* @param pos the new position for the preview figure
*/
public void movePreview(IntPoint pos) {
state.movePreview(pos);
public TradeHandler getTradeHandler() {
return tradeHandler;
}
/**
@@ -144,7 +154,6 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
LOGGER.log(Level.ERROR, "trying to send {0} with sender==null", msg); //NON-NLS
} else {
clientSender.send(msg);
System.out.println("Message gesendet");
}
}
@@ -191,157 +200,125 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
state.update(delta);
}
/**
* Handles the response for buying a property.
*
* @param msg the message containing the buy property response
*/
@Override
public void received(BuyPropertyResponse msg) {
if (msg.isSuccessful()) {
setInfoText("You successfully bought " + msg.getPropertyName() + "!");
playSound(Sound.MONEY_LOST);
} else {
setInfoText("Unable to buy " + msg.getPropertyName() + ". Reason: " + msg.getReason());
}
public boolean isTurn() {
return state.isTurn();
}
/**
* Handles the result of a dice roll.
*
* @param msg the message containing the dice roll result
*/
@Override
public void received(DiceResult msg) {
setInfoText("You rolled a " + msg.calcTotal() + "!");
//Set the dice images
playSound(Sound.DICE_ROLL);
notifyListeners(new DiceRollEvent(msg.getRollResult().get(0), msg.getRollResult().get(1)));
}
/**
* Handles drawing an event card.
*
* @param msg the message containing the drawn card details
*/
@Override
public void received(EventDrawCard msg) {
setInfoText("Event card drawn: " + msg.getCardDescription());
// Kartenlogik
playSound(Sound.EVENT_CARD);
notifyListeners(new EventCardEvent(msg.getCardDescription()));
}
/**
* Handles the game over message.
*
* @param msg the message containing game over details
*/
@Override
public void received(GameOver msg) {
if (msg.isWinner()) {
setInfoText("Congratulations! You have won the game!");
//Winner popup
notifyListeners(new PopUpEvent("Winner", msg));
playSound(Sound.WINNER);
} else {
setInfoText("Game over. Better luck next time!");
// Looser popup
notifyListeners(new PopUpEvent("Looser", msg));
playSound(Sound.LOSER);
}
}
/**
* Handles the start of the game.
*
* @param msg the game start message
*/
@Override
public void received(GameStart msg) {
players = msg.getPlayers();
setInfoText("The game has started! Good luck!");
playerHandler = msg.getPlayerHandler();
setState(new WaitForTurnState(this));
for (Player player : playerHandler.getPlayers()) {
board.add(player.getFigure());
}
notifyListeners(new ButtonStatusEvent(false));
notifyListeners(new UpdatePlayerView());
}
/**
* Handles jail-related events.
*
* @param msg the message containing jail event details
*/
@Override
public void received(JailEvent msg) {
if (msg.isGoingToJail()) {
setInfoText("You are sent to jail!");
playSound(Sound.GULAG);
} else {
setInfoText("You are out of jail!");
notifyListeners(new PopUpEvent("goingToJail", msg));
}
}
/**
* Updates the status of a player.
*
* @param msg the message containing player status update details
*/
@Override
public void received(PlayerStatusUpdate msg) {
setInfoText("Player " + msg.getPlayerName() + " status updated: " + msg.getStatus());
playerHandler = msg.getPlayerHandler();
System.out.println("Update Player");
notifyListeners(new UpdatePlayerView());
}
/**
* Handles timeout warnings.
*
* @param msg the message containing timeout warning details
*/
@Override
public void received(TimeOutWarning msg) {
setInfoText("Warning! Time is running out. You have " + msg.getRemainingTime() + " seconds left.");
notifyListeners(new PopUpEvent("timeout", msg));
}
/**
* Displays the player's assets in response to a server query.
*
* @param msg the message containing the player's assets
*/
@Override
public void received(ViewAssetsResponse msg) {
setInfoText("Your current assets are being displayed.");
boardManager = msg.getboard();
}
/**
* Handles trade replies from other players.
*
* @param msg the message containing the trade reply
*/
@Override
public void received(TradeReply msg) {
if (msg.getTradeHandler().getStatus()) {
setInfoText("Trade accepted by " + msg.getTradeHandler().getReceiver().getName() + ".");
if (msg.isAccepted()) {
playSound(Sound.TRADE_ACCEPTED);
notifyListeners(new PopUpEvent("tradepos", msg));
} else {
setInfoText("Trade rejected by " + msg.getTradeHandler().getReceiver().getName() + ".");
playSound(Sound.TRADE_REJECTED);
notifyListeners(new PopUpEvent("tradeneg", msg));
}
}
/**
* Handles trade requests from other players.
*
* @param msg the message containing the trade request details
*/
@Override
public void received(TradeRequest msg) {
setInfoText("Trade offer received from " + msg.getTradeHandler().getSender().getName());
// playSound(Sound.TRADE_REQUEST); no sound effect
// notifyListeners();
tradeHandler = msg.getTradeHandler();
notifyListeners(new PopUpEvent("tradeRequest", msg));
}
/**
* Handles the transition to the next player's turn.
*
* @param msg the message indicating it's the next player's turn
*/
@Override
public void received(NextPlayerTurn msg) {
setInfoText("It's your turn!");
setState(new ActiveState(this));
notifyListeners(new ButtonStatusEvent(true));
}
@Override
public void received(BuyPropertyRequest msg) {
notifyListeners(new PopUpEvent("Buy", msg));
}
@Override
public void received(NotificationMessage msg) {
if (msg.getKeyWord().equals("rent")) {
notifyListeners(new PopUpEvent("rent", msg));
playSound(Sound.MONEY_LOST);
} else if (msg.getKeyWord().equals("jailpay")) {
playSound(Sound.MONEY_LOST);
notifyListeners(new PopUpEvent(msg.getKeyWord(), msg));
} else if(msg.getKeyWord().equals("NoMoneyWarning")) {
notifyListeners(new PopUpEvent("NoMoneyWarning", msg));
} else if (msg.getKeyWord().equals("jailtryagain")) {
notifyListeners(new PopUpEvent("jailtryagain", msg));
} else if(msg.getKeyWord().equals("ReceivedRent")) {
playSound(Sound.MONEY_COLLECTED);
notifyListeners(new PopUpEvent("ReceivedRent", msg));
}
}
@Override
public void received(BuildInfo msg) {
System.out.println("TRIGGER BUILD INFO");
if (msg.isAdded()) {
BuildingProperty property = ((BuildingProperty)boardManager.getFieldAtIndex(msg.getId()));
if (property.getHotel() == 1 ) {
board.add(new Hotel(property.getId()));
} else {
board.add(new House( property.getHouses(), property.getId()));
}
}
}
}

View File

@@ -6,24 +6,29 @@
////////////////////////////////////////
package pp.monopoly.game.client;
import pp.monopoly.model.IntPoint;
import java.io.File;
import java.io.IOException;
import java.lang.System.Logger.Level;
import pp.monopoly.message.server.GameStart;
import pp.monopoly.message.server.NextPlayerTurn;
import pp.monopoly.message.server.NotificationMessage;
import pp.monopoly.message.server.PlayerStatusUpdate;
import pp.monopoly.message.server.ViewAssetsResponse;
/**
* Defines the behavior and state transitions for the client-side game logic.
* Different states of the game logic implement this interface to handle various game events and actions.
* Defines the behavior and state transitions for the client-side game logic in Monopoly.
* Different states of the game logic implement this abstract class to handle various game events and actions.
*/
abstract class ClientState {
/**
* The game logic object.
* The game logic object managing the client-side state.
*/
final ClientGameLogic logic;
/**
* Constructs a client state of the specified game logic.
* Constructs a client state for the specified game logic.
*
* @param logic the game logic
*/
@@ -49,31 +54,53 @@ abstract class ClientState {
}
/**
* Checks if the battle state should be shown.
* Checks if the player's turn should be shown in the current state.
*
* @return true if the battle state should be shown, false otherwise
* @return true if the player's turn should be shown, false otherwise
*/
boolean showTurn() {
boolean isTurn() {
return false;
}
/**
* Moves the preview figure to the specified position.
* Starts the battle based on the server message.
*
* @param pos the new position for the preview figure
* @param msg the message indicating whose turn it is to shoot
*/
void movePreview(IntPoint pos) {
ClientGameLogic.LOGGER.log(Level.DEBUG, "movePreview has no effect in {0}", getName()); //NON-NLS
void receivedGameStart(GameStart msg) {
ClientGameLogic.LOGGER.log(Level.ERROR, "receivedGameStart not allowed in {0}", getName()); //NON-NLS
}
/**
* Loads a map from the specified file.
* Updateds the view based on the new player status.
*
* @param file the file to load the map from
* @throws IOException if the map cannot be loaded in the current state
* @param msg the message containing the new player status
*/
void loadMap(File file) throws IOException {
throw new IOException("You are not allowed to load a map in this state of the game");
void recivedPlayerStatusUpdate(PlayerStatusUpdate msg) {
ClientGameLogic.LOGGER.log(Level.ERROR, "recivedPlayerStatusUpdate not allowed in {0}", getName()); //NON-NLS
}
void recivedNextPlayerTurn(NextPlayerTurn msg) {
ClientGameLogic.LOGGER.log(Level.ERROR, "recivedNextPlayerTurn not allowed in {0}", getName()); //NON-NLS
}
void recivedNotificationMessage(NotificationMessage msg) {
ClientGameLogic.LOGGER.log(Level.ERROR, "recivedNotificationMessage not allowed in {0}", getName()); //NON-NLS
}
void recivedViewAssetsResponse(ViewAssetsResponse msg) {
ClientGameLogic.LOGGER.log(Level.ERROR, "recivedViewAssetsResponse not allowed in {0}", getName()); //NON-NLS
}
/**
* Loads a game configuration from the specified file.
*
* @param file the file to load the game configuration from
* @throws IOException if the configuration cannot be loaded in the current state
*/
void loadGameConfig(File file) throws IOException {
throw new IOException("You are not allowed to load a game configuration in this state of the game.");
}
/**
@@ -81,5 +108,7 @@ abstract class ClientState {
*
* @param delta time in seconds since the last update call
*/
void update(float delta) { /* do nothing by default */ }
void update(float delta) {
// Default implementation does nothing
}
}

View File

@@ -1,8 +1,24 @@
package pp.monopoly.game.client;
public class LobbyState extends ClientState{
import pp.monopoly.message.server.GameStart;
/**
* Represents the lobby state of the client in the Monopoly game.
* Extends {@link ClientState}.
*/
public class LobbyState extends ClientState {
/**
* Constructs a LobbyState with the specified game logic.
*
* @param logic the client-side game logic
*/
LobbyState(ClientGameLogic logic) {
super(logic);
}
@Override
void receivedGameStart(GameStart msg) {
logic.setState(new WaitForTurnState(logic));
}
}

View File

@@ -1,8 +1,29 @@
package pp.monopoly.game.client;
public class WaitForTurnState extends ClientState{
import pp.monopoly.message.server.NextPlayerTurn;
/**
* Represents the state where the client is waiting for their turn in the Monopoly game.
* Extends {@link ClientState}.
*/
public class WaitForTurnState extends ClientState {
/**
* Constructs a WaitForTurnState with the specified game logic.
*
* @param logic the client-side game logic
*/
WaitForTurnState(ClientGameLogic logic) {
super(logic);
}
@Override
boolean isTurn() {
return true;
}
@Override
void recivedNextPlayerTurn(NextPlayerTurn msg) {
logic.setState(new ActiveState(logic));
}
}

View File

@@ -7,12 +7,21 @@
package pp.monopoly.game.server;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.message.server.BuyPropertyRequest;
import pp.monopoly.message.server.DiceResult;
import pp.monopoly.message.server.EventDrawCard;
import pp.monopoly.message.server.JailEvent;
import pp.monopoly.message.server.NextPlayerTurn;
import pp.monopoly.message.server.NotificationMessage;
import pp.monopoly.message.server.PlayerStatusUpdate;
import pp.monopoly.model.FieldVisitor;
import pp.monopoly.model.Figure;
import pp.monopoly.model.card.Card;
@@ -36,12 +45,12 @@ public class Player implements FieldVisitor<Void>{
private String name;
private int accountBalance = 15000;
private Figure figure;
private List<PropertyField> properties;
private Set<Integer> properties = new HashSet<>();
private int getOutOfJailCard;
private int fieldID;
private DiceResult rollResult;
private final PlayerHandler handler;
private PlayerState state = new LobbyState();
private transient final PlayerHandler handler;
private transient PlayerState state = new WaitForTurnState();
/**
* Default constructor for serialization purposes.
@@ -76,15 +85,19 @@ public class Player implements FieldVisitor<Void>{
public void setFigure(Figure figure) {
this.figure = figure;
}
public Figure getFigure(){
return figure;
}
public PlayerColor getColor() {
public static PlayerColor getColor(int id) {
switch ((id%6)+1) {
case 1: return PlayerColor.BLUE;
case 2: return PlayerColor.GREEN_DARK;
case 3: return PlayerColor.GREEN_LIGHT;
case 1: return PlayerColor.CYAN;
case 2: return PlayerColor.YELLOW;
case 3: return PlayerColor.RED;
case 4: return PlayerColor.PINK;
case 5: return PlayerColor.RED;
case 6: return PlayerColor.YELLOW;
case 5: return PlayerColor.GREEN;
case 6: return PlayerColor.PURPLE;
default:
return null;
@@ -122,53 +135,96 @@ public class Player implements FieldVisitor<Void>{
public int getFieldID() {
return fieldID;
}
void setActive() {
state = new ActiveState();
System.out.println("State: "+state.getClass().getName());
if(!(state instanceof JailState)) {
state = new ActiveState();
} else {
String message = (((JailState)state).remainingAttempts <= 0) ? "jailpay" : "jailtryagain";
handler.getLogic().send(this, new NotificationMessage(message));
}
}
boolean finishTurn() {
if(canFinishTurn()) {
state = new WaitForTurnState();
if (state instanceof ActiveState) state = new WaitForTurnState();
return true;
}
else return false;
else {
bankrupt();
return false;
}
}
boolean canFinishTurn() {
return accountBalance >= 0;
}
/**
* Moves by the specified amount of steps
* @param steps the number of steps to move
* @return the new position
*/
public int move(int steps){
return movePos(fieldID+steps);
public PlayerState getState() {
return state;
}
public void setState(PlayerState state) {
this.state = state;
}
/**
* Moves the player to the specified Position on the board
* @param position the position to move to
* Moves the player by a given number of steps, handling board wrapping.
*
* @param steps number of steps to move
* @return the new position
*/
public int movePos(int position){
fieldID = fieldID+position;
if(fieldID >= 40) {
fieldID = fieldID%40;
earnMoney(2000);
public int move(int steps) {
if (fieldID + steps >= 40) {
earnMoney(2000); // Passing GO gives money
}
fieldID = (fieldID + steps) % 40;
figure.moveTo(fieldID);
handler.getLogic().send(this, new PlayerStatusUpdate(handler));
handler.getLogic().getBoardManager().getFieldAtIndex(fieldID).accept(this);
return fieldID;
}
/**
* Sets the player to the specified Position on the board
* @param position the position to move to
* @return the new position
*/
public int setPosition(int position){
if(position < 40 && position >= 0) {
fieldID = position;
figure.moveTo(fieldID);
handler.getLogic().send(this, new PlayerStatusUpdate(handler));
handler.getLogic().getBoardManager().getFieldAtIndex(fieldID).accept(this);
}
return fieldID;
}
/**
* Sets the player to the specified Position on the board
* @param position the position to move to
* @return the new position
*/
public int setPositionWithMoney(int position){
if(position < 40 && position >= 0) {
if(position < fieldID) {
earnMoney(2000);
}
fieldID = position;
figure.moveTo(fieldID);
handler.getLogic().send(this, new PlayerStatusUpdate(handler));
handler.getLogic().getBoardManager().getFieldAtIndex(fieldID).accept(this);
}
return fieldID;
}
/**
* Gets all the properties owned by this player
* @return List of all properties owned by this player
*/
public List<PropertyField> getProperties() {
return properties;
public List<Integer> getProperties() {
return new ArrayList<>(properties);
}
/**
@@ -178,7 +234,7 @@ public class Player implements FieldVisitor<Void>{
*/
public void buyProperty(PropertyField property) {
if (property.getOwner() == null && accountBalance >= property.getPrice()) {
properties.add(property);
properties.add(property.getId());
property.setOwner(this);
pay(property.getPrice());
}
@@ -190,7 +246,7 @@ public class Player implements FieldVisitor<Void>{
*/
public void sellProperty(PropertyField property) {
if (property.getOwner() == this) {
properties.remove(property);
properties.remove(property.getId());
property.setOwner(null);
}
}
@@ -275,38 +331,63 @@ public class Player implements FieldVisitor<Void>{
state.useJailCard();
}
private void sendRentNotification(String keyword, Player player, int amount) {
NotificationMessage msg = new NotificationMessage(keyword);
msg.setRentAmount(amount);
msg.setRentOwnerId(player.getName());
getHandler().getLogic().send(player, msg);
}
@Override
public Void visit(BuildingProperty field) {
int rent = field.calcRent();
field.getOwner().earnMoney(rent);
pay(rent);
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);
pay(rent);
sendRentNotification("ReceivedRent", field.getOwner(), rent);
sendRentNotification("rent", this, rent);
}
return null;
}
@Override
public Void visit(FoodField field) {
int factor = 4;
if (field.getOwner().getNumProp(field) == 2) {
factor = 10;
if(field.getOwner() == null) {
if (field.getPrice() <= accountBalance) getHandler().getLogic().send(this, new BuyPropertyRequest());
} else if (field.getOwner() != this){
int factor = 40;
if (field.getOwner().getNumProp(field) == 2) {
factor = 100;
}
int rent = rollResult.calcTotal()*factor;
field.getOwner().earnMoney(rent);
pay(rent);
sendRentNotification("ReceivedRent", field.getOwner(), rent);
sendRentNotification("rent", this, rent);
}
field.getOwner().earnMoney(rollResult.calcTotal()*factor);
pay(rollResult.calcTotal()*factor);
return null;
}
@Override
public Void visit(GateField field) {
int rent = field.calcRent() * field.getOwner().getNumProp(field);
if(field.getOwner() == null) {
if (field.getPrice() <= accountBalance) getHandler().getLogic().send(this, new BuyPropertyRequest());
} else if (field.getOwner() != this){
int rent = field.calcRent() * field.getOwner().getNumProp(field);
field.getOwner().earnMoney(rent);
pay(rent);
field.getOwner().earnMoney(rent);
pay(rent);
sendRentNotification("ReceivedRent", field.getOwner(), rent);
sendRentNotification("rent", this, rent);
}
return null;
}
@Override
public Void visit(GulagField field) {
state = new JailState();
return null;
}
@@ -319,21 +400,21 @@ public class Player implements FieldVisitor<Void>{
@Override
public Void visit(EventField field) {
Card c = getHandler().getLogic().getDeckHelper().drawCard();
getHandler().getLogic().getDeckHelper().visit(c, this);
getHandler().getLogic().getDeckHelper().visit(c, this); //Logic
getHandler().getLogic().send(this, new EventDrawCard(c.getDescription())); // Card notification
getHandler().getLogic().send(this, new PlayerStatusUpdate(getHandler())); //update view
return null;
}
@Override
public Void visit(WacheField field) {
movePos(10);
moveToJail();
return null;
}
@Override
public Void visit(GoField field) {
earnMoney(2000);
GulagField res = (GulagField) handler.getLogic().getBoardManager().getFieldAtIndex(10);
res.accept(this);
return null;
}
@@ -346,6 +427,30 @@ public class Player implements FieldVisitor<Void>{
return null;
}
public List<PropertyField> getPropertyFields() {
List<PropertyField> properties = new ArrayList<>();
for (Integer i : this.properties) {
properties.add((PropertyField)getHandler().getLogic().getBoardManager().getFieldAtIndex(i));
}
return properties;
}
/**
* Sends the player to jail by setting position and state.
*/
public void moveToJail() {
setPosition(10); // Jail position on the board
state = new JailState();
handler.getLogic().send(this, new JailEvent(true));
}
void jail() {
state = new JailState();
fieldID = 10;
}
/**
* Return the number of Properties of the speciefied fild type
* @param field the type of field to search for
@@ -353,7 +458,9 @@ public class Player implements FieldVisitor<Void>{
*/
public int getNumProp(PropertyField field) {
int count = 0;
for (PropertyField propertyField : properties) {
if (properties.isEmpty()) return 0;
for (PropertyField propertyField : getPropertyFields()) {
if (propertyField.getClass() == field.getClass()) {
count++;
}
@@ -364,7 +471,7 @@ public class Player implements FieldVisitor<Void>{
public int getNumHouses() {
int total = 0;
for (PropertyField field : properties) {
for (PropertyField field : getPropertyFields()) {
if (field.getClass() == BuildingProperty.class) {
total += ((BuildingProperty) field).getHouses();
}
@@ -374,15 +481,16 @@ public class Player implements FieldVisitor<Void>{
public int getNumHotels() {
int total = 0;
for (PropertyField field : properties) {
for (PropertyField field : getPropertyFields()) {
if (field.getClass() == BuildingProperty.class) {
total += ((BuildingProperty) field).getHotel();
}
}
return total;
}
// private static int c = 0;
/**
/**
* Inner class for dice functionality in the game.
* Rolls random dice values.
*/
@@ -396,22 +504,36 @@ public class Player implements FieldVisitor<Void>{
*/
private static int rollDice() {
return random.nextInt(6) + 1;
// c++;
// return (c%2 == 0)? 3: 2;
// if(c < 7) {
// return 3;
// } else {
// return (c%4)+1;
// }
}
}
/**
* Rolls two dice and returns a list with the results.
* Rolls two dice, handles movement logic, and checks for doublets.
*
* @return a List of two integers representing the dice roll results
* @return the DiceResult of the roll
*/
DiceResult rollDice() {
return state.rollDice();
}
private void bankrupt() {
for (PropertyField field : getPropertyFields()) {
field.setOwner(null);
}
properties.clear();
}
/**
* A interface representing the PlayerStates
*/
private interface PlayerState {
interface PlayerState {
/**
* Handles the logic for rolling Dice
* @return the {@link DiceResult} of this the DiceRoll
@@ -436,10 +558,39 @@ public class Player implements FieldVisitor<Void>{
*/
private class ActiveState implements PlayerState {
private int doubletscounter = 3;
private boolean mayRollDice = true;
@Override
public DiceResult rollDice() {
if (!mayRollDice) {
throw new IllegalStateException("Player is not allowed to roll dice at this moment.");
}
List<Integer> roll = List.of(Dice.rollDice(), Dice.rollDice());
rollResult = new DiceResult(roll);
rollResult = new DiceResult(roll.get(0), roll.get(1));
mayRollDice = false;
// Check for doublets
if (rollResult.isDoublets()) {
doubletscounter--; // Decrement doublets counter
if (doubletscounter <= 0) {
// Player rolls third doublet -> move to jail
moveToJail();
doubletscounter = 3; // Reset doublets counter
} else {
// Player rolls doublets but can roll again
mayRollDice = true;
move(rollResult.calcTotal()); // Move player for this roll
getHandler().getLogic().send(Player.this, new NextPlayerTurn());
}
} else {
// No doublets -> move normally and reset doublets counter
move(rollResult.calcTotal());
}
return rollResult;
}
@@ -455,71 +606,55 @@ public class Player implements FieldVisitor<Void>{
}
/**
* A class to represent the Lobby PlayerState
* Set when in Lobby
*/
private class LobbyState implements PlayerState{
@Override
public DiceResult rollDice() {
//do nothing
return null;
}
@Override
public void payBail() {
//do nothing
}
@Override
public void useJailCard() {
// do nothing
}
}
/**
* A class to represent the Jailed PlayerState
* Set when in Gulag
*/
private class JailState implements PlayerState {
private int DoubletsCounter = 3;
private int remainingAttempts = 3;
@Override
public DiceResult rollDice() {
List<Integer> roll = List.of(Dice.rollDice(), Dice.rollDice());
rollResult = new DiceResult(roll);
if (rollResult.isDoublets()) {
state = new ActiveState();
} else if (DoubletsCounter == 0) {
} else {
DoubletsCounter--;
if (remainingAttempts <= 0) {
return null;
}
List<Integer> roll = List.of(Dice.rollDice(), Dice.rollDice());
rollResult = new DiceResult(roll.get(0), roll.get(1));
handler.getLogic().send(Player.this, new DiceResult (rollResult.getRollResult().get(0), rollResult.getRollResult().get(1)));
if (rollResult.isDoublets()) {
handler.getLogic().send(Player.this, new JailEvent(false));
state = new ActiveState();
getHandler().getLogic().send(Player.this, new NextPlayerTurn());
} else {
remainingAttempts--;
}
System.out.println("Feld:"+fieldID);
return rollResult;
}
@Override
public void payBail() {
pay(500);
state = new ActiveState();
pay(500);
state = new ActiveState();
getHandler().getLogic().send(Player.this, new NextPlayerTurn());
}
@Override
public void useJailCard() {
getOutOfJailCard--;
state = new ActiveState();
if (getOutOfJailCard > 0) {
removeJailCard();
state = new ActiveState();
}
}
}
private class WaitForTurnState implements PlayerState {
@Override
public DiceResult rollDice() {
throw new UnsupportedOperationException("not allowed");
return null;
}
@Override
@@ -531,4 +666,17 @@ public class Player implements FieldVisitor<Void>{
}
}
@Override
public String toString() {
return "Player{name=" + name + ", figure=" + figure + "}";
}
public void addProperty(Integer id) {
properties.add(id);
}
public void removeProperty(Integer id) {
properties.remove(id);
}
}

View File

@@ -6,22 +6,24 @@ import com.jme3.math.ColorRGBA;
* Enum representing six distinct colors for players in the game.
*/
public enum PlayerColor {
GREEN_LIGHT(new ColorRGBA(0 / 255f, 204 / 255f, 0 / 255f, 1)), // Hex: 00cc00
RED(new ColorRGBA(255 / 255f, 0 / 255f, 0 / 255f, 1)), // Hex: ff0000
BLUE(new ColorRGBA(0 / 255f, 0 / 255f, 204 / 255f, 1)), // Hex: 0000cc
PINK(new ColorRGBA(255 / 255f, 77 / 255f, 166 / 255f, 1)), // Hex: ff4da6
GREEN_DARK(new ColorRGBA(0 / 255f, 102 / 255f, 0 / 255f, 1)), // Hex: 006600
YELLOW(new ColorRGBA(255 / 255f, 255 / 255f, 0 / 255f, 1)); // Hex: ffff00
CYAN(new ColorRGBA(1 / 255f, 190 / 255f, 254 / 255f, 1), "Cyan"),
YELLOW(new ColorRGBA(255 / 255f, 255 / 255f, 0 / 255f, 1), "Gelb"),
RED(new ColorRGBA(255 / 255f, 0 / 255f, 0 / 255f, 1), "Rot"),
PINK(new ColorRGBA(255 / 255f, 77 / 255f, 166 / 255f, 1), "Pink"),
GREEN(new ColorRGBA(0 / 255f, 204 / 255f, 0 / 255f, 1), "Grün"),
PURPLE(new ColorRGBA(143 / 255f, 0 / 255f, 255 / 255f, 1), "Lila");
private final ColorRGBA color;
private final String colorName;
/**
* Constructs a PlayerColor with the specified ColorRGBA value.
*
* @param color the ColorRGBA value associated with the player color
*/
PlayerColor(ColorRGBA color) {
PlayerColor(ColorRGBA color, String colorName) {
this.color = color;
this.colorName = colorName;
}
/**
@@ -32,4 +34,13 @@ public enum PlayerColor {
public ColorRGBA getColor() {
return color;
}
/**
* Gets the Name of the Color
*
* @return a String with re corresponing name
*/
public String getColorName() {
return colorName;
}
}

View File

@@ -1,9 +1,9 @@
package pp.monopoly.game.server;
import java.util.LinkedList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
@@ -16,10 +16,9 @@ import pp.monopoly.model.LimitedLinkedList;
*/
@Serializable
public class PlayerHandler {
private List<Player> players = new LimitedLinkedList<>(6);
private List<Player> players = new LinkedList<>();
private Set<Player> readyPlayers = new HashSet<>();
private ServerGameLogic logic;
private Player hostPlayer;
private transient ServerGameLogic logic;
private Player extra = null;
/**
@@ -55,14 +54,6 @@ public class PlayerHandler {
players.addAll(players);
}
/**
* Return the host player
* @return the host player
*/
public Player getHostPlayer() {
return hostPlayer;
}
/**
* Return the number of players
* @return number of players in the game
@@ -114,9 +105,6 @@ public class PlayerHandler {
throw new IllegalArgumentException("Player already registered");
}
players.add(player);
if(hostPlayer == null) {
hostPlayer = player;
}
}
/**

View File

@@ -2,16 +2,26 @@ package pp.monopoly.game.server;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.jme3.math.Vector3f;
import pp.monopoly.MonopolyConfig;
import pp.monopoly.message.client.BuyPropertyRequest;
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.BuildInfo;
import pp.monopoly.message.server.GameOver;
import pp.monopoly.message.server.GameStart;
import pp.monopoly.message.server.NextPlayerTurn;
import pp.monopoly.message.server.PlayerStatusUpdate;
@@ -19,11 +29,12 @@ import pp.monopoly.message.server.ServerMessage;
import pp.monopoly.message.server.TradeReply;
import pp.monopoly.message.server.TradeRequest;
import pp.monopoly.message.server.ViewAssetsResponse;
import pp.monopoly.model.Board;
import pp.monopoly.model.Figure;
import pp.monopoly.model.Rotation;
import pp.monopoly.model.TradeHandler;
import pp.monopoly.model.card.DeckHelper;
import pp.monopoly.model.fields.BoardManager;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.model.fields.PropertyField;
/**
@@ -87,6 +98,17 @@ public class ServerGameLogic implements ClientInterpreter {
}
}
/**
* Sends a message to all players in the game.
*
* @param msg the ServerMessage to send
*/
void sendAll(ServerMessage msg) {
for (Player player : playerHandler.getPlayers()) {
send(player, msg);
}
}
/**
* Adds a new player to the game if the game is in the LOBBY state and the maximum
* player limit has not been reached.
@@ -132,7 +154,6 @@ public class ServerGameLogic implements ClientInterpreter {
playerHandler.addPlayer(player);
LOGGER.log(Level.DEBUG, "Player added: {0}", player.getId());
System.out.println("Anzahl Spieler verbunden:"+ playerHandler.getPlayerCount());
return player;
}
@@ -145,20 +166,16 @@ public class ServerGameLogic implements ClientInterpreter {
* @param from the connection ID of the player who sent the request
*/
@Override
public void received(BuyPropertyRequest msg, int from) {
public void received(BuyPropertyResponse msg, int from) {
Player player = playerHandler.getPlayerById(from);
if (player != null && state == ServerState.INGAME) {
PropertyField property = (PropertyField) boardManager.getFieldAtIndex(player.move(0)); // Assuming player position for property
if (player != null) {
PropertyField property = (PropertyField) boardManager.getFieldAtIndex(player.getFieldID()); // Assuming player position for property
if (property.getOwner() == null && player.getAccountBalance() >= property.getPrice()) {
player.buyProperty(property);
property.setOwner(player);
player.earnMoney(-property.getPrice());
LOGGER.log(Level.INFO, "Player {0} bought property {1}", player.getName(), property.getName());
} else {
LOGGER.log(Level.WARNING, "Player {0} cannot buy property {1}", player.getName(), property.getName());
}
player.buyProperty(property);
System.out.println("Properties:" +player.getProperties().toString());
LOGGER.log(Level.INFO, "Player {0} bought property {1}", player.getName(), property.getName());
}
updateAllPlayers();
}
/**
@@ -170,14 +187,23 @@ public class ServerGameLogic implements ClientInterpreter {
@Override
public void received(EndTurn msg, int from) {
Player player = playerHandler.getPlayerById(from);
if (player != null && state == ServerState.INGAME) {
if (player != null && player == playerHandler.getPlayerAtIndex(0)) {
if (player.finishTurn()) {
LOGGER.log(Level.DEBUG, "Ending turn for player {0}", player.getName());
Player next = playerHandler.nextPlayer();
next.setActive();
send(next, new NextPlayerTurn(next));
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();
}
/**
@@ -189,14 +215,17 @@ public class ServerGameLogic implements ClientInterpreter {
@Override
public void received(PlayerReady msg, int from) {
Player player = playerHandler.getPlayerById(from);
if(player == playerHandler.getHostPlayer()) {
if(player.getId() == 0) {
startMoney = msg.getStartMoney();
}
if (player != null) {
player.setName(msg.getName());
player.setFigure(new Figure(1, -10, -10, Rotation.LEFT, msg.getFigure()));
//TODO add figure to the map
String name = msg.getName();
String truc = name.length() > 10 ? name.substring(0, 15) : name;
player.setName(truc);
Figure figure = new Figure(new Vector3f(0, 1, 0), Rotation.NORTH, msg.getFigure());
player.setFigure(figure);
// board.addFigure(figure);
playerHandler.setPlayerReady(player, true);
LOGGER.log(Level.DEBUG, "Player {0} is ready", player.getName());
}
@@ -204,10 +233,10 @@ public class ServerGameLogic implements ClientInterpreter {
if(playerHandler.allPlayersReady()) {
playerHandler.setStartBalance(startMoney);
for (Player p : playerHandler.getPlayers()) {
send(p, new GameStart(playerHandler.getPlayers()));
send(p, new GameStart(playerHandler));
}
playerHandler.randomOrder();
send(playerHandler.getPlayerAtIndex(0), new NextPlayerTurn(playerHandler.getPlayerAtIndex(0)));
send(playerHandler.getPlayerAtIndex(0), new NextPlayerTurn());
}
}
@@ -220,8 +249,9 @@ public class ServerGameLogic implements ClientInterpreter {
@Override
public void received(RollDice msg, int from) {
Player player = playerHandler.getPlayerById(from);
if (player != null && state == ServerState.INGAME) {
if (player != null && player == playerHandler.getPlayerAtIndex(0)) {
send(player, player.rollDice());
updateAllPlayers();
}
}
@@ -234,11 +264,11 @@ public class ServerGameLogic implements ClientInterpreter {
@Override
public void received(TradeOffer msg, int from) {
Player sender = playerHandler.getPlayerById(from);
Player receiver = playerHandler.getPlayerById(msg.getReceiverId());
Player receiver = msg.getTradeHandler().getReceiver();
if (sender != null && receiver != null) {
LOGGER.log(Level.INFO, "Player {0} offers a trade to player {1}", sender.getName(), receiver.getName());
send(playerHandler.getPlayerById(msg.getReceiverId()), new TradeRequest(msg.getReceiverId(), msg.getTradeHandler()));
send(receiver, new TradeRequest(msg.getTradeHandler()));
}
}
@@ -250,15 +280,79 @@ public class ServerGameLogic implements ClientInterpreter {
*/
@Override
public void received(TradeResponse msg, int from) {
Player responder = playerHandler.getPlayerById(from);
Player initiator = playerHandler.getPlayerById(msg.getInitiatorId());
TradeHandler tradeHandler = msg.getTradeHandler();
if (responder != null && initiator != null) {
LOGGER.log(Level.INFO, "Player {0} responded to trade with player {1}", responder.getName(), initiator.getName());
send(initiator, new TradeReply(msg.getInitiatorId(), msg.getTradeHandler()));
Player receiver = playerHandler.getPlayerById(tradeHandler.getReceiver().getId());
Player sender = playerHandler.getPlayerById(tradeHandler.getSender().getId());
if(msg.isAccepted()) {
Set<PropertyField> offered = new HashSet<>();
for (PropertyField propertyField : tradeHandler.getOfferedProperties()) {
offered.add( (PropertyField) boardManager.getFieldAtIndex(propertyField.getId()));
}
Set<PropertyField> requested = new HashSet<>();
for (PropertyField propertyField : tradeHandler.getRequestedProperties()) {
requested.add( (PropertyField) boardManager.getFieldAtIndex(propertyField.getId()));
}
executeTrade(sender, receiver, offered, requested);
sender.earnMoney(tradeHandler.getRequestedAmount());
sender.pay(tradeHandler.getOfferedAmount());
receiver.earnMoney(tradeHandler.getOfferedAmount());
receiver.pay(tradeHandler.getRequestedAmount());
for (int i = 0; i < tradeHandler.getOfferedJailCards(); i++) {
sender.removeJailCard();
receiver.addJailCard();
}
for (int i = 0; i < tradeHandler.getRequestedJailCards(); i++) {
sender.addJailCard();
receiver.removeJailCard();
}
}
updateAllPlayers();
if (receiver != null && sender != null) {
LOGGER.log(Level.INFO, "Player {0} responded to trade with player {1}", receiver.getName(), sender.getName());
send(sender, new TradeReply(msg.isAccepted(), msg.getTradeHandler()));
}
}
/**
* Executes the trade by transferring requested properties from the receiver
* to the sender and offered properties from the sender to the receiver.
*/
public void executeTrade(Player sender, Player receiver, Set<PropertyField> requestedProperties, Set<PropertyField> offeredProperties) {
// Transfer requested properties from receiver to sender
for (PropertyField field : requestedProperties) {
receiver.removeProperty(field.getId());
sender.addProperty(field.getId());
field.setOwner(sender); // Update ownership
System.out.printf("Property %s transferred from %s to %s.\n",
field.getName(), receiver.getName(), sender.getName());
}
// Transfer offered properties from sender to receiver
for (PropertyField field : offeredProperties) {
sender.removeProperty(field.getId());
receiver.addProperty(field.getId());
field.setOwner(receiver); // Update ownership
System.out.printf("Property %s transferred from %s to %s.\n",
field.getName(), sender.getName(), receiver.getName());
}
}
/**
* Handles a ViewAssetsRequest message, sending the player a response containing their assets.
*
@@ -268,11 +362,17 @@ public class ServerGameLogic implements ClientInterpreter {
@Override
public void received(ViewAssetsRequest msg, int from) {
Player sender = playerHandler.getPlayerById(from);
Player player = msg.getPlayer();
if (sender != null && player != null) {
LOGGER.log(Level.DEBUG, "Processing ViewAssetsRequest for player {0}", sender.getName());
send(sender, new ViewAssetsResponse(boardManager, player.getProperties(), player.getAccountBalance(), player.getNumJailCard()));
if (sender != null) {
LOGGER.log(Level.DEBUG, "Processing ViewAssetsRequest for player {0}", sender.getName());
send(sender, new ViewAssetsResponse(boardManager));
updateAllPlayers();
}
}
private void updateAllPlayers() {
for (Player player : playerHandler.getPlayers()) {
send(player, new PlayerStatusUpdate(playerHandler));
send(player, new ViewAssetsResponse(boardManager));
}
}
@@ -292,4 +392,72 @@ public class ServerGameLogic implements ClientInterpreter {
public DeckHelper getDeckHelper() {
return deckHelper;
}
@Override
public void received(AlterProperty msg, int from) {
Player sender = playerHandler.getPlayerById(from);
Set<PropertyField> properties = new HashSet<>();
for (Integer integer : msg.getProperties()) {
properties.add( (PropertyField)boardManager.getFieldAtIndex(integer));
}
if (msg.getKeyword().equals("TakeMortage")) {
for (PropertyField field : properties) {
if(field instanceof BuildingProperty) {
if (((BuildingProperty)field).getHouses()!=0) {
break;
}
}
field.setMortgaged(true);
sender.earnMoney(field.getHypo());
}
} else if (msg.getKeyword().equals("RepayMortage")) {
for (PropertyField field : properties) {
if(sender.getAccountBalance() >= field.getHypo()) {
field.setMortgaged(false);
sender.pay(field.getHypo());
}
}
} else if(msg.getKeyword().equals("BuyHouse")) {
for (BuildingProperty field : properties.stream().map(p -> (BuildingProperty) p).collect(Collectors.toList())) {
if (boardManager.canBuild(field) && sender.getAccountBalance() >= field.getHousePrice()) {
field.build();
updateAllPlayers();
sendAll( new BuildInfo(field.getId(), true));
sender.pay(field.getHousePrice());
}
}
} else if(msg.getKeyword().equals("SellHouse")) {
for (BuildingProperty field : properties.stream().map(p -> (BuildingProperty) p).collect(Collectors.toList())) {
if (boardManager.canSell(field)) {
field.sell();
updateAllPlayers();
sendAll( new BuildInfo(field.getId(), false));
sender.earnMoney(field.getHousePrice() / 2);
}
}
}
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();
} else if(msg.getKeyword().equals("hack")) {
// for (BuildingProperty bp : boardManager.getPropertyFields( List.of(1,3)).stream().filter(p -> p instanceof BuildingProperty).map(p -> (BuildingProperty) p).collect(Collectors.toList())) {
// bp.build();
// }
for(PropertyField field : boardManager.getBoard().stream().filter(p -> p instanceof PropertyField).map(p -> (PropertyField) p).collect(Collectors.toList())) {
field.setOwner(playerHandler.getPlayerById(0));
playerHandler.getPlayerById(0).addProperty(field.getId());
}
playerHandler.getPlayerById(0).earnMoney(20000);
}
updateAllPlayers();
}
}

View File

@@ -0,0 +1,36 @@
package pp.monopoly.message.client;
import java.util.Set;
import com.jme3.network.serializing.Serializable;
@Serializable
public class AlterProperty extends ClientMessage{
private String keyword;
private Set<Integer> properties;
private AlterProperty() {}
public AlterProperty(String keyword) {
this.keyword = keyword;
}
@Override
public void accept(ClientInterpreter interpreter, int from) {
interpreter.received(this, from);
}
public String getKeyword() {
return keyword;
}
public void setProperties(Set<Integer> properties) {
this.properties = properties;
}
public Set<Integer> getProperties() {
return properties;
}
}

View File

@@ -1,39 +0,0 @@
package pp.monopoly.message.client;
import com.jme3.network.serializing.Serializable;
/**
* Represents a request from a player to buy a property.
*/
@Serializable
public class BuyPropertyRequest extends ClientMessage{
private int propertyId;
/**
* Default constructor for serialization purposes.
*/
private BuyPropertyRequest() { /* empty */ }
/**
* Constructs a BuyPropertyRequest with the specified property ID.
*
* @param propertyId the ID of the property to buy
*/
public BuyPropertyRequest(int propertyId) {
this.propertyId = propertyId;
}
/**
* Gets the ID of the property to buy.
*
* @return the property ID
*/
public int getPropertyId() {
return propertyId;
}
@Override
public void accept(ClientInterpreter interpreter, int from) {
interpreter.received(this, from);
}
}

View File

@@ -0,0 +1,22 @@
package pp.monopoly.message.client;
import com.jme3.network.serializing.Serializable;
/**
* Represents a request from a player to buy a property.
*/
@Serializable
public class BuyPropertyResponse extends ClientMessage{
/**
* Constructs a BuyPropertyRequest
*
*/
public BuyPropertyResponse() {}
@Override
public void accept(ClientInterpreter interpreter, int from) {
interpreter.received(this, from);
}
}

View File

@@ -17,7 +17,7 @@ public interface ClientInterpreter {
* @param msg the BuyPropertyRequest to be processed
* @param from the connection ID from which the message was received
*/
void received(BuyPropertyRequest msg, int from);
void received(BuyPropertyResponse msg, int from);
/**
* Processes a received EndTurn.
@@ -66,4 +66,20 @@ public interface ClientInterpreter {
* @param from the connection ID from which the message was received
*/
void received(ViewAssetsRequest msg, int from);
/**
* Processes a received AlterProperty.
*
* @param msg the AlterProperty to be processed
* @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

@@ -18,11 +18,11 @@ public class PlayerReady extends ClientMessage {
private PlayerReady() { /* empty */ }
/**
* Constructs a PlayerReady message.
*
* @param isReady true if the player is ready, false otherwise
* @param name the name of the player
* @param color the color of the player (can be null)
* Constructs a PlayerReady message
* @param isReady is the player ready
* @param name the name of the Player
* @param figure the figure corresponding to the Player
* @param startMoney the initial capital of the game
*/
public PlayerReady(boolean isReady, String name, String figure, int startMoney) {
this.isReady = isReady;

View File

@@ -11,7 +11,7 @@ public class RollDice extends ClientMessage{
/**
* Default constructor for serialization purposes.
*/
private RollDice() { /* empty */ }
public RollDice() { /* empty */ }
@Override
public void accept(ClientInterpreter interpreter, int from) {

View File

@@ -9,7 +9,6 @@ import pp.monopoly.model.TradeHandler;
*/
@Serializable
public class TradeOffer extends ClientMessage{
private int receiverId;
private TradeHandler tradehandler;
/**
@@ -20,15 +19,12 @@ public class TradeOffer extends ClientMessage{
/**
* Constructs a TradeOffer with the specified details.
*
* @param receiverId the ID of the player receiving the Request
* @param tradehandler the tradehandler
*/
public TradeOffer(int receiverId, TradeHandler tradehandler) {
this.receiverId = receiverId;
public TradeOffer(TradeHandler tradehandler) {
this.tradehandler = tradehandler;
}
public int getReceiverId() { return receiverId; }
public TradeHandler getTradeHandler() { return tradehandler; }
@Override

View File

@@ -9,7 +9,7 @@ import pp.monopoly.model.TradeHandler;
*/
@Serializable
public class TradeResponse extends ClientMessage{
private int initiatorId;
private boolean status;
private TradeHandler tradeHandler;
/**
@@ -20,15 +20,15 @@ public class TradeResponse extends ClientMessage{
/**
* Constructs a TradeResponse with the specified response details.
*
* @param initiatorId the ID of the player who initiated the trade
* @param accepted true if the offer is accepted, false if declined
* @param status the ID of the player who initiated the trade
* @param tradeHandler the TradeHandler corresponding to the Trade
*/
public TradeResponse(int initiatorId, TradeHandler tradeHandler) {
this.initiatorId = initiatorId;
public TradeResponse(boolean status, TradeHandler tradeHandler) {
this.status = status;
this.tradeHandler = tradeHandler;
}
public int getInitiatorId() { return initiatorId; }
public boolean isAccepted() { return status; }
public TradeHandler getTradeHandler() {
return tradeHandler;
}

View File

@@ -2,23 +2,13 @@ package pp.monopoly.message.client;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
/**
* Represents a request from a player to view their assets.
*/
@Serializable
public class ViewAssetsRequest extends ClientMessage{
private Player player;
/**
* Default constructor for serialization purposes.
*/
private ViewAssetsRequest() { /* empty */ }
public ViewAssetsRequest(Player player) {
this.player = player;
public ViewAssetsRequest() {
}
@Override
@@ -26,8 +16,4 @@ public class ViewAssetsRequest extends ClientMessage{
interpreter.received(this, from);
}
public Player getPlayer() {
return player;
}
}

View File

@@ -0,0 +1,34 @@
package pp.monopoly.message.server;
import com.jme3.network.serializing.Serializable;
@Serializable
public class BuildInfo extends ServerMessage {
private final int id;
private final boolean added;
private BuildInfo() {
this(0, false);
}
public BuildInfo(int id, boolean added) {
this.id = id;
this.added = added;
}
public int getId() {
return id;
}
public boolean isAdded() {
return added;
}
@Override
public void accept(ServerInterpreter interpreter) {
interpreter.received(this);
}
}

View File

@@ -0,0 +1,14 @@
package pp.monopoly.message.server;
import com.jme3.network.serializing.Serializable;
@Serializable
public class BuyPropertyRequest extends ServerMessage{
public BuyPropertyRequest(){}
@Override
public void accept(ServerInterpreter interpreter) {
interpreter.received(this);
}
}

View File

@@ -1,47 +0,0 @@
package pp.monopoly.message.server;
import com.jme3.network.serializing.Serializable;
/**
* Represents the server's response to a player's request to buy a property.
*/
@Serializable
public class BuyPropertyResponse extends ServerMessage{
private boolean successful;
private String propertyName;
private String reason; // Reason for failure, if any
/**
* Default constructor for serialization purposes.
*/
private BuyPropertyResponse() { /* empty */ }
public BuyPropertyResponse(boolean successful, String propertyName, String reason) {
this.successful = successful;
this.propertyName = propertyName;
this.reason = reason;
}
public boolean isSuccessful() {
return successful;
}
public String getPropertyName() {
return propertyName;
}
public String getReason() {
return reason;
}
@Override
public void accept(ServerInterpreter interpreter) {
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
}

View File

@@ -7,19 +7,24 @@ import com.jme3.network.serializing.Serializable;
@Serializable
public class DiceResult extends ServerMessage{
private List<Integer> rollResult;
private final int a;
private final int b;
/**
* Default constructor for serialization purposes.
*/
private DiceResult() { /* empty */ }
private DiceResult() {
a = 1;
b = 1;
}
public DiceResult(List<Integer> rollResult) {
this.rollResult = rollResult;
public DiceResult(int a, int b) {
this.a = a;
this.b = b;
}
public List<Integer> getRollResult() {
return rollResult;
return List.of(a,b);
}
@Override
@@ -27,17 +32,11 @@ public class DiceResult extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
public boolean isDoublets() {
return rollResult.get(0) == rollResult.get(1);
return a == b;
}
public int calcTotal() {
return rollResult.get(0)+rollResult.get(1);
return a+b;
}
}

View File

@@ -20,12 +20,6 @@ public class EventDrawCard extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
public String getCardDescription() {
return cardDescription;
}

View File

@@ -24,10 +24,4 @@ public class GameOver extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
}

View File

@@ -1,27 +1,25 @@
package pp.monopoly.message.server;
import java.util.List;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
import pp.monopoly.game.server.PlayerHandler;
@Serializable
public class GameStart extends ServerMessage{
private List<Player> players;
private PlayerHandler playerHandler;
/**
* Default constructor for serialization purposes.
*/
private GameStart() { /* empty */ }
public GameStart(List<Player> players) {
this.players = players;
public GameStart(PlayerHandler playerHandler) {
this.playerHandler = playerHandler;
}
public List<Player> getPlayers() {
return players;
public PlayerHandler getPlayerHandler() {
return playerHandler;
}
@Override
@@ -29,10 +27,4 @@ public class GameStart extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
}

View File

@@ -25,10 +25,4 @@ public class JailEvent extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
}

View File

@@ -2,35 +2,18 @@ package pp.monopoly.message.server;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class NextPlayerTurn extends ServerMessage{
private Player player;
/**
* Default constructor for serialization purposes.
*/
private NextPlayerTurn() { /* empty */ }
public NextPlayerTurn(Player player) {
this.player = player;
public NextPlayerTurn() {
}
@Override
public void accept(ServerInterpreter interpreter) {
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
public Player getPlayer() {
return player;
}
}

View File

@@ -0,0 +1,44 @@
package pp.monopoly.message.server;
import com.jme3.network.serializing.Serializable;
@Serializable
public class NotificationMessage extends ServerMessage{
private final String keyWord;
private int rentAmount;
private String rentOwner;
private NotificationMessage(){ keyWord = null;}
public NotificationMessage(String keyWord) {
this.keyWord = keyWord;
}
public int getRentAmount() {
return rentAmount;
}
public void setRentAmount(int rentAmount) {
this.rentAmount = rentAmount;
}
public void setRentOwnerId(String rentOwnerId) {
this.rentOwner = rentOwnerId;
}
public String getRentOwner() {
return rentOwner;
}
public String getKeyWord() {
return keyWord;
}
@Override
public void accept(ServerInterpreter interpreter) {
interpreter.received(this);
}
}

View File

@@ -2,36 +2,24 @@ package pp.monopoly.message.server;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.PlayerColor;
import pp.monopoly.game.server.PlayerHandler;
@Serializable
public class PlayerStatusUpdate extends ServerMessage{
private String playerName;
private String status;
private PlayerColor color;
private PlayerHandler playerHandler;
/**
* Default constructor for serialization purposes.
*/
private PlayerStatusUpdate() { /* empty */ }
public PlayerStatusUpdate(String playerName, String status, PlayerColor color) {
this.playerName = playerName;
this.status = status;
this.color = color;
public PlayerStatusUpdate(PlayerHandler playerHandler) {
this.playerHandler = playerHandler;
}
public String getPlayerName() {
return playerName;
}
public String getStatus() {
return status;
}
public PlayerColor getColor() {
return color;
public PlayerHandler getPlayerHandler() {
return playerHandler;
}
@Override
@@ -39,10 +27,4 @@ public class PlayerStatusUpdate extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
}

View File

@@ -13,13 +13,6 @@ package pp.monopoly.message.server;
*/
public interface ServerInterpreter {
/**
* Handles a BuyPropertyResponse message received from the server.
*
* @param msg the BuyPropertyResponse message received
*/
void received(BuyPropertyResponse msg);
/**
* Handles a DiceResult message received from the server.
*
@@ -96,4 +89,25 @@ public interface ServerInterpreter {
* @param msg the NextPlayerTurn message received
*/
void received(NextPlayerTurn msg);
/**
* Handles a BuyPropertyRequest message received from the server.
*
* @param msg the BuyPropertyRequest message received
*/
void received(BuyPropertyRequest msg);
/**
* Handles a NotificationMessage message received from the server.
*
* @param msg the NotificationMessage message received
*/
void received(NotificationMessage msg);
/**
* Handles a BuildInfo message received from the server.
*
* @param msg the BuildInfo message received
*/
void received(BuildInfo msg);
}

View File

@@ -28,12 +28,4 @@ public abstract class ServerMessage extends AbstractMessage {
* @param interpreter the visitor to be used for processing
*/
public abstract void accept(ServerInterpreter interpreter);
/**
* Gets the bundle key of the informational text to be shown at the client.
* This key is used to retrieve the appropriate localized text for display.
*
* @return the bundle key of the informational text
*/
public abstract String getInfoTextKey();
}

View File

@@ -25,10 +25,4 @@ public class TimeOutWarning extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
}

View File

@@ -9,7 +9,7 @@ import pp.monopoly.model.TradeHandler;
*/
@Serializable
public class TradeReply extends ServerMessage{
private int initiatorId;
private boolean status;
private TradeHandler tradeHandler;
/**
@@ -20,15 +20,15 @@ public class TradeReply extends ServerMessage{
/**
* Constructs a TradeResponse with the specified response details.
*
* @param initiatorId the ID of the player who initiated the trade
* @param accepted true if the offer is accepted, false if declined
* @param status the ID of the player who initiated the trade
* @param tradeHandler the TradeHandler corresponding to the trade
*/
public TradeReply(int initiatorId, TradeHandler tradeHandler) {
this.initiatorId = initiatorId;
public TradeReply(boolean status, TradeHandler tradeHandler) {
this.status = status;
this.tradeHandler = tradeHandler;
}
public int getInitiatorId() { return initiatorId; }
public boolean isAccepted() { return status; }
public TradeHandler getTradeHandler() {
return tradeHandler;
}
@@ -38,10 +38,4 @@ public class TradeReply extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
}

View File

@@ -9,7 +9,6 @@ import pp.monopoly.model.TradeHandler;
*/
@Serializable
public class TradeRequest extends ServerMessage{
private int receiverId;
private TradeHandler tradehandler;
@@ -21,15 +20,12 @@ public class TradeRequest extends ServerMessage{
/**
* Constructs a TradeRequest with the specified details.
*
* @param receiverId the ID of the player receiving the Request
* @param tradehandler the tradehandler
*/
public TradeRequest(int receiverId, TradeHandler tradehandler) {
this.receiverId = receiverId;
public TradeRequest(TradeHandler tradehandler) {
this.tradehandler = tradehandler;
}
public int getReceiverId() { return receiverId; }
public TradeHandler getTradeHandler() { return tradehandler; }
@@ -38,10 +34,4 @@ public class TradeRequest extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
}

View File

@@ -1,11 +1,9 @@
package pp.monopoly.message.server;
import java.util.List;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.model.fields.BoardManager;
import pp.monopoly.model.fields.PropertyField;
/**
* Represents a response containing the player's assets.
@@ -13,10 +11,7 @@ import pp.monopoly.model.fields.PropertyField;
@Serializable
public class ViewAssetsResponse extends ServerMessage{
private List<PropertyField> properties;
private BoardManager board;
private int accountBalance;
private int jailCards;
/**
* Default constructor for serialization purposes.
@@ -24,16 +19,12 @@ public class ViewAssetsResponse extends ServerMessage{
private ViewAssetsResponse() { /* empty */ }
/**
*
* Constructs a ViewAssetsResponse with the specified properties and account balance.
*
* @param properties a List of PropertyField objects representing the player's properties
* @param accountBalance the player's current account balance
* @param board the BoardManager representing the current Status
*/
public ViewAssetsResponse(BoardManager board, List<PropertyField> properties, int accountBalance, int jailCards) {
public ViewAssetsResponse(BoardManager board) {
this.board = board;
this.properties = properties;
this.accountBalance = accountBalance;
this.jailCards = jailCards;
}
@Override
@@ -41,25 +32,8 @@ public class ViewAssetsResponse extends ServerMessage{
interpreter.received(this);
}
@Override
public String getInfoTextKey() {
// TODO Auto-generated method stub
throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'");
}
public List<PropertyField> getProperties() {
return properties;
}
public int getAccountBalance() {
return accountBalance;
}
public int getJailCards() {
return jailCards;
}
public BoardManager getboard() {
return board;
}
}

View File

@@ -1,10 +1,3 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.model;
import java.util.ArrayList;
@@ -66,7 +59,34 @@ public class Board {
*/
private void addItem(Item item) {
items.add(item);
notifyListeners((GameEvent) new ItemAddedEvent(item, null));
notifyListeners(new ItemAddedEvent(this, item));
}
/**
* Adds a figure to the map and triggers an item addition event.
*
* @param figure the figure to be added to the map
*/
public void add(Figure figure) {
addItem(figure);
}
/**
* Adds a house to the map and triggers an item addition event.
*
* @param house the house to be added to the map
*/
public void add(House house) {
addItem(house);
}
/**
* Adds a hotel to the map and triggers an item addition event.
*
* @param hotel the hotel to be added to the map
*/
public void add(Hotel hotel) {
addItem(hotel);
}
/**
@@ -76,7 +96,11 @@ public class Board {
*/
public void remove(Item item) {
items.remove(item);
notifyListeners((GameEvent) new ItemRemovedEvent(item, null)); // Falls es ein entsprechendes ItemRemovedEvent gibt
notifyListeners((GameEvent) new ItemRemovedEvent(this, item));
}
public void removePlayers() {
items.removeIf(item -> item instanceof Figure);
}
/**
@@ -96,6 +120,33 @@ public class Board {
private <T extends Item> Stream<T> getItems(Class<T> clazz) {
return items.stream().filter(clazz::isInstance).map(clazz::cast);
}
/**
* Returns a stream of all figures currently on the map.
*
* @return a stream of all figures on the map
*/
public Stream<Figure> getFigures() {
return getItems(Figure.class);
}
/**
* Returns a stream of all houses currently on the map.
*
* @return a stream of all houses on the map
*/
public Stream<House> getHouses() {
return getItems(House.class);
}
/**
* Returns a stream of all hotels currently on the map.
*
* @return a stream of all hotels on the map
*/
public Stream<Hotel> getHotels() {
return getItems(Hotel.class);
}
/**
* Returns an unmodifiable list of all items currently on the map.
@@ -124,28 +175,6 @@ public class Board {
return height;
}
/**
* Validates whether the specified position is within the map boundaries.
*
* @param pos the position to validate
* @return true if the position is within the map, false otherwise
*/
public boolean isValid(IntPosition pos) {
return isValid(pos.getX(), pos.getY());
}
/**
* Checks if the specified coordinates are within the map boundaries.
*
* @param x the x-coordinate to validate
* @param y the y-coordinate to validate
* @return true if the coordinates are valid, false otherwise
*/
public boolean isValid(int x, int y) {
return x >= 0 && x < width &&
y >= 0 && y < height;
}
/**
* Returns a string representation of the map.
*

View File

@@ -1,29 +1,22 @@
package pp.monopoly.model;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Random;
import com.jme3.math.Vector3f;
import com.jme3.network.serializing.Serializable;
import static java.lang.Math.max;
import static java.lang.Math.min;
@Serializable
public class Figure implements Item{
private final String type;
private final int length; // The length of the Figure
private int x; // The x-coordinate of the Figure's position
private int y; // The y-coordinate of the Figure's position
private Vector3f position;
private Rotation rot; // The rotation of the Figure
private final Set<IntPoint> damaged = new HashSet<>(); // The set of positions that have been hit on this ship
/**
* Default constructor for serialization. Initializes a Figure with length 0,
* at position (0, 0), with a default rotation of RIGHT.
* at position (0, 0), with a default rotation of NORTH.
*/
private Figure() {
this(0, 0, 0, Rotation.RIGHT, "cube");
this(null, Rotation.NORTH, "");
}
/**
@@ -31,14 +24,12 @@ public class Figure implements Item{
*
* @param length the length of the Figure
* @param x the x-coordinate of the Figure's initial position
* @param y the y-coordinate of the Figure's initial position
* @param z the z-coordinate of the Figure's initial position
* @param rot the rotation of the Figure
*/
public Figure(int length, int x, int y, Rotation rot, String type) {
this.x = x;
this.y = y;
public Figure(Vector3f position, Rotation rot, String type) {
this.position = calculateFieldPosition(0);
this.rot = rot;
this.length = length;
this.type = type;
}
@@ -47,8 +38,17 @@ public class Figure implements Item{
*
* @return the x-coordinate of the Figure
*/
public int getX() {
return x;
public float getX() {
return position.getX();
}
/**
* Returns the current z-coordinate of the Figure's position.
*
* @return the z-coordinate of the Figure
*/
public float getZ() {
return position.getZ();
}
/**
@@ -56,19 +56,17 @@ public class Figure implements Item{
*
* @return the y-coordinate of the Figure
*/
public int getY() {
return y;
public float getY() {
return position.getY();
}
/**
* Moves the Figure to the specified coordinates.
* Returns the current position of the Figure.
*
* @param x the new x-coordinate of the Figure's position
* @param y the new y-coordinate of the Figure's position
* @return the position of the Figure
*/
public void moveTo(int x, int y) {
this.x = x;
this.y = y;
public Vector3f getPos() {
return position;
}
/**
@@ -76,96 +74,72 @@ public class Figure implements Item{
*
* @param pos the new position of the Figure
*/
public void moveTo(IntPosition pos) {
moveTo(pos.getX(), pos.getY());
public void moveTo(Vector3f pos) {
position = pos;
}
/**
* Moves the Figure to the specified coordinates.
*
* @param x the new x-coordinate of the Figure's position
* @param y the new y-coordinate of the Figure's position
* @param fieldId the position to move to
*/
public void moveTo(int fieldId) {
moveTo(fieldIdToPosition(fieldId));
moveTo(calculateFieldPosition(fieldId));
}
private IntPoint fieldIdToPosition(int fieldId) {
if (fieldId < 0 || fieldId > 39) {
throw new IllegalArgumentException("Invalid fieldId: " + fieldId);
private Vector3f calculateFieldPosition(int fieldID) {
float baseX = 0.0f;
float baseZ = 0.0f;
switch (fieldID) {
case 0: baseX = -9.1f; baseZ = -9.1f; break;
case 1: baseX = -6.5f; baseZ = -9.1f; break;
case 2: baseX = -4.9f; baseZ = -9.1f; break;
case 3: baseX = -3.3f; baseZ = -9.1f; break;
case 4: baseX = -1.6f; baseZ = -9.1f; break;
case 5: baseX = 0.0f; baseZ = -9.1f; break;
case 6: baseX = 1.6f; baseZ = -9.1f; break;
case 7: baseX = 3.3f; baseZ = -9.1f; break;
case 8: baseX = 4.9f; baseZ = -9.1f; break;
case 9: baseX = 6.5f; baseZ = -9.1f; break;
case 10: baseX = 9.1f; baseZ = -9.1f; break;
case 11: baseX = 9.1f; baseZ = -6.5f; break;
case 12: baseX = 9.1f; baseZ = -4.9f; break;
case 13: baseX = 9.1f; baseZ = -3.3f; break;
case 14: baseX = 9.1f; baseZ = -1.6f; break;
case 15: baseX = 9.1f; baseZ = 0.0f; break;
case 16: baseX = 9.1f; baseZ = 1.6f; break;
case 17: baseX = 9.1f; baseZ = 3.3f; break;
case 18: baseX = 9.1f; baseZ = 4.9f; break;
case 19: baseX = 9.1f; baseZ = 6.5f; break;
case 20: baseX = 9.1f; baseZ = 9.1f; break;
case 21: baseX = 6.5f; baseZ = 9.1f; break;
case 22: baseX = 4.9f; baseZ = 9.1f; break;
case 23: baseX = 3.3f; baseZ = 9.1f; break;
case 24: baseX = 1.6f; baseZ = 9.1f; break;
case 25: baseX = 0.0f; baseZ = 9.1f; break;
case 26: baseX = -1.6f; baseZ = 9.1f; break;
case 27: baseX = -3.3f; baseZ = 9.1f; break;
case 28: baseX = -4.9f; baseZ = 9.1f; break;
case 29: baseX = -6.5f; baseZ = 9.1f; break;
case 30: baseX = -9.1f; baseZ = 9.1f; break;
case 31: baseX = -9.1f; baseZ = 6.5f; break;
case 32: baseX = -9.1f; baseZ = 4.9f; break;
case 33: baseX = -9.1f; baseZ = 3.3f; break;
case 34: baseX = -9.1f; baseZ = 1.6f; break;
case 35: baseX = -9.1f; baseZ = 0.0f; break;
case 36: baseX = -9.1f; baseZ = -1.6f; break;
case 37: baseX = -9.1f; baseZ = -3.3f; break;
case 38: baseX = -9.1f; baseZ = -4.9f; break;
case 39: baseX = -9.1f; baseZ = -6.5f; break;
default: throw new IllegalArgumentException("Ungültige Feld-ID: " + fieldID);
}
// Determine which edge and position along the edge
if (fieldId <= 9) {
// Bottom edge: From (-10, -10) to (10, -10)
int x = -10 + fieldId * 2;
return new IntPoint(x, -10);
} else if (fieldId <= 19) {
// Right edge: From (10, -10) to (10, 10)
int y = -10 + (fieldId - 10) * 2;
return new IntPoint(10, y);
} else if (fieldId <= 29) {
// Top edge: From (10, 10) to (-10, 10)
int x = 10 - (fieldId - 20) * 2;
return new IntPoint(x, 10);
} else {
// Left edge: From (-10, 10) to (-10, -10)
int y = 10 - (fieldId - 30) * 2;
return new IntPoint(-10, y);
}
}
float xOffset = new Random().nextFloat();
float zOffset = new Random().nextFloat();
private Rotation fieldIdToRotation(int fieldId) {
if (fieldId >= 0 && fieldId <= 10) return Rotation.DOWN;
else if (fieldId <= 20) return Rotation.LEFT;
else if (fieldId <= 30) return Rotation.UP;
else if (fieldId <= 39) return Rotation.RIGHT;
else throw new IllegalArgumentException();
}
/**
* Returns the length of the Figure.
*
* @return the length of the Figure
*/
public int getLength() {
return length;
}
/**
* Returns the minimum x-coordinate that the Figure occupies based on its current position and rotation.
*
* @return the minimum x-coordinate of the Figure
*/
public int getMinX() {
return x + min(0, (length - 1) * rot.dx());
}
/**
* Returns the maximum x-coordinate that the Figure occupies based on its current position and rotation.
*
* @return the maximum x-coordinate of the Figure
*/
public int getMaxX() {
return x + max(0, (length - 1) * rot.dx());
}
/**
* Returns the minimum y-coordinate that the Figure occupies based on its current position and rotation.
*
* @return the minimum y-coordinate of the Figure
*/
public int getMinY() {
return y + min(0, (length - 1) * rot.dy());
}
/**
* Returns the maximum y-coordinate that the Figure occupies based on its current position and rotation.
*
* @return the maximum y-coordinate of the Figure
*/
public int getMaxY() {
return y + max(0, (length - 1) * rot.dy());
//TODO adjust y pos
return new Vector3f(baseX + xOffset, 1, baseZ + zOffset);
}
/**
@@ -186,99 +160,6 @@ public class Figure implements Item{
this.rot = rot;
}
/**
* Rotates the Figure by 90 degrees clockwise.
*/
public void rotated() {
setRotation(rot.rotate());
}
/**
* Attempts to hit the Figure at the specified position.
* If the position is part of the Figure, the hit is recorded.
*
* @param x the x-coordinate of the position to hit
* @param y the y-coordinate of the position to hit
* @return true if the position is part of the Figure, false otherwise
* @see #contains(int, int)
*/
public boolean hit(int x, int y) {
if (!contains(x, y))
return false;
damaged.add(new IntPoint(x, y));
return true;
}
/**
* Attempts to hit the Figure at the specified position.
* If the position is part of the Figure, the hit is recorded.
* This is a convenience method for {@linkplain #hit(int, int)}.
*
* @param position the position to hit
* @return true if the position is part of the Figure, false otherwise
*/
public boolean hit(IntPosition position) {
return hit(position.getX(), position.getY());
}
/**
* Returns the positions of this Figure that have been hit.
*
* @return a set of positions that have been hit
* @see #hit(int, int)
*/
public Set<IntPoint> getDamaged() {
return Collections.unmodifiableSet(damaged);
}
/**
* Checks whether the specified position is covered by the Figure. This method does
* not record a hit, only checks coverage.
* This is a convenience method for {@linkplain #contains(int, int)}.
*
* @param pos the position to check
* @return true if the position is covered by the Figure, false otherwise
*/
public boolean contains(IntPosition pos) {
return contains(pos.getX(), pos.getY());
}
/**
* Checks whether the specified position is covered by the Figure. This method does
* not record a hit, only checks coverage.
*
* @param x the x-coordinate of the position to check
* @param y the y-coordinate of the position to check
* @return true if the position is covered by the Figure, false otherwise
*/
public boolean contains(int x, int y) {
return getMinX() <= x && x <= getMaxX() &&
getMinY() <= y && y <= getMaxY();
}
/**
* Determines if the Figure has been completely destroyed. A Figure is considered
* destroyed if all of its positions have been hit.
*
* @return true if the Figure is destroyed, false otherwise
* @see #hit(int, int)
*/
public boolean isDestroyed() {
return damaged.size() == length;
}
/**
* Checks whether this Figure collides with another Figure. Two Figures collide
* if any of their occupied positions overlap.
*
* @param other the other Figure to check collision with
* @return true if the Figures collide, false otherwise
*/
public boolean collidesWith(Figure other) {
return other.getMaxX() >= getMinX() && getMaxX() >= other.getMinX() &&
other.getMaxY() >= getMinY() && getMaxY() >= other.getMinY();
}
/**
* Returns a string representation of the Figure, including its length, position,
* and rotation.
@@ -287,7 +168,7 @@ public class Figure implements Item{
*/
@Override
public String toString() {
return "Ship{length=" + length + ", x=" + x + ", y=" + y + ", rot=" + rot + '}'; //NON-NLS
return "Figur{ x=" + position.getX() + ", z=" + position.getZ() + ", rot=" + rot + '}'; //NON-NLS
}
/**
@@ -313,5 +194,9 @@ public class Figure implements Item{
public void accept(VoidVisitor visitor) {
visitor.visit(this);
}
public String getType() {
return type;
}
}

View File

@@ -0,0 +1,99 @@
package pp.monopoly.model;
import com.jme3.math.Vector3f;
public class Hotel implements Item{
/**
* The ID of the field the hotel is on.
*/
private final int fieldID;
/**
* Creates a new hotel with fieldID 0.
*/
private Hotel() {
this.fieldID = 0;
}
/**
* Creates a new hotel on the given field.
*
* @param fieldID the ID of the field the hotel is on
*/
public Hotel(int fieldID) {
this.fieldID = fieldID;
}
@Override
public <T> T accept(Visitor<T> visitor) {
return visitor.visit(this);
}
@Override
public void accept(VoidVisitor visitor) {
visitor.visit(this);
}
/**
* Returns the position of the building on the field.
*
* @return the position of the building on the field
*/
public Vector3f getPos() {
float baseX = 0.0f;
float baseZ = 0.0f;
switch (fieldID) {
case 0: baseX = -8.4f; baseZ = -7.7f; break;
case 1: baseX = -6.3f; baseZ = -7.7f; break;
case 2: baseX = -4.7f; baseZ = -7.7f; break;
case 3: baseX = -3.1f; baseZ = -7.7f; break;
case 4: baseX = -1.4f; baseZ = -7.7f; break;
case 5: baseX = 0.2f; baseZ = -7.7f; break;
case 6: baseX = 1.8f; baseZ = -7.7f; break;
case 7: baseX = 3.5f; baseZ = -7.7f; break;
case 8: baseX = 5.1f; baseZ = -7.7f; break;
case 9: baseX = 6.7f; baseZ = -7.7f; break;
case 10: baseX = 8.2f; baseZ = -7.7f; break;
case 11: baseX = 8.2f; baseZ = -6.5f; break; //passt
case 12: baseX = 8.2f; baseZ = -4.9f; break; //passt
case 13: baseX = 8.2f; baseZ = -3.3f; break; //passt
case 14: baseX = 8.2f; baseZ = -1.6f; break; //passt
case 15: baseX = 8.2f; baseZ = 0.0f; break; //passt
case 16: baseX = 8.2f; baseZ = 1.6f; break; //passt
case 17: baseX = 8.2f; baseZ = 3.3f; break; //passt
case 18: baseX = 8.2f; baseZ = 4.9f; break; //passt
case 19: baseX = 8.2f; baseZ = 6.5f; break; //passt
case 20: baseX = 8.2f; baseZ = 7.7f; break;
case 21: baseX = 6.5f; baseZ = 7.7f; break;
case 22: baseX = 4.9f; baseZ = 7.7f; break;
case 23: baseX = 3.3f; baseZ = 7.7f; break;
case 24: baseX = 1.6f; baseZ = 7.7f; break;
case 25: baseX = 0.0f; baseZ = 7.7f; break;
case 26: baseX = -1.6f; baseZ = 7.7f; break;
case 27: baseX = -3.3f; baseZ = 7.7f; break;
case 28: baseX = -4.9f; baseZ = 7.7f; break;
case 29: baseX = -6.5f; baseZ = 7.7f; break;
case 30: baseX = -7.2f; baseZ = 7.7f; break;
case 31: baseX = -7.2f; baseZ = 6.5f; break;
case 32: baseX = -7.2f; baseZ = 4.9f; break;
case 33: baseX = -7.2f; baseZ = 3.3f; break;
case 34: baseX = -7.2f; baseZ = 1.6f; break;
case 35: baseX = -7.2f; baseZ = 0.0f; break;
case 36: baseX = -7.2f; baseZ = -1.6f; break;
case 37: baseX = -7.2f; baseZ = -3.3f; break;
case 38: baseX = -7.2f; baseZ = -4.9f; break;
case 39: baseX = -7.2f; baseZ = -6.5f; break;
default: throw new IllegalArgumentException("Ungültige Feld-ID: " + fieldID);
}
return new Vector3f(baseX, 0, baseZ);
}
@Override
public Rotation getRot() {
// TODO
return Rotation.NORTH;
}
}

View File

@@ -0,0 +1,130 @@
package pp.monopoly.model;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.network.serializing.Serializable;
/**
* A class representing a house in the Monopoly game.
*/
@Serializable
public class House implements Item{
/**
* The stage of the house.
*/
private final int stage;
private final int fieldID;
/**
* Creates a new house with stage 0.
*/
private House() {
this.stage = 0;
this.fieldID = 0;
}
/**
* Creates a new house with the given stage.
*
* @param stage the stage of the house
*/
public House(int stage, int fieldID) {
this.stage = stage;
this.fieldID = fieldID;
}
/**
* Returns the stage of the house.
*
* @return the stage of the house
*/
public int getStage() {
return stage;
}
@Override
public <T> T accept(Visitor<T> visitor) {
return visitor.visit(this);
}
@Override
public void accept(VoidVisitor visitor) {
visitor.visit(this);
}
/**
* Returns the position of the building on the field.
*
* @return the position of the building on the field
*/
public Vector3f getPos() {
float baseX = 0.0f;
float baseZ = 0.0f;
switch (fieldID) {
case 0: baseX = -8.4f; baseZ = -7.7f; break;
case 1: baseX = -6.3f; baseZ = -7.7f; break;
case 2: baseX = -4.7f; baseZ = -7.7f; break;
case 3: baseX = -3.1f; baseZ = -7.7f; break;
case 4: baseX = -1.4f; baseZ = -7.7f; break;
case 5: baseX = 0.2f; baseZ = -7.7f; break;
case 6: baseX = 1.8f; baseZ = -7.7f; break;
case 7: baseX = 3.5f; baseZ = -7.7f; break;
case 8: baseX = 5.1f; baseZ = -7.7f; break;
case 9: baseX = 6.7f; baseZ = -7.7f; break;
case 10: baseX = 8.2f; baseZ = -7.7f; break;
case 11: baseX = 8.2f; baseZ = -6.5f; break; //passt
case 12: baseX = 8.2f; baseZ = -4.9f; break; //passt
case 13: baseX = 8.2f; baseZ = -3.3f; break; //passt
case 14: baseX = 8.2f; baseZ = -1.6f; break; //passt
case 15: baseX = 8.2f; baseZ = 0.0f; break; //passt
case 16: baseX = 8.2f; baseZ = 1.6f; break; //passt
case 17: baseX = 8.2f; baseZ = 3.3f; break; //passt
case 18: baseX = 8.2f; baseZ = 4.9f; break; //passt
case 19: baseX = 8.2f; baseZ = 6.5f; break; //passt
case 20: baseX = 8.2f; baseZ = 7.7f; break;
case 21: baseX = 6.5f; baseZ = 7.7f; break;
case 22: baseX = 4.9f; baseZ = 7.7f; break;
case 23: baseX = 3.3f; baseZ = 7.7f; break;
case 24: baseX = 1.6f; baseZ = 7.7f; break;
case 25: baseX = 0.0f; baseZ = 7.7f; break;
case 26: baseX = -1.6f; baseZ = 7.7f; break;
case 27: baseX = -3.3f; baseZ = 7.7f; break;
case 28: baseX = -4.9f; baseZ = 7.7f; break;
case 29: baseX = -6.5f; baseZ = 7.7f; break;
case 30: baseX = -7.2f; baseZ = 7.7f; break;
case 31: baseX = -7.2f; baseZ = 6.5f; break;
case 32: baseX = -7.2f; baseZ = 4.9f; break;
case 33: baseX = -7.2f; baseZ = 3.3f; break;
case 34: baseX = -7.2f; baseZ = 1.6f; break;
case 35: baseX = -7.2f; baseZ = 0.0f; break;
case 36: baseX = -7.2f; baseZ = -1.6f; break;
case 37: baseX = -7.2f; baseZ = -3.3f; break;
case 38: baseX = -7.2f; baseZ = -4.9f; break;
case 39: baseX = -7.2f; baseZ = -6.5f; break;
default: throw new IllegalArgumentException("Ungültige Feld-ID: " + fieldID);
}
return new Vector3f(baseX, 0, baseZ);
}
@Override
public Rotation getRot() {
//TODO
return Rotation.NORTH;
}
public Quaternion getAlignment() {
Quaternion rotation = new Quaternion();
if (fieldID >= 1 && fieldID <= 10) {
rotation.fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y);
} else if (fieldID >= 21 && fieldID <= 30) {
rotation.fromAngleAxis(3 * FastMath.HALF_PI, Vector3f.UNIT_Y);
} else if (fieldID >= 31 && fieldID <= 39) {
rotation.fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y);
}
return rotation;
}
}

View File

@@ -7,6 +7,8 @@
package pp.monopoly.model;
import com.jme3.math.Vector3f;
/**
* An interface representing any item on a board
* It extends the IntPosition interface to provide position information.
@@ -28,4 +30,19 @@ public interface Item {
* @param visitor the visitor performing operations on the item
*/
void accept(VoidVisitor visitor);
/**
* Returns the rotation of the item on the board.
*
* @return the rotation of the item on the board
*/
Rotation getRot();
/**
* Returns the position of the item on the board.
*
* @return the position of the item on the board
*/
Vector3f getPos();
}

View File

@@ -1,67 +1,108 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.model;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import java.io.Serializable;
import static pp.util.FloatMath.PI;
import static pp.util.FloatMath.TWO_PI;
/**
* Represents the rotation of a Item and provides functionality related to rotation.
* Represents the rotation of an item in cardinal directions and provides corresponding 3D rotation quaternions.
*/
public enum Rotation implements Serializable {
/**
* Represents the item facing upwards.
* Represents the item facing north (positive Z-axis).
*/
UP,
NORTH(0, new Quaternion().fromAngleAxis(0, Vector3f.UNIT_Y)),
/**
* Represents the item facing rightwards.
* Represents the item facing south (negative Z-axis).
*/
RIGHT,
SOUTH(PI, new Quaternion().fromAngleAxis(PI, Vector3f.UNIT_Y)),
/**
* Represents the item facing downwards.
* Represents the item facing west (negative X-axis).
*/
DOWN,
WEST(3 * PI / 2, new Quaternion().fromAngleAxis(3 * PI / 2, Vector3f.UNIT_Y)),
/**
* Represents the item facing leftwards.
* Represents the item facing east (positive X-axis).
*/
LEFT;
EAST(PI / 2, new Quaternion().fromAngleAxis(PI / 2, Vector3f.UNIT_Y));
/**
* Gets the change in x-coordinate corresponding to this rotation.
*
* @return the change in x-coordinate
*/
public int dx() {
return switch (this) {
case UP, DOWN -> 0;
case RIGHT -> 1;
case LEFT -> -1;
};
private final float radians;
private final Quaternion rotation;
Rotation(float radians, Quaternion rotation) {
this.radians = radians;
this.rotation = rotation;
}
/**
* Gets the change in y-coordinate corresponding to this rotation.
* Gets the 3D rotation quaternion corresponding to this rotation.
*
* @return the change in y-coordinate
* @return the rotation as a {@link Quaternion}.
*/
public int dy() {
return switch (this) {
case UP -> 1;
case LEFT, RIGHT -> 0;
case DOWN -> -1;
};
public Quaternion toQuaternion() {
return rotation;
}
/**
* Rotates the orientation clockwise and returns the next rotation.
* Gets the rotation in radians.
*
* @return the next rotation after rotating clockwise
* @return the rotation in radians.
*/
public Rotation rotate() {
public float radians() {
return radians;
}
/**
* Rotates clockwise (90 degrees or PI/2 radians) and returns the next rotation.
*
* @return the next rotation after rotating clockwise.
*/
public Rotation rotateClockwise() {
return values()[(ordinal() + 1) % values().length];
}
/**
* Rotates counterclockwise (270 degrees or 3 * PI / 2 radians) and returns the next rotation.
*
* @return the next rotation after rotating counterclockwise.
*/
public Rotation rotateCounterclockwise() {
return values()[(ordinal() - 1 + values().length) % values().length];
}
/**
* Rotates by the specified radians and returns the corresponding rotation.
* Valid radians are multiples of PI/2.
*
* @param radians the radians to rotate. Must be a multiple of PI/2.
* @return the resulting rotation.
* @throws IllegalArgumentException if the radians are not a multiple of PI/2.
*/
public Rotation rotateByRadians(float radians) {
if ((radians % (PI / 2)) != 0) {
throw new IllegalArgumentException("Radians must be a multiple of PI/2.");
}
int steps = (int) ((radians / (PI / 2)) % values().length);
if (steps < 0) {
steps += values().length; // Normalize negative steps.
}
return values()[(ordinal() + steps) % values().length];
}
/**
* Finds the rotation closest to the specified radians value.
*
* @param radians the radians value.
* @return the nearest rotation.
*/
public static Rotation closestToRadians(float radians) {
float normalized = (radians % TWO_PI + TWO_PI) % TWO_PI; // Normalize to 0-TWO_PI.
if (normalized < PI / 4 || normalized >= 7 * PI / 4) return NORTH;
if (normalized < 3 * PI / 4) return EAST;
if (normalized < 5 * PI / 4) return SOUTH;
return WEST;
}
}

View File

@@ -3,23 +3,30 @@ package pp.monopoly.model;
import pp.monopoly.game.server.Player;
import pp.monopoly.model.fields.PropertyField;
import java.util.List;
import java.util.HashSet;
import java.util.Set;
import com.jme3.network.serializing.Serializable;
/**
* Handles a single trade between two players.
* Encapsulates trade details, validation, acceptance, and rejection.
*/
@Serializable
public class TradeHandler {
private final Player sender;
private final Player receiver;
private final int offeredAmount;
private final List<PropertyField> offeredProperties;
private final int offeredJailCards;
private final int requestedAmount;
private final List<PropertyField> requestedProperties;
private final int requestedJailCards;
private Boolean status = null;
private Player receiver;
private int offeredAmount;
private Set<PropertyField> offeredProperties = new HashSet<>();
private int offeredJailCards;
private int requestedAmount;
private Set<PropertyField> requestedProperties = new HashSet<>();
private int requestedJailCards;
private TradeHandler() {
sender = null;
}
/**
* Constructs a TradeHandler for a single trade instance.
@@ -33,8 +40,8 @@ public class TradeHandler {
* @param requestedProperties the properties requested from the receiver
* @param requestedJailCards the jail cards requested from the receiver
*/
public TradeHandler(Player sender, Player receiver, int offeredAmount, List<PropertyField> offeredProperties,
int offeredJailCards, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) {
public TradeHandler(Player sender, Player receiver, int offeredAmount, Set<PropertyField> offeredProperties,
int offeredJailCards, int requestedAmount, Set<PropertyField> requestedProperties, int requestedJailCards) {
this.sender = sender;
this.receiver = receiver;
this.offeredAmount = offeredAmount;
@@ -45,6 +52,15 @@ public class TradeHandler {
this.requestedJailCards = requestedJailCards;
}
/**
* Constructs a TradeHandler for a single trade instance.
*
* @param sender the Player initiating the trade
*/
public TradeHandler(Player sender) {
this.sender = sender;
}
public int getOfferedAmount() {
return offeredAmount;
}
@@ -53,7 +69,7 @@ public class TradeHandler {
return offeredJailCards;
}
public List<PropertyField> getOfferedProperties() {
public Set<PropertyField> getOfferedProperties() {
return offeredProperties;
}
@@ -69,7 +85,7 @@ public class TradeHandler {
return requestedJailCards;
}
public List<PropertyField> getRequestedProperties() {
public Set<PropertyField> getRequestedProperties() {
return requestedProperties;
}
@@ -77,136 +93,31 @@ public class TradeHandler {
return sender;
}
public Boolean getStatus() {
return status;
public void setOfferedAmount(int offeredAmount) {
this.offeredAmount = offeredAmount;
}
/**
* Initiates the trade and validates its terms.
*
* @return true if the trade is valid and can proceed, false otherwise
*/
public boolean initiateTrade() {
if (!validateTrade()) {
System.out.println("Trade offer is invalid.");
return false;
}
System.out.println("Trade initiated by " + sender.getName() + " to " + receiver.getName());
return true;
public void setOfferedJailCards(int offeredJailCards) {
this.offeredJailCards = offeredJailCards;
}
/**
* Completes the trade by transferring money, properties, and jail cards.
*/
public void acceptTrade() {
// Transfer money
sender.earnMoney(-offeredAmount);
receiver.earnMoney(offeredAmount);
receiver.earnMoney(-requestedAmount);
sender.earnMoney(requestedAmount);
// Transfer properties
if (offeredProperties != null) {
for (PropertyField property : offeredProperties) {
transferProperty(sender, receiver, property);
}
}
if (requestedProperties != null) {
for (PropertyField property : requestedProperties) {
transferProperty(receiver, sender, property);
}
}
// Transfer jail cards
transferJailCards(sender, receiver, offeredJailCards);
transferJailCards(receiver, sender, requestedJailCards);
System.out.println("Trade completed between " + sender.getName() + " and " + receiver.getName());
public void setOfferedProperties(Set<PropertyField> offeredProperties) {
this.offeredProperties = offeredProperties;
}
/**
* Rejects the trade.
*/
public void rejectTrade() {
System.out.println(receiver.getName() + " rejected the trade.");
public void setReceiver(Player receiver) {
this.receiver = receiver;
}
public void setRequestedAmount(int requestedAmount) {
this.requestedAmount = requestedAmount;
}
/**
* Validates the trade offer by checking ownership, balances, and jail cards.
*
* @return true if the trade is valid, false otherwise
*/
private boolean validateTrade() {
// Validate sender's ability to offer money
if (sender.getAccountBalance() < offeredAmount) {
System.out.println("Sender does not have enough money to offer.");
return false;
}
// Validate receiver's ability to fulfill the requested amount
if (receiver.getAccountBalance() < requestedAmount) {
System.out.println("Receiver does not have enough money to fulfill the request.");
return false;
}
// Validate property ownership
if (offeredProperties != null) {
for (PropertyField property : offeredProperties) {
if (!sender.getProperties().contains(property)) {
System.out.println("Sender does not own property: " + property.getName());
return false;
}
}
}
if (requestedProperties != null) {
for (PropertyField property : requestedProperties) {
if (!receiver.getProperties().contains(property)) {
System.out.println("Receiver does not own property: " + property.getName());
return false;
}
}
}
// Validate jail cards
if (sender.getNumJailCard() < offeredJailCards) {
System.out.println("Sender does not have enough jail cards to offer.");
return false;
}
if (receiver.getNumJailCard() < requestedJailCards) {
System.out.println("Receiver does not have enough jail cards to fulfill the request.");
return false;
}
return true;
public void setRequestedJailCards(int requestedJailCards) {
this.requestedJailCards = requestedJailCards;
}
/**
* Transfers a property between players.
*
* @param from the Player transferring the property
* @param to the Player receiving the property
* @param property the PropertyField being transferred
*/
private void transferProperty(Player from, Player to, PropertyField property) {
from.sellProperty(property);
to.buyProperty(property);
property.setOwner(to);
System.out.println("Property " + property.getName() + " transferred from " + from.getName() + " to " + to.getName());
}
/**
* Transfers jail cards between players.
*
* @param from the Player transferring jail cards
* @param to the Player receiving jail cards
* @param numCards the number of jail cards to transfer
*/
private void transferJailCards(Player from, Player to, int numCards) {
for (int i = 0; i < numCards; i++) {
from.removeJailCard();
to.addJailCard();
}
System.out.println(numCards + " jail card(s) transferred from " + from.getName() + " to " + to.getName());
public void setRequestedProperties(Set<PropertyField> requestedProperties) {
this.requestedProperties = requestedProperties;
}
}

View File

@@ -1,10 +1,3 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.model;
/**
@@ -22,4 +15,20 @@ public interface Visitor<T> {
*/
T visit(Figure figure);
/**
* Visits a Hotel element.
*
* @param hotel the Hotel element to visit
* @return the result of visiting the hotel element
*/
T visit(Hotel hotel);
/**
* Visits a house element.
*
* @param house the House element to visit
* @return the result of visiting the house element
*/
T visit(House figure);
}

View File

@@ -1,10 +1,3 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.model;
/**
@@ -19,4 +12,18 @@ public interface VoidVisitor {
*/
void visit(Figure figure);
/**
* Visits a Hotel element.
*
* @param hotel the Hotel element to visit
*/
void visit(Hotel hotel);
/**
* Visits a House element.
*
* @param house the House element to visit
*/
void visit(House house);
}

View File

@@ -19,7 +19,7 @@ public class Card {
return description;
} // TODO wird gerade in der EventCard zur erstellung des Popup genutzt
String getKeyword() {
public String getKeyword() {
return keyword;
}
}

View File

@@ -16,38 +16,40 @@ public class DeckHelper{
public DeckHelper() {
cards = new LinkedList<Card>();
cards.add(new Card("Du wurdest mit einem Dienst KFZ geblitzt. Zahle: 800", "dienst-kfz-blitzer"));
cards.add(new Card("Du wurdest mit einem Dienst KFZ geblitzt. Zahle 800 EUR", "dienst-kfz-blitzer"));
cards.add(new Card("Die erste Spoparty steht bevor. Ziehe vor zum 23er.", "spoparty"));
cards.add(new Card("Du kommst aus dem Gulak frei.", "gulak-frei-1"));
cards.add(new Card("Du kommst aus dem Gulak frei.", "gulak-frei-2"));
cards.add(new Card("Du kommst aus dem Gulak frei!", "gulak-frei-1"));
cards.add(new Card("Du kommst aus dem Gulak frei!", "gulak-frei-2"));
cards.add(new Card("Du hast den Dienstführerschein bestanden. Ziehe vor bis Teststrecke.", "dienstfuehrerschein"));
cards.add(new Card("Malkmus läd zum Pubquiz ein. Rücke vor bis zum 20er.", "pubquiz"));
cards.add(new Card("Du warst ohne Namensschild in der Truppenküche. Rücke vor zum 10er. Gehe nicht über Monatsgehalt. Ziehe keine 2000x€ ein.", "namensschild-truppenkueche"));
cards.add(new Card("Du hast heute die Spendierhosen an und gibst eine Runde in der Unibar. Zahle jedem Spieler: 400", "spendierhosen-unibar"));
cards.add(new Card("Deine IGF-Daten sind verschwunden. Statte Padubrin einen Besuch ab und gib ihm einen Jägermeister aus. Zahle 250 EUR", "IGF-Padubrin"));
cards.add(new Card("Du hast heute die Spendierhosen an und gibst eine Runde in der Unibar. Zahle jedem Spieler 400 EUR", "spendierhosen-unibar"));
cards.add(new Card("Du warst in der Prüfungsphase krank. Gehe 3 Felder zurück.", "pruefungsphase-krank"));
cards.add(new Card("Ziehe vor bis zum nächsten Monatsgehalt.", "naechstes-monatsgehalt"));
cards.add(new Card("Du hast ein Antreten verschlafen. Zahle: 500", "antreten-verschlafen-1"));
cards.add(new Card("Du hast den Maibock organisiert. Du erhältst: 3000", "maibock-organisiert"));
cards.add(new Card("Der Spieß macht eine unangekündigte Inventur. Zahle für jedes Haus: 400 und jedes Hotel: 2800", "inventur-haeuser-hotels"));
cards.add(new Card("Es gab keine Mozzarella Bällchen mehr für Thoma. Alle Spieler ziehen vor auf Gym.", "dienstsport-gym"));
cards.add(new Card("Auf deiner Stube wurde Schimmel gefunden. Gehe ins Gulak. Begib Dich direkt dorthin. Gehe nicht über Monatsgehalt. Ziehe nicht ein.", "schimmel-gulak"));
cards.add(new Card("Deine Stube ist nach einer Partynacht nicht mehr bewohnbar. Du ziehst ins Gulak. Begib Dich direkt dorthin. Gehe nicht über Monatsgehalt. Ziehe nicht ein.", "partynacht-gulak"));
cards.add(new Card("Du hast ein Antreten verschlafen. Zahle 500 EUR", "antreten-verschlafen-1"));
cards.add(new Card("Du hast den Maibock organisiert. Du erhältst 3000 EUR", "maibock-organisiert"));
cards.add(new Card("Der Spieß macht eine unangekündigte Inventur. Zahle für jedes Haus 400 EUR und für jedes Hotel 2800 EUR", "inventur-haeuser-hotels"));
cards.add(new Card("Es gab keine Mozzarella-Bällchen mehr für Thoma. Rücke vor bis aufs Gym.", "dienstsport-gym"));
cards.add(new Card("Auf deiner Stube wurde Schimmel gefunden. Gehe ins Gulak. Begib dich direkt dorthin. Gehe nicht über Monatsgehalt. Ziehe nicht 2000 EUR ein.", "schimmel-gulak"));
cards.add(new Card("Deine Stube ist nach einer Partynacht nicht mehr bewohnbar. Du ziehst ins Gulak. Begib dich direkt dorthin. Gehe nicht über Monatsgehalt. Ziehe nicht 2000 EUR ein.", "partynacht-gulak"));
cards.add(new Card("Das Jahresabschlussantreten steht an. Ziehe vor bis Schwimmhalle.", "jahresabschlussantreten"));
cards.add(new Card("Du wurdest beim Verkaufen von Versicherungen erwischt. Zahle: 4000", "verkaufen-versicherungen"));
cards.add(new Card("Du wurdest beim Verkaufen von Versicherungen erwischt. Zahle 4000 EUR", "verkaufen-versicherungen"));
cards.add(new Card("Du musstest einen Rückstuferantrag stellen. Setze eine Runde aus.", "rueckstuferantrag"));
cards.add(new Card("Auf einer Hausfeier bist du betrunken auf der Treppe gestürzt und dabei auf einen Kameraden gefallen. Zahle: 800 und gehe zurück zu SanZ.", "hausfeier-sturz"));
cards.add(new Card("Beförderung. Beim nächsten Monatsgehalt ziehst du ein: 3000€", "befoerderung"));
cards.add(new Card("Du entscheidest dich für eine Dienstreise nach Lourd. Zahle: 1000 und setze eine Runde aus.", "dienstreise-lourd"));
cards.add(new Card("Auf einer Hausfeier bist du betrunken auf der Treppe gestürzt und dabei auf einen Kameraden gefallen. Zahle 800 EUR und gehe zurück zum SanZ.", "hausfeier-sturz"));
cards.add(new Card("Beförderung. Beim nächsten Monatsgehalt ziehst du 3000 EUR ein", "befoerderung"));
cards.add(new Card("Du entscheidest dich für eine Dienstreise nach Lourd. Zahle 1000 EUR und setze eine Runde aus.", "dienstreise-lourd"));
cards.add(new Card("Du warst fleißig Blutspenden und erhältst einen Tag Sonderurlaub. Du bist nochmal an der Reihe.", "blutspenden-sonderurlaub"));
cards.add(new Card("Dir wurde auf dem Oktoberfest dein Geldbeutel geklaut. Gebe 10% deines Vermögens ab.", "geldbeutel-oktoberfest"));
cards.add(new Card("Du wirst von deinem Chef für vorbildliches Verhalten gelobt. Du erhältst: 4000", "lob-chef"));
cards.add(new Card("Deine Bekanntschaft von letzter Nacht war eine Spo. Lasse dich testen und zahle: 200", "spo-testen"));
cards.add(new Card("Du wurdest von Kranz geexmattet. Gehe zurück zu Prüfungsamt.", "kranz-exmatrikulation"));
cards.add(new Card("Du wirst von deinem Chef für vorbildliches Verhalten gelobt. Du erhältst 4000 EUR", "lob-chef"));
cards.add(new Card("Deine Bekanntschaft von letzter Nacht war eine Spo. Lasse dich testen und zahle 200 EUR", "spo-testen"));
cards.add(new Card("Du wurdest von Kranz geexmattet. Gehe zurück zum Prüfungsamt.", "kranz-exmatrikulation"));
cards.add(new Card("Die letzte Party ist ein wenig eskaliert. Setze eine Runde aus.", "party-eskaliert"));
cards.add(new Card("Du wurdest zur VP gewählt und schmeißt eine Einstandsparty. Zahle: 800", "vp-einstandsparty"));
cards.add(new Card("Du hast eine Party veranstaltet und dick Gewinn gemacht. Ziehe ein: 1500€", "party-gewinn"));
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 ein: 1000€", "jodel-eispenis"));
cards.add(new Card("Dein Jodel eines Schneepenis mit Unterhodenbeleuchtung geht viral. Ziehe 1000 EUR ein", "jodel-eispenis"));
shuffle();
}
public void visit(Card card, Player player) {
@@ -73,8 +75,8 @@ public class DeckHelper{
pubquiz(player);
break;
case "namensschild-truppenkueche":
namensschildTruppenkueche(player);
case "IGF-Padubrin":
igfPadubrin(player);
break;
case "spendierhosen-unibar":
@@ -187,7 +189,7 @@ public class DeckHelper{
}
private void spoparty(Player player) {
player.movePos(14);
player.setPositionWithMoney(14);
}
private void gulakFrei(Player player) {
@@ -195,15 +197,16 @@ public class DeckHelper{
}
private void dienstfuehrerschein(Player player) {
player.movePos(20);
player.setPositionWithMoney(20);
}
private void pubquiz(Player player) {
player.movePos(39);
player.setPositionWithMoney(39);
}
private void namensschildTruppenkueche(Player player) {
//TODO
private void igfPadubrin(Player player) {
player.setPositionWithMoney(24);
player.pay(250);
}
private void spendierhosenUnibar(Player player) {
@@ -214,11 +217,11 @@ public class DeckHelper{
}
private void pruefungsphaseKrank(Player player) {
player.movePos(player.getFieldID() - 3);
player.setPosition(player.getFieldID() - 3);
}
private void naechstesMonatsgehalt(Player player) {
player.movePos(0);
player.setPositionWithMoney(0);
}
private void antretenVerschlafen(Player player) {
@@ -234,21 +237,19 @@ public class DeckHelper{
}
private void dienstsportGym(Player player) {
for (Player p : player.getHandler().getPlayers()) {
p.movePos(1);
}
player.setPositionWithMoney(1);
}
private void schimmelGulak(Player player) {
player.movePos(10);
player.moveToJail();
}
private void partynachtGulak(Player player) {
player.movePos(10);
player.moveToJail();
}
private void jahresabschlussantreten(Player player) {
player.movePos(17);
player.setPositionWithMoney(16);
}
private void verkaufenVersicherungen(Player player) {
@@ -261,7 +262,7 @@ public class DeckHelper{
private void hausfeierSturz(Player player) {
player.pay(800);
player.movePos(32);
player.setPosition(32);
}
private void befoerderung(Player player) {
@@ -290,7 +291,7 @@ public class DeckHelper{
}
private void kranzExmatrikulation(Player player) {
player.movePos(5);
player.setPosition(29);
}
private void partyEskaliert(Player player) {

View File

@@ -3,10 +3,14 @@ package pp.monopoly.model.fields;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Collectors;
import com.jme3.network.serializing.Serializable;
/**
* Simple Manager class responsible for managing the GameBoard of Monopoly
*/
@Serializable
public class BoardManager {
private List<Field> board;
@@ -57,7 +61,7 @@ public class BoardManager {
fields.add(new BuildingProperty("Prüfungsamt", 29, 2800, 240, 1500, FieldColor.YELLOW));
fields.add(new WacheField());
fields.add(new BuildingProperty("Feuerwehr", 31, 3000, 260, 2000, FieldColor.GREEN));
fields.add(new BuildingProperty("SanZ", 32, 300, 260, 2000, FieldColor.GREEN));
fields.add(new BuildingProperty("SanZ", 32, 3000, 260, 2000, FieldColor.GREEN));
fields.add(new EventField("Maibock", 33));
fields.add(new BuildingProperty("Rechenzentrum", 34, 3200, 280, 2000, FieldColor.GREEN));
fields.add(new GateField("Osttor", 35));
@@ -69,6 +73,15 @@ public class BoardManager {
return fields;
}
public Field getFieldByName(String name) {
for (Field field : board) {
if (field.getName().equals(name)) {
return field;
}
}
throw new NoSuchElementException();
}
/**
* Method to find the Field at specific index
@@ -92,4 +105,96 @@ public class BoardManager {
public List<Field> getBoard() {
return board;
}
public List<PropertyField> getPropertyFields(List<Integer> source) {
List<PropertyField> properties = new ArrayList<>();
for (Integer i : source) {
properties.add((PropertyField)getFieldAtIndex(i));
}
return properties;
}
public boolean canBuild(BuildingProperty field) {
if (field == null) {
return false; // Null check for safety
}
//Check for hotel
if (field.getHotel() == 1) {
return false;
}
// Get the color group of the property
FieldColor groupColor = field.getColor();
// Get all properties of the same color group
List<BuildingProperty> groupProperties = board.stream()
.filter(f -> f instanceof BuildingProperty)
.map(f -> (BuildingProperty) f)
.filter(bp -> bp.getColor() == groupColor)
.collect(Collectors.toList());
// Check if the player owns all properties in the color group
if (!groupProperties.stream().allMatch(bp -> bp.getOwner() != null && !bp.isMortgaged() && bp.getOwner().getId() == field.getOwner().getId())) {
return false; // The player must own all properties in the color group
}
// Ensure balanced building: Check if building levels are balanced within the group
int currentHouses = field.getHouses();
return groupProperties.stream()
.allMatch(bp -> bp.getHouses() >= currentHouses);
}
/**
* Checks if a House can be sold on the given Property
* @param field the Property to check
* @return true if a house can be sold on the property, false otherwise
*/
public boolean canSell(BuildingProperty field) {
if (field == null) {
return false; // Null check for safety
}
// Get the color group of the property
FieldColor groupColor = field.getColor();
// Get all properties of the same color group
List<BuildingProperty> groupProperties = board.stream()
.filter(f -> f instanceof BuildingProperty)
.map(f -> (BuildingProperty) f)
.filter(bp -> bp.getColor() == groupColor)
.collect(Collectors.toList());
// Check if the property has houses or a hotel to sell
if (field.getHouses() == 0 && field.getHotel() == 0) {
return false; // No houses or hotels to sell
}
// Ensure balanced selling: You cannot sell houses unevenly in the group
int currentHouses = field.getHouses();
int currentHotel = field.getHotel();
// If there is a hotel, selling is allowed only if all other properties have max houses
if (currentHotel > 0) {
return groupProperties.stream()
.allMatch(bp -> bp.getHouses() == 4 || bp.equals(field));
}
// If there are houses, check that selling does not unbalance the group
return groupProperties.stream()
.allMatch(bp -> bp.getHouses() <= currentHouses);
}
/**
* Gibt eine Liste von BuildingProperty-Feldern zurück, auf denen Häuser oder Hotels stehen.
* @return Liste von BuildingProperty-Feldern mit Gebäuden
*/
public List<BuildingProperty> getPropertiesWithBuildings() {
return board.stream()
.filter(f -> f instanceof BuildingProperty)
.map(f -> (BuildingProperty) f)
.filter(bp -> bp.getHouses() > 0 || bp.getHotel() > 0)
.collect(Collectors.toList());
}
}

View File

@@ -3,12 +3,14 @@ package pp.monopoly.model.fields;
import java.util.ArrayList;
import java.util.List;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class BuildingProperty extends PropertyField {
private int houses;
private boolean hotel = false;
private final int housePrice;
private final FieldColor color;
private final int rentFactor1 = 5;
@@ -17,6 +19,12 @@ public class BuildingProperty extends PropertyField {
private final int rentFactor4 = 55;
private final int rentFactorHotel = 70;
private BuildingProperty(){
super("", 0, 0, 0);
this.housePrice = 0;
this.color = null;
}
BuildingProperty(String name, int id, int price, int rent, int housePrice, FieldColor color) {
super(name, id, price, rent);
this.housePrice = housePrice;
@@ -25,41 +33,31 @@ public class BuildingProperty extends PropertyField {
@Override
public int calcRent() {
if (hotel) {
return (int) Math.round(rent*rentFactorHotel/10)*10;
}
switch (houses) {
case 1:
return (int) Math.round(rent*rentFactor1/10)*10;
return (int) Math.round(rent*rentFactor1/10)*10;
case 2:
return (int) Math.round(rent*rentFactor2/10)*10;
return (int) Math.round(rent*rentFactor2/10)*10;
case 3:
return (int) Math.round(rent*rentFactor3/10)*10;
return (int) Math.round(rent*rentFactor3/10)*10;
case 4:
return (int) Math.round(rent*rentFactor4/10)*10;
case 5:
return (int) Math.round(rent*rentFactorHotel/10)*10;
default:
return rent;
}
}
public boolean buildHouse() {
if (houses < 4) {
public boolean build() {
if (houses < 5) {
houses++;
return true;
}
return false;
}
public boolean buildHotel() {
if (hotel) {
return false;
}
hotel = true;
return true;
}
public boolean removeHouse() {
public boolean sell() {
if (houses == 0) {
return false;
}
@@ -67,14 +65,6 @@ public class BuildingProperty extends PropertyField {
return true;
}
public boolean removeHotel() {
if (!hotel) {
return false;
}
hotel = false;
return true;
}
@Override
public void accept(Player player) {
player.visit(this);
@@ -104,6 +94,6 @@ public class BuildingProperty extends PropertyField {
}
public int getHotel() {
return hotel ? 1:0;
return (houses == 5)? 1:0;
}
}

View File

@@ -1,11 +1,18 @@
package pp.monopoly.model.fields;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class EventField extends Field{
public EventField(String name, int id) {
private EventField() {
super("", 0);
}
EventField(String name, int id) {
super(name, id);
}

View File

@@ -1,11 +1,19 @@
package pp.monopoly.model.fields;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
abstract class Field {
@Serializable
public abstract class Field {
protected final String name;
protected final int id;
private Field() {
this.name = "";
this.id = 0;
}
protected Field(String name, int id) {
this.name = name;
this.id= id;

View File

@@ -17,6 +17,10 @@ public enum FieldColor {
private final ColorRGBA color;
private FieldColor() {
this.color = null;
}
/**
* Constructs a FieldColor with the specified ColorRGBA value.
*

View File

@@ -1,11 +1,19 @@
package pp.monopoly.model.fields;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class FineField extends Field{
private final int fine;
private FineField() {
super("", 0);
this.fine = 0;
}
FineField(String name, int id, int fine) {
super(name, id);
this.fine = fine;

View File

@@ -1,10 +1,17 @@
package pp.monopoly.model.fields;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class FoodField extends PropertyField {
public FoodField(String name, int id) {
private FoodField() {
super("", 0, 0, 0);
}
FoodField(String name, int id) {
super(name, id, 1500,0);
}

View File

@@ -1,11 +1,17 @@
package pp.monopoly.model.fields;
import pp.monopoly.game.server.Player;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class GateField extends PropertyField{
private GateField() {
super("", 0, 0, 0);
}
GateField(String name, int id) {
super(name, id, 2000, 25);
super(name, id, 2000, 250);
}
@Override

View File

@@ -1,10 +1,12 @@
package pp.monopoly.model.fields;
import pp.monopoly.game.server.Player;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class GoField extends Field{
public GoField() {
GoField() {
super("Monatsgehalt", 0);
}

View File

@@ -1,7 +1,9 @@
package pp.monopoly.model.fields;
import pp.monopoly.game.server.Player;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class GulagField extends Field{
private int bailCost = 500;

View File

@@ -1,11 +1,14 @@
package pp.monopoly.model.fields;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
/**
* Represents an abstract property field in the Monopoly game.
* Contains attributes related to ownership, price, rent, and mortgage status.
*/
@Serializable
public abstract class PropertyField extends Field {
private final int price;
@@ -13,6 +16,12 @@ public abstract class PropertyField extends Field {
private Player owner;
private boolean mortgaged = false;
private PropertyField() {
super("", 0);
this.price = 0;
this.rent = 0;
}
/**
* Constructs a PropertyField with the specified name, ID, price, and rent.
*

View File

@@ -1,7 +1,10 @@
package pp.monopoly.model.fields;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class TestStreckeField extends Field{
private int money;

View File

@@ -1,10 +1,13 @@
package pp.monopoly.model.fields;
import com.jme3.network.serializing.Serializable;
import pp.monopoly.game.server.Player;
@Serializable
public class WacheField extends Field{
public WacheField() {
WacheField() {
super("Wache", 30);
}

View File

@@ -0,0 +1,13 @@
package pp.monopoly.notification;
public record ButtonStatusEvent(boolean buttonsEnabled) implements GameEvent{
/**
* Notifies the game event listener of this event.
*
* @param listener the game event listener
*/
@Override
public void notifyListener(GameEventListener listener) {
listener.receivedEvent(this);
}
}

View File

@@ -0,0 +1,10 @@
package pp.monopoly.notification;
public record DiceRollEvent(int a, int b) implements GameEvent{
@Override
public void notifyListener(GameEventListener listener) {
listener.receivedEvent(this);
}
}

View File

@@ -0,0 +1,10 @@
package pp.monopoly.notification;
public record EventCardEvent(String description) implements GameEvent{
@Override
public void notifyListener(GameEventListener listener) {
listener.receivedEvent(this);
}
}

View File

@@ -45,4 +45,39 @@ public interface GameEventListener {
* @param event the received event
*/
default void receivedEvent(ClientStateEvent event) { /* do nothing */ }
/**
* Indicates that the dice result has changed.
*
* @param event the received event
*/
default void receivedEvent(DiceRollEvent event) { /*Do nothing */}
/**
* Indicates that the players assets have changed.
*
* @param event the received event
*/
default void receivedEvent(UpdatePlayerView event) { /*Do nothing */}
/**
* Indicates that an event card has been drawn
*
* @param event the received event
*/
default void receivedEvent(EventCardEvent event) { /*Do nothing */}
/**
* Indicates that all buttons in the toolbar should be disabled
*
* @param event the received event
*/
default void receivedEvent(ButtonStatusEvent event) { /*Do nothing */}
/**
* Indicates that all buttons in the toolbar should be disabled
*
* @param event the received event
*/
default void receivedEvent(PopUpEvent event) { /*Do nothing */}
}

View File

@@ -6,36 +6,10 @@ import pp.monopoly.model.Item;
/**
* Event that is triggered when an item is added to a board.
*/
public class ItemAddedEvent {
private final Item item;
private final Board board;
public record ItemAddedEvent(Board board, Item item) implements GameEvent {
/**
* Constructs a new ItemAddedEvent.
*
* @param item the item that was added
* @param board the board to which the item was added
*/
public ItemAddedEvent(Item item, Board board) {
this.item = item;
this.board = board;
}
/**
* Gets the item that was added.
*
* @return the added item
*/
public Item getItem() {
return item;
}
/**
* Gets the board to which the item was added.
*
* @return the board
*/
public Board getBoard() {
return board;
@Override
public void notifyListener(GameEventListener listener) {
listener.receivedEvent(this);
}
}

View File

@@ -1,36 +1,15 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.notification;
import pp.monopoly.model.Board;
import pp.monopoly.model.Item;
/**
* Event when an item gets removed.
*
* @param item the destroyed item
* Event that is triggered when an item is removed from a board.
*/
public class ItemRemovedEvent {
private final Item item;
private final Board board;
public record ItemRemovedEvent(Board board, Item item) implements GameEvent {
public ItemRemovedEvent(Item item, Board board) {
this.item = item;
this.board = board;
}
public Item getItem() {
return item;
}
public Board getBoard() {
return board;
@Override
public void notifyListener(GameEventListener listener) {
listener.receivedEvent(this);
}
}

View File

@@ -0,0 +1,12 @@
package pp.monopoly.notification;
import pp.monopoly.message.server.ServerMessage;
public record PopUpEvent(String msg, ServerMessage message) implements GameEvent{
@Override
public void notifyListener(GameEventListener listener) {
listener.receivedEvent(this);
}
}

View File

@@ -0,0 +1,13 @@
package pp.monopoly.notification;
public record UpdatePlayerView() implements GameEvent{
/**
* Notifies the game event listener of this event.
*
* @param listener the game event listener
*/
@Override
public void notifyListener(GameEventListener listener) {
listener.receivedEvent(this);
}
}

View File

@@ -6,37 +6,37 @@
########################################
#
monopoly.name=Monopoly
button.play=Start Game
button.ready=Ready
button.rotate=Rotate
server.connection.failed=Failed to establish a server connection.
its.your.turn=It's your turn! Click on the opponent's field to shoot...
lost.connection.to.server=Lost connection to server. The game terminated.
place.ships.in.your.map=Place ships in your map.
wait.for.an.opponent=Wait for an opponent!
wait.for.opponent=Wait for your opponent!
confirm.leaving=Would you really like to leave the game?
you.lost.the.game=You lost the game!
you.won.the.game=You won the game!
button.yes=Yes
button.no=No
button.play=Spiel starten
button.ready=Bereit
button.rotate=Drehen
server.connection.failed=Fehler beim Herstellen einer Serververbindung.
its.your.turn=Du bist am Zug! Klicke auf das Feld des Gegners, um zu schießen...
lost.connection.to.server=Verbindung zum Server verloren. Das Spiel wurde beendet.
place.ships.in.your.map=Platziere Schiffe auf deiner Karte.
wait.for.an.opponent=Warte auf einen Gegner!
wait.for.opponent=Warte auf deinen Gegner!
confirm.leaving=Möchtest du das Spiel wirklich beenden?
you.lost.the.game=Du hast das Spiel verloren!
you.won.the.game=Du hast das Spiel gewonnen!
button.yes=Ja
button.no=Nein
button.ok=Ok
button.connect=Connect
button.cancel=Cancel
button.connect=Verbinden
button.cancel=Abbrechen
server.dialog=Server
host.name=Host
port.number=Port
wait.its.not.your.turn=Wait, it's not your turn!!
menu.quit=Quit game
menu.return-to-game=Return to game
menu.sound-enabled=Sound switched on
menu.background-sound-enabled=Background music switched on
menu.map.load=Load map from file...
menu.map.save=Save map in file...
label.file=File:
label.connecting=Connecting...
dialog.error=Error
dialog.question=Question
port.must.be.integer=Port must be an integer number
map.doesnt.fit=The map doesn't fit to this game
client.server-start=Start server
wait.its.not.your.turn=Warte, es ist nicht dein Zug!!
menu.quit=Spiel beenden
menu.return-to-game=Zum Spiel zurückkehren
menu.sound-enabled=Ton eingeschaltet
menu.background-sound-enabled=Hintergrundmusik eingeschaltet
menu.map.load=Karte aus Datei laden...
menu.map.save=Karte in Datei speichern...
label.file=Datei:
label.connecting=Verbinde...
dialog.error=Fehler
dialog.question=Frage
port.must.be.integer=Port muss eine ganze Zahl sein
map.doesnt.fit=Die Karte passt nicht zu diesem Spiel
client.server-start=Server starten

View File

@@ -1,387 +0,0 @@
/*
package pp.monopoly;
import org.junit.Test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
public class Testhandbuch {
// T001 UC-game-01 - testStartApplication
@Test
public void testStartApplication() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
MainMenu mainMenu = app.getStateManager().getState(MainMenu.class);
assertNotNull(mainMenu);
}
// T002 UC-game-02 - testOpenStartMenu
@Test
public void testOpenStartMenu() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
MainMenu mainMenu = app.getStateManager().getState(MainMenu.class);
mainMenu.showMenu();
assertTrue(mainMenu.isMenuVisible());
}
// T003 UC-game-03 - testNavigateToPlayOption
@Test
public void testNavigateToPlayOption() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
MainMenu mainMenu = app.getStateManager().getState(MainMenu.class);
mainMenu.showMenu();
mainMenu.startNewGame();
NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class);
assertNotNull(newGameMenu);
}
// T004 UC-game-04 - testExitApplicationFromMenu
@Test
public void testExitApplicationFromMenu() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
MainMenu mainMenu = app.getStateManager().getState(MainMenu.class);
mainMenu.showMenu();
mainMenu.exitGame();
assertTrue(app.isClosed());
}
// T005 UC-game-05 - testOpenSettingsFromMenu
@Test
public void testOpenSettingsFromMenu() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
MainMenu mainMenu = app.getStateManager().getState(MainMenu.class);
mainMenu.showMenu();
mainMenu.openSettings();
SettingMenu settingMenu = app.getStateManager().getState(SettingMenu.class);
assertNotNull(settingMenu);
}
// T006 UC-game-06 - testOpenGameMenuWithESC
@Test
public void testOpenGameMenuWithESC() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
app.simpleUpdate(0.1f);
GameMenu gameMenu = app.getStateManager().getState(GameMenu.class);
assertTrue(gameMenu.isVisible());
}
// T007 UC-game-07 - testEnterHostName
@Test
public void testEnterHostName() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class);
newGameMenu.showMenu();
newGameMenu.enterHostName("localhost");
assertEquals("localhost", newGameMenu.getHostName());
}
// T008 UC-game-07 - testEnterPortNumber
@Test
public void testEnterPortNumber() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class);
newGameMenu.showMenu();
newGameMenu.enterPortNumber(12345);
assertEquals(12345, newGameMenu.getPortNumber());
}
// T009 UC-game-07 - testCancelGameCreation
@Test
public void testCancelGameCreation() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class);
newGameMenu.showMenu();
newGameMenu.cancel();
MainMenu mainMenu = app.getStateManager().getState(MainMenu.class);
assertTrue(mainMenu.isMenuVisible());
}
// T010 UC-game-08 - testEnterPlayerLobby
@Test
public void testEnterPlayerLobby() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
app.getStateManager().getState(NetworkDialog.class).connect();
Lobby lobby = app.getStateManager().getState(Lobby.class);
assertNotNull(lobby);
}
// T011 UC-game-09 - testEnterStartingCapital
@Test
public void testEnterStartingCapital() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class);
newGameMenu.showMenu();
newGameMenu.enterStartingCapital(1500);
assertEquals(1500, newGameMenu.getStartingCapital());
}
// T012 UC-game-09 - testIncreaseStartingCapital
@Test
public void testIncreaseStartingCapital() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class);
newGameMenu.showMenu();
newGameMenu.enterStartingCapital(1500);
newGameMenu.increaseStartingCapital(100);
assertEquals(1600, newGameMenu.getStartingCapital());
}
// T013 UC-game-09 - testDecreaseStartingCapital
@Test
public void testDecreaseStartingCapital() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class);
newGameMenu.showMenu();
newGameMenu.enterStartingCapital(1500);
newGameMenu.decreaseStartingCapital(100);
assertEquals(1400, newGameMenu.getStartingCapital());
}
// T014 UC-game-10 - testDefaultPlayerName
@Test
public void testDefaultPlayerName() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
app.getStateManager().getState(Lobby.class).initializePlayerNames();
assertEquals("Spieler 1", app.getStateManager().getState(Lobby.class).getPlayerName(0));
assertEquals("Spieler 2", app.getStateManager().getState(Lobby.class).getPlayerName(1));
}
// T015 UC-game-11 - testEnterDisplayName
@Test
public void testEnterDisplayName() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Lobby lobby = app.getStateManager().getState(Lobby.class);
lobby.enterDisplayName("TestPlayer");
assertEquals("TestPlayer", lobby.getPlayerName(0));
}
// T016 UC-game-11 - testDuplicateNameEntry
@Test
public void testDuplicateNameEntry() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Lobby lobby = app.getStateManager().getState(Lobby.class);
lobby.enterDisplayName("Player1");
assertTrue(lobby.isDuplicateName("Player1"));
}
// T017 UC-game-12 - testSelectPlayerColor
@Test
public void testSelectPlayerColor() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Lobby lobby = app.getStateManager().getState(Lobby.class);
lobby.selectColor("Red");
assertEquals("Red", lobby.getPlayerColor(0));
}
// T018 UC-game-12 - testSelectOccupiedColor
@Test
public void testSelectOccupiedColor() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Lobby lobby = app.getStateManager().getState(Lobby.class);
lobby.selectColor("Red");
assertTrue(lobby.isColorOccupied("Red"));
}
// T019 UC-game-13 - testSelectPlayerToken
@Test
public void testSelectPlayerToken() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Lobby lobby = app.getStateManager().getState(Lobby.class);
lobby.selectToken("Ship");
assertEquals("Ship", lobby.getPlayerToken(0));
}
// T020 UC-game-13 - testSelectOccupiedToken
@Test
public void testSelectOccupiedToken() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Lobby lobby = app.getStateManager().getState(Lobby.class);
lobby.selectToken("Ship");
assertTrue(lobby.isTokenOccupied("Ship"));
}
// T021 UC-game-14 - testCancelPlayerLobby
@Test
public void testCancelPlayerLobby() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Lobby lobby = app.getStateManager().getState(Lobby.class);
lobby.cancel();
MainMenu mainMenu = app.getStateManager().getState(MainMenu.class);
assertTrue(mainMenu.isMenuVisible());
}
// T022 UC-game-15 - testOpenLobbyMenuWithESC
@Test
public void testOpenLobbyMenuWithESC() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
app.simpleUpdate(0.1f);
LobbyMenu lobbyMenu = app.getStateManager().getState(LobbyMenu.class);
assertTrue(lobbyMenu.isVisible());
}
// T023 UC-game-16 - testPlayerReadyConfirmation
@Test
public void testPlayerReadyConfirmation() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Lobby lobby = app.getStateManager().getState(Lobby.class);
lobby.setPlayerReady(true);
assertTrue(lobby.isPlayerReady(0));
}
// T024 UC-game-17 - testStartGame
@Test
public void testStartGame() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Lobby lobby = app.getStateManager().getState(Lobby.class);
lobby.startGame();
assertEquals(GameState.InGame, lobby.getGameState());
}
// T025 UC-game-18 - testPlayerMovement
@Test
public void testPlayerMovement() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player = game.getPlayer(0);
player.move(5);
assertEquals(5, player.getPosition());
}
// T026 UC-game-19 - testPurchaseProperty
@Test
public void testPurchaseProperty() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player = game.getPlayer(0);
Property property = game.getProperty(0);
player.buyProperty(property);
assertTrue(player.getProperties().contains(property));
}
// T027 UC-game-20 - testMovePlayerOnDiceRoll
@Test
public void testMovePlayerOnDiceRoll() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player = game.getPlayer(0);
int initialPosition = player.getPosition();
player.rollDice();
assertTrue(player.getPosition() > initialPosition);
}
// T028 UC-game-21 - testPassGo
@Test
public void testPassGo() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player = game.getPlayer(0);
player.move(40); // Assuming 40 steps moves player to Go
assertTrue(player.passedGo());
}
// T029 UC-game-22 - testCollectMoneyFromGo
@Test
public void testCollectMoneyFromGo() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player = game.getPlayer(0);
int initialBalance = player.getBalance();
player.move(40); // Move to Go
assertTrue(player.getBalance() > initialBalance);
}
// T030 UC-game-23 - testPayRent
@Test
public void testPayRent() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player = game.getPlayer(0);
PropertyField property = (PropertyField) game.getField(1);
player.move(1);
int initialBalance = player.getBalance();
player.payRent(property);
assertTrue(player.getBalance() < initialBalance);
}
// T031 UC-game-24 - testDeclareBankruptcy
@Test
public void testDeclareBankruptcy() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player = game.getPlayer(0);
player.declareBankruptcy();
assertEquals(PlayerState.Bankrupt, player.getState());
}
// T032 UC-game-25 - testTradeProperty
@Test
public void testTradeProperty() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player1 = game.getPlayer(0);
Player player2 = game.getPlayer(1);
Property property = game.getProperty(0);
player1.offerTrade(player2, property);
assertTrue(player2.hasProperty(property));
}
// T033 UC-game-26 - testGameOverCondition
@Test
public void testGameOverCondition() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player = game.getPlayer(0);
player.declareBankruptcy();
assertTrue(game.isGameOver());
}
// T034 UC-game-27 - testPlayerInJail
@Test
public void testPlayerInJail() {
MonopolyApp app = new MonopolyApp();
app.simpleInitApp();
Game game = app.getStateManager().getState(Game.class);
Player player = game.getPlayer(0);
game.sendToJail(player);
assertEquals(PlayerState.InJail, player.getState());
}
}
*/

View File

@@ -92,13 +92,13 @@ public class ClientLogicTest {
// T006: UC-game-06 - Testet, ob das Spielmenü geöffnet wird, wenn der Spieler im Spiel „ESC“ drückt
public void testOpenGameMenuWithESC() {
// Simuliere den ESC-Tastendruck
doNothing().when(app).handleEscape(true);
doNothing().when(app).escape(true);
// Rufe die ESC-Tastenmethode auf
app.handleEscape(true);
app.escape(true);
// Verifiziere, dass die Methode aufgerufen wurde
verify(app, times(1)).handleEscape(true);
verify(app, times(1)).escape(true);
}
}
/*

View File

@@ -1,9 +0,0 @@
package pp.monopoly.model;
import org.junit.Test;
import static org.junit.Assert.fail;
public class RandomPositionIteratorTest {
}