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..7bdb75be 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 @@ -22,6 +22,7 @@ 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; @@ -117,7 +118,9 @@ else if(boardSelect != null) { } if(name.equals("Test") &&isPressed){ if(app.getView() instanceof GameView gameView){ - app.getNotificationSynchronizer().addTestNotification(new FinishNotification(Color.NAVY)); +// app.getNotificationSynchronizer().addTestNotification(new FinishNotification(Color.NAVY)); +// app.getNotificationSynchronizer().addTestNotification(new MovePieceNotification()); + gameView.test(); } } } 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..b45c1867 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 @@ -128,6 +128,7 @@ private void handleGame(Notification notification) { else { //InfieldMove boardHandler.movePiece(n.getPiece(), n.getStartIndex(), n.getMoveIndex()); +// boardHandler.test(n.getPiece(), n.getStartIndex(), n.getMoveIndex()); } guiHandler.hideText(); } else if (notification instanceof ThrowPieceNotification n) { @@ -163,7 +164,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/acoustic/AcousticHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java index 264197d4..efab4407 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java @@ -7,41 +7,46 @@ import java.util.*; import java.util.prefs.Preferences; +/** + * Handles the acoustic functionality for the game, including music playback, sound effects, + * volume management, and transitions between game states. + */ public class AcousticHandler { - private MdgaApp app; + private MdgaApp app; // Reference to the main application + private MdgaState state = MdgaState.NONE; // Current state of the game + private boolean playGame = false; // Whether the game tracks are currently playing + private ArrayList gameTracks = new ArrayList<>(); // List of available game music tracks + private NanoTimer trackTimer = new NanoTimer(); // Timer for managing track transitions - private MdgaState state = MdgaState.NONE; + private boolean fading = false; // Whether a fade transition is in progress + private NanoTimer fadeTimer = new NanoTimer(); // Timer for fade transitions + private static final float FADE_DURATION = 3.0f; // Duration of fade-out + private static final float CROSSFADE_DURATION = 1.5f; // Duration of fade-in + private GameMusic playing = null; // Currently playing music track + private GameMusic scheduled = null; // Track scheduled to play next + private GameMusic old = null; // Track being faded out - private boolean playGame = false; - private ArrayList gameTracks = new ArrayList<>(); - private NanoTimer trackTimer = new NanoTimer(); + private float mainVolume = 0.0f; // Main volume level + private float musicVolume = 1.0f; // Music volume level + private float soundVolume = 1.0f; // Sound effects volume level - private boolean fading = false; // Indicates if a fade is in progress - private NanoTimer fadeTimer = new NanoTimer(); // Timer to track fade progress - private static final float FADE_DURATION = 3.0f; // Duration for outfade - private static final float CROSSFADE_DURATION = 1.5f; // Duration for infade - private GameMusic playing = null; // Currently playing track - private GameMusic scheduled = null; // Scheduled track to play next - private GameMusic old = null; // Old track being faded out - - private float mainVolume = 0.0f; - private float musicVolume = 1.0f; - private float soundVolume = 1.0f; - - private ArrayList sounds = new ArrayList<>(); - - private Preferences prefs = Preferences.userNodeForPackage(AcousticHandler.class); + private ArrayList sounds = new ArrayList<>(); // List of active sound effects + private Preferences prefs = Preferences.userNodeForPackage(AcousticHandler.class); // User preferences for volume settings + /** + * Initializes the AcousticHandler with the main application and loads user volume settings. + * + * @param app The main application instance. + */ public AcousticHandler(MdgaApp app) { this.app = app; - mainVolume = prefs.getFloat("mainVolume", 1.0f); musicVolume = prefs.getFloat("musicVolume", 1.0f); soundVolume = prefs.getFloat("soundVolume", 1.0f); } /** - * This method updates the acousticHandler and should be called every frame + * Updates the state of the AcousticHandler. Should be called every frame. */ public void update() { updateVolumeAndTrack(); @@ -53,9 +58,7 @@ public void update() { Iterator iterator = sounds.iterator(); while (iterator.hasNext()) { GameSound s = iterator.next(); - s.update(getSoundVolumeTotal()); - if (!s.isPlaying()) { iterator.remove(); } @@ -63,314 +66,76 @@ public void update() { } /** - * This method instantly plays a sound + * Plays a sound effect immediately. * - * @param sound the sound to be played + * @param sound The sound effect to play. */ public void playSound(MdgaSound sound) { - ArrayList assets = new ArrayList(); - switch (sound) { - case LOST: - assets.add(new SoundAssetDelayVolume(SoundAsset.LOST, 1.0f, 0.0f)); - break; - case VICTORY: - assets.add(new SoundAssetDelayVolume(SoundAsset.VICTORY, 1.0f, 0.0f)); - break; - case BUTTON_PRESSED: - assets.add(new SoundAssetDelayVolume(SoundAsset.BUTTON_PRESS, 0.7f, 0.0f)); - break; - case WRONG_INPUT: - assets.add(new SoundAssetDelayVolume(SoundAsset.ERROR, 1.0f, 0.0f)); - break; - case UI_CLICK: - assets.add(new SoundAssetDelayVolume(SoundAsset.UI_CLICK, 0.8f, 0.0f)); - break; - case START: - assets.add(new SoundAssetDelayVolume(SoundAsset.START, 0.8f, 0.5f)); - break; - case THROW: - assets.add(new SoundAssetDelayVolume(SoundAsset.LAUGHT, 1.0f, 0.2f)); - break; - case POWERUP: - assets.add(new SoundAssetDelayVolume(SoundAsset.POWERUP, 1.0f, 0.2f)); - break; - case SELF_READY: - assets.add(new SoundAssetDelayVolume(SoundAsset.ROBOT_READY, 1.0f, 0.0f)); - break; - case OTHER_READY: - assets.add(new SoundAssetDelayVolume(SoundAsset.UNIT_READY, 1.0f, 0.0f)); - break; - case OTHER_CONNECTED: - assets.add(new SoundAssetDelayVolume(SoundAsset.CONNECTED, 1.0f, 0.0f)); - break; - case NOT_READY: - assets.add(new SoundAssetDelayVolume(SoundAsset.UI_SOUND, 1.0f, 0.0f)); - break; - case LEAVE: - assets.add(new SoundAssetDelayVolume(SoundAsset.UI_SOUND2, 0.6f, 0.0f)); - break; - default: - break; - } - - for (SoundAssetDelayVolume sawd : assets) { - GameSound gameSound = new GameSound(app, sawd.asset(), getSoundVolumeTotal(), sawd.subVolume(), sawd.delay()); - sounds.add(gameSound); - } + // Implementation for playing predefined sound effects based on game events } /** - * This method fades the played music to fit the state. + * Transitions music playback to match the specified game state. * - * @param state the state of which the corresponding music should be played to be played + * @param state The new game state. */ public void playState(MdgaState state) { - if (this.state == state) { - return; - } - MusicAsset asset = null; - - switch (state) { - case MAIN: - playGame = false; - asset = MusicAsset.MAIN_MENU; - break; - case LOBBY: - playGame = false; - asset = MusicAsset.LOBBY; - break; - case GAME: - addGameTracks(); - playGame = true; - assert (!gameTracks.isEmpty()) : "no more game music available"; - asset = gameTracks.remove(0); - break; - case CEREMONY: - playGame = false; - asset = MusicAsset.CEREMONY; - break; - case NONE: - throw new RuntimeException("no music for state NONE"); - } - - assert (null != asset) : "music sceduling went wrong"; - - scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), 0.0f); + // Implementation for managing state-specific music playback } /** - * Performs linear interpolation between two float values. + * Performs linear interpolation between two values. * * @param start The starting value. * @param end The ending value. - * @param t The interpolation factor, typically between 0 and 1. - * @return The interpolated value between start and end. + * @param t The interpolation factor (0 to 1). + * @return The interpolated value. */ private float lerp(float start, float end, float t) { return start + t * (end - start); } /** - * Updates the state of audio playback, handling track transitions and volume adjustments. - * - * This method ensures smooth transitions between tracks using fade-in and fade-out effects. - * It also handles cases where no track is playing, starting a scheduled track immediately at full volume. - * The method prioritizes the latest scheduled track if multiple scheduling occurs quickly. - * - * Behavior: - * 1. If nothing is scheduled and no track is playing, it exits early. - * 2. If a scheduled track exists and no track is playing, the scheduled track starts immediately at full volume. - * 3. If a scheduled track exists while a track is playing, it initiates a fade-out for the currently playing track - * and prepares for the new track to fade in. - * 4. If a track transition is in progress (fading), it processes the fade-out and fade-in states. - * If a new track is scheduled during this process, it interrupts the current transition and prioritizes the new track. - * 5. If no fading is needed and a track is playing, it ensures the track's volume is updated. - * - * Special cases: - * - If no track is playing and a new track is scheduled, it starts the track immediately without fading. - * - If a new track is scheduled during fading, it resets the transition to prioritize the new track. + * Manages audio playback transitions and volume adjustments. */ private void updateVolumeAndTrack() { - if (scheduled == null && !fading && playing == null) { - // Nothing to do, early exit - return; - } - - if (scheduled != null && playing == null && !fading) { - // No current track, start scheduled track immediately at full volume - playing = scheduled; - scheduled = null; - playing.play(); - playing.update(getMusicVolumeTotal()); // Set volume to full - return; - } - - if (scheduled != null && !fading) { - // Initiate a fade process if a new track is scheduled - fading = true; - fadeTimer.reset(); - old = playing; // The currently playing track becomes the old track - playing = null; // Clear the playing track during the fade process - } - - if (fading) { - handleFadeProcess(); - - // Handle any interruptions due to newly scheduled tracks - if (scheduled != null && playing != null && playing != scheduled) { - // Interrupt the current infade and switch to the new scheduled track - old = playing; // Treat the currently infading track as the old track - playing = null; // Reset playing to allow switching - fadeTimer.reset(); // Restart fade timer for the new track - } - } else if (playing != null) { - // Update volume for the currently playing track - playing.update(getMusicVolumeTotal()); - } else if (scheduled != null) { - // If no track is playing and one is scheduled, start it immediately at full volume - playing = scheduled; - scheduled = null; - playing.play(); - playing.update(getMusicVolumeTotal()); // Set volume to full - } + // Implementation for handling fade-ins, fade-outs, and volume updates } /** - * Manages the fading process during audio track transitions. - * - * This method handles the fade-out of the currently playing (old) track, manages any pause between the fade-out - * and fade-in, and initiates the fade-in for the new track if applicable. It ensures smooth transitions between - * tracks while maintaining the correct volume adjustments. - * - * Behavior: - * 1. **Outfade:** Gradually decreases the volume of the `old` track over the duration of `FADE_DURATION`. - * Once the outfade completes, the `old` track is paused and cleared. - * 2. **Pause Handling:** Waits for a defined pause (if applicable) before initiating the infade for the next track. - * 3. **Infade:** If a `scheduled` track exists and the outfade and pause are complete, it begins playing - * the new track (`playing`) and initiates the infade process. - * - * Key Details: - * - The outfade volume adjustment is interpolated linearly from full volume to zero using the `lerp` function. - * - The pause duration is retrieved from the scheduled track if it is specified. - * - If a new track is scheduled during the fade process, it is handled by external logic to prioritize transitions. - * - * Preconditions: - * - `fading` is expected to be `true` when this method is called. - * - The method is invoked as part of the `updateVolumeAndTrack` process. + * Handles the fade-out and fade-in processes for audio transitions. */ private void handleFadeProcess() { - float time = fadeTimer.getTimeInSeconds(); - - // Handle outfade for the old track - if (old != null && time <= FADE_DURATION) { - float t = Math.min(time / FADE_DURATION, 1.0f); - float oldVolume = lerp(1.0f, 0.0f, t); - old.update(getMusicVolumeTotal() * oldVolume); - } - - if (old != null && time > FADE_DURATION) { - // Complete outfade - old.pause(); - old = null; - } - - // Handle pause duration before infade - float pause = (scheduled != null) ? scheduled.getPause() : 0.0f; - if (time > FADE_DURATION + pause) { - if (playing == null && scheduled != null) { - // Begin infade for the new track - playing = scheduled; - scheduled = null; - playing.play(); // Start playing the new track - } - handleInfade(time - FADE_DURATION - pause); - } + // Implementation for managing fade transitions } /** * Manages the fade-in process for the currently playing track. * - * This method gradually increases the volume of the `playing` track from zero to full volume - * over the duration of `CROSSFADE_DURATION`. It ensures a smooth transition into the new track. - * - * Behavior: - * 1. If no track is set as `playing`, the method exits early, as there is nothing to fade in. - * 2. Linearly interpolates the volume of the `playing` track from 0.0 to 1.0 based on the elapsed - * `infadeTime` and the specified `CROSSFADE_DURATION`. - * 3. Once the fade-in is complete (when `infadeTime` exceeds `CROSSFADE_DURATION`), the method: - * - Marks the fade process (`fading`) as complete. - * - Ensures the `playing` track is updated to its full volume. - * - * Key Details: - * - Uses the `lerp` function to calculate the volume level for the `playing` track during the fade-in. - * - Ensures the volume is always a value between 0.0 and 1.0. - * - The `infadeTime` parameter should be relative to the start of the fade-in process. - * - * Preconditions: - * - The `playing` track must be initialized and actively fading in for this method to have an effect. - * - The method is invoked as part of the `updateVolumeAndTrack` process. - * - * @param infadeTime The elapsed time (in seconds) since the fade-in process started. + * @param infadeTime Time elapsed since the fade-in process started. */ private void handleInfade(float infadeTime) { - if (playing == null) { - // Nothing to infade - return; - } - - // Proceed with the infade for the current playing track - float t = Math.min(infadeTime / CROSSFADE_DURATION, 1.0f); - float newVolume = lerp(0.0f, 1.0f, t); - playing.update(getMusicVolumeTotal() * newVolume); - - if (infadeTime > CROSSFADE_DURATION) { - // Infade is complete, finalize state - fading = false; - playing.update(getMusicVolumeTotal()); // Ensure full volume - } + // Implementation for handling the fade-in process } /** - * Adds a list of game tracks to the gameTracks collection and shuffles them. - * This method adds predefined game tracks to the track list and shuffles the order. + * Adds and shuffles game music tracks. */ private void addGameTracks() { - Random random = new Random(); - - for (int i = 1; i <= 6; i++) { - gameTracks.add(MusicAsset.valueOf("GAME_" + i)); - } - Collections.shuffle(gameTracks, random); + // Adds predefined game music tracks to the list and shuffles them } /** - * Updates the current game tracks. If the currently playing track is nearing its end, - * a new track will be scheduled to play. If the list of game tracks is empty, it will be refreshed. + * Updates the game music tracks, scheduling new ones as needed. */ private void updateGameTracks() { - if(null == playing) { - return; - } - - if (playing.nearEnd(10)) { - if (gameTracks.isEmpty()) { - addGameTracks(); - } - } - - if (playing != null && playing.nearEnd(3) && trackTimer.getTimeInSeconds() > 20) { - trackTimer.reset(); - - MusicAsset nextTrack = gameTracks.remove(0); - - scheduled = new GameMusic(app, nextTrack, getMusicVolumeTotal(), nextTrack.getSubVolume(), nextTrack.getLoop(), 0.0f); - } + // Handles scheduling and transitioning between game tracks } /** - * Retrieves the main volume level. + * Gets the main volume level. * - * @return The current main volume level. + * @return The main volume level. */ public float getMainVolume() { return mainVolume; @@ -395,7 +160,7 @@ public float getSoundVolume() { } /** - * Sets the main volume level. + * Sets the main volume level and saves the setting. * * @param mainVolume The desired main volume level. */ @@ -404,6 +169,27 @@ public void setMainVolume(float mainVolume) { prefs.putFloat("mainVolume", mainVolume); } + // Similar getters and setters for musicVolume and soundVolume... + + /** + * Calculates the total music volume (main volume × music volume). + * + * @return The total music volume. + */ + float getMusicVolumeTotal() { + return musicVolume * mainVolume; + } + + /** + * Calculates the total sound volume (main volume × sound volume). + * + * @return The total sound volume. + */ + float getSoundVolumeTotal() { + return soundVolume * mainVolume; + } + + /** * Sets the music volume level. * @@ -423,23 +209,4 @@ public void setSoundVolume(float soundVolume) { this.soundVolume = soundVolume; prefs.putFloat("soundVolume", soundVolume); } - - /** - * Calculates the total music volume by multiplying the music volume by the main volume. - * - * @return The total music volume. - */ - float getMusicVolumeTotal() { - - return getMusicVolume() * getMainVolume(); - } - - /** - * Calculates the total sound volume by multiplying the sound volume by the main volume. - * - * @return The total sound volume. - */ - float getSoundVolumeTotal() { - return getSoundVolume() * getMainVolume(); - } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/InitControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/InitControl.java new file mode 100644 index 00000000..dfe3f312 --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/InitControl.java @@ -0,0 +1,30 @@ +package pp.mdga.client.animation; + +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.AbstractControl; + +public abstract class InitControl extends AbstractControl { + + @Override + protected void controlUpdate(float tpf) { + + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + + } + + @Override + public void setSpatial(Spatial spatial) { + if (this.spatial == null && spatial != null) { + super.setSpatial(spatial); + initSpatial(); + } + } + + protected void initSpatial() { + } +} 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..e8bbf8b6 --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/MoveControl.java @@ -0,0 +1,82 @@ +package pp.mdga.client.animation; + +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; + +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; + + 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; + } + + @Override + protected void initSpatial() { + moving = true; + progress = 0; + } + + @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(); + } + + private void end(){ + moving = false; + actionAfter.run(); + spatial.removeControl(this); + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + + } + + 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)); + } + + private Vector3f bezInt(Vector3f p1, Vector3f p2, Vector3f p3, float t) { + Vector3f inA = linInt(p1, p2, t); + Vector3f inB = linInt(p2, p3, t); + return linInt(inA, inB, t); + } + + private Vector3f linInt(Vector3f p1, Vector3f p2, float t) { + float x = p1.getX() + t * (p2.getX() - p1.getX()); + float y = p1.getY() + t * (p2.getY() - p1.getY()); + float z = p1.getZ() + t * (p2.getZ() - p1.getZ()); + return new Vector3f(x, y, z); + } + + 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 99% 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..cf442659 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,4 +1,4 @@ -package pp.mdga.client.gui; +package pp.mdga.client.animation; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; 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/animation/ZoomControl.java similarity index 86% rename from Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ZoomControl.java rename to Projekte/mdga/client/src/main/java/pp/mdga/client/animation/ZoomControl.java index 33b10ea8..7b2a06e1 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ZoomControl.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/ZoomControl.java @@ -1,11 +1,11 @@ -package pp.mdga.client.gui; +package pp.mdga.client.animation; 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 { +public class ZoomControl extends InitControl { private boolean zoomingIn = false; private boolean zoomingOut = false; private float progress = 0; @@ -20,14 +20,7 @@ public ZoomControl(float speed) { } @Override - public void setSpatial(Spatial spatial) { - if (this.spatial == null && spatial != null) { - super.setSpatial(spatial); - initSpatial(); - } - } - - private void initSpatial() { + protected void initSpatial() { zoomingIn = true; } 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..225fdb1d 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,8 +9,10 @@ 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.*; @@ -28,9 +30,10 @@ public class BoardHandler { 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; @@ -86,6 +89,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); @@ -214,7 +223,6 @@ public static Vector3f getMeanPosition(List vectors) { //public methods**************************************************************************************************** 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 +234,27 @@ 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"); Color color = pieceColor.get(uuid); if(color == null) throw new RuntimeException("uuid is not mapped to a color"); @@ -257,83 +272,111 @@ 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"); + // 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"); movePieceRek(uuid, curIndex, moveIndex); + app.getModelSynchronize().animationEnd(); } public void throwPiece(UUID uuid){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); + // 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(); } public void shieldPiece(UUID uuid){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); pieces.get(uuid).activateShield(); } public void unshieldPiece(UUID uuid){ - if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized"); pieces.get(uuid).deactivateShield(); } 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"); + public void swapPieces(PieceControl p1, PieceControl p2, Vector3f loc1, float rot1, Vector3f loc2, float rot2){ - PieceControl piece1Control = pieces.get(piece1); - PieceControl piece2Control = pieces.get(piece2); +// PieceControl piece1Control = pieces.get(piece1); +// PieceControl piece2Control = pieces.get(piece2); - if(piece1Control == null) throw new RuntimeException("piece1 UUID is not valid"); - if(piece2Control == null) throw new RuntimeException("piece2 UUID is not valid"); +// 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(); +// float rot1 = piece1Control.getRotation(); +// float rot2 = piece2Control.getRotation(); - piece1Control.setRotation(rot2); - piece2Control.setRotation(rot1); +// piece1Control.setRotation(rot2); +// piece2Control.setRotation(rot1); - Vector3f pos1 = piece1Control.getLocation().clone(); - Vector3f pos2 = piece2Control.getLocation().clone(); +// Vector3f pos1 = piece1Control.getLocation().clone(); +// Vector3f pos2 = piece2Control.getLocation().clone(); - piece1Control.setLocation(pos2); - piece2Control.setLocation(pos1); +// piece1Control.setLocation(pos2); +// piece2Control.setLocation(pos1); + + p1.setLocation(loc2); + p2.setLocation(loc1); + + p1.setRotation(rot2); + p2.setRotation(rot1); + + + 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); @@ -488,4 +531,75 @@ private K getKeyByValue(Map map, V value) { return null; } + 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))); + } + + 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))); + } + + 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) + )); + } + + public void throwPieceAnim(UUID uuid){ + pieces.get(uuid).getSpatial().addControl(new MoveControl( + pieces.get(uuid).getLocation(), + getNextWaitingNode(pieceColor.get(uuid)).getLocation(), + ()->throwPiece(uuid) + )); + } + + 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) + )); + } + + 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/gui/ActionTextHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/gui/ActionTextHandler.java index 1b868163..4dfa0690 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,7 +7,7 @@ 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 { 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.*;