diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/BoardAppState.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/BoardAppState.java new file mode 100644 index 0000000..815c131 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/BoardAppState.java @@ -0,0 +1,207 @@ +package pp.monopoly.client; + +import com.jme3.app.Application; +import com.jme3.app.state.AppStateManager; +import com.jme3.asset.AssetManager; +import com.jme3.light.AmbientLight; +import com.jme3.light.DirectionalLight; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; +import com.jme3.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Box; +import com.jme3.shadow.DirectionalLightShadowRenderer; +import com.jme3.shadow.EdgeFilteringMode; +import com.jme3.texture.Texture; +import com.jme3.util.SkyFactory; +import com.jme3.util.TangentBinormalGenerator; +import pp.monopoly.model.Board; +import pp.monopoly.client.gui.BobTheBuilder; +import pp.monopoly.client.gui.Toolbar; +import pp.util.FloatMath; + +import static pp.util.FloatMath.PI; +import static pp.util.FloatMath.TWO_PI; +import static pp.util.FloatMath.cos; +import static pp.util.FloatMath.sin; +import static pp.util.FloatMath.sqrt; + +/** + * Manages the rendering and visual aspects of the sea and sky in the Battleship game. + * This state is responsible for setting up and updating the sea, sky, and lighting + * conditions, and controls the camera to create a dynamic view of the game environment. + */ +public class BoardAppState extends MonopolyAppState { + /** + * The path to the unshaded texture material. + */ + private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS + + /** + * The path to the sea texture material. + */ + private static final String BoardTexture = "Pictures/board2.png"; //NON-NLS + + /** + * The root node for all visual elements in this state. + */ + private final Node viewNode = new Node("view"); //NON-NLS + + /** + * The node containing the scene elements, such as the sea surface. + */ + private final Node sceneNode = new Node("scene"); //NON-NLS + + /** + * Synchronizes the buildings's visual representation with the game logic. + */ + private BobTheBuilder bobTheBuilder; + + /** + * Initializes the state by setting up the sky, lights, and other visual components. + * This method is called when the state is first attached to the state manager. + * + * @param stateManager the state manager + * @param application the application + */ + @Override + public void initialize(AppStateManager stateManager, Application application) { + super.initialize(stateManager, application); + viewNode.attachChild(sceneNode); + setupLights(); + setupSky(); + } + + /** + * Enables the sea and sky state, setting up the scene and registering any necessary listeners. + * This method is called when the state is set to active. + */ + @Override + protected void enableState() { + getApp().getRootNode().detachAllChildren(); + getApp().getGuiNode().detachAllChildren(); + + getApp().getGuiNode().attachChild(new Toolbar(getApp())); + sceneNode.detachAllChildren(); + setupScene(); + if (bobTheBuilder == null) { + bobTheBuilder = new BobTheBuilder(getApp(), sceneNode, getGameLogic().getBoard()); + getGameLogic().addListener(bobTheBuilder); + } + getApp().getRootNode().attachChild(viewNode); + } + //TODO remove this only for camera testing + private static final float ABOVE_SEA_LEVEL = 10f; + private static final float INCLINATION = 2.5f; + private float cameraAngle; + + /** + * Adjusts the camera position and orientation to create a circular motion around + * the center of the map. This provides a dynamic view of the sea and surrounding environment. + */ + private void adjustCamera() { + final Board board = getGameLogic().getBoard(); + final float mx = 0.5f * board.getWidth(); + final float my = 0.5f * board.getHeight(); + // final float radius = 2f * sqrt(mx * mx + my + my); + // final float cos = radius * cos(cameraAngle); + // final float sin = radius * sin(cameraAngle); + // final float x = mx - cos; + // final float y = my - sin; + final Camera camera = getApp().getCamera(); + camera.setLocation(new Vector3f(20, ABOVE_SEA_LEVEL, 0)); + camera.lookAt(new Vector3f(0,0, 0), + Vector3f.UNIT_Y); + camera.update(); + } + + /** + * Disables the sea and sky state, removing visual elements from the scene and unregistering listeners. + * This method is called when the state is set to inactive. + */ + @Override + protected void disableState() { + getApp().getRootNode().detachChild(viewNode); + if (bobTheBuilder != null) { + getGameLogic().removeListener(bobTheBuilder); + bobTheBuilder = null; + } + } + + /** + * Updates the state each frame, moving the camera to simulate it circling around the map. + * + * @param tpf the time per frame (seconds) + */ + @Override + public void update(float tpf) { + super.update(tpf); + //TODO remove this only for camera testing + cameraAngle += TWO_PI * 0.05f * tpf; + adjustCamera(); + } + + /** + * Sets up the lighting for the scene, including directional and ambient lights. + * Also configures shadows to enhance the visual depth of the scene. + */ + private void setupLights() { + final AssetManager assetManager = getApp().getAssetManager(); + // final DirectionalLightShadowRenderer shRend = new DirectionalLightShadowRenderer(assetManager, 2048, 3); + // shRend.setLambda(0.55f); + // shRend.setShadowIntensity(0.6f); + // shRend.setEdgeFilteringMode(EdgeFilteringMode.Bilinear); + // getApp().getViewPort().addProcessor(shRend); + + // final DirectionalLight sun = new DirectionalLight(); + // sun.setDirection(new Vector3f(-1f, -0.7f, -1f).normalizeLocal()); + // viewNode.addLight(sun); + // shRend.setLight(sun); + + final AmbientLight ambientLight = new AmbientLight(new ColorRGBA(1f, 1f, 1f, 1f)); + viewNode.addLight(ambientLight); + } + + /** + * Sets up the sky in the scene using a skybox with textures for all six directions. + * This creates a realistic and immersive environment for the sea. + */ + private void setupSky() { + final AssetManager assetManager = getApp().getAssetManager(); + final Texture west = assetManager.loadTexture("Pictures/Backdrop/left.jpg"); //NON-NLS + final Texture east = assetManager.loadTexture("Pictures/Backdrop/right.jpg"); //NON-NLS + final Texture north = assetManager.loadTexture("Pictures/Backdrop/front.jpg"); //NON-NLS + final Texture south = assetManager.loadTexture("Pictures/Backdrop/back.jpg"); //NON-NLS + final Texture up = assetManager.loadTexture("Pictures/Backdrop/up.jpg"); //NON-NLS + final Texture down = assetManager.loadTexture("Pictures/Backdrop/down.jpg"); //NON-NLS + final Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down); + // sky.rotate(0, PI, 0); + viewNode.attachChild(sky); + } + + /** + * Sets up the sea surface in the scene. This includes creating the sea mesh, + * applying textures, and enabling shadows. + */ + private void setupScene() { + final Board board = getGameLogic().getBoard(); + final float x = board.getWidth(); + final float y = board.getHeight(); + final Box seaMesh = new Box(y, 0.1f, x); + final Geometry seaGeo = new Geometry("sea", seaMesh); //NON-NLS + // seaGeo.setLocalTranslation(new Vector3f(y, -0.1f, x)); + // seaMesh.scaleTextureCoordinates(new Vector2f(4f, 4f)); + final Material seaMat = new Material(getApp().getAssetManager(), "Common/MatDefs/Light/Lighting.j3md"); + Texture texture = getApp().getAssetManager().loadTexture("Pictures/board2.png"); + seaMat.setTexture("DiffuseMap", texture); + seaGeo.setMaterial(seaMat); + seaGeo.setShadowMode(ShadowMode.CastAndReceive); + TangentBinormalGenerator.generate(seaGeo); + sceneNode.attachChild(seaGeo); + } +} \ No newline at end of file diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java index fada1dc..c165269 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java @@ -285,7 +285,7 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga attachGameSound(); attachGameMusic(); - stateManager.attach(new GameAppState()); + stateManager.attach(new BoardAppState()); } /** @@ -405,7 +405,7 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga */ @Override public void receivedEvent(ClientStateEvent event) { - stateManager.getState(GameAppState.class).setEnabled(true); + stateManager.getState(BoardAppState.class).setEnabled(logic.isTurn()); } /** diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BoardSynchronizer.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BoardSynchronizer.java deleted file mode 100644 index 007389d..0000000 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BoardSynchronizer.java +++ /dev/null @@ -1,74 +0,0 @@ -package pp.monopoly.client.gui; - -import com.jme3.scene.Node; -import com.jme3.scene.Spatial; - -import pp.monopoly.model.Board; -import pp.monopoly.model.Item; -import pp.monopoly.model.Visitor; -import pp.monopoly.notification.GameEventListener; -import pp.monopoly.notification.ItemAddedEvent; -import pp.monopoly.notification.ItemRemovedEvent; -import pp.view.ModelViewSynchronizer; - -/** - * Abstract base class for synchronizing the visual representation of a {@link Board} with its model state. - * This class handles the addition and removal of items from the map, ensuring that changes in the model - * are accurately reflected in the view. - */ -abstract class BoardSynchronizer extends ModelViewSynchronizer implements Visitor, GameEventListener { - protected final Board board; - - /** - * Constructs a new BoardSynchronizer. - * - * @param board the game board to synchronize - * @param root the root node to which the view representations of the board items are attached - */ - protected BoardSynchronizer(Board board, Node root) { - super(root); - this.board = board; - } - - /** - * Translates a model item into its corresponding visual representation. - * - * @param item the item from the model to be translated - * @return the visual representation of the item as a {@link Spatial} - */ - @Override - protected Spatial translate(Item item) { - return item.accept(this); - } - - /** - * Adds the existing items from the board to the view during initialization. - */ - protected void addExisting() { - board.getItems().forEach(this::add); - } - - /** - * Handles the event when an item is removed from the board. - * - * @param event the event indicating that an item has been removed from the board - */ - @Override - public void receivedEvent(ItemRemovedEvent event) { - if (board == event.getBoard()) { - delete(event.getItem()); - } - } - - /** - * Handles the event when an item is added to the board. - * - * @param event the event indicating that an item has been added to the board - */ - @Override - public void receivedEvent(ItemAddedEvent event) { - if (board == event.getBoard()) { - add(event.getItem()); - } - } -} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BobTheBuilder.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BobTheBuilder.java new file mode 100644 index 0000000..1d6d63f --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BobTheBuilder.java @@ -0,0 +1,90 @@ +package pp.monopoly.client.gui; + +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import com.jme3.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Box; + +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.model.Board; +import pp.monopoly.model.Figure; +import pp.monopoly.model.Hotel; +import pp.monopoly.model.House; + +public class BobTheBuilder extends GameBoardSynchronizer { + + private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS + private static final String COLOR = "Color"; //NON-NLS + private static final String FIGURE = "figure"; //NON-NLS + private static final String HOUSE = "house"; //NON-NLS + private static final String HOTEL = "hotel"; //NON-NLS + + private final MonopolyApp app; + private final Board board; + + public BobTheBuilder(MonopolyApp app, Node root, Board board) { + super(app.getGameLogic().getBoard(), root); + this.app = app; + this.board = board; + addExisting(); + } + + @Override + public Spatial visit(Figure figure) { + return createBox(figure); + } + + @Override + public Spatial visit(Hotel hotel) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'visit'"); + } + + @Override + public Spatial visit(House figure) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'visit'"); + } + + /** + * Creates a simple box to represent a battleship that is not of the "King George V" type. + * + * @param ship the battleship to be represented + * @return the geometry representing the battleship as a box + */ + private Spatial createBox(Figure figure) { + final Box box = new Box(3, + 3f, + 3); + final Geometry geometry = new Geometry(FIGURE, box); + geometry.setMaterial(createColoredMaterial(ColorRGBA.Blue)); + geometry.setShadowMode(ShadowMode.CastAndReceive); + geometry.setLocalTranslation(0, 2, 0); + + return geometry; + } + + /** + * Creates a new {@link Material} with the specified color. + * If the color includes transparency (i.e., alpha value less than 1), + * the material's render state is set to use alpha blending, allowing for + * semi-transparent rendering. + * + * @param color the {@link ColorRGBA} to be applied to the material. If the alpha value + * of the color is less than 1, the material will support transparency. + * @return a {@link Material} instance configured with the specified color and, + * if necessary, alpha blending enabled. + */ + private Material createColoredMaterial(ColorRGBA color) { + final Material material = new Material(app.getAssetManager(), UNSHADED); + if (color.getAlpha() < 1f) + material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + material.setColor(COLOR, color); + return material; + } + +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java index 7263a18..0f9624c 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java @@ -1,120 +1,88 @@ -//////////////////////////////////////// -// Programming project code -// UniBw M, 2022, 2023, 2024 -// www.unibw.de/inf2 -// (c) Mark Minas (mark.minas@unibw.de) -//////////////////////////////////////// - package pp.monopoly.client.gui; -import com.jme3.material.Material; -import com.jme3.material.RenderState.BlendMode; -import com.jme3.math.ColorRGBA; -import com.jme3.renderer.queue.RenderQueue.ShadowMode; -import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; -import com.jme3.scene.shape.Box; - -import pp.monopoly.client.MonopolyApp; -import pp.monopoly.game.server.PlayerColor; +import pp.monopoly.model.Item; +import pp.monopoly.model.Visitor; +import pp.monopoly.notification.GameEventListener; +import pp.monopoly.notification.ItemAddedEvent; +import pp.monopoly.notification.ItemRemovedEvent; +import pp.monopoly.notification.UpdatePlayerView; import pp.monopoly.model.Board; -import pp.monopoly.model.Figure; -import pp.monopoly.model.Rotation; -import static pp.util.FloatMath.HALF_PI; -import static pp.util.FloatMath.PI; +import pp.view.ModelViewSynchronizer; /** - * The {@code GameBoardSynchronizer} class is responsible for synchronizing the graphical - * representation of the Game board and figures with the underlying data model. - * It extends the {@link BoardSynchronizer} to provide specific synchronization - * logic for the Game board. + * Abstract base class for synchronizing the visual representation of a {@link Board} with its model state. + * This class handles the addition and removal of items from the board, ensuring that changes in the model + * are accurately reflected in the view. + *

+ * Subclasses are responsible for providing the specific implementation of how each item in the map + * is represented visually by implementing the {@link Visitor} interface. + *

*/ -class GameBoardSynchronizer extends BoardSynchronizer { - private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS - private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md"; - private static final String COLOR = "Color"; //NON-NLS - private static final String FIGURE = "figure"; //NON-NLS - - private final MonopolyApp app; +abstract class GameBoardSynchronizer extends ModelViewSynchronizer implements Visitor, GameEventListener { + // The board that this synchronizer is responsible for + private final Board board; /** - * Constructs a {@code GameBoardSynchronizer} object with the specified application, root node, and ship map. + * Constructs a new GameBoardSynchronizer. + * Initializes the synchronizer with the provided board and the root node for attaching view representations. * - * @param app the Monopoly application - * @param root the root node to which graphical elements will be attached - * @param map the Game Board containing fields and figures + * @param map the board to be synchronized + * @param root the root node to which the view representations of the board items are attached */ - public GameBoardSynchronizer(MonopolyApp app, Node root, Board board) { - super(board, root); - this.app = app; + protected GameBoardSynchronizer(Board board, Node root) { + super(root); + this.board = board; + } + + /** + * Translates a model item into its corresponding visual representation. + * The specific visual representation is determined by the concrete implementation of the {@link Visitor} interface. + * + * @param item the item from the model to be translated + * @return the visual representation of the item as a {@link Spatial} + */ + @Override + protected Spatial translate(Item item) { + return item.accept(this); + } + + /** + * Adds the existing items from the board to the view. + * This method should be called during initialization to ensure that all current items in the board + * are visually represented. + */ + protected void addExisting() { + board.getItems().forEach(this::add); + } + + /** + * Handles the event when an item is removed from the board. + * Removes the visual representation of the item from the view if it belongs to the synchronized board. + * + * @param event the event indicating that an item has been removed from the board + */ + @Override + public void receivedEvent(ItemRemovedEvent event) { + if (board == event.board()) + delete(event.item()); + } + + /** + * Handles the event when an item is added to the board. + * Adds the visual representation of the new item to the view if it belongs to the synchronized board. + * + * @param event the event indicating that an item has been added to the board + */ + @Override + public void receivedEvent(ItemAddedEvent event) { + if (board == event.board()) + add(event.item()); + } + + public void recievedEvent(UpdatePlayerView event) { + clear(); addExisting(); } - - /** - * Visits a {@link Figure} and creates a graphical representation of it. - * The representation is a 3D model. - * - * @param figure the figure to be represented - * @return the node containing the graphical representation of the figure - */ - public Spatial visit(Figure figure) { - final Node node = new Node(FIGURE); - node.attachChild(createBox(figure)); - final float x = 1; - final float z = 1; - node.setLocalTranslation(x, 0f, z); - return node; - } - - /** - * Creates a representation of a figure - * - * @param figure the figure to be represented - * @return the geometry representing the figure - */ - private Spatial createBox(Figure figure) { - final Box box = new Box(0.5f * (figure.getMaxY() - figure.getMinY()) + 0.3f, - 0.3f, - 0.5f * (figure.getMaxX() - figure.getMinX()) + 0.3f); - final Geometry geometry = new Geometry(FIGURE, box); - geometry.setMaterial(createColoredMaterial(PlayerColor.PINK.getColor())); - geometry.setShadowMode(ShadowMode.CastAndReceive); - - return geometry; - } - - /** - * Creates a new {@link Material} with the specified color. - * If the color includes transparency (i.e., alpha value less than 1), - * the material's render state is set to use alpha blending, allowing for - * semi-transparent rendering. - * - * @param color the {@link ColorRGBA} to be applied to the material. If the alpha value - * of the color is less than 1, the material will support transparency. - * @return a {@link Material} instance configured with the specified color and, - * if necessary, alpha blending enabled. - */ - private Material createColoredMaterial(ColorRGBA color) { - final Material material = new Material(app.getAssetManager(), UNSHADED); - if (color.getAlpha() < 1f) - material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); - material.setColor(COLOR, color); - return material; - } - - /** - * Calculates the rotation angle for the specified rotation. - * - * @param rot the rotation of the battleship - * @return the rotation angle in radians - */ - private static float calculateRotationAngle(Rotation rot) { - return switch (rot) { - case RIGHT -> HALF_PI; - case DOWN -> 0f; - case LEFT -> -HALF_PI; - case UP -> PI; - }; - } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ActiveState.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ActiveState.java index ff6702e..9be8d2d 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ActiveState.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ActiveState.java @@ -1,5 +1,9 @@ package pp.monopoly.game.client; +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}. @@ -15,4 +19,22 @@ public class ActiveState extends ClientState { 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) { + + } } \ No newline at end of file diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java index da18a9f..fda6a67 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java @@ -5,6 +5,7 @@ import java.lang.System.Logger.Level; 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.BuyPropertyRequest; @@ -22,7 +23,6 @@ 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.TradeHandler; import pp.monopoly.model.fields.BoardManager; import pp.monopoly.notification.ClientStateEvent; @@ -33,6 +33,7 @@ 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; @@ -54,7 +55,7 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { private final List listeners = new ArrayList<>(); /** The game board representing the player's current state. */ - private Board board; + private Board board = new Board(10, 10, null); /** The current state of the client game logic. */ private ClientState state = new LobbyState(this); @@ -121,15 +122,6 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { return tradeHandler; } - /** - * 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); - } - /** * Sets the informational text to be displayed to the player. * @@ -204,32 +196,21 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { state.update(delta); } - /** - * Handles the result of a dice roll. - * - * @param msg the message containing the dice roll result - */ + public boolean isTurn() { + return state.isTurn(); + } + @Override public void received(DiceResult msg) { 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) { 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()) { @@ -241,23 +222,14 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { } } - /** - * Handles the start of the game. - * - * @param msg the game start message - */ @Override public void received(GameStart msg) { playerHandler = msg.getPlayerHandler(); setState(new WaitForTurnState(this)); 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()) { @@ -266,11 +238,6 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { } } - /** - * Updates the status of a player. - * - * @param msg the message containing player status update details - */ @Override public void received(PlayerStatusUpdate msg) { playerHandler = msg.getPlayerHandler(); @@ -278,31 +245,16 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { notifyListeners(new UpdatePlayerView()); } - /** - * Handles timeout warnings. - * - * @param msg the message containing timeout warning details - */ @Override public void received(TimeOutWarning msg) { 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) { 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.isAccepted()) { @@ -314,24 +266,15 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { } } - /** - * Handles trade requests from other players. - * - * @param msg the message containing the trade request details - */ @Override public void received(TradeRequest msg) { 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) { + setState(new ActiveState(this)); notifyListeners(new ButtonStatusEvent(true)); } @@ -357,4 +300,5 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { notifyListeners(new PopUpEvent("ReceivedRent", msg)); } } + } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientState.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientState.java index b9f2361..2ae8ad2 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientState.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientState.java @@ -11,7 +11,11 @@ import java.io.File; import java.io.IOException; import java.lang.System.Logger.Level; -import pp.monopoly.model.IntPoint; +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 in Monopoly. @@ -54,17 +58,39 @@ abstract class ClientState { * * @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 on the game board. + * 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 + } + + /** + * Updateds the view based on the new player status. + * + * @param msg the message containing the new player status + */ + 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 } /** diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/LobbyState.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/LobbyState.java index f2db68b..27d8adf 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/LobbyState.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/LobbyState.java @@ -1,5 +1,7 @@ package pp.monopoly.game.client; +import pp.monopoly.message.server.GameStart; + /** * Represents the lobby state of the client in the Monopoly game. * Extends {@link ClientState}. @@ -14,4 +16,9 @@ public class LobbyState extends ClientState { LobbyState(ClientGameLogic logic) { super(logic); } + + @Override + void receivedGameStart(GameStart msg) { + logic.setState(new WaitForTurnState(logic)); + } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/WaitForTurnState.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/WaitForTurnState.java index 0575966..c4dfaff 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/WaitForTurnState.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/WaitForTurnState.java @@ -1,5 +1,7 @@ package pp.monopoly.game.client; +import pp.monopoly.message.server.NextPlayerTurn; + /** * Represents the state where the client is waiting for their turn in the Monopoly game. * Extends {@link ClientState}. @@ -14,4 +16,14 @@ public class WaitForTurnState extends ClientState { WaitForTurnState(ClientGameLogic logic) { super(logic); } + + @Override + boolean isTurn() { + return true; + } + + @Override + void recivedNextPlayerTurn(NextPlayerTurn msg) { + logic.setState(new ActiveState(logic)); + } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ServerInterpreter.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ServerInterpreter.java index 72aa99d..42219d1 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ServerInterpreter.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ServerInterpreter.java @@ -91,16 +91,16 @@ public interface ServerInterpreter { void received(NextPlayerTurn msg); /** - * Handles a NextPlayerTurn message received from the server. + * Handles a BuyPropertyRequest message received from the server. * - * @param msg the NextPlayerTurn message received + * @param msg the BuyPropertyRequest message received */ void received(BuyPropertyRequest msg); /** - * Handles a NextPlayerTurn message received from the server. + * Handles a NotificationMessage message received from the server. * - * @param msg the NextPlayerTurn message received + * @param msg the NotificationMessage message received */ void received(NotificationMessage msg); } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Board.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Board.java index e46308f..811e8c8 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Board.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Board.java @@ -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((GameEvent) 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,7 @@ 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)); } /** @@ -96,6 +116,33 @@ public class Board { private Stream getItems(Class 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
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 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 getHotels() { + return getItems(Hotel.class); + } /** * Returns an unmodifiable list of all items currently on the map. @@ -124,28 +171,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. * diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Hotel.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Hotel.java new file mode 100644 index 0000000..266da04 --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Hotel.java @@ -0,0 +1,93 @@ +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 accept(Visitor 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 getBuildingPosition() { + 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); + } +} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/House.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/House.java new file mode 100644 index 0000000..a7e317b --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/House.java @@ -0,0 +1,110 @@ +package pp.monopoly.model; + +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. + */ + public 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 accept(Visitor 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 getBuildingPosition() { + 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); + } + +} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Visitor.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Visitor.java index bba2ace..c088605 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Visitor.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Visitor.java @@ -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 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); + } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/VoidVisitor.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/VoidVisitor.java index 0654feb..e775cfe 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/VoidVisitor.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/VoidVisitor.java @@ -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); + } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/ItemAddedEvent.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/ItemAddedEvent.java index 00cb797..5f1cee7 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/ItemAddedEvent.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/ItemAddedEvent.java @@ -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); } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/ItemRemovedEvent.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/ItemRemovedEvent.java index 462fc55..29d968e 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/ItemRemovedEvent.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/ItemRemovedEvent.java @@ -1,35 +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. - * + * 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); } } - -