diff --git a/Projekte/.run/MdgaApp.run.xml b/Projekte/.run/MdgaApp.run.xml
index 0ed782b0..82e2f2b6 100644
--- a/Projekte/.run/MdgaApp.run.xml
+++ b/Projekte/.run/MdgaApp.run.xml
@@ -6,7 +6,7 @@
-
+
@@ -14,4 +14,4 @@
-
+
\ No newline at end of file
diff --git a/Projekte/mdga/client/build.gradle b/Projekte/mdga/client/build.gradle
index 06605dff..ab3c4212 100644
--- a/Projekte/mdga/client/build.gradle
+++ b/Projekte/mdga/client/build.gradle
@@ -9,6 +9,12 @@ implementation project(":jme-common")
implementation project(":mdga:model")
implementation libs.jme3.desktop
+ implementation libs.jme3.core
+ implementation libs.jme3.lwjgl3
+ implementation libs.jme3.lwjgl
+ implementation libs.jme3.desktop
+ implementation libs.jme3.effects
+
runtimeOnly libs.jme3.awt.dialogs
runtimeOnly libs.jme3.plugins
diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardAsset.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java
similarity index 58%
rename from Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardAsset.java
rename to Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java
index cc52bee7..7dd38eef 100644
--- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardAsset.java
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java
@@ -1,6 +1,6 @@
-package pp.mdga.client.board;
+package pp.mdga.client;
-enum BoardAsset {
+public enum Asset {
bigTent,
cardStack,
cir,
@@ -9,45 +9,60 @@ enum BoardAsset {
lw,
marine,
node_home_blue("./node_home/node_home.j3o", "./node_home/node_home_blue_diff.png"),
+ node_wait_blue("./node_home/node_home.j3o", "./node_home/node_home_blue_diff.png"),
node_home_black("./node_home/node_home.j3o", "./node_home/node_home_black_diff.png"),
+ node_wait_black("./node_home/node_home.j3o", "./node_home/node_home_black_diff.png"),
node_home_green("./node_home/node_home.j3o", "./node_home/node_home_green_diff.png"),
- node_home_yellow("./node_home/node_home.j3o", "./node_home/node_home_yellow_diff.png"),
+ node_wait_green("./node_home/node_home.j3o", "./node_home/node_home_green_diff.png"),
+ node_home_yellow("./node_home/node_home.j3o", "./node_home/node_home_orange_diff.png"),
+ node_wait_yellow("./node_home/node_home.j3o", "./node_home/node_home_orange_diff.png"),
node_normal,
node_start("./node_normal/node_normal.j3o", "./node_normal/node_start_diff.png"),
node_bonus("./node_normal/node_normal.j3o", "./node_normal/node_bonus_diff.png"),
radar,
- shieldCard,
- ship,
+ ship(0.8f),
smallTent,
- swapCard,
tank,
+// world(1.2f),
+ world("./world_new/world_export_new.obj", "./world_new/world_new_diff.png", 1.2f),
+ shield_ring("./shield_ring/shield_ring.obj", null),
+ tree_small("./tree_small/tree_small.obj", "./tree_small/tree_small_diff.png"),
+ tree_big("./tree_big/tree_big.obj", "./tree_big/tree_big_diff.png"),
turboCard,
- world(1.2f);
+ swapCard,
+ shieldCard
+ ;
private final String modelPath;
private final String diffPath;
private final float size;
- BoardAsset() {
+ Asset() {
String folderFileName = "./" + name() + "/" + name();
this.modelPath = folderFileName + ".j3o";
this.diffPath = folderFileName + "_diff.png";
this.size = 1f;
}
- BoardAsset(String modelPath, String diffPath) {
+ Asset(String modelPath, String diffPath) {
this.modelPath = modelPath;
this.diffPath = diffPath;
this.size = 1f;
}
- BoardAsset(float size) {
+ Asset(float size) {
String folderFileName = "./" + name() + "/" + name();
this.modelPath = folderFileName + ".j3o";
this.diffPath = folderFileName + "_diff.png";
this.size = size;
}
+ Asset(String modelPath, String diffPath, float size){
+ this.modelPath = modelPath;
+ this.diffPath = diffPath;
+ this.size = size;
+ }
+
public String getModelPath() {
return modelPath;
}
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 e58c5bc3..2f2e1638 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,29 +1,36 @@
package pp.mdga.client;
import com.jme3.app.SimpleApplication;
-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.BoardView;
-import pp.mdga.client.dialog.DialogView;
+import pp.mdga.client.view.*;
public class MdgaApp extends SimpleApplication {
private AnimationHandler animationHandler;
private AcousticHandler acousticHandler;
- private BoardView boardView;
- 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 = 1f;
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);
MdgaApp app = new MdgaApp();
app.setSettings(settings);
@@ -35,37 +42,55 @@ public static void main(String[] args) {
public void simpleInitApp() {
animationHandler = new AnimationHandler(this);
acousticHandler = new AcousticHandler(this);
- boardView = new BoardView(this);
- dialogView = new DialogView(this);
+ notificationSynchronizer = new NotificationSynchronizer(this);
+ inputSyncronizer = new InputSyncronizer(this);
+ modelSyncronizer = new ModelSyncronizer(this);
- //dialogView.mainMenu();
- //acousticHandler.playState(MdgaState.GAME);
+ inputManager.deleteMapping("SIMPLEAPP_Exit");
+ GuiGlobals.initialize(this);
- acousticHandler.playSound(MdgaSound.LOST);
- acousticHandler.playSound(MdgaSound.VICTORY);
+ 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() {
@@ -76,11 +101,17 @@ public AcousticHandler getAcousticHandler() {
return acousticHandler;
}
- public BoardView getBoardView() {
- return boardView;
+ 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..fae79021
--- /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(String name) {
+ //TODO call from somewhere
+ System.out.println("setName:" + name);
+ }
+
+ 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/AssetOnMap.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/AssetOnMap.java
index 7984ad77..d78d48fd 100644
--- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/AssetOnMap.java
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/AssetOnMap.java
@@ -1,3 +1,5 @@
package pp.mdga.client.board;
-record AssetOnMap(BoardAsset asset, int x, int y, float rot) {}
+import pp.mdga.client.Asset;
+
+record AssetOnMap(Asset asset, int x, int y, float rot) {}
diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java
new file mode 100644
index 00000000..acfd4090
--- /dev/null
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java
@@ -0,0 +1,323 @@
+package pp.mdga.client.board;
+
+import com.jme3.material.Material;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.AbstractControl;
+import pp.mdga.client.Asset;
+import pp.mdga.client.MdgaApp;
+import pp.mdga.game.Color;
+
+import java.util.*;
+
+public class BoardHandler {
+ private static final float GRID_SIZE = 1.72f;
+ private static final float GRID_ELEVATION = 0.0f;
+ private static final String MAP_NAME = "map.mdga";
+
+ private final MdgaApp app;
+
+ private PileControl drawPile = null;
+ private PileControl discardPile = null;
+
+ private ArrayList infield;
+ private Map pieces;
+
+ private Map> colorAssetsMap;
+ private Map> homeNodesMap;
+ private Map> waitingNodesMap;
+ private Map> waitingPiecesMap;
+ private Map pieceColor;
+
+ private Node node;
+
+ private FilterPostProcessor fpp;
+
+ private boolean init;
+
+ public BoardHandler(MdgaApp app, FilterPostProcessor fpp) {
+ if(app == null) throw new RuntimeException("app is null");
+
+ this.init = false;
+ this.app = app;
+ this.fpp = fpp;
+ }
+
+ private void addFigureToPlayerMap(Color col, AssetOnMap assetOnMap) {
+ List inMap = addItemToMapList(colorAssetsMap, col, assetOnMap);
+ if (inMap.size() > 4) throw new RuntimeException("to many assets for " + col);
+ }
+
+ private void initMap() {
+ if (init) return;
+
+ this.init = true;
+ this.node = new Node("Asset Node");
+ app.getRootNode().attachChild(node);
+
+ this.pieces = new HashMap<>();
+ this.colorAssetsMap = new HashMap<>();
+ this.infield = new ArrayList<>(40);
+ this.homeNodesMap = new HashMap<>();
+ this.waitingNodesMap = new HashMap<>();
+ this.waitingPiecesMap = new HashMap<>();
+ this.pieceColor = new HashMap<>();
+
+ List assetOnMaps = MapLoader.loadMap(MAP_NAME);
+
+ for (AssetOnMap assetOnMap : assetOnMaps) {
+ switch (assetOnMap.asset()) {
+ case lw -> addFigureToPlayerMap(assetToColor(Asset.lw), assetOnMap);
+ case heer -> addFigureToPlayerMap(assetToColor(Asset.heer), assetOnMap);
+ case cir -> addFigureToPlayerMap(assetToColor(Asset.cir), assetOnMap);
+ case marine -> addFigureToPlayerMap(assetToColor(Asset.marine), assetOnMap);
+ case node_normal, node_bonus, node_start ->
+ infield.add(displayAndControl(assetOnMap, new NodeControl()));
+ case node_home_black -> addHomeNode(homeNodesMap, Color.AIRFORCE, assetOnMap);
+ case node_home_blue -> addHomeNode(homeNodesMap, Color.NAVY, assetOnMap);
+ case node_home_green -> addHomeNode(homeNodesMap, Color.ARMY, assetOnMap);
+ case node_home_yellow -> addHomeNode(homeNodesMap, Color.CYBER, assetOnMap);
+ case node_wait_black -> addHomeNode(waitingNodesMap, Color.AIRFORCE, assetOnMap);
+ 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);
+
+ default -> displayAsset(assetOnMap);
+ }
+ }
+ }
+
+ private Color assetToColor(Asset asset) {
+ return switch (asset) {
+ case lw -> Color.AIRFORCE;
+ case heer -> Color.ARMY;
+ case marine -> Color.NAVY;
+ case cir -> Color.CYBER;
+ default -> throw new RuntimeException("invalid asset");
+ };
+ }
+
+ private Spatial createModel(Asset asset, Vector3f pos, float rot) {
+ String modelName = asset.getModelPath();
+ String texName = asset.getDiffPath();
+ Spatial model = app.getAssetManager().loadModel(modelName);
+ model.scale(asset.getSize());
+ 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));
+ model.setMaterial(mat);
+ node.attachChild(model);
+// app.getRootNode().attachChild(model);
+ return model;
+ }
+
+ private static Vector3f gridToWorld(int x, int y) {
+ return new Vector3f(GRID_SIZE * x, GRID_SIZE * y, GRID_ELEVATION);
+ }
+
+ private Spatial displayAsset(AssetOnMap assetOnMap) {
+ int x = assetOnMap.x();
+ int y = assetOnMap.y();
+ return createModel(assetOnMap.asset(), gridToWorld(x, y), assetOnMap.rot());
+ }
+
+ private T displayAndControl(AssetOnMap assetOnMap, T control) {
+ Spatial spatial = displayAsset(assetOnMap);
+ spatial.addControl(control);
+ return control;
+ }
+
+ private void movePieceToNode(PieceControl pieceControl, NodeControl nodeControl){
+ pieceControl.setLocation(nodeControl.getLocation());
+ }
+
+ private void addHomeNode(Map> map, Color color, AssetOnMap assetOnMap){
+ List homeNodes = addItemToMapList(map, color, displayAndControl(assetOnMap, new NodeControl()));
+ if (homeNodes.size() > 4) throw new RuntimeException("too many homeNodes for " + color);
+ }
+
+ private float getRotationMove(Vector3f prev, Vector3f next) {
+ Vector3f direction = next.subtract(prev).normalizeLocal();
+ //I had to reverse dir.y, because then it worked.
+ return (float) Math.toDegrees(Math.atan2(direction.x, -direction.y));
+ }
+
+ private void movePiece_rek(UUID uuid, int curIndex, int moveIndex){
+ if (curIndex == moveIndex) return;
+
+ curIndex = (curIndex + 1) % 40;
+
+ PieceControl pieceControl = pieces.get(uuid);
+ NodeControl nodeControl = infield.get(curIndex);
+
+ pieceControl.setRotation(getRotationMove(pieceControl.getLocation(),nodeControl.getLocation()));
+
+ movePieceToNode(pieceControl, nodeControl);
+
+ movePiece_rek(uuid, curIndex, moveIndex);
+ }
+
+ private List addItemToMapList(Map> map, E key, T item){
+ List list = map.getOrDefault(key, new ArrayList<>());
+ list.add(item);
+ map.put(key, list);
+ return list;
+ }
+
+ private List removeItemFromMapList(Map> map, E key, T item){
+ List list = map.getOrDefault(key, new ArrayList<>());
+ list.remove(item);
+ map.put(key, list);
+ return list;
+ }
+
+ //public methods****************************************************************************************************
+ public void addPlayer(Color color, List uuid) {
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ List playerAssets = colorAssetsMap.get(color);
+ if (playerAssets == null) throw new RuntimeException("Assets for Player color are not defined");
+ if (uuid.size() != playerAssets.size()) throw new RuntimeException("UUID array and playerAssets are not the same size");
+
+ List waitNodes = waitingNodesMap.get(color);
+ if (waitNodes.size() != playerAssets.size()) throw new RuntimeException("waitNodes size does not match playerAssets size");
+
+
+ for (int i = 0; i < playerAssets.size(); i++){
+ AssetOnMap assetOnMap = playerAssets.get(i);
+ PieceControl pieceControl = displayAndControl(assetOnMap, new PieceControl(assetOnMap.rot(), app.getAssetManager(), app, fpp));
+ movePieceToNode(pieceControl, waitNodes.get(i));
+
+ pieces.put(uuid.get(i), pieceControl);
+
+ pieceColor.put(uuid.get(i), color);
+
+ addItemToMapList(waitingPiecesMap, color, pieceControl);
+ }
+ }
+
+ public void moveHomePiece(UUID uuid, int index){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ Color color = pieceColor.get(uuid);
+ if(color == null) throw new RuntimeException("uuid is not mapped to a color");
+
+ List homeNodes = homeNodesMap.get(color);
+
+ if(homeNodesMap.size() != 4) throw new RuntimeException("HomeNodes for" + color + " are not properly defined");
+
+ PieceControl pieceControl = pieces.get(uuid);
+ NodeControl nodeControl = homeNodes.get(index);
+ movePieceToNode(pieceControl, nodeControl);
+
+ //rotate piece in direction of homeNodes
+ NodeControl firstHomeNode = homeNodes.get(0);
+ NodeControl lastHomeNode = homeNodes.get(homeNodes.size()-1);
+
+ pieceControl.setRotation(getRotationMove(firstHomeNode.getLocation(), lastHomeNode.getLocation()));
+ }
+
+ public void movePieceStart(UUID uuid, int nodeIndex){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ Color color = pieceColor.get(uuid);
+ if(color == null) throw new RuntimeException("uuid is not mapped to a color");
+
+ PieceControl pieceControl = pieces.get(uuid);
+ movePieceToNode(pieceControl, infield.get(nodeIndex));
+
+ removeItemFromMapList(waitingPiecesMap, color, pieceControl);
+ }
+
+ public void movePiece(UUID uuid, int curIndex, int moveIndex){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ movePiece_rek(uuid, curIndex, moveIndex);
+ }
+
+ public void throwPiece(UUID uuid){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ Color color = pieceColor.get(uuid);
+ if(color == null) throw new RuntimeException("uuid is not mapped to a color");
+
+
+ PieceControl pieceControl = pieces.get(uuid);
+ List waitNodes = waitingNodesMap.get(color);
+ List waitPieces = waitingPiecesMap.get(color);
+
+ movePieceToNode(pieceControl, waitNodes.get(waitPieces.size()));
+ pieceControl.rotateInit();
+ }
+
+ public void shieldPiece(UUID uuid){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ pieces.get(uuid).activateShield();
+ }
+
+ public void unshieldPiece(UUID uuid){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ pieces.get(uuid).deactivateShield();
+ }
+
+ public void suppressShield(UUID uuid){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ pieces.get(uuid).suppressShield();
+ }
+
+ public void swapPieces(UUID piece1, UUID piece2){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ PieceControl piece1_control = pieces.get(piece1);
+ PieceControl piece2_control = pieces.get(piece2);
+
+ if(piece1_control == null) throw new RuntimeException("piece1 UUID is not valid");
+ if(piece2_control == null) throw new RuntimeException("piece2 UUID is not valid");
+
+ float rot1 = piece1_control.getRotation();
+ float rot2 = piece2_control.getRotation();
+
+ piece1_control.setRotation(rot2);
+ piece2_control.setRotation(rot1);
+
+ Vector3f pos1 = piece1_control.getLocation().clone();
+ Vector3f pos2 = piece2_control.getLocation().clone();
+
+ piece1_control.setLocation(pos2);
+ piece2_control.setLocation(pos1);
+ }
+
+ public void init(){
+ initMap();
+ }
+
+ public void shutdown(){
+ if (!init) return;
+
+ init = false;
+ app.getRootNode().detachChild(node);
+ }
+
+
+ //List
+ //List
+ public void highlight(UUID uuid, boolean bool){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ pieces.get(uuid).outline(bool);
+ }
+
+ public void unHighlight(UUID uuid){
+ if (!init) throw new RuntimeException("BoardHandler is not initialized");
+
+ pieces.get(uuid).deOutline();
+ }
+}
diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardView.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardView.java
deleted file mode 100644
index ee2a6c02..00000000
--- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardView.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package pp.mdga.client.board;
-
-import com.jme3.light.AmbientLight;
-import com.jme3.light.DirectionalLight;
-import com.jme3.material.Material;
-import com.jme3.math.ColorRGBA;
-import com.jme3.math.Vector3f;
-import com.jme3.renderer.queue.RenderQueue;
-import com.jme3.scene.Spatial;
-import com.jme3.scene.control.AbstractControl;
-import com.jme3.shadow.DirectionalLightShadowRenderer;
-import pp.mdga.client.MdgaApp;
-import pp.mdga.game.Color;
-
-import java.util.*;
-
-public class BoardView {
- private static final float GRID_SIZE = 1.72f;
- private static final float GRID_ELEVATION = 0.0f;
- private static final String MAP_NAME = "map.mdga";
-
- private final MdgaApp app;
-
- private PileControl drawPile = null;
- private PileControl discardPile = null;
-
- private ArrayList infield = new ArrayList<>(40);
- private Map pieces;
-
- private Map> playerMap;
-
- public BoardView(MdgaApp app) {
- assert (app != null) : "app is null";
-
- this.app = app;
-
- this.pieces = new HashMap<>();
- this.playerMap = new HashMap<>();
-
- initMap();
- initCamera();
- }
-
- private void addFigureToPlayerMap(Color col, AssetOnMap assetOnMap) {
- List inMap = playerMap.getOrDefault(col, new ArrayList<>());
- inMap.add(assetOnMap);
-
- assert (inMap.size() <= 4) : "to many assets for one player";
-
- playerMap.put(col, inMap);
- }
-
- private void initMap() {
- List assetOnMaps = MapLoader.loadMap(MAP_NAME);
-
- for (AssetOnMap assetOnMap : assetOnMaps) {
- switch (assetOnMap.asset()) {
- case lw -> addFigureToPlayerMap(assetToColor(BoardAsset.lw), assetOnMap);
- case heer -> addFigureToPlayerMap(assetToColor(BoardAsset.heer), assetOnMap);
- case cir -> addFigureToPlayerMap(assetToColor(BoardAsset.cir), assetOnMap);
- case marine -> addFigureToPlayerMap(assetToColor(BoardAsset.marine), assetOnMap);
- case node_normal, node_bonus, node_start ->
- infield.add(displayAndControl(assetOnMap, new NodeControl()));
- default -> displayAsset(assetOnMap);
- }
- }
- }
-
- private void initCamera() {
- app.getFlyByCamera().setEnabled(true);
- int zoom = 20;
- 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.setColor(ColorRGBA.White);
- sun.setDirection(new Vector3f(0.3f, 0, -1));
- app.getRootNode().addLight(sun);
-
- AmbientLight ambient = new AmbientLight();
- ambient.setColor(new ColorRGBA(0.3f, 0.3f, 0.3f, 1));
- app.getRootNode().addLight(ambient);
-
- int SHADOWMAP_SIZE = 1024 * 8;
- DirectionalLightShadowRenderer dlsr = new DirectionalLightShadowRenderer(app.getAssetManager(), SHADOWMAP_SIZE, 4);
- dlsr.setLight(sun);
- app.getViewPort().addProcessor(dlsr);
- }
-
- private Color assetToColor(BoardAsset asset) {
- return switch (asset) {
- case lw -> Color.AIRFORCE;
- case heer -> Color.ARMY;
- case marine -> Color.NAVY;
- case cir -> Color.CYBER;
- default -> throw new RuntimeException("invalid asset");
- };
- }
-
- private Spatial createModel(BoardAsset asset, Vector3f pos, float rot) {
- String modelName = asset.getModelPath();
- String texName = asset.getDiffPath();
- Spatial model = app.getAssetManager().loadModel(modelName);
- model.scale(asset.getSize());
- model.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(90 + 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));
- model.setMaterial(mat);
- app.getRootNode().attachChild(model);
- return model;
- }
-
- private static Vector3f gridToWorld(int x, int y) {
- return new Vector3f(GRID_SIZE * x, GRID_SIZE * y, GRID_ELEVATION);
- }
-
- public void addPlayer(Color color, UUID uuid) {
- List playerAssets = playerMap.get(color);
- assert (playerAssets != null) : "Assets for Player color are not defined";
-
- for (AssetOnMap assetOnMap : playerAssets) {
- pieces.put(uuid, displayAndControl(assetOnMap, new PieceControl()));
- }
- }
-
- //displays an assets and return the created asset
- private Spatial displayAsset(AssetOnMap assetOnMap) {
- int x = assetOnMap.x();
- int y = assetOnMap.y();
- return createModel(assetOnMap.asset(), gridToWorld(x, y), assetOnMap.rot());
- }
-
- private T displayAndControl(AssetOnMap assetOnMap, T control) {
- Spatial spatial = displayAsset(assetOnMap);
- spatial.addControl(control);
- return control;
- }
-}
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
new file mode 100644
index 00000000..ba4ab0d6
--- /dev/null
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/CameraHandler.java
@@ -0,0 +1,48 @@
+package pp.mdga.client.board;
+
+import com.jme3.light.AmbientLight;
+import com.jme3.light.DirectionalLight;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.shadow.DirectionalLightShadowFilter;
+import pp.mdga.client.MdgaApp;
+
+public class CameraHandler {
+ MdgaApp app;
+
+ private DirectionalLight sun;
+ private AmbientLight ambient;
+
+ private static final int SHADOWMAP_SIZE = 1024 * 8;
+
+ public CameraHandler(MdgaApp app, FilterPostProcessor fpp){
+ this.app = app;
+
+ sun = new DirectionalLight();
+ sun.setColor(ColorRGBA.White);
+ sun.setDirection(new Vector3f(0.3f, 0, -1));
+
+ ambient = new AmbientLight();
+ ambient.setColor(new ColorRGBA(0.3f, 0.3f, 0.3f, 1));
+
+ DirectionalLightShadowFilter dlsf = new DirectionalLightShadowFilter(app.getAssetManager(), SHADOWMAP_SIZE, 4);
+ dlsf.setLight(sun);
+ fpp.addFilter(dlsf);
+ }
+
+ public void init() {
+ app.getFlyByCamera().setEnabled(true);
+ int zoom = 20;
+ 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/board/MapLoader.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java
index 2e176d83..8f57623f 100644
--- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/MapLoader.java
@@ -1,5 +1,8 @@
package pp.mdga.client.board;
+
+import pp.mdga.client.Asset;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -43,7 +46,7 @@ public static List loadMap(String mapName) {
float rot = Float.parseFloat(parts[2]);
- BoardAsset asset = getLoadedAsset(assetName);
+ Asset asset = getLoadedAsset(assetName);
assetsOnMap.add(new AssetOnMap(asset, x, y, rot));
}
}
@@ -57,26 +60,32 @@ public static List loadMap(String mapName) {
return assetsOnMap;
}
- private static BoardAsset getLoadedAsset(String assetName) {
+ private static Asset getLoadedAsset(String assetName) {
return switch (assetName) {
- case "lw" -> BoardAsset.lw;
- case "cir" -> BoardAsset.cir;
- case "marine" -> BoardAsset.marine;
- case "heer" -> BoardAsset.heer;
- case "node" -> BoardAsset.node_normal;
- case "node_start" -> BoardAsset.node_start;
- case "node_bonus" -> BoardAsset.node_bonus;
- case "node_home_blue" -> BoardAsset.node_home_blue;
- case "node_home_yellow" -> BoardAsset.node_home_yellow;
- case "node_home_black" -> BoardAsset.node_home_black;
- case "node_home_green" -> BoardAsset.node_home_green;
- case "world" -> BoardAsset.world;
- case "jet" -> BoardAsset.jet;
- case "big_tent" -> BoardAsset.bigTent;
- case "small_tent" -> BoardAsset.smallTent;
- case "radar" -> BoardAsset.radar;
- case "ship" -> BoardAsset.ship;
- case "tank" -> BoardAsset.tank;
+ case "lw" -> Asset.lw;
+ case "cir" -> Asset.cir;
+ case "marine" -> Asset.marine;
+ case "heer" -> Asset.heer;
+ case "node" -> Asset.node_normal;
+ case "node_start" -> Asset.node_start;
+ case "node_bonus" -> Asset.node_bonus;
+ case "node_home_blue" -> Asset.node_home_blue;
+ case "node_home_yellow" -> Asset.node_home_yellow;
+ case "node_home_black" -> Asset.node_home_black;
+ case "node_home_green" -> Asset.node_home_green;
+ case "node_wait_blue" -> Asset.node_wait_blue;
+ case "node_wait_yellow" -> Asset.node_wait_yellow;
+ case "node_wait_black" -> Asset.node_wait_black;
+ case "node_wait_green" -> Asset.node_wait_green;
+ case "world" -> Asset.world;
+ case "jet" -> Asset.jet;
+ case "big_tent" -> Asset.bigTent;
+ case "small_tent" -> Asset.smallTent;
+ case "radar" -> Asset.radar;
+ case "ship" -> Asset.ship;
+ case "tank" -> Asset.tank;
+ case "tree_small" -> Asset.tree_small;
+ case "tree_big" -> Asset.tree_big;
default -> throw new IllegalStateException("Unexpected value: " + assetName);
};
}
diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/NodeControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/NodeControl.java
index f636a9f7..62739017 100644
--- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/NodeControl.java
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/NodeControl.java
@@ -1,5 +1,6 @@
package pp.mdga.client.board;
+import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
@@ -8,11 +9,14 @@ public class NodeControl extends AbstractControl {
@Override
protected void controlUpdate(float v) {
-
}
@Override
protected void controlRender(RenderManager renderManager, ViewPort viewPort) {
}
+
+ public Vector3f getLocation(){
+ return this.getSpatial().getLocalTranslation();
+ }
}
diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Outline/OutlineFilter.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Outline/OutlineFilter.java
new file mode 100644
index 00000000..0a255fe3
--- /dev/null
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Outline/OutlineFilter.java
@@ -0,0 +1,79 @@
+package pp.mdga.client.board.Outline;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.material.MaterialDef;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector2f;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.texture.FrameBuffer;
+
+
+public class OutlineFilter extends Filter {
+
+ private OutlinePreFilter outlinePreFilter;
+ private ColorRGBA outlineColor = new ColorRGBA(0, 1, 0, 1);
+ private float outlineWidth = 1;
+
+ public OutlineFilter(OutlinePreFilter outlinePreFilter) {
+ super("OutlineFilter");
+ this.outlinePreFilter = outlinePreFilter;
+ }
+
+ @Override
+ protected void initFilter(AssetManager assetManager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ MaterialDef matDef = (MaterialDef) assetManager.loadAsset("MatDefs/SelectObjectOutliner/Outline.j3md");
+ material = new Material(matDef);
+ material.setVector2("Resolution", new Vector2f(w, h));
+ material.setColor("OutlineColor", outlineColor);
+ material.setFloat("OutlineWidth", outlineWidth);
+ }
+
+ @Override
+ protected void preFrame(float tpf) {
+ super.preFrame(tpf);
+ material.setTexture("OutlineDepthTexture", outlinePreFilter.getOutlineTexture());
+// System.out.println("OutlineFilter.preFrame()");
+ }
+
+ @Override
+ protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
+ super.postFrame(renderManager, viewPort, prevFilterBuffer, sceneBuffer);
+// material.setTexture("OutlineDepthTexture", outlinePreFilter.getDefaultPassDepthTexture());
+// System.out.println("OutlineFilter.postFrame()");
+ }
+
+ @Override
+ protected Material getMaterial() {
+ return material;
+ }
+
+ public ColorRGBA getOutlineColor() {
+ return outlineColor;
+ }
+
+ public void setOutlineColor(ColorRGBA outlineColor) {
+ this.outlineColor = outlineColor;
+ if (material != null) {
+ material.setColor("OutlineColor", outlineColor);
+ }
+ }
+
+ public float getOutlineWidth() {
+ return outlineWidth;
+ }
+
+ public void setOutlineWidth(float outlineWidth) {
+ this.outlineWidth = outlineWidth;
+ if (material != null) {
+ material.setFloat("OutlineWidth", outlineWidth);
+ }
+ }
+
+ public OutlinePreFilter getOutlinePreFilter() {
+ return outlinePreFilter;
+ }
+
+}
diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Outline/OutlinePreFilter.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Outline/OutlinePreFilter.java
new file mode 100644
index 00000000..8406b82f
--- /dev/null
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Outline/OutlinePreFilter.java
@@ -0,0 +1,67 @@
+package pp.mdga.client.board.Outline;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.post.Filter;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.Renderer;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image.Format;
+import com.jme3.texture.Texture;
+
+
+public class OutlinePreFilter extends Filter {
+
+ private Pass normalPass;
+ private RenderManager renderManager;
+
+ /**
+ * Creates a OutlinePreFilter
+ */
+ public OutlinePreFilter() {
+ super("OutlinePreFilter");
+ }
+
+ @Override
+ protected boolean isRequiresDepthTexture() {
+ return true;
+ }
+
+ @Override
+ protected void postQueue(RenderQueue queue) {
+ Renderer r = renderManager.getRenderer();
+ r.setFrameBuffer(normalPass.getRenderFrameBuffer());
+ renderManager.getRenderer().clearBuffers(true, true, false);
+ }
+
+ @Override
+ protected void postFrame(RenderManager renderManager, ViewPort viewPort, FrameBuffer prevFilterBuffer, FrameBuffer sceneBuffer) {
+ super.postFrame(renderManager, viewPort, prevFilterBuffer, sceneBuffer);
+
+ }
+
+ @Override
+ protected Material getMaterial() {
+ return material;
+ }
+
+ public Texture getOutlineTexture() {
+ return normalPass.getRenderedTexture();
+ }
+
+ @Override
+ protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
+ this.renderManager = renderManager;
+ normalPass = new Pass();
+ normalPass.init(renderManager.getRenderer(), w, h, Format.RGBA8, Format.Depth);
+ material = new Material(manager, "MatDefs/SelectObjectOutliner/OutlinePre.j3md");
+ }
+
+ @Override
+ protected void cleanUpFilter(Renderer r) {
+ normalPass.cleanup(r);
+ }
+
+}
diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Outline/SelectObjectOutliner.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Outline/SelectObjectOutliner.java
new file mode 100644
index 00000000..e54a97c8
--- /dev/null
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Outline/SelectObjectOutliner.java
@@ -0,0 +1,77 @@
+package pp.mdga.client.board.Outline;
+
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState;
+import com.jme3.material.RenderState.BlendMode;
+import com.jme3.math.ColorRGBA;
+import com.jme3.post.Filter;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import pp.mdga.game.Color;
+
+public class SelectObjectOutliner {
+
+ private final FilterPostProcessor fpp;
+ private final RenderManager renderManager;
+ private final AssetManager assetManager;
+ private final Camera cam;
+ private final int width;
+ private boolean selected;
+ private ViewPort outlineViewport = null;
+ private OutlineFilter outlineFilter = null;
+
+
+ public SelectObjectOutliner(int width, FilterPostProcessor fpp, RenderManager renderManager, AssetManager assetManager, Camera cam) {
+ this.selected = false;
+ this.fpp = fpp;
+ this.renderManager = renderManager;
+ this.assetManager = assetManager;
+ this.cam = cam;
+ this.width = width;
+ }
+
+ public void deselect(Spatial model) {
+ if(selected){
+ selected = false;
+ hideOutlineFilterEffect(model);
+ }
+ }
+
+ public void select(Spatial model, ColorRGBA color) {
+ if(!selected){
+ selected = true;
+ showOutlineFilterEffect(model, width, color);
+ }
+ }
+
+ private void hideOutlineFilterEffect(Spatial model) {
+ outlineFilter.setEnabled(false);
+ outlineFilter.getOutlinePreFilter().setEnabled(false);
+ fpp.removeFilter(outlineFilter);
+ outlineViewport.detachScene(model);
+ renderManager.removePreView(outlineViewport);
+ outlineViewport = null;
+ }
+
+ private void showOutlineFilterEffect(Spatial model, int width, ColorRGBA color) {
+ outlineViewport = renderManager.createPreView("outlineViewport", cam);
+ FilterPostProcessor outlineFpp = new FilterPostProcessor(assetManager);
+
+ OutlinePreFilter outlinePreFilter = new OutlinePreFilter();
+ outlineFpp.addFilter(outlinePreFilter);
+
+ outlineViewport.attachScene(model);
+ outlineViewport.addProcessor(outlineFpp);
+
+ outlineFilter = new OutlineFilter(outlinePreFilter);
+ outlineFilter.setOutlineColor(color);
+ outlineFilter.setOutlineWidth(width);
+
+ fpp.addFilter(outlineFilter);
+ }
+}
diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PieceControl.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PieceControl.java
index 264f8439..112268a1 100644
--- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PieceControl.java
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/PieceControl.java
@@ -1,18 +1,150 @@
package pp.mdga.client.board;
+import com.jme3.asset.AssetManager;
+import com.jme3.material.Material;
+import com.jme3.material.RenderState;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
+import pp.mdga.client.Asset;
+import pp.mdga.client.MdgaApp;
+import pp.mdga.client.board.Outline.SelectObjectOutliner;
public class PieceControl extends AbstractControl {
+ private final float initRotation;
+ private final AssetManager assetManager;
+ private Spatial shieldRing;
+ private final Material shieldMat;
+
+ private static final float SHIELD_SPEED = 1f;
+ private static final float SHIELD_TRANSPARENCY = 0.6f;
+ private static final ColorRGBA SHIELD_COLOR = new ColorRGBA(0, 0.9f, 1, SHIELD_TRANSPARENCY);
+ private static final ColorRGBA SHIELD_SUPPRESSED_COLOR = new ColorRGBA(1f, 0.5f, 0, SHIELD_TRANSPARENCY);
+ private static final float SHIELD_Z = 0f;
+
+ SelectObjectOutliner outlineOwn;
+
+ private static final ColorRGBA OUTLINE_OWN_COLOR = ColorRGBA.White;
+ private static final ColorRGBA OUTLINE_ENEMY_COLOR = ColorRGBA.Red;
+ private static final int OUTLINE_THICKNESS = 4;
+ private final Node parentNode;
+
+
+ public PieceControl(float initRotation, AssetManager assetManager, MdgaApp app, FilterPostProcessor fpp){
+ super();
+ this.parentNode = new Node();
+ this.initRotation = initRotation;
+ this.assetManager = assetManager;
+ this.shieldRing = null;
+ this.shieldMat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ this.shieldMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
+
+
+ outlineOwn = new SelectObjectOutliner(OUTLINE_THICKNESS, fpp, app.getRenderManager(), app.getAssetManager(), app.getCamera());
+
+ }
+
+ public float getRotation() {
+ return (float) Math.toDegrees(this.spatial.getLocalRotation().toAngleAxis(new Vector3f(0,0,1)));
+ }
+
+ public void setRotation(float rot){
+ Quaternion quaternion = new Quaternion();
+ quaternion.fromAngleAxis((float) Math.toRadians(rot), new Vector3f(0,0,1));
+ this.spatial.setLocalRotation(quaternion);
+ }
+
+ public Vector3f getLocation(){
+ return this.getSpatial().getLocalTranslation();
+ }
@Override
- protected void controlUpdate(float v) {
-
+ protected void controlUpdate(float delta) {
+ if(shieldRing != null){
+ shieldRing.rotate(0, 0, delta * SHIELD_SPEED);
+ }
}
@Override
protected void controlRender(RenderManager renderManager, ViewPort viewPort) {
}
+
+ public void setLocation(Vector3f loc){
+ this.getSpatial().setLocalTranslation(loc);
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial){
+ if(this.getSpatial() == null && spatial != null){
+ super.setSpatial(spatial);
+ initSpatial();
+ }
+ else{
+ super.setSpatial(spatial);
+ }
+ }
+
+ public void initSpatial(){
+ setRotation(this.initRotation);
+
+ Node oldParent = this.spatial.getParent();
+ this.parentNode.setName(this.spatial.getName() + " Parent");
+ oldParent.detachChild(this.getSpatial());
+ this.parentNode.attachChild(this.getSpatial());
+ oldParent.attachChild(this.parentNode);
+ }
+
+ public void rotateInit() {
+// rotate(rotation - initRotation);
+ }
+
+ public void activateShield(){
+ shieldRing = assetManager.loadModel(Asset.shield_ring.getModelPath());
+ shieldRing.scale(1f);
+ shieldRing.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(0));
+ shieldRing.setLocalTranslation(spatial.getLocalTranslation().add(new Vector3f(0,0,SHIELD_Z)));
+
+
+ shieldRing.setQueueBucket(RenderQueue.Bucket.Transparent); // Render in the transparent bucket
+ shieldMat.setColor("Color", SHIELD_COLOR);
+ shieldRing.setMaterial(shieldMat);
+
+ parentNode.attachChild(shieldRing);
+ }
+
+ public void deactivateShield(){
+ parentNode.detachChild(shieldRing);
+ shieldRing = null;
+ }
+
+ public void suppressShield(){
+ assert(shieldRing != null) : "PieceControl: shieldRing is not set";
+ shieldMat.setColor("Color", SHIELD_SUPPRESSED_COLOR);
+ }
+
+ public void setMaterial(Material mat){
+ this.spatial.setMaterial(mat);
+ }
+
+ public Material getMaterial(){
+ return ((Geometry) this.spatial).getMaterial();
+ }
+
+ public void outline(boolean enemy) {
+ ColorRGBA color = enemy ? OUTLINE_ENEMY_COLOR : OUTLINE_OWN_COLOR;
+ outlineOwn.select(this.getSpatial(), color);
+ }
+
+ public void deOutline() {
+ outlineOwn.deselect(this.getSpatial());
+ }
}
diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Rotation.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Rotation.java
new file mode 100644
index 00000000..5db07057
--- /dev/null
+++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/Rotation.java
@@ -0,0 +1,12 @@
+package pp.mdga.client.board;
+
+public enum Rotation {
+ UP,
+ RIGHT,
+ DOWN,
+ LEFT,
+ UP_LEFT,
+ UP_RIGHT,
+ DOWN_RIGHT,
+ DOWN_LEFT
+}
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