diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSyncronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSyncronizer.java new file mode 100644 index 00000000..63271e5e --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSyncronizer.java @@ -0,0 +1,81 @@ +package pp.mdga.client; + +import com.jme3.input.InputManager; +import com.jme3.input.KeyInput; +import com.jme3.input.MouseInput; +import com.jme3.input.controls.*; + +public class InputSyncronizer { + + private MdgaApp app; + private InputManager inputManager; + + protected boolean rightMousePressed = false; + private float rotationAngle = 0f; + private int scrollValue = 50; + + InputSyncronizer(MdgaApp app) { + this.app = app; + + this.inputManager = app.getInputManager(); + + setupInput(); + } + + public void update() { + rotateModel(); + updateScrollValue(); + } + + private void setupInput() { + inputManager.addMapping("Settings", new KeyTrigger(KeyInput.KEY_ESCAPE)); + + inputManager.addMapping("RotateRightMouse", new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); + inputManager.addMapping("MouseLeft", new MouseAxisTrigger(MouseInput.AXIS_X, false)); // Left movement + inputManager.addMapping("MouseRight", new MouseAxisTrigger(MouseInput.AXIS_X, true)); // Right movement + inputManager.addMapping("MouseScrollUp", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, false)); // Scroll up + inputManager.addMapping("MouseScrollDown", new MouseAxisTrigger(MouseInput.AXIS_WHEEL, true)); // Scroll down + + inputManager.addListener(actionListener, "Settings", "RotateRightMouse"); + inputManager.addListener(analogListener, "MouseLeft", "MouseRight", "MouseScrollUp", "MouseScrollDown"); + } + + private final ActionListener actionListener = new ActionListener() { + @Override + public void onAction(String name, boolean isPressed, float tpf) { + if (name.equals("Settings") && isPressed) { + app.getView().pressEscape(); + } + if (name.equals("RotateRightMouse")) { + rightMousePressed = isPressed; + } + } + }; + + private final AnalogListener analogListener = new AnalogListener() { + @Override + public void onAnalog(String name, float value, float tpf) { + if (name.equals("MouseLeft") && rightMousePressed) { + rotationAngle -= value * 360f; + rotateModel(); + } else if (name.equals("MouseRight") && rightMousePressed) { + rotationAngle += value * 360f; + rotateModel(); + } else if (name.equals("MouseScrollUp")) { + scrollValue = Math.min(100, scrollValue + 1); + updateScrollValue(); + } else if (name.equals("MouseScrollDown")) { + scrollValue = Math.max(1, scrollValue - 1); + updateScrollValue(); + } + } + }; + + private void rotateModel() { + //System.out.println("Rotation Angle: " + rotationAngle); + } + + private void updateScrollValue() { + //System.out.println("Scroll Value: " + scrollValue); + } +} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java index f984e549..95e69fc1 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaApp.java @@ -1,45 +1,39 @@ package pp.mdga.client; import com.jme3.app.SimpleApplication; -import com.jme3.input.KeyInput; -import com.jme3.input.MouseInput; -import com.jme3.input.controls.ActionListener; -import com.jme3.input.controls.KeyTrigger; -import com.jme3.input.controls.MouseButtonTrigger; -import com.jme3.post.FilterPostProcessor; -import com.jme3.renderer.lwjgl.LwjglRender; -import com.jme3.system.NanoTimer; +import com.simsilica.lemur.GuiGlobals; import pp.mdga.client.acoustic.AcousticHandler; -import pp.mdga.client.acoustic.MdgaSound; import pp.mdga.client.animation.AnimationHandler; import com.jme3.system.AppSettings; -import pp.mdga.client.board.BoardHandler; -import pp.mdga.client.board.CameraHandler; -import pp.mdga.client.dialog.DialogView; +import pp.mdga.client.view.*; import pp.mdga.game.Color; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; - +import pp.mdga.notification.TskSelectNotification; public class MdgaApp extends SimpleApplication { private AnimationHandler animationHandler; private AcousticHandler acousticHandler; - private BoardHandler boardHandler; - private DialogView dialogView; + private NotificationSynchronizer notificationSynchronizer; + private InputSyncronizer inputSyncronizer; + private ModelSyncronizer modelSyncronizer; - NanoTimer test = new NanoTimer(); - private MdgaState testState = MdgaState.MAIN; + MdgaView view = null; + private MdgaState state = MdgaState.MAIN; + + private static float resolutionFactor = 1; public static void main(String[] args) { AppSettings settings = new AppSettings(true); settings.setSamples(128); settings.setCenterWindow(true); - settings.setWidth(1280); - settings.setHeight(720); + + int width = (int)(1280 * resolutionFactor); + int height = (int)(720 * resolutionFactor); + + settings.setWidth(width); + settings.setHeight(height); + settings.setVSync(false); - settings.setRenderer(AppSettings.LWJGL_OPENGL45); + MdgaApp app = new MdgaApp(); app.setSettings(settings); app.setShowSettings(false); @@ -50,87 +44,55 @@ public static void main(String[] args) { public void simpleInitApp() { animationHandler = new AnimationHandler(this); acousticHandler = new AcousticHandler(this); - dialogView = new DialogView(this); - - //Filter für Outline: Reihenfolge CameraHandler(dlsf) -> BoardHandler -> viewPort.addProcessor einhalten! - FilterPostProcessor fpp = new FilterPostProcessor(assetManager); - new CameraHandler(this, fpp); - boardHandler = new BoardHandler(this, fpp); - viewPort.addProcessor(fpp); - - - //dialogView.mainMenu(); - //acousticHandler.playState(MdgaState.GAME); - -// acousticHandler.playSound(MdgaSound.LOST); -// acousticHandler.playSound(MdgaSound.VICTORY); - - - - UUID test0 = UUID.randomUUID(); - UUID test1 = UUID.randomUUID(); - List testList = new ArrayList<>(); - testList.add(UUID.randomUUID()); - testList.add(UUID.randomUUID()); - testList.add(test1); - testList.add(test0); - - UUID test0_1 = UUID.randomUUID(); - UUID test1_1 = UUID.randomUUID(); - List testList_1 = new ArrayList<>(); - testList_1.add(UUID.randomUUID()); - testList_1.add(UUID.randomUUID()); - testList_1.add(test1_1); - testList_1.add(test0_1); - -// boardHandler.init(); - - inputManager.addMapping("h", new KeyTrigger(KeyInput.KEY_H)); - inputManager.addMapping("j", new KeyTrigger(KeyInput.KEY_J)); - inputManager.addListener(new ActionListener() { - @Override - public void onAction(String name, boolean isPressed, float tpf) { - if (name.equals("h") && isPressed) { - boardHandler.init(); - } - if(name.equals("j") && isPressed){ - boardHandler.shutdown(); - } - } - }, "h", "j"); - - -// boardHandler.addPlayer(Color.AIRFORCE, testList); -// boardHandler.movePieceStart(test0, 0); -// -// boardHandler.addPlayer(Color.CYBER, testList_1); -// boardHandler.movePieceStart(test0_1, 10); - + notificationSynchronizer = new NotificationSynchronizer(this); + inputSyncronizer = new InputSyncronizer(this); + modelSyncronizer = new ModelSyncronizer(this); + inputManager.deleteMapping("SIMPLEAPP_Exit"); + GuiGlobals.initialize(this); + enter(state); } @Override public void simpleUpdate(float tpf) { + inputSyncronizer.update(); + view.update(); acousticHandler.update(); + notificationSynchronizer.update(); + } - //test.reset(); - if (test.getTimeInSeconds() > 10) { - if (testState == MdgaState.MAIN) { - testState = MdgaState.LOBBY; - acousticHandler.playState(MdgaState.MAIN); - } - else if (testState == MdgaState.LOBBY) { - testState = MdgaState.CEREMONY; - acousticHandler.playState(MdgaState.LOBBY); - } - else { - testState = MdgaState.MAIN; - acousticHandler.playState(MdgaState.CEREMONY); - } - - test.reset(); + public void enter(MdgaState state) { + if(null != view) { + view.leave(); } + + this.state = state; + + switch (state) { + case MAIN: + view = new MainView(this); + break; + case LOBBY: + view = new LobbyView(this); + break; + case GAME: + view = new GameView(this); + break; + case CEREMONY: + view = new CeremonyView(this); + break; + case NONE: + throw new RuntimeException("cant enter state NONE"); + } + + acousticHandler.playState(state); + + view.enter(); + } + + public void afteGameCleanup() { + //TODO } public AnimationHandler getAnimationHandler() { @@ -141,11 +103,17 @@ public AcousticHandler getAcousticHandler() { return acousticHandler; } - public BoardHandler getBoardView() { - return boardHandler; + public MdgaState getState() {return state; } + + public float getResolutionFactor() { + return resolutionFactor; } - public DialogView getDialogView() { - return dialogView; + public MdgaView getView() { + return view; + } + + public ModelSyncronizer getModelSyncronizer() { + return modelSyncronizer; } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaState.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaState.java index d178bce7..58414b23 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaState.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/MdgaState.java @@ -5,44 +5,9 @@ import pp.mdga.notification.PlayerInGameNotification; public enum MdgaState { - NONE { - @Override - void handleNotification(MdgaApp app, Notification notification) { - throw new RuntimeException("unexpected notification"); - } - }, - MAIN { - @Override - void handleNotification(MdgaApp app, Notification notification) { - throw new RuntimeException("unexpected notification"); - } - }, - LOBBY { - @Override - void handleNotification(MdgaApp app, Notification notification) { - throw new RuntimeException("unexpected notification"); - } - }, - GAME { - @Override - void handleNotification(MdgaApp app, Notification notification) { - if (notification instanceof PlayerInGameNotification) { - //TODO - } - else if (notification instanceof PieceInGameNotification) { - //TODO - } - else { - throw new RuntimeException("unexpected notification"); - } - } - }, - CEREMONY { - @Override - void handleNotification(MdgaApp app, Notification notification) { - throw new RuntimeException("unexpected notification"); - } - }; - - abstract void handleNotification(MdgaApp app, Notification notification); + NONE, + MAIN, + LOBBY, + GAME, + CEREMONY; } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/ModelSyncronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/ModelSyncronizer.java new file mode 100644 index 00000000..d2eec292 --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/ModelSyncronizer.java @@ -0,0 +1,72 @@ +package pp.mdga.client; + +import pp.mdga.client.view.LobbyView; +import pp.mdga.game.Color; + +public class ModelSyncronizer { + private MdgaApp app; + + ModelSyncronizer(MdgaApp app) { + this.app = app; + } + + public void selectPiece() { + //TODO call from somewhere + System.out.println("selectPiece"); + } + + public void selectCard() { + //TODO call from somewhere + System.out.println("selectCard"); + } + + public void selectTsk(Color color) { + //TODO call from somewhere + System.out.println("selectTsk: " + color); + LobbyView view = (LobbyView) app.getView(); + view.setTaken(color, true, true, "OwnPlayerName"); + } + + public void unselectTsk() { + //TODO call from somewhere + System.out.println("unselectTsk"); + } + + public void rolledDice() { + //TODO call from somewhere + System.out.println("rolledDice"); + } + + public void setName() { + //TODO call from somewhere + System.out.println("setName"); + } + + public void setReady() { + //TODO call from somewhere + System.out.println("setReady"); + app.enter(MdgaState.GAME); + } + + public void setHost(int port) { + //TODO call from somewhere + System.out.println("setHost: " + port); + app.enter(MdgaState.LOBBY); + } + + public void setJoin(String ip, int port) { + //TODO call from somewhere + System.out.println("setJoin"); + app.enter(MdgaState.LOBBY); + } + + public void leave() { + System.out.println("leave"); + app.enter(MdgaState.MAIN); + } + + public void enter(MdgaState state) { + System.out.println("enter:" + state); + app.enter(state); + } +} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java index 2a95a2a7..76767434 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java @@ -1,23 +1,136 @@ package pp.mdga.client; -import pp.mdga.notification.Notification; +import pp.mdga.client.view.GameView; +import pp.mdga.client.view.LobbyView; +import pp.mdga.notification.*; import java.util.ArrayList; public class NotificationSynchronizer { private final MdgaApp app; - private MdgaState state = MdgaState.MAIN; + + private ArrayList notifications = new ArrayList<>(); NotificationSynchronizer(MdgaApp app) { this.app = app; } - void update() { - ArrayList notifications = new ArrayList<>(); + public void addTestNotification(Notification n) { + notifications.add(n); + } + + public void update() { //TODO fetch model notifications for (Notification n : notifications) { - state.handleNotification(app, n); + switch (app.getState()) { + case MAIN: + handleMain(n); + break; + case LOBBY: + handleLobby(n); + break; + case GAME: + handleGame(n); + break; + case CEREMONY: + handleCeremony(n); + break; + case NONE: + throw new RuntimeException("no notification expected: " + n.toString()); + } + } + } + + private void handleMain(Notification notification) { + if (notification instanceof LobbyDialogNotification) { + app.enter(MdgaState.LOBBY); + } else { + throw new RuntimeException("notification not expected: " + notification.toString()); + } + } + + private void handleLobby(Notification notification) { + LobbyView lobbyView = (LobbyView) app.getView(); + + if (notification instanceof TskSelectNotification) { + TskSelectNotification n = (TskSelectNotification)notification; + lobbyView.setTaken(n.getColor(), true, n.isSelf(), n.getName()); + } else if (notification instanceof TskUnselectNotification) { + TskUnselectNotification n = (TskUnselectNotification)notification; + lobbyView.setTaken(n.getColor(), false, false, null); + } else if (notification instanceof GameNotification) { + app.enter(MdgaState.GAME); + } else { + throw new RuntimeException("notification not expected: " + notification.toString()); + } + } + + private void handleGame(Notification notification) { + GameView gameView = (GameView) app.getView(); + + if (notification instanceof AcquireCardNotification) { + // Handle AcquireCardNotification + } else if (notification instanceof ActivePlayerNotification) { + // Handle ActivePlayerNotification + } else if (notification instanceof CeremonyNotification) { + app.enter(MdgaState.CEREMONY); + } else if (notification instanceof DiceNowNotification) { + // Handle DiceNowNotification + } else if (notification instanceof DicingNotification) { + // Handle DicingNotification + } else if (notification instanceof DrawCardNotification) { + // Handle DrawCardNotification + } else if (notification instanceof HomeMoveNotification) { + HomeMoveNotification n = (HomeMoveNotification)notification; + gameView.getBoardHandler().moveHomePiece(n.getPieceId(), n.getHomeIndex()); + } else if (notification instanceof InterruptNotification) { + // Handle InterruptNotification + } else if (notification instanceof MovePieceNotification) { + MovePieceNotification n = (MovePieceNotification)notification; + //gameView.getBoardHandler().movePiece(n.get); //TODO + } else if (notification instanceof MoveThrowPieceNotification) { + MoveThrowPieceNotification n = (MoveThrowPieceNotification)notification; + //gameView.getBoardHandler().throwPiece(n.); //TODO + } else if (notification instanceof NoShieldNotification) { + NoShieldNotification n = (NoShieldNotification)notification; + gameView.getBoardHandler().unshieldPiece(n.getPieceId()); + } else if (notification instanceof PieceInGameNotification) { + // Handle PieceInGameNotification + } else if (notification instanceof PlayCardNotification) { + // Handle PlayCardNotification + } else if (notification instanceof PlayerInGameNotification) { + // Handle PlayerInGameNotification + } else if (notification instanceof ResumeNotification) { + // Handle ResumeNotification + } else if (notification instanceof RollDiceNotification) { + // Handle RollDiceNotification + } else if (notification instanceof SelectableCardsNotification) { + // Handle SelectableCardsNotification + } else if (notification instanceof SelectablePiecesNotification) { + // Handle SelectablePiecesNotification + } else if (notification instanceof ShieldActiveNotification) { + ShieldActiveNotification n = (ShieldActiveNotification)notification; + gameView.getBoardHandler().shieldPiece(n.getPieceId()); + } else if (notification instanceof ShieldSuppressedNotification) { + ShieldSuppressedNotification n = (ShieldSuppressedNotification)notification; + gameView.getBoardHandler().suppressShield(n.getPieceId()); + } else if (notification instanceof StartDialogNotification) { + app.enter(MdgaState.MAIN); + } else if (notification instanceof SwapPieceNotification) { + // Handle SwapPieceNotification + } else if (notification instanceof WaitMoveNotification) { + // Handle WaitMoveNotification + } else { + throw new RuntimeException("notification not expected: " + notification.toString()); + } + } + + private void handleCeremony(Notification notification) { + if (notification instanceof StartDialogNotification) { + app.enter(MdgaState.MAIN); + } else { + throw new RuntimeException("notification not expected: " + notification.toString()); } } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java index e03715ed..08979182 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java @@ -15,17 +15,18 @@ public class AcousticHandler { private ArrayList gameTracks = new ArrayList<>(); private NanoTimer trackTimer = new NanoTimer(); - private boolean fading = false; - private NanoTimer fadeTimer = new NanoTimer(); - private static final float FADE_DURATION = 3.0f; - private static final float CROSSFADE_DURATION = 1.5f; + private boolean fading = false; // Indicates if a fade is in progress + private NanoTimer fadeTimer = new NanoTimer(); // Timer to track fade progress + private static final float FADE_DURATION = 3.0f; // Duration for outfade + private static final float CROSSFADE_DURATION = 1.5f; // Duration for infade + private GameMusic playing = null; // Currently playing track + private GameMusic scheduled = null; // Scheduled track to play next + private GameMusic old = null; // Old track being faded out private float mainVolume = 1.0f; private float musicVolume = 1.0f; private float soundVolume = 1.0f; - private GameMusic scheduled = null; - private GameMusic playing = null; private ArrayList sounds = new ArrayList<>(); public AcousticHandler(MdgaApp app) { @@ -88,7 +89,6 @@ public void playState(MdgaState state) { if (this.state == state) { return; } - MusicAsset asset = null; switch (state) { @@ -103,18 +103,20 @@ public void playState(MdgaState state) { case GAME: addGameTracks(); playGame = true; - assert (gameTracks.size() > 0) : "no more game music available"; + assert (!gameTracks.isEmpty()) : "no more game music available"; asset = gameTracks.remove(0); break; case CEREMONY: playGame = false; asset = MusicAsset.CEREMONY; break; + case NONE: + throw new RuntimeException("no music for state NONE"); } assert (null != asset) : "music sceduling went wrong"; - scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop()); + scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), 0.0f); } /** @@ -130,56 +132,162 @@ private float lerp(float start, float end, float t) { } /** - * Updates the current volume and handles track crossfading logic. - * This method is responsible for fading out the currently playing track, - * fading in the scheduled track, and handling crossfade between the two tracks. + * Updates the state of audio playback, handling track transitions and volume adjustments. + * + * This method ensures smooth transitions between tracks using fade-in and fade-out effects. + * It also handles cases where no track is playing, starting a scheduled track immediately at full volume. + * The method prioritizes the latest scheduled track if multiple scheduling occurs quickly. + * + * Behavior: + * 1. If nothing is scheduled and no track is playing, it exits early. + * 2. If a scheduled track exists and no track is playing, the scheduled track starts immediately at full volume. + * 3. If a scheduled track exists while a track is playing, it initiates a fade-out for the currently playing track + * and prepares for the new track to fade in. + * 4. If a track transition is in progress (fading), it processes the fade-out and fade-in states. + * If a new track is scheduled during this process, it interrupts the current transition and prioritizes the new track. + * 5. If no fading is needed and a track is playing, it ensures the track's volume is updated. + * + * Special cases: + * - If no track is playing and a new track is scheduled, it starts the track immediately without fading. + * - If a new track is scheduled during fading, it resets the transition to prioritize the new track. */ private void updateVolumeAndTrack() { - if (playing == null && scheduled != null && !fading) { + if (scheduled == null && !fading && playing == null) { + // Nothing to do, early exit + return; + } + + if (scheduled != null && playing == null && !fading) { + // No current track, start scheduled track immediately at full volume playing = scheduled; scheduled = null; playing.play(); + playing.update(getMusicVolumeTotal()); // Set volume to full return; } if (scheduled != null && !fading) { + // Initiate a fade process if a new track is scheduled fading = true; fadeTimer.reset(); + old = playing; // The currently playing track becomes the old track + playing = null; // Clear the playing track during the fade process } if (fading) { - float time = fadeTimer.getTimeInSeconds(); + handleFadeProcess(); - if (time <= FADE_DURATION) { - float t = Math.min(time / FADE_DURATION, 1.0f); - float oldVolume = lerp(1.0f, 0.0f, t); - if (playing != null) { - playing.update(getMusicVolumeTotal() * oldVolume); - } + // Handle any interruptions due to newly scheduled tracks + if (scheduled != null && playing != null && playing != scheduled) { + // Interrupt the current infade and switch to the new scheduled track + old = playing; // Treat the currently infading track as the old track + playing = null; // Reset playing to allow switching + fadeTimer.reset(); // Restart fade timer for the new track } + } else if (playing != null) { + // Update volume for the currently playing track + playing.update(getMusicVolumeTotal()); + } else if (scheduled != null) { + // If no track is playing and one is scheduled, start it immediately at full volume + playing = scheduled; + scheduled = null; + playing.play(); + playing.update(getMusicVolumeTotal()); // Set volume to full + } + } - if (time > FADE_DURATION && time <= FADE_DURATION + CROSSFADE_DURATION) { - float t = Math.min((time - FADE_DURATION) / CROSSFADE_DURATION, 1.0f); - float newVolume = lerp(0.0f, 1.0f, t); + /** + * Manages the fading process during audio track transitions. + * + * This method handles the fade-out of the currently playing (old) track, manages any pause between the fade-out + * and fade-in, and initiates the fade-in for the new track if applicable. It ensures smooth transitions between + * tracks while maintaining the correct volume adjustments. + * + * Behavior: + * 1. **Outfade:** Gradually decreases the volume of the `old` track over the duration of `FADE_DURATION`. + * Once the outfade completes, the `old` track is paused and cleared. + * 2. **Pause Handling:** Waits for a defined pause (if applicable) before initiating the infade for the next track. + * 3. **Infade:** If a `scheduled` track exists and the outfade and pause are complete, it begins playing + * the new track (`playing`) and initiates the infade process. + * + * Key Details: + * - The outfade volume adjustment is interpolated linearly from full volume to zero using the `lerp` function. + * - The pause duration is retrieved from the scheduled track if it is specified. + * - If a new track is scheduled during the fade process, it is handled by external logic to prioritize transitions. + * + * Preconditions: + * - `fading` is expected to be `true` when this method is called. + * - The method is invoked as part of the `updateVolumeAndTrack` process. + */ + private void handleFadeProcess() { + float time = fadeTimer.getTimeInSeconds(); - if (!scheduled.isPlaying()) { - scheduled.play(); - } - scheduled.update(getMusicVolumeTotal() * newVolume); - } + // Handle outfade for the old track + if (old != null && time <= FADE_DURATION) { + float t = Math.min(time / FADE_DURATION, 1.0f); + float oldVolume = lerp(1.0f, 0.0f, t); + old.update(getMusicVolumeTotal() * oldVolume); + } - if (time > FADE_DURATION + CROSSFADE_DURATION) { - if (playing != null) { - playing.pause(); - } + if (old != null && time > FADE_DURATION) { + // Complete outfade + old.pause(); + old = null; + } + + // Handle pause duration before infade + float pause = (scheduled != null) ? scheduled.getPause() : 0.0f; + if (time > FADE_DURATION + pause) { + if (playing == null && scheduled != null) { + // Begin infade for the new track playing = scheduled; scheduled = null; - - fading = false; + playing.play(); // Start playing the new track } + handleInfade(time - FADE_DURATION - pause); } - else if (playing != null) { - playing.update(getMusicVolumeTotal()); + } + + /** + * Manages the fade-in process for the currently playing track. + * + * This method gradually increases the volume of the `playing` track from zero to full volume + * over the duration of `CROSSFADE_DURATION`. It ensures a smooth transition into the new track. + * + * Behavior: + * 1. If no track is set as `playing`, the method exits early, as there is nothing to fade in. + * 2. Linearly interpolates the volume of the `playing` track from 0.0 to 1.0 based on the elapsed + * `infadeTime` and the specified `CROSSFADE_DURATION`. + * 3. Once the fade-in is complete (when `infadeTime` exceeds `CROSSFADE_DURATION`), the method: + * - Marks the fade process (`fading`) as complete. + * - Ensures the `playing` track is updated to its full volume. + * + * Key Details: + * - Uses the `lerp` function to calculate the volume level for the `playing` track during the fade-in. + * - Ensures the volume is always a value between 0.0 and 1.0. + * - The `infadeTime` parameter should be relative to the start of the fade-in process. + * + * Preconditions: + * - The `playing` track must be initialized and actively fading in for this method to have an effect. + * - The method is invoked as part of the `updateVolumeAndTrack` process. + * + * @param infadeTime The elapsed time (in seconds) since the fade-in process started. + */ + private void handleInfade(float infadeTime) { + if (playing == null) { + // Nothing to infade + return; + } + + // Proceed with the infade for the current playing track + float t = Math.min(infadeTime / CROSSFADE_DURATION, 1.0f); + float newVolume = lerp(0.0f, 1.0f, t); + playing.update(getMusicVolumeTotal() * newVolume); + + if (infadeTime > CROSSFADE_DURATION) { + // Infade is complete, finalize state + fading = false; + playing.update(getMusicVolumeTotal()); // Ensure full volume } } @@ -201,6 +309,10 @@ private void addGameTracks() { * a new track will be scheduled to play. If the list of game tracks is empty, it will be refreshed. */ private void updateGameTracks() { + if(null == playing) { + return; + } + if (playing.nearEnd(10)) { if (gameTracks.isEmpty()) { addGameTracks(); @@ -212,7 +324,7 @@ private void updateGameTracks() { MusicAsset nextTrack = gameTracks.remove(0); - scheduled = new GameMusic(app, nextTrack, getMusicVolumeTotal(), nextTrack.getSubVolume(), nextTrack.getLoop()); + scheduled = new GameMusic(app, nextTrack, getMusicVolumeTotal(), nextTrack.getSubVolume(), nextTrack.getLoop(), 0.0f); } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/GameMusic.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/GameMusic.java index fd3156bb..551a9780 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/GameMusic.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/GameMusic.java @@ -14,6 +14,7 @@ class GameMusic { private float volume; private final float subVolume; private final AudioNode music; + private float pause; /** * Constructs a new GameMusic object. @@ -24,9 +25,10 @@ class GameMusic { * @param subVolume A relative volume that modifies the base music volume, typically a percentage. * @param loop A flag indicating whether the music should loop once it finishes. */ - GameMusic(MdgaApp app, MusicAsset asset, float volume, float subVolume, boolean loop) { + GameMusic(MdgaApp app, MusicAsset asset, float volume, float subVolume, boolean loop, float pause) { this.volume = volume; this.subVolume = subVolume; + this.pause = pause; music = new AudioNode(app.getAssetManager(), asset.getPath(), AudioData.DataType.Stream); music.setPositional(false); @@ -107,4 +109,8 @@ void update(float newVolume) { music.setVolume(volume * subVolume); } } + + float getPause() { + return pause; + } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MusicAsset.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MusicAsset.java index 58073c0e..d8f14600 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MusicAsset.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MusicAsset.java @@ -7,15 +7,15 @@ * These music assets are used to control the music that plays in different parts of the game, such as menus and in-game music. */ enum MusicAsset { - MAIN_MENU("Spaceship.wav", 1.0f), - LOBBY("DeadPlanet.wav", 1.0f), - CEREMONY("80s,Disco,Life.wav", 1.0f), - GAME_1("NeonRoadTrip.wav", false, 1.0f), - GAME_2("NoPressureTrance.wav", false, 1.0f), - GAME_3("TheSynthRave.wav", false, 1.0f), - GAME_4("LaserParty.wav", false, 1.0f), - GAME_5("RetroNoir.wav", false, 1.0f), - GAME_6("SpaceInvaders.wav", false, 1.0f); + 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); private final String path; private final boolean loop; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java index 63266f65..d523e6d6 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java @@ -6,40 +6,43 @@ import com.jme3.math.Vector3f; import com.jme3.post.FilterPostProcessor; import com.jme3.shadow.DirectionalLightShadowFilter; -import com.jme3.shadow.DirectionalLightShadowRenderer; import pp.mdga.client.MdgaApp; public class CameraHandler { MdgaApp app; + private DirectionalLight sun; + private AmbientLight ambient; private static final int SHADOWMAP_SIZE = 1024 * 8; - FilterPostProcessor fpp; - public CameraHandler(MdgaApp app, FilterPostProcessor fpp){ this.app = app; - this.fpp = fpp; - initCamera(); - } - private void initCamera() { - app.getFlyByCamera().setEnabled(true); - int zoom = 15; - app.getCamera().setLocation(new Vector3f(-zoom, 0, zoom)); - app.getCamera().lookAt(new Vector3f(0, 0, 0), new Vector3f(0, 0, 1)); - - DirectionalLight sun = new DirectionalLight(); + sun = new DirectionalLight(); sun.setColor(ColorRGBA.White); sun.setDirection(new Vector3f(0.3f, 0, -1)); - app.getRootNode().addLight(sun); - AmbientLight ambient = new AmbientLight(); + ambient = new AmbientLight(); ambient.setColor(new ColorRGBA(0.3f, 0.3f, 0.3f, 1)); - app.getRootNode().addLight(ambient); DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), SHADOWMAP_SIZE, 4); dlsf.setLight(sun); fpp.addFilter(dlsf); } + + public void init() { + app.getFlyByCamera().setEnabled(true); + int zoom = 15; + app.getCamera().setLocation(new Vector3f(-zoom, 0, zoom)); + app.getCamera().lookAt(new Vector3f(0, 0, 0), new Vector3f(0, 0, 1)); + + app.getRootNode().addLight(sun); + app.getRootNode().addLight(ambient); + } + + public void shutdown() { + app.getRootNode().removeLight(sun); + app.getRootNode().removeLight(ambient); + } } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/Dialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/Dialog.java index a39a2634..421f4cff 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/Dialog.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/Dialog.java @@ -1,5 +1,82 @@ package pp.mdga.client.dialog; -public class Dialog { +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.HAlignment; +import com.simsilica.lemur.VAlignment; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import pp.mdga.client.MdgaApp; +import static com.jme3.math.FastMath.floor; + +public abstract class Dialog { + protected final ColorRGBA COLOR_DEFAULT = ColorRGBA.Gray; + protected final ColorRGBA COLOR_HOVER = ColorRGBA.DarkGray; + + protected Container container; + + protected final MdgaApp app; + private final Node node; + + protected final float vertical_step; + protected final float horitontal_step; + + protected float fontSize = 35; + + public Dialog(MdgaApp app, Node node) { + this.app = app; + this.node = node; + this.container = new Container(); + + this.horitontal_step = app.getCamera().getWidth() / 16; + this.vertical_step = app.getCamera().getHeight() / 9; + + int val = (int) (32 * Math.min(app.getResolutionFactor() * 0.9f, 1)); + fontSize = val; + } + + public void show() { + node.attachChild(container); + } + + public void hide () { + node.detachChild(container); + } + + protected void createButton(String label, Runnable action, Vector3f size) { + Button button = new Button(label); + button.addClickCommands(source -> action.run()); + button.setFontSize(fontSize); + button.setHighlightColor(ColorRGBA.White); + button.setColor(ColorRGBA.Black); + button.setPreferredSize(size); + button.setTextHAlignment(HAlignment.Center); + button.setTextVAlignment(VAlignment.Center); + + QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT); + background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor()); + button.setBackground(background); + + button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> { + QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER); + hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor()); + source.setBackground(hoverBackground); + button.setHighlightColor(ColorRGBA.White); + button.setColor(ColorRGBA.Black); + }); + + button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> { + QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT); + normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor()); + source.setBackground(normalBackground); + button.setHighlightColor(ColorRGBA.White); + button.setColor(ColorRGBA.Black); + }); + + container.addChild(button); + } } + diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/DialogView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/DialogView.java deleted file mode 100644 index 13f9b2a2..00000000 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/DialogView.java +++ /dev/null @@ -1,26 +0,0 @@ -package pp.mdga.client.dialog; - -import pp.dialog.DialogManager; -import pp.mdga.client.MdgaApp; - -public class DialogView { - private MdgaApp app; - - private DialogManager dialogManager = new DialogManager(app); - - private StartDialog dialog; - - public DialogView(MdgaApp app) { - this.app = app; - } - - DialogManager getDialogManager() { - return dialogManager; - } - - public void mainMenu() { - //dialogManager = new DialogManager(app); - //di - //MainMenuDialog mainMenuDialog = new MainMenuDialog(app); - } -} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/HostDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/HostDialog.java new file mode 100644 index 00000000..4d7ff73c --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/HostDialog.java @@ -0,0 +1,143 @@ +package pp.mdga.client.dialog; + +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.jme3.texture.Texture; +import com.simsilica.lemur.*; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import com.simsilica.lemur.component.SpringGridLayout; +import pp.mdga.client.MdgaApp; + +public class HostDialog extends Dialog { + private TextField portInput; + + public HostDialog(MdgaApp app, Node node, Runnable backAction) { + super(app, node); + + QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray); + quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor()); + container.setBackground(quad1); + + Texture texture = app.getAssetManager().loadTexture("mdga_logo.png"); + + QuadBackgroundComponent b = new QuadBackgroundComponent(texture); + + Panel imagePanel = new Panel(); + imagePanel.setBackground(b); + + container.addChild(imagePanel).setPreferredSize(new Vector3f(texture.getImage().getWidth() / 4, texture.getImage().getHeight() / 4, 0)); + + //abstandshalter + container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray)); + + createTextField(); + + //abstandshalter + container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray)); + + Container sub = new Container(new SpringGridLayout(Axis.X, Axis.Y)); + + createButton(sub, "Abbrechen", backAction, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0)); + + sub.addChild(new Panel(40 * app.getResolutionFactor(), 0 * app.getResolutionFactor(), ColorRGBA.Gray)); + + createButton(sub, "Starten", () -> tryStart(), new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0)); + + container.addChild(sub); + } + + void tryStart() { + if (null == portInput.getText()) { + portInput.setText(""); + return; + } + + int port = 0; + + try { + port = Integer.parseInt(portInput.getText()); + } catch (NumberFormatException e) { + portInput.setText(""); + return; + } + + if(port >= 1 && port <= 65535) { + app.getModelSyncronizer().setHost(port); + } + + portInput.setText(""); + } + + @Override + public void show() { + super.show(); + + container.setLocalTranslation( + app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2, + app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2, + 0 + ); + } + + @Override + public void hide() { + super.hide(); + } + + private void createTextField() { + Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y)); + + Label nameLabel = new Label("Port:\t"); + nameLabel.setFontSize(fontSize); + nameLabel.setColor(ColorRGBA.Black); + + portInput = new TextField(""); + + portInput.setColor(ColorRGBA.Black); + portInput.setTextHAlignment(HAlignment.Left); + portInput.setFontSize(fontSize); + portInput.setSingleLine(true); + + QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray); + portInput.setBackground(grayBackground); + + subContainer.addChild(nameLabel); + subContainer.addChild(portInput); + + container.addChild(subContainer); + } + + protected void createButton(Container c, String label, Runnable action, Vector3f size) { + Button button = new Button(label); + button.addClickCommands(source -> action.run()); + button.setFontSize(fontSize); + button.setHighlightColor(ColorRGBA.White); + button.setColor(ColorRGBA.Black); + button.setPreferredSize(size); + button.setTextHAlignment(HAlignment.Center); + button.setTextVAlignment(VAlignment.Center); + + QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT); + background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor()); + button.setBackground(background); + + button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> { + QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER); + hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor()); + source.setBackground(hoverBackground); + button.setHighlightColor(ColorRGBA.White); + button.setColor(ColorRGBA.Black); + }); + + button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> { + QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT); + normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor()); + source.setBackground(normalBackground); + button.setHighlightColor(ColorRGBA.White); + button.setColor(ColorRGBA.Black); + }); + + c.addChild(button); + } +} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/InterruptDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/InterruptDialog.java deleted file mode 100644 index 4fd87803..00000000 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/InterruptDialog.java +++ /dev/null @@ -1,4 +0,0 @@ -package pp.mdga.client.dialog; - -public class InterruptDialog { -} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/JoinDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/JoinDialog.java new file mode 100644 index 00000000..1e23cb24 --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/JoinDialog.java @@ -0,0 +1,192 @@ +package pp.mdga.client.dialog; + +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.jme3.texture.Texture; +import com.simsilica.lemur.*; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import com.simsilica.lemur.component.SpringGridLayout; +import pp.mdga.client.MdgaApp; + +import java.util.regex.Pattern; + +public class JoinDialog extends Dialog { + private TextField portInput; + private TextField ipInput; + + public JoinDialog(MdgaApp app, Node node, Runnable backAction) { + super(app, node); + + QuadBackgroundComponent quad1 = new QuadBackgroundComponent(ColorRGBA.Gray); + quad1.setMargin(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor()); + container.setBackground(quad1); + + Texture texture = app.getAssetManager().loadTexture("mdga_logo.png"); + + QuadBackgroundComponent b = new QuadBackgroundComponent(texture); + + Panel imagePanel = new Panel(); + imagePanel.setBackground(b); + + container.addChild(imagePanel).setPreferredSize(new Vector3f(texture.getImage().getWidth() / 4, texture.getImage().getHeight() / 4, 0)); + + //abstandshalter + container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray)); + + createIpField(); + + //abstandshalter + container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray)); + + createPortField(); + + //abstandshalter + container.addChild(new Panel(100 * app.getResolutionFactor(), 50 * app.getResolutionFactor(), ColorRGBA.Gray)); + + Container sub = new Container(new SpringGridLayout(Axis.X, Axis.Y)); + + createButton(sub, "Abbrechen", backAction, new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0)); + + sub.addChild(new Panel(40 * app.getResolutionFactor(), 0 * app.getResolutionFactor(), ColorRGBA.Gray)); + + createButton(sub, "Beitreten", () -> tryJoin(), new Vector3f(170 * app.getResolutionFactor(), 60 * app.getResolutionFactor(), 0)); + + container.addChild(sub); + } + + void tryJoin() { + if (null == portInput.getText()) { + portInput.setText(""); + ipInput.setText(""); + return; + } + + int port = 0; + + try { + port = Integer.parseInt(portInput.getText()); + } catch (NumberFormatException e) { + portInput.setText(""); + ipInput.setText(""); + return; + } + + if(!(port >= 1 && port <= 65535)) { + portInput.setText(""); + ipInput.setText(""); + return; + } + + if (null == ipInput.getText()) { + portInput.setText(""); + ipInput.setText(""); + return; + } + + String ipv4Pattern = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; + + if(!Pattern.matches(ipv4Pattern, ipInput.getText())) { + portInput.setText(""); + ipInput.setText(""); + return; + } + + app.getModelSyncronizer().setJoin(ipInput.getText(), port); + } + + @Override + public void show() { + super.show(); + + container.setLocalTranslation( + app.getCamera().getWidth() / 2 - container.getPreferredSize().x / 2, + app.getCamera().getHeight() / 2 + container.getPreferredSize().y / 2, + 0 + ); + } + + @Override + public void hide() { + super.hide(); + } + + private void createPortField() { + Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y)); + + Label nameLabel = new Label("Port:\t"); + nameLabel.setFontSize(fontSize); + nameLabel.setColor(ColorRGBA.Black); + + portInput = new TextField(""); + + portInput.setColor(ColorRGBA.Black); + portInput.setTextHAlignment(HAlignment.Left); + portInput.setFontSize(fontSize); + portInput.setSingleLine(true); + + QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray); + portInput.setBackground(grayBackground); + + subContainer.addChild(nameLabel); + subContainer.addChild(portInput); + + container.addChild(subContainer); + } + + private void createIpField() { + Container subContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y)); + + Label nameLabel = new Label("Ip:\t"); + nameLabel.setFontSize(fontSize); + nameLabel.setColor(ColorRGBA.Black); + + ipInput = new TextField(""); + + ipInput.setColor(ColorRGBA.Black); + ipInput.setTextHAlignment(HAlignment.Left); + ipInput.setFontSize(fontSize); + ipInput.setSingleLine(true); + + QuadBackgroundComponent grayBackground = new QuadBackgroundComponent(ColorRGBA.DarkGray); + ipInput.setBackground(grayBackground); + + subContainer.addChild(nameLabel); + subContainer.addChild(ipInput); + + container.addChild(subContainer); + } + + protected void createButton(Container c, String label, Runnable action, Vector3f size) { + Button button = new Button(label); + button.addClickCommands(source -> action.run()); + button.setFontSize(fontSize); + button.setHighlightColor(ColorRGBA.White); + button.setColor(ColorRGBA.Black); + button.setPreferredSize(size); + button.setTextHAlignment(HAlignment.Center); + button.setTextVAlignment(VAlignment.Center); + + QuadBackgroundComponent background = new QuadBackgroundComponent(COLOR_DEFAULT); + background.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor()); + button.setBackground(background); + + button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOn, (source) -> { + QuadBackgroundComponent hoverBackground = new QuadBackgroundComponent(COLOR_HOVER); + hoverBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor()); + source.setBackground(hoverBackground); + button.setHighlightColor(ColorRGBA.White); + button.setColor(ColorRGBA.Black); + }); + + button.addCommands(com.simsilica.lemur.Button.ButtonAction.HighlightOff, (source) -> { + QuadBackgroundComponent normalBackground = new QuadBackgroundComponent(COLOR_DEFAULT); + normalBackground.setMargin(5 * app.getResolutionFactor(), 5 * app.getResolutionFactor()); + source.setBackground(normalBackground); + button.setHighlightColor(ColorRGBA.White); + button.setColor(ColorRGBA.Black); + }); + + c.addChild(button); + } +} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/LobbyButtonDialog.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/LobbyButtonDialog.java new file mode 100644 index 00000000..5c1195d9 --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/dialog/LobbyButtonDialog.java @@ -0,0 +1,165 @@ +package pp.mdga.client.dialog; + +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Command; +import com.simsilica.lemur.HAlignment; +import com.simsilica.lemur.VAlignment; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import pp.mdga.client.MdgaApp; +import pp.mdga.game.Color; + +public class LobbyButtonDialog extends Dialog { + private final int pos; + + private Button button; + + private Command