diff --git a/Projekte/.run/MdgaApp.run.xml b/Projekte/.run/MdgaApp.run.xml index 8b467373..123a07af 100644 --- a/Projekte/.run/MdgaApp.run.xml +++ b/Projekte/.run/MdgaApp.run.xml @@ -1,5 +1,7 @@ + - + \ No newline at end of file diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/InitControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/InitControl.java new file mode 100644 index 00000000..73f259ff --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/InitControl.java @@ -0,0 +1,48 @@ +package pp.mdga.client; + +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.AbstractControl; + +/** + * An abstract control class that serves as a base for initializing spatial objects + * in jMonkeyEngine. This class overrides the controlUpdate and controlRender methods + * from the AbstractControl class, providing default empty implementations, + * and adds the ability to initialize spatial objects when they are set. + */ +public abstract class InitControl extends AbstractControl { + + @Override + protected void controlUpdate(float tpf) { + + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + + } + + /** + * Sets the spatial object to be controlled. This method also initializes the spatial + * if it is being set for the first time. + * + * @param spatial The spatial object to control. + */ + @Override + public void setSpatial(Spatial spatial) { + if (this.spatial == null && spatial != null) { + super.setSpatial(spatial); + initSpatial(); + } + } + + /** + * Initializes the spatial object. This method can be overridden by subclasses + * to define custom initialization logic for the spatial. + * This method is called automatically when the spatial is set for the first time. + */ + protected void initSpatial() { + // Default empty implementation. Override to add initialization logic. + } +} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java index e66c6ede..807a37c4 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java @@ -17,11 +17,13 @@ import pp.mdga.client.board.OutlineControl; import pp.mdga.client.board.PieceControl; import pp.mdga.client.gui.CardControl; +import pp.mdga.client.gui.DiceControl; import pp.mdga.client.view.GameView; import pp.mdga.game.BonusCard; import pp.mdga.game.Color; import pp.mdga.game.Piece; import pp.mdga.notification.FinishNotification; +import pp.mdga.notification.MovePieceNotification; import pp.mdga.notification.SelectableCardsNotification; import java.util.List; @@ -92,10 +94,14 @@ public void onAction(String name, boolean isPressed, float tpf) { } if(name.equals("Click") && isPressed) { if (app.getView() instanceof GameView gameView) { + DiceControl diceSelect = checkHover(gameView.getGuiHandler().getCardLayerCamera(), gameView.getGuiHandler().getCardLayerRootNode(), DiceControl.class); CardControl cardLayerSelect = checkHover(gameView.getGuiHandler().getCardLayerCamera(), gameView.getGuiHandler().getCardLayerRootNode(), CardControl.class); OutlineControl boardSelect = checkHover(app.getCamera(), app.getRootNode(), OutlineControl.class); - if(cardLayerSelect != null) { + if(diceSelect != null) { + app.getModelSynchronize().rolledDice(); + } + else if(cardLayerSelect != null) { //cardSelect if(cardLayerSelect.isSelectable()) gameView.getGuiHandler().selectCard(cardLayerSelect); } @@ -117,7 +123,11 @@ else if(boardSelect != null) { } if(name.equals("Test") &&isPressed){ if(app.getView() instanceof GameView gameView){ - app.getNotificationSynchronizer().addTestNotification(new FinishNotification(Color.NAVY)); +// gameView.getGuiHandler().rollRankingResult(Color.AIRFORCE, 1); +// gameView.getGuiHandler().rollRankingResult(Color.ARMY, 2); +// gameView.getGuiHandler().rollRankingResult(Color.NAVY, 3); +// gameView.getGuiHandler().rollRankingResult(Color.CYBER, 4); + gameView.getGuiHandler().showDice(); } } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index 80a44dbd..9eca1ab8 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -1,14 +1,11 @@ package pp.mdga.client; import com.jme3.app.SimpleApplication; -import com.jme3.system.JmeContext; +import com.jme3.system.AppSettings; import com.simsilica.lemur.GuiGlobals; import pp.mdga.client.acoustic.AcousticHandler; -import pp.mdga.client.animation.AnimationHandler; -import com.jme3.system.AppSettings; import pp.mdga.client.dialog.JoinDialog; import pp.mdga.client.view.*; -import pp.mdga.message.server.ServerInterpreter; import java.io.IOException; import java.util.concurrent.ExecutorService; @@ -23,9 +20,6 @@ public class MdgaApp extends SimpleApplication { private static Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class); - /** Handles animations in the application. */ - private AnimationHandler animationHandler; - /** Handles acoustic effects and state-based sounds. */ private AcousticHandler acousticHandler; @@ -88,6 +82,7 @@ public static void main(String[] args) { MdgaApp app = new MdgaApp(); app.setSettings(settings); app.setShowSettings(false); + app.setPauseOnLostFocus(false); app.start(); } @@ -102,7 +97,6 @@ public void simpleInitApp() { flyCam.setEnabled(false); - animationHandler = new AnimationHandler(this); acousticHandler = new AcousticHandler(this); notificationSynchronizer = new NotificationSynchronizer(this); inputSynchronizer = new InputSynchronizer(this); @@ -163,14 +157,6 @@ public void enter(MdgaState state) { view.enter(); } - /** - * Gets the animation handler. - * - * @return the {@link AnimationHandler} instance - */ - public AnimationHandler getAnimationHandler() { - return animationHandler; - } /** * Gets the acoustic handler. diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/ModelSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/ModelSynchronizer.java index e1c1b987..0b26a305 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/ModelSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/ModelSynchronizer.java @@ -107,8 +107,8 @@ public void selectTsk(Color color) { app.getGameLogic().selectTsk(color); } - public void unselectTsk() { - app.getGameLogic().selectTsk(Color.NONE); + public void unselectTsk(Color color) { + app.getGameLogic().deselectTSK(color); } public void rolledDice() { diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java index a704e3e8..f23eda0e 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java @@ -60,15 +60,15 @@ private void handleLobby(Notification notification) { if (notification instanceof TskSelectNotification n) { lobbyView.setTaken(n.getColor(), true, n.isSelf(), n.getName()); - lobbyView.setTaken(n.getColor(), true, false, n.getName()); } else if (notification instanceof StartDialogNotification) { app.enter(MdgaState.MAIN); } else if (notification instanceof TskUnselectNotification n) { lobbyView.setTaken(n.getColor(), false, false, null); } else if(notification instanceof LobbyReadyNotification lobbyReadyNotification) { lobbyView.setReady(lobbyReadyNotification.getColor(), lobbyReadyNotification.isReady()); - } else if (notification instanceof GameNotification) { + } else if (notification instanceof GameNotification n) { app.enter(MdgaState.GAME); + ((GameView) app.getView()).setOwnColor(n.getOwnColor()); } else { throw new RuntimeException("notification not expected: " + notification.toString()); } @@ -84,6 +84,7 @@ private void handleGame(Notification notification) { guiHandler.addCard(n.getBonusCard()); } else if (notification instanceof ActivePlayerNotification n) { gameView.getGuiHandler().setActivePlayer(n.getColor()); + boardHandler.showDice(n.getColor()); } else if (notification instanceof CeremonyNotification ceremonyNotification) { app.enter(MdgaState.CEREMONY); CeremonyView ceremonyView = (CeremonyView) app.getView(); @@ -116,22 +117,22 @@ private void handleGame(Notification notification) { } else if (notification instanceof DrawCardNotification n) { guiHandler.drawCard(n.getColor()); } else if (notification instanceof HomeMoveNotification home) { - boardHandler.moveHomePiece(home.getPieceId(), home.getHomeIndex()); + boardHandler.movePieceHomeAnim(home.getPieceId(), home.getHomeIndex()); guiHandler.hideText(); } else if (notification instanceof InterruptNotification) { app.enter(MdgaState.LOBBY); } else if (notification instanceof MovePieceNotification n) { if(n.isMoveStart()) { //StartMove - boardHandler.movePieceStart(n.getPiece(), n.getMoveIndex()); + boardHandler.movePieceStartAnim(n.getPiece(), n.getMoveIndex()); } else { //InfieldMove - boardHandler.movePiece(n.getPiece(), n.getStartIndex(), n.getMoveIndex()); + boardHandler.movePieceAnim(n.getPiece(), n.getStartIndex(), n.getMoveIndex()); } guiHandler.hideText(); } else if (notification instanceof ThrowPieceNotification n) { - boardHandler.throwPiece(n.getPieceId()); + boardHandler.throwPieceAnim(n.getPieceId()); } else if (notification instanceof NoShieldNotification n) { boardHandler.unshieldPiece(n.getPieceId()); } else if (notification instanceof PlayCardNotification n) { @@ -147,10 +148,12 @@ private void handleGame(Notification notification) { } else if (notification instanceof ResumeNotification) { //TODO } else if (notification instanceof RollDiceNotification n) { + gameView.getGuiHandler().hideText(); if(n.getColor() == gameView.getOwnColor()){ guiHandler.rollDice(n.getEyes(), n.isTurbo() ? n.getMultiplier() : -1); } else { + boardHandler.hideDice(); if (n.isTurbo()) guiHandler.showRolledDiceMult(n.getEyes(), n.getMultiplier(), n.getColor()); else guiHandler.showRolledDice(n.getEyes(), n.getColor()); } @@ -163,7 +166,7 @@ private void handleGame(Notification notification) { } else if (notification instanceof StartDialogNotification) { app.enter(MdgaState.MAIN); } else if (notification instanceof SwapPieceNotification n) { - boardHandler.swapPieces(n.getFirstPiece(), n.getSecondPiece()); +// boardHandler.swapPieces(n.getFirstPiece(), n.getSecondPiece()); guiHandler.swap(); } else if (notification instanceof WaitMoveNotification) { //TODO ??? diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/Animation.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/Animation.java deleted file mode 100644 index b808f9d3..00000000 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/Animation.java +++ /dev/null @@ -1,10 +0,0 @@ -package pp.mdga.client.animation; - -abstract class Animation { - - abstract void play(); - - abstract void stop(); - - abstract boolean isOver(); -} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/AnimationHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/AnimationHandler.java deleted file mode 100644 index cee17634..00000000 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/AnimationHandler.java +++ /dev/null @@ -1,29 +0,0 @@ -package pp.mdga.client.animation; - -import pp.mdga.client.MdgaApp; - -public class AnimationHandler { - private MdgaApp app; - - private Animation animation = null; - - public AnimationHandler(MdgaApp app) { - this.app = app; - } - - public void playAnimation(MdgaAnimation type) { - - } - - public void update() { - if (null == animation) { - return; - } - - if (animation.isOver()) { - animation = null; - - //trigger next state in model - } - } -} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/EmptyAnimation.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/EmptyAnimation.java deleted file mode 100644 index b51d5535..00000000 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/EmptyAnimation.java +++ /dev/null @@ -1,18 +0,0 @@ -package pp.mdga.client.animation; - -class EmptyAnimation extends Animation { - @Override - void play() { - //nothing - } - - @Override - void stop() { - //nothing - } - - @Override - boolean isOver() { - return true; - } -} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/MdgaAnimation.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/MdgaAnimation.java deleted file mode 100644 index cddf535d..00000000 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/MdgaAnimation.java +++ /dev/null @@ -1,4 +0,0 @@ -package pp.mdga.client.animation; - -public enum MdgaAnimation { -} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/MoveControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/MoveControl.java new file mode 100644 index 00000000..8aea4efc --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/MoveControl.java @@ -0,0 +1,109 @@ +package pp.mdga.client.animation; + +import com.jme3.math.Vector3f; +import pp.mdga.client.InitControl; + +/** + * A control that smoothly moves a spatial from an initial position to an end position + * using a quadratic interpolation, with the option to perform an action after the movement is complete. + * The movement path includes an intermediate "middle" position at a specified height. + * + *

Movement speed can be adjusted by modifying the MOVE_SPEED constant. The movement easing follows + * an ease-in-out curve to create a smooth start and stop effect. + *

+ */ +public class MoveControl extends InitControl { + + private boolean moving; + private final Vector3f initPos; + private final Vector3f endPos; + private final Vector3f middlePos; + private final static float HEIGHT = 2; + private final static float MOVE_SPEED = 1f; + private float progress = 0; + private final Runnable actionAfter; + + /** + * Creates a new MoveControl with specified initial and end positions, and an action to run after the movement. + * The movement follows a path with a midpoint at a fixed height. + * + * @param initPos The starting position of the spatial. + * @param endPos The target position of the spatial. + * @param actionAfter A Runnable that will be executed after the movement finishes. + */ + public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter){ + moving = false; + this.initPos = initPos; + this.endPos = endPos; + middlePos = new Vector3f( + (initPos.x + endPos.x) / 2, + (initPos.y + endPos.y) / 2, + HEIGHT + ); + this.actionAfter = actionAfter; + } + + /** + * Initializes the movement by resetting the progress and setting the moving flag to true. + * This is called automatically when the spatial is set. + */ + @Override + protected void initSpatial() { + moving = true; + progress = 0; + } + + /** + * Updates the movement of the spatial by interpolating its position along the defined path. + * The movement is smoothed using an easing function. + * Once the movement reaches the target, the {@link #end()} method is called to finish the movement. + * + * @param tpf Time per frame, the time elapsed since the last frame. + */ + @Override + protected void controlUpdate(float tpf) { + if(!moving) return; + progress += tpf * MOVE_SPEED; + if(progress > 1) progress = 1; + spatial.setLocalTranslation(quadInt(initPos,middlePos,endPos, easeInOut(progress))); + if(progress == 1) end(); + } + + /** + * Ends the movement by stopping the interpolation, running the action after the movement, + * and removing this control from the spatial. + */ + private void end(){ + moving = false; + actionAfter.run(); + spatial.removeControl(this); + } + + /** + * Performs quadratic interpolation between three points. + * + * @param p1 The initial point. + * @param p2 The middle point. + * @param p3 The final point. + * @param t The interpolation parameter (0 <= t <= 1). + * @return The interpolated point. + */ + private Vector3f quadInt(Vector3f p1, Vector3f p2, Vector3f p3, float t) { + // Quadratic interpolation: (1-t)^2 * p1 + 2 * (1-t) * t * p2 + t^2 * p3 + float oneMinusT = 1 - t; + return p1.mult(oneMinusT * oneMinusT) + .add(p2.mult(2 * oneMinusT * t)) + .add(p3.mult(t * t)); + } + + /** + * A smooth ease-in-out function for interpolation. + * It accelerates and decelerates the interpolation for a smoother effect. + * + * @param x The interpolation parameter (0 <= x <= 1). + * @return The adjusted interpolation value. + */ + private float easeInOut(float x){ + return x < 0.5 ? 4 * x * x * x : (float) (1 - Math.pow(-2 * x + 2, 3) / 2); + } +} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/SymbolControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/SymbolControl.java similarity index 57% rename from Projekte/mdga/client/src/main/java/pp/mdga/client/gui/SymbolControl.java rename to Projekte/mdga/client/src/main/java/pp/mdga/client/animation/SymbolControl.java index cfeb0ced..1abf533c 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/SymbolControl.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/SymbolControl.java @@ -1,13 +1,23 @@ -package pp.mdga.client.gui; +package pp.mdga.client.animation; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.scene.control.AbstractControl; +import pp.mdga.client.InitControl; import pp.mdga.game.BonusCard; -public class SymbolControl extends AbstractControl { +/** + * A control that manages the animation of symbols representing different bonus card states. + * The symbol can animate with zoom, rotation, and translation effects based on the state of the bonus card. + * + *

The control supports three main states: SHIELD, SWAP, and TURBO. Each state has its own specific animation logic: + *

+ *

+ */ +public class SymbolControl extends InitControl { private boolean zoomingIn = false; private boolean zoomingOut = false; private float zoomSpeed = 1f; @@ -18,7 +28,12 @@ public class SymbolControl extends AbstractControl { private Quaternion initialRotation = null; private float y = 5; - + /** + * Updates the symbol animation based on the current bonus card state. + * The method calls the corresponding update method for each state (SHIELD, SWAP, TURBO). + * + * @param tpf Time per frame, the time elapsed since the last frame. + */ @Override protected void controlUpdate(float tpf) { if (state == null) return; @@ -30,11 +45,12 @@ protected void controlUpdate(float tpf) { } } - @Override - protected void controlRender(RenderManager rm, ViewPort vp) { - - } - + /** + * Updates the symbol when the state is SHIELD. The symbol zooms in and then zooms out. + * When the zooming out finishes, the symbol is removed from the parent spatial. + * + * @param tpf Time per frame, the time elapsed since the last frame. + */ private void shieldUpdate(float tpf) { if (zoomingIn) { progress += tpf * zoomSpeed; @@ -57,6 +73,12 @@ private void shieldUpdate(float tpf) { } } + /** + * Updates the symbol when the state is SWAP. The symbol rotates 360 degrees. + * After the rotation finishes, the symbol is removed from the parent spatial. + * + * @param tpf Time per frame, the time elapsed since the last frame. + */ private void swapUpdate(float tpf) { if (initialRotation == null) { initialRotation = spatial.getLocalRotation().clone(); @@ -80,6 +102,12 @@ private void swapUpdate(float tpf) { } } + /** + * Updates the symbol when the state is TURBO. The symbol moves along the Y-axis with a zoom effect. + * After the movement finishes, the symbol is removed from the parent spatial. + * + * @param tpf Time per frame, the time elapsed since the last frame. + */ private void turboUpdate(float tpf) { if (zoomingIn) { progress += tpf * zoomSpeed; @@ -103,6 +131,10 @@ private void turboUpdate(float tpf) { } } + /** + * Starts the SHIELD animation by zooming the symbol in and out. + * The symbol will first zoom in and then zoom out, and will be removed from the parent spatial once done. + */ public void shield() { if (state != null) throw new RuntimeException("another state is avtive"); state = BonusCard.SHIELD; @@ -112,6 +144,10 @@ public void shield() { spatial.setLocalScale(1f); } + /** + * Starts the SWAP animation by rotating the symbol 360 degrees. + * The symbol will rotate once and then be removed from the parent spatial. + */ public void swap() { if (state != null) throw new RuntimeException("another state is avtive"); spatial.setLocalScale(3); @@ -119,6 +155,10 @@ public void swap() { progress = -0.2f; } + /** + * Starts the TURBO animation by moving the symbol along the Y-axis. + * The symbol will move upwards and then return to its initial position. + */ public void turbo() { if (state != null) throw new RuntimeException("another state is avtive"); spatial.setLocalScale(2); @@ -128,19 +168,45 @@ public void turbo() { progress = 0; } + /** + * Performs linear interpolation between two values. + * + * @param start The starting value. + * @param end The target value. + * @param t The interpolation parameter (0 <= t <= 1). + * @return The interpolated value. + */ private static float lerp(float start, float end, float t) { return (1 - t) * start + t * end; } + /** + * Ease-out function for smoothing the interpolation. + * + * @param t The interpolation parameter (0 <= t <= 1). + * @return The eased value. + */ private static float easeOut(float t) { return (float) Math.sqrt(1 - Math.pow(t - 1, 2)); } + /** + * Ease-in-out function for smoothing the interpolation. + * + * @param t The interpolation parameter (0 <= t <= 1). + * @return The eased value. + */ private float easeInOut(float t) { if (t > 1) t = 1; return (float) -(Math.cos(Math.PI * t) - 1) / 2; } + /** + * Ease-in function for smoothing the interpolation. + * + * @param t The interpolation parameter (0 <= t <= 1). + * @return The eased value. + */ private float easeIn(float t) { return t * t * t * t; } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/ZoomControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/ZoomControl.java new file mode 100644 index 00000000..eb4319cd --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/ZoomControl.java @@ -0,0 +1,110 @@ +package pp.mdga.client.animation; + +import pp.mdga.client.InitControl; + +/** + * A control that applies a zoom effect to a spatial, smoothly scaling it in and out. + * The zoom effect can be customized with speed and scaling factor. + * + *

The control supports zooming in and out with ease-in and ease-out transitions. + * It starts by zooming in, and once complete, it zooms out, eventually removing the spatial from its parent when the animation ends.

+ */ +public class ZoomControl extends InitControl { + private boolean zoomingIn = false; + private boolean zoomingOut = false; + private float progress = 0; + private float zoomSpeed = 1f; + private float zoomFactor = 1f; + + /** + * Constructs a new ZoomControl with the default zoom speed. + */ + public ZoomControl() { + } + + /** + * Constructs a new ZoomControl with a specified zoom speed. + * + * @param speed The speed at which the zoom effect occurs. + */ + public ZoomControl(float speed) { + zoomSpeed = speed; + } + + /** + * Initializes the spatial for the zoom effect. This method is called when the control is added to the spatial. + * It sets the zooming state to zooming in. + */ + @Override + protected void initSpatial() { + zoomingIn = true; + } + + /** + * Updates the zoom effect over time, either zooming in or zooming out. + * + * @param tpf Time per frame, the time elapsed since the last frame. + */ + @Override + protected void controlUpdate(float tpf) { + if (zoomingIn) { + progress += tpf * zoomSpeed; + if (progress > 1) progress = 1; + spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress))); + if (progress >= 1) { + zoomingIn = false; + zoomingOut = true; + progress = 0; + } + } else if (zoomingOut) { + progress += tpf * zoomSpeed; + spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress))); + if (progress > 1) { + zoomingOut = false; + end(); + } + } + } + + /** + * Ends the zoom animation by removing the spatial from its parent and the control from the spatial. + */ + private void end() { + spatial.removeFromParent(); + spatial.removeControl(this); + } + + /** + * Performs linear interpolation between two values. + * + * @param start The starting value. + * @param end The target value. + * @param t The interpolation parameter (0 <= t <= 1). + * @return The interpolated value. + */ + private static float lerp(float start, float end, float t) { + return (1 - t) * start + t * end; + } + + /** + * Ease-out function for smoothing the zoom-in transition. + * + * @param x The interpolation parameter (0 <= x <= 1). + * @return The eased value. + */ + private float easeOut(float x) { + return x == 1 ? 1 : (float) (1 - Math.pow(2, -10 * x)); + + } + + /** + * Ease-in function for smoothing the zoom-out transition. + * + * @param x The interpolation parameter (0 <= x <= 1). + * @return The eased value. + */ + private float easeIn(float x) { + return x == 0 ? 0 : (float) Math.pow(2, 10 * x - 10); + + } +} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/AssetOnMap.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/AssetOnMap.java index d78d48fd..ac6bdf53 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/AssetOnMap.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/AssetOnMap.java @@ -2,4 +2,7 @@ import pp.mdga.client.Asset; +/** + * Record for holding Asset information + */ record AssetOnMap(Asset asset, int x, int y, float rot) {} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java index 74a2223d..a00508e8 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java @@ -9,34 +9,45 @@ import com.jme3.scene.control.AbstractControl; import pp.mdga.client.Asset; import pp.mdga.client.MdgaApp; +import pp.mdga.client.animation.MoveControl; import pp.mdga.client.gui.DiceControl; import pp.mdga.game.Color; +import pp.mdga.game.Piece; import java.util.*; +/** + * BoardHandler is responsible for managing the game board in the MDGA client, including handling + * the initialization, movement, and management of game pieces and assets. + * It works closely with the MdgaApp to create and manipulate 3D models of assets, track player pieces, + * and manage movement across the game board. + */ public class BoardHandler { + // Constants defining the grid size and elevation of the board private static final float GRID_SIZE = 1.72f; private static final float GRID_ELEVATION = 0.0f; private static final String MAP_NAME = "Maps/map.mdga"; + // The main application instance for accessing game assets and logic private final MdgaApp app; + // Collection of in-game assets and board elements private ArrayList infield; private Map pieces; - private Map> colorAssetsMap; private Map> homeNodesMap; private Map> waitingNodesMap; private Map> waitingPiecesMap; + private Map> waitingNodes; private Map pieceColor; - private Node rootNodeBoard; + private final Node rootNodeBoard; private final Node rootNode; - private final FilterPostProcessor fpp; private boolean isInitialised; + // Flags and lists for handling piece selection and movement private List selectableOwnPieces; private List selectableEnemyPieces; private List outlineNodes; @@ -44,6 +55,14 @@ public class BoardHandler { private PieceControl selectedEnemyPiece; private DiceControl diceControl; + /** + * Creates a new BoardHandler. + * + * @param app The main application instance + * @param rootNode The root node where the board will be attached + * @param fpp The post-processor for effects like shadows or filters + * @throws RuntimeException if the app is null + */ public BoardHandler(MdgaApp app, Node rootNode, FilterPostProcessor fpp) { if(app == null) throw new RuntimeException("app is null"); @@ -54,6 +73,9 @@ public BoardHandler(MdgaApp app, Node rootNode, FilterPostProcessor fpp) { isInitialised = false; } + /** + * Initializes the game board by setting up the pieces and nodes. + */ public void init() { isInitialised = true; selectableOwnPieces = new ArrayList<>(); @@ -65,17 +87,30 @@ public void init() { rootNode.attachChild(rootNodeBoard); } + /** + * Shuts down the board handler by detaching all board-related nodes and clearing selected pieces. + */ public void shutdown(){ clearSelectable(); isInitialised = false; rootNode.detachChild(rootNodeBoard); } + /** + * Adds an asset to the map of player assets, ensuring that the player does not have too many assets. + * + * @param col The color of the player + * @param assetOnMap The asset to be added + * @throws RuntimeException if there are too many assets for the player + */ private void addFigureToPlayerMap(Color col, AssetOnMap assetOnMap) { List inMap = addItemToMapList(colorAssetsMap, col, assetOnMap); if (inMap.size() > 4) throw new RuntimeException("to many assets for " + col); } + /** + * Initializes the map with the assets loaded from the map file and corresponding nodes. + */ private void initMap() { pieces = new HashMap<>(); colorAssetsMap = new HashMap<>(); @@ -86,6 +121,12 @@ private void initMap() { pieceColor = new HashMap<>(); diceControl = new DiceControl(app.getAssetManager()); diceControl.create(new Vector3f(0,0,0), 0.7f, true); + waitingNodes = new HashMap<>(); + waitingNodes.put(Color.AIRFORCE, new HashMap<>()); + waitingNodes.put(Color.ARMY, new HashMap<>()); + waitingNodes.put(Color.NAVY, new HashMap<>()); + waitingNodes.put(Color.CYBER, new HashMap<>()); + List assetOnMaps = MapLoader.loadMap(MAP_NAME); @@ -111,6 +152,13 @@ private void initMap() { } } + /** + * Converts an asset to its corresponding color. + * + * @param asset The asset to be converted + * @return The color associated with the asset + * @throws RuntimeException if the asset is invalid + */ private Color assetToColor(Asset asset) { return switch (asset) { case lw -> Color.AIRFORCE; @@ -121,6 +169,14 @@ private Color assetToColor(Asset asset) { }; } + /** + * Creates a 3D model of an asset and adds it to the board. + * + * @param asset The asset to be displayed + * @param pos The position of the asset on the board + * @param rot The rotation of the asset + * @return The Spatial representation of the asset + */ private Spatial createModel(Asset asset, Vector3f pos, float rot) { String modelName = asset.getModelPath(); String texName = asset.getDiffPath(); @@ -137,10 +193,23 @@ private Spatial createModel(Asset asset, Vector3f pos, float rot) { return model; } + /** + * Converts grid coordinates to world space. + * + * @param x The x-coordinate on the grid + * @param y The y-coordinate on the grid + * @return The corresponding world position + */ private static Vector3f gridToWorld(int x, int y) { return new Vector3f(GRID_SIZE * x, GRID_SIZE * y, GRID_ELEVATION); } + /** + * Displays an asset on the map at the given position with the specified rotation. + * + * @param assetOnMap The asset to be displayed. + * @return A spatial representation of the asset at the specified location and rotation. + */ private Spatial displayAsset(AssetOnMap assetOnMap) { int x = assetOnMap.x(); int y = assetOnMap.y(); @@ -162,6 +231,13 @@ private void addHomeNode(Map> map, Color color, AssetOn if (homeNodes.size() > 4) throw new RuntimeException("too many homeNodes for " + color); } + /** + * Calculates the rotation angle required to move a piece from one position to another. + * + * @param prev The previous position. + * @param next The target position. + * @return The rotation angle in degrees. + */ private float getRotationMove(Vector3f prev, Vector3f next) { Vector3f direction = next.subtract(prev).normalizeLocal(); //I had to reverse dir.y, because then it worked. @@ -170,6 +246,14 @@ private float getRotationMove(Vector3f prev, Vector3f next) { return newRot; } + /** + * Recursively moves a piece from its current index to the destination index, + * to keep track of the piece rotation. + * + * @param uuid The UUID of the piece to move. + * @param curIndex The current index of the piece. + * @param moveIndex The target index to move the piece to. + */ private void movePieceRek(UUID uuid, int curIndex, int moveIndex){ if (curIndex == moveIndex) return; @@ -202,6 +286,12 @@ private Vector3f getWaitingPos(Color color){ return getMeanPosition(waitingNodesMap.get(color).stream().map(NodeControl::getLocation).toList()); } + /** + * Gets the mean position of a list of vectors. + * + * @param vectors The list of vectors. + * @return The mean position as a Vector3f. + */ public static Vector3f getMeanPosition(List vectors) { if (vectors.isEmpty()) return new Vector3f(0, 0, 0); @@ -212,9 +302,14 @@ public static Vector3f getMeanPosition(List vectors) { return sum.divide(vectors.size()); } - //public methods**************************************************************************************************** + /** + * Adds a player to the game by associating a color and a list of UUIDs to corresponding assets and waiting nodes. + * + * @param color the color of the player + * @param uuid the list of UUIDs representing the player's assets + * @throws RuntimeException if the number of assets or waiting nodes does not match the provided UUIDs + */ public void addPlayer(Color color, List uuid) { - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); List playerAssets = colorAssetsMap.get(color); if (playerAssets == null) throw new RuntimeException("Assets for Player color are not defined"); @@ -226,20 +321,34 @@ public void addPlayer(Color color, List uuid) { for (int i = 0; i < playerAssets.size(); i++){ AssetOnMap assetOnMap = playerAssets.get(i); + UUID pieceUuid = uuid.get(i); + + // Initialize PieceControl PieceControl pieceControl = displayAndControl(assetOnMap, new PieceControl(assetOnMap.rot(), app.getAssetManager(), app, fpp)); pieceControl.setRotation(assetOnMap.rot()); - movePieceToNode(pieceControl, waitNodes.get(i)); - pieces.put(uuid.get(i), pieceControl); + // Assign piece to waiting node + NodeControl waitNode = getNextWaitingNode(color); + waitingNodes.get(color).put(pieceUuid, waitNode); - pieceColor.put(uuid.get(i), color); + // Move piece to node + movePieceToNode(pieceControl, waitNode); + // Update mappings + pieces.put(pieceUuid, pieceControl); + pieceColor.put(pieceUuid, color); addItemToMapList(waitingPiecesMap, color, pieceControl); } } - public void moveHomePiece(UUID uuid, int index){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); + /** + * Moves a piece to its corresponding home node based on the given index. + * + * @param uuid the UUID of the piece to move + * @param index the index of the home node to move the piece to + * @throws RuntimeException if the UUID is not mapped to a color or if the home nodes are not properly defined + */ + private void moveHomePiece(UUID uuid, int index){ Color color = pieceColor.get(uuid); if(color == null) throw new RuntimeException("uuid is not mapped to a color"); @@ -257,90 +366,145 @@ public void moveHomePiece(UUID uuid, int index){ NodeControl lastHomeNode = homeNodes.get(homeNodes.size()-1); pieceControl.setRotation(getRotationMove(firstHomeNode.getLocation(), lastHomeNode.getLocation())); + app.getModelSynchronize().animationEnd(); } - public void movePieceStart(UUID uuid, int nodeIndex){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); + /** + * Starts the movement of a piece to a target node based on the given index. + * + * @param uuid the UUID of the piece to move + * @param nodeIndex the index of the target node to move the piece to + * @throws RuntimeException if the UUID is not mapped to a color or the piece control is not found + * @throws IllegalArgumentException if the node index is invalid + */ + private void movePieceStart(UUID uuid, int nodeIndex){ + // Farbe des Pieces abrufen Color color = pieceColor.get(uuid); - if(color == null) throw new RuntimeException("uuid is not mapped to a color"); + if (color == null) throw new RuntimeException("UUID is not mapped to a color"); + // PieceControl abrufen PieceControl pieceControl = pieces.get(uuid); - movePieceToNode(pieceControl, infield.get(nodeIndex)); + if (pieceControl == null) throw new RuntimeException("PieceControl not found for UUID: " + uuid); + + // Zielknoten abrufen und prüfen + if (nodeIndex < 0 || nodeIndex >= infield.size()) { + throw new IllegalArgumentException("Invalid nodeIndex: " + nodeIndex); + } + NodeControl targetNode = infield.get(nodeIndex); + + movePieceToNode(pieceControl, targetNode); removeItemFromMapList(waitingPiecesMap, color, pieceControl); + waitingNodes.get(color).remove(uuid); + app.getModelSynchronize().animationEnd(); } - public void movePiece(UUID uuid, int curIndex, int moveIndex){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); + /** + * Moves a piece from its current position to the target position based on the given indexes. + * + * @param uuid the UUID of the piece to move + * @param curIndex the current index of the piece + * @param moveIndex the target index of the move + */ + private void movePiece(UUID uuid, int curIndex, int moveIndex){ movePieceRek(uuid, curIndex, moveIndex); + app.getModelSynchronize().animationEnd(); } - public void throwPiece(UUID uuid){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); + /** + * Throws a piece to the next available waiting node and updates the waiting node mapping. + * + * @param uuid the UUID of the piece to throw + * @throws RuntimeException if the UUID is not mapped to a color or if no available waiting nodes are found + */ + private void throwPiece(UUID uuid){ + // Farbe des Pieces abrufen Color color = pieceColor.get(uuid); - if(color == null) throw new RuntimeException("uuid is not mapped to a color"); - + if (color == null) throw new RuntimeException("UUID is not mapped to a color"); + // PieceControl abrufen PieceControl pieceControl = pieces.get(uuid); - List waitNodes = waitingNodesMap.get(color); - List waitPieces = waitingPiecesMap.get(color); + if (pieceControl == null) throw new RuntimeException("PieceControl not found for UUID: " + uuid); - movePieceToNode(pieceControl, waitNodes.get(waitPieces.size())); + // Nächste freie Waiting Node abrufen + NodeControl nextWaitNode = getNextWaitingNode(color); + if (nextWaitNode == null) { + throw new IllegalStateException("No available waiting nodes for color: " + color); + } + + // Bewegung durchführen + movePieceToNode(pieceControl, nextWaitNode); + + // Waiting Nodes aktualisieren + waitingNodes.get(color).put(uuid, nextWaitNode); + + // Synchronisation oder Animation pieceControl.rotateInit(); + app.getModelSynchronize().animationEnd(); } + + /** + * Activates the shield for the specified piece. + * + * @param uuid the UUID of the piece to shield + */ public void shieldPiece(UUID uuid){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); pieces.get(uuid).activateShield(); } + /** + * Deactivates the shield for the specified piece. + * + * @param uuid the UUID of the piece to unshield + */ public void unshieldPiece(UUID uuid){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); pieces.get(uuid).deactivateShield(); } + /** + * Suppresses the shield for the specified piece. + * + * @param uuid the UUID of the piece to suppress the shield + */ public void suppressShield(UUID uuid){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); pieces.get(uuid).suppressShield(); } - public void swapPieces(UUID piece1, UUID piece2){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); + /** + * Swaps the positions and rotations of two pieces. + * + * @param p1 the first piece to swap + * @param p2 the second piece to swap + * @param loc1 the original location of the first piece + * @param rot1 the original rotation of the first piece + * @param loc2 the original location of the second piece + * @param rot2 the original rotation of the second piece + */ + private void swapPieces(PieceControl p1, PieceControl p2, Vector3f loc1, float rot1, Vector3f loc2, float rot2){ + p1.setLocation(loc2); + p2.setLocation(loc1); - PieceControl piece1Control = pieces.get(piece1); - PieceControl piece2Control = pieces.get(piece2); + p1.setRotation(rot2); + p2.setRotation(rot1); - if(piece1Control == null) throw new RuntimeException("piece1 UUID is not valid"); - if(piece2Control == null) throw new RuntimeException("piece2 UUID is not valid"); - - float rot1 = piece1Control.getRotation(); - float rot2 = piece2Control.getRotation(); - - piece1Control.setRotation(rot2); - piece2Control.setRotation(rot1); - - Vector3f pos1 = piece1Control.getLocation().clone(); - Vector3f pos2 = piece2Control.getLocation().clone(); - - piece1Control.setLocation(pos2); - piece2Control.setLocation(pos1); + app.getModelSynchronize().animationEnd(); } - public void highlight(UUID uuid, boolean bool){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); - - pieces.get(uuid).highlight(bool); - pieces.get(uuid).setSelectable(bool); - - } - - //called when (dice) moveNum is received from server to display the movable pieces and corresponding moveNodes + /** + * Outlines the possible move nodes for a list of pieces based on the move indices and whether it's a home move. + * + * @param pieces the list of UUIDs representing the pieces to outline + * @param moveIndexe the list of indices for the target move nodes + * @param homeMoves the list indicating whether the move is a home move + * @throws RuntimeException if the sizes of the input lists do not match + */ public void outlineMove(List pieces, List moveIndexe, List homeMoves) { if(pieces.size() != moveIndexe.size() || pieces.size() != homeMoves.size()) throw new RuntimeException("arrays are not the same size"); @@ -370,7 +534,12 @@ public void outlineMove(List pieces, List moveIndexe, List ownPieces, List enemyPieces){ selectableEnemyPieces.clear(); @@ -394,6 +563,11 @@ public void outlineSwap(List ownPieces, List enemyPieces){ } } + /** + * Outlines the pieces that can be shielded based on the provided list of pieces. + * + * @param pieces the list of UUIDs representing the pieces to be shielded + */ public void outlineShield(List pieces){ selectableOwnPieces.clear(); selectableEnemyPieces.clear(); @@ -409,7 +583,11 @@ public void outlineShield(List pieces){ } } - //called from inputSynchronizer when a piece is selectable + /** + * Selects a piece from either the own or enemy pieces based on the input and deselects others if needed. + * + * @param pieceSelected the PieceControl instance representing the piece selected by the user + */ public void pieceSelect(PieceControl pieceSelected) { boolean isSelected = pieceSelected.isSelected(); if(selectableOwnPieces.contains(pieceSelected)){ @@ -443,7 +621,9 @@ else if(selectableEnemyPieces.contains(pieceSelected)) { app.getModelSynchronize().select(getKeyByValue(pieces, selectedOwnPiece), getKeyByValue(pieces, selectedEnemyPiece)); } - //called when view is no longer needed to select pieces + /** + * Clears all highlighted, selectable, and selected pieces and nodes. + */ public void clearSelectable(){ for(PieceControl p : selectableEnemyPieces) { p.unSelect(); @@ -465,16 +645,20 @@ public void clearSelectable(){ selectedOwnPiece = null; } - public void enableHover(UUID uuid){ - pieces.get(uuid).setHoverable(true); - } - + /** + * Displays the dice for the specified color at the appropriate position. + * + * @param color the color of the player whose dice should be displayed + */ public void showDice(Color color){ rootNodeBoard.attachChild(diceControl.getSpatial()); diceControl.setPos(getWaitingPos(color).add(new Vector3f(0,0,4))); diceControl.spin(); } + /** + * Hides the dice from the view. + */ public void hideDice(){ diceControl.hide(); } @@ -488,4 +672,107 @@ private K getKeyByValue(Map map, V value) { return null; } + /** + * Animates the movement of a piece from its current index to a target index. + * + * @param uuid the UUID of the piece to animate + * @param curIndex the current index of the piece + * @param moveIndex the target index to animate the piece to + */ + public void movePieceAnim(UUID uuid, int curIndex, int moveIndex){ + pieces.get(uuid).getSpatial().addControl(new MoveControl( + infield.get(curIndex).getLocation(), + infield.get(moveIndex).getLocation(), + ()->movePiece(uuid,curIndex,moveIndex))); + } + + /** + * Animates the movement of a piece to its home position based on the given home index. + * + * @param uuid the UUID of the piece to animate + * @param homeIndex the index of the home node to move the piece to + */ + public void movePieceHomeAnim(UUID uuid, int homeIndex){ + pieces.get(uuid).getSpatial().addControl(new MoveControl( + pieces.get(uuid).getLocation(), + homeNodesMap.get(pieceColor.get(uuid)).get(homeIndex).getLocation(), + ()->moveHomePiece(uuid,homeIndex))); + } + + /** + * Animates the start of the movement of a piece to a target index. + * + * @param uuid the UUID of the piece to animate + * @param moveIndex the target index to animate the piece to + */ + public void movePieceStartAnim(UUID uuid, int moveIndex){ + pieces.get(uuid).getSpatial().addControl(new MoveControl( + pieces.get(uuid).getLocation(), + infield.get(moveIndex).getLocation(), + ()->movePieceStart(uuid, moveIndex) + )); + } + + /** + * Animates the throwing of a piece to the next available waiting node. + * + * @param uuid the UUID of the piece to animate + */ + public void throwPieceAnim(UUID uuid){ + pieces.get(uuid).getSpatial().addControl(new MoveControl( + pieces.get(uuid).getLocation(), + getNextWaitingNode(pieceColor.get(uuid)).getLocation(), + ()->throwPiece(uuid) + )); + } + + /** + * Animates the swapping of two pieces by swapping their positions and rotations. + * + * @param piece1 the UUID of the first piece + * @param piece2 the UUID of the second piece + */ + public void swapPieceAnim(UUID piece1, UUID piece2){ + PieceControl piece1Control = pieces.get(piece1); + PieceControl piece2Control = pieces.get(piece2); + + Vector3f loc1 = piece1Control.getLocation().clone(); + Vector3f loc2 = piece2Control.getLocation().clone(); + float rot1 = piece1Control.getRotation(); + float rot2 = piece2Control.getRotation(); + + piece1Control.getSpatial().addControl(new MoveControl( + piece1Control.getLocation().clone(), + piece2Control.getLocation().clone(), + ()->{} + )); + piece2Control.getSpatial().addControl(new MoveControl( + piece2Control.getLocation().clone(), + piece1Control.getLocation().clone(), + ()->swapPieces(piece1Control,piece2Control,loc1,rot1,loc2,rot2) + )); + } + + /** + * Retrieves the next available waiting node for the specified color. + * + * @param color the color of the player to get the next waiting node for + * @return the next available NodeControl for the specified color + * @throws IllegalStateException if no available waiting nodes are found for the color + */ + private NodeControl getNextWaitingNode(Color color) { + List nodes = waitingNodesMap.get(color); + + if (nodes == null || nodes.isEmpty()) { + throw new IllegalStateException("Keine verfügbaren Warteschleifen-Knoten für die Farbe " + color); + } + + for (NodeControl node : nodes) { + if (!waitingNodes.getOrDefault(color, new HashMap<>()).containsValue(node)) { + return node; + } + } + + throw new IllegalStateException("Keine freien Nodes im Wartebereich für die Farbe " + color); + } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java index c0469eef..57da8393 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java @@ -15,6 +15,10 @@ import pp.mdga.client.MdgaApp; import pp.mdga.game.Color; +/** + * Handles the camera position, rotation, and lighting effects for the game. + * Provides methods for camera initialization, updates based on user input, and shutdown operations. + */ public class CameraHandler { MdgaApp app; @@ -34,6 +38,12 @@ public class CameraHandler { private boolean init; private boolean initRot; + /** + * Constructor for the CameraHandler. Initializes the camera settings and lighting. + * + * @param app The main application instance that provides the camera and root node. + * @param fpp The FilterPostProcessor used for post-processing effects. + */ public CameraHandler(MdgaApp app, FilterPostProcessor fpp) { init = false; initRot = false; @@ -61,6 +71,12 @@ public CameraHandler(MdgaApp app, FilterPostProcessor fpp) { } + /** + * Initializes the camera with a specific color orientation. + * Adds lights, sky, and shadow filters to the scene. + * + * @param ownColor The color that defines the initial camera view angle. + */ public void init(Color ownColor) { app.getRootNode().addLight(sun); app.getRootNode().addLight(ambient); @@ -72,6 +88,10 @@ public void init(Color ownColor) { app.getInputSynchronize().setRotation(getInitAngleByColor(ownColor)*2); } + /** + * Shuts down the camera handler by removing all lights, sky, and filters, + * and resets the camera position and rotation to its default state. + */ public void shutdown() { app.getRootNode().removeLight(sun); app.getRootNode().removeLight(ambient); @@ -84,6 +104,13 @@ public void shutdown() { fpp.removeFilter(dlsf); } + /** + * Updates the camera position and rotation based on user input (scroll and rotation). + * Adjusts the vertical angle and radius based on zoom and rotation values. + * + * @param scroll The scroll input, determining zoom level. + * @param rotation The rotation input, determining camera orientation. + */ public void update(float scroll, float rotation) { if(!init) return; float scrollValue = Math.max(0, Math.min(scroll, 100)); @@ -117,6 +144,12 @@ public void update(float scroll, float rotation) { app.getCamera().lookAt(Vector3f.ZERO, Vector3f.UNIT_Z); } + /** + * Returns the camera angle based on the specified color. + * + * @param color The color used to determine the camera angle. + * @return The camera angle in degrees. + */ private float getAngleByColor(Color color){ return switch (color){ case ARMY -> 0; @@ -127,6 +160,12 @@ private float getAngleByColor(Color color){ }; } + /** + * Returns the initial camera angle based on the specified color. + * + * @param color The color used to determine the camera angle. + * @return The initial camera angle in degrees. + */ private float getInitAngleByColor(Color color){ return (getAngleByColor(color) + 180) % 360; } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java index c85a7dcc..9d283f8f 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java @@ -10,11 +10,27 @@ import java.util.ArrayList; import java.util.List; +/** + * A utility class for loading and parsing map data from a file. + * The map contains asset names and coordinates for objects placed on the map. + */ class MapLoader { + /** + * Private constructor to prevent instantiation. + */ private MapLoader() { } + /** + * Loads a map file and parses its contents into a list of assets and their positions. + * Each line in the map file defines an asset, its coordinates, and its rotation. + * + * @param mapName The name of the map file to load. The file is expected to be located in the resources directory. + * @return A list of {@link AssetOnMap} objects representing the assets placed on the map. + * @throws IOException If an error occurs while reading the map file. + * @throws IllegalArgumentException If the map file contains invalid data. + */ public static List loadMap(String mapName) { List assetsOnMap = new ArrayList<>(); @@ -60,6 +76,13 @@ public static List loadMap(String mapName) { return assetsOnMap; } + /** + * Returns the corresponding {@link Asset} for a given asset name. + * + * @param assetName The name of the asset to load. + * @return The {@link Asset} associated with the given name. + * @throws IllegalStateException If the asset name is unrecognized. + */ private static Asset getLoadedAsset(String assetName) { return switch (assetName) { case "lw" -> Asset.lw; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/NodeControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/NodeControl.java index 5288b537..d3f1d80e 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/NodeControl.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/NodeControl.java @@ -8,19 +8,40 @@ import com.jme3.scene.control.AbstractControl; import pp.mdga.client.MdgaApp; +/** + * A control that adds highlighting functionality to a node in the game. + * This class extends {@link OutlineControl} to add an outline effect when the node is highlighted. + */ public class NodeControl extends OutlineControl { private static final ColorRGBA OUTLINE_HIGHLIGHT_COLOR = ColorRGBA.White; private static final int OUTLINE_HIGHLIGHT_WIDTH = 6; + /** + * Constructs a {@link NodeControl} with the specified application and post processor. + * This constructor sets up the necessary elements for highlighting functionality. + * + * @param app The {@link MdgaApp} instance to use for the application context. + * @param fpp The {@link FilterPostProcessor} to apply post-processing effects. + */ public NodeControl(MdgaApp app, FilterPostProcessor fpp) { super(app, fpp); } + /** + * Returns the location of the node in 3D space. + * This is the node's local translation in the scene. + * + * @return The {@link Vector3f} representing the node's location. + */ public Vector3f getLocation(){ return this.getSpatial().getLocalTranslation(); } + /** + * Highlights the node by applying an outline effect. + * The outline color and width are predefined as white and 6, respectively. + */ public void highlight() { super.outline(OUTLINE_HIGHLIGHT_COLOR, OUTLINE_HIGHLIGHT_WIDTH); } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/OutlineControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/OutlineControl.java index 7872dbe4..f0dfba51 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/OutlineControl.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/OutlineControl.java @@ -3,16 +3,19 @@ import com.jme3.math.ColorRGBA; import com.jme3.post.FilterPostProcessor; import com.jme3.renderer.Camera; -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.scene.Spatial; -import com.jme3.scene.control.AbstractControl; import pp.mdga.client.MdgaApp; -import pp.mdga.client.board.outline.SelectObjectOutliner; +import pp.mdga.client.InitControl; +import pp.mdga.client.outline.SelectObjectOutliner; -public class OutlineControl extends AbstractControl { - private static final int THICKNESS_DEFAULT = 6; +/** + * A control that provides outline functionality to a spatial object. + * This class is responsible for adding an outline effect to a spatial + * object, allowing it to be highlighted or deselected. + */ +public class OutlineControl extends InitControl { + /** The {@link SelectObjectOutliner} responsible for managing the outline effect. */ private final SelectObjectOutliner outlineOwn; + private static final int THICKNESS_DEFAULT = 6; private MdgaApp app; @@ -31,44 +34,33 @@ public OutlineControl(MdgaApp app, FilterPostProcessor fpp, Camera cam, int thic outlineOwn = new SelectObjectOutliner(thickness, fpp, app.getRenderManager(), app.getAssetManager(), cam, app); } + /** + * Applies an outline to the spatial object with the given color. + * + * @param color The {@link ColorRGBA} representing the color of the outline. + */ public void outline(ColorRGBA color){ outlineOwn.select(spatial, color); } + /** + * Applies an outline to the spatial object with the given color and width. + * + * @param color The {@link ColorRGBA} representing the color of the outline. + * @param width The width of the outline. + */ public void outline(ColorRGBA color, int width){ deOutline(); outlineOwn.select(spatial, color, width); } + /** + * Removes the outline effect from the spatial object. + */ public void deOutline(){ outlineOwn.deselect(spatial); } - @Override - protected void controlUpdate(float tpf) { - - } - - @Override - protected void controlRender(RenderManager rm, ViewPort vp) { - - } - - public void initSpatial(){ - - } - - @Override - public void setSpatial(Spatial spatial){ - if(this.spatial == null && spatial != null){ - super.setSpatial(spatial); - initSpatial(); - } - else{ - super.setSpatial(spatial); - } - } - public MdgaApp getApp() { return app; } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PieceControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PieceControl.java index d9b22030..4a75533d 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PieceControl.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PieceControl.java @@ -14,6 +14,12 @@ import pp.mdga.client.Asset; import pp.mdga.client.MdgaApp; +/** + * A control that manages the behavior and properties of a game piece, such as its rotation, + * position, shield activation, and highlighting. This class extends {@link OutlineControl} + * to provide outline functionality and includes additional features like shield effects, + * hover states, and selection states. + */ public class PieceControl extends OutlineControl { private final float initRotation; private final AssetManager assetManager; @@ -43,7 +49,15 @@ public class PieceControl extends OutlineControl { private boolean selectable; private boolean select; - + /** + * Constructs a {@link PieceControl} with the specified initial rotation, asset manager, + * application, and post-processor. + * + * @param initRotation The initial rotation of the piece in degrees. + * @param assetManager The {@link AssetManager} used for loading models and materials. + * @param app The {@link MdgaApp} instance to use for the application context. + * @param fpp The {@link FilterPostProcessor} to apply post-processing effects. + */ public PieceControl(float initRotation, AssetManager assetManager, MdgaApp app, FilterPostProcessor fpp){ super(app, fpp); this.parentNode = new Node(); @@ -59,10 +73,20 @@ public PieceControl(float initRotation, AssetManager assetManager, MdgaApp app, select = false; } + /** + * Gets the current rotation of the piece in degrees. + * + * @return The rotation of the piece in degrees. + */ public float getRotation() { return (float) Math.toDegrees(spatial.getLocalRotation().toAngleAxis(new Vector3f(0,0,1))); } + /** + * Sets the rotation of the piece to the specified value in degrees. + * + * @param rot The rotation in degrees to set. + */ public void setRotation(float rot){ if(rot < 0) rot =- 360; @@ -71,10 +95,20 @@ public void setRotation(float rot){ spatial.setLocalRotation(quaternion); } + /** + * Gets the current location (position) of the piece. + * + * @return The location of the piece as a {@link Vector3f}. + */ public Vector3f getLocation(){ return spatial.getLocalTranslation(); } + /** + * Updates the piece control every frame. If the shield is active, it will rotate. + * + * @param delta The time difference between frames (time per frame). + */ @Override protected void controlUpdate(float delta) { if(shieldRing != null){ @@ -82,10 +116,19 @@ protected void controlUpdate(float delta) { } } + /** + * Sets the location (position) of the piece. + * + * @param loc The location to set as a {@link Vector3f}. + */ public void setLocation(Vector3f loc){ this.spatial.setLocalTranslation(loc); } + /** + * Initializes the spatial object and sets its rotation. + * This also moves the spatial to a new parent node for organizational purposes. + */ @Override public void initSpatial(){ setRotation(this.initRotation); @@ -101,6 +144,10 @@ public void rotateInit() { // rotate(rotation - initRotation); } + /** + * Activates the shield around the piece. + * This adds a visual shield effect in the form of a rotating ring. + */ public void activateShield(){ shieldRing = assetManager.loadModel(Asset.shieldRing.getModelPath()); shieldRing.scale(1f); @@ -115,11 +162,18 @@ public void activateShield(){ parentNode.attachChild(shieldRing); } + /** + * Deactivates the shield by removing the shield ring from the scene. + */ + public void deactivateShield(){ parentNode.detachChild(shieldRing); shieldRing = null; } + /** + * Suppresses the shield, changing its color to a suppressed state. + */ public void suppressShield(){ assert(shieldRing != null) : "PieceControl: shieldRing is not set"; shieldMat.setColor("Color", SHIELD_SUPPRESSED_COLOR); @@ -133,22 +187,36 @@ public Material getMaterial(){ return ((Geometry) getSpatial()).getMaterial(); } + /** + * Highlights the piece with the appropriate outline color based on whether it is an enemy or not. + * + * @param enemy True if the piece is an enemy, false if it is owned by the player. + */ public void highlight(boolean enemy) { this.enemy = enemy; highlight = true; super.outline(enemy ? OUTLINE_ENEMY_COLOR : OUTLINE_OWN_COLOR, OUTLINE_HIGHLIGHT_WIDTH); } + /** + * Removes the highlight effect from the piece. + */ public void unHighlight(){ highlight = false; deOutline(); } + /** + * Applies a hover effect on the piece if it is hoverable. + */ public void hover(){ if(!hoverable) return; super.outline(enemy ? OUTLINE_ENEMY_HOVER_COLOR : OUTLINE_OWN_HOVER_COLOR, OUTLINE_HOVER_WIDTH); } + /** + * Removes the hover effect from the piece. + */ public void hoverOff(){ if(!hoverable) return; @@ -157,28 +225,56 @@ public void hoverOff(){ else deOutline(); } + /** + * Deselects the piece and removes the selection outline. If the piece was highlighted, + * it will be re-highlighted. Otherwise, the outline is removed. + */ public void unSelect(){ select = false; if(highlight) highlight(enemy); else deOutline(); } + /** + * Selects the piece and applies the selection outline. If the piece is an enemy, it will + * be outlined with the enemy selection color; otherwise, the own selection color will be used. + */ public void select(){ if(!selectable) return; select = true; super.outline(enemy ? OUTLINE_ENEMY_SELECT_COLOR : OUTLINE_OWN_SELECT_COLOR, OUTLINE_SELECT_WIDTH); } + /** + * Sets whether the piece is selectable. + * + * @param selectable True if the piece can be selected, false otherwise. + */ public void setSelectable(boolean selectable){ this.selectable = selectable; } + /** + * Checks if the piece is selected. + * + * @return True if the piece is selected, false otherwise. + */ public boolean isSelected() { return select; } + /** + * Checks if the piece is selectable. + * + * @return True if the piece is selectable, false otherwise. + */ public boolean isSelectable() { return selectable; } + /** + * Sets whether the piece is hoverable. + * + * @param hoverable True if the piece can be hovered over, false otherwise. + */ public void setHoverable(boolean hoverable) { this.hoverable = hoverable; } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PileControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PileControl.java deleted file mode 100644 index f4e604c6..00000000 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PileControl.java +++ /dev/null @@ -1,5 +0,0 @@ -package pp.mdga.client.board; - -class PileControl { - -} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Rotation.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Rotation.java deleted file mode 100644 index 5db07057..00000000 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Rotation.java +++ /dev/null @@ -1,12 +0,0 @@ -package pp.mdga.client.board; - -public enum Rotation { - UP, - RIGHT, - DOWN, - LEFT, - UP_LEFT, - UP_RIGHT, - DOWN_RIGHT, - DOWN_LEFT -} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java index 1b868163..b5bd3571 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java @@ -7,22 +7,23 @@ import com.jme3.math.Vector3f; import com.jme3.scene.Node; import com.jme3.system.AppSettings; -import pp.mdga.client.Asset; +import pp.mdga.client.animation.ZoomControl; import pp.mdga.game.Color; -public class ActionTextHandler { + class ActionTextHandler { private Node root; private BitmapFont font; private AppSettings appSettings; + private int ranking; - public ActionTextHandler(Node guiNode, AssetManager assetManager, AppSettings appSettings){ + ActionTextHandler(Node guiNode, AssetManager assetManager, AppSettings appSettings){ root = new Node("actionTextRoot"); guiNode.attachChild(root); root.setLocalTranslation(center(appSettings.getWidth(), appSettings.getHeight(), Vector3f.ZERO)); font = assetManager.loadFont("Fonts/Gunplay.fnt"); this.appSettings = appSettings; - + ranking = 0; } private Node createTextWithSpacing(String[] textArr, float spacing, float size, ColorRGBA[] colorArr) { @@ -74,48 +75,48 @@ private Vector3f centerText(float width, float height, Vector3f pos){ return center(-width, height, pos); } - public void activePlayer(String name, Color color){ + void activePlayer(String name, Color color){ createTopText(new String[]{name," ist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void ownActive(Color color){ + void ownActive(Color color){ createTopText(new String[]{"Du"," bist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void diceNum(int diceNum, String name, Color color){ + void diceNum(int diceNum, String name, Color color){ createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0); createTopText(String.valueOf(diceNum), 10, 100, ColorRGBA.White, 100); } - public void diceNumMult(int diceNum,int mult, String name, Color color){ + void diceNumMult(int diceNum,int mult, String name, Color color){ createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0); createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 100); } - public void ownDice(int diceNum){ + void ownDice(int diceNum){ createTopText(String.valueOf(diceNum), 10, 100, ColorRGBA.White, 0); } - public void ownDiceMult(int diceNum, int mult){ + void ownDiceMult(int diceNum, int mult){ createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 0); } - public void drawCard(String name, Color color){ + void drawCard(String name, Color color){ createTopText(new String[]{name," erhält eine Bonuskarte"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void drawCardOwn(Color color){ + void drawCardOwn(Color color){ createTopText(new String[]{"Du"," erhälst eine Bonuskarte"}, 5,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void finishText(String name, Color color){ + void finishText(String name, Color color){ createTopText(new String[]{name," ist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } - public void finishTextOwn(Color color){ + void finishTextOwn(Color color){ createTopText(new String[]{"Du", " bist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl()); } @@ -131,9 +132,25 @@ private ColorRGBA playerColorToColorRGBA(Color color){ }; } - public void hide(){ - root.detachAllChildren(); + void hide(){ + ranking = 0; + root.detachAllChildren(); } + float paddingRanked = 100; + + void rollRankingResult(String name, Color color, int eye){ + createTopText(new String[]{name,": "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking); + ranking++; + } + + void rollRankingResultOwn(Color color, int eye){ + createTopText(new String[]{"Du",": "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking); + ranking++; + } + + void diceNow(){ + createTopText("Klicke zum Würfeln", 5, 80, ColorRGBA.White, 0); + } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayerHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayerHandler.java index fede1dba..116e0987 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayerHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/CardLayerHandler.java @@ -10,6 +10,7 @@ import com.jme3.texture.Texture2D; import pp.mdga.client.Asset; import pp.mdga.client.MdgaApp; +import pp.mdga.client.animation.SymbolControl; import pp.mdga.game.BonusCard; import java.util.*; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java index 8761e3da..d4128081 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/GuiHandler.java @@ -67,6 +67,7 @@ public void showRolledDice(int rollNum, Color color) { public void showDice() { cardLayerHandler.showDice(); + actionTextHandler.diceNow(); } public void hideDice() { @@ -134,5 +135,10 @@ public void finish(Color color){ else actionTextHandler.finishText(playerNameHandler.getName(color), color); } + public void rollRankingResult(Color color, int eye){ + if(ownColor == color) actionTextHandler.rollRankingResultOwn(color, eye); + else actionTextHandler.rollRankingResult(playerNameHandler.getName(color), color, eye); + } + } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java index 1b197d16..2d4bdbf7 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/PlayerNameHandler.java @@ -120,6 +120,7 @@ public void setActivePlayer(Color color) { } public String getName(Color color){ + if(!colorNameMap.containsKey(color)) throw new RuntimeException("color is not in colorNameMap"); return colorNameMap.get(color); } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ZoomControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ZoomControl.java deleted file mode 100644 index 33b10ea8..00000000 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ZoomControl.java +++ /dev/null @@ -1,84 +0,0 @@ -package pp.mdga.client.gui; - -import com.jme3.renderer.RenderManager; -import com.jme3.renderer.ViewPort; -import com.jme3.scene.Spatial; -import com.jme3.scene.control.AbstractControl; - -public class ZoomControl extends AbstractControl { - private boolean zoomingIn = false; - private boolean zoomingOut = false; - private float progress = 0; - private float zoomSpeed = 1f; - private float zoomFactor = 1f; - - public ZoomControl() { - } - - public ZoomControl(float speed) { - zoomSpeed = speed; - } - - @Override - public void setSpatial(Spatial spatial) { - if (this.spatial == null && spatial != null) { - super.setSpatial(spatial); - initSpatial(); - } - } - - private void initSpatial() { - zoomingIn = true; - } - - @Override - protected void controlUpdate(float tpf) { - if (zoomingIn) { - progress += tpf * zoomSpeed; - if (progress > 1) progress = 1; - spatial.setLocalScale(lerp(0, zoomFactor, easeOut(progress))); - if (progress >= 1) { - zoomingIn = false; - zoomingOut = true; - progress = 0; - } - } else if (zoomingOut) { - progress += tpf * zoomSpeed; - spatial.setLocalScale(lerp(zoomFactor, 0, easeIn(progress))); - if (progress > 1) { - zoomingOut = false; - end(); - } - } - } - - private void end() { - spatial.removeFromParent(); - spatial.removeControl(this); - } - - @Override - protected void controlRender(RenderManager rm, ViewPort vp) { - - } - - private static float lerp(float start, float end, float t) { - return (1 - t) * start + t * end; - } - - // private static float easeOut(float t) { -// return (float) Math.sqrt(1 - Math.pow(t - 1, 2)); -// } - private float easeOut(float x) { - return x == 1 ? 1 : (float) (1 - Math.pow(2, -10 * x)); - - } - - // private float easeIn(float t) { -// return t * t * t * t; -// } - private float easeIn(float x) { - return x == 0 ? 0 : (float) Math.pow(2, 10 * x - 10); - - } -} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/OutlineFilter.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/outline/OutlineFilter.java similarity index 98% rename from Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/OutlineFilter.java rename to Projekte/mdga/client/src/main/java/pp/mdga/client/outline/OutlineFilter.java index d9fcaef9..109e2de7 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/OutlineFilter.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/outline/OutlineFilter.java @@ -1,4 +1,4 @@ -package pp.mdga.client.board.outline; +package pp.mdga.client.outline; import com.jme3.asset.AssetManager; import com.jme3.material.Material; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/OutlinePreFilter.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/outline/OutlinePreFilter.java similarity index 97% rename from Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/OutlinePreFilter.java rename to Projekte/mdga/client/src/main/java/pp/mdga/client/outline/OutlinePreFilter.java index 9a385045..cb2ae654 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/OutlinePreFilter.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/outline/OutlinePreFilter.java @@ -1,4 +1,4 @@ -package pp.mdga.client.board.outline; +package pp.mdga.client.outline; import com.jme3.asset.AssetManager; import com.jme3.material.Material; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/OutlineProFilter.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/outline/OutlineProFilter.java similarity index 98% rename from Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/OutlineProFilter.java rename to Projekte/mdga/client/src/main/java/pp/mdga/client/outline/OutlineProFilter.java index c40a9435..c49f6158 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/OutlineProFilter.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/outline/OutlineProFilter.java @@ -1,4 +1,4 @@ -package pp.mdga.client.board.outline; +package pp.mdga.client.outline; import com.jme3.asset.AssetManager; import com.jme3.material.Material; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/SelectObjectOutliner.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/outline/SelectObjectOutliner.java similarity index 98% rename from Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/SelectObjectOutliner.java rename to Projekte/mdga/client/src/main/java/pp/mdga/client/outline/SelectObjectOutliner.java index 3cfbc76c..aa5049a6 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/outline/SelectObjectOutliner.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/outline/SelectObjectOutliner.java @@ -1,4 +1,4 @@ -package pp.mdga.client.board.outline; +package pp.mdga.client.outline; import com.jme3.asset.AssetManager; import com.jme3.math.ColorRGBA; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java index 533dabd7..b8e8bfb4 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/server/MdgaServer.java @@ -2,6 +2,7 @@ import com.jme3.network.*; import com.jme3.network.serializing.Serializer; +import com.jme3.network.serializing.serializers.EnumSerializer; import pp.mdga.game.*; import pp.mdga.message.client.*; import pp.mdga.message.server.*; @@ -12,7 +13,6 @@ import java.io.IOException; import java.lang.System.Logger; import java.lang.System.Logger.Level; -import java.net.InetAddress; import java.util.Map; import java.util.UUID; import java.util.concurrent.BlockingQueue; @@ -130,6 +130,7 @@ private void initializeSerializables() { Serializer.registerClass(ReconnectBriefingMessage.class); Serializer.registerClass(ResumeGameMessage.class); Serializer.registerClass(ServerStartGameMessage.class); + Serializer.registerClass(ShutdownMessage.class); Serializer.registerClass(StartPieceMessage.class); Serializer.registerClass(UpdateReadyMessage.class); Serializer.registerClass(UpdateTSKMessage.class); @@ -143,6 +144,8 @@ private void initializeSerializables() { Serializer.registerClass(StartNode.class); Serializer.registerClass(PlayerData.class); Serializer.registerClass(HomeNode.class); + Serializer.registerClass(PlayerDataMessage.class); + Serializer.registerClass(StartBriefingMessage.class); } private void registerListeners() { diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java index 41a2b412..f28719ac 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/GameView.java @@ -64,6 +64,13 @@ public void onEnter() { app.getViewPort().addProcessor(fpp); app.getAcousticHandler().playSound(MdgaSound.START); + +// guiHandler.addPlayer(Color.AIRFORCE, "Cedric"); +// guiHandler.addPlayer(Color.ARMY, "Ben"); +// guiHandler.addPlayer(Color.CYBER, "Felix"); +// guiHandler.addPlayer(Color.NAVY, "Daniel"); + + } @Override diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java index 0ee82c56..e96cbbb7 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/view/LobbyView.java @@ -228,7 +228,7 @@ private void toggleTsk(Color color) { app.getModelSynchronize().selectTsk(color); break; case SELF: - app.getModelSynchronize().unselectTsk(); + app.getModelSynchronize().unselectTsk(color); break; case OTHER: //nothing diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java index 38541512..cf6e5e5a 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/CeremonyState.java @@ -18,7 +18,7 @@ public CeremonyState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - currentState = podiumState; + setState(podiumState); logic.addNotification(createCeremonyNotification()); } @@ -28,7 +28,9 @@ public void exit() { } public void setState(CeremonyStates state){ - this.currentState.exit(); + if(this.currentState != null){ + this.currentState.exit(); + } state.enter(); currentState = state; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java index eeb76f55..f3a0602b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientGameLogic.java @@ -190,6 +190,19 @@ public void received(ServerStartGameMessage msg) { state.received(msg); } + @Override + public void received(ShutdownMessage msg) {state.received(msg);} + + @Override + public void received(StartBriefingMessage msg) { + state.received(msg); + } + + @Override + public void received(PlayerDataMessage msg) { + state.received(msg); + } + @Override public void received(StartPieceMessage msg) { state.received(msg); @@ -236,6 +249,10 @@ public void selectTsk(Color color){ state.selectTSK(color); } + public void deselectTSK(Color color){ + state.deselectTSK(color); + } + public void selectDice(){ state.selectDice(); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java index e53a5d92..ae500c42 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/ClientState.java @@ -150,6 +150,9 @@ public void received(ServerStartGameMessage msg) { LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); } + @Override + public void received(ShutdownMessage msg) {LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg);} + @Override public void received(StartPieceMessage msg) { LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); @@ -180,6 +183,15 @@ public void received(WaitPieceMessage msg) { LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); } + @Override + public void received(StartBriefingMessage msg) { + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + } + + public void received(PlayerDataMessage msg) { + LOGGER.log(Level.DEBUG, "Received {0} not allowed.", msg); + } + public void selectPiece(Piece piece) { LOGGER.log(Level.DEBUG, "Selecting piece not allowed."); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java index 5f7cf182..c8578cd3 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/DialogsState.java @@ -30,13 +30,15 @@ public void exit(){ @Override public void enter(){ - currentState = startDialogState; + setState(startDialogState); ownPlayerID = 0; ownPlayerName = null; } public void setState(DialogStates newState){ - currentState.exit(); + if(currentState != null){ + currentState.exit(); + } newState.enter(); currentState = newState; } @@ -69,11 +71,6 @@ public StartDialogState getStartDialog() { return startDialogState; } - public void startGame(){ - exit(); - logic.setState(logic.getGameState()); - } - @Override public void selectLeave(){ currentState.selectLeave(); @@ -144,6 +141,16 @@ public void received(ServerStartGameMessage msg){ currentState.received(msg); } + @Override + public void received(PlayerDataMessage msg){ + currentState.received(msg); + } + + @Override + public void received(StartBriefingMessage msg){ + currentState.received(msg); + } + public DialogStates getState() { return currentState; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java index af449db8..31b3f340 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/GameState.java @@ -27,7 +27,6 @@ public class GameState extends ClientState { */ public GameState(ClientState parent, ClientGameLogic logic) { super(parent, logic); - state = determineStartPlayerState; } /** @@ -35,7 +34,7 @@ public GameState(ClientState parent, ClientGameLogic logic) { */ @Override public void enter() { - + this.setState(this.determineStartPlayerState); } /** @@ -52,8 +51,10 @@ public void exit() { * @param newState the state to be set */ public void setState(GameStates newState){ - state.exit(); - state.enter(); + if(this.state != null){ + this.state.exit(); + } + newState.enter(); state = newState; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java index 7678624a..2d48cbe1 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/LobbyState.java @@ -10,7 +10,9 @@ import pp.mdga.message.client.*; import pp.mdga.message.server.LobbyPlayerJoinedMessage; import pp.mdga.message.server.LobbyPlayerLeaveMessage; +import pp.mdga.message.server.PlayerDataMessage; import pp.mdga.message.server.ServerStartGameMessage; +import pp.mdga.message.server.StartBriefingMessage; import pp.mdga.message.server.UpdateReadyMessage; import pp.mdga.message.server.UpdateTSKMessage; import pp.mdga.notification.*; @@ -74,17 +76,26 @@ public void selectStart(){ } @Override - public void received(ServerStartGameMessage msg){ + public void received(StartBriefingMessage msg){ logic.getGame().setBoard(msg.getBoard()); - logic.addNotification(new GameNotification(logic.getGame().getPlayers().get(parent.getOwnPlayerId()).getColor())); - for(Map.Entry entry : msg.getBoard().getPlayerData().entrySet()){ - List pieceIds = new ArrayList<>(); + } + + public void received(PlayerDataMessage msg){ + logic.getGame().getBoard().addPlayerData(msg.getColor(), msg.getPlayerData()); + } + + @Override + public void received(ServerStartGameMessage msg){ + logic.addNotification(new GameNotification(logic.getGame().getPlayerById(parent.getOwnPlayerId()).getColor())); + for (Map.Entry entry : logic.getGame().getBoard().getPlayerData().entrySet()) { + List pieceList = new ArrayList<>(); for(Piece piece : entry.getValue().getPieces()){ - pieceIds.add(piece.getUuid()); + System.out.println(piece.getUuid()); + pieceList.add(piece.getUuid()); } - logic.addNotification(new PlayerInGameNotification(entry.getKey(), pieceIds, logic.getGame().getPlayerByColor(entry.getKey()).getName())); + logic.addNotification(new PlayerInGameNotification(entry.getKey(), pieceList , logic.getGame().getPlayerByColor(entry.getKey()).getName())); } - parent.startGame(); + logic.setState(logic.getGameState()); } @Override @@ -95,15 +106,20 @@ public void received(LobbyPlayerJoinedMessage msg){ if (msg.isHost() && msg.getId() == parent.getOwnPlayerId()){ logic.setHost(true); } - logic.addNotification(new TskSelectNotification(msg.getPlayer().getColor(), msg.getPlayer().getName(), parent.getOwnPlayerId()== msg.getId())); + + logic.addNotification(new TskSelectNotification(msg.getPlayer().getColor(), msg.getPlayer().getName(), msg.getPlayer().getName().equals(parent.getOwnPlayerName()))); logic.getGame().getPlayers().put(msg.getId(), msg.getPlayer()); } @Override public void received(UpdateTSKMessage msg){ - logic.addNotification(new TskUnselectNotification(logic.getGame().getPlayers().get(msg.getId()).getColor())); + if(msg.isTaken()) { + logic.addNotification(new TskSelectNotification(msg.getColor(), logic.getGame().getPlayers().get(msg.getId()).getName(), parent.getOwnPlayerId()== msg.getId())); + } else { + logic.addNotification(new TskUnselectNotification(logic.getGame().getPlayers().get(msg.getId()).getColor())); + } + logic.getGame().getPlayers().get(msg.getId()).setColor(msg.getColor()); - logic.addNotification(new TskSelectNotification(msg.getColor(), logic.getGame().getPlayers().get(msg.getId()).getName(), parent.getOwnPlayerId()== msg.getId())); } @Override diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java index 09212dee..98234319 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/dialogState/NetworkDialogState.java @@ -14,34 +14,6 @@ public NetworkDialogState(ClientState parent, ClientGameLogic logic) { this.parent = (DialogsState) parent; } - private boolean checkIP(String IP){ - String[] parts = IP.split("\\."); - - // Step 2: Check if there are exactly 4 parts - if (parts.length != 4) { - return false; - } - - // Step 3: Check each part for valid number - for (String part : parts) { - try { - // Step 4: Convert each part into a number - int num = Integer.parseInt(part); - - // Step 5: Check whether the number lies in between 0 and 255 - if (num < 0 || num > 255) { - return false; - } - } catch (NumberFormatException e) { - // If parsing fails, it's not a valid number - return false; - } - } - - // If all checks passed, return true - return true; - } - @Override public void enter() { } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java index cf3c48c2..4794785a 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/DetermineStartPlayerState.java @@ -25,7 +25,7 @@ public DetermineStartPlayerState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - state = rollRankingDiceState; + this.setState(this.rollRankingDiceState); } @Override @@ -34,7 +34,9 @@ public void exit() { } public void setState(DetermineStartPlayerStates state) { - this.state.exit(); + if(this.state != null){ + this.state.exit(); + } state.enter(); this.state = state; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/TurnState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/TurnState.java index b59a35b8..5f075eff 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/TurnState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/TurnState.java @@ -31,7 +31,7 @@ public TurnState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - state = powerCardState; + this.setState(this.powerCardState); } @Override @@ -40,7 +40,9 @@ public void exit() { } public void setState(TurnStates state){ - this.state.exit(); + if(this.state != null){ + this.state.exit(); + } state.enter(); this.state = state; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java index 3c1ec5b9..e6a98522 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/determineStartPlayerState/RollRankingDiceState.java @@ -6,6 +6,7 @@ import pp.mdga.message.client.RequestDieMessage; import pp.mdga.message.server.DieMessage; import pp.mdga.notification.DiceNowNotification; +import pp.mdga.notification.RollDiceNotification; public class RollRankingDiceState extends DetermineStartPlayerStates { @@ -23,16 +24,17 @@ public void enter() { @Override public void exit() { - } @Override public void selectDice(){ + System.out.println("selectDice"); logic.send(new RequestDieMessage()); } @Override public void received(DieMessage msg){ + logic.addNotification(new RollDiceNotification(logic.getGame().getPlayerById(logic.getDialogs().getOwnPlayerId()).getColor(), msg.getDiceEye(),true)); parent.setState(parent.getWaitRanking()); } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/ChoosePieceState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/ChoosePieceState.java index b1a0c7e3..47dce439 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/ChoosePieceState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/ChoosePieceState.java @@ -24,7 +24,7 @@ public ChoosePieceState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - currentState = noPieceState; + this.setState(this.noPieceState); } @Override @@ -34,9 +34,11 @@ public void exit() { } public void setState(ChoosePieceStates state){ - currentState.exit(); + if(currentState != null){ + currentState.exit(); + } + state.enter(); currentState = state; - currentState.enter(); } @Override diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/PowerCardState.java b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/PowerCardState.java index fd80b68d..5a5fd5c3 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/PowerCardState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/client/gameState/turnState/PowerCardState.java @@ -31,7 +31,7 @@ public PowerCardState(ClientState parent, ClientGameLogic logic) { @Override public void enter() { - state = choosePowerCardState; + this.setState(this.choosePowerCardState); } public void exit() { @@ -40,7 +40,9 @@ public void exit() { } public void setState(PowerCardStates state) { - this.state.exit(); + if(this.state != null){ + this.state.exit(); + } state.enter(); this.state = state; } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java index 64b671db..a01c1247 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/Piece.java @@ -42,7 +42,9 @@ public Piece(Color color, PieceState state, int id) { } private Piece() { - color = null; + color = Color.NONE; + state = PieceState.WAITING; + shield = ShieldState.NONE; } /** diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java index abab1258..780d8df0 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/PieceState.java @@ -1,5 +1,7 @@ package pp.mdga.game; +import com.jme3.network.serializing.Serializable; + /** * Represents the state of a piece. */ @@ -19,5 +21,21 @@ public enum PieceState { /** * The piece is finished. */ - HOMEFINISHED + HOMEFINISHED; + + PieceState(){ + + } + + public static PieceState getPieceStateByIndex(int index){ + if (index < 0 || index >= values().length) { + throw new IllegalArgumentException(""); + } + return values()[index]; + } + + + public PieceState next() { + return values()[(ordinal() + 1) % values().length]; + } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java index 8fcc85ae..cb2a788b 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/PlayerData.java @@ -43,10 +43,23 @@ public PlayerData(Color color) { } } + /** + * Constructor. + */ private PlayerData() { - homeNodes = null; - waitingArea = null; - pieces = null; + homeNodes = new HomeNode[4]; + waitingArea = new Piece[4]; + pieces = new Piece[4]; + } + + /** + * This method will be used to check if the player is finished. + * ToDo: Currently return always false. Implement logic! + * + * @return true or false. + */ + public boolean isFinished() { + return false; } /** diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/ShieldState.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/ShieldState.java index 6bff3f52..19a09a5d 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/ShieldState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/ShieldState.java @@ -1,8 +1,11 @@ package pp.mdga.game; +import com.jme3.network.serializing.Serializable; + /** * Represents the state of a piece's shield. */ +@Serializable public enum ShieldState { /** * The shield is not active. @@ -15,5 +18,20 @@ public enum ShieldState { /** * The shield is suppressed, when the piece is on a start node. */ - SUPPRESSED + SUPPRESSED; + + ShieldState(){ + + } + + public static ShieldState getShieldStateByIndex(int index){ + if (index < 0 || index >= values().length) { + throw new IllegalArgumentException(""); + } + return values()[index]; + } + + public ShieldState next() { + return values()[(ordinal() + 1) % values().length]; + } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java b/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java index bb203084..24b2c267 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/game/StartNode.java @@ -22,7 +22,7 @@ public StartNode(Color color) { } private StartNode() { - color = null; + color = Color.NONE; } /** diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/PlayerDataMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/PlayerDataMessage.java new file mode 100644 index 00000000..03171e73 --- /dev/null +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/PlayerDataMessage.java @@ -0,0 +1,40 @@ +package pp.mdga.message.server; + +import com.jme3.network.serializing.Serializable; +import pp.mdga.game.Color; +import pp.mdga.game.PlayerData; + +@Serializable +public class PlayerDataMessage extends ServerMessage{ + + private final PlayerData playerData; + private final Color color; + + public PlayerDataMessage(PlayerData playerData, Color color){ + super(); + this.playerData = playerData; + this.color = color; + } + + private PlayerDataMessage(){ + this(null, null); + } + + @Override + public void accept(ServerInterpreter interpreter) { + interpreter.received(this); + } + + @Override + public String getInfoTextKey() { + return ""; + } + + public PlayerData getPlayerData(){ + return playerData; + } + + public Color getColor(){ + return color; + } +} diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerInterpreter.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerInterpreter.java index d7e7487c..4491f9c1 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerInterpreter.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerInterpreter.java @@ -207,4 +207,15 @@ public interface ServerInterpreter { * @param msg the SelectPiece message received. */ void received(SelectPieceMessage msg); + + /** + * Handles a SelectTSK message received from the server. + * + * @param shutdownMessage the SelectTSK message received. + */ + void received(ShutdownMessage shutdownMessage); + + void received(StartBriefingMessage msg); + + void received(PlayerDataMessage msg); } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerStartGameMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerStartGameMessage.java index 37c22e1e..69e3fb96 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerStartGameMessage.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ServerStartGameMessage.java @@ -8,28 +8,13 @@ */ @Serializable public class ServerStartGameMessage extends ServerMessage { - /** - * Create ServerStartGameMessage attributes. - */ - private final Board board; /** * Constructs a new ServerStartGame instance. */ public ServerStartGameMessage() { super(); - this.board = null; } - - /** - * Constructor. - * - * @param board as the complete board of this game as a Board object. - */ - public ServerStartGameMessage(Board board) { - this.board = board; - } - /** * Accepts a visitor to process this message. * @@ -40,15 +25,6 @@ public void accept(ServerInterpreter interpreter) { interpreter.received(this); } - /** - * This method will be used to return board attribute of ServerStartGameMessage class. - * - * @return board as a Board object. - */ - public Board getBoard() { - return this.board; - } - /** * Returns a string representation of this message. * diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ShutdownMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ShutdownMessage.java new file mode 100644 index 00000000..8c898f03 --- /dev/null +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/ShutdownMessage.java @@ -0,0 +1,30 @@ +package pp.mdga.message.server; + +import com.jme3.network.serializing.Serializable; + +/** + * A message sent by the server to inform the clients that the server is shutting down. + */ +@Serializable +public class ShutdownMessage extends ServerMessage { + /** + * Accepts a visitor to process this message. + * + * @param interpreter the visitor to process this message + */ + @Override + public void accept(ServerInterpreter interpreter) { + interpreter.received(this); + } + + /** + * Gets the bundle key of the informational text to be shown at the client. + * This key is used to retrieve the appropriate localized text for display. + * + * @return the bundle key of the informational text + */ + @Override + public String getInfoTextKey() { + return ""; + } +} diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/StartBriefingMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/StartBriefingMessage.java new file mode 100644 index 00000000..ba465990 --- /dev/null +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/StartBriefingMessage.java @@ -0,0 +1,33 @@ +package pp.mdga.message.server; + +import com.jme3.network.serializing.Serializable; +import pp.mdga.game.Board; + +@Serializable +public class StartBriefingMessage extends ServerMessage { + + private final Board board; + + public StartBriefingMessage(Board board) { + super(); + this.board = board; + } + + private StartBriefingMessage() { + this(null); + } + + public Board getBoard() { + return this.board; + } + + @Override + public void accept(ServerInterpreter interpreter) { + interpreter.received(this); + } + + @Override + public String getInfoTextKey() { + return ""; + } +} diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/UpdateTSKMessage.java b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/UpdateTSKMessage.java index bbbdf14b..2da6f682 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/message/server/UpdateTSKMessage.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/message/server/UpdateTSKMessage.java @@ -18,23 +18,26 @@ public class UpdateTSKMessage extends ServerMessage { */ private final Color color; + private final boolean isTaken; + /** * Constructs a new UpdateTSK instance with the specified id and color. * * @param id the name associated with the update * @param color the color associated with the update */ - public UpdateTSKMessage(int id, Color color) { + public UpdateTSKMessage(int id, Color color, boolean isTaken) { super(); this.id = id; this.color = color; + this.isTaken = isTaken; } /** * Default constructor for serialization purposes. */ private UpdateTSKMessage() { - this(0, null); + this(0, null, false); } /** @@ -84,4 +87,8 @@ public String toString() { public String getInfoTextKey() { return ""; } + + public boolean isTaken() { + return isTaken; + } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java b/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java index 13bf849b..2e6f03e1 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/notification/RollDiceNotification.java @@ -11,6 +11,7 @@ public class RollDiceNotification extends Notification{ private int eyes; private boolean turbo; private int multiplier; + private boolean isRanking; /** * Constructor. @@ -22,6 +23,15 @@ public RollDiceNotification(Color color, int eyes) { this.eyes = eyes; this.turbo = false; this.multiplier = -1; + this.isRanking = false; + } + + public RollDiceNotification(Color color, int eyes, boolean isRanking) { + this.color = color; + this.eyes = eyes; + this.turbo = false; + this.multiplier = -1; + this.isRanking = isRanking; } public RollDiceNotification(Color color, int eyes, boolean turbo, int multiplier) { @@ -29,6 +39,7 @@ public RollDiceNotification(Color color, int eyes, boolean turbo, int multiplier this.eyes = eyes; this.turbo = turbo; this.multiplier = multiplier; + this.isRanking = false; } /** @@ -54,4 +65,6 @@ public int getMultiplier() { public boolean isTurbo() { return turbo; } + + public boolean isRanking() { return isRanking; } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java index 6d72dcc5..167992c2 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/LobbyState.java @@ -7,6 +7,7 @@ import pp.mdga.message.server.*; import pp.mdga.server.ServerGameLogic; +import java.util.ArrayList; import java.util.Map; /** @@ -48,7 +49,7 @@ public void exit() { * This method will be used to initialize the game and all necessary objects. */ public void initializeGame() { - for (Map.Entry entry: this.logic.getGame().getPlayers().entrySet()) { + for (Map.Entry entry : this.logic.getGame().getPlayers().entrySet()) { this.logic.getGame().getBoard().addPlayerData(entry.getValue().getColor(), new PlayerData(entry.getValue().getColor())); } } @@ -63,7 +64,7 @@ public void initializeGame() { @Override public void received(JoinedLobbyMessage msg, int from) { Player player = new Player(msg.getName()); - player.setColor(Color.getColorByIndex(this.logic.getGame().getPlayers().size())); + player.setColor(Color.NONE); this.logic.getGame().addPlayer(from, player); for (Map.Entry entry : this.logic.getGame().getPlayers().entrySet()) { this.logic.getServerSender().broadcast(new LobbyPlayerJoinedMessage(entry.getKey(), entry.getValue(), entry.getKey() == this.logic.getGame().getHost())); @@ -84,8 +85,13 @@ public void received(SelectTSKMessage msg, int from) { return; } } + + if (this.logic.getGame().getPlayerById(from).getColor() != Color.NONE) { + this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, this.logic.getGame().getPlayerById(from).getColor(), false)); + } + this.logic.getGame().getPlayerById(from).setColor(msg.getColor()); - this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, msg.getColor())); + this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, msg.getColor(), true)); } /** @@ -98,7 +104,7 @@ public void received(SelectTSKMessage msg, int from) { @Override public void received(DeselectTSKMessage msg, int from) { this.logic.getGame().getPlayerById(from).setColor(Color.NONE); - this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, Color.NONE)); + this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, Color.NONE, false)); } /** @@ -110,6 +116,28 @@ public void received(DeselectTSKMessage msg, int from) { */ @Override public void received(LobbyReadyMessage msg, int from) { + //assign a free color + if (this.logic.getGame().getPlayerById(from).getColor() == Color.NONE) { + ArrayList colors = new ArrayList<>(); + colors.add(Color.ARMY); + colors.add(Color.AIRFORCE); + colors.add(Color.NAVY); + colors.add(Color.CYBER); + + for (Map.Entry entry : this.logic.getGame().getPlayers().entrySet()) { + if (colors.contains(entry.getValue().getColor())) { + colors.remove(entry.getValue().getColor()); + } + } + + if (colors.size() < 1) { + throw new RuntimeException("can not assign a color"); + } + + this.logic.getGame().getPlayerById(from).setColor(colors.get(0)); + this.logic.getServerSender().broadcast(new UpdateTSKMessage(from, colors.get(0), true)); + } + this.logic.getGame().getPlayerById(from).setReady(true); this.logic.getServerSender().broadcast(new UpdateReadyMessage(from, true)); for (Map.Entry entry : this.logic.getGame().getPlayers().entrySet()) { @@ -120,8 +148,12 @@ public void received(LobbyReadyMessage msg, int from) { this.logic.getGame().setAllReady(true); if (this.logic.getGame().allReady()) { + this.logic.getServerSender().broadcast(new StartBriefingMessage(this.logic.getGame().getBoard())); this.initializeGame(); - this.logic.getServerSender().broadcast(new ServerStartGameMessage(this.logic.getGame().getBoard())); + for (Map.Entry entry : logic.getGame().getBoard().getPlayerData().entrySet()) { + this.logic.getServerSender().broadcast(new PlayerDataMessage(entry.getValue(), entry.getKey())); + } + this.logic.getServerSender().broadcast(new ServerStartGameMessage()); } } @@ -148,6 +180,9 @@ public void received(LobbyNotReadyMessage msg, int from) { */ @Override public void received(LeaveGameMessage msg, int from) { + if (from == this.logic.getGame().getHost()) { + this.logic.getServerSender().broadcast(new ShutdownMessage()); + } this.logic.getGame().removePlayer(from); this.logic.getServerSender().broadcast(new LobbyPlayerLeaveMessage(from)); this.logic.getServerSender().disconnectClient(from); @@ -164,7 +199,7 @@ public void received(LeaveGameMessage msg, int from) { public void received(StartGameMessage msg, int from) { if (msg.isForceStartGame() || this.logic.getGame().allReady()) { this.initializeGame(); - this.logic.getServerSender().broadcast(new ServerStartGameMessage(this.logic.getGame().getBoard())); + this.logic.getServerSender().broadcast(new ServerStartGameMessage()); this.logic.setCurrentState(this.logic.getGameState()); } } diff --git a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/DetermineStartPlayerState.java b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/DetermineStartPlayerState.java index 3e76cf70..42c71629 100644 --- a/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/DetermineStartPlayerState.java +++ b/Projekte/mdga/model/src/main/java/pp/mdga/server/automaton/game/DetermineStartPlayerState.java @@ -4,6 +4,7 @@ import pp.mdga.message.client.RequestDieMessage; import pp.mdga.message.server.ActivePlayerMessage; import pp.mdga.message.server.DieMessage; +import pp.mdga.message.server.EndOfTurnMessage; import pp.mdga.server.ServerGameLogic; import pp.mdga.server.automaton.GameState; @@ -69,6 +70,7 @@ public void received(RequestDieMessage msg, int from) { maximumRoll = entry.getValue(); } else { this.playersHaveToRoll.remove(entry.getKey()); + this.logic.getServerSender().send(entry.getKey(), new EndOfTurnMessage()); } }