Merge branch 'development' into 'dev/server_h'
Development See merge request progproj/gruppen-ht24/Gruppe-01!37
@@ -28,7 +28,7 @@ public enum Asset {
 | 
			
		||||
    ship(0.8f),
 | 
			
		||||
    smallTent,
 | 
			
		||||
    tank,
 | 
			
		||||
    world(1.2f),
 | 
			
		||||
    world("Models/world/world_export_newTex.obj", "Models/world/world_diff.png",1.2f),
 | 
			
		||||
    shieldRing("Models/shieldRing/shieldRing.j3o", null),
 | 
			
		||||
    treeSmall(1.2f),
 | 
			
		||||
    treeBig(1.2f),
 | 
			
		||||
@@ -40,6 +40,11 @@ public enum Asset {
 | 
			
		||||
    shieldSymbol("Models/shieldCard/shieldSymbol.j3o", "Models/shieldCard/shieldCard_diff.png"),
 | 
			
		||||
    dice,
 | 
			
		||||
    missile("Models/missile/AVMT300.obj", "Models/missile/texture.jpg", 0.1f),
 | 
			
		||||
    tankShoot("Models/tank/tank_shoot_bot.obj", "Models/tank/tank_diff.png"),
 | 
			
		||||
    tankShootTop("Models/tank/tank_shoot_top.obj", "Models/tank/tank_diff.png"),
 | 
			
		||||
    treesSmallBackground("Models/treeSmall/small_trees_background.obj", "Models/treeSmall/treeSmall_diff.png", 1.2f),
 | 
			
		||||
    treesBigBackground("Models/treeBig/big_trees_background.obj", "Models/treeBig/treeBig_diff.png", 1.2f),
 | 
			
		||||
    shell
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    private final String modelPath;
 | 
			
		||||
 
 | 
			
		||||
@@ -158,7 +158,7 @@ else if(boardSelect != null) {
 | 
			
		||||
                        gameView.getBoardHandler().movePieceStartAnim(p,0);
 | 
			
		||||
                        //gameView.getBoardHandler().movePieceAnim(p,0, 8);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        gameView.getBoardHandler().throwMissileAnim(p);
 | 
			
		||||
                        gameView.getBoardHandler().throwPiece(p, Color.NAVY);
 | 
			
		||||
                        //gameView.getBoardHandler().movePieceStartAnim(p,0);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,16 +1,8 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
import pp.mdga.client.acoustic.MdgaSound;
 | 
			
		||||
import pp.mdga.client.server.MdgaServer;
 | 
			
		||||
import pp.mdga.client.view.CeremonyView;
 | 
			
		||||
import pp.mdga.client.view.GameView;
 | 
			
		||||
import pp.mdga.client.view.LobbyView;
 | 
			
		||||
import pp.mdga.game.BonusCard;
 | 
			
		||||
import pp.mdga.game.Color;
 | 
			
		||||
import pp.mdga.message.client.LobbyReadyMessage;
 | 
			
		||||
import pp.mdga.notification.AcquireCardNotification;
 | 
			
		||||
import pp.mdga.notification.DrawCardNotification;
 | 
			
		||||
import pp.mdga.notification.TskSelectNotification;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.logging.Level;
 | 
			
		||||
@@ -75,10 +67,10 @@ public void selectCard(BonusCard card) {
 | 
			
		||||
 | 
			
		||||
        GameView gameView = (GameView) app.getView();
 | 
			
		||||
 | 
			
		||||
        if(card == null) {
 | 
			
		||||
        if(card != null) {
 | 
			
		||||
            gameView.needConfirm();
 | 
			
		||||
        } else {
 | 
			
		||||
            gameView.needNoPower();
 | 
			
		||||
            gameView.showNoPower();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -95,16 +87,12 @@ public void confirm() {
 | 
			
		||||
            app.getGameLogic().selectPiece(a);
 | 
			
		||||
            gameView.getBoardHandler().clearSelectable();
 | 
			
		||||
        } else {
 | 
			
		||||
            if(null == card) {
 | 
			
		||||
                app.getGameLogic().selectCard(null);
 | 
			
		||||
            } else {
 | 
			
		||||
                app.getGameLogic().selectCard(card);
 | 
			
		||||
                gameView.getGuiHandler().clearSelectableCards();
 | 
			
		||||
            }
 | 
			
		||||
            app.getGameLogic().selectCard(card);
 | 
			
		||||
            gameView.getGuiHandler().clearSelectableCards();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        gameView.noConfirm();
 | 
			
		||||
        gameView.noNoPower();
 | 
			
		||||
        gameView.hideNoPower();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void selectTsk(Color color) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
import com.jme3.system.NanoTimer;
 | 
			
		||||
import pp.mdga.client.acoustic.MdgaSound;
 | 
			
		||||
import pp.mdga.client.board.BoardHandler;
 | 
			
		||||
import pp.mdga.client.gui.GuiHandler;
 | 
			
		||||
@@ -16,13 +17,26 @@ public class NotificationSynchronizer {
 | 
			
		||||
 | 
			
		||||
    private ArrayList<Notification> notifications = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    private NanoTimer timer = new NanoTimer();
 | 
			
		||||
    private float delay = 0;
 | 
			
		||||
 | 
			
		||||
    private static final float STANDARD_DELAY = 2.5f;
 | 
			
		||||
 | 
			
		||||
    NotificationSynchronizer(MdgaApp app) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void update() {
 | 
			
		||||
        Notification n = app.getGameLogic().getNotification();
 | 
			
		||||
        while (n != null) {
 | 
			
		||||
        while (timer.getTimeInSeconds() >= delay) {
 | 
			
		||||
            Notification n = app.getGameLogic().getNotification();
 | 
			
		||||
 | 
			
		||||
            if(n == null) {
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            timer.reset();
 | 
			
		||||
            delay = 0;
 | 
			
		||||
 | 
			
		||||
            if(n instanceof InfoNotification infoNotification) {
 | 
			
		||||
                app.getView().showInfo(infoNotification.getMessage(), infoNotification.isError());
 | 
			
		||||
                return;
 | 
			
		||||
@@ -46,8 +60,6 @@ public void update() {
 | 
			
		||||
                        throw new RuntimeException("no notification expected: " + n.getClass().getName());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            n = app.getGameLogic().getNotification();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -90,10 +102,12 @@ private void handleGame(Notification notification) {
 | 
			
		||||
        if (notification instanceof AcquireCardNotification n) {
 | 
			
		||||
            guiHandler.addCardOwn(n.getBonusCard());
 | 
			
		||||
            app.getAcousticHandler().playSound(MdgaSound.BONUS);
 | 
			
		||||
            delay = STANDARD_DELAY;
 | 
			
		||||
        } else if (notification instanceof ActivePlayerNotification n) {
 | 
			
		||||
            gameView.getGuiHandler().setActivePlayer(n.getColor());
 | 
			
		||||
            boardHandler.showDice(n.getColor());
 | 
			
		||||
            app.getAcousticHandler().playSound(MdgaSound.UI90);
 | 
			
		||||
            delay = STANDARD_DELAY;
 | 
			
		||||
        } else if (notification instanceof CeremonyNotification ceremonyNotification) {
 | 
			
		||||
            app.enter(MdgaState.CEREMONY);
 | 
			
		||||
            CeremonyView ceremonyView = (CeremonyView) app.getView();
 | 
			
		||||
@@ -141,7 +155,7 @@ private void handleGame(Notification notification) {
 | 
			
		||||
            }
 | 
			
		||||
            guiHandler.hideText();
 | 
			
		||||
        } else if (notification instanceof ThrowPieceNotification n) {
 | 
			
		||||
            boardHandler.throwBombAnim(n.getPieceId());
 | 
			
		||||
            boardHandler.throwPiece(n.getPieceId(), n.getThrowColor());
 | 
			
		||||
        } else if (notification instanceof NoShieldNotification n) {
 | 
			
		||||
            boardHandler.unshieldPiece(n.getPieceId());
 | 
			
		||||
        } else if (notification instanceof PlayCardNotification n) {
 | 
			
		||||
@@ -162,9 +176,10 @@ private void handleGame(Notification notification) {
 | 
			
		||||
                if (n.isTurbo()) guiHandler.showRolledDiceMult(n.getEyes(), n.getMultiplier(), n.getColor());
 | 
			
		||||
                else guiHandler.showRolledDice(n.getEyes(), n.getColor());
 | 
			
		||||
            }
 | 
			
		||||
            delay = 7;
 | 
			
		||||
        } else if (notification instanceof SelectableCardsNotification n) {
 | 
			
		||||
            guiHandler.setSelectableCards(n.getCards());
 | 
			
		||||
            gameView.needNoPower();
 | 
			
		||||
            gameView.showNoPower();
 | 
			
		||||
        } else if (notification instanceof ShieldActiveNotification n) {
 | 
			
		||||
            boardHandler.shieldPiece(n.getPieceId());
 | 
			
		||||
        } else if (notification instanceof ShieldSuppressedNotification n) {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										47
									
								
								Projekte/mdga/client/src/main/java/pp/mdga/client/Util.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,47 @@
 | 
			
		||||
package pp.mdga.client;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
 | 
			
		||||
public class Util {
 | 
			
		||||
    private Util(){}
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs linear interpolation between two values.
 | 
			
		||||
     *
 | 
			
		||||
     * @param start The starting value.
 | 
			
		||||
     * @param end The ending value.
 | 
			
		||||
     * @param t A parameter between 0 and 1 representing the interpolation progress.
 | 
			
		||||
     * @return The interpolated value.
 | 
			
		||||
     */
 | 
			
		||||
    public static float linInt(float start, float end, float t) {
 | 
			
		||||
        return start + t * (end - start);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs quadratic interpolation between three points.
 | 
			
		||||
     *
 | 
			
		||||
     * @param p1 The initial point.
 | 
			
		||||
     * @param p2 The middle point.
 | 
			
		||||
     * @param p3 The final point.
 | 
			
		||||
     * @param t The interpolation parameter (0 <= t <= 1).
 | 
			
		||||
     * @return The interpolated point.
 | 
			
		||||
     */
 | 
			
		||||
    public static Vector3f quadInt(Vector3f p1, Vector3f p2, Vector3f p3, float t) {
 | 
			
		||||
        // Quadratic interpolation: (1-t)^2 * p1 + 2 * (1-t) * t * p2 + t^2 * p3
 | 
			
		||||
        float oneMinusT = 1 - t;
 | 
			
		||||
        return p1.mult(oneMinusT * oneMinusT)
 | 
			
		||||
                .add(p2.mult(2 * oneMinusT * t))
 | 
			
		||||
                .add(p3.mult(t * t));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A smooth ease-in-out function for interpolation.
 | 
			
		||||
     * It accelerates and decelerates the interpolation for a smoother effect.
 | 
			
		||||
     *
 | 
			
		||||
     * @param x The interpolation parameter (0 <= x <= 1).
 | 
			
		||||
     * @return The adjusted interpolation value.
 | 
			
		||||
     */
 | 
			
		||||
    public static float easeInOut(float x){
 | 
			
		||||
        return x < 0.5 ? 4 * x * x * x : (float) (1 - Math.pow(-2 * x + 2, 3) / 2);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -18,12 +18,14 @@ public class AcousticHandler {
 | 
			
		||||
 | 
			
		||||
    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 FADE_DURATION = 2.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 GameMusic birds;
 | 
			
		||||
 | 
			
		||||
    private float mainVolume = 0.0f;
 | 
			
		||||
    private float musicVolume = 1.0f;
 | 
			
		||||
    private float soundVolume = 1.0f;
 | 
			
		||||
@@ -38,6 +40,8 @@ public AcousticHandler(MdgaApp app) {
 | 
			
		||||
        mainVolume = prefs.getFloat("mainVolume", 1.0f);
 | 
			
		||||
        musicVolume = prefs.getFloat("musicVolume", 1.0f);
 | 
			
		||||
        soundVolume = prefs.getFloat("soundVolume", 1.0f);
 | 
			
		||||
 | 
			
		||||
        birds = new GameMusic(app, MusicAsset.BIRDS, getSoundVolumeTotal(), MusicAsset.BIRDS.getSubVolume(), MusicAsset.BIRDS.getLoop(), 0.0f);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -60,6 +64,8 @@ public void update() {
 | 
			
		||||
                iterator.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        birds.update(Math.min(getSoundVolumeTotal(), getMusicVolumeTotal() > 0 ? 0 : 1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -132,6 +138,15 @@ public void playSound(MdgaSound sound) {
 | 
			
		||||
            case MATRIX:
 | 
			
		||||
                assets.add(new SoundAssetDelayVolume(SoundAsset.MATRIX, 1.0f, 0.0f));
 | 
			
		||||
                break;
 | 
			
		||||
            case TURRET_ROTATE:
 | 
			
		||||
                assets.add(new SoundAssetDelayVolume(SoundAsset.TURRET_ROTATE, 0.7f, 0f));
 | 
			
		||||
                break;
 | 
			
		||||
            case TANK_SHOOT:
 | 
			
		||||
                assets.add(new SoundAssetDelayVolume(SoundAsset.TANK_SHOOT, 0.7f, 0f));
 | 
			
		||||
                break;
 | 
			
		||||
            case TANK_EXPLOSION:
 | 
			
		||||
                assets.add(new SoundAssetDelayVolume(SoundAsset.EXPLOSION_1, 1.0f, 0f));
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -153,6 +168,10 @@ public void playState(MdgaState state) {
 | 
			
		||||
        }
 | 
			
		||||
        MusicAsset asset = null;
 | 
			
		||||
 | 
			
		||||
        birds.pause();
 | 
			
		||||
 | 
			
		||||
        float pause = 0.0f;
 | 
			
		||||
 | 
			
		||||
        switch (state) {
 | 
			
		||||
            case MAIN:
 | 
			
		||||
                playGame = false;
 | 
			
		||||
@@ -163,10 +182,12 @@ public void playState(MdgaState state) {
 | 
			
		||||
                asset = MusicAsset.LOBBY;
 | 
			
		||||
                break;
 | 
			
		||||
            case GAME:
 | 
			
		||||
                birds.play();
 | 
			
		||||
                addGameTracks();
 | 
			
		||||
                playGame = true;
 | 
			
		||||
                assert (!gameTracks.isEmpty()) : "no more game music available";
 | 
			
		||||
                asset = gameTracks.remove(0);
 | 
			
		||||
                pause = 2.0f;
 | 
			
		||||
                break;
 | 
			
		||||
            case CEREMONY:
 | 
			
		||||
                playGame = false;
 | 
			
		||||
@@ -178,7 +199,7 @@ public void playState(MdgaState state) {
 | 
			
		||||
 | 
			
		||||
        assert (null != asset) : "music sceduling went wrong";
 | 
			
		||||
 | 
			
		||||
        scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), 0.0f);
 | 
			
		||||
        scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), pause);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -454,7 +475,7 @@ public void setSoundVolume(float soundVolume) {
 | 
			
		||||
     */
 | 
			
		||||
    float getMusicVolumeTotal() {
 | 
			
		||||
 | 
			
		||||
        return getMusicVolume() * getMainVolume();
 | 
			
		||||
        return getMusicVolume() * getMainVolume() / 2;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 
 | 
			
		||||
@@ -38,4 +38,7 @@ public enum MdgaSound {
 | 
			
		||||
    UI90,
 | 
			
		||||
    MISSILE,
 | 
			
		||||
    MATRIX,
 | 
			
		||||
    TURRET_ROTATE,
 | 
			
		||||
    TANK_SHOOT,
 | 
			
		||||
    TANK_EXPLOSION
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,12 +10,13 @@ enum MusicAsset {
 | 
			
		||||
    MAIN_MENU("Spaceship.wav", true, 1.0f),
 | 
			
		||||
    LOBBY("DeadPlanet.wav", true, 1.0f),
 | 
			
		||||
    CEREMONY("80s,Disco,Life.wav", true, 1.0f),
 | 
			
		||||
    GAME_1("NeonRoadTrip.wav", 1.0f),
 | 
			
		||||
    GAME_2("NoPressureTrance.wav", 1.0f),
 | 
			
		||||
    GAME_3("TheSynthRave.wav", 1.0f),
 | 
			
		||||
    GAME_4("LaserParty.wav", 1.0f),
 | 
			
		||||
    GAME_5("RetroNoir.wav", 1.0f),
 | 
			
		||||
    GAME_6("SpaceInvaders.wav", 1.0f);
 | 
			
		||||
    GAME_1("NeonRoadTrip.wav", 0.5f),
 | 
			
		||||
    GAME_2("NoPressureTrance.wav", 0.5f),
 | 
			
		||||
    GAME_3("TheSynthRave.wav", 0.5f),
 | 
			
		||||
    GAME_4("LaserParty.wav", 0.5f),
 | 
			
		||||
    GAME_5("RetroNoir.wav", 0.5f),
 | 
			
		||||
    GAME_6("SpaceInvaders.wav", 0.5f),
 | 
			
		||||
    BIRDS("nature-ambience.ogg", true, 1.0f);
 | 
			
		||||
 | 
			
		||||
    private final String path;
 | 
			
		||||
    private final boolean loop;
 | 
			
		||||
 
 | 
			
		||||
@@ -37,7 +37,11 @@ enum SoundAsset {
 | 
			
		||||
    LOSE("lose.ogg"),
 | 
			
		||||
    MISSILE("missile.ogg"),
 | 
			
		||||
    MATRIX("matrix.wav"),
 | 
			
		||||
    CONNECTED("connected.wav");
 | 
			
		||||
    CONNECTED("connected.wav"),
 | 
			
		||||
    TURRET_ROTATE("turret_rotate.ogg"),
 | 
			
		||||
    TANK_SHOOT("tank_shoot.ogg")
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private final String path;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
import pp.mdga.client.InitControl;
 | 
			
		||||
 | 
			
		||||
public class ActionControl extends InitControl {
 | 
			
		||||
    private final Runnable runnable;
 | 
			
		||||
 | 
			
		||||
    public ActionControl(Runnable runnable){
 | 
			
		||||
        this.runnable = runnable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    protected void action(){
 | 
			
		||||
        if(runnable == null) throw new RuntimeException("runnable is null");
 | 
			
		||||
        else runnable.run();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -10,6 +10,10 @@
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.acoustic.MdgaSound;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code Explosion} class represents an explosion effect in a 3D environment.
 | 
			
		||||
 * It manages the creation, configuration, and triggering of particle emitters for fire and smoke effects.
 | 
			
		||||
 */
 | 
			
		||||
public class Explosion {
 | 
			
		||||
 | 
			
		||||
    private final Node rootNode;
 | 
			
		||||
@@ -23,11 +27,11 @@ public class Explosion {
 | 
			
		||||
    private final Material mat;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Konstruktor für die Explosion.
 | 
			
		||||
     * Constructor for the {@code Explosion} class.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app      Die Hauptanwendung.
 | 
			
		||||
     * @param rootNode Der Root-Knoten, an den die Explosion angefügt wird.
 | 
			
		||||
     * @param location Der Ort der Explosion in World-Koordinaten.
 | 
			
		||||
     * @param app      The main application managing the explosion.
 | 
			
		||||
     * @param rootNode The root node to which the explosion effects will be attached.
 | 
			
		||||
     * @param location The location of the explosion in world coordinates.
 | 
			
		||||
     */
 | 
			
		||||
    public Explosion(MdgaApp app, Node rootNode, Vector3f location) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
@@ -38,7 +42,8 @@ public Explosion(MdgaApp app, Node rootNode, Vector3f location) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initialisiert den Partikel-Emitter für die Explosion.
 | 
			
		||||
     * Initializes the particle emitters for the explosion effect.
 | 
			
		||||
     * Configures the fire and smoke emitters with appearance, behavior, and lifespan.
 | 
			
		||||
     */
 | 
			
		||||
    private void initializeEmitter() {
 | 
			
		||||
        fire = new ParticleEmitter("Effect", Type.Triangle,50);
 | 
			
		||||
@@ -77,7 +82,8 @@ private void initializeEmitter() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Löst die Explosion aus.
 | 
			
		||||
     * Triggers the explosion effect by attaching and activating the particle emitters for fire and smoke.
 | 
			
		||||
     * Both emitters are automatically detached after a predefined duration.
 | 
			
		||||
     */
 | 
			
		||||
    public void trigger() {
 | 
			
		||||
        if (!triggered) {
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
import com.jme3.renderer.queue.RenderQueue;
 | 
			
		||||
import com.jme3.scene.Geometry;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import pp.mdga.client.InitControl;
 | 
			
		||||
 | 
			
		||||
import static pp.mdga.client.Util.linInt;
 | 
			
		||||
 | 
			
		||||
public class FadeControl extends ActionControl {
 | 
			
		||||
    private float duration; // Duration of the fade effect
 | 
			
		||||
    private float timeElapsed = 0;
 | 
			
		||||
    private boolean init = false;
 | 
			
		||||
    private float startAlpha;
 | 
			
		||||
    private float endAlpha;
 | 
			
		||||
 | 
			
		||||
    public FadeControl(float duration, float startAlpha, float endAlpha, Runnable actionAfter) {
 | 
			
		||||
        super(actionAfter);
 | 
			
		||||
        this.duration = duration;
 | 
			
		||||
        this.startAlpha = startAlpha;
 | 
			
		||||
        this.endAlpha = endAlpha;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public FadeControl(float duration, float startAlpha, float endAlpha) {
 | 
			
		||||
        this(duration, startAlpha, endAlpha, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void initSpatial() {
 | 
			
		||||
        init = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlUpdate(float tpf) {
 | 
			
		||||
        if (!init) return;
 | 
			
		||||
 | 
			
		||||
        timeElapsed += tpf;
 | 
			
		||||
        float t = timeElapsed / duration; // Calculate progress (0 to 1)
 | 
			
		||||
 | 
			
		||||
        if (t >= 1) {
 | 
			
		||||
            // Fade complete
 | 
			
		||||
            t = 1;
 | 
			
		||||
            init = false;
 | 
			
		||||
            spatial.removeControl(this);
 | 
			
		||||
            action();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        float alpha = linInt(startAlpha, endAlpha, t); // Interpolate alpha
 | 
			
		||||
 | 
			
		||||
        // Update the material's alpha
 | 
			
		||||
        if (spatial instanceof Geometry geometry) {
 | 
			
		||||
            Material mat = geometry.getMaterial();
 | 
			
		||||
            if (mat != null) {
 | 
			
		||||
                ColorRGBA diffuse = (ColorRGBA) mat.getParam("Diffuse").getValue();
 | 
			
		||||
                mat.setColor("Diffuse", new ColorRGBA(diffuse.r, diffuse.g, diffuse.b, alpha));
 | 
			
		||||
 | 
			
		||||
                ColorRGBA ambient = (ColorRGBA) mat.getParam("Ambient").getValue();
 | 
			
		||||
                mat.setColor("Ambient", new ColorRGBA(ambient.r, ambient.g, ambient.b, alpha));
 | 
			
		||||
 | 
			
		||||
                // Disable shadows when the object is nearly invisible
 | 
			
		||||
                if (alpha <= 0.1f) {
 | 
			
		||||
                    geometry.setShadowMode(RenderQueue.ShadowMode.Off);
 | 
			
		||||
                } else {
 | 
			
		||||
                    geometry.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
 | 
			
		||||
                }
 | 
			
		||||
            } else throw new RuntimeException("Material is null");
 | 
			
		||||
        } else throw new RuntimeException("Spatial is not instance of Geometry");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -17,30 +17,36 @@
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code JetAnimation} class handles the animation of a jet model in a 3D environment.
 | 
			
		||||
 * It creates a jet model, animates its movement along a curved path, triggers an explosion at a target point,
 | 
			
		||||
 * and performs additional actions upon animation completion.
 | 
			
		||||
 */
 | 
			
		||||
public class JetAnimation {
 | 
			
		||||
 | 
			
		||||
    private final MdgaApp app;         // Referenz auf die Hauptanwendung
 | 
			
		||||
    private final Node rootNode;      // Root-Knoten, an dem die Animation hängt
 | 
			
		||||
    private Spatial jetModel;         // Das Model des "jet"
 | 
			
		||||
    private final Vector3f spawnPoint; // Spawnpunkt des Jets
 | 
			
		||||
    private final Vector3f nodePoint; // Punkt des überflogenen Knotens
 | 
			
		||||
    private final Vector3f despawnPoint; // Punkt, an dem der Jet despawnt
 | 
			
		||||
    private final float curveHeight;  // Maximale Höhe der Kurve
 | 
			
		||||
    private final float animationDuration; // Dauer der Animation
 | 
			
		||||
    private final MdgaApp app;
 | 
			
		||||
    private final Node rootNode;
 | 
			
		||||
    private Spatial jetModel;
 | 
			
		||||
    private final Vector3f spawnPoint;
 | 
			
		||||
    private final Vector3f nodePoint;
 | 
			
		||||
    private final Vector3f despawnPoint;
 | 
			
		||||
    private final float curveHeight;
 | 
			
		||||
    private final float animationDuration;
 | 
			
		||||
    private Explosion explosion;
 | 
			
		||||
    private final UUID id;
 | 
			
		||||
    private Runnable actionAfter;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Konstruktor für die ThrowAnimation-Klasse.
 | 
			
		||||
     * Constructor for the {@code JetAnimation} class.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app                Die Hauptanwendung
 | 
			
		||||
     * @param rootNode           Der Root-Knoten, an dem der Jet angefügt wird
 | 
			
		||||
     * @param uuid              Die UUID des pieces
 | 
			
		||||
     * @param targetPoint         Der Punkt, an dem der Jet spawnt
 | 
			
		||||
     * @param curveHeight        Die maximale Höhe der Flugkurve
 | 
			
		||||
     * @param animationDuration  Die Gesamtdauer der Animation in Sekunden
 | 
			
		||||
     * @param app              The main application managing the jet animation.
 | 
			
		||||
     * @param rootNode         The root node to which the jet model will be attached.
 | 
			
		||||
     * @param uuid             A unique identifier for the animation.
 | 
			
		||||
     * @param targetPoint      The target point where the explosion will occur.
 | 
			
		||||
     * @param curveHeight      The height of the curve for the jet's flight path.
 | 
			
		||||
     * @param animationDuration The total duration of the jet animation.
 | 
			
		||||
     */
 | 
			
		||||
    public JetAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f targetPoint, float curveHeight, float animationDuration) {
 | 
			
		||||
    public JetAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f targetPoint, float curveHeight, float animationDuration, Runnable actionAfter) {
 | 
			
		||||
        Vector3f spawnPoint = targetPoint.add(170, 50, 50);
 | 
			
		||||
 | 
			
		||||
        Vector3f controlPoint = targetPoint.add(new Vector3f(0, 0, -45));
 | 
			
		||||
@@ -57,11 +63,12 @@ public JetAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f targetPoint,
 | 
			
		||||
 | 
			
		||||
        id = uuid;
 | 
			
		||||
 | 
			
		||||
        explosion = new Explosion(app, rootNode, targetPoint);
 | 
			
		||||
        explosion = new Explosion(app, rootNode, nodePoint);
 | 
			
		||||
        this.actionAfter = actionAfter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Startet die Animation.
 | 
			
		||||
     * Starts the jet animation by spawning the jet model and initiating its movement along the predefined path.
 | 
			
		||||
     */
 | 
			
		||||
    public void start() {
 | 
			
		||||
        app.getAcousticHandler().playSound(MdgaSound.JET);
 | 
			
		||||
@@ -70,7 +77,7 @@ public void start() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Spawnt den Jet an der spezifizierten Position.
 | 
			
		||||
     * Spawns the jet model at the designated spawn point, applying material, scaling, and rotation.
 | 
			
		||||
     */
 | 
			
		||||
    private void spawnJet() {
 | 
			
		||||
        jetModel = app.getAssetManager().loadModel(Asset.jet.getModelPath());
 | 
			
		||||
@@ -86,7 +93,8 @@ private void spawnJet() {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Animiert den Jet entlang einer Kurve und lässt ihn anschließend verschwinden.
 | 
			
		||||
     * Animates the jet along a Bezier curve path, triggers the explosion effect at the appropriate time,
 | 
			
		||||
     * and performs cleanup operations after the animation completes.
 | 
			
		||||
     */
 | 
			
		||||
    private void animateJet() {
 | 
			
		||||
        Vector3f controlPoint1 = spawnPoint.add(0, curveHeight, 0);
 | 
			
		||||
@@ -118,26 +126,34 @@ protected void controlUpdate(float tpf) {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (elapsedTime > 6.0f) {
 | 
			
		||||
                    GameView gameView = (GameView) app.getView();
 | 
			
		||||
                    BoardHandler boardHandler = gameView.getBoardHandler();
 | 
			
		||||
 | 
			
		||||
                    boardHandler.throwPieceAnim(id);
 | 
			
		||||
                    endAnim();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void controlRender(RenderManager rm, ViewPort vp) {
 | 
			
		||||
                // Wird hier nicht benötigt
 | 
			
		||||
            }
 | 
			
		||||
            protected void controlRender(RenderManager rm, ViewPort vp) {}
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void endAnim(){
 | 
			
		||||
        actionAfter.run();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Repräsentiert eine 3D-Bezier-Kurve mit vier Kontrollpunkten.
 | 
			
		||||
     * The {@code BezierCurve3f} class represents a 3D cubic Bezier curve.
 | 
			
		||||
     * It provides methods to interpolate positions and derivatives along the curve.
 | 
			
		||||
     */
 | 
			
		||||
    private static class BezierCurve3f {
 | 
			
		||||
        private final Vector3f p0, p1, p2, p3;
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Constructor for the {@code BezierCurve3f} class.
 | 
			
		||||
         *
 | 
			
		||||
         * @param p0 The starting point of the curve.
 | 
			
		||||
         * @param p1 The first control point influencing the curve's shape.
 | 
			
		||||
         * @param p2 The second control point influencing the curve's shape.
 | 
			
		||||
         * @param p3 The endpoint of the curve.
 | 
			
		||||
         */
 | 
			
		||||
        public BezierCurve3f(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
 | 
			
		||||
            this.p0 = p0;
 | 
			
		||||
            this.p1 = p1;
 | 
			
		||||
@@ -145,6 +161,12 @@ public BezierCurve3f(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
 | 
			
		||||
            this.p3 = p3;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Interpolates a position along the curve at a given progress value {@code t}.
 | 
			
		||||
         *
 | 
			
		||||
         * @param t The progress value (0.0 to 1.0) along the curve.
 | 
			
		||||
         * @return The interpolated position on the curve.
 | 
			
		||||
         */
 | 
			
		||||
        public Vector3f interpolate(float t) {
 | 
			
		||||
            float u = 1 - t;
 | 
			
		||||
            float tt = t * t;
 | 
			
		||||
@@ -159,6 +181,12 @@ public Vector3f interpolate(float t) {
 | 
			
		||||
            return point;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Computes the derivative at a given progress value {@code t}, representing the direction along the curve.
 | 
			
		||||
         *
 | 
			
		||||
         * @param t The progress value (0.0 to 1.0) along the curve.
 | 
			
		||||
         * @return The derivative (direction vector) at the specified progress.
 | 
			
		||||
         */
 | 
			
		||||
        public Vector3f interpolateDerivative(float t) {
 | 
			
		||||
            float u = 1 - t;
 | 
			
		||||
            float tt = t * t;
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,200 @@
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
import com.jme3.effect.ParticleEmitter;
 | 
			
		||||
import com.jme3.effect.ParticleMesh.Type;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.material.RenderState;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import pp.mdga.client.InitControl;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.acoustic.MdgaSound;
 | 
			
		||||
 | 
			
		||||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
public class MatrixAnimation extends ActionControl {
 | 
			
		||||
    private MdgaApp app;
 | 
			
		||||
    private static final Random RANDOM = new Random();
 | 
			
		||||
    private Vector3f radarPos;
 | 
			
		||||
    private Runnable runnable;
 | 
			
		||||
    private boolean init = false;
 | 
			
		||||
    private List<ParticleEmitter> activeEmitter = new ArrayList<>();
 | 
			
		||||
    private ParticleEmitter radarEmitter = null;
 | 
			
		||||
    private float timeElapsed = 0f;
 | 
			
		||||
 | 
			
		||||
    private enum MatrixState{
 | 
			
		||||
        RADAR_ON,
 | 
			
		||||
        RADAR_OFF,
 | 
			
		||||
        MATRIX_ON,
 | 
			
		||||
        MATRIX_OFF
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private MatrixState state;
 | 
			
		||||
    public MatrixAnimation(MdgaApp app, Vector3f radarPos, Runnable runnable){
 | 
			
		||||
        super(runnable);
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.radarPos = radarPos;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void initSpatial() {
 | 
			
		||||
        state = MatrixState.RADAR_ON;
 | 
			
		||||
        timeElapsed = 0;
 | 
			
		||||
        init = true;
 | 
			
		||||
        radar();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlUpdate(float tpf) {
 | 
			
		||||
        if(!init) return;
 | 
			
		||||
 | 
			
		||||
        timeElapsed += tpf;
 | 
			
		||||
 | 
			
		||||
        switch(state){
 | 
			
		||||
            case RADAR_ON -> {
 | 
			
		||||
                if(timeElapsed >= 2f){
 | 
			
		||||
                    state = MatrixState.RADAR_OFF;
 | 
			
		||||
                    timeElapsed = 0;
 | 
			
		||||
                    radarEmitter.setParticlesPerSec(0);
 | 
			
		||||
                    new Timer().schedule(new TimerTask() {
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void run() {
 | 
			
		||||
                            app.getRootNode().detachChild(radarEmitter);
 | 
			
		||||
                        }
 | 
			
		||||
                    }, 3000);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            case RADAR_OFF -> {
 | 
			
		||||
                if(timeElapsed >= 0.1f){
 | 
			
		||||
                    state = MatrixState.MATRIX_ON;
 | 
			
		||||
                    timeElapsed = 0;
 | 
			
		||||
                    matrix();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
            case MATRIX_ON -> {
 | 
			
		||||
                if(timeElapsed >= 3f){
 | 
			
		||||
                    state = MatrixState.MATRIX_OFF;
 | 
			
		||||
                    timeElapsed = 0;
 | 
			
		||||
                    turnOff();
 | 
			
		||||
                    new Timer().schedule(new TimerTask() {
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public void run() {
 | 
			
		||||
                            for (ParticleEmitter particleEmitter : activeEmitter){
 | 
			
		||||
                                app.getRootNode().detachChild(particleEmitter);
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }, 3000);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            case MATRIX_OFF -> {
 | 
			
		||||
                if(timeElapsed >= 0.5f){
 | 
			
		||||
                    init = false;
 | 
			
		||||
                    spatial.removeControl(this);
 | 
			
		||||
                    action();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void turnOff(){
 | 
			
		||||
        for (ParticleEmitter particleEmitter : activeEmitter){
 | 
			
		||||
            particleEmitter.setParticlesPerSec(0f);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void radar(){
 | 
			
		||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
			
		||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Images/particle/radar_beam.png"));
 | 
			
		||||
        ParticleEmitter emitter = new ParticleEmitter("Effect", Type.Triangle, 50);
 | 
			
		||||
        emitter.setMaterial(mat);
 | 
			
		||||
        emitter.setImagesX(1); // columns
 | 
			
		||||
        emitter.setImagesY(1); // rows
 | 
			
		||||
        emitter.setSelectRandomImage(true);
 | 
			
		||||
        emitter.setStartColor(ColorRGBA.White);
 | 
			
		||||
        emitter.setEndColor(ColorRGBA.Black);
 | 
			
		||||
        emitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0f, 0f, 2));
 | 
			
		||||
        emitter.getParticleInfluencer().setVelocityVariation(0f);
 | 
			
		||||
        emitter.setStartSize(0.1f);
 | 
			
		||||
        emitter.setEndSize(10);
 | 
			
		||||
        emitter.setGravity(0, 0, 0);
 | 
			
		||||
        float life = 2.6f;
 | 
			
		||||
        emitter.setLowLife(life);
 | 
			
		||||
        emitter.setHighLife(life);
 | 
			
		||||
        emitter.setLocalTranslation(radarPos.add(new Vector3f(0,0,5)));
 | 
			
		||||
        emitter.setParticlesPerSec(1.8f);
 | 
			
		||||
        app.getRootNode().attachChild(emitter);
 | 
			
		||||
        radarEmitter = emitter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void matrix(){
 | 
			
		||||
       for(int i = 0; i < 5; i++){
 | 
			
		||||
           particleStream(
 | 
			
		||||
               generateMatrixColor(),
 | 
			
		||||
               generateMatrixColor(),
 | 
			
		||||
               getRandomFloat(0,1f),
 | 
			
		||||
               getRandomPosition(),
 | 
			
		||||
               getRandomFloat(1,2)
 | 
			
		||||
           );
 | 
			
		||||
       }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private void particleStream(ColorRGBA start, ColorRGBA end, float speedVar, Vector3f pos, float spawnVar){
 | 
			
		||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
			
		||||
        mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
 | 
			
		||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Images/particle/particle_cir.png"));
 | 
			
		||||
        ParticleEmitter matrix = new ParticleEmitter("Effect", Type.Triangle, 50);
 | 
			
		||||
        matrix.setMaterial(mat);
 | 
			
		||||
        matrix.setImagesX(2); // columns
 | 
			
		||||
        matrix.setImagesY(1); // rows
 | 
			
		||||
        matrix.setSelectRandomImage(true);
 | 
			
		||||
        matrix.setStartColor(start);
 | 
			
		||||
        matrix.setEndColor(end);
 | 
			
		||||
        matrix.getParticleInfluencer().setInitialVelocity(new Vector3f(0f, 0f, -6f - speedVar));
 | 
			
		||||
        matrix.getParticleInfluencer().setVelocityVariation(0f);
 | 
			
		||||
        matrix.setStartSize(0.4f);
 | 
			
		||||
        matrix.setEndSize(0.6f);
 | 
			
		||||
        matrix.setGravity(0, 0, 2f);
 | 
			
		||||
        matrix.setLowLife(3f);
 | 
			
		||||
        matrix.setHighLife(3f);
 | 
			
		||||
        matrix.setLocalTranslation(spatial.getLocalTranslation().add(pos).add(new Vector3f(0,0,15)));
 | 
			
		||||
        matrix.setParticlesPerSec(spawnVar);
 | 
			
		||||
        app.getRootNode().attachChild(matrix);
 | 
			
		||||
        activeEmitter.add(matrix);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static Vector3f getRandomPosition() {
 | 
			
		||||
        // Generate a random angle in radians (0 to 2π)
 | 
			
		||||
        float angle = (float) (2 * Math.PI * RANDOM.nextDouble());
 | 
			
		||||
 | 
			
		||||
        // Generate a random radius with uniform distribution
 | 
			
		||||
        float radius = (float) Math.sqrt(RANDOM.nextDouble());
 | 
			
		||||
        radius *= 1f;
 | 
			
		||||
 | 
			
		||||
        // Convert polar coordinates to Cartesian
 | 
			
		||||
        float x = radius * (float) Math.cos(angle);
 | 
			
		||||
        float y = radius * (float) Math.sin(angle);
 | 
			
		||||
 | 
			
		||||
        return new Vector3f(x,y,0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static float getRandomFloat(float start, float end) {
 | 
			
		||||
        if (start > end) {
 | 
			
		||||
            throw new IllegalArgumentException("Start must be less than or equal to end.");
 | 
			
		||||
        }
 | 
			
		||||
        return start + RANDOM.nextFloat() * (end - start);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static ColorRGBA generateMatrixColor() {
 | 
			
		||||
        // Red is dominant
 | 
			
		||||
        float red = 0.8f + RANDOM.nextFloat() * 0.2f;  // Red channel: 0.8 to 1.0
 | 
			
		||||
        // Green is moderately high
 | 
			
		||||
        float green = 0.4f + RANDOM.nextFloat() * 0.3f;  // Green channel: 0.4 to 0.7
 | 
			
		||||
        // Blue is minimal
 | 
			
		||||
        float blue = RANDOM.nextFloat() * 0.2f;  // Blue channel: 0.0 to 0.2
 | 
			
		||||
        float alpha = 1.0f;  // Fully opaque
 | 
			
		||||
 | 
			
		||||
        return new ColorRGBA(red, green, blue, alpha);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -16,25 +16,30 @@
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code MissileAnimation} class handles the animation of a missile moving along a parabolic path
 | 
			
		||||
 * towards a target point in a 3D environment. It also triggers an explosion at the target upon impact.
 | 
			
		||||
 */
 | 
			
		||||
public class MissileAnimation {
 | 
			
		||||
 | 
			
		||||
    private final Node rootNode;       // Root-Knoten, an den die Animation gehängt wird
 | 
			
		||||
    private final MdgaApp app;        // Referenz auf die Hauptanwendung
 | 
			
		||||
    private final Vector3f start;     // Startpunkt der Rakete
 | 
			
		||||
    private final Vector3f target;    // Zielpunkt der Rakete
 | 
			
		||||
    private final float flightTime;   // Gesamtdauer des Flugs
 | 
			
		||||
    private final Node rootNode;
 | 
			
		||||
    private final MdgaApp app;
 | 
			
		||||
    private final Vector3f start;
 | 
			
		||||
    private final Vector3f target;
 | 
			
		||||
    private final float flightTime;
 | 
			
		||||
    private Explosion explosion;
 | 
			
		||||
    private Spatial missileModel;     // 3D-Modell der Rakete
 | 
			
		||||
    private Spatial missileModel;
 | 
			
		||||
 | 
			
		||||
    private UUID id;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Konstruktor für die MissileAnimation.
 | 
			
		||||
     * Constructor for the {@code MissileAnimation} class.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app       Die Hauptanwendung.
 | 
			
		||||
     * @param rootNode  Der Root-Knoten, an den die Animation gehängt wird.
 | 
			
		||||
     * @param target    Der Zielpunkt der Rakete.
 | 
			
		||||
     * @param flightTime Die Zeit, die die Rakete für den gesamten Flug benötigt.
 | 
			
		||||
     * @param app       The main application managing the missile animation.
 | 
			
		||||
     * @param rootNode  The root node to which the missile model will be attached.
 | 
			
		||||
     * @param uuid      A unique identifier for the missile animation.
 | 
			
		||||
     * @param target    The target point where the missile will explode.
 | 
			
		||||
     * @param flightTime The total flight time of the missile.
 | 
			
		||||
     */
 | 
			
		||||
    public MissileAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f target, float flightTime) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
@@ -52,30 +57,33 @@ public MissileAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f target,
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Startet die Raketenanimation.
 | 
			
		||||
     * Starts the missile animation by loading the missile model and initiating its parabolic movement.
 | 
			
		||||
     */
 | 
			
		||||
    public void start() {
 | 
			
		||||
        Smoke s = new Smoke(app, rootNode, start);
 | 
			
		||||
        s.trigger();
 | 
			
		||||
        loadMissile();
 | 
			
		||||
        app.getAcousticHandler().playSound(MdgaSound.MISSILE);
 | 
			
		||||
        animateMissile();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Lädt das Raketenmodell und setzt es auf den Startpunkt.
 | 
			
		||||
     * Loads the missile model into the scene, applies scaling, material, and sets its initial position.
 | 
			
		||||
     */
 | 
			
		||||
    private void loadMissile() {
 | 
			
		||||
        missileModel = app.getAssetManager().loadModel(Asset.missile.getModelPath()); // Lade das Missile-Modell
 | 
			
		||||
        missileModel = app.getAssetManager().loadModel(Asset.missile.getModelPath());
 | 
			
		||||
        missileModel.scale(Asset.missile.getSize());
 | 
			
		||||
        missileModel.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
 | 
			
		||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
 | 
			
		||||
        mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(Asset.missile.getDiffPath()));
 | 
			
		||||
        missileModel.setMaterial(mat);
 | 
			
		||||
        missileModel.setLocalTranslation(start); // Setze Startposition
 | 
			
		||||
        rootNode.attachChild(missileModel); // Füge das Modell zur Szene hinzu
 | 
			
		||||
        missileModel.setLocalTranslation(start);
 | 
			
		||||
        rootNode.attachChild(missileModel);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Animiert die Rakete entlang einer Parabel.
 | 
			
		||||
     * Animates the missile along a parabolic path, triggers the explosion near the target,
 | 
			
		||||
     * and removes the missile model after the animation completes.
 | 
			
		||||
     */
 | 
			
		||||
    private void animateMissile() {
 | 
			
		||||
        missileModel.addControl(new AbstractControl() {
 | 
			
		||||
@@ -93,44 +101,39 @@ protected void controlUpdate(float tpf) {
 | 
			
		||||
                if (progress >= 1) {
 | 
			
		||||
                    explosion.trigger();
 | 
			
		||||
 | 
			
		||||
                    // Flug abgeschlossen
 | 
			
		||||
                    rootNode.detachChild(missileModel); // Entferne Rakete nach dem Ziel
 | 
			
		||||
                    this.spatial.removeControl(this);   // Entferne die Steuerung
 | 
			
		||||
                    rootNode.detachChild(missileModel);
 | 
			
		||||
                    this.spatial.removeControl(this);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Berechne die aktuelle Position entlang der Parabel
 | 
			
		||||
                Vector3f currentPosition = computeParabolicPath(start, target, progress);
 | 
			
		||||
                missileModel.setLocalTranslation(currentPosition);
 | 
			
		||||
 | 
			
		||||
                // Passe die Ausrichtung an (Nase der Rakete zeigt in Flugrichtung)
 | 
			
		||||
                Vector3f direction = computeParabolicPath(start, target, progress + 0.01f)
 | 
			
		||||
                    .subtract(currentPosition)
 | 
			
		||||
                    .normalizeLocal();
 | 
			
		||||
                missileModel.lookAt(currentPosition.add(direction), Vector3f.UNIT_Y); // Z ist oben, Y ist "Up"
 | 
			
		||||
                missileModel.lookAt(currentPosition.add(direction), Vector3f.UNIT_Y);
 | 
			
		||||
                missileModel.rotate(0, FastMath.HALF_PI, 0);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void controlRender(RenderManager rm, ViewPort vp) {
 | 
			
		||||
                // Keine Render-Logik benötigt
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Berechnet eine Parabelbewegung von `start` zu `target`.
 | 
			
		||||
     * Computes a position along a parabolic path at a given progress value {@code t}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param start Der Startpunkt der Rakete.
 | 
			
		||||
     * @param target Der Zielpunkt der Rakete.
 | 
			
		||||
     * @param t Der Fortschritt des Flugs (0 bis 1).
 | 
			
		||||
     * @return Die Position der Rakete entlang der Parabel.
 | 
			
		||||
     * @param start The starting point of the missile's flight.
 | 
			
		||||
     * @param target The target point of the missile's flight.
 | 
			
		||||
     * @param t The progress value (0.0 to 1.0) along the flight path.
 | 
			
		||||
     * @return The interpolated position along the parabolic path.
 | 
			
		||||
     */
 | 
			
		||||
    private Vector3f computeParabolicPath(Vector3f start, Vector3f target, float t) {
 | 
			
		||||
        Vector3f midPoint = start.add(target).multLocal(0.5f); // Berechne die Mitte zwischen Start und Ziel
 | 
			
		||||
        midPoint.addLocal(0, 0, 20); // Erhöhe den Scheitelpunkt der Parabel entlang der Z-Achse
 | 
			
		||||
        Vector3f midPoint = start.add(target).multLocal(0.5f);
 | 
			
		||||
        midPoint.addLocal(0, 0, 20);
 | 
			
		||||
 | 
			
		||||
        // Quadratische Interpolation (Parabel)
 | 
			
		||||
        Vector3f startToMid = FastMath.interpolateLinear(t, start, midPoint);
 | 
			
		||||
        Vector3f midToTarget = FastMath.interpolateLinear(t, midPoint, target);
 | 
			
		||||
        return FastMath.interpolateLinear(t, startToMid, midToTarget);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,8 @@
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import pp.mdga.client.InitControl;
 | 
			
		||||
 | 
			
		||||
import static pp.mdga.client.Util.*;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * A control that smoothly moves a spatial from an initial position to an end position
 | 
			
		||||
@@ -12,16 +13,16 @@
 | 
			
		||||
 * an ease-in-out curve to create a smooth start and stop effect.
 | 
			
		||||
 * </p>
 | 
			
		||||
 */
 | 
			
		||||
public class MoveControl extends InitControl {
 | 
			
		||||
public class MoveControl extends ActionControl {
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
    private final float height;
 | 
			
		||||
    private final float duration;
 | 
			
		||||
    private float timer = 0;
 | 
			
		||||
    private boolean easing;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new MoveControl with specified initial and end positions, and an action to run after the movement.
 | 
			
		||||
@@ -32,15 +33,22 @@ public class MoveControl extends InitControl {
 | 
			
		||||
     * @param actionAfter A Runnable that will be executed after the movement finishes.
 | 
			
		||||
     */
 | 
			
		||||
    public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter){
 | 
			
		||||
        this(initPos, endPos, actionAfter, 2, 1, true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter, float height, float duration, boolean easing){
 | 
			
		||||
        super(actionAfter);
 | 
			
		||||
        moving = false;
 | 
			
		||||
        this.initPos = initPos;
 | 
			
		||||
        this.endPos = endPos;
 | 
			
		||||
        this.height = height;
 | 
			
		||||
        this.duration = duration;
 | 
			
		||||
        this.easing = easing;
 | 
			
		||||
        middlePos = new Vector3f(
 | 
			
		||||
            (initPos.x + endPos.x) / 2,
 | 
			
		||||
            (initPos.y + endPos.y) / 2,
 | 
			
		||||
            HEIGHT
 | 
			
		||||
                (initPos.x + endPos.x) / 2,
 | 
			
		||||
                (initPos.y + endPos.y) / 2,
 | 
			
		||||
                height
 | 
			
		||||
        );
 | 
			
		||||
        this.actionAfter = actionAfter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -50,7 +58,7 @@ public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter){
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void initSpatial() {
 | 
			
		||||
        moving = true;
 | 
			
		||||
        progress = 0;
 | 
			
		||||
        timer = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -63,10 +71,16 @@ protected void initSpatial() {
 | 
			
		||||
    @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();
 | 
			
		||||
        timer += tpf;
 | 
			
		||||
 | 
			
		||||
        float t = timer / duration;
 | 
			
		||||
        if (t >= 1) t = 1;
 | 
			
		||||
 | 
			
		||||
        float interpolated = easing ? easeInOut(t) : t;
 | 
			
		||||
 | 
			
		||||
        spatial.setLocalTranslation(quadInt(initPos,middlePos,endPos, interpolated));
 | 
			
		||||
 | 
			
		||||
        if(t >= 1) end();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -75,35 +89,11 @@ protected void controlUpdate(float tpf) {
 | 
			
		||||
     */
 | 
			
		||||
    private void end(){
 | 
			
		||||
        moving = false;
 | 
			
		||||
        actionAfter.run();
 | 
			
		||||
        spatial.removeControl(this);
 | 
			
		||||
        action();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Performs quadratic interpolation between three points.
 | 
			
		||||
     *
 | 
			
		||||
     * @param p1 The initial point.
 | 
			
		||||
     * @param p2 The middle point.
 | 
			
		||||
     * @param p3 The final point.
 | 
			
		||||
     * @param t The interpolation parameter (0 <= t <= 1).
 | 
			
		||||
     * @return The interpolated point.
 | 
			
		||||
     */
 | 
			
		||||
    private Vector3f quadInt(Vector3f p1, Vector3f p2, Vector3f p3, float t) {
 | 
			
		||||
        // Quadratic interpolation: (1-t)^2 * p1 + 2 * (1-t) * t * p2 + t^2 * p3
 | 
			
		||||
        float oneMinusT = 1 - t;
 | 
			
		||||
        return p1.mult(oneMinusT * oneMinusT)
 | 
			
		||||
                 .add(p2.mult(2 * oneMinusT * t))
 | 
			
		||||
                 .add(p3.mult(t * t));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * A smooth ease-in-out function for interpolation.
 | 
			
		||||
     * It accelerates and decelerates the interpolation for a smoother effect.
 | 
			
		||||
     *
 | 
			
		||||
     * @param x The interpolation parameter (0 <= x <= 1).
 | 
			
		||||
     * @return The adjusted interpolation value.
 | 
			
		||||
     */
 | 
			
		||||
    private float easeInOut(float x){
 | 
			
		||||
            return x < 0.5 ? 4 * x * x * x : (float) (1 - Math.pow(-2 * x + 2, 3) / 2);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,167 @@
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
import com.jme3.effect.ParticleEmitter;
 | 
			
		||||
import com.jme3.effect.ParticleMesh;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.material.RenderState;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.FastMath;
 | 
			
		||||
import com.jme3.math.Quaternion;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.renderer.queue.RenderQueue;
 | 
			
		||||
import com.jme3.scene.Geometry;
 | 
			
		||||
import com.jme3.scene.Spatial;
 | 
			
		||||
import com.jme3.scene.shape.Box;
 | 
			
		||||
import pp.mdga.client.Asset;
 | 
			
		||||
import pp.mdga.client.InitControl;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.acoustic.MdgaSound;
 | 
			
		||||
import pp.mdga.client.board.TankTopControl;
 | 
			
		||||
 | 
			
		||||
import java.util.Timer;
 | 
			
		||||
import java.util.TimerTask;
 | 
			
		||||
 | 
			
		||||
import static com.jme3.material.Materials.LIGHTING;
 | 
			
		||||
import static com.jme3.material.Materials.UNSHADED;
 | 
			
		||||
 | 
			
		||||
public class ShellAnimation extends ActionControl {
 | 
			
		||||
    private static final float FLYING_DURATION = 1.25f;
 | 
			
		||||
    private static final float FLYING_HEIGHT = 12f;
 | 
			
		||||
    private TankTopControl tankTopControl;
 | 
			
		||||
    private MdgaApp app;
 | 
			
		||||
 | 
			
		||||
    public ShellAnimation(TankTopControl tankTopControl, MdgaApp app, Runnable actionAfter){
 | 
			
		||||
        super(actionAfter);
 | 
			
		||||
        this.tankTopControl = tankTopControl;
 | 
			
		||||
        this.app = app;
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void initSpatial() {
 | 
			
		||||
        tankTopControl.rotate(spatial.getLocalTranslation(), this::shoot);
 | 
			
		||||
        app.getAcousticHandler().playSound(MdgaSound.TURRET_ROTATE);
 | 
			
		||||
        app.getRootNode().attachChild(createShell());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Vector3f getShootPos(){
 | 
			
		||||
        Vector3f localOffset = new Vector3f(0, -5.4f, 2.9f);
 | 
			
		||||
        Quaternion turretRotation = tankTopControl.getSpatial().getLocalRotation();
 | 
			
		||||
        Vector3f transformedOffset = turretRotation.mult(localOffset);
 | 
			
		||||
        return tankTopControl.getSpatial().getLocalTranslation().add(transformedOffset);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void shoot(){
 | 
			
		||||
        app.getAcousticHandler().playSound(MdgaSound.TANK_SHOOT);
 | 
			
		||||
        Vector3f shootPos = getShootPos();
 | 
			
		||||
        createEffect(
 | 
			
		||||
                shootPos,
 | 
			
		||||
                "Images/particle/flame.png",
 | 
			
		||||
                2, 2,
 | 
			
		||||
                1, 3,
 | 
			
		||||
                1f,
 | 
			
		||||
                0.3f, 0.7f,
 | 
			
		||||
                new ColorRGBA(1f, 0.8f, 0.4f, 0.5f),
 | 
			
		||||
                new ColorRGBA(1f, 0f, 0f, 0f)
 | 
			
		||||
        );
 | 
			
		||||
        createEffect(
 | 
			
		||||
                shootPos,
 | 
			
		||||
                "Images/particle/vapor_cloud.png",
 | 
			
		||||
                3, 3,
 | 
			
		||||
                0.3f, 0.8f,
 | 
			
		||||
                10,
 | 
			
		||||
                0.1f, 0.35f,
 | 
			
		||||
                new ColorRGBA(0.5f,0.5f,0.5f,0.5f),
 | 
			
		||||
                ColorRGBA.Black
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        Spatial shell = createShell();
 | 
			
		||||
        app.getRootNode().attachChild(shell);
 | 
			
		||||
        shell.addControl(new ShellControl(this::hitExplosion, shootPos, spatial.getLocalTranslation(), FLYING_HEIGHT, FLYING_DURATION, app.getAssetManager()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Spatial createShell(){
 | 
			
		||||
        Spatial model = app.getAssetManager().loadModel(Asset.shell.getModelPath());
 | 
			
		||||
        model.scale(.16f);
 | 
			
		||||
        model.setLocalTranslation(tankTopControl.getSpatial().getLocalTranslation());
 | 
			
		||||
 | 
			
		||||
        Vector3f shootPos = tankTopControl.getSpatial().getLocalTranslation();
 | 
			
		||||
        Vector3f targetPos = spatial.getLocalTranslation();
 | 
			
		||||
        Vector3f direction = targetPos.subtract(shootPos).normalize();
 | 
			
		||||
 | 
			
		||||
        Quaternion rotation = new Quaternion();
 | 
			
		||||
        rotation.lookAt(direction, new Vector3f(1,0,0)); // Assuming UNIT_Y is the up vector
 | 
			
		||||
 | 
			
		||||
        model.setLocalRotation(rotation);
 | 
			
		||||
        model.rotate(FastMath.HALF_PI,0,0);
 | 
			
		||||
 | 
			
		||||
        Material mat = new Material(app.getAssetManager(), LIGHTING);
 | 
			
		||||
        mat.setBoolean("UseMaterialColors", true);
 | 
			
		||||
        ColorRGBA color = ColorRGBA.fromRGBA255(143,117,0,255);
 | 
			
		||||
        mat.setColor("Diffuse", color);
 | 
			
		||||
        mat.setColor("Ambient", color);
 | 
			
		||||
        model.setMaterial(mat);
 | 
			
		||||
        return model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void hitExplosion(){
 | 
			
		||||
        app.getAcousticHandler().playSound(MdgaSound.TANK_EXPLOSION);
 | 
			
		||||
        createEffect(
 | 
			
		||||
                spatial.getLocalTranslation().setZ(1),
 | 
			
		||||
                "Images/particle/flame.png",
 | 
			
		||||
                2, 2,
 | 
			
		||||
                1, 5,
 | 
			
		||||
                2f,
 | 
			
		||||
                0.3f, 0.7f,
 | 
			
		||||
                new ColorRGBA(1f, 0.8f, 0.4f, 0.5f),
 | 
			
		||||
                new ColorRGBA(1f, 0f, 0f, 0f)
 | 
			
		||||
        );
 | 
			
		||||
        new Timer().schedule(new TimerTask() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
                action();
 | 
			
		||||
            }
 | 
			
		||||
        }, 800);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void createEffect(Vector3f shootPos,
 | 
			
		||||
                              String image,
 | 
			
		||||
                              int x, int y,
 | 
			
		||||
                              float startSize, float endSize,
 | 
			
		||||
                              float velocity,
 | 
			
		||||
                              float lowLife, float highLife,
 | 
			
		||||
                              ColorRGBA start, ColorRGBA end){
 | 
			
		||||
        // Create a particle emitter for the explosion
 | 
			
		||||
        ParticleEmitter explosionEmitter = new ParticleEmitter("Explosion", ParticleMesh.Type.Triangle, 100);
 | 
			
		||||
        Material explosionMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
			
		||||
        explosionMat.setTexture("Texture", app.getAssetManager().loadTexture(image));
 | 
			
		||||
        explosionEmitter.setMaterial(explosionMat);
 | 
			
		||||
 | 
			
		||||
        // Particle properties
 | 
			
		||||
        explosionEmitter.setImagesX(x); // Columns in the texture
 | 
			
		||||
        explosionEmitter.setImagesY(y); // Rows in the texture
 | 
			
		||||
        explosionEmitter.setSelectRandomImage(true); // Randomize images for variety
 | 
			
		||||
 | 
			
		||||
        explosionEmitter.setStartColor(start); // Bright yellowish orange
 | 
			
		||||
        explosionEmitter.setEndColor(end); // Fade to transparent red
 | 
			
		||||
 | 
			
		||||
        explosionEmitter.setStartSize(startSize); // Initial size
 | 
			
		||||
        explosionEmitter.setEndSize(endSize); // Final size
 | 
			
		||||
        explosionEmitter.setLowLife(lowLife); // Minimum lifetime of particles
 | 
			
		||||
        explosionEmitter.setHighLife(highLife); // Maximum lifetime of particles
 | 
			
		||||
        explosionEmitter.setGravity(0, 0, 1); // Gravity to pull particles down
 | 
			
		||||
        explosionEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, velocity));
 | 
			
		||||
        explosionEmitter.getParticleInfluencer().setVelocityVariation(1f); // Adds randomness to the initial velocity
 | 
			
		||||
        explosionEmitter.setFacingVelocity(true); // Particles face their velocity direction
 | 
			
		||||
        explosionEmitter.setLocalTranslation(shootPos);
 | 
			
		||||
        explosionEmitter.setParticlesPerSec(0);
 | 
			
		||||
        explosionEmitter.emitAllParticles();
 | 
			
		||||
        app.getRootNode().attachChild(explosionEmitter);
 | 
			
		||||
        new Timer().schedule(new TimerTask() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void run() {
 | 
			
		||||
                app.getRootNode().detachChild(explosionEmitter);
 | 
			
		||||
            }
 | 
			
		||||
        }, 1000);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,89 @@
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
import com.jme3.asset.AssetManager;
 | 
			
		||||
import com.jme3.effect.ParticleEmitter;
 | 
			
		||||
import com.jme3.effect.ParticleMesh;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.FastMath;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import pp.mdga.client.InitControl;
 | 
			
		||||
 | 
			
		||||
public class ShellControl extends ActionControl {
 | 
			
		||||
    private final Vector3f shootPos;
 | 
			
		||||
    private final Vector3f endPos;
 | 
			
		||||
    private final float height;
 | 
			
		||||
    private final float duration;
 | 
			
		||||
    private Vector3f oldPos;
 | 
			
		||||
    private ParticleEmitter emitter;
 | 
			
		||||
    private AssetManager assetManager;
 | 
			
		||||
 | 
			
		||||
    public ShellControl(Runnable runnable, Vector3f shootPos, Vector3f endPos, float height, float duration, AssetManager assetManager){
 | 
			
		||||
        super(runnable);
 | 
			
		||||
        this.shootPos = shootPos;
 | 
			
		||||
        this.endPos = endPos;
 | 
			
		||||
        this.height = height;
 | 
			
		||||
        this.duration = duration;
 | 
			
		||||
        this.assetManager = assetManager;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void initSpatial() {
 | 
			
		||||
        spatial.addControl(new MoveControl(
 | 
			
		||||
                shootPos,
 | 
			
		||||
                endPos,
 | 
			
		||||
                ()->{
 | 
			
		||||
                    emitter.killAllParticles();
 | 
			
		||||
                    emitter.setParticlesPerSec(0);
 | 
			
		||||
                    emitter.removeFromParent();
 | 
			
		||||
                    spatial.removeControl(this);
 | 
			
		||||
                    spatial.removeFromParent();
 | 
			
		||||
                    action();
 | 
			
		||||
                },
 | 
			
		||||
                height,
 | 
			
		||||
                duration,
 | 
			
		||||
                false
 | 
			
		||||
        ));
 | 
			
		||||
        oldPos = spatial.getLocalTranslation().clone();
 | 
			
		||||
        createEmitter();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void createEmitter() {
 | 
			
		||||
        emitter = new ParticleEmitter("ShellTrail", ParticleMesh.Type.Triangle, 200);
 | 
			
		||||
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
 | 
			
		||||
        mat.setTexture("Texture", assetManager.loadTexture("Images/particle/line.png")); // Nutze eine schmale, linienartige Textur
 | 
			
		||||
        emitter.setMaterial(mat);
 | 
			
		||||
 | 
			
		||||
        // Comic-Style Farben
 | 
			
		||||
        emitter.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f)); // Reinweiß
 | 
			
		||||
        emitter.setEndColor(new ColorRGBA(1f, 1f, 1f, 0f)); // Transparent
 | 
			
		||||
 | 
			
		||||
        // Partikelgröße und Lebensdauer
 | 
			
		||||
        emitter.setStartSize(0.15f); // Startgröße
 | 
			
		||||
        emitter.setEndSize(0.1f); // Endgröße
 | 
			
		||||
        emitter.setLowLife(0.14f); // Sehr kurze Lebensdauer
 | 
			
		||||
        emitter.setHighLife(0.14f);
 | 
			
		||||
 | 
			
		||||
        emitter.setGravity(0, 0, 0); // Keine Gravitation
 | 
			
		||||
        emitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0));
 | 
			
		||||
        emitter.getParticleInfluencer().setVelocityVariation(0f); // Kein Variationsspielraum
 | 
			
		||||
 | 
			
		||||
        // Hohe Dichte für eine glatte Spur
 | 
			
		||||
        emitter.setParticlesPerSec(500);
 | 
			
		||||
 | 
			
		||||
        // Zur Shell hinzufügen
 | 
			
		||||
        spatial.getParent().attachChild(emitter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlUpdate(float tpf) {
 | 
			
		||||
        Vector3f direction = spatial.getLocalTranslation().subtract(oldPos).normalize();
 | 
			
		||||
        if (direction.lengthSquared() > 0) {
 | 
			
		||||
            spatial.getLocalRotation().lookAt(direction, Vector3f.UNIT_X);
 | 
			
		||||
            spatial.rotate(FastMath.HALF_PI,0,0);
 | 
			
		||||
        }
 | 
			
		||||
        oldPos = spatial.getLocalTranslation().clone();
 | 
			
		||||
 | 
			
		||||
        emitter.setLocalTranslation(spatial.getLocalTranslation().clone());
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,126 @@
 | 
			
		||||
package pp.mdga.client.animation;
 | 
			
		||||
 | 
			
		||||
import com.jme3.effect.ParticleEmitter;
 | 
			
		||||
import com.jme3.effect.ParticleMesh;
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.scene.Node;
 | 
			
		||||
import com.jme3.scene.control.AbstractControl;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.acoustic.MdgaSound;
 | 
			
		||||
 | 
			
		||||
public class Smoke {
 | 
			
		||||
 | 
			
		||||
    private final Node rootNode;
 | 
			
		||||
    private final MdgaApp app;
 | 
			
		||||
    private final Vector3f location;
 | 
			
		||||
    private ParticleEmitter fire;
 | 
			
		||||
    private ParticleEmitter smoke;
 | 
			
		||||
 | 
			
		||||
    private boolean triggered = false;
 | 
			
		||||
 | 
			
		||||
    private final Material mat;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructor for the {@code Explosion} class.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app      The main application managing the explosion.
 | 
			
		||||
     * @param rootNode The root node to which the explosion effects will be attached.
 | 
			
		||||
     * @param location The location of the explosion in world coordinates.
 | 
			
		||||
     */
 | 
			
		||||
    public Smoke(MdgaApp app, Node rootNode, Vector3f location) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.rootNode = rootNode;
 | 
			
		||||
        this.location = location;
 | 
			
		||||
 | 
			
		||||
        this.mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initializes the particle emitters for the explosion effect.
 | 
			
		||||
     * Configures the fire and smoke emitters with appearance, behavior, and lifespan.
 | 
			
		||||
     */
 | 
			
		||||
    private void initializeEmitter() {
 | 
			
		||||
        fire = new ParticleEmitter("Effect", ParticleMesh.Type.Triangle,50);
 | 
			
		||||
        fire.setMaterial(mat);
 | 
			
		||||
        fire.setStartColor(ColorRGBA.DarkGray);
 | 
			
		||||
        fire.setEndColor(ColorRGBA.DarkGray);
 | 
			
		||||
        fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0.2f,0.2f,8f));
 | 
			
		||||
        fire.getParticleInfluencer().setVelocityVariation(0.4f);
 | 
			
		||||
        fire.setStartSize(0.1f);
 | 
			
		||||
        fire.setEndSize(0.8f);
 | 
			
		||||
        fire.setGravity(0, 0, -0.1f);
 | 
			
		||||
        fire.setLowLife(0.5f);
 | 
			
		||||
        fire.setHighLife(1.2f);
 | 
			
		||||
        fire.setParticlesPerSec(0);
 | 
			
		||||
 | 
			
		||||
        fire.setLocalTranslation(location);
 | 
			
		||||
 | 
			
		||||
        smoke = new ParticleEmitter("Effect2", ParticleMesh.Type.Triangle,40);
 | 
			
		||||
        smoke.setMaterial(mat);
 | 
			
		||||
        smoke.setImagesX(2);
 | 
			
		||||
        smoke.setImagesY(2);
 | 
			
		||||
        smoke.setStartColor(ColorRGBA.DarkGray);
 | 
			
		||||
        smoke.setEndColor(new ColorRGBA(0.05f, 0.05f, 0.05f, 1));
 | 
			
		||||
        smoke.getParticleInfluencer().setInitialVelocity(new Vector3f(0.0f,0.0f,2f));
 | 
			
		||||
        smoke.getParticleInfluencer().setVelocityVariation(0.5f);
 | 
			
		||||
        smoke.setStartSize(0.2f);
 | 
			
		||||
        smoke.setEndSize(0.5f);
 | 
			
		||||
        smoke.setGravity(0, 0, -0.3f);
 | 
			
		||||
        smoke.setLowLife(1.2f);
 | 
			
		||||
        smoke.setHighLife(2.5f);
 | 
			
		||||
        smoke.setParticlesPerSec(0);
 | 
			
		||||
 | 
			
		||||
        smoke.setLocalTranslation(location);
 | 
			
		||||
 | 
			
		||||
        app.getAcousticHandler().playSound(MdgaSound.EXPLOSION);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Triggers the explosion effect by attaching and activating the particle emitters for fire and smoke.
 | 
			
		||||
     * Both emitters are automatically detached after a predefined duration.
 | 
			
		||||
     */
 | 
			
		||||
    public void trigger() {
 | 
			
		||||
        if (!triggered) {
 | 
			
		||||
            triggered = true;
 | 
			
		||||
            initializeEmitter();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        rootNode.attachChild(fire);
 | 
			
		||||
        fire.emitAllParticles();
 | 
			
		||||
        fire.addControl(new AbstractControl() {
 | 
			
		||||
            private float elapsedTime = 0;
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void controlUpdate(float tpf) {
 | 
			
		||||
                elapsedTime += tpf;
 | 
			
		||||
                if (elapsedTime > 10f) {
 | 
			
		||||
                    rootNode.detachChild(fire);
 | 
			
		||||
                    fire.removeControl(this);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {}
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        rootNode.attachChild(smoke);
 | 
			
		||||
        smoke.emitAllParticles();
 | 
			
		||||
        smoke.addControl(new AbstractControl() {
 | 
			
		||||
            private float elapsedTime = 0;
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void controlUpdate(float tpf) {
 | 
			
		||||
                elapsedTime += tpf;
 | 
			
		||||
                if (elapsedTime > 10f) {
 | 
			
		||||
                    rootNode.detachChild(smoke);
 | 
			
		||||
                    smoke.removeControl(this);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {}
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,9 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
import com.jme3.material.Material;
 | 
			
		||||
import com.jme3.material.RenderState;
 | 
			
		||||
import com.jme3.material.RenderState.BlendMode;
 | 
			
		||||
import com.jme3.math.ColorRGBA;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import com.jme3.post.FilterPostProcessor;
 | 
			
		||||
import com.jme3.renderer.queue.RenderQueue;
 | 
			
		||||
@@ -10,9 +13,7 @@
 | 
			
		||||
import pp.mdga.client.Asset;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
import pp.mdga.client.acoustic.MdgaSound;
 | 
			
		||||
import pp.mdga.client.animation.MissileAnimation;
 | 
			
		||||
import pp.mdga.client.animation.MoveControl;
 | 
			
		||||
import pp.mdga.client.animation.JetAnimation;
 | 
			
		||||
import pp.mdga.client.animation.*;
 | 
			
		||||
import pp.mdga.client.gui.DiceControl;
 | 
			
		||||
import pp.mdga.game.Color;
 | 
			
		||||
 | 
			
		||||
@@ -56,6 +57,10 @@ public class BoardHandler {
 | 
			
		||||
    private PieceControl selectedOwnPiece;
 | 
			
		||||
    private PieceControl selectedEnemyPiece;
 | 
			
		||||
    private DiceControl diceControl;
 | 
			
		||||
    //Radar Position for Matrix animation
 | 
			
		||||
    private Vector3f radarPos;
 | 
			
		||||
    //TankTop for shellAnimation
 | 
			
		||||
    private TankTopControl tankTop;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new BoardHandler.
 | 
			
		||||
@@ -148,12 +153,24 @@ private void initMap() {
 | 
			
		||||
                case node_wait_blue -> addHomeNode(waitingNodesMap, Color.NAVY, assetOnMap);
 | 
			
		||||
                case node_wait_green -> addHomeNode(waitingNodesMap, Color.ARMY, assetOnMap);
 | 
			
		||||
                case node_wait_yellow -> addHomeNode(waitingNodesMap, Color.CYBER, assetOnMap);
 | 
			
		||||
                case radar -> addRadar(assetOnMap);
 | 
			
		||||
                case tankShoot -> addTankShoot(assetOnMap);
 | 
			
		||||
 | 
			
		||||
                default -> displayAsset(assetOnMap);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void addTankShoot(AssetOnMap assetOnMap) {
 | 
			
		||||
        displayAsset(assetOnMap);
 | 
			
		||||
        tankTop = displayAndControl(new AssetOnMap(Asset.tankShootTop, assetOnMap.x(), assetOnMap.y(), assetOnMap.rot()), new TankTopControl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void addRadar(AssetOnMap assetOnMap) {
 | 
			
		||||
        radarPos = gridToWorld(assetOnMap.x(), assetOnMap.y());
 | 
			
		||||
        displayAsset(assetOnMap);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts an asset to its corresponding color.
 | 
			
		||||
     *
 | 
			
		||||
@@ -187,11 +204,16 @@ private Spatial createModel(Asset asset, Vector3f pos, float rot) {
 | 
			
		||||
        model.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(rot));
 | 
			
		||||
        model.setLocalTranslation(pos);
 | 
			
		||||
        model.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
 | 
			
		||||
 | 
			
		||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
 | 
			
		||||
        mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(texName));
 | 
			
		||||
        mat.setBoolean("UseMaterialColors", true); // Required for Material Colors
 | 
			
		||||
        mat.setColor("Diffuse", new ColorRGBA(1, 1, 1, 1)); // White color with full alpha
 | 
			
		||||
        mat.setColor("Ambient", new ColorRGBA(1, 1, 1, 1)); // Ambient color with full alpha
 | 
			
		||||
        mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
 | 
			
		||||
        model.setMaterial(mat);
 | 
			
		||||
        rootNodeBoard.attachChild(model);
 | 
			
		||||
 | 
			
		||||
        rootNodeBoard.attachChild(model);
 | 
			
		||||
        return model;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -218,16 +240,38 @@ private Spatial displayAsset(AssetOnMap assetOnMap) {
 | 
			
		||||
        return createModel(assetOnMap.asset(), gridToWorld(x, y), assetOnMap.rot());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds a visual representation of an asset to the scene, attaches a control to it, and returns the control.
 | 
			
		||||
     *
 | 
			
		||||
     * @param assetOnMap The asset to be displayed in the 3D environment.
 | 
			
		||||
     * @param control    The control to be added to the spatial representing the asset.
 | 
			
		||||
     * @param <T>        The type of control, extending {@code AbstractControl}.
 | 
			
		||||
     * @return The control that was added to the spatial.
 | 
			
		||||
     */
 | 
			
		||||
    private <T extends AbstractControl> T displayAndControl(AssetOnMap assetOnMap, T control) {
 | 
			
		||||
        Spatial spatial = displayAsset(assetOnMap);
 | 
			
		||||
        spatial.addControl(control);
 | 
			
		||||
        return control;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Moves a piece in the 3D environment to the location of a specified node.
 | 
			
		||||
     *
 | 
			
		||||
     * @param pieceControl The control managing the piece to be moved.
 | 
			
		||||
     * @param nodeControl  The control managing the target node to which the piece will move.
 | 
			
		||||
     */
 | 
			
		||||
    private void movePieceToNode(PieceControl pieceControl, NodeControl nodeControl){
 | 
			
		||||
        pieceControl.setLocation(nodeControl.getLocation());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds a home node for a specific player color, attaching it to the map of home nodes.
 | 
			
		||||
     *
 | 
			
		||||
     * @param map          The map storing lists of home nodes by player color.
 | 
			
		||||
     * @param color        The color associated with the home nodes to be added.
 | 
			
		||||
     * @param assetOnMap   The asset representing the home node in the 3D environment.
 | 
			
		||||
     * @throws RuntimeException if more than 4 home nodes are added for a single color.
 | 
			
		||||
     */
 | 
			
		||||
    private void addHomeNode(Map<Color, List<NodeControl>> map, Color color, AssetOnMap assetOnMap){
 | 
			
		||||
        List<NodeControl> homeNodes = addItemToMapList(map, color, displayAndControl(assetOnMap, new NodeControl(app, fpp)));
 | 
			
		||||
        if (homeNodes.size() > 4) throw new RuntimeException("too many homeNodes for " + color);
 | 
			
		||||
@@ -271,6 +315,16 @@ private void movePieceRek(UUID uuid, int curIndex, int moveIndex){
 | 
			
		||||
        movePieceRek(uuid, curIndex, moveIndex);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds an item to a list in a map. If the key does not exist in the map, a new list is created.
 | 
			
		||||
     *
 | 
			
		||||
     * @param map  The map containing lists of items.
 | 
			
		||||
     * @param key  The key associated with the list in the map.
 | 
			
		||||
     * @param item The item to be added to the list.
 | 
			
		||||
     * @param <T>  The type of items in the list.
 | 
			
		||||
     * @param <E>  The type of the key in the map.
 | 
			
		||||
     * @return The updated list associated with the specified key.
 | 
			
		||||
     */
 | 
			
		||||
    private <T, E> List<T> addItemToMapList(Map<E,List<T>> map, E key, T item){
 | 
			
		||||
        List<T> list = map.getOrDefault(key, new ArrayList<>());
 | 
			
		||||
        list.add(item);
 | 
			
		||||
@@ -278,12 +332,27 @@ private <T, E> List<T> addItemToMapList(Map<E,List<T>> map, E key, T item){
 | 
			
		||||
        return list;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Removes an item from a list in a map. If the key does not exist in the map, a new list is created.
 | 
			
		||||
     *
 | 
			
		||||
     * @param map  The map containing lists of items.
 | 
			
		||||
     * @param key  The key associated with the list in the map.
 | 
			
		||||
     * @param item The item to be removed from the list.
 | 
			
		||||
     * @param <T>  The type of items in the list.
 | 
			
		||||
     * @param <E>  The type of the key in the map.
 | 
			
		||||
     */
 | 
			
		||||
    private <T, E> void removeItemFromMapList(Map<E,List<T>> map, E key, T item){
 | 
			
		||||
        List<T> list = map.getOrDefault(key, new ArrayList<>());
 | 
			
		||||
        list.remove(item);
 | 
			
		||||
        map.put(key, list);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculates the mean position of the waiting nodes for a specific color.
 | 
			
		||||
     *
 | 
			
		||||
     * @param color The color associated with the waiting nodes.
 | 
			
		||||
     * @return The mean position of the waiting nodes as a {@code Vector3f}.
 | 
			
		||||
     */
 | 
			
		||||
    private Vector3f getWaitingPos(Color color){
 | 
			
		||||
        return getMeanPosition(waitingNodesMap.get(color).stream().map(NodeControl::getLocation).toList());
 | 
			
		||||
    }
 | 
			
		||||
@@ -723,34 +792,57 @@ public void movePieceStartAnim(UUID uuid, int moveIndex){
 | 
			
		||||
     */
 | 
			
		||||
    public void throwPieceAnim(UUID uuid){
 | 
			
		||||
        pieces.get(uuid).getSpatial().addControl(new MoveControl(
 | 
			
		||||
            pieces.get(uuid).getLocation(), getNextWaitingNode(pieceColor.get(uuid)).getLocation(), ()->throwPiece(uuid))
 | 
			
		||||
            pieces.get(uuid).getLocation(), getNextWaitingNode(pieceColor.get(uuid)).getLocation(),
 | 
			
		||||
                ()->throwPiece(uuid))
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Animates the throwing of a piece to the next available waiting node and plays jet animation.
 | 
			
		||||
     *
 | 
			
		||||
     * @param uuid the UUID of the piece to animate
 | 
			
		||||
     */
 | 
			
		||||
    public void throwBombAnim(UUID uuid){
 | 
			
		||||
        Vector3f targetPoint = pieces.get(uuid).getLocation();
 | 
			
		||||
 | 
			
		||||
        JetAnimation anim = new JetAnimation(app, rootNode, uuid, targetPoint, 40, 6);
 | 
			
		||||
        anim.start();
 | 
			
		||||
    public void throwPiece(UUID uuid, Color throwColor){
 | 
			
		||||
        switch(throwColor){
 | 
			
		||||
            case ARMY -> throwShell(uuid);
 | 
			
		||||
            case NAVY -> throwMissle(uuid);
 | 
			
		||||
            case CYBER -> throwMatrix(uuid);
 | 
			
		||||
            case AIRFORCE -> throwBomb(uuid);
 | 
			
		||||
            default -> throw new RuntimeException("invalid color");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Animates the throwing of a piece to the next available waiting node and plays ship animation.
 | 
			
		||||
     * Animates the throwing of a piece to the next available waiting node.
 | 
			
		||||
     *
 | 
			
		||||
     * @param uuid the UUID of the piece to animate
 | 
			
		||||
     */
 | 
			
		||||
    public void throwMissileAnim(UUID uuid){
 | 
			
		||||
    private void throwBomb(UUID uuid) {
 | 
			
		||||
        Vector3f targetPoint = pieces.get(uuid).getLocation();
 | 
			
		||||
 | 
			
		||||
        JetAnimation anim = new JetAnimation(app, rootNode, uuid, targetPoint, 40, 6, ()->throwPieceAnim(uuid));
 | 
			
		||||
        anim.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void throwMatrix(UUID uuid) {
 | 
			
		||||
        //app.getAcousticHandler().playSound(MdgaSound.MATRIX);
 | 
			
		||||
        Spatial piece = pieces.get(uuid).getSpatial();
 | 
			
		||||
        piece.addControl(new MatrixAnimation(app, radarPos,()-> {
 | 
			
		||||
            piece.addControl(new FadeControl(1,1,0,
 | 
			
		||||
                    () -> {
 | 
			
		||||
                        throwPiece(uuid);
 | 
			
		||||
                        piece.addControl(new FadeControl(1,0,1));
 | 
			
		||||
                    }
 | 
			
		||||
            ));
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void throwMissle(UUID uuid) {
 | 
			
		||||
        Vector3f targetPoint = pieces.get(uuid).getLocation();
 | 
			
		||||
 | 
			
		||||
        MissileAnimation anim = new MissileAnimation(app, rootNode, uuid, targetPoint, 2);
 | 
			
		||||
        anim.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void throwShell(UUID uuid) {
 | 
			
		||||
        pieces.get(uuid).getSpatial().addControl(new ShellAnimation(tankTop, app, ()-> throwPieceAnim(uuid)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Animates the swapping of two pieces by swapping their positions and rotations.
 | 
			
		||||
     *
 | 
			
		||||
 
 | 
			
		||||
@@ -109,6 +109,9 @@ private static Asset getLoadedAsset(String assetName) {
 | 
			
		||||
            case "tank" -> Asset.tank;
 | 
			
		||||
            case "treeSmall" -> Asset.treeSmall;
 | 
			
		||||
            case "treeBig" -> Asset.treeBig;
 | 
			
		||||
            case "tank_shoot" -> Asset.tankShoot;
 | 
			
		||||
            case "treesBigBackground" -> Asset.treesBigBackground;
 | 
			
		||||
            case "treesSmallBackground" -> Asset.treesSmallBackground;
 | 
			
		||||
            default -> throw new IllegalStateException("Unexpected value: " + assetName);
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -18,17 +18,37 @@ public class OutlineControl extends InitControl {
 | 
			
		||||
    private static final int THICKNESS_DEFAULT = 6;
 | 
			
		||||
    private MdgaApp app;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs an {@code OutlineControl} with default thickness for the object outline.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app The main application managing the outline control.
 | 
			
		||||
     * @param fpp The {@code FilterPostProcessor} used for post-processing effects.
 | 
			
		||||
     */
 | 
			
		||||
    public OutlineControl(MdgaApp app, FilterPostProcessor fpp){
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), app.getCamera(), app);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs an {@code OutlineControl} with default thickness, allowing a custom camera to be specified.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app The main application managing the outline control.
 | 
			
		||||
     * @param fpp The {@code FilterPostProcessor} used for post-processing effects.
 | 
			
		||||
     * @param cam The camera used for rendering the outlined objects.
 | 
			
		||||
     */
 | 
			
		||||
    public OutlineControl(MdgaApp app, FilterPostProcessor fpp, Camera cam){
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), cam, app);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs an {@code OutlineControl} with a specified thickness and custom camera.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app       The main application managing the outline control.
 | 
			
		||||
     * @param fpp       The {@code FilterPostProcessor} used for post-processing effects.
 | 
			
		||||
     * @param cam       The camera used for rendering the outlined objects.
 | 
			
		||||
     * @param thickness The thickness of the outline.
 | 
			
		||||
     */
 | 
			
		||||
    public OutlineControl(MdgaApp app, FilterPostProcessor fpp, Camera cam, int thickness){
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        outlineOwn = new SelectObjectOutliner(thickness, fpp, app.getRenderManager(), app.getAssetManager(), cam, app);
 | 
			
		||||
@@ -61,6 +81,11 @@ public void deOutline(){
 | 
			
		||||
        outlineOwn.deselect(spatial);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the instance of the {@code MdgaApp} associated with this control.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The {@code MdgaApp} instance.
 | 
			
		||||
     */
 | 
			
		||||
    public MdgaApp getApp() {
 | 
			
		||||
        return app;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -141,7 +141,7 @@ public void initSpatial(){
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void rotateInit() {
 | 
			
		||||
//        rotate(rotation - initRotation);
 | 
			
		||||
        setRotation(initRotation);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -278,4 +278,5 @@ public boolean isSelectable() {
 | 
			
		||||
    public void setHoverable(boolean hoverable) {
 | 
			
		||||
        this.hoverable = hoverable;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,105 @@
 | 
			
		||||
package pp.mdga.client.board;
 | 
			
		||||
 | 
			
		||||
import com.jme3.math.FastMath;
 | 
			
		||||
import com.jme3.math.Quaternion;
 | 
			
		||||
import com.jme3.math.Vector3f;
 | 
			
		||||
import pp.mdga.client.InitControl;
 | 
			
		||||
 | 
			
		||||
import static pp.mdga.client.Util.linInt;
 | 
			
		||||
 | 
			
		||||
public class TankTopControl extends InitControl {
 | 
			
		||||
 | 
			
		||||
    private float timer = 0; // Time elapsed
 | 
			
		||||
    private final static float DURATION = 1.5f; // Total rotation duration in seconds
 | 
			
		||||
    private boolean rotating = false; // Flag to track if rotation is active
 | 
			
		||||
    private float startAngle = 0;
 | 
			
		||||
    private float endAngle = 0;
 | 
			
		||||
    private Runnable actionAfter = null;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void controlUpdate(float tpf) {
 | 
			
		||||
        if (!rotating) return;
 | 
			
		||||
 | 
			
		||||
        // Update the timer
 | 
			
		||||
        timer += tpf;
 | 
			
		||||
 | 
			
		||||
        // Calculate interpolation factor (0 to 1)
 | 
			
		||||
        float t = timer / DURATION;
 | 
			
		||||
 | 
			
		||||
        if (t >= 1) t = 1;
 | 
			
		||||
 | 
			
		||||
        float curAngle = linInt(startAngle, endAngle, t);
 | 
			
		||||
 | 
			
		||||
        // Interpolate the rotation
 | 
			
		||||
        Quaternion interpolatedRotation = new Quaternion();
 | 
			
		||||
        interpolatedRotation.fromAngleAxis((float) Math.toRadians(curAngle), Vector3f.UNIT_Z);
 | 
			
		||||
 | 
			
		||||
        // Apply the interpolated rotation to the spatial
 | 
			
		||||
        spatial.setLocalRotation(interpolatedRotation);
 | 
			
		||||
 | 
			
		||||
        if(t >= 1){
 | 
			
		||||
            rotating = false;
 | 
			
		||||
            if(actionAfter != null) actionAfter.run();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void rotate(Vector3f enemyPos, Runnable actionAfter) {
 | 
			
		||||
        if (spatial == null) throw new RuntimeException("spatial is null");
 | 
			
		||||
 | 
			
		||||
        startAngle = getOwnAngle();
 | 
			
		||||
        endAngle = getEnemyAngle(enemyPos);
 | 
			
		||||
 | 
			
		||||
        // Adjust endAngle to ensure the shortest path
 | 
			
		||||
        float deltaAngle = endAngle - startAngle;
 | 
			
		||||
        if (deltaAngle > 180) {
 | 
			
		||||
            endAngle -= 360; // Rotate counterclockwise
 | 
			
		||||
        } else if (deltaAngle < -180) {
 | 
			
		||||
            endAngle += 360; // Rotate clockwise
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        timer = 0;
 | 
			
		||||
        rotating = true;
 | 
			
		||||
        this.actionAfter = actionAfter; // Store the action to execute after rotation
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private float getEnemyAngle(Vector3f enemyPos){
 | 
			
		||||
        // Direction to the enemy in the XY plane
 | 
			
		||||
        Vector3f direction = enemyPos.subtract(spatial.getLocalTranslation());
 | 
			
		||||
        direction.z = 0; // Project to XY plane
 | 
			
		||||
        direction.normalizeLocal();
 | 
			
		||||
 | 
			
		||||
        Vector3f reference = Vector3f.UNIT_Y.mult(-1);
 | 
			
		||||
 | 
			
		||||
        // Calculate the angle between the direction vector and the reference vector
 | 
			
		||||
        float angle = FastMath.acos(reference.dot(direction));
 | 
			
		||||
 | 
			
		||||
        // Determine rotation direction using the cross product
 | 
			
		||||
        Vector3f cross = reference.cross(direction);
 | 
			
		||||
        if (cross.z < 0) {
 | 
			
		||||
            angle = -angle;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return (float) Math.toDegrees(angle); // Return the absolute angle in degrees
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private float getOwnAngle() {
 | 
			
		||||
        // Tank's forward direction in the XY plane
 | 
			
		||||
        Vector3f forward = spatial.getLocalRotation().mult(Vector3f.UNIT_Y);
 | 
			
		||||
        forward.z = 0; // Project to XY plane
 | 
			
		||||
        forward.normalizeLocal();
 | 
			
		||||
 | 
			
		||||
        // Reference vector: Positive X-axis
 | 
			
		||||
        Vector3f reference = Vector3f.UNIT_Y;
 | 
			
		||||
 | 
			
		||||
        // Calculate the angle between the forward vector and the reference vector
 | 
			
		||||
        float angle = FastMath.acos(reference.dot(forward));
 | 
			
		||||
 | 
			
		||||
        // Determine rotation direction using the cross product
 | 
			
		||||
        Vector3f cross = reference.cross(forward);
 | 
			
		||||
        if (cross.z < 0) { // For Z-up, check the Z component of the cross product
 | 
			
		||||
            angle = -angle;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return (float) Math.toDegrees(angle); // Return the absolute angle in radians
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -77,12 +77,17 @@ public SliderButton(MdgaApp app, Node node, String label) {
 | 
			
		||||
        QuadBackgroundComponent background = new QuadBackgroundComponent(BUTTON_NORMAL);
 | 
			
		||||
        slider.setBackground(background);
 | 
			
		||||
 | 
			
		||||
        // Set label background
 | 
			
		||||
        QuadBackgroundComponent labelBackground = new QuadBackgroundComponent(BUTTON_NORMAL);
 | 
			
		||||
        this.label.setBackground(labelBackground);
 | 
			
		||||
 | 
			
		||||
        // Configure the label font
 | 
			
		||||
        this.label.setFont(font);
 | 
			
		||||
        this.label.setTextHAlignment(HAlignment.Center);
 | 
			
		||||
 | 
			
		||||
        // Default position and size
 | 
			
		||||
        pos = new Vector2f(0, 0);
 | 
			
		||||
        size = new Vector2f(5.5f, 1);
 | 
			
		||||
        size = new Vector2f(6f, 1);
 | 
			
		||||
 | 
			
		||||
        // Add label and slider to container
 | 
			
		||||
        container.addChild(this.label);
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,11 @@
 | 
			
		||||
import pp.mdga.client.button.SliderButton;
 | 
			
		||||
import pp.mdga.client.view.MdgaView;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code AudioSettingsDialog} class represents a dialog for adjusting audio settings in the application.
 | 
			
		||||
 * It provides controls for managing main volume, music volume, and sound effect volume, and includes
 | 
			
		||||
 * a button to return to the previous menu.
 | 
			
		||||
 */
 | 
			
		||||
public class AudioSettingsDialog extends Dialog {
 | 
			
		||||
    private final MdgaView view;
 | 
			
		||||
 | 
			
		||||
@@ -18,6 +23,13 @@ public class AudioSettingsDialog extends Dialog {
 | 
			
		||||
 | 
			
		||||
    private boolean active = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs an {@code AudioSettingsDialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app  The main application managing the dialog.
 | 
			
		||||
     * @param node The root node for attaching UI elements.
 | 
			
		||||
     * @param view The current view, used for navigation and interaction with the dialog.
 | 
			
		||||
     */
 | 
			
		||||
    public AudioSettingsDialog(MdgaApp app, Node node, MdgaView view) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
@@ -42,6 +54,9 @@ public AudioSettingsDialog(MdgaApp app, Node node, MdgaView view) {
 | 
			
		||||
        backButton.setPos(new Vector2f(0, 1.8f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is shown. Initializes and displays the volume controls and back button.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onShow() {
 | 
			
		||||
        active = true;
 | 
			
		||||
@@ -57,6 +72,9 @@ protected void onShow() {
 | 
			
		||||
        soundVolume.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is hidden. Hides all volume controls and the back button.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onHide() {
 | 
			
		||||
        active = false;
 | 
			
		||||
@@ -68,6 +86,10 @@ protected void onHide() {
 | 
			
		||||
        soundVolume.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the application audio settings based on the current values of the sliders.
 | 
			
		||||
     * This method is called continuously while the dialog is active.
 | 
			
		||||
     */
 | 
			
		||||
    public void update() {
 | 
			
		||||
        if(!active) {
 | 
			
		||||
            return;
 | 
			
		||||
 
 | 
			
		||||
@@ -10,17 +10,30 @@
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code CeremonyDialog} class displays a dialog containing statistical data in a tabular format.
 | 
			
		||||
 * It allows adding rows of statistics and manages their visibility when shown or hidden.
 | 
			
		||||
 */
 | 
			
		||||
public class CeremonyDialog extends Dialog {
 | 
			
		||||
    private ArrayList<ArrayList<LabelButton>> labels;
 | 
			
		||||
 | 
			
		||||
    float offsetX;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a {@code CeremonyDialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app  The main application managing the dialog.
 | 
			
		||||
     * @param node The root node for attaching UI elements.
 | 
			
		||||
     */
 | 
			
		||||
    public CeremonyDialog(MdgaApp app, Node node) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        prepare();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is shown. Makes all label buttons in the table visible.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onShow() {
 | 
			
		||||
        for (ArrayList<LabelButton> row : labels) {
 | 
			
		||||
@@ -30,6 +43,9 @@ protected void onShow() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is hidden. Hides all label buttons in the table.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onHide() {
 | 
			
		||||
        for (ArrayList<LabelButton> row : labels) {
 | 
			
		||||
@@ -39,6 +55,17 @@ protected void onHide() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds a row of statistical data to the dialog.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name The name of the player or category for the row.
 | 
			
		||||
     * @param v1   The value for the first column.
 | 
			
		||||
     * @param v2   The value for the second column.
 | 
			
		||||
     * @param v3   The value for the third column.
 | 
			
		||||
     * @param v4   The value for the fourth column.
 | 
			
		||||
     * @param v5   The value for the fifth column.
 | 
			
		||||
     * @param v6   The value for the sixth column.
 | 
			
		||||
     */
 | 
			
		||||
    public void addStatisticsRow(String name, int v1, int v2, int v3, int v4, int v5, int v6) {
 | 
			
		||||
        float offsetYSmall = 0.5f;
 | 
			
		||||
 | 
			
		||||
@@ -76,6 +103,9 @@ public void addStatisticsRow(String name, int v1, int v2, int v3, int v4, int v5
 | 
			
		||||
        labels.add(row);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Prepares the initial layout of the dialog, including header labels.
 | 
			
		||||
     */
 | 
			
		||||
    public void prepare() {
 | 
			
		||||
        offsetX = 0.5f;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,30 +4,53 @@
 | 
			
		||||
import com.simsilica.lemur.Container;
 | 
			
		||||
import pp.mdga.client.MdgaApp;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code Dialog} class serves as an abstract base class for dialogs in the application.
 | 
			
		||||
 * It provides functionality for showing and hiding the dialog and defines abstract methods
 | 
			
		||||
 * for custom behavior when the dialog is shown or hidden.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class Dialog {
 | 
			
		||||
    protected final MdgaApp app;
 | 
			
		||||
    protected final Node node = new Node();
 | 
			
		||||
 | 
			
		||||
    private final Node root;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a {@code Dialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app  The main application managing the dialog.
 | 
			
		||||
     * @param node The root node to which the dialog's node will be attached.
 | 
			
		||||
     */
 | 
			
		||||
    Dialog(MdgaApp app, Node node) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.root = node;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Shows the dialog by attaching its node to the root node and invoking the {@code onShow} method.
 | 
			
		||||
     */
 | 
			
		||||
    public void show() {
 | 
			
		||||
        root.attachChild(node);
 | 
			
		||||
 | 
			
		||||
        onShow();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Hides the dialog by detaching its node from the root node and invoking the {@code onHide} method.
 | 
			
		||||
     */
 | 
			
		||||
    public void hide() {
 | 
			
		||||
        root.detachChild(node);
 | 
			
		||||
 | 
			
		||||
        onHide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is shown. Subclasses must implement this method to define custom behavior.
 | 
			
		||||
     */
 | 
			
		||||
    protected abstract void onShow();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is hidden. Subclasses must implement this method to define custom behavior.
 | 
			
		||||
     */
 | 
			
		||||
    protected abstract void onHide();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,10 @@
 | 
			
		||||
 | 
			
		||||
import java.util.prefs.Preferences;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code HostDialog} class represents a dialog for hosting a network game session.
 | 
			
		||||
 * It allows users to input a port number, start hosting a server, and navigate back to the previous view.
 | 
			
		||||
 */
 | 
			
		||||
public class HostDialog  extends NetworkDialog {
 | 
			
		||||
    private InputButton portInput;
 | 
			
		||||
 | 
			
		||||
@@ -22,6 +26,13 @@ public class HostDialog  extends NetworkDialog {
 | 
			
		||||
 | 
			
		||||
    private Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a {@code HostDialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app  The main application managing the dialog.
 | 
			
		||||
     * @param node The root node for attaching UI elements.
 | 
			
		||||
     * @param view The main view used for navigation and interaction with the dialog.
 | 
			
		||||
     */
 | 
			
		||||
    public HostDialog(MdgaApp app, Node node, MainView view) {
 | 
			
		||||
        super(app, node, (NetworkSupport) app.getNetworkSupport());
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +50,9 @@ public HostDialog(MdgaApp app, Node node, MainView view) {
 | 
			
		||||
        offset += 1.5f;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is shown. Displays all input fields and buttons.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onShow() {
 | 
			
		||||
        portInput.show();
 | 
			
		||||
@@ -46,6 +60,9 @@ protected void onShow() {
 | 
			
		||||
        backButton.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is hidden. Hides all input fields and buttons.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onHide() {
 | 
			
		||||
        portInput.hide();
 | 
			
		||||
@@ -53,27 +70,44 @@ protected void onHide() {
 | 
			
		||||
        backButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the state of the port input field.
 | 
			
		||||
     * This method is called periodically to synchronize the dialog state.
 | 
			
		||||
     */
 | 
			
		||||
    public void update() {
 | 
			
		||||
        portInput.update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the currently entered port number, saves it to preferences, and sets it as the active port.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The port number as a string.
 | 
			
		||||
     */
 | 
			
		||||
    public String getPort() {
 | 
			
		||||
        prefs.put("hostPort", portInput.getString());
 | 
			
		||||
        setPortNumber(Integer.parseInt(portInput.getString()));
 | 
			
		||||
        return portInput.getString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets the port input field to its default value and updates preferences accordingly.
 | 
			
		||||
     */
 | 
			
		||||
    public void resetPort() {
 | 
			
		||||
        portInput.reset();
 | 
			
		||||
        prefs.put("hostPort", "11111");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Starts the server to host a network game.
 | 
			
		||||
     */
 | 
			
		||||
    public void hostServer() {
 | 
			
		||||
        startServer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Connects to the server as a client.
 | 
			
		||||
     */
 | 
			
		||||
    public void connectServerAsClient() {
 | 
			
		||||
        connectServer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,10 @@
 | 
			
		||||
import pp.mdga.client.view.MdgaView;
 | 
			
		||||
import pp.mdga.game.Color;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code InterruptDialog} class represents a dialog that interrupts the game flow,
 | 
			
		||||
 * providing a message and the option to force an action if the user is a host.
 | 
			
		||||
 */
 | 
			
		||||
public class InterruptDialog extends Dialog {
 | 
			
		||||
    private ButtonRight forceButton;
 | 
			
		||||
 | 
			
		||||
@@ -17,33 +21,50 @@ public class InterruptDialog extends Dialog {
 | 
			
		||||
 | 
			
		||||
    private String text = "";
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs an {@code InterruptDialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app  The main application managing the dialog.
 | 
			
		||||
     * @param node The root node for attaching UI elements.
 | 
			
		||||
     */
 | 
			
		||||
    public InterruptDialog(MdgaApp app, Node node) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
        forceButton = new ButtonRight(app, node, () -> app.getModelSynchronize().force(), "Erzwingen", 1);
 | 
			
		||||
 | 
			
		||||
        label = new LabelButton(app, node, "Warte auf " + text + "...", new Vector2f(5.5f * 1.5f, 2), new Vector2f(0.5f, 0f), false);
 | 
			
		||||
 | 
			
		||||
        float offset = 2.8f;
 | 
			
		||||
 | 
			
		||||
        label.setPos(new Vector2f(0, MenuButton.VERTICAL - offset));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is shown. Displays the label and optionally the force button if the user is the host.
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onShow() {
 | 
			
		||||
        if(app.getGameLogic().isHost()) {
 | 
			
		||||
            forceButton.show();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        label = new LabelButton(app, node, "Warte auf " + text + "...", new Vector2f(5.5f * 1.5f, 2), new Vector2f(0.5f, 0f), false);
 | 
			
		||||
 | 
			
		||||
        float offset = 2.8f;
 | 
			
		||||
        label.setPos(new Vector2f(0, MenuButton.VERTICAL - offset));
 | 
			
		||||
 | 
			
		||||
        label.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is hidden. Hides the label and the force button.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onHide() {
 | 
			
		||||
        forceButton.hide();
 | 
			
		||||
        label.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the displayed text based on the specified color.
 | 
			
		||||
     *
 | 
			
		||||
     * @param color The color used to determine the text (e.g., "Luftwaffe" for AIRFORCE).
 | 
			
		||||
     */
 | 
			
		||||
    public void setColor(Color color) {
 | 
			
		||||
        switch (color) {
 | 
			
		||||
            case AIRFORCE:
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,10 @@
 | 
			
		||||
 | 
			
		||||
import java.util.prefs.Preferences;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code JoinDialog} class represents a dialog for joining a network game.
 | 
			
		||||
 * It allows users to input an IP address and port number, connect to a server, or navigate back to the previous view.
 | 
			
		||||
 */
 | 
			
		||||
public class JoinDialog extends NetworkDialog {
 | 
			
		||||
    private InputButton ipInput;
 | 
			
		||||
    private InputButton portInput;
 | 
			
		||||
@@ -24,6 +28,13 @@ public class JoinDialog extends NetworkDialog {
 | 
			
		||||
 | 
			
		||||
    private Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a {@code JoinDialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app  The main application managing the dialog.
 | 
			
		||||
     * @param node The root node for attaching UI elements.
 | 
			
		||||
     * @param view The main view used for navigation and interaction with the dialog.
 | 
			
		||||
     */
 | 
			
		||||
    public JoinDialog(MdgaApp app, Node node, MainView view) {
 | 
			
		||||
        super(app, node, (NetworkSupport) app.getNetworkSupport());
 | 
			
		||||
 | 
			
		||||
@@ -46,6 +57,9 @@ public JoinDialog(MdgaApp app, Node node, MainView view) {
 | 
			
		||||
        offset += 1.5f;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is shown. Displays all input fields and buttons.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onShow() {
 | 
			
		||||
        ipInput.show();
 | 
			
		||||
@@ -54,6 +68,9 @@ protected void onShow() {
 | 
			
		||||
        backButton.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is hidden. Hides all input fields and buttons.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onHide() {
 | 
			
		||||
        ipInput.hide();
 | 
			
		||||
@@ -62,37 +79,62 @@ protected void onHide() {
 | 
			
		||||
        backButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the state of the input fields. This method is called periodically to synchronize the dialog state.
 | 
			
		||||
     */
 | 
			
		||||
    public void update() {
 | 
			
		||||
        ipInput.update();
 | 
			
		||||
        portInput.update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the currently entered IP address, saves it to preferences, and sets it as the hostname.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The IP address as a string.
 | 
			
		||||
     */
 | 
			
		||||
    public String getIpt() {
 | 
			
		||||
        prefs.put("joinIp", ipInput.getString());
 | 
			
		||||
        setHostname(ipInput.getString());
 | 
			
		||||
        return ipInput.getString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets the IP input field to its default value and updates preferences accordingly.
 | 
			
		||||
     */
 | 
			
		||||
    public void resetIp() {
 | 
			
		||||
        ipInput.reset();
 | 
			
		||||
        prefs.put("joinIp", "");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the currently entered port number, saves it to preferences, and sets it as the active port.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The port number as a string.
 | 
			
		||||
     */
 | 
			
		||||
    public String getPort() {
 | 
			
		||||
        prefs.put("joinPort", portInput.getString());
 | 
			
		||||
        setPortNumber(Integer.parseInt(portInput.getString()));
 | 
			
		||||
        return portInput.getString();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Resets the port input field to its default value and updates preferences accordingly.
 | 
			
		||||
     */
 | 
			
		||||
    public void resetPort() {
 | 
			
		||||
        portInput.reset();
 | 
			
		||||
        prefs.put("joinPort", "11111");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Connects to the server using the current IP address and port number.
 | 
			
		||||
     */
 | 
			
		||||
    public void connectToServer() {
 | 
			
		||||
        connectServer();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Disconnects from the server if a network connection exists.
 | 
			
		||||
     */
 | 
			
		||||
    public void disconnect() {
 | 
			
		||||
        NetworkSupport network = getNetwork();
 | 
			
		||||
        if (network != null) {
 | 
			
		||||
@@ -104,4 +146,3 @@ public void disconnect() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,11 @@
 | 
			
		||||
import java.util.concurrent.ExecutionException;
 | 
			
		||||
import java.util.concurrent.Future;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code NetworkDialog} class serves as an abstract base class for dialogs
 | 
			
		||||
 * that involve network-related functionalities, such as connecting to a server or hosting a game.
 | 
			
		||||
 * It provides methods for initializing, connecting to, and managing a network server.
 | 
			
		||||
 */
 | 
			
		||||
public abstract class NetworkDialog extends Dialog {
 | 
			
		||||
 | 
			
		||||
    private NetworkSupport network;
 | 
			
		||||
@@ -17,19 +22,41 @@ public abstract class NetworkDialog extends Dialog {
 | 
			
		||||
    private MdgaServer serverInstance;
 | 
			
		||||
    private Thread serverThread;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a {@code NetworkDialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app      The main application managing the dialog.
 | 
			
		||||
     * @param node     The root node for attaching UI elements.
 | 
			
		||||
     * @param network  The network support instance for managing network interactions.
 | 
			
		||||
     */
 | 
			
		||||
    public NetworkDialog(MdgaApp app, Node node, NetworkSupport network) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
        this.network = network;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the hostname for the network connection.
 | 
			
		||||
     *
 | 
			
		||||
     * @param hostname The hostname or IP address of the server.
 | 
			
		||||
     */
 | 
			
		||||
    public void setHostname(String hostname) {
 | 
			
		||||
        this.hostname = hostname;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the port number for the network connection.
 | 
			
		||||
     *
 | 
			
		||||
     * @param portNumber The port number to use for the connection.
 | 
			
		||||
     */
 | 
			
		||||
    public void setPortNumber(int portNumber) {
 | 
			
		||||
        this.portNumber = portNumber;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Initializes the network connection using the current hostname and port number.
 | 
			
		||||
     *
 | 
			
		||||
     * @return {@code null} if successful, otherwise throws an exception.
 | 
			
		||||
     */
 | 
			
		||||
    protected Object initNetwork() {
 | 
			
		||||
        try {
 | 
			
		||||
            this.network.initNetwork(this.hostname, this.portNumber);
 | 
			
		||||
@@ -39,6 +66,9 @@ protected Object initNetwork() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Starts the process of connecting to a server asynchronously.
 | 
			
		||||
     */
 | 
			
		||||
    protected void connectServer() {
 | 
			
		||||
        try {
 | 
			
		||||
            connectionFuture = this.network.getApp().getExecutor().submit(this::initNetwork);
 | 
			
		||||
@@ -47,6 +77,9 @@ protected void connectServer() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Starts hosting a server in a separate thread.
 | 
			
		||||
     */
 | 
			
		||||
    protected void startServer() {
 | 
			
		||||
        serverThread = new Thread(() -> {
 | 
			
		||||
            try {
 | 
			
		||||
@@ -60,6 +93,9 @@ protected void startServer() {
 | 
			
		||||
        serverThread.start();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Shuts down the hosted server and cleans up resources.
 | 
			
		||||
     */
 | 
			
		||||
    public void shutdownServer() {
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
@@ -85,6 +121,11 @@ public void shutdownServer() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the state of the connection process.
 | 
			
		||||
     *
 | 
			
		||||
     * @param delta The time elapsed since the last update call.
 | 
			
		||||
     */
 | 
			
		||||
    public void update(float delta) {
 | 
			
		||||
        if (this.connectionFuture != null && this.connectionFuture.isDone()) {
 | 
			
		||||
            try {
 | 
			
		||||
@@ -97,6 +138,11 @@ public void update(float delta) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the {@code NetworkSupport} instance associated with this dialog.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The {@code NetworkSupport} instance.
 | 
			
		||||
     */
 | 
			
		||||
    public NetworkSupport getNetwork() {
 | 
			
		||||
        return network;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,10 @@
 | 
			
		||||
import pp.mdga.client.view.MainView;
 | 
			
		||||
import pp.mdga.client.view.MdgaView;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code SettingsDialog} class represents a dialog for navigating to various settings sections,
 | 
			
		||||
 * such as video and audio settings, or returning to the previous view.
 | 
			
		||||
 */
 | 
			
		||||
public class SettingsDialog extends Dialog {
 | 
			
		||||
    private MenuButton videoButton;
 | 
			
		||||
    private MenuButton audioButton;
 | 
			
		||||
@@ -15,6 +19,13 @@ public class SettingsDialog extends Dialog {
 | 
			
		||||
 | 
			
		||||
    private final MdgaView view;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a {@code SettingsDialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app  The main application managing the dialog.
 | 
			
		||||
     * @param node The root node for attaching UI elements.
 | 
			
		||||
     * @param view The view managing navigation and interaction with the settings dialog.
 | 
			
		||||
     */
 | 
			
		||||
    public SettingsDialog(MdgaApp app, Node node, MdgaView view) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
@@ -34,6 +45,9 @@ public SettingsDialog(MdgaApp app, Node node, MdgaView view) {
 | 
			
		||||
        backButton.setPos(new Vector2f(0, 1.8f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is shown. Displays all buttons for video settings, audio settings, and back navigation.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onShow() {
 | 
			
		||||
        videoButton.show();
 | 
			
		||||
@@ -41,6 +55,9 @@ protected void onShow() {
 | 
			
		||||
        backButton.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is hidden. Hides all buttons for video settings, audio settings, and back navigation.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onHide() {
 | 
			
		||||
        videoButton.hide();
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,10 @@
 | 
			
		||||
import java.util.Random;
 | 
			
		||||
import java.util.random.RandomGenerator;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code StartDialog} class represents the initial dialog in the application,
 | 
			
		||||
 * allowing the user to input their name, host or join a game, or exit the application.
 | 
			
		||||
 */
 | 
			
		||||
public class StartDialog extends Dialog {
 | 
			
		||||
    private InputButton nameInput;
 | 
			
		||||
 | 
			
		||||
@@ -23,6 +27,13 @@ public class StartDialog extends Dialog {
 | 
			
		||||
 | 
			
		||||
    private final MainView view;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a {@code StartDialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app  The main application managing the dialog.
 | 
			
		||||
     * @param node The root node for attaching UI elements.
 | 
			
		||||
     * @param view The main view used for navigation and interaction with the dialog.
 | 
			
		||||
     */
 | 
			
		||||
    public StartDialog(MdgaApp app, Node node, MainView view) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
@@ -48,6 +59,9 @@ public StartDialog(MdgaApp app, Node node, MainView view) {
 | 
			
		||||
        endButton.setPos(new Vector2f(0, 1.8f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is shown. Displays the name input field and all buttons.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onShow() {
 | 
			
		||||
        nameInput.show();
 | 
			
		||||
@@ -57,6 +71,9 @@ protected void onShow() {
 | 
			
		||||
        endButton.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is hidden. Hides the name input field and all buttons.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onHide ()
 | 
			
		||||
    {
 | 
			
		||||
@@ -67,10 +84,18 @@ protected void onHide ()
 | 
			
		||||
        endButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the state of the name input field. This method is called periodically to synchronize the dialog state.
 | 
			
		||||
     */
 | 
			
		||||
    public void update() {
 | 
			
		||||
        nameInput.update();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Retrieves the name entered by the user. If no name is provided, a random name is generated.
 | 
			
		||||
     *
 | 
			
		||||
     * @return The user's name or a randomly generated name.
 | 
			
		||||
     */
 | 
			
		||||
    public String getName() {
 | 
			
		||||
        String name = nameInput.getString();
 | 
			
		||||
 | 
			
		||||
@@ -206,7 +231,7 @@ public String getName() {
 | 
			
		||||
                "FluffiKopf",
 | 
			
		||||
                "DonutDöner",
 | 
			
		||||
                "VollpfostenX",
 | 
			
		||||
                "Schraubenschlüssel",
 | 
			
		||||
                "Waschlappen",
 | 
			
		||||
                "Witzepumper",
 | 
			
		||||
                "ToastTraum",
 | 
			
		||||
                "FroschFighter",
 | 
			
		||||
@@ -263,22 +288,22 @@ public String getName() {
 | 
			
		||||
                "VulkanKeks",
 | 
			
		||||
                "WasserToast",
 | 
			
		||||
                "MenschSalat",
 | 
			
		||||
                "KampfKohlenhydrate",
 | 
			
		||||
                "KampfKohl",
 | 
			
		||||
                "SockenZirkus",
 | 
			
		||||
                "SchwimmBärchen",
 | 
			
		||||
                "TanzenderDachgepäckträger",
 | 
			
		||||
                "TanzenderPudel",
 | 
			
		||||
                "PizzamarktMensch",
 | 
			
		||||
                "ZahnarztZocker",
 | 
			
		||||
                "RollerCoasterTester",
 | 
			
		||||
                "WaschmaschinenPilot",
 | 
			
		||||
                "RollerRudi",
 | 
			
		||||
                "PupsPilot",
 | 
			
		||||
                "WitzigeZwiebel",
 | 
			
		||||
                "Pillenschlucker",
 | 
			
		||||
                "ZwiebelReiter",
 | 
			
		||||
                "HüpfenderKaktus",
 | 
			
		||||
                "KochenderAsteroid",
 | 
			
		||||
                "AsteroidenAlf",
 | 
			
		||||
                "ChaosKarotte",
 | 
			
		||||
                "WolkenFurz",
 | 
			
		||||
                "SchnitzelPartikel",
 | 
			
		||||
                "Krümelmonster",
 | 
			
		||||
                "WackelBiene",
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,11 @@
 | 
			
		||||
 | 
			
		||||
import java.util.prefs.Preferences;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code VideoSettingsDialog} class represents a dialog for configuring video settings,
 | 
			
		||||
 * such as resolution and fullscreen mode. It also provides an option to restart the application
 | 
			
		||||
 * when certain settings are changed.
 | 
			
		||||
 */
 | 
			
		||||
public class VideoSettingsDialog extends Dialog {
 | 
			
		||||
    private static Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
 | 
			
		||||
 | 
			
		||||
@@ -29,6 +34,13 @@ public class VideoSettingsDialog extends Dialog {
 | 
			
		||||
 | 
			
		||||
    private boolean active = false;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs a {@code VideoSettingsDialog}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param app  The main application managing the dialog.
 | 
			
		||||
     * @param node The root node for attaching UI elements.
 | 
			
		||||
     * @param view The view managing navigation and interaction with the video settings dialog.
 | 
			
		||||
     */
 | 
			
		||||
    public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) {
 | 
			
		||||
        super(app, node);
 | 
			
		||||
 | 
			
		||||
@@ -67,6 +79,9 @@ public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) {
 | 
			
		||||
        backButton.setPos(new Vector2f(0, 1.8f));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is shown. Displays all buttons and marks the dialog as active.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onShow() {
 | 
			
		||||
        active = true;
 | 
			
		||||
@@ -83,6 +98,9 @@ protected void onShow() {
 | 
			
		||||
        backButton.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called when the dialog is hidden. Hides all buttons and marks the dialog as inactive.
 | 
			
		||||
     */
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onHide() {
 | 
			
		||||
        active = false;
 | 
			
		||||
@@ -100,12 +118,23 @@ protected void onHide() {
 | 
			
		||||
        restartButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the dialog's state. This method can be used for periodic updates while the dialog is active.
 | 
			
		||||
     */
 | 
			
		||||
    public void update() {
 | 
			
		||||
        if(!active) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Updates the resolution settings and optionally triggers the restart button if changes are detected.
 | 
			
		||||
     *
 | 
			
		||||
     * @param width         The width of the resolution.
 | 
			
		||||
     * @param height        The height of the resolution.
 | 
			
		||||
     * @param imageFactor   The scaling factor for the resolution.
 | 
			
		||||
     * @param isFullscreen  {@code true} if fullscreen mode is enabled, {@code false} otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    public void updateResolution(int width, int height, float imageFactor, boolean isFullscreen) {
 | 
			
		||||
        if(width != prefs.getInt("width", 1280) || height != prefs.getInt("height", 720) || isFullscreen != prefs.getBoolean("fullscreen", false)) {
 | 
			
		||||
            restartButton.show();
 | 
			
		||||
 
 | 
			
		||||
@@ -10,12 +10,25 @@
 | 
			
		||||
import pp.mdga.client.animation.ZoomControl;
 | 
			
		||||
import pp.mdga.game.Color;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The {@code ActionTextHandler} class manages the display of animated and stylized text messages in the game's UI.
 | 
			
		||||
 * It supports dynamic text creation with spacing, color, and effects, such as dice rolls, player actions, and rankings.
 | 
			
		||||
 */
 | 
			
		||||
 class ActionTextHandler {
 | 
			
		||||
    private Node root;
 | 
			
		||||
    private BitmapFont font;
 | 
			
		||||
    private AppSettings appSettings;
 | 
			
		||||
    private int ranking;
 | 
			
		||||
 | 
			
		||||
    float paddingRanked = 100;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Constructs an {@code ActionTextHandler}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param guiNode      The GUI node where the text messages will be displayed.
 | 
			
		||||
     * @param assetManager The asset manager used to load fonts and other assets.
 | 
			
		||||
     * @param appSettings  The application settings for positioning and sizing.
 | 
			
		||||
     */
 | 
			
		||||
     ActionTextHandler(Node guiNode, AssetManager assetManager, AppSettings appSettings){
 | 
			
		||||
        root = new Node("actionTextRoot");
 | 
			
		||||
        guiNode.attachChild(root);
 | 
			
		||||
@@ -26,6 +39,16 @@ class ActionTextHandler {
 | 
			
		||||
        ranking = 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a {@code Node} containing text with specified spacing, size, and colors for each segment of the text.
 | 
			
		||||
     *
 | 
			
		||||
     * @param textArr  An array of strings representing the text to be displayed.
 | 
			
		||||
     * @param spacing  The spacing between individual characters.
 | 
			
		||||
     * @param size     The size of the text.
 | 
			
		||||
     * @param colorArr An array of {@code ColorRGBA} representing the color for each string in {@code textArr}.
 | 
			
		||||
     * @return A {@code Node} containing the styled text with spacing and color applied.
 | 
			
		||||
     * @throws RuntimeException if the lengths of {@code textArr} and {@code colorArr} do not match.
 | 
			
		||||
     */
 | 
			
		||||
    private Node createTextWithSpacing(String[] textArr, float spacing, float size, ColorRGBA[] colorArr) {
 | 
			
		||||
        if(textArr.length != colorArr.length) throw new RuntimeException("text and color are not the same length");
 | 
			
		||||
 | 
			
		||||
@@ -52,18 +75,55 @@ private Node createTextWithSpacing(String[] textArr, float spacing, float size,
 | 
			
		||||
        return textNode;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a {@code Node} containing text with specified spacing, size, and a single color.
 | 
			
		||||
     *
 | 
			
		||||
     * @param text    The text to be displayed.
 | 
			
		||||
     * @param spacing The spacing between individual characters.
 | 
			
		||||
     * @param size    The size of the text.
 | 
			
		||||
     * @param color   The color of the text.
 | 
			
		||||
     * @return A {@code Node} containing the styled text.
 | 
			
		||||
     */
 | 
			
		||||
    private Node createTextWithSpacing(String text, float spacing, float size, ColorRGBA color) {
 | 
			
		||||
        return createTextWithSpacing(new String[]{text}, spacing, size, new ColorRGBA[]{color});
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculates the center position of a rectangle given its width, height, and an origin position.
 | 
			
		||||
     *
 | 
			
		||||
     * @param width  The width of the rectangle.
 | 
			
		||||
     * @param height The height of the rectangle.
 | 
			
		||||
     * @param pos    The origin position of the rectangle.
 | 
			
		||||
     * @return A {@code Vector3f} representing the center position.
 | 
			
		||||
     */
 | 
			
		||||
    private Vector3f center(float width, float height, Vector3f pos){
 | 
			
		||||
        return new Vector3f(pos.x+width/2, pos.y+height/2,0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates and positions a single-line text at the top of the screen with a specified vertical offset.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name    The text to be displayed.
 | 
			
		||||
     * @param spacing The spacing between individual characters.
 | 
			
		||||
     * @param size    The size of the text.
 | 
			
		||||
     * @param color   The color of the text.
 | 
			
		||||
     * @param top     The vertical offset from the top of the screen.
 | 
			
		||||
     * @return A {@code Node} containing the styled text positioned at the top.
 | 
			
		||||
     */
 | 
			
		||||
    private Node createTopText(String name, float spacing, float size, ColorRGBA color, float top){
 | 
			
		||||
        return createTopText(new String[]{name}, spacing, size, new ColorRGBA[]{color}, top);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates and positions multi-line text at the top of the screen with specified vertical offset, spacing, and colors.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name    An array of strings representing the text to be displayed.
 | 
			
		||||
     * @param spacing The spacing between individual characters.
 | 
			
		||||
     * @param size    The size of the text.
 | 
			
		||||
     * @param color   An array of {@code ColorRGBA} representing the color for each string in {@code name}.
 | 
			
		||||
     * @param top     The vertical offset from the top of the screen.
 | 
			
		||||
     * @return A {@code Node} containing the styled text positioned at the top.
 | 
			
		||||
     */
 | 
			
		||||
    private Node createTopText(String[] name, float spacing, float size, ColorRGBA color[], float top){
 | 
			
		||||
        Node text = createTextWithSpacing(name, spacing, size, color);
 | 
			
		||||
        text.setLocalTranslation(0, (appSettings.getHeight()/2f)*0.8f-top,0);
 | 
			
		||||
@@ -71,18 +131,44 @@ private Node createTopText(String[] name, float spacing, float size, ColorRGBA c
 | 
			
		||||
        return text;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Calculates the center position of a rectangle with negative width offset.
 | 
			
		||||
     *
 | 
			
		||||
     * @param width  The negative width of the rectangle.
 | 
			
		||||
     * @param height The height of the rectangle.
 | 
			
		||||
     * @param pos    The origin position of the rectangle.
 | 
			
		||||
     * @return A {@code Vector3f} representing the center position.
 | 
			
		||||
     */
 | 
			
		||||
    private Vector3f centerText(float width, float height, Vector3f pos){
 | 
			
		||||
        return center(-width, height, pos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a message indicating the active player.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name  The name of the active player.
 | 
			
		||||
     * @param color The color representing the player's team.
 | 
			
		||||
     */
 | 
			
		||||
     void activePlayer(String name, Color color){
 | 
			
		||||
        createTopText(new String[]{name," ist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a message indicating that the current player is active.
 | 
			
		||||
     *
 | 
			
		||||
     * @param color The color representing the player's team.
 | 
			
		||||
     */
 | 
			
		||||
     void ownActive(Color color){
 | 
			
		||||
        createTopText(new String[]{"Du"," bist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a dice roll result for a player.
 | 
			
		||||
     *
 | 
			
		||||
     * @param diceNum The number rolled on the dice.
 | 
			
		||||
     * @param name    The name of the player.
 | 
			
		||||
     * @param color   The color representing the player's team.
 | 
			
		||||
     */
 | 
			
		||||
     void diceNum(int diceNum, String name, Color color){
 | 
			
		||||
        createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0);
 | 
			
		||||
 | 
			
		||||
@@ -90,38 +176,84 @@ void diceNum(int diceNum, String name, Color color){
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a dice roll result with a multiplier for a player.
 | 
			
		||||
     *
 | 
			
		||||
     * @param diceNum The number rolled on the dice.
 | 
			
		||||
     * @param mult    The multiplier applied to the dice result.
 | 
			
		||||
     * @param name    The name of the player.
 | 
			
		||||
     * @param color   The color representing the player's team.
 | 
			
		||||
     */
 | 
			
		||||
     void diceNumMult(int diceNum,int mult, String name, Color color){
 | 
			
		||||
        createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0);
 | 
			
		||||
 | 
			
		||||
        createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 100);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays the dice roll result for the current player.
 | 
			
		||||
     *
 | 
			
		||||
     * @param diceNum The number rolled on the dice.
 | 
			
		||||
     */
 | 
			
		||||
     void ownDice(int diceNum){
 | 
			
		||||
        createTopText(String.valueOf(diceNum), 10, 100, ColorRGBA.White, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays the dice roll result with a multiplier for the current player.
 | 
			
		||||
     *
 | 
			
		||||
     * @param diceNum The number rolled on the dice.
 | 
			
		||||
     * @param mult    The multiplier applied to the dice result.
 | 
			
		||||
     */
 | 
			
		||||
     void ownDiceMult(int diceNum, int mult){
 | 
			
		||||
        createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a message indicating that a specified player received a bonus card.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name  The name of the player who received the bonus card.
 | 
			
		||||
     * @param color The color representing the player's team.
 | 
			
		||||
     */
 | 
			
		||||
     void drawCard(String name, Color color){
 | 
			
		||||
        createTopText(new String[]{name," erhält eine Bonuskarte"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a message indicating that the current player received a bonus card.
 | 
			
		||||
     *
 | 
			
		||||
     * @param color The color representing the player's team.
 | 
			
		||||
     */
 | 
			
		||||
     void drawCardOwn(Color color){
 | 
			
		||||
        createTopText(new String[]{"Du","  erhälst eine Bonuskarte"}, 5,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a message indicating that a specified player has completed their turn or action.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name  The name of the player who finished.
 | 
			
		||||
     * @param color The color representing the player's team.
 | 
			
		||||
     */
 | 
			
		||||
     void finishText(String name, Color color){
 | 
			
		||||
        createTopText(new String[]{name," ist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a message indicating that the current player has completed their turn or action.
 | 
			
		||||
     *
 | 
			
		||||
     * @param color The color representing the player's team.
 | 
			
		||||
     */
 | 
			
		||||
     void finishTextOwn(Color color){
 | 
			
		||||
        createTopText(new String[]{"Du", " bist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Converts a player's team color to a corresponding {@code ColorRGBA}.
 | 
			
		||||
     *
 | 
			
		||||
     * @param color The player's team color.
 | 
			
		||||
     * @return The corresponding {@code ColorRGBA}.
 | 
			
		||||
     * @throws RuntimeException if the color is invalid.
 | 
			
		||||
     */
 | 
			
		||||
    private ColorRGBA playerColorToColorRGBA(Color color){
 | 
			
		||||
        return switch (color){
 | 
			
		||||
            case ARMY -> ColorRGBA.Green;
 | 
			
		||||
@@ -132,25 +264,41 @@ private ColorRGBA playerColorToColorRGBA(Color color){
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Hides all text messages displayed by the handler and resets the ranking counter.
 | 
			
		||||
     */
 | 
			
		||||
     void hide(){
 | 
			
		||||
         ranking = 0;
 | 
			
		||||
         root.detachAllChildren();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    float paddingRanked = 100;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a ranked dice roll result for a specified player.
 | 
			
		||||
     *
 | 
			
		||||
     * @param name  The name of the player.
 | 
			
		||||
     * @param color The color representing the player's team.
 | 
			
		||||
     * @param eye   The dice roll result.
 | 
			
		||||
     */
 | 
			
		||||
    void rollRankingResult(String name, Color color, int eye){
 | 
			
		||||
        createTopText(new String[]{name,":  "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking);
 | 
			
		||||
        ranking++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a ranked dice roll result for the current player.
 | 
			
		||||
     *
 | 
			
		||||
     * @param color The color representing the player's team.
 | 
			
		||||
     * @param eye   The dice roll result.
 | 
			
		||||
     */
 | 
			
		||||
     void rollRankingResultOwn(Color color, int eye){
 | 
			
		||||
         createTopText(new String[]{"Du",":  "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking);
 | 
			
		||||
         ranking++;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Displays a message prompting the player to roll the dice.
 | 
			
		||||
     */
 | 
			
		||||
     void diceNow(){
 | 
			
		||||
         createTopText("Klicke  zum  Würfeln", 5, 80, ColorRGBA.White, 0);
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -52,6 +52,7 @@ public GameView(MdgaApp app) {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onEnter() {
 | 
			
		||||
        setOwnColor(Color.AIRFORCE);
 | 
			
		||||
        camera.init(ownColor);
 | 
			
		||||
        boardHandler.init();
 | 
			
		||||
        guiHandler.init(ownColor);
 | 
			
		||||
@@ -122,13 +123,13 @@ public void noConfirm() {
 | 
			
		||||
        confirmButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void needNoPower() {
 | 
			
		||||
    public void showNoPower() {
 | 
			
		||||
        confirmButton.hide();
 | 
			
		||||
        noPowerButton.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void noNoPower() {
 | 
			
		||||
        noPowerButton.show();
 | 
			
		||||
    public void hideNoPower() {
 | 
			
		||||
        noPowerButton.hide();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void enterInterrupt(Color color) {
 | 
			
		||||
 
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 216 KiB  | 
| 
		 Before Width: | Height: | Size: 274 KiB  | 
| 
		 After Width: | Height: | Size: 46 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								Projekte/mdga/client/src/main/resources/Images/particle/line.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| 
		 After Width: | Height: | Size: 140 B  | 
| 
		 After Width: | Height: | Size: 29 KiB  | 
| 
		 After Width: | Height: | Size: 18 KiB  | 
@@ -1,4 +1,6 @@
 | 
			
		||||
world 0,0 90
 | 
			
		||||
treesBigBackground 0,0 90
 | 
			
		||||
treesSmallBackground 0,0 90
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#Marine Pos
 | 
			
		||||
@@ -56,7 +58,8 @@ big_tent -10,-9 130
 | 
			
		||||
big_tent 9,-10 225
 | 
			
		||||
radar 0,10 -20
 | 
			
		||||
tank -1,-10 135
 | 
			
		||||
tank 0,-18 180
 | 
			
		||||
#tank 0,-18 180
 | 
			
		||||
tank_shoot 0,-18 180
 | 
			
		||||
tank 3,-18 180
 | 
			
		||||
tank -3,-18 180
 | 
			
		||||
 | 
			
		||||
@@ -270,3 +273,4 @@ treeBig 12,22 360
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
// Samplers for textures
 | 
			
		||||
uniform sampler2D m_Texture;
 | 
			
		||||
uniform sampler2D m_OutlineDepthTexture;
 | 
			
		||||
uniform sampler2D m_DepthTexture;
 | 
			
		||||
 | 
			
		||||
// Input texture coordinates from the vertex shader
 | 
			
		||||
in vec2 texCoord;
 | 
			
		||||
@@ -15,26 +14,25 @@ uniform float m_OutlineWidth;
 | 
			
		||||
out vec4 fragColor;
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
    // Sample depth textures
 | 
			
		||||
    // Sample depth textures at various offsets
 | 
			
		||||
    vec4 depth = texture(m_OutlineDepthTexture, texCoord);
 | 
			
		||||
    vec4 depth1 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth2 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth3 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(-m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth4 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(-m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth5 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(0.0, m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth6 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(0.0, -m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth7 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(m_OutlineWidth, 0.0)) / m_Resolution);
 | 
			
		||||
    vec4 depth8 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(-m_OutlineWidth, 0.0)) / m_Resolution);
 | 
			
		||||
    vec4 depth1 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth2 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth3 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth4 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth5 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth6 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, -m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth7 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, 0.0)) / m_Resolution);
 | 
			
		||||
    vec4 depth8 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, 0.0)) / m_Resolution);
 | 
			
		||||
 | 
			
		||||
    // Sample the main texture
 | 
			
		||||
    vec4 color = texture(m_Texture, texCoord);
 | 
			
		||||
 | 
			
		||||
    // Determine whether to apply the outline color
 | 
			
		||||
    if (depth == vec4(0.0) &&
 | 
			
		||||
       (depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth ||
 | 
			
		||||
        depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)) {
 | 
			
		||||
        fragColor = m_OutlineColor; // Apply outline color
 | 
			
		||||
    } else {
 | 
			
		||||
        fragColor = color; // Use the original texture color
 | 
			
		||||
    }
 | 
			
		||||
    // Check if an outline should be applied
 | 
			
		||||
    bool isEdge = (depth == vec4(0.0)) &&
 | 
			
		||||
                  (depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth ||
 | 
			
		||||
                   depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth);
 | 
			
		||||
 | 
			
		||||
    // Output the final color
 | 
			
		||||
    fragColor = isEdge ? m_OutlineColor : color;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,10 @@
 | 
			
		||||
 | 
			
		||||
// Use 'in' instead of 'varying' for inputs from the vertex shader
 | 
			
		||||
// Input texture coordinates from the vertex shader
 | 
			
		||||
in vec2 texCoord;
 | 
			
		||||
 | 
			
		||||
// Declare a custom output variable for the fragment color
 | 
			
		||||
// Output variable for the fragment color
 | 
			
		||||
out vec4 fragColor;
 | 
			
		||||
 | 
			
		||||
// Uniform samplers
 | 
			
		||||
// Uniform samplers for textures
 | 
			
		||||
uniform sampler2D m_Texture;
 | 
			
		||||
uniform sampler2D m_NormalsTexture;
 | 
			
		||||
uniform sampler2D m_DepthTexture;
 | 
			
		||||
@@ -13,6 +12,7 @@ uniform sampler2D m_DepthTexture;
 | 
			
		||||
void main() {
 | 
			
		||||
    // Sample the texture at the given texture coordinates
 | 
			
		||||
    vec4 color = texture(m_Texture, texCoord);
 | 
			
		||||
 | 
			
		||||
    // Assign the color to the output variable
 | 
			
		||||
    fragColor = color;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,109 +1,78 @@
 | 
			
		||||
// Uniform samplers
 | 
			
		||||
uniform sampler2D m_Texture;
 | 
			
		||||
uniform sampler2D m_OutlineDepthTexture;
 | 
			
		||||
uniform sampler2D m_DepthTexture;
 | 
			
		||||
varying vec2 texCoord;
 | 
			
		||||
 | 
			
		||||
// Input texture coordinates from the vertex shader
 | 
			
		||||
in vec2 texCoord;
 | 
			
		||||
 | 
			
		||||
// Uniforms for resolution, outline color, and width
 | 
			
		||||
uniform vec2 m_Resolution;
 | 
			
		||||
uniform vec4 m_OutlineColor;
 | 
			
		||||
uniform float m_OutlineWidth;
 | 
			
		||||
 | 
			
		||||
// Output variable for fragment color
 | 
			
		||||
out vec4 fragColor;
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
	vec4 depth = texture2D(m_OutlineDepthTexture, texCoord);
 | 
			
		||||
	vec4 depth1 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth2 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,-m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth3 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth4 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,-m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth5 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth6 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,-m_OutlineWidth))/m_Resolution);
 | 
			
		||||
	vec4 depth7 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,0.))/m_Resolution);
 | 
			
		||||
	vec4 depth8 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,0.))/m_Resolution);
 | 
			
		||||
	vec4 color = texture2D(m_Texture, texCoord);
 | 
			
		||||
	//如果是背景
 | 
			
		||||
	float ratio=0.;
 | 
			
		||||
	if(depth==vec4(0.) && (depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth||depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)){
 | 
			
		||||
		float dist=m_OutlineWidth;
 | 
			
		||||
		//距离边的像素
 | 
			
		||||
		vec4 nearDepth;
 | 
			
		||||
		if(depth1 != depth){
 | 
			
		||||
			for(float i=0.;i<m_OutlineWidth;i++){
 | 
			
		||||
				nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(i,i))/m_Resolution);
 | 
			
		||||
				if(nearDepth != depth){
 | 
			
		||||
					dist = i;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}else
 | 
			
		||||
		if(depth2 != depth){
 | 
			
		||||
			for(float i=0.;i<m_OutlineWidth;i++){
 | 
			
		||||
				nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(i,-i))/m_Resolution);
 | 
			
		||||
				if(nearDepth != depth){
 | 
			
		||||
					dist = i;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}else
 | 
			
		||||
		if(depth3 != depth){
 | 
			
		||||
			for(float i=0.;i<m_OutlineWidth;i++){
 | 
			
		||||
				nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-i,i))/m_Resolution);
 | 
			
		||||
				if(nearDepth != depth){
 | 
			
		||||
					dist = i;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}else
 | 
			
		||||
		if(depth4 != depth){
 | 
			
		||||
			for(float i=0.;i<m_OutlineWidth;i++){
 | 
			
		||||
				nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-i,-i))/m_Resolution);
 | 
			
		||||
				if(nearDepth != depth){
 | 
			
		||||
					dist = i;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}else
 | 
			
		||||
		if(depth5 != depth){
 | 
			
		||||
			for(float i=0.;i<m_OutlineWidth;i++){
 | 
			
		||||
				nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,i))/m_Resolution);
 | 
			
		||||
				if(nearDepth != depth){
 | 
			
		||||
					dist = i;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}else
 | 
			
		||||
		if(depth6 != depth){
 | 
			
		||||
			for(float i=0.;i<m_OutlineWidth;i++){
 | 
			
		||||
				nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,-i))/m_Resolution);
 | 
			
		||||
				if(nearDepth != depth){
 | 
			
		||||
					dist = i;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}else
 | 
			
		||||
		if(depth7 != depth){
 | 
			
		||||
			for(float i=0.;i<m_OutlineWidth;i++){
 | 
			
		||||
				nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(i,0.))/m_Resolution);
 | 
			
		||||
				if(nearDepth != depth){
 | 
			
		||||
					dist = i;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}else
 | 
			
		||||
		if(depth8 != depth){
 | 
			
		||||
			for(float i=0.;i<m_OutlineWidth;i++){
 | 
			
		||||
				nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-i,0.))/m_Resolution);
 | 
			
		||||
				if(nearDepth != depth){
 | 
			
		||||
					dist = i;
 | 
			
		||||
					break;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		//0:场景颜色		1:outline颜色 
 | 
			
		||||
		ratio = clamp(1.- dist/m_OutlineWidth,0.,1.);
 | 
			
		||||
		//float off = (1.-ratio*ratio)*(1.-ratio*ratio);
 | 
			
		||||
		gl_FragColor = color*(1.-ratio) +m_OutlineColor*ratio;
 | 
			
		||||
		//gl_FragColor = m_OutlineColor;
 | 
			
		||||
	}else{
 | 
			
		||||
		gl_FragColor = color;
 | 
			
		||||
	}
 | 
			
		||||
	//debug
 | 
			
		||||
	//gl_FragColor = vec4(0.,(1.-ratio),0.,1.);
 | 
			
		||||
}
 | 
			
		||||
    vec4 depth = texture(m_OutlineDepthTexture, texCoord);
 | 
			
		||||
    vec4 depth1 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth2 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth3 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth4 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth5 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth6 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, -m_OutlineWidth)) / m_Resolution);
 | 
			
		||||
    vec4 depth7 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, 0.0)) / m_Resolution);
 | 
			
		||||
    vec4 depth8 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, 0.0)) / m_Resolution);
 | 
			
		||||
 | 
			
		||||
    vec4 color = texture(m_Texture, texCoord);
 | 
			
		||||
 | 
			
		||||
    float ratio = 0.0;
 | 
			
		||||
    if (depth == vec4(0.0) &&
 | 
			
		||||
        (depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth ||
 | 
			
		||||
         depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)) {
 | 
			
		||||
        float dist = m_OutlineWidth;
 | 
			
		||||
        vec4 nearDepth;
 | 
			
		||||
 | 
			
		||||
        // Iterate to find the distance to the nearest edge
 | 
			
		||||
        for (float i = 0.0; i < m_OutlineWidth; i++) {
 | 
			
		||||
            if (depth1 != depth) {
 | 
			
		||||
                nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(i, i)) / m_Resolution);
 | 
			
		||||
                if (nearDepth != depth) { dist = i; break; }
 | 
			
		||||
            } else if (depth2 != depth) {
 | 
			
		||||
                nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(i, -i)) / m_Resolution);
 | 
			
		||||
                if (nearDepth != depth) { dist = i; break; }
 | 
			
		||||
            } else if (depth3 != depth) {
 | 
			
		||||
                nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-i, i)) / m_Resolution);
 | 
			
		||||
                if (nearDepth != depth) { dist = i; break; }
 | 
			
		||||
            } else if (depth4 != depth) {
 | 
			
		||||
                nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-i, -i)) / m_Resolution);
 | 
			
		||||
                if (nearDepth != depth) { dist = i; break; }
 | 
			
		||||
            } else if (depth5 != depth) {
 | 
			
		||||
                nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, i)) / m_Resolution);
 | 
			
		||||
                if (nearDepth != depth) { dist = i; break; }
 | 
			
		||||
            } else if (depth6 != depth) {
 | 
			
		||||
                nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, -i)) / m_Resolution);
 | 
			
		||||
                if (nearDepth != depth) { dist = i; break; }
 | 
			
		||||
            } else if (depth7 != depth) {
 | 
			
		||||
                nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(i, 0.0)) / m_Resolution);
 | 
			
		||||
                if (nearDepth != depth) { dist = i; break; }
 | 
			
		||||
            } else if (depth8 != depth) {
 | 
			
		||||
                nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-i, 0.0)) / m_Resolution);
 | 
			
		||||
                if (nearDepth != depth) { dist = i; break; }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Calculate ratio for outline blending
 | 
			
		||||
        ratio = clamp(1.0 - dist / m_OutlineWidth, 0.0, 1.0);
 | 
			
		||||
 | 
			
		||||
        // Blend the outline color with the base color
 | 
			
		||||
        fragColor = color * (1.0 - ratio) + m_OutlineColor * ratio;
 | 
			
		||||
    } else {
 | 
			
		||||
        // No outline, use the base texture color
 | 
			
		||||
        fragColor = color;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Optional: Debugging outline visualization
 | 
			
		||||
    // fragColor = vec4(0.0, 1.0 - ratio, 0.0, 1.0);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
// Use 'in' for vertex attributes
 | 
			
		||||
// Vertex attributes
 | 
			
		||||
in vec4 inPosition;
 | 
			
		||||
in vec2 inTexCoord;
 | 
			
		||||
 | 
			
		||||
// Use 'out' for passing data to the fragment shader
 | 
			
		||||
// Output to fragment shader
 | 
			
		||||
out vec2 texCoord;
 | 
			
		||||
 | 
			
		||||
void main() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								Projekte/mdga/client/src/main/resources/Models/shell/shell.j3o
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,554 @@
 | 
			
		||||
# Blender 3.6.5
 | 
			
		||||
# www.blender.org
 | 
			
		||||
o tank_top
 | 
			
		||||
v 0.136104 -4.382030 2.447842
 | 
			
		||||
v 0.136104 -4.313100 2.681757
 | 
			
		||||
v -0.107755 -4.382030 2.447842
 | 
			
		||||
v -0.107755 -4.313100 2.681757
 | 
			
		||||
v 0.136104 -5.171963 2.680617
 | 
			
		||||
v 0.136104 -5.103033 2.914532
 | 
			
		||||
v -0.107755 -5.171963 2.680617
 | 
			
		||||
v -0.107755 -5.103033 2.914532
 | 
			
		||||
v 0.104217 -1.266581 1.563035
 | 
			
		||||
v 0.104217 -1.215678 1.735777
 | 
			
		||||
v -0.075869 -1.266581 1.563035
 | 
			
		||||
v -0.075869 -1.215678 1.735777
 | 
			
		||||
v 0.104218 -5.321341 2.757877
 | 
			
		||||
v 0.104218 -5.270438 2.930620
 | 
			
		||||
v -0.075868 -5.321341 2.757877
 | 
			
		||||
v -0.075868 -5.270438 2.930620
 | 
			
		||||
v 0.170290 -1.697908 1.621255
 | 
			
		||||
v 0.170290 -1.609653 1.920754
 | 
			
		||||
v -0.141942 -1.697908 1.621255
 | 
			
		||||
v -0.141942 -1.609653 1.920754
 | 
			
		||||
v 0.229706 -1.077489 1.376490
 | 
			
		||||
v 0.229706 -0.955645 1.789975
 | 
			
		||||
v -0.201358 -1.077489 1.376490
 | 
			
		||||
v -0.201358 -0.955645 1.789975
 | 
			
		||||
v 0.514900 0.276165 1.806222
 | 
			
		||||
v 0.514900 0.588869 2.433130
 | 
			
		||||
v -0.442998 0.276164 1.806222
 | 
			
		||||
v -0.442998 0.588869 2.433130
 | 
			
		||||
v 0.514900 0.342851 1.772958
 | 
			
		||||
v 0.514900 0.655556 2.399867
 | 
			
		||||
v -0.442998 0.342851 1.772958
 | 
			
		||||
v -0.442998 0.655556 2.399867
 | 
			
		||||
v -0.571633 -0.809063 1.704162
 | 
			
		||||
v -0.571633 -0.809063 2.393599
 | 
			
		||||
v -0.723737 -0.809063 1.704162
 | 
			
		||||
v -0.723737 -0.809063 2.393599
 | 
			
		||||
v -0.571633 -0.656959 1.704161
 | 
			
		||||
v -0.571633 -0.656959 2.393600
 | 
			
		||||
v -0.723737 -0.656959 1.704161
 | 
			
		||||
v -0.723737 -0.656959 2.393600
 | 
			
		||||
v -0.950927 -0.897974 2.389141
 | 
			
		||||
v -0.950927 -0.803313 2.389141
 | 
			
		||||
v -0.861627 -0.897974 2.604730
 | 
			
		||||
v -0.861627 -0.803313 2.604730
 | 
			
		||||
v -0.646038 -0.897974 2.694029
 | 
			
		||||
v -0.646038 -0.803313 2.694029
 | 
			
		||||
v -0.430449 -0.897974 2.604729
 | 
			
		||||
v -0.430449 -0.803313 2.604730
 | 
			
		||||
v -0.341149 -0.897974 2.389141
 | 
			
		||||
v -0.341149 -0.803313 2.389141
 | 
			
		||||
v -0.430449 -0.897974 2.173552
 | 
			
		||||
v -0.430449 -0.803313 2.173552
 | 
			
		||||
v -0.646038 -0.897974 2.084251
 | 
			
		||||
v -0.646038 -0.803313 2.084251
 | 
			
		||||
v -0.861627 -0.897974 2.173552
 | 
			
		||||
v -0.861627 -0.803313 2.173552
 | 
			
		||||
v -0.799948 -0.357073 2.051851
 | 
			
		||||
v -0.784767 0.368207 2.051851
 | 
			
		||||
v -0.423762 0.888095 2.051851
 | 
			
		||||
v 0.525389 0.907164 2.051851
 | 
			
		||||
v 0.864924 0.401350 2.051851
 | 
			
		||||
v 0.849742 -0.323930 2.051851
 | 
			
		||||
v 0.387428 -1.063397 2.051851
 | 
			
		||||
v -0.368214 -1.078578 2.051851
 | 
			
		||||
v -1.026731 -0.445682 1.384443
 | 
			
		||||
v -1.007409 0.477460 1.384443
 | 
			
		||||
v -0.547920 1.139177 1.384443
 | 
			
		||||
v 0.660166 1.163448 1.384443
 | 
			
		||||
v 1.092329 0.519645 1.384443
 | 
			
		||||
v 1.073006 -0.403497 1.384443
 | 
			
		||||
v 0.484569 -1.344695 1.384443
 | 
			
		||||
v -0.477218 -1.364018 1.384443
 | 
			
		||||
v -1.077985 -0.465708 1.887314
 | 
			
		||||
v -1.057726 0.502151 1.887314
 | 
			
		||||
v -0.575980 1.195922 1.887314
 | 
			
		||||
v 0.690626 1.221368 1.887314
 | 
			
		||||
v 1.143722 0.546380 1.887314
 | 
			
		||||
v 1.123464 -0.421479 1.887314
 | 
			
		||||
v 0.506523 -1.408269 1.887314
 | 
			
		||||
v -0.501853 -1.428528 1.887314
 | 
			
		||||
v -1.085964 0.516008 1.535923
 | 
			
		||||
v -0.591726 1.227766 1.535923
 | 
			
		||||
v 0.707720 1.253873 1.535923
 | 
			
		||||
v 1.172564 0.561383 1.535923
 | 
			
		||||
v 1.151780 -0.431571 1.535923
 | 
			
		||||
v 0.518844 -1.443946 1.535923
 | 
			
		||||
v -0.515678 -1.464730 1.535923
 | 
			
		||||
v -1.106748 -0.476946 1.535923
 | 
			
		||||
v 0.612842 -0.142856 2.090828
 | 
			
		||||
v 0.612842 0.599197 2.091472
 | 
			
		||||
v -0.540940 -0.142856 2.090828
 | 
			
		||||
v -0.540940 0.599197 2.091472
 | 
			
		||||
v 0.612842 -0.142788 2.011893
 | 
			
		||||
v 0.612842 0.599266 2.012537
 | 
			
		||||
v -0.540940 -0.142788 2.011893
 | 
			
		||||
v -0.540940 0.599265 2.012537
 | 
			
		||||
v 0.445467 -0.035209 2.095495
 | 
			
		||||
v 0.445467 0.491550 2.095952
 | 
			
		||||
v -0.373565 -0.035210 2.095495
 | 
			
		||||
v -0.373565 0.491550 2.095952
 | 
			
		||||
vn -0.0000 0.9592 -0.2827
 | 
			
		||||
vn -1.0000 -0.0000 -0.0000
 | 
			
		||||
vn -0.0000 -0.9592 0.2827
 | 
			
		||||
vn 1.0000 -0.0000 -0.0000
 | 
			
		||||
vn -0.0000 -0.2827 -0.9592
 | 
			
		||||
vn -0.0000 0.2827 0.9592
 | 
			
		||||
vn -0.9960 -0.0855 0.0252
 | 
			
		||||
vn 0.9960 -0.0855 0.0252
 | 
			
		||||
vn -0.0000 -0.3670 -0.9302
 | 
			
		||||
vn -0.0000 0.1961 0.9806
 | 
			
		||||
vn -0.0000 -0.8949 0.4464
 | 
			
		||||
vn -0.0000 0.8949 -0.4464
 | 
			
		||||
vn -0.0000 -0.4464 -0.8949
 | 
			
		||||
vn -0.0000 0.4464 0.8949
 | 
			
		||||
vn -0.0000 -1.0000 -0.0000
 | 
			
		||||
vn -0.0000 1.0000 -0.0000
 | 
			
		||||
vn -0.0000 -0.0000 -1.0000
 | 
			
		||||
vn -0.0000 -0.0000 1.0000
 | 
			
		||||
vn -0.9239 -0.0000 0.3827
 | 
			
		||||
vn -0.3827 -0.0000 0.9239
 | 
			
		||||
vn 0.3827 -0.0000 0.9239
 | 
			
		||||
vn 0.9239 -0.0000 0.3827
 | 
			
		||||
vn 0.9239 -0.0000 -0.3827
 | 
			
		||||
vn 0.3827 -0.0000 -0.9239
 | 
			
		||||
vn -0.3827 -0.0000 -0.9239
 | 
			
		||||
vn -0.9239 -0.0000 -0.3827
 | 
			
		||||
vn -0.5124 0.0107 0.8587
 | 
			
		||||
vn -0.3944 0.2739 0.8772
 | 
			
		||||
vn -0.0094 0.4678 0.8838
 | 
			
		||||
vn 0.3870 0.2598 0.8847
 | 
			
		||||
vn 0.5124 -0.0107 0.8587
 | 
			
		||||
vn 0.4253 -0.2659 0.8651
 | 
			
		||||
vn 0.0086 -0.4282 0.9037
 | 
			
		||||
vn -0.4187 -0.2505 0.8729
 | 
			
		||||
vn -0.8549 -0.5116 0.0863
 | 
			
		||||
vn 0.0200 -0.9946 0.1017
 | 
			
		||||
vn 0.8450 -0.5283 0.0833
 | 
			
		||||
vn 0.9965 -0.0209 0.0809
 | 
			
		||||
vn 0.8268 0.5550 0.0916
 | 
			
		||||
vn -0.0200 0.9956 0.0911
 | 
			
		||||
vn -0.8182 0.5681 0.0882
 | 
			
		||||
vn -0.9965 0.0209 0.0809
 | 
			
		||||
vn -0.8856 0.0185 -0.4640
 | 
			
		||||
vn -0.7133 0.4953 -0.4959
 | 
			
		||||
vn -0.0173 0.8609 -0.5085
 | 
			
		||||
vn 0.7140 0.4793 -0.5103
 | 
			
		||||
vn 0.8856 -0.0185 -0.4640
 | 
			
		||||
vn 0.7463 -0.4666 -0.4746
 | 
			
		||||
vn 0.0168 -0.8346 -0.5506
 | 
			
		||||
vn -0.7489 -0.4482 -0.4881
 | 
			
		||||
vn -0.0000 -0.0009 1.0000
 | 
			
		||||
vn -0.0000 0.0009 -1.0000
 | 
			
		||||
vn -0.0000 -1.0000 -0.0009
 | 
			
		||||
vn -0.0000 1.0000 0.0009
 | 
			
		||||
vt 0.302708 0.924112
 | 
			
		||||
vt 0.288040 0.909443
 | 
			
		||||
vt 0.302708 0.909443
 | 
			
		||||
vt 0.693823 0.755933
 | 
			
		||||
vt 0.708491 0.805468
 | 
			
		||||
vt 0.693821 0.805468
 | 
			
		||||
vt 0.933974 0.202686
 | 
			
		||||
vt 0.919330 0.188017
 | 
			
		||||
vt 0.933973 0.188017
 | 
			
		||||
vt 0.651344 0.857452
 | 
			
		||||
vt 0.636674 0.807918
 | 
			
		||||
vt 0.651343 0.807918
 | 
			
		||||
vt 0.134278 0.805715
 | 
			
		||||
vt 0.119609 0.855249
 | 
			
		||||
vt 0.119609 0.805715
 | 
			
		||||
vt 0.708491 0.751615
 | 
			
		||||
vt 0.693871 0.702081
 | 
			
		||||
vt 0.708540 0.702096
 | 
			
		||||
vt 0.766441 0.570082
 | 
			
		||||
vt 0.755608 0.559249
 | 
			
		||||
vt 0.766441 0.559249
 | 
			
		||||
vt 0.537931 0.359392
 | 
			
		||||
vt 0.548757 0.613657
 | 
			
		||||
vt 0.537924 0.613657
 | 
			
		||||
vt 0.781573 0.570082
 | 
			
		||||
vt 0.770759 0.559250
 | 
			
		||||
vt 0.781572 0.559250
 | 
			
		||||
vt 0.579073 0.613652
 | 
			
		||||
vt 0.568233 0.359392
 | 
			
		||||
vt 0.579066 0.359392
 | 
			
		||||
vt 0.563915 0.359392
 | 
			
		||||
vt 0.553082 0.613655
 | 
			
		||||
vt 0.553082 0.359392
 | 
			
		||||
vt 0.437696 0.622253
 | 
			
		||||
vt 0.448781 0.368077
 | 
			
		||||
vt 0.448529 0.622264
 | 
			
		||||
vt 0.615148 0.975854
 | 
			
		||||
vt 0.633896 0.957072
 | 
			
		||||
vt 0.633897 0.975854
 | 
			
		||||
vt 0.948652 0.917591
 | 
			
		||||
vt 0.926304 0.957587
 | 
			
		||||
vt 0.929870 0.917591
 | 
			
		||||
vt 0.106854 0.798464
 | 
			
		||||
vt 0.132782 0.772534
 | 
			
		||||
vt 0.132782 0.798464
 | 
			
		||||
vt 0.952676 0.496495
 | 
			
		||||
vt 0.930312 0.536496
 | 
			
		||||
vt 0.926746 0.496495
 | 
			
		||||
vt 0.938021 0.614748
 | 
			
		||||
vt 0.960377 0.574830
 | 
			
		||||
vt 0.956803 0.614748
 | 
			
		||||
vt 0.948620 0.913273
 | 
			
		||||
vt 0.926304 0.873209
 | 
			
		||||
vt 0.952234 0.873235
 | 
			
		||||
vt 0.288040 0.924112
 | 
			
		||||
vt 0.708492 0.755933
 | 
			
		||||
vt 0.919331 0.202686
 | 
			
		||||
vt 0.636675 0.857452
 | 
			
		||||
vt 0.134278 0.855249
 | 
			
		||||
vt 0.693822 0.751601
 | 
			
		||||
vt 0.755609 0.570082
 | 
			
		||||
vt 0.548764 0.359392
 | 
			
		||||
vt 0.770759 0.570082
 | 
			
		||||
vt 0.568241 0.613652
 | 
			
		||||
vt 0.563915 0.613655
 | 
			
		||||
vt 0.437949 0.368066
 | 
			
		||||
vt 0.615148 0.957072
 | 
			
		||||
vt 0.952234 0.957587
 | 
			
		||||
vt 0.106853 0.772534
 | 
			
		||||
vt 0.949094 0.536496
 | 
			
		||||
vt 0.934447 0.574830
 | 
			
		||||
vt 0.929838 0.913255
 | 
			
		||||
vt 0.040925 0.927951
 | 
			
		||||
vt 0.002159 0.870340
 | 
			
		||||
vt 0.040923 0.870330
 | 
			
		||||
vt 0.834247 0.352865
 | 
			
		||||
vt 0.838729 0.310724
 | 
			
		||||
vt 0.838729 0.352865
 | 
			
		||||
vt 0.508750 0.936735
 | 
			
		||||
vt 0.546231 0.879133
 | 
			
		||||
vt 0.546232 0.936754
 | 
			
		||||
vt 0.843047 0.310724
 | 
			
		||||
vt 0.847530 0.352865
 | 
			
		||||
vt 0.843047 0.352865
 | 
			
		||||
vt 0.697810 0.640142
 | 
			
		||||
vt 0.693822 0.697763
 | 
			
		||||
vt 0.693822 0.640142
 | 
			
		||||
vt 0.323097 0.582179
 | 
			
		||||
vt 0.319198 0.524554
 | 
			
		||||
vt 0.323154 0.524558
 | 
			
		||||
vt 0.768084 0.828693
 | 
			
		||||
vt 0.777234 0.870091
 | 
			
		||||
vt 0.768084 0.870092
 | 
			
		||||
vt 0.499788 0.814544
 | 
			
		||||
vt 0.508937 0.856016
 | 
			
		||||
vt 0.499788 0.856016
 | 
			
		||||
vt 0.218712 0.819723
 | 
			
		||||
vt 0.227862 0.861192
 | 
			
		||||
vt 0.218712 0.861192
 | 
			
		||||
vt 0.420031 0.812900
 | 
			
		||||
vt 0.429180 0.854372
 | 
			
		||||
vt 0.420031 0.854372
 | 
			
		||||
vt 0.785891 0.559250
 | 
			
		||||
vt 0.795040 0.568399
 | 
			
		||||
vt 0.785891 0.568399
 | 
			
		||||
vt 0.537924 0.700847
 | 
			
		||||
vt 0.547080 0.691706
 | 
			
		||||
vt 0.547071 0.700856
 | 
			
		||||
vt 0.966064 0.285237
 | 
			
		||||
vt 0.971814 0.298192
 | 
			
		||||
vt 0.966120 0.298192
 | 
			
		||||
vt 0.453257 0.607137
 | 
			
		||||
vt 0.458792 0.620111
 | 
			
		||||
vt 0.453099 0.620105
 | 
			
		||||
vt 0.453125 0.594168
 | 
			
		||||
vt 0.458949 0.607142
 | 
			
		||||
vt 0.961680 0.298192
 | 
			
		||||
vt 0.956052 0.285237
 | 
			
		||||
vt 0.961746 0.285237
 | 
			
		||||
vt 0.961746 0.311174
 | 
			
		||||
vt 0.955987 0.298192
 | 
			
		||||
vt 0.578003 0.963076
 | 
			
		||||
vt 0.583636 0.950107
 | 
			
		||||
vt 0.583697 0.963076
 | 
			
		||||
vt 0.713986 0.956996
 | 
			
		||||
vt 0.737947 0.947070
 | 
			
		||||
vt 0.747872 0.971032
 | 
			
		||||
vt 0.578003 0.937139
 | 
			
		||||
vt 0.577943 0.950107
 | 
			
		||||
vt 0.971758 0.311174
 | 
			
		||||
vt 0.966064 0.311174
 | 
			
		||||
vt 0.307851 0.954728
 | 
			
		||||
vt 0.317762 0.944793
 | 
			
		||||
vt 0.331777 0.978655
 | 
			
		||||
vt 0.921986 0.878344
 | 
			
		||||
vt 0.905406 0.827575
 | 
			
		||||
vt 0.921986 0.820132
 | 
			
		||||
vt 0.884350 0.795866
 | 
			
		||||
vt 0.893886 0.777817
 | 
			
		||||
vt 0.827291 0.793524
 | 
			
		||||
vt 0.817744 0.774692
 | 
			
		||||
vt 0.789645 0.814701
 | 
			
		||||
vt 0.806235 0.823505
 | 
			
		||||
vt 0.789645 0.872913
 | 
			
		||||
vt 0.833108 0.912166
 | 
			
		||||
vt 0.825506 0.933015
 | 
			
		||||
vt 0.878533 0.914030
 | 
			
		||||
vt 0.886125 0.935503
 | 
			
		||||
vt 0.036904 0.863435
 | 
			
		||||
vt 0.057955 0.805815
 | 
			
		||||
vt 0.058039 0.865604
 | 
			
		||||
vt 0.893784 0.134399
 | 
			
		||||
vt 0.914942 0.195798
 | 
			
		||||
vt 0.893712 0.196631
 | 
			
		||||
vt 0.011369 0.804651
 | 
			
		||||
vt 0.032412 0.863875
 | 
			
		||||
vt 0.011276 0.866012
 | 
			
		||||
vt 0.032503 0.745834
 | 
			
		||||
vt 0.011370 0.744910
 | 
			
		||||
vt 0.032435 0.705568
 | 
			
		||||
vt 0.011300 0.703600
 | 
			
		||||
vt 0.297104 0.788044
 | 
			
		||||
vt 0.318245 0.865182
 | 
			
		||||
vt 0.297086 0.866212
 | 
			
		||||
vt 0.057959 0.746102
 | 
			
		||||
vt 0.036897 0.705526
 | 
			
		||||
vt 0.058032 0.703600
 | 
			
		||||
vt 0.036826 0.746953
 | 
			
		||||
vt 0.067083 0.748470
 | 
			
		||||
vt 0.067078 0.803985
 | 
			
		||||
vt 0.067151 0.708957
 | 
			
		||||
vt 0.288040 0.863346
 | 
			
		||||
vt 0.288056 0.790674
 | 
			
		||||
vt 0.002181 0.709075
 | 
			
		||||
vt 0.002246 0.747480
 | 
			
		||||
vt 0.002245 0.803021
 | 
			
		||||
vt 0.002159 0.860067
 | 
			
		||||
vt 0.884977 0.194314
 | 
			
		||||
vt 0.885044 0.136458
 | 
			
		||||
vt 0.067157 0.859570
 | 
			
		||||
vt 0.709979 0.282916
 | 
			
		||||
vt 0.754532 0.352364
 | 
			
		||||
vt 0.709910 0.352320
 | 
			
		||||
vt 0.702128 0.640146
 | 
			
		||||
vt 0.706914 0.684776
 | 
			
		||||
vt 0.702166 0.684781
 | 
			
		||||
vt 0.415360 0.813107
 | 
			
		||||
vt 0.370725 0.882511
 | 
			
		||||
vt 0.370725 0.813107
 | 
			
		||||
vt 0.427272 0.808484
 | 
			
		||||
vt 0.432057 0.763854
 | 
			
		||||
vt 0.432020 0.808488
 | 
			
		||||
vt 0.542664 0.617975
 | 
			
		||||
vt 0.537927 0.687379
 | 
			
		||||
vt 0.537924 0.617975
 | 
			
		||||
vt 0.457848 0.589850
 | 
			
		||||
vt 0.453099 0.520446
 | 
			
		||||
vt 0.457847 0.520446
 | 
			
		||||
vt 0.919379 0.134399
 | 
			
		||||
vt 0.951006 0.183699
 | 
			
		||||
vt 0.919330 0.183667
 | 
			
		||||
vt 0.002161 0.927961
 | 
			
		||||
vt 0.834247 0.310724
 | 
			
		||||
vt 0.508749 0.879113
 | 
			
		||||
vt 0.847530 0.310724
 | 
			
		||||
vt 0.697810 0.697763
 | 
			
		||||
vt 0.319140 0.582175
 | 
			
		||||
vt 0.777234 0.828693
 | 
			
		||||
vt 0.508937 0.814544
 | 
			
		||||
vt 0.227862 0.819722
 | 
			
		||||
vt 0.429180 0.812900
 | 
			
		||||
vt 0.795040 0.559250
 | 
			
		||||
vt 0.537933 0.691697
 | 
			
		||||
vt 0.971758 0.285237
 | 
			
		||||
vt 0.458817 0.594174
 | 
			
		||||
vt 0.956052 0.311174
 | 
			
		||||
vt 0.723911 0.980958
 | 
			
		||||
vt 0.713986 0.971033
 | 
			
		||||
vt 0.723911 0.947070
 | 
			
		||||
vt 0.747872 0.956995
 | 
			
		||||
vt 0.737947 0.980958
 | 
			
		||||
vt 0.583697 0.937139
 | 
			
		||||
vt 0.331777 0.944776
 | 
			
		||||
vt 0.341688 0.954686
 | 
			
		||||
vt 0.341688 0.968720
 | 
			
		||||
vt 0.317762 0.978673
 | 
			
		||||
vt 0.307851 0.968762
 | 
			
		||||
vt 0.905406 0.871198
 | 
			
		||||
vt 0.806235 0.867128
 | 
			
		||||
vt 0.036821 0.805157
 | 
			
		||||
vt 0.915012 0.135139
 | 
			
		||||
vt 0.032502 0.804065
 | 
			
		||||
vt 0.318263 0.788989
 | 
			
		||||
vt 0.754601 0.282960
 | 
			
		||||
vt 0.706876 0.640142
 | 
			
		||||
vt 0.415360 0.882511
 | 
			
		||||
vt 0.427309 0.763850
 | 
			
		||||
vt 0.542666 0.687379
 | 
			
		||||
vt 0.453100 0.589850
 | 
			
		||||
vt 0.951055 0.134431
 | 
			
		||||
s 1
 | 
			
		||||
f 3/1/1 2/2/1 1/3/1
 | 
			
		||||
f 7/4/2 4/5/2 3/6/2
 | 
			
		||||
f 5/7/3 8/8/3 7/9/3
 | 
			
		||||
f 1/10/4 6/11/4 5/12/4
 | 
			
		||||
f 1/13/5 7/14/5 3/15/5
 | 
			
		||||
f 8/16/6 2/17/6 4/18/6
 | 
			
		||||
f 11/19/1 10/20/1 9/21/1
 | 
			
		||||
f 15/22/2 12/23/2 11/24/2
 | 
			
		||||
f 13/25/3 16/26/3 15/27/3
 | 
			
		||||
f 9/28/4 14/29/4 13/30/4
 | 
			
		||||
f 9/31/5 15/32/5 11/33/5
 | 
			
		||||
f 14/34/6 12/35/6 16/36/6
 | 
			
		||||
f 18/37/3 19/38/3 17/39/3
 | 
			
		||||
f 20/40/7 23/41/7 19/42/7
 | 
			
		||||
f 24/43/1 21/44/1 23/45/1
 | 
			
		||||
f 22/46/8 17/47/8 21/48/8
 | 
			
		||||
f 19/49/9 21/50/9 17/51/9
 | 
			
		||||
f 20/52/10 22/53/10 24/54/10
 | 
			
		||||
f 3/1/1 4/55/1 2/2/1
 | 
			
		||||
f 7/4/2 8/56/2 4/5/2
 | 
			
		||||
f 5/7/3 6/57/3 8/8/3
 | 
			
		||||
f 1/10/4 2/58/4 6/11/4
 | 
			
		||||
f 1/13/5 5/59/5 7/14/5
 | 
			
		||||
f 8/16/6 6/60/6 2/17/6
 | 
			
		||||
f 11/19/1 12/61/1 10/20/1
 | 
			
		||||
f 15/22/2 16/62/2 12/23/2
 | 
			
		||||
f 13/25/3 14/63/3 16/26/3
 | 
			
		||||
f 9/28/4 10/64/4 14/29/4
 | 
			
		||||
f 9/31/5 13/65/5 15/32/5
 | 
			
		||||
f 14/34/6 10/66/6 12/35/6
 | 
			
		||||
f 18/37/3 20/67/3 19/38/3
 | 
			
		||||
f 20/40/7 24/68/7 23/41/7
 | 
			
		||||
f 24/43/1 22/69/1 21/44/1
 | 
			
		||||
f 22/46/8 18/70/8 17/47/8
 | 
			
		||||
f 19/49/9 23/71/9 21/50/9
 | 
			
		||||
f 20/52/10 18/72/10 22/53/10
 | 
			
		||||
f 25/73/11 28/74/11 27/75/11
 | 
			
		||||
f 27/76/2 32/77/2 31/78/2
 | 
			
		||||
f 32/79/12 29/80/12 31/81/12
 | 
			
		||||
f 30/82/4 25/83/4 29/84/4
 | 
			
		||||
f 31/85/13 25/86/13 27/87/13
 | 
			
		||||
f 32/88/14 26/89/14 30/90/14
 | 
			
		||||
f 34/91/15 35/92/15 33/93/15
 | 
			
		||||
f 36/94/2 39/95/2 35/96/2
 | 
			
		||||
f 40/97/16 37/98/16 39/99/16
 | 
			
		||||
f 38/100/4 33/101/4 37/102/4
 | 
			
		||||
f 35/103/17 37/104/17 33/105/17
 | 
			
		||||
f 36/106/18 38/107/18 40/108/18
 | 
			
		||||
f 43/109/19 42/110/19 41/111/19
 | 
			
		||||
f 45/112/20 44/113/20 43/114/20
 | 
			
		||||
f 47/115/21 46/116/21 45/112/21
 | 
			
		||||
f 49/117/22 48/118/22 47/119/22
 | 
			
		||||
f 51/120/23 50/121/23 49/117/23
 | 
			
		||||
f 51/122/24 54/123/24 52/124/24
 | 
			
		||||
f 46/125/16 50/126/16 54/127/16
 | 
			
		||||
f 55/128/25 54/123/25 53/129/25
 | 
			
		||||
f 41/111/26 56/130/26 55/131/26
 | 
			
		||||
f 45/132/15 43/133/15 51/134/15
 | 
			
		||||
f 73/135/27 58/136/27 74/137/27
 | 
			
		||||
f 74/137/28 59/138/28 75/139/28
 | 
			
		||||
f 75/139/29 60/140/29 76/141/29
 | 
			
		||||
f 60/140/30 77/142/30 76/141/30
 | 
			
		||||
f 61/143/31 78/144/31 77/142/31
 | 
			
		||||
f 78/144/32 63/145/32 79/146/32
 | 
			
		||||
f 61/143/18 60/140/18 59/138/18
 | 
			
		||||
f 79/146/33 64/147/33 80/148/33
 | 
			
		||||
f 64/147/34 73/135/34 80/148/34
 | 
			
		||||
f 80/149/35 88/150/35 87/151/35
 | 
			
		||||
f 86/152/36 80/153/36 87/154/36
 | 
			
		||||
f 85/155/37 79/156/37 86/157/37
 | 
			
		||||
f 77/158/38 85/155/38 84/159/38
 | 
			
		||||
f 76/160/39 84/159/39 83/161/39
 | 
			
		||||
f 82/162/40 76/163/40 83/164/40
 | 
			
		||||
f 81/165/41 75/166/41 82/167/41
 | 
			
		||||
f 88/150/42 74/168/42 81/165/42
 | 
			
		||||
f 88/150/43 66/169/43 65/170/43
 | 
			
		||||
f 81/165/44 67/171/44 66/169/44
 | 
			
		||||
f 82/162/45 68/172/45 67/173/45
 | 
			
		||||
f 68/174/46 84/159/46 69/175/46
 | 
			
		||||
f 69/175/47 85/155/47 70/176/47
 | 
			
		||||
f 85/155/48 71/177/48 70/176/48
 | 
			
		||||
f 86/152/49 72/178/49 71/179/49
 | 
			
		||||
f 72/180/50 88/150/50 65/170/50
 | 
			
		||||
f 89/181/51 92/182/51 91/183/51
 | 
			
		||||
f 92/184/2 95/185/2 91/186/2
 | 
			
		||||
f 96/187/52 93/188/52 95/189/52
 | 
			
		||||
f 93/190/4 90/191/4 89/192/4
 | 
			
		||||
f 95/193/53 89/194/53 91/195/53
 | 
			
		||||
f 96/196/54 90/197/54 94/198/54
 | 
			
		||||
f 97/199/51 100/200/51 99/201/51
 | 
			
		||||
f 25/73/11 26/202/11 28/74/11
 | 
			
		||||
f 27/76/2 28/203/2 32/77/2
 | 
			
		||||
f 32/79/12 30/204/12 29/80/12
 | 
			
		||||
f 30/82/4 26/205/4 25/83/4
 | 
			
		||||
f 31/85/13 29/206/13 25/86/13
 | 
			
		||||
f 32/88/14 28/207/14 26/89/14
 | 
			
		||||
f 34/91/15 36/208/15 35/92/15
 | 
			
		||||
f 36/94/2 40/209/2 39/95/2
 | 
			
		||||
f 40/97/16 38/210/16 37/98/16
 | 
			
		||||
f 38/100/4 34/211/4 33/101/4
 | 
			
		||||
f 35/103/17 39/212/17 37/104/17
 | 
			
		||||
f 36/106/18 34/213/18 38/107/18
 | 
			
		||||
f 43/109/19 44/214/19 42/110/19
 | 
			
		||||
f 45/112/20 46/116/20 44/113/20
 | 
			
		||||
f 47/115/21 48/215/21 46/116/21
 | 
			
		||||
f 49/117/22 50/121/22 48/118/22
 | 
			
		||||
f 51/120/23 52/216/23 50/121/23
 | 
			
		||||
f 51/122/24 53/129/24 54/123/24
 | 
			
		||||
f 42/217/16 44/218/16 46/125/16
 | 
			
		||||
f 46/125/16 48/219/16 50/126/16
 | 
			
		||||
f 50/126/16 52/220/16 54/127/16
 | 
			
		||||
f 54/127/16 56/221/16 42/217/16
 | 
			
		||||
f 42/217/16 46/125/16 54/127/16
 | 
			
		||||
f 55/128/25 56/222/25 54/123/25
 | 
			
		||||
f 41/111/26 42/110/26 56/130/26
 | 
			
		||||
f 43/133/15 41/223/15 51/134/15
 | 
			
		||||
f 41/223/15 55/224/15 51/134/15
 | 
			
		||||
f 55/224/15 53/225/15 51/134/15
 | 
			
		||||
f 51/134/15 49/226/15 47/227/15
 | 
			
		||||
f 47/227/15 45/132/15 51/134/15
 | 
			
		||||
f 73/135/27 57/228/27 58/136/27
 | 
			
		||||
f 74/137/28 58/136/28 59/138/28
 | 
			
		||||
f 75/139/29 59/138/29 60/140/29
 | 
			
		||||
f 60/140/30 61/143/30 77/142/30
 | 
			
		||||
f 61/143/31 62/229/31 78/144/31
 | 
			
		||||
f 78/144/32 62/229/32 63/145/32
 | 
			
		||||
f 59/138/18 58/136/18 61/143/18
 | 
			
		||||
f 58/136/18 57/228/18 62/229/18
 | 
			
		||||
f 61/143/18 58/136/18 62/229/18
 | 
			
		||||
f 57/228/18 64/147/18 63/145/18
 | 
			
		||||
f 63/145/18 62/229/18 57/228/18
 | 
			
		||||
f 79/146/33 63/145/33 64/147/33
 | 
			
		||||
f 64/147/34 57/228/34 73/135/34
 | 
			
		||||
f 80/149/35 73/230/35 88/150/35
 | 
			
		||||
f 86/152/36 79/231/36 80/153/36
 | 
			
		||||
f 85/155/37 78/232/37 79/156/37
 | 
			
		||||
f 77/158/38 78/232/38 85/155/38
 | 
			
		||||
f 76/160/39 77/158/39 84/159/39
 | 
			
		||||
f 82/162/40 75/233/40 76/163/40
 | 
			
		||||
f 81/165/41 74/168/41 75/166/41
 | 
			
		||||
f 88/150/42 73/230/42 74/168/42
 | 
			
		||||
f 88/150/43 81/165/43 66/169/43
 | 
			
		||||
f 81/165/44 82/167/44 67/171/44
 | 
			
		||||
f 82/162/45 83/164/45 68/172/45
 | 
			
		||||
f 68/174/46 83/161/46 84/159/46
 | 
			
		||||
f 69/175/47 84/159/47 85/155/47
 | 
			
		||||
f 85/155/48 86/157/48 71/177/48
 | 
			
		||||
f 86/152/49 87/154/49 72/178/49
 | 
			
		||||
f 72/180/50 87/151/50 88/150/50
 | 
			
		||||
f 89/181/51 90/234/51 92/182/51
 | 
			
		||||
f 92/184/2 96/235/2 95/185/2
 | 
			
		||||
f 96/187/52 94/236/52 93/188/52
 | 
			
		||||
f 93/190/4 94/237/4 90/191/4
 | 
			
		||||
f 95/193/53 93/238/53 89/194/53
 | 
			
		||||
f 96/196/54 92/239/54 90/197/54
 | 
			
		||||
f 97/199/51 98/240/51 100/200/51
 | 
			
		||||
| 
		 Before Width: | Height: | Size: 10 MiB After Width: | Height: | Size: 13 MiB  | 
| 
		 After Width: | Height: | Size: 10 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								Projekte/mdga/client/src/main/resources/Sounds/tank_shoot.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								Projekte/mdga/client/src/main/resources/Sounds/turret_rotate.ogg
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -38,7 +38,7 @@ protected void handlePowerCard(PlayCardMessage msg) {
 | 
			
		||||
    protected void throwPiece(Piece piece) {
 | 
			
		||||
        logic.getGame().getBoard().getInfield()[logic.getGame().getBoard().getInfieldIndexOfPiece(piece)].clearOccupant();
 | 
			
		||||
        logic.getGame().getPlayerByColor(piece.getColor()).addWaitingPiece(piece);
 | 
			
		||||
        logic.addNotification(new ThrowPieceNotification(piece.getUuid()));
 | 
			
		||||
        logic.addNotification(new ThrowPieceNotification(piece.getUuid(), piece.getColor()));
 | 
			
		||||
        logic.getGame().getPlayerByColor(piece.getColor()).getPlayerStatistic().increasePiecesBeingThrown();
 | 
			
		||||
        logic.getGame().getGameStatistics().increasePiecesBeingThrown();
 | 
			
		||||
        piece.setState(PieceState.WAITING);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
package pp.mdga.notification;
 | 
			
		||||
 | 
			
		||||
import pp.mdga.game.Color;
 | 
			
		||||
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -10,12 +12,14 @@ public class ThrowPieceNotification extends Notification {
 | 
			
		||||
     * The id of the piece that was thrown.
 | 
			
		||||
     */
 | 
			
		||||
    private final UUID pieceId;
 | 
			
		||||
    private final Color throwColor;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * This constructor is used to create a new ThrowPieceNotification
 | 
			
		||||
     */
 | 
			
		||||
    public ThrowPieceNotification(UUID pieceId) {
 | 
			
		||||
    public ThrowPieceNotification(UUID pieceId, Color throwColor) {
 | 
			
		||||
        this.pieceId = pieceId;
 | 
			
		||||
        this.throwColor = throwColor;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -26,4 +30,8 @@ public ThrowPieceNotification(UUID pieceId) {
 | 
			
		||||
    public UUID getPieceId() {
 | 
			
		||||
        return pieceId;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Color getThrowColor() {
 | 
			
		||||
        return throwColor;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||