added move/swap/throw animation; reworked waitingNodes logic in BoardHandler
This commit is contained in:
		@@ -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();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -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 ???
 | 
			
		||||
 
 | 
			
		||||
@@ -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<MusicAsset> 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<MusicAsset> 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<GameSound> sounds = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    private Preferences prefs = Preferences.userNodeForPackage(AcousticHandler.class);
 | 
			
		||||
    private ArrayList<GameSound> 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<GameSound> 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<SoundAssetDelayVolume> assets = new ArrayList<SoundAssetDelayVolume>();
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package pp.mdga.client.gui;
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.Quaternion;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -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<Color, List<NodeControl>> homeNodesMap;
 | 
			
		||||
    private Map<Color, List<NodeControl>> waitingNodesMap;
 | 
			
		||||
    private Map<Color, List<PieceControl>> waitingPiecesMap;
 | 
			
		||||
    private Map<Color, Map<UUID, NodeControl>> waitingNodes;
 | 
			
		||||
    private Map<UUID, Color> 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<AssetOnMap> assetOnMaps = MapLoader.loadMap(MAP_NAME);
 | 
			
		||||
 | 
			
		||||
@@ -214,7 +223,6 @@ public static Vector3f getMeanPosition(List<Vector3f> vectors) {
 | 
			
		||||
 | 
			
		||||
    //public methods****************************************************************************************************
 | 
			
		||||
    public void addPlayer(Color color, List<UUID> uuid) {
 | 
			
		||||
        if (!isInitialised) throw new RuntimeException("BoardHandler is not initialized");
 | 
			
		||||
 | 
			
		||||
        List<AssetOnMap> 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> 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<NodeControl> waitNodes = waitingNodesMap.get(color);
 | 
			
		||||
        List<PieceControl> 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, V> K getKeyByValue(Map<K, V> 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<NodeControl> 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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.*;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user