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..28bcf89 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/BoardAppState.java @@ -0,0 +1,217 @@ +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.FastMath; +import com.jme3.math.Quaternion; +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 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; + + /** + * The pop-up manager for displaying messages and notifications. + */ + private PopUpManager popUpManager;; + + /** + * 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); + popUpManager = new PopUpManager(getApp()); + 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(); + + new Toolbar(getApp()).open(); + sceneNode.detachAllChildren(); + setupScene(); + if (bobTheBuilder == null) { + bobTheBuilder = new BobTheBuilder(getApp(), getApp().getRootNode()); + System.out.println("LISTENER IS REGISTEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"); + 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(x, ABOVE_SEA_LEVEL, y)); + 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); //NONs-NLS + seaGeo.setLocalTranslation(new Vector3f(0, -0.1f, 0)); + Quaternion rotation = new com.jme3.math.Quaternion(); + rotation.fromAngleAxis(FastMath.HALF_PI, com.jme3.math.Vector3f.UNIT_Y); + seaGeo.setLocalRotation(rotation); + 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/PopUpManager.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/PopUpManager.java new file mode 100644 index 0000000..53e6dfa --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/PopUpManager.java @@ -0,0 +1,97 @@ +package pp.monopoly.client; + +import java.util.Timer; +import java.util.TimerTask; + +import pp.monopoly.client.gui.popups.AcceptTrade; +import pp.monopoly.client.gui.popups.BuildingPropertyCard; +import pp.monopoly.client.gui.popups.ConfirmTrade; +import pp.monopoly.client.gui.popups.EventCardPopup; +import pp.monopoly.client.gui.popups.FoodFieldCard; +import pp.monopoly.client.gui.popups.GateFieldCard; +import pp.monopoly.client.gui.popups.Gulag; +import pp.monopoly.client.gui.popups.GulagInfo; +import pp.monopoly.client.gui.popups.LooserPopUp; +import pp.monopoly.client.gui.popups.NoMoneyWarning; +import pp.monopoly.client.gui.popups.ReceivedRent; +import pp.monopoly.client.gui.popups.RejectTrade; +import pp.monopoly.client.gui.popups.Rent; +import pp.monopoly.client.gui.popups.TimeOut; +import pp.monopoly.client.gui.popups.WinnerPopUp; +import pp.monopoly.message.server.NotificationMessage; +import pp.monopoly.message.server.TradeReply; +import pp.monopoly.model.fields.BuildingProperty; +import pp.monopoly.model.fields.FoodField; +import pp.monopoly.model.fields.GateField; +import pp.monopoly.notification.EventCardEvent; +import pp.monopoly.notification.GameEventListener; +import pp.monopoly.notification.PopUpEvent; + +public class PopUpManager implements GameEventListener { + + private final MonopolyApp app; + + public PopUpManager(MonopolyApp app) { + this.app = app; + app.getGameLogic().addListener(this); + } + + @Override + public void receivedEvent(PopUpEvent event) { + if (event.msg().equals("Buy")) { + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + app.enqueue(() -> { + int field = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID(); + Object fieldObject = app.getGameLogic().getBoardManager().getFieldAtIndex(field); + + if (fieldObject instanceof BuildingProperty) { + new BuildingPropertyCard(app).open(); + } else if (fieldObject instanceof GateField) { + new GateFieldCard(app).open(); + } else if (fieldObject instanceof FoodField) { + new FoodFieldCard(app).open(); + } + }); + } + }, 2500); + } else if (event.msg().equals("Winner")) { + new WinnerPopUp(app).open(); + } else if (event.msg().equals("Looser")) { + new LooserPopUp(app).open(); + } else if (event.msg().equals("timeout")) { + new TimeOut(app).open(); + } else if (event.msg().equals("tradeRequest")) { + new ConfirmTrade(app).open(); + } else if (event.msg().equals("goingToJail")) { + new Gulag(app).open(); + } else if (event.msg().equals("NoMoneyWarning")) { + new NoMoneyWarning(app).open(); + } else if(event.msg().equals("rent")) { + new Rent(app, ( (NotificationMessage) event.message()).getRentOwner(), ( (NotificationMessage) event.message()).getRentAmount() ).open(); + } else if (event.msg().equals("jailtryagain")) { + new GulagInfo(app, 1).open(); + } else if (event.msg().equals("jailpay")) { + new GulagInfo(app, 3).open(); + } else if (event.msg().equals("tradepos")) { + new AcceptTrade(app, (TradeReply) event.message()).open(); + } else if (event.msg().equals("tradeneg")) { + new RejectTrade(app, (TradeReply) event.message()).open(); + } else if (event.msg().equals("ReceivedRent")) { + new ReceivedRent(app, ( (NotificationMessage) event.message()).getRentOwner(), ( (NotificationMessage) event.message()).getRentAmount() ).open(); + } + } + + @Override + public void receivedEvent(EventCardEvent event) { + Timer timer = new Timer(); + timer.schedule(new TimerTask() { + @Override + public void run() { + app.enqueue(() -> new EventCardPopup(app, event.description()).open()); + } + }, 2500); + } +} 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..4fc8acc --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BobTheBuilder.java @@ -0,0 +1,159 @@ +package pp.monopoly.client.gui; + +import static com.jme3.material.Materials.LIGHTING; + +import java.util.stream.Collectors; + +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.Player; +import pp.monopoly.model.Figure; +import pp.monopoly.model.Hotel; +import pp.monopoly.model.House; +import pp.monopoly.model.Item; +import pp.monopoly.notification.UpdatePlayerView; + +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; + + public BobTheBuilder(MonopolyApp app, Node root) { + super(app.getGameLogic().getBoard(), root); + this.app = app; + addExisting(); + } + + + @Override + public Spatial visit(Figure figure) { + final Node node = new Node(FIGURE); + node.attachChild(createFigure(figure)); + + // Setze die Position basierend auf der Feld-ID + node.setLocalTranslation(figure.getPos()); + + // Setze die Rotation basierend auf der Feld-ID + node.setLocalRotation(figure.getRot().toQuaternion()); + // node.addControl(new FigureControl(figure)); + return node; + } + + + @Override + public Spatial visit(Hotel hotel) { + final Node node = new Node(HOTEL); + node.attachChild(createHotel(hotel)); + + // Setze die Position basierend auf der Feld-ID + node.setLocalTranslation(hotel.getPos()); + + // Setze die Rotation basierend auf der Feld-ID + node.setLocalRotation(hotel.getRot().toQuaternion()); + + return node; + } + + + @Override + public Spatial visit(House house) { + final Node node = new Node(HOUSE); + node.attachChild(createHouse(house)); + + // Setze die Position basierend auf der Feld-ID + node.setLocalTranslation(house.getPos()); + + // Setze die Rotation basierend auf der Feld-ID + node.setLocalRotation(house.getAlignment()); + + return node; + } + + private Spatial createFigure(Figure figure) { + // Lade das Modell + Spatial model = app.getAssetManager().loadModel("models/" + "Spielfiguren/" + figure.getType() + "/" + figure.getType() + ".j3o"); + + // Skaliere und positioniere das Modell + model.scale(0.5f); + + return model; + } + + private Spatial createHotel(Hotel hotel) { + Spatial model = app.getAssetManager().loadModel("models/Hotel/Hotel.j3o"); + model.scale(0.2f); + model.setShadowMode(ShadowMode.CastAndReceive); + return model; + } + + + private Spatial createHouse(House house) { + Spatial model = app.getAssetManager().loadModel("models/Haus/"+house.getStage()+"Haus.j3o"); + model.scale(0.5f); + model.setShadowMode(ShadowMode.CastAndReceive); + return model; + } + + /** + * 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(Item item) { + 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; + } + + @Override + public void receivedEvent(UpdatePlayerView event) { + board.removePlayers(); + + //TODO transition animation + for (Player player : app.getGameLogic().getPlayerHandler().getPlayers()) { + board.add(player.getFigure()); + } + for (Item item : board.getItems().stream().filter(p -> p instanceof Figure).collect(Collectors.toList())) { + add(item); + } + } +} 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..dde8954 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,87 @@ -//////////////////////////////////////// -// 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.DiceRollEvent; +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 + protected 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; - addExisting(); + protected GameBoardSynchronizer(Board board, Node root) { + super(root); + this.board = board; } /** - * Visits a {@link Figure} and creates a graphical representation of it. - * The representation is a 3D model. + * 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 figure the figure to be represented - * @return the node containing the graphical representation of the figure + * @param item the item from the model to be translated + * @return the visual representation of the item as a {@link Spatial} */ - 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; + @Override + protected Spatial translate(Item item) { + return item.accept(this); } /** - * Creates a representation of a figure - * - * @param figure the figure to be represented - * @return the geometry representing the figure + * 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. */ - 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); + protected void addExisting() { + board.getItems().forEach(this::add); + } - return geometry; + /** + * Handles the event when an item is removed from the ship map. + * Removes the visual representation of the item from the view if it belongs to the synchronized ship map. + * + * @param event the event indicating that an item has been removed from the ship map + */ + @Override + public void receivedEvent(ItemRemovedEvent event) { + if (board == event.board()) + delete(event.item()); } /** - * 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. + * Handles the event when an item is added to the ship map. + * Adds the visual representation of the new item to the view if it belongs to the synchronized ship map. * - * @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. + * @param event the event indicating that an item has been added to the ship map */ - 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; + @Override + public void receivedEvent(ItemAddedEvent event) { + if (board == event.board()){ + add(event.item()); + } } - /** - * 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..2a5a9ce 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,8 +5,10 @@ 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.BuildInfo; import pp.monopoly.message.server.BuyPropertyRequest; import pp.monopoly.message.server.DiceResult; import pp.monopoly.message.server.EventDrawCard; @@ -22,9 +24,11 @@ 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; @@ -33,6 +37,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 +59,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, this); /** The current state of the client game logic. */ private ClientState state = new LobbyState(this); @@ -121,15 +126,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 +200,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 +226,17 @@ 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)); + 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()) { @@ -266,11 +245,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 +252,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 +273,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 +307,18 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { 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())); + } + } + } + } 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/game/server/ServerGameLogic.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/ServerGameLogic.java index 5c2da6d..66befed 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/ServerGameLogic.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/ServerGameLogic.java @@ -7,6 +7,8 @@ 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.AlterProperty; import pp.monopoly.message.client.BuyPropertyResponse; @@ -18,6 +20,7 @@ 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; @@ -95,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. @@ -209,7 +223,9 @@ public class ServerGameLogic implements ClientInterpreter { String name = msg.getName(); String truc = name.length() > 10 ? name.substring(0, 15) : name; player.setName(truc); - player.setFigure(new Figure(1, -10, -10, Rotation.LEFT, msg.getFigure())); + 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()); } @@ -407,6 +423,8 @@ public class ServerGameLogic implements ClientInterpreter { 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()); } } @@ -414,11 +432,12 @@ public class ServerGameLogic implements ClientInterpreter { 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(); } @@ -429,9 +448,9 @@ public class ServerGameLogic implements ClientInterpreter { } 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 (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()); diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/BuildInfo.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/BuildInfo.java new file mode 100644 index 0000000..72a3a9c --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/BuildInfo.java @@ -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); + } + +} 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..b7dc8f4 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,23 @@ 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); + + /** + * Handles a BuildInfo message received from the server. + * + * @param msg the BuildInfo message received + */ + void received(BuildInfo 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..48b037a 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(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 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 +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. * diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Figure.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Figure.java index c64a384..a9e6fce 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Figure.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Figure.java @@ -1,28 +1,22 @@ package pp.monopoly.model; -import static java.lang.Math.max; -import static java.lang.Math.min; -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; @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 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, ""); } /** @@ -30,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; } @@ -46,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(); } /** @@ -55,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; } /** @@ -75,8 +74,8 @@ 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; } /** @@ -85,85 +84,62 @@ public class Figure implements Item{ * @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); } /** @@ -184,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 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. @@ -285,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 } /** 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..b58ec25 --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Hotel.java @@ -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 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 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; + } +} 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..13a606e --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/House.java @@ -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 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 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; + } + +} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Item.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Item.java index c3e2a5e..e664002 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Item.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Item.java @@ -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(); + } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Rotation.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Rotation.java index 39d95c1..ddf3abb 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Rotation.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Rotation.java @@ -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; -/** - * Represents the rotation of a Item and provides functionality related to rotation. - */ +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 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; + } } 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); } } - - diff --git a/Projekte/monopoly/server/src/main/java/pp/monopoly/server/MonopolyServer.java b/Projekte/monopoly/server/src/main/java/pp/monopoly/server/MonopolyServer.java index 95f04c4..ef2b4f1 100644 --- a/Projekte/monopoly/server/src/main/java/pp/monopoly/server/MonopolyServer.java +++ b/Projekte/monopoly/server/src/main/java/pp/monopoly/server/MonopolyServer.java @@ -39,6 +39,7 @@ 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.BuyPropertyRequest; import pp.monopoly.message.server.DiceResult; import pp.monopoly.message.server.EventDrawCard; @@ -192,6 +193,7 @@ public class MonopolyServer implements MessageListener, Connec Serializer.registerClass(JailEvent.class); Serializer.registerClass(AlterProperty.class); Serializer.registerClass(GameOver.class); + Serializer.registerClass(BuildInfo.class); } /**