merge development
This commit is contained in:
		@@ -1,5 +1,7 @@
 | 
			
		||||
<component name="ProjectRunConfigurationManager">
 | 
			
		||||
  <configuration default="false" name="MdgaApp" type="Application" factoryName="Application" singleton="false" nameIsGenerated="true">
 | 
			
		||||
    <option name="ALTERNATIVE_JRE_PATH" value="temurin-20" />
 | 
			
		||||
    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
 | 
			
		||||
    <option name="MAIN_CLASS_NAME" value="pp.mdga.client.MdgaApp" />
 | 
			
		||||
    <module name="Projekte.mdga.client.main" />
 | 
			
		||||
    <option name="VM_PARAMETERS" value="-Djava.util.logging.config.file=logging.properties -ea" />
 | 
			
		||||
@@ -14,4 +16,4 @@
 | 
			
		||||
      <option name="Make" enabled="true" />
 | 
			
		||||
    </method>
 | 
			
		||||
  </configuration>
 | 
			
		||||
</component>
 | 
			
		||||
</component>
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
import com.jme3.app.SimpleApplication;
 | 
			
		||||
import com.jme3.system.AppSettings;
 | 
			
		||||
import com.simsilica.lemur.GuiGlobals;
 | 
			
		||||
import pp.mdga.client.acoustic.AcousticHandler;
 | 
			
		||||
import com.jme3.system.AppSettings;
 | 
			
		||||
import pp.mdga.client.dialog.JoinDialog;
 | 
			
		||||
import pp.mdga.client.view.*;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -60,7 +60,6 @@ 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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,46 +7,41 @@
 | 
			
		||||
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; // 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 MdgaApp app;
 | 
			
		||||
 | 
			
		||||
    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 MdgaState state = MdgaState.NONE;
 | 
			
		||||
 | 
			
		||||
    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 playGame = false;
 | 
			
		||||
    private ArrayList<MusicAsset> gameTracks = new ArrayList<>();
 | 
			
		||||
    private NanoTimer trackTimer = new NanoTimer();
 | 
			
		||||
 | 
			
		||||
    private ArrayList<GameSound> sounds = new ArrayList<>(); // List of active sound effects
 | 
			
		||||
    private Preferences prefs = Preferences.userNodeForPackage(AcousticHandler.class); // User preferences for volume settings
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the state of the AcousticHandler. Should be called every frame.
 | 
			
		||||
     * This method updates the acousticHandler and should be called every frame
 | 
			
		||||
     */
 | 
			
		||||
    public void update() {
 | 
			
		||||
        updateVolumeAndTrack();
 | 
			
		||||
@@ -58,7 +53,9 @@ public void update() {
 | 
			
		||||
        Iterator<GameSound> iterator = sounds.iterator();
 | 
			
		||||
        while (iterator.hasNext()) {
 | 
			
		||||
            GameSound s = iterator.next();
 | 
			
		||||
 | 
			
		||||
            s.update(getSoundVolumeTotal());
 | 
			
		||||
 | 
			
		||||
            if (!s.isPlaying()) {
 | 
			
		||||
                iterator.remove();
 | 
			
		||||
            }
 | 
			
		||||
@@ -66,76 +63,314 @@ public void update() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Plays a sound effect immediately.
 | 
			
		||||
     * This method instantly plays a sound
 | 
			
		||||
     *
 | 
			
		||||
     * @param sound The sound effect to play.
 | 
			
		||||
     * @param sound the sound to be played
 | 
			
		||||
     */
 | 
			
		||||
    public void playSound(MdgaSound sound) {
 | 
			
		||||
        // Implementation for playing predefined sound effects based on game events
 | 
			
		||||
        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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Transitions music playback to match the specified game state.
 | 
			
		||||
     * This method fades the played music to fit the state.
 | 
			
		||||
     *
 | 
			
		||||
     * @param state The new game state.
 | 
			
		||||
     * @param state the state of which the corresponding music should be played to be played
 | 
			
		||||
     */
 | 
			
		||||
    public void playState(MdgaState state) {
 | 
			
		||||
        // Implementation for managing state-specific music playback
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs linear interpolation between two values.
 | 
			
		||||
     * Performs linear interpolation between two float values.
 | 
			
		||||
     *
 | 
			
		||||
     * @param start The starting value.
 | 
			
		||||
     * @param end   The ending value.
 | 
			
		||||
     * @param t     The interpolation factor (0 to 1).
 | 
			
		||||
     * @return The interpolated value.
 | 
			
		||||
     * @param t     The interpolation factor, typically between 0 and 1.
 | 
			
		||||
     * @return The interpolated value between start and end.
 | 
			
		||||
     */
 | 
			
		||||
    private float lerp(float start, float end, float t) {
 | 
			
		||||
        return start + t * (end - start);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Manages audio playback transitions and volume adjustments.
 | 
			
		||||
     * 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.
 | 
			
		||||
     */
 | 
			
		||||
    private void updateVolumeAndTrack() {
 | 
			
		||||
        // Implementation for handling fade-ins, fade-outs, and volume updates
 | 
			
		||||
        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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handles the fade-out and fade-in processes for audio transitions.
 | 
			
		||||
     * 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.
 | 
			
		||||
     */
 | 
			
		||||
    private void handleFadeProcess() {
 | 
			
		||||
        // Implementation for managing fade transitions
 | 
			
		||||
        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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Manages the fade-in process for the currently playing track.
 | 
			
		||||
     *
 | 
			
		||||
     * @param infadeTime Time elapsed since the fade-in process started.
 | 
			
		||||
     * 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.
 | 
			
		||||
     */
 | 
			
		||||
    private void handleInfade(float infadeTime) {
 | 
			
		||||
        // Implementation for handling the fade-in process
 | 
			
		||||
        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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds and shuffles game music tracks.
 | 
			
		||||
     * 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.
 | 
			
		||||
     */
 | 
			
		||||
    private void addGameTracks() {
 | 
			
		||||
        // Adds predefined game music tracks to the list and shuffles them
 | 
			
		||||
        Random random = new Random();
 | 
			
		||||
 | 
			
		||||
        for (int i = 1; i <= 6; i++) {
 | 
			
		||||
            gameTracks.add(MusicAsset.valueOf("GAME_" + i));
 | 
			
		||||
        }
 | 
			
		||||
        Collections.shuffle(gameTracks, random);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the game music tracks, scheduling new ones as needed.
 | 
			
		||||
     * 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.
 | 
			
		||||
     */
 | 
			
		||||
    private void updateGameTracks() {
 | 
			
		||||
        // Handles scheduling and transitioning between game tracks
 | 
			
		||||
        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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Gets the main volume level.
 | 
			
		||||
     * Retrieves the main volume level.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The main volume level.
 | 
			
		||||
     * @return The current main volume level.
 | 
			
		||||
     */
 | 
			
		||||
    public float getMainVolume() {
 | 
			
		||||
        return mainVolume;
 | 
			
		||||
@@ -160,7 +395,7 @@ public float getSoundVolume() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the main volume level and saves the setting.
 | 
			
		||||
     * Sets the main volume level.
 | 
			
		||||
     *
 | 
			
		||||
     * @param mainVolume The desired main volume level.
 | 
			
		||||
     */
 | 
			
		||||
@@ -169,27 +404,6 @@ 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.
 | 
			
		||||
     *
 | 
			
		||||
@@ -209,4 +423,23 @@ 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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -144,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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -193,6 +193,16 @@ public void received(ServerStartGameMessage 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);
 | 
			
		||||
@@ -239,6 +249,10 @@ public void selectTsk(Color color){
 | 
			
		||||
        state.selectTSK(color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deselectTSK(Color color){
 | 
			
		||||
        state.deselectTSK(color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void selectDice(){
 | 
			
		||||
        state.selectDice();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -183,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.");
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -144,6 +144,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;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -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,15 +76,24 @@ 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<Color, PlayerData> entry : msg.getBoard().getPlayerData().entrySet()){
 | 
			
		||||
            List<UUID> 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<Color, PlayerData> entry : logic.getGame().getBoard().getPlayerData().entrySet()) {
 | 
			
		||||
            List<UUID> 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();
 | 
			
		||||
    }
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@ public DetermineStartPlayerState(ClientState parent, ClientGameLogic logic) {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void enter() {
 | 
			
		||||
        state = rollRankingDiceState;
 | 
			
		||||
        this.setState(this.rollRankingDiceState);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
 
 | 
			
		||||
@@ -12,17 +12,17 @@ public class Piece {
 | 
			
		||||
    /**
 | 
			
		||||
     * The shield state of the piece.
 | 
			
		||||
     */
 | 
			
		||||
    private int shield;
 | 
			
		||||
    private ShieldState shield;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The current state of the piece.
 | 
			
		||||
     */
 | 
			
		||||
    private int state;
 | 
			
		||||
    private PieceState state;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The color of the piece.
 | 
			
		||||
     */
 | 
			
		||||
    private final int color;
 | 
			
		||||
    private final Color color;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The unique identifier of the piece.
 | 
			
		||||
@@ -36,15 +36,15 @@ public class Piece {
 | 
			
		||||
     * @param state the state of the piece
 | 
			
		||||
     */
 | 
			
		||||
    public Piece(Color color, PieceState state, int id) {
 | 
			
		||||
        this.color = color.ordinal();
 | 
			
		||||
        this.state = state.ordinal();
 | 
			
		||||
        shield = ShieldState.NONE.ordinal();
 | 
			
		||||
        this.color = color;
 | 
			
		||||
        this.state = state;
 | 
			
		||||
        shield = ShieldState.NONE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Piece() {
 | 
			
		||||
        color = Color.NONE.ordinal();
 | 
			
		||||
        state = PieceState.WAITING.ordinal();
 | 
			
		||||
        shield = ShieldState.NONE.ordinal();
 | 
			
		||||
        color = Color.NONE;
 | 
			
		||||
        state = PieceState.WAITING;
 | 
			
		||||
        shield = ShieldState.NONE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -53,7 +53,7 @@ private Piece() {
 | 
			
		||||
     * @return the color of the piece
 | 
			
		||||
     */
 | 
			
		||||
    public void setShield(ShieldState shield) {
 | 
			
		||||
        this.shield = shield.ordinal();
 | 
			
		||||
        this.shield = shield;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -62,7 +62,7 @@ public void setShield(ShieldState shield) {
 | 
			
		||||
     * @return the color of the piece
 | 
			
		||||
     */
 | 
			
		||||
    public ShieldState getShield() {
 | 
			
		||||
        return ShieldState.values()[shield];
 | 
			
		||||
        return shield;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -71,7 +71,7 @@ public ShieldState getShield() {
 | 
			
		||||
     * @param state the state of the piece
 | 
			
		||||
     */
 | 
			
		||||
    public void setState(PieceState state) {
 | 
			
		||||
        this.state = state.ordinal();
 | 
			
		||||
        this.state = state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -80,7 +80,7 @@ public void setState(PieceState state) {
 | 
			
		||||
     * @return the color of the piece
 | 
			
		||||
     */
 | 
			
		||||
    public PieceState getState() {
 | 
			
		||||
        return PieceState.values()[state];
 | 
			
		||||
        return state;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -89,7 +89,7 @@ public PieceState getState() {
 | 
			
		||||
     * @return the color of the piece
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isShielded() {
 | 
			
		||||
        return shield == ShieldState.ACTIVE.ordinal();
 | 
			
		||||
        return shield == ShieldState.ACTIVE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -98,7 +98,7 @@ public boolean isShielded() {
 | 
			
		||||
     * @return the color of the piece
 | 
			
		||||
     */
 | 
			
		||||
    public boolean isSuppressed() {
 | 
			
		||||
        return shield == ShieldState.SUPPRESSED.ordinal();
 | 
			
		||||
        return shield == ShieldState.SUPPRESSED;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -107,7 +107,7 @@ public boolean isSuppressed() {
 | 
			
		||||
     * @return the color of the piece
 | 
			
		||||
     */
 | 
			
		||||
    public Color getColor() {
 | 
			
		||||
        return Color.values()[color];
 | 
			
		||||
        return color;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ public class StartNode extends Node {
 | 
			
		||||
    /**
 | 
			
		||||
     * The color of the node.
 | 
			
		||||
     */
 | 
			
		||||
    private int color;
 | 
			
		||||
    private Color color;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new start node with the given color.
 | 
			
		||||
@@ -18,11 +18,11 @@ public class StartNode extends Node {
 | 
			
		||||
     * @param color the color of the node
 | 
			
		||||
     */
 | 
			
		||||
    public StartNode(Color color) {
 | 
			
		||||
        this.color = color.ordinal();
 | 
			
		||||
        this.color = color;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private StartNode() {
 | 
			
		||||
        color = Color.NONE.ordinal();
 | 
			
		||||
        color = Color.NONE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -31,7 +31,7 @@ private StartNode() {
 | 
			
		||||
     * @return the color of the node
 | 
			
		||||
     */
 | 
			
		||||
    public Color getColor() {
 | 
			
		||||
        return Color.values()[color];
 | 
			
		||||
        return color;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -40,6 +40,6 @@ public Color getColor() {
 | 
			
		||||
     * @param color the new color of the node
 | 
			
		||||
     */
 | 
			
		||||
    public void setColor(Color color) {
 | 
			
		||||
        this.color = color.ordinal();
 | 
			
		||||
        this.color = color;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -214,4 +214,8 @@ public interface ServerInterpreter {
 | 
			
		||||
     * @param shutdownMessage the SelectTSK message received.
 | 
			
		||||
     */
 | 
			
		||||
    void received(ShutdownMessage shutdownMessage);
 | 
			
		||||
 | 
			
		||||
    void received(StartBriefingMessage msg);
 | 
			
		||||
 | 
			
		||||
    void received(PlayerDataMessage msg);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -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 "";
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -11,17 +11,19 @@ public class RollDiceNotification extends Notification{
 | 
			
		||||
    private int eyes;
 | 
			
		||||
    private boolean turbo;
 | 
			
		||||
    private int multiplier;
 | 
			
		||||
    private boolean isRanking;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor.
 | 
			
		||||
     * @param color the color of the player that rolled the die.
 | 
			
		||||
     * @param eyes the number of eyes that were rolled.
 | 
			
		||||
     */
 | 
			
		||||
    public RollDiceNotification(Color color, int eyes) {
 | 
			
		||||
    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 +31,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 +57,6 @@ public int getMultiplier() {
 | 
			
		||||
    public boolean isTurbo() {
 | 
			
		||||
        return turbo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isRanking() { return isRanking; }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -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<Integer, Player> entry: this.logic.getGame().getPlayers().entrySet()) {
 | 
			
		||||
        for (Map.Entry<Integer, Player> 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<Integer, Player> 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<Color> colors = new ArrayList<>();
 | 
			
		||||
            colors.add(Color.ARMY);
 | 
			
		||||
            colors.add(Color.AIRFORCE);
 | 
			
		||||
            colors.add(Color.NAVY);
 | 
			
		||||
            colors.add(Color.CYBER);
 | 
			
		||||
 | 
			
		||||
            for (Map.Entry<Integer, Player> 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<Integer, Player> 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<Color, PlayerData> entry : logic.getGame().getBoard().getPlayerData().entrySet()) {
 | 
			
		||||
                this.logic.getServerSender().broadcast(new PlayerDataMessage(entry.getValue(), entry.getKey()));
 | 
			
		||||
            }
 | 
			
		||||
            this.logic.getServerSender().broadcast(new ServerStartGameMessage());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -167,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());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user