27 Commits

Author SHA1 Message Date
Fleischer Hanno
ec295c94f1 fixed the equals method in piece 2024-12-08 11:23:53 +01:00
Hanno Fleischer
c9c99709ba added more communication fixes, states now use correct messages 2024-12-08 09:52:23 +01:00
Hanno Fleischer
8b27ccce22 adjusted stattransition methods to work correctly 2024-12-08 03:08:47 +01:00
Hanno Fleischer
8c22d935a9 implemented rest of the server logic in choosepiece substates, and began to fix bugs after testing 2024-12-08 01:59:29 +01:00
Hanno Fleischer
0c49d7ed1c Merge branch 'dev/server_h' of https://athene1.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-01 into dev/server_h 2024-12-07 15:37:02 +01:00
Hanno Fleischer
2ba6a22422 Merge branch 'development' into 'dev/server_h'
Development

See merge request progproj/gruppen-ht24/Gruppe-01!37
2024-12-07 14:36:25 +00:00
Hanno Fleischer
c37bac4614 replaced DicaAgain message with DicveNow 2024-12-07 15:34:34 +01:00
Felix Koppe
06b37584cb Merge dev/client_beck into development 2024-12-07 15:05:03 +01:00
Felix Koppe
0c42a2df88 Merge branch 'dev/client_beck' into development
# Conflicts:
#	Projekte/mdga/client/src/main/java/pp/mdga/client/Asset.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MdgaSound.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/SoundAsset.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/animation/JetAnimation.java
#	Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java
#	Projekte/mdga/model/src/main/java/pp/mdga/client/gamestate/GameStates.java
2024-12-07 15:00:27 +01:00
Felix Koppe
d75d704878 Add smoke effekt to missileAnimation 2024-12-07 14:55:08 +01:00
Cedric Beck
6d3c733f91 added effect for shell flying 2024-12-07 14:45:39 +01:00
Felix Koppe
f96da2c46c Add notification delay 2024-12-07 14:37:19 +01:00
Cedric Beck
1a079dad44 added shell asset for ShellAnimation 2024-12-07 14:22:36 +01:00
Cedric Beck
32f49a6181 added shellAnimation without shell asset 2024-12-07 13:30:30 +01:00
Felix Koppe
525809899e Minor improvements 2024-12-07 11:03:30 +01:00
Felix Koppe
fd9708752c Merge dev/model into development 2024-12-06 18:49:15 +01:00
Felix Koppe
236d3db930 Add ambience 2024-12-06 18:10:09 +01:00
Cedric Beck
29c6b13300 added MatrixAnimation 2024-12-06 17:09:06 +01:00
Felix Koppe
6059e93276 Fix interruptDialog 2024-12-06 16:52:08 +01:00
Felix Koppe
f2eeb6dab4 Fix logic error regarding cardSelection 2024-12-06 16:27:01 +01:00
Cedric Beck
2ac2de645b working on matrix-animation 2024-12-06 14:56:04 +01:00
Felix Koppe
d39f85fbe9 Add some javaDoc to client 2024-12-06 14:26:13 +01:00
Felix Koppe
960a57caba Fix broken lose sound 2024-12-06 13:28:38 +01:00
Felix Koppe
36631df2e9 Fix broken jet-sound 2024-12-06 13:26:28 +01:00
Felix Koppe
df27c23cd5 Fix shadercode 2024-12-06 13:19:17 +01:00
Felix Koppe
acd64d1507 Fix shadercode 2024-12-06 13:14:54 +01:00
Felix Koppe
76f86c8a66 Improve audioSettings 2024-12-06 11:35:05 +01:00
115 changed files with 881548 additions and 449 deletions

View File

@@ -28,7 +28,7 @@ public enum Asset {
ship(0.8f),
smallTent,
tank,
world(1.2f),
world("Models/world/world_export_newTex.obj", "Models/world/world_diff.png",1.2f),
shieldRing("Models/shieldRing/shieldRing.j3o", null),
treeSmall(1.2f),
treeBig(1.2f),
@@ -40,6 +40,11 @@ public enum Asset {
shieldSymbol("Models/shieldCard/shieldSymbol.j3o", "Models/shieldCard/shieldCard_diff.png"),
dice,
missile("Models/missile/AVMT300.obj", "Models/missile/texture.jpg", 0.1f),
tankShoot("Models/tank/tank_shoot_bot.obj", "Models/tank/tank_diff.png"),
tankShootTop("Models/tank/tank_shoot_top.obj", "Models/tank/tank_diff.png"),
treesSmallBackground("Models/treeSmall/small_trees_background.obj", "Models/treeSmall/treeSmall_diff.png", 1.2f),
treesBigBackground("Models/treeBig/big_trees_background.obj", "Models/treeBig/treeBig_diff.png", 1.2f),
shell
;
private final String modelPath;

View File

@@ -158,7 +158,7 @@ else if(boardSelect != null) {
gameView.getBoardHandler().movePieceStartAnim(p,0);
//gameView.getBoardHandler().movePieceAnim(p,0, 8);
} else {
gameView.getBoardHandler().throwMissileAnim(p);
gameView.getBoardHandler().throwPiece(p, Color.NAVY);
//gameView.getBoardHandler().movePieceStartAnim(p,0);
}

View File

@@ -1,16 +1,8 @@
package pp.mdga.client;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.server.MdgaServer;
import pp.mdga.client.view.CeremonyView;
import pp.mdga.client.view.GameView;
import pp.mdga.client.view.LobbyView;
import pp.mdga.game.BonusCard;
import pp.mdga.game.Color;
import pp.mdga.message.client.LobbyReadyMessage;
import pp.mdga.notification.AcquireCardNotification;
import pp.mdga.notification.DrawCardNotification;
import pp.mdga.notification.TskSelectNotification;
import java.util.UUID;
import java.util.logging.Level;
@@ -75,10 +67,10 @@ public void selectCard(BonusCard card) {
GameView gameView = (GameView) app.getView();
if(card == null) {
if(card != null) {
gameView.needConfirm();
} else {
gameView.needNoPower();
gameView.showNoPower();
}
}
@@ -95,16 +87,12 @@ public void confirm() {
app.getGameLogic().selectPiece(a);
gameView.getBoardHandler().clearSelectable();
} else {
if(null == card) {
app.getGameLogic().selectCard(null);
} else {
app.getGameLogic().selectCard(card);
gameView.getGuiHandler().clearSelectableCards();
}
app.getGameLogic().selectCard(card);
gameView.getGuiHandler().clearSelectableCards();
}
gameView.noConfirm();
gameView.noNoPower();
gameView.hideNoPower();
}
public void selectTsk(Color color) {

View File

@@ -1,5 +1,6 @@
package pp.mdga.client;
import com.jme3.system.NanoTimer;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.board.BoardHandler;
import pp.mdga.client.gui.GuiHandler;
@@ -16,13 +17,26 @@ public class NotificationSynchronizer {
private ArrayList<Notification> notifications = new ArrayList<>();
private NanoTimer timer = new NanoTimer();
private float delay = 0;
private static final float STANDARD_DELAY = 2.5f;
NotificationSynchronizer(MdgaApp app) {
this.app = app;
}
public void update() {
Notification n = app.getGameLogic().getNotification();
while (n != null) {
while (timer.getTimeInSeconds() >= delay) {
Notification n = app.getGameLogic().getNotification();
if(n == null) {
return;
}
timer.reset();
delay = 0;
if(n instanceof InfoNotification infoNotification) {
app.getView().showInfo(infoNotification.getMessage(), infoNotification.isError());
return;
@@ -46,8 +60,6 @@ public void update() {
throw new RuntimeException("no notification expected: " + n.getClass().getName());
}
}
n = app.getGameLogic().getNotification();
}
}
@@ -90,10 +102,12 @@ private void handleGame(Notification notification) {
if (notification instanceof AcquireCardNotification n) {
guiHandler.addCardOwn(n.getBonusCard());
app.getAcousticHandler().playSound(MdgaSound.BONUS);
delay = STANDARD_DELAY;
} else if (notification instanceof ActivePlayerNotification n) {
gameView.getGuiHandler().setActivePlayer(n.getColor());
boardHandler.showDice(n.getColor());
app.getAcousticHandler().playSound(MdgaSound.UI90);
delay = STANDARD_DELAY;
} else if (notification instanceof CeremonyNotification ceremonyNotification) {
app.enter(MdgaState.CEREMONY);
CeremonyView ceremonyView = (CeremonyView) app.getView();
@@ -141,7 +155,7 @@ private void handleGame(Notification notification) {
}
guiHandler.hideText();
} else if (notification instanceof ThrowPieceNotification n) {
boardHandler.throwBombAnim(n.getPieceId());
boardHandler.throwPiece(n.getPieceId(), n.getThrowColor());
} else if (notification instanceof NoShieldNotification n) {
boardHandler.unshieldPiece(n.getPieceId());
} else if (notification instanceof PlayCardNotification n) {
@@ -162,9 +176,10 @@ private void handleGame(Notification notification) {
if (n.isTurbo()) guiHandler.showRolledDiceMult(n.getEyes(), n.getMultiplier(), n.getColor());
else guiHandler.showRolledDice(n.getEyes(), n.getColor());
}
delay = 7;
} else if (notification instanceof SelectableCardsNotification n) {
guiHandler.setSelectableCards(n.getCards());
gameView.needNoPower();
gameView.showNoPower();
} else if (notification instanceof ShieldActiveNotification n) {
boardHandler.shieldPiece(n.getPieceId());
} else if (notification instanceof ShieldSuppressedNotification n) {

View File

@@ -0,0 +1,47 @@
package pp.mdga.client;
import com.jme3.math.Vector3f;
public class Util {
private Util(){}
/**
* Performs linear interpolation between two values.
*
* @param start The starting value.
* @param end The ending value.
* @param t A parameter between 0 and 1 representing the interpolation progress.
* @return The interpolated value.
*/
public static float linInt(float start, float end, float t) {
return start + t * (end - start);
}
/**
* Performs quadratic interpolation between three points.
*
* @param p1 The initial point.
* @param p2 The middle point.
* @param p3 The final point.
* @param t The interpolation parameter (0 <= t <= 1).
* @return The interpolated point.
*/
public static Vector3f quadInt(Vector3f p1, Vector3f p2, Vector3f p3, float t) {
// Quadratic interpolation: (1-t)^2 * p1 + 2 * (1-t) * t * p2 + t^2 * p3
float oneMinusT = 1 - t;
return p1.mult(oneMinusT * oneMinusT)
.add(p2.mult(2 * oneMinusT * t))
.add(p3.mult(t * t));
}
/**
* A smooth ease-in-out function for interpolation.
* It accelerates and decelerates the interpolation for a smoother effect.
*
* @param x The interpolation parameter (0 <= x <= 1).
* @return The adjusted interpolation value.
*/
public static float easeInOut(float x){
return x < 0.5 ? 4 * x * x * x : (float) (1 - Math.pow(-2 * x + 2, 3) / 2);
}
}

View File

@@ -18,12 +18,14 @@ public class AcousticHandler {
private boolean fading = false; // Indicates if a fade is in progress
private NanoTimer fadeTimer = new NanoTimer(); // Timer to track fade progress
private static final float FADE_DURATION = 3.0f; // Duration for outfade
private static final float FADE_DURATION = 2.0f; // Duration for outfade
private static final float CROSSFADE_DURATION = 1.5f; // Duration for infade
private GameMusic playing = null; // Currently playing track
private GameMusic scheduled = null; // Scheduled track to play next
private GameMusic old = null; // Old track being faded out
private GameMusic birds;
private float mainVolume = 0.0f;
private float musicVolume = 1.0f;
private float soundVolume = 1.0f;
@@ -38,6 +40,8 @@ public AcousticHandler(MdgaApp app) {
mainVolume = prefs.getFloat("mainVolume", 1.0f);
musicVolume = prefs.getFloat("musicVolume", 1.0f);
soundVolume = prefs.getFloat("soundVolume", 1.0f);
birds = new GameMusic(app, MusicAsset.BIRDS, getSoundVolumeTotal(), MusicAsset.BIRDS.getSubVolume(), MusicAsset.BIRDS.getLoop(), 0.0f);
}
/**
@@ -60,6 +64,8 @@ public void update() {
iterator.remove();
}
}
birds.update(Math.min(getSoundVolumeTotal(), getMusicVolumeTotal() > 0 ? 0 : 1));
}
/**
@@ -132,6 +138,15 @@ public void playSound(MdgaSound sound) {
case MATRIX:
assets.add(new SoundAssetDelayVolume(SoundAsset.MATRIX, 1.0f, 0.0f));
break;
case TURRET_ROTATE:
assets.add(new SoundAssetDelayVolume(SoundAsset.TURRET_ROTATE, 0.7f, 0f));
break;
case TANK_SHOOT:
assets.add(new SoundAssetDelayVolume(SoundAsset.TANK_SHOOT, 0.7f, 0f));
break;
case TANK_EXPLOSION:
assets.add(new SoundAssetDelayVolume(SoundAsset.EXPLOSION_1, 1.0f, 0f));
break;
default:
break;
}
@@ -153,6 +168,10 @@ public void playState(MdgaState state) {
}
MusicAsset asset = null;
birds.pause();
float pause = 0.0f;
switch (state) {
case MAIN:
playGame = false;
@@ -163,10 +182,12 @@ public void playState(MdgaState state) {
asset = MusicAsset.LOBBY;
break;
case GAME:
birds.play();
addGameTracks();
playGame = true;
assert (!gameTracks.isEmpty()) : "no more game music available";
asset = gameTracks.remove(0);
pause = 2.0f;
break;
case CEREMONY:
playGame = false;
@@ -178,7 +199,7 @@ public void playState(MdgaState state) {
assert (null != asset) : "music sceduling went wrong";
scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), 0.0f);
scheduled = new GameMusic(app, asset, getMusicVolumeTotal(), asset.getSubVolume(), asset.getLoop(), pause);
}
/**
@@ -454,7 +475,7 @@ public void setSoundVolume(float soundVolume) {
*/
float getMusicVolumeTotal() {
return getMusicVolume() * getMainVolume();
return getMusicVolume() * getMainVolume() / 2;
}
/**

View File

@@ -38,4 +38,7 @@ public enum MdgaSound {
UI90,
MISSILE,
MATRIX,
TURRET_ROTATE,
TANK_SHOOT,
TANK_EXPLOSION
}

View File

@@ -10,12 +10,13 @@ enum MusicAsset {
MAIN_MENU("Spaceship.wav", true, 1.0f),
LOBBY("DeadPlanet.wav", true, 1.0f),
CEREMONY("80s,Disco,Life.wav", true, 1.0f),
GAME_1("NeonRoadTrip.wav", 1.0f),
GAME_2("NoPressureTrance.wav", 1.0f),
GAME_3("TheSynthRave.wav", 1.0f),
GAME_4("LaserParty.wav", 1.0f),
GAME_5("RetroNoir.wav", 1.0f),
GAME_6("SpaceInvaders.wav", 1.0f);
GAME_1("NeonRoadTrip.wav", 0.5f),
GAME_2("NoPressureTrance.wav", 0.5f),
GAME_3("TheSynthRave.wav", 0.5f),
GAME_4("LaserParty.wav", 0.5f),
GAME_5("RetroNoir.wav", 0.5f),
GAME_6("SpaceInvaders.wav", 0.5f),
BIRDS("nature-ambience.ogg", true, 1.0f);
private final String path;
private final boolean loop;

View File

@@ -37,7 +37,11 @@ enum SoundAsset {
LOSE("lose.ogg"),
MISSILE("missile.ogg"),
MATRIX("matrix.wav"),
CONNECTED("connected.wav");
CONNECTED("connected.wav"),
TURRET_ROTATE("turret_rotate.ogg"),
TANK_SHOOT("tank_shoot.ogg")
;
private final String path;

View File

@@ -0,0 +1,18 @@
package pp.mdga.client.animation;
import pp.mdga.client.InitControl;
public class ActionControl extends InitControl {
private final Runnable runnable;
public ActionControl(Runnable runnable){
this.runnable = runnable;
}
protected void action(){
if(runnable == null) throw new RuntimeException("runnable is null");
else runnable.run();
}
}

View File

@@ -10,6 +10,10 @@
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
/**
* The {@code Explosion} class represents an explosion effect in a 3D environment.
* It manages the creation, configuration, and triggering of particle emitters for fire and smoke effects.
*/
public class Explosion {
private final Node rootNode;
@@ -23,11 +27,11 @@ public class Explosion {
private final Material mat;
/**
* Konstruktor für die Explosion.
* Constructor for the {@code Explosion} class.
*
* @param app Die Hauptanwendung.
* @param rootNode Der Root-Knoten, an den die Explosion angefügt wird.
* @param location Der Ort der Explosion in World-Koordinaten.
* @param app The main application managing the explosion.
* @param rootNode The root node to which the explosion effects will be attached.
* @param location The location of the explosion in world coordinates.
*/
public Explosion(MdgaApp app, Node rootNode, Vector3f location) {
this.app = app;
@@ -38,7 +42,8 @@ public Explosion(MdgaApp app, Node rootNode, Vector3f location) {
}
/**
* Initialisiert den Partikel-Emitter für die Explosion.
* Initializes the particle emitters for the explosion effect.
* Configures the fire and smoke emitters with appearance, behavior, and lifespan.
*/
private void initializeEmitter() {
fire = new ParticleEmitter("Effect", Type.Triangle,50);
@@ -77,7 +82,8 @@ private void initializeEmitter() {
}
/**
* Löst die Explosion aus.
* Triggers the explosion effect by attaching and activating the particle emitters for fire and smoke.
* Both emitters are automatically detached after a predefined duration.
*/
public void trigger() {
if (!triggered) {

View File

@@ -0,0 +1,70 @@
package pp.mdga.client.animation;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import pp.mdga.client.InitControl;
import static pp.mdga.client.Util.linInt;
public class FadeControl extends ActionControl {
private float duration; // Duration of the fade effect
private float timeElapsed = 0;
private boolean init = false;
private float startAlpha;
private float endAlpha;
public FadeControl(float duration, float startAlpha, float endAlpha, Runnable actionAfter) {
super(actionAfter);
this.duration = duration;
this.startAlpha = startAlpha;
this.endAlpha = endAlpha;
}
public FadeControl(float duration, float startAlpha, float endAlpha) {
this(duration, startAlpha, endAlpha, null);
}
@Override
protected void initSpatial() {
init = true;
}
@Override
protected void controlUpdate(float tpf) {
if (!init) return;
timeElapsed += tpf;
float t = timeElapsed / duration; // Calculate progress (0 to 1)
if (t >= 1) {
// Fade complete
t = 1;
init = false;
spatial.removeControl(this);
action();
}
float alpha = linInt(startAlpha, endAlpha, t); // Interpolate alpha
// Update the material's alpha
if (spatial instanceof Geometry geometry) {
Material mat = geometry.getMaterial();
if (mat != null) {
ColorRGBA diffuse = (ColorRGBA) mat.getParam("Diffuse").getValue();
mat.setColor("Diffuse", new ColorRGBA(diffuse.r, diffuse.g, diffuse.b, alpha));
ColorRGBA ambient = (ColorRGBA) mat.getParam("Ambient").getValue();
mat.setColor("Ambient", new ColorRGBA(ambient.r, ambient.g, ambient.b, alpha));
// Disable shadows when the object is nearly invisible
if (alpha <= 0.1f) {
geometry.setShadowMode(RenderQueue.ShadowMode.Off);
} else {
geometry.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
}
} else throw new RuntimeException("Material is null");
} else throw new RuntimeException("Spatial is not instance of Geometry");
}
}

View File

@@ -17,30 +17,36 @@
import java.util.UUID;
/**
* The {@code JetAnimation} class handles the animation of a jet model in a 3D environment.
* It creates a jet model, animates its movement along a curved path, triggers an explosion at a target point,
* and performs additional actions upon animation completion.
*/
public class JetAnimation {
private final MdgaApp app; // Referenz auf die Hauptanwendung
private final Node rootNode; // Root-Knoten, an dem die Animation hängt
private Spatial jetModel; // Das Model des "jet"
private final Vector3f spawnPoint; // Spawnpunkt des Jets
private final Vector3f nodePoint; // Punkt des überflogenen Knotens
private final Vector3f despawnPoint; // Punkt, an dem der Jet despawnt
private final float curveHeight; // Maximale Höhe der Kurve
private final float animationDuration; // Dauer der Animation
private final MdgaApp app;
private final Node rootNode;
private Spatial jetModel;
private final Vector3f spawnPoint;
private final Vector3f nodePoint;
private final Vector3f despawnPoint;
private final float curveHeight;
private final float animationDuration;
private Explosion explosion;
private final UUID id;
private Runnable actionAfter;
/**
* Konstruktor für die ThrowAnimation-Klasse.
* Constructor for the {@code JetAnimation} class.
*
* @param app Die Hauptanwendung
* @param rootNode Der Root-Knoten, an dem der Jet angefügt wird
* @param uuid Die UUID des pieces
* @param targetPoint Der Punkt, an dem der Jet spawnt
* @param curveHeight Die maximale Höhe der Flugkurve
* @param animationDuration Die Gesamtdauer der Animation in Sekunden
* @param app The main application managing the jet animation.
* @param rootNode The root node to which the jet model will be attached.
* @param uuid A unique identifier for the animation.
* @param targetPoint The target point where the explosion will occur.
* @param curveHeight The height of the curve for the jet's flight path.
* @param animationDuration The total duration of the jet animation.
*/
public JetAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f targetPoint, float curveHeight, float animationDuration) {
public JetAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f targetPoint, float curveHeight, float animationDuration, Runnable actionAfter) {
Vector3f spawnPoint = targetPoint.add(170, 50, 50);
Vector3f controlPoint = targetPoint.add(new Vector3f(0, 0, -45));
@@ -57,11 +63,12 @@ public JetAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f targetPoint,
id = uuid;
explosion = new Explosion(app, rootNode, targetPoint);
explosion = new Explosion(app, rootNode, nodePoint);
this.actionAfter = actionAfter;
}
/**
* Startet die Animation.
* Starts the jet animation by spawning the jet model and initiating its movement along the predefined path.
*/
public void start() {
app.getAcousticHandler().playSound(MdgaSound.JET);
@@ -70,7 +77,7 @@ public void start() {
}
/**
* Spawnt den Jet an der spezifizierten Position.
* Spawns the jet model at the designated spawn point, applying material, scaling, and rotation.
*/
private void spawnJet() {
jetModel = app.getAssetManager().loadModel(Asset.jet.getModelPath());
@@ -86,7 +93,8 @@ private void spawnJet() {
}
/**
* Animiert den Jet entlang einer Kurve und lässt ihn anschließend verschwinden.
* Animates the jet along a Bezier curve path, triggers the explosion effect at the appropriate time,
* and performs cleanup operations after the animation completes.
*/
private void animateJet() {
Vector3f controlPoint1 = spawnPoint.add(0, curveHeight, 0);
@@ -118,26 +126,34 @@ protected void controlUpdate(float tpf) {
}
if (elapsedTime > 6.0f) {
GameView gameView = (GameView) app.getView();
BoardHandler boardHandler = gameView.getBoardHandler();
boardHandler.throwPieceAnim(id);
endAnim();
}
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
// Wird hier nicht benötigt
}
protected void controlRender(RenderManager rm, ViewPort vp) {}
});
}
private void endAnim(){
actionAfter.run();
}
/**
* Repräsentiert eine 3D-Bezier-Kurve mit vier Kontrollpunkten.
* The {@code BezierCurve3f} class represents a 3D cubic Bezier curve.
* It provides methods to interpolate positions and derivatives along the curve.
*/
private static class BezierCurve3f {
private final Vector3f p0, p1, p2, p3;
/**
* Constructor for the {@code BezierCurve3f} class.
*
* @param p0 The starting point of the curve.
* @param p1 The first control point influencing the curve's shape.
* @param p2 The second control point influencing the curve's shape.
* @param p3 The endpoint of the curve.
*/
public BezierCurve3f(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
this.p0 = p0;
this.p1 = p1;
@@ -145,6 +161,12 @@ public BezierCurve3f(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) {
this.p3 = p3;
}
/**
* Interpolates a position along the curve at a given progress value {@code t}.
*
* @param t The progress value (0.0 to 1.0) along the curve.
* @return The interpolated position on the curve.
*/
public Vector3f interpolate(float t) {
float u = 1 - t;
float tt = t * t;
@@ -159,6 +181,12 @@ public Vector3f interpolate(float t) {
return point;
}
/**
* Computes the derivative at a given progress value {@code t}, representing the direction along the curve.
*
* @param t The progress value (0.0 to 1.0) along the curve.
* @return The derivative (direction vector) at the specified progress.
*/
public Vector3f interpolateDerivative(float t) {
float u = 1 - t;
float tt = t * t;

View File

@@ -0,0 +1,200 @@
package pp.mdga.client.animation;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh.Type;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
import java.util.*;
public class MatrixAnimation extends ActionControl {
private MdgaApp app;
private static final Random RANDOM = new Random();
private Vector3f radarPos;
private Runnable runnable;
private boolean init = false;
private List<ParticleEmitter> activeEmitter = new ArrayList<>();
private ParticleEmitter radarEmitter = null;
private float timeElapsed = 0f;
private enum MatrixState{
RADAR_ON,
RADAR_OFF,
MATRIX_ON,
MATRIX_OFF
}
private MatrixState state;
public MatrixAnimation(MdgaApp app, Vector3f radarPos, Runnable runnable){
super(runnable);
this.app = app;
this.radarPos = radarPos;
}
@Override
protected void initSpatial() {
state = MatrixState.RADAR_ON;
timeElapsed = 0;
init = true;
radar();
}
@Override
protected void controlUpdate(float tpf) {
if(!init) return;
timeElapsed += tpf;
switch(state){
case RADAR_ON -> {
if(timeElapsed >= 2f){
state = MatrixState.RADAR_OFF;
timeElapsed = 0;
radarEmitter.setParticlesPerSec(0);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
app.getRootNode().detachChild(radarEmitter);
}
}, 3000);
}
}
case RADAR_OFF -> {
if(timeElapsed >= 0.1f){
state = MatrixState.MATRIX_ON;
timeElapsed = 0;
matrix();
}
}
case MATRIX_ON -> {
if(timeElapsed >= 3f){
state = MatrixState.MATRIX_OFF;
timeElapsed = 0;
turnOff();
new Timer().schedule(new TimerTask() {
@Override
public void run() {
for (ParticleEmitter particleEmitter : activeEmitter){
app.getRootNode().detachChild(particleEmitter);
}
}
}, 3000);
}
}
case MATRIX_OFF -> {
if(timeElapsed >= 0.5f){
init = false;
spatial.removeControl(this);
action();
}
}
}
}
private void turnOff(){
for (ParticleEmitter particleEmitter : activeEmitter){
particleEmitter.setParticlesPerSec(0f);
}
}
private void radar(){
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture", app.getAssetManager().loadTexture("Images/particle/radar_beam.png"));
ParticleEmitter emitter = new ParticleEmitter("Effect", Type.Triangle, 50);
emitter.setMaterial(mat);
emitter.setImagesX(1); // columns
emitter.setImagesY(1); // rows
emitter.setSelectRandomImage(true);
emitter.setStartColor(ColorRGBA.White);
emitter.setEndColor(ColorRGBA.Black);
emitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0f, 0f, 2));
emitter.getParticleInfluencer().setVelocityVariation(0f);
emitter.setStartSize(0.1f);
emitter.setEndSize(10);
emitter.setGravity(0, 0, 0);
float life = 2.6f;
emitter.setLowLife(life);
emitter.setHighLife(life);
emitter.setLocalTranslation(radarPos.add(new Vector3f(0,0,5)));
emitter.setParticlesPerSec(1.8f);
app.getRootNode().attachChild(emitter);
radarEmitter = emitter;
}
private void matrix(){
for(int i = 0; i < 5; i++){
particleStream(
generateMatrixColor(),
generateMatrixColor(),
getRandomFloat(0,1f),
getRandomPosition(),
getRandomFloat(1,2)
);
}
}
private void particleStream(ColorRGBA start, ColorRGBA end, float speedVar, Vector3f pos, float spawnVar){
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
mat.setTexture("Texture", app.getAssetManager().loadTexture("Images/particle/particle_cir.png"));
ParticleEmitter matrix = new ParticleEmitter("Effect", Type.Triangle, 50);
matrix.setMaterial(mat);
matrix.setImagesX(2); // columns
matrix.setImagesY(1); // rows
matrix.setSelectRandomImage(true);
matrix.setStartColor(start);
matrix.setEndColor(end);
matrix.getParticleInfluencer().setInitialVelocity(new Vector3f(0f, 0f, -6f - speedVar));
matrix.getParticleInfluencer().setVelocityVariation(0f);
matrix.setStartSize(0.4f);
matrix.setEndSize(0.6f);
matrix.setGravity(0, 0, 2f);
matrix.setLowLife(3f);
matrix.setHighLife(3f);
matrix.setLocalTranslation(spatial.getLocalTranslation().add(pos).add(new Vector3f(0,0,15)));
matrix.setParticlesPerSec(spawnVar);
app.getRootNode().attachChild(matrix);
activeEmitter.add(matrix);
}
public static Vector3f getRandomPosition() {
// Generate a random angle in radians (0 to 2π)
float angle = (float) (2 * Math.PI * RANDOM.nextDouble());
// Generate a random radius with uniform distribution
float radius = (float) Math.sqrt(RANDOM.nextDouble());
radius *= 1f;
// Convert polar coordinates to Cartesian
float x = radius * (float) Math.cos(angle);
float y = radius * (float) Math.sin(angle);
return new Vector3f(x,y,0);
}
public static float getRandomFloat(float start, float end) {
if (start > end) {
throw new IllegalArgumentException("Start must be less than or equal to end.");
}
return start + RANDOM.nextFloat() * (end - start);
}
public static ColorRGBA generateMatrixColor() {
// Red is dominant
float red = 0.8f + RANDOM.nextFloat() * 0.2f; // Red channel: 0.8 to 1.0
// Green is moderately high
float green = 0.4f + RANDOM.nextFloat() * 0.3f; // Green channel: 0.4 to 0.7
// Blue is minimal
float blue = RANDOM.nextFloat() * 0.2f; // Blue channel: 0.0 to 0.2
float alpha = 1.0f; // Fully opaque
return new ColorRGBA(red, green, blue, alpha);
}
}

View File

@@ -16,25 +16,30 @@
import java.util.UUID;
/**
* The {@code MissileAnimation} class handles the animation of a missile moving along a parabolic path
* towards a target point in a 3D environment. It also triggers an explosion at the target upon impact.
*/
public class MissileAnimation {
private final Node rootNode; // Root-Knoten, an den die Animation gehängt wird
private final MdgaApp app; // Referenz auf die Hauptanwendung
private final Vector3f start; // Startpunkt der Rakete
private final Vector3f target; // Zielpunkt der Rakete
private final float flightTime; // Gesamtdauer des Flugs
private final Node rootNode;
private final MdgaApp app;
private final Vector3f start;
private final Vector3f target;
private final float flightTime;
private Explosion explosion;
private Spatial missileModel; // 3D-Modell der Rakete
private Spatial missileModel;
private UUID id;
/**
* Konstruktor für die MissileAnimation.
* Constructor for the {@code MissileAnimation} class.
*
* @param app Die Hauptanwendung.
* @param rootNode Der Root-Knoten, an den die Animation gehängt wird.
* @param target Der Zielpunkt der Rakete.
* @param flightTime Die Zeit, die die Rakete für den gesamten Flug benötigt.
* @param app The main application managing the missile animation.
* @param rootNode The root node to which the missile model will be attached.
* @param uuid A unique identifier for the missile animation.
* @param target The target point where the missile will explode.
* @param flightTime The total flight time of the missile.
*/
public MissileAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f target, float flightTime) {
this.app = app;
@@ -52,30 +57,33 @@ public MissileAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f target,
}
/**
* Startet die Raketenanimation.
* Starts the missile animation by loading the missile model and initiating its parabolic movement.
*/
public void start() {
Smoke s = new Smoke(app, rootNode, start);
s.trigger();
loadMissile();
app.getAcousticHandler().playSound(MdgaSound.MISSILE);
animateMissile();
}
/**
* Lädt das Raketenmodell und setzt es auf den Startpunkt.
* Loads the missile model into the scene, applies scaling, material, and sets its initial position.
*/
private void loadMissile() {
missileModel = app.getAssetManager().loadModel(Asset.missile.getModelPath()); // Lade das Missile-Modell
missileModel = app.getAssetManager().loadModel(Asset.missile.getModelPath());
missileModel.scale(Asset.missile.getSize());
missileModel.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(Asset.missile.getDiffPath()));
missileModel.setMaterial(mat);
missileModel.setLocalTranslation(start); // Setze Startposition
rootNode.attachChild(missileModel); // Füge das Modell zur Szene hinzu
missileModel.setLocalTranslation(start);
rootNode.attachChild(missileModel);
}
/**
* Animiert die Rakete entlang einer Parabel.
* Animates the missile along a parabolic path, triggers the explosion near the target,
* and removes the missile model after the animation completes.
*/
private void animateMissile() {
missileModel.addControl(new AbstractControl() {
@@ -93,44 +101,39 @@ protected void controlUpdate(float tpf) {
if (progress >= 1) {
explosion.trigger();
// Flug abgeschlossen
rootNode.detachChild(missileModel); // Entferne Rakete nach dem Ziel
this.spatial.removeControl(this); // Entferne die Steuerung
rootNode.detachChild(missileModel);
this.spatial.removeControl(this);
return;
}
// Berechne die aktuelle Position entlang der Parabel
Vector3f currentPosition = computeParabolicPath(start, target, progress);
missileModel.setLocalTranslation(currentPosition);
// Passe die Ausrichtung an (Nase der Rakete zeigt in Flugrichtung)
Vector3f direction = computeParabolicPath(start, target, progress + 0.01f)
.subtract(currentPosition)
.normalizeLocal();
missileModel.lookAt(currentPosition.add(direction), Vector3f.UNIT_Y); // Z ist oben, Y ist "Up"
missileModel.lookAt(currentPosition.add(direction), Vector3f.UNIT_Y);
missileModel.rotate(0, FastMath.HALF_PI, 0);
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
// Keine Render-Logik benötigt
}
});
}
/**
* Berechnet eine Parabelbewegung von `start` zu `target`.
* Computes a position along a parabolic path at a given progress value {@code t}.
*
* @param start Der Startpunkt der Rakete.
* @param target Der Zielpunkt der Rakete.
* @param t Der Fortschritt des Flugs (0 bis 1).
* @return Die Position der Rakete entlang der Parabel.
* @param start The starting point of the missile's flight.
* @param target The target point of the missile's flight.
* @param t The progress value (0.0 to 1.0) along the flight path.
* @return The interpolated position along the parabolic path.
*/
private Vector3f computeParabolicPath(Vector3f start, Vector3f target, float t) {
Vector3f midPoint = start.add(target).multLocal(0.5f); // Berechne die Mitte zwischen Start und Ziel
midPoint.addLocal(0, 0, 20); // Erhöhe den Scheitelpunkt der Parabel entlang der Z-Achse
Vector3f midPoint = start.add(target).multLocal(0.5f);
midPoint.addLocal(0, 0, 20);
// Quadratische Interpolation (Parabel)
Vector3f startToMid = FastMath.interpolateLinear(t, start, midPoint);
Vector3f midToTarget = FastMath.interpolateLinear(t, midPoint, target);
return FastMath.interpolateLinear(t, startToMid, midToTarget);

View File

@@ -1,7 +1,8 @@
package pp.mdga.client.animation;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
import static pp.mdga.client.Util.*;
/**
* A control that smoothly moves a spatial from an initial position to an end position
@@ -12,16 +13,16 @@
* an ease-in-out curve to create a smooth start and stop effect.
* </p>
*/
public class MoveControl extends InitControl {
public class MoveControl extends ActionControl {
private boolean moving;
private final Vector3f initPos;
private final Vector3f endPos;
private final Vector3f middlePos;
private final static float HEIGHT = 2;
private final static float MOVE_SPEED = 1f;
private float progress = 0;
private final Runnable actionAfter;
private final float height;
private final float duration;
private float timer = 0;
private boolean easing;
/**
* Creates a new MoveControl with specified initial and end positions, and an action to run after the movement.
@@ -32,15 +33,22 @@ public class MoveControl extends InitControl {
* @param actionAfter A Runnable that will be executed after the movement finishes.
*/
public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter){
this(initPos, endPos, actionAfter, 2, 1, true);
}
public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter, float height, float duration, boolean easing){
super(actionAfter);
moving = false;
this.initPos = initPos;
this.endPos = endPos;
this.height = height;
this.duration = duration;
this.easing = easing;
middlePos = new Vector3f(
(initPos.x + endPos.x) / 2,
(initPos.y + endPos.y) / 2,
HEIGHT
(initPos.x + endPos.x) / 2,
(initPos.y + endPos.y) / 2,
height
);
this.actionAfter = actionAfter;
}
/**
@@ -50,7 +58,7 @@ public MoveControl(Vector3f initPos, Vector3f endPos, Runnable actionAfter){
@Override
protected void initSpatial() {
moving = true;
progress = 0;
timer = 0;
}
/**
@@ -63,10 +71,16 @@ protected void initSpatial() {
@Override
protected void controlUpdate(float tpf) {
if(!moving) return;
progress += tpf * MOVE_SPEED;
if(progress > 1) progress = 1;
spatial.setLocalTranslation(quadInt(initPos,middlePos,endPos, easeInOut(progress)));
if(progress == 1) end();
timer += tpf;
float t = timer / duration;
if (t >= 1) t = 1;
float interpolated = easing ? easeInOut(t) : t;
spatial.setLocalTranslation(quadInt(initPos,middlePos,endPos, interpolated));
if(t >= 1) end();
}
/**
@@ -75,35 +89,11 @@ protected void controlUpdate(float tpf) {
*/
private void end(){
moving = false;
actionAfter.run();
spatial.removeControl(this);
action();
}
/**
* Performs quadratic interpolation between three points.
*
* @param p1 The initial point.
* @param p2 The middle point.
* @param p3 The final point.
* @param t The interpolation parameter (0 <= t <= 1).
* @return The interpolated point.
*/
private Vector3f quadInt(Vector3f p1, Vector3f p2, Vector3f p3, float t) {
// Quadratic interpolation: (1-t)^2 * p1 + 2 * (1-t) * t * p2 + t^2 * p3
float oneMinusT = 1 - t;
return p1.mult(oneMinusT * oneMinusT)
.add(p2.mult(2 * oneMinusT * t))
.add(p3.mult(t * t));
}
/**
* A smooth ease-in-out function for interpolation.
* It accelerates and decelerates the interpolation for a smoother effect.
*
* @param x The interpolation parameter (0 <= x <= 1).
* @return The adjusted interpolation value.
*/
private float easeInOut(float x){
return x < 0.5 ? 4 * x * x * x : (float) (1 - Math.pow(-2 * x + 2, 3) / 2);
}
}

View File

@@ -0,0 +1,167 @@
package pp.mdga.client.animation;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue;
import com.jme3.scene.Geometry;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import pp.mdga.client.Asset;
import pp.mdga.client.InitControl;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.board.TankTopControl;
import java.util.Timer;
import java.util.TimerTask;
import static com.jme3.material.Materials.LIGHTING;
import static com.jme3.material.Materials.UNSHADED;
public class ShellAnimation extends ActionControl {
private static final float FLYING_DURATION = 1.25f;
private static final float FLYING_HEIGHT = 12f;
private TankTopControl tankTopControl;
private MdgaApp app;
public ShellAnimation(TankTopControl tankTopControl, MdgaApp app, Runnable actionAfter){
super(actionAfter);
this.tankTopControl = tankTopControl;
this.app = app;
}
@Override
protected void initSpatial() {
tankTopControl.rotate(spatial.getLocalTranslation(), this::shoot);
app.getAcousticHandler().playSound(MdgaSound.TURRET_ROTATE);
app.getRootNode().attachChild(createShell());
}
private Vector3f getShootPos(){
Vector3f localOffset = new Vector3f(0, -5.4f, 2.9f);
Quaternion turretRotation = tankTopControl.getSpatial().getLocalRotation();
Vector3f transformedOffset = turretRotation.mult(localOffset);
return tankTopControl.getSpatial().getLocalTranslation().add(transformedOffset);
}
private void shoot(){
app.getAcousticHandler().playSound(MdgaSound.TANK_SHOOT);
Vector3f shootPos = getShootPos();
createEffect(
shootPos,
"Images/particle/flame.png",
2, 2,
1, 3,
1f,
0.3f, 0.7f,
new ColorRGBA(1f, 0.8f, 0.4f, 0.5f),
new ColorRGBA(1f, 0f, 0f, 0f)
);
createEffect(
shootPos,
"Images/particle/vapor_cloud.png",
3, 3,
0.3f, 0.8f,
10,
0.1f, 0.35f,
new ColorRGBA(0.5f,0.5f,0.5f,0.5f),
ColorRGBA.Black
);
Spatial shell = createShell();
app.getRootNode().attachChild(shell);
shell.addControl(new ShellControl(this::hitExplosion, shootPos, spatial.getLocalTranslation(), FLYING_HEIGHT, FLYING_DURATION, app.getAssetManager()));
}
private Spatial createShell(){
Spatial model = app.getAssetManager().loadModel(Asset.shell.getModelPath());
model.scale(.16f);
model.setLocalTranslation(tankTopControl.getSpatial().getLocalTranslation());
Vector3f shootPos = tankTopControl.getSpatial().getLocalTranslation();
Vector3f targetPos = spatial.getLocalTranslation();
Vector3f direction = targetPos.subtract(shootPos).normalize();
Quaternion rotation = new Quaternion();
rotation.lookAt(direction, new Vector3f(1,0,0)); // Assuming UNIT_Y is the up vector
model.setLocalRotation(rotation);
model.rotate(FastMath.HALF_PI,0,0);
Material mat = new Material(app.getAssetManager(), LIGHTING);
mat.setBoolean("UseMaterialColors", true);
ColorRGBA color = ColorRGBA.fromRGBA255(143,117,0,255);
mat.setColor("Diffuse", color);
mat.setColor("Ambient", color);
model.setMaterial(mat);
return model;
}
private void hitExplosion(){
app.getAcousticHandler().playSound(MdgaSound.TANK_EXPLOSION);
createEffect(
spatial.getLocalTranslation().setZ(1),
"Images/particle/flame.png",
2, 2,
1, 5,
2f,
0.3f, 0.7f,
new ColorRGBA(1f, 0.8f, 0.4f, 0.5f),
new ColorRGBA(1f, 0f, 0f, 0f)
);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
action();
}
}, 800);
}
private void createEffect(Vector3f shootPos,
String image,
int x, int y,
float startSize, float endSize,
float velocity,
float lowLife, float highLife,
ColorRGBA start, ColorRGBA end){
// Create a particle emitter for the explosion
ParticleEmitter explosionEmitter = new ParticleEmitter("Explosion", ParticleMesh.Type.Triangle, 100);
Material explosionMat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
explosionMat.setTexture("Texture", app.getAssetManager().loadTexture(image));
explosionEmitter.setMaterial(explosionMat);
// Particle properties
explosionEmitter.setImagesX(x); // Columns in the texture
explosionEmitter.setImagesY(y); // Rows in the texture
explosionEmitter.setSelectRandomImage(true); // Randomize images for variety
explosionEmitter.setStartColor(start); // Bright yellowish orange
explosionEmitter.setEndColor(end); // Fade to transparent red
explosionEmitter.setStartSize(startSize); // Initial size
explosionEmitter.setEndSize(endSize); // Final size
explosionEmitter.setLowLife(lowLife); // Minimum lifetime of particles
explosionEmitter.setHighLife(highLife); // Maximum lifetime of particles
explosionEmitter.setGravity(0, 0, 1); // Gravity to pull particles down
explosionEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, velocity));
explosionEmitter.getParticleInfluencer().setVelocityVariation(1f); // Adds randomness to the initial velocity
explosionEmitter.setFacingVelocity(true); // Particles face their velocity direction
explosionEmitter.setLocalTranslation(shootPos);
explosionEmitter.setParticlesPerSec(0);
explosionEmitter.emitAllParticles();
app.getRootNode().attachChild(explosionEmitter);
new Timer().schedule(new TimerTask() {
@Override
public void run() {
app.getRootNode().detachChild(explosionEmitter);
}
}, 1000);
}
}

View File

@@ -0,0 +1,89 @@
package pp.mdga.client.animation;
import com.jme3.asset.AssetManager;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
public class ShellControl extends ActionControl {
private final Vector3f shootPos;
private final Vector3f endPos;
private final float height;
private final float duration;
private Vector3f oldPos;
private ParticleEmitter emitter;
private AssetManager assetManager;
public ShellControl(Runnable runnable, Vector3f shootPos, Vector3f endPos, float height, float duration, AssetManager assetManager){
super(runnable);
this.shootPos = shootPos;
this.endPos = endPos;
this.height = height;
this.duration = duration;
this.assetManager = assetManager;
}
@Override
protected void initSpatial() {
spatial.addControl(new MoveControl(
shootPos,
endPos,
()->{
emitter.killAllParticles();
emitter.setParticlesPerSec(0);
emitter.removeFromParent();
spatial.removeControl(this);
spatial.removeFromParent();
action();
},
height,
duration,
false
));
oldPos = spatial.getLocalTranslation().clone();
createEmitter();
}
private void createEmitter() {
emitter = new ParticleEmitter("ShellTrail", ParticleMesh.Type.Triangle, 200);
Material mat = new Material(assetManager, "Common/MatDefs/Misc/Particle.j3md");
mat.setTexture("Texture", assetManager.loadTexture("Images/particle/line.png")); // Nutze eine schmale, linienartige Textur
emitter.setMaterial(mat);
// Comic-Style Farben
emitter.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f)); // Reinweiß
emitter.setEndColor(new ColorRGBA(1f, 1f, 1f, 0f)); // Transparent
// Partikelgröße und Lebensdauer
emitter.setStartSize(0.15f); // Startgröße
emitter.setEndSize(0.1f); // Endgröße
emitter.setLowLife(0.14f); // Sehr kurze Lebensdauer
emitter.setHighLife(0.14f);
emitter.setGravity(0, 0, 0); // Keine Gravitation
emitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0));
emitter.getParticleInfluencer().setVelocityVariation(0f); // Kein Variationsspielraum
// Hohe Dichte für eine glatte Spur
emitter.setParticlesPerSec(500);
// Zur Shell hinzufügen
spatial.getParent().attachChild(emitter);
}
@Override
protected void controlUpdate(float tpf) {
Vector3f direction = spatial.getLocalTranslation().subtract(oldPos).normalize();
if (direction.lengthSquared() > 0) {
spatial.getLocalRotation().lookAt(direction, Vector3f.UNIT_X);
spatial.rotate(FastMath.HALF_PI,0,0);
}
oldPos = spatial.getLocalTranslation().clone();
emitter.setLocalTranslation(spatial.getLocalTranslation().clone());
}
}

View File

@@ -0,0 +1,126 @@
package pp.mdga.client.animation;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.control.AbstractControl;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
public class Smoke {
private final Node rootNode;
private final MdgaApp app;
private final Vector3f location;
private ParticleEmitter fire;
private ParticleEmitter smoke;
private boolean triggered = false;
private final Material mat;
/**
* Constructor for the {@code Explosion} class.
*
* @param app The main application managing the explosion.
* @param rootNode The root node to which the explosion effects will be attached.
* @param location The location of the explosion in world coordinates.
*/
public Smoke(MdgaApp app, Node rootNode, Vector3f location) {
this.app = app;
this.rootNode = rootNode;
this.location = location;
this.mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
}
/**
* Initializes the particle emitters for the explosion effect.
* Configures the fire and smoke emitters with appearance, behavior, and lifespan.
*/
private void initializeEmitter() {
fire = new ParticleEmitter("Effect", ParticleMesh.Type.Triangle,50);
fire.setMaterial(mat);
fire.setStartColor(ColorRGBA.DarkGray);
fire.setEndColor(ColorRGBA.DarkGray);
fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0.2f,0.2f,8f));
fire.getParticleInfluencer().setVelocityVariation(0.4f);
fire.setStartSize(0.1f);
fire.setEndSize(0.8f);
fire.setGravity(0, 0, -0.1f);
fire.setLowLife(0.5f);
fire.setHighLife(1.2f);
fire.setParticlesPerSec(0);
fire.setLocalTranslation(location);
smoke = new ParticleEmitter("Effect2", ParticleMesh.Type.Triangle,40);
smoke.setMaterial(mat);
smoke.setImagesX(2);
smoke.setImagesY(2);
smoke.setStartColor(ColorRGBA.DarkGray);
smoke.setEndColor(new ColorRGBA(0.05f, 0.05f, 0.05f, 1));
smoke.getParticleInfluencer().setInitialVelocity(new Vector3f(0.0f,0.0f,2f));
smoke.getParticleInfluencer().setVelocityVariation(0.5f);
smoke.setStartSize(0.2f);
smoke.setEndSize(0.5f);
smoke.setGravity(0, 0, -0.3f);
smoke.setLowLife(1.2f);
smoke.setHighLife(2.5f);
smoke.setParticlesPerSec(0);
smoke.setLocalTranslation(location);
app.getAcousticHandler().playSound(MdgaSound.EXPLOSION);
}
/**
* Triggers the explosion effect by attaching and activating the particle emitters for fire and smoke.
* Both emitters are automatically detached after a predefined duration.
*/
public void trigger() {
if (!triggered) {
triggered = true;
initializeEmitter();
}
rootNode.attachChild(fire);
fire.emitAllParticles();
fire.addControl(new AbstractControl() {
private float elapsedTime = 0;
@Override
protected void controlUpdate(float tpf) {
elapsedTime += tpf;
if (elapsedTime > 10f) {
rootNode.detachChild(fire);
fire.removeControl(this);
}
}
@Override
protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {}
});
rootNode.attachChild(smoke);
smoke.emitAllParticles();
smoke.addControl(new AbstractControl() {
private float elapsedTime = 0;
@Override
protected void controlUpdate(float tpf) {
elapsedTime += tpf;
if (elapsedTime > 10f) {
rootNode.detachChild(smoke);
smoke.removeControl(this);
}
}
@Override
protected void controlRender(com.jme3.renderer.RenderManager rm, com.jme3.renderer.ViewPort vp) {}
});
}
}

View File

@@ -1,6 +1,9 @@
package pp.mdga.client.board;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.post.FilterPostProcessor;
import com.jme3.renderer.queue.RenderQueue;
@@ -10,9 +13,7 @@
import pp.mdga.client.Asset;
import pp.mdga.client.MdgaApp;
import pp.mdga.client.acoustic.MdgaSound;
import pp.mdga.client.animation.MissileAnimation;
import pp.mdga.client.animation.MoveControl;
import pp.mdga.client.animation.JetAnimation;
import pp.mdga.client.animation.*;
import pp.mdga.client.gui.DiceControl;
import pp.mdga.game.Color;
@@ -56,6 +57,10 @@ public class BoardHandler {
private PieceControl selectedOwnPiece;
private PieceControl selectedEnemyPiece;
private DiceControl diceControl;
//Radar Position for Matrix animation
private Vector3f radarPos;
//TankTop for shellAnimation
private TankTopControl tankTop;
/**
* Creates a new BoardHandler.
@@ -148,12 +153,24 @@ private void initMap() {
case node_wait_blue -> addHomeNode(waitingNodesMap, Color.NAVY, assetOnMap);
case node_wait_green -> addHomeNode(waitingNodesMap, Color.ARMY, assetOnMap);
case node_wait_yellow -> addHomeNode(waitingNodesMap, Color.CYBER, assetOnMap);
case radar -> addRadar(assetOnMap);
case tankShoot -> addTankShoot(assetOnMap);
default -> displayAsset(assetOnMap);
}
}
}
private void addTankShoot(AssetOnMap assetOnMap) {
displayAsset(assetOnMap);
tankTop = displayAndControl(new AssetOnMap(Asset.tankShootTop, assetOnMap.x(), assetOnMap.y(), assetOnMap.rot()), new TankTopControl());
}
private void addRadar(AssetOnMap assetOnMap) {
radarPos = gridToWorld(assetOnMap.x(), assetOnMap.y());
displayAsset(assetOnMap);
}
/**
* Converts an asset to its corresponding color.
*
@@ -187,11 +204,16 @@ private Spatial createModel(Asset asset, Vector3f pos, float rot) {
model.rotate((float) Math.toRadians(0), 0, (float) Math.toRadians(rot));
model.setLocalTranslation(pos);
model.setShadowMode(RenderQueue.ShadowMode.CastAndReceive);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(texName));
mat.setBoolean("UseMaterialColors", true); // Required for Material Colors
mat.setColor("Diffuse", new ColorRGBA(1, 1, 1, 1)); // White color with full alpha
mat.setColor("Ambient", new ColorRGBA(1, 1, 1, 1)); // Ambient color with full alpha
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
model.setMaterial(mat);
rootNodeBoard.attachChild(model);
rootNodeBoard.attachChild(model);
return model;
}
@@ -218,16 +240,38 @@ private Spatial displayAsset(AssetOnMap assetOnMap) {
return createModel(assetOnMap.asset(), gridToWorld(x, y), assetOnMap.rot());
}
/**
* Adds a visual representation of an asset to the scene, attaches a control to it, and returns the control.
*
* @param assetOnMap The asset to be displayed in the 3D environment.
* @param control The control to be added to the spatial representing the asset.
* @param <T> The type of control, extending {@code AbstractControl}.
* @return The control that was added to the spatial.
*/
private <T extends AbstractControl> T displayAndControl(AssetOnMap assetOnMap, T control) {
Spatial spatial = displayAsset(assetOnMap);
spatial.addControl(control);
return control;
}
/**
* Moves a piece in the 3D environment to the location of a specified node.
*
* @param pieceControl The control managing the piece to be moved.
* @param nodeControl The control managing the target node to which the piece will move.
*/
private void movePieceToNode(PieceControl pieceControl, NodeControl nodeControl){
pieceControl.setLocation(nodeControl.getLocation());
}
/**
* Adds a home node for a specific player color, attaching it to the map of home nodes.
*
* @param map The map storing lists of home nodes by player color.
* @param color The color associated with the home nodes to be added.
* @param assetOnMap The asset representing the home node in the 3D environment.
* @throws RuntimeException if more than 4 home nodes are added for a single color.
*/
private void addHomeNode(Map<Color, List<NodeControl>> map, Color color, AssetOnMap assetOnMap){
List<NodeControl> homeNodes = addItemToMapList(map, color, displayAndControl(assetOnMap, new NodeControl(app, fpp)));
if (homeNodes.size() > 4) throw new RuntimeException("too many homeNodes for " + color);
@@ -271,6 +315,16 @@ private void movePieceRek(UUID uuid, int curIndex, int moveIndex){
movePieceRek(uuid, curIndex, moveIndex);
}
/**
* Adds an item to a list in a map. If the key does not exist in the map, a new list is created.
*
* @param map The map containing lists of items.
* @param key The key associated with the list in the map.
* @param item The item to be added to the list.
* @param <T> The type of items in the list.
* @param <E> The type of the key in the map.
* @return The updated list associated with the specified key.
*/
private <T, E> List<T> addItemToMapList(Map<E,List<T>> map, E key, T item){
List<T> list = map.getOrDefault(key, new ArrayList<>());
list.add(item);
@@ -278,12 +332,27 @@ private <T, E> List<T> addItemToMapList(Map<E,List<T>> map, E key, T item){
return list;
}
/**
* Removes an item from a list in a map. If the key does not exist in the map, a new list is created.
*
* @param map The map containing lists of items.
* @param key The key associated with the list in the map.
* @param item The item to be removed from the list.
* @param <T> The type of items in the list.
* @param <E> The type of the key in the map.
*/
private <T, E> void removeItemFromMapList(Map<E,List<T>> map, E key, T item){
List<T> list = map.getOrDefault(key, new ArrayList<>());
list.remove(item);
map.put(key, list);
}
/**
* Calculates the mean position of the waiting nodes for a specific color.
*
* @param color The color associated with the waiting nodes.
* @return The mean position of the waiting nodes as a {@code Vector3f}.
*/
private Vector3f getWaitingPos(Color color){
return getMeanPosition(waitingNodesMap.get(color).stream().map(NodeControl::getLocation).toList());
}
@@ -723,34 +792,57 @@ public void movePieceStartAnim(UUID uuid, int moveIndex){
*/
public void throwPieceAnim(UUID uuid){
pieces.get(uuid).getSpatial().addControl(new MoveControl(
pieces.get(uuid).getLocation(), getNextWaitingNode(pieceColor.get(uuid)).getLocation(), ()->throwPiece(uuid))
pieces.get(uuid).getLocation(), getNextWaitingNode(pieceColor.get(uuid)).getLocation(),
()->throwPiece(uuid))
);
}
/**
* Animates the throwing of a piece to the next available waiting node and plays jet animation.
*
* @param uuid the UUID of the piece to animate
*/
public void throwBombAnim(UUID uuid){
Vector3f targetPoint = pieces.get(uuid).getLocation();
JetAnimation anim = new JetAnimation(app, rootNode, uuid, targetPoint, 40, 6);
anim.start();
public void throwPiece(UUID uuid, Color throwColor){
switch(throwColor){
case ARMY -> throwShell(uuid);
case NAVY -> throwMissle(uuid);
case CYBER -> throwMatrix(uuid);
case AIRFORCE -> throwBomb(uuid);
default -> throw new RuntimeException("invalid color");
}
}
/**
* Animates the throwing of a piece to the next available waiting node and plays ship animation.
* Animates the throwing of a piece to the next available waiting node.
*
* @param uuid the UUID of the piece to animate
*/
public void throwMissileAnim(UUID uuid){
private void throwBomb(UUID uuid) {
Vector3f targetPoint = pieces.get(uuid).getLocation();
JetAnimation anim = new JetAnimation(app, rootNode, uuid, targetPoint, 40, 6, ()->throwPieceAnim(uuid));
anim.start();
}
private void throwMatrix(UUID uuid) {
//app.getAcousticHandler().playSound(MdgaSound.MATRIX);
Spatial piece = pieces.get(uuid).getSpatial();
piece.addControl(new MatrixAnimation(app, radarPos,()-> {
piece.addControl(new FadeControl(1,1,0,
() -> {
throwPiece(uuid);
piece.addControl(new FadeControl(1,0,1));
}
));
}));
}
private void throwMissle(UUID uuid) {
Vector3f targetPoint = pieces.get(uuid).getLocation();
MissileAnimation anim = new MissileAnimation(app, rootNode, uuid, targetPoint, 2);
anim.start();
}
private void throwShell(UUID uuid) {
pieces.get(uuid).getSpatial().addControl(new ShellAnimation(tankTop, app, ()-> throwPieceAnim(uuid)));
}
/**
* Animates the swapping of two pieces by swapping their positions and rotations.
*

View File

@@ -109,6 +109,9 @@ private static Asset getLoadedAsset(String assetName) {
case "tank" -> Asset.tank;
case "treeSmall" -> Asset.treeSmall;
case "treeBig" -> Asset.treeBig;
case "tank_shoot" -> Asset.tankShoot;
case "treesBigBackground" -> Asset.treesBigBackground;
case "treesSmallBackground" -> Asset.treesSmallBackground;
default -> throw new IllegalStateException("Unexpected value: " + assetName);
};
}

View File

@@ -18,17 +18,37 @@ public class OutlineControl extends InitControl {
private static final int THICKNESS_DEFAULT = 6;
private MdgaApp app;
/**
* Constructs an {@code OutlineControl} with default thickness for the object outline.
*
* @param app The main application managing the outline control.
* @param fpp The {@code FilterPostProcessor} used for post-processing effects.
*/
public OutlineControl(MdgaApp app, FilterPostProcessor fpp){
this.app = app;
outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), app.getCamera(), app);
}
/**
* Constructs an {@code OutlineControl} with default thickness, allowing a custom camera to be specified.
*
* @param app The main application managing the outline control.
* @param fpp The {@code FilterPostProcessor} used for post-processing effects.
* @param cam The camera used for rendering the outlined objects.
*/
public OutlineControl(MdgaApp app, FilterPostProcessor fpp, Camera cam){
this.app = app;
outlineOwn = new SelectObjectOutliner(THICKNESS_DEFAULT, fpp, app.getRenderManager(), app.getAssetManager(), cam, app);
}
/**
* Constructs an {@code OutlineControl} with a specified thickness and custom camera.
*
* @param app The main application managing the outline control.
* @param fpp The {@code FilterPostProcessor} used for post-processing effects.
* @param cam The camera used for rendering the outlined objects.
* @param thickness The thickness of the outline.
*/
public OutlineControl(MdgaApp app, FilterPostProcessor fpp, Camera cam, int thickness){
this.app = app;
outlineOwn = new SelectObjectOutliner(thickness, fpp, app.getRenderManager(), app.getAssetManager(), cam, app);
@@ -61,6 +81,11 @@ public void deOutline(){
outlineOwn.deselect(spatial);
}
/**
* Retrieves the instance of the {@code MdgaApp} associated with this control.
*
* @return The {@code MdgaApp} instance.
*/
public MdgaApp getApp() {
return app;
}

View File

@@ -141,7 +141,7 @@ public void initSpatial(){
}
public void rotateInit() {
// rotate(rotation - initRotation);
setRotation(initRotation);
}
/**
@@ -278,4 +278,5 @@ public boolean isSelectable() {
public void setHoverable(boolean hoverable) {
this.hoverable = hoverable;
}
}

View File

@@ -0,0 +1,105 @@
package pp.mdga.client.board;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import pp.mdga.client.InitControl;
import static pp.mdga.client.Util.linInt;
public class TankTopControl extends InitControl {
private float timer = 0; // Time elapsed
private final static float DURATION = 1.5f; // Total rotation duration in seconds
private boolean rotating = false; // Flag to track if rotation is active
private float startAngle = 0;
private float endAngle = 0;
private Runnable actionAfter = null;
@Override
protected void controlUpdate(float tpf) {
if (!rotating) return;
// Update the timer
timer += tpf;
// Calculate interpolation factor (0 to 1)
float t = timer / DURATION;
if (t >= 1) t = 1;
float curAngle = linInt(startAngle, endAngle, t);
// Interpolate the rotation
Quaternion interpolatedRotation = new Quaternion();
interpolatedRotation.fromAngleAxis((float) Math.toRadians(curAngle), Vector3f.UNIT_Z);
// Apply the interpolated rotation to the spatial
spatial.setLocalRotation(interpolatedRotation);
if(t >= 1){
rotating = false;
if(actionAfter != null) actionAfter.run();
}
}
public void rotate(Vector3f enemyPos, Runnable actionAfter) {
if (spatial == null) throw new RuntimeException("spatial is null");
startAngle = getOwnAngle();
endAngle = getEnemyAngle(enemyPos);
// Adjust endAngle to ensure the shortest path
float deltaAngle = endAngle - startAngle;
if (deltaAngle > 180) {
endAngle -= 360; // Rotate counterclockwise
} else if (deltaAngle < -180) {
endAngle += 360; // Rotate clockwise
}
timer = 0;
rotating = true;
this.actionAfter = actionAfter; // Store the action to execute after rotation
}
private float getEnemyAngle(Vector3f enemyPos){
// Direction to the enemy in the XY plane
Vector3f direction = enemyPos.subtract(spatial.getLocalTranslation());
direction.z = 0; // Project to XY plane
direction.normalizeLocal();
Vector3f reference = Vector3f.UNIT_Y.mult(-1);
// Calculate the angle between the direction vector and the reference vector
float angle = FastMath.acos(reference.dot(direction));
// Determine rotation direction using the cross product
Vector3f cross = reference.cross(direction);
if (cross.z < 0) {
angle = -angle;
}
return (float) Math.toDegrees(angle); // Return the absolute angle in degrees
}
private float getOwnAngle() {
// Tank's forward direction in the XY plane
Vector3f forward = spatial.getLocalRotation().mult(Vector3f.UNIT_Y);
forward.z = 0; // Project to XY plane
forward.normalizeLocal();
// Reference vector: Positive X-axis
Vector3f reference = Vector3f.UNIT_Y;
// Calculate the angle between the forward vector and the reference vector
float angle = FastMath.acos(reference.dot(forward));
// Determine rotation direction using the cross product
Vector3f cross = reference.cross(forward);
if (cross.z < 0) { // For Z-up, check the Z component of the cross product
angle = -angle;
}
return (float) Math.toDegrees(angle); // Return the absolute angle in radians
}
}

View File

@@ -77,12 +77,17 @@ public SliderButton(MdgaApp app, Node node, String label) {
QuadBackgroundComponent background = new QuadBackgroundComponent(BUTTON_NORMAL);
slider.setBackground(background);
// Set label background
QuadBackgroundComponent labelBackground = new QuadBackgroundComponent(BUTTON_NORMAL);
this.label.setBackground(labelBackground);
// Configure the label font
this.label.setFont(font);
this.label.setTextHAlignment(HAlignment.Center);
// Default position and size
pos = new Vector2f(0, 0);
size = new Vector2f(5.5f, 1);
size = new Vector2f(6f, 1);
// Add label and slider to container
container.addChild(this.label);

View File

@@ -7,6 +7,11 @@
import pp.mdga.client.button.SliderButton;
import pp.mdga.client.view.MdgaView;
/**
* The {@code AudioSettingsDialog} class represents a dialog for adjusting audio settings in the application.
* It provides controls for managing main volume, music volume, and sound effect volume, and includes
* a button to return to the previous menu.
*/
public class AudioSettingsDialog extends Dialog {
private final MdgaView view;
@@ -18,6 +23,13 @@ public class AudioSettingsDialog extends Dialog {
private boolean active = false;
/**
* Constructs an {@code AudioSettingsDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The current view, used for navigation and interaction with the dialog.
*/
public AudioSettingsDialog(MdgaApp app, Node node, MdgaView view) {
super(app, node);
@@ -42,6 +54,9 @@ public AudioSettingsDialog(MdgaApp app, Node node, MdgaView view) {
backButton.setPos(new Vector2f(0, 1.8f));
}
/**
* Called when the dialog is shown. Initializes and displays the volume controls and back button.
*/
@Override
protected void onShow() {
active = true;
@@ -57,6 +72,9 @@ protected void onShow() {
soundVolume.show();
}
/**
* Called when the dialog is hidden. Hides all volume controls and the back button.
*/
@Override
protected void onHide() {
active = false;
@@ -68,6 +86,10 @@ protected void onHide() {
soundVolume.hide();
}
/**
* Updates the application audio settings based on the current values of the sliders.
* This method is called continuously while the dialog is active.
*/
public void update() {
if(!active) {
return;

View File

@@ -10,17 +10,30 @@
import java.util.ArrayList;
/**
* The {@code CeremonyDialog} class displays a dialog containing statistical data in a tabular format.
* It allows adding rows of statistics and manages their visibility when shown or hidden.
*/
public class CeremonyDialog extends Dialog {
private ArrayList<ArrayList<LabelButton>> labels;
float offsetX;
/**
* Constructs a {@code CeremonyDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
*/
public CeremonyDialog(MdgaApp app, Node node) {
super(app, node);
prepare();
}
/**
* Called when the dialog is shown. Makes all label buttons in the table visible.
*/
@Override
protected void onShow() {
for (ArrayList<LabelButton> row : labels) {
@@ -30,6 +43,9 @@ protected void onShow() {
}
}
/**
* Called when the dialog is hidden. Hides all label buttons in the table.
*/
@Override
protected void onHide() {
for (ArrayList<LabelButton> row : labels) {
@@ -39,6 +55,17 @@ protected void onHide() {
}
}
/**
* Adds a row of statistical data to the dialog.
*
* @param name The name of the player or category for the row.
* @param v1 The value for the first column.
* @param v2 The value for the second column.
* @param v3 The value for the third column.
* @param v4 The value for the fourth column.
* @param v5 The value for the fifth column.
* @param v6 The value for the sixth column.
*/
public void addStatisticsRow(String name, int v1, int v2, int v3, int v4, int v5, int v6) {
float offsetYSmall = 0.5f;
@@ -76,6 +103,9 @@ public void addStatisticsRow(String name, int v1, int v2, int v3, int v4, int v5
labels.add(row);
}
/**
* Prepares the initial layout of the dialog, including header labels.
*/
public void prepare() {
offsetX = 0.5f;

View File

@@ -4,30 +4,53 @@
import com.simsilica.lemur.Container;
import pp.mdga.client.MdgaApp;
/**
* The {@code Dialog} class serves as an abstract base class for dialogs in the application.
* It provides functionality for showing and hiding the dialog and defines abstract methods
* for custom behavior when the dialog is shown or hidden.
*/
public abstract class Dialog {
protected final MdgaApp app;
protected final Node node = new Node();
private final Node root;
/**
* Constructs a {@code Dialog}.
*
* @param app The main application managing the dialog.
* @param node The root node to which the dialog's node will be attached.
*/
Dialog(MdgaApp app, Node node) {
this.app = app;
this.root = node;
}
/**
* Shows the dialog by attaching its node to the root node and invoking the {@code onShow} method.
*/
public void show() {
root.attachChild(node);
onShow();
}
/**
* Hides the dialog by detaching its node from the root node and invoking the {@code onHide} method.
*/
public void hide() {
root.detachChild(node);
onHide();
}
/**
* Called when the dialog is shown. Subclasses must implement this method to define custom behavior.
*/
protected abstract void onShow();
/**
* Called when the dialog is hidden. Subclasses must implement this method to define custom behavior.
*/
protected abstract void onHide();
}

View File

@@ -12,6 +12,10 @@
import java.util.prefs.Preferences;
/**
* The {@code HostDialog} class represents a dialog for hosting a network game session.
* It allows users to input a port number, start hosting a server, and navigate back to the previous view.
*/
public class HostDialog extends NetworkDialog {
private InputButton portInput;
@@ -22,6 +26,13 @@ public class HostDialog extends NetworkDialog {
private Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
/**
* Constructs a {@code HostDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The main view used for navigation and interaction with the dialog.
*/
public HostDialog(MdgaApp app, Node node, MainView view) {
super(app, node, (NetworkSupport) app.getNetworkSupport());
@@ -39,6 +50,9 @@ public HostDialog(MdgaApp app, Node node, MainView view) {
offset += 1.5f;
}
/**
* Called when the dialog is shown. Displays all input fields and buttons.
*/
@Override
protected void onShow() {
portInput.show();
@@ -46,6 +60,9 @@ protected void onShow() {
backButton.show();
}
/**
* Called when the dialog is hidden. Hides all input fields and buttons.
*/
@Override
protected void onHide() {
portInput.hide();
@@ -53,27 +70,44 @@ protected void onHide() {
backButton.hide();
}
/**
* Updates the state of the port input field.
* This method is called periodically to synchronize the dialog state.
*/
public void update() {
portInput.update();
}
/**
* Retrieves the currently entered port number, saves it to preferences, and sets it as the active port.
*
* @return The port number as a string.
*/
public String getPort() {
prefs.put("hostPort", portInput.getString());
setPortNumber(Integer.parseInt(portInput.getString()));
return portInput.getString();
}
/**
* Resets the port input field to its default value and updates preferences accordingly.
*/
public void resetPort() {
portInput.reset();
prefs.put("hostPort", "11111");
}
/**
* Starts the server to host a network game.
*/
public void hostServer() {
startServer();
}
/**
* Connects to the server as a client.
*/
public void connectServerAsClient() {
connectServer();
}
}

View File

@@ -10,6 +10,10 @@
import pp.mdga.client.view.MdgaView;
import pp.mdga.game.Color;
/**
* The {@code InterruptDialog} class represents a dialog that interrupts the game flow,
* providing a message and the option to force an action if the user is a host.
*/
public class InterruptDialog extends Dialog {
private ButtonRight forceButton;
@@ -17,33 +21,50 @@ public class InterruptDialog extends Dialog {
private String text = "";
/**
* Constructs an {@code InterruptDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
*/
public InterruptDialog(MdgaApp app, Node node) {
super(app, node);
forceButton = new ButtonRight(app, node, () -> app.getModelSynchronize().force(), "Erzwingen", 1);
label = new LabelButton(app, node, "Warte auf " + text + "...", new Vector2f(5.5f * 1.5f, 2), new Vector2f(0.5f, 0f), false);
float offset = 2.8f;
label.setPos(new Vector2f(0, MenuButton.VERTICAL - offset));
}
/**
* Called when the dialog is shown. Displays the label and optionally the force button if the user is the host.
*/
@Override
protected void onShow() {
if(app.getGameLogic().isHost()) {
forceButton.show();
}
label = new LabelButton(app, node, "Warte auf " + text + "...", new Vector2f(5.5f * 1.5f, 2), new Vector2f(0.5f, 0f), false);
float offset = 2.8f;
label.setPos(new Vector2f(0, MenuButton.VERTICAL - offset));
label.show();
}
/**
* Called when the dialog is hidden. Hides the label and the force button.
*/
@Override
protected void onHide() {
forceButton.hide();
label.hide();
}
/**
* Sets the displayed text based on the specified color.
*
* @param color The color used to determine the text (e.g., "Luftwaffe" for AIRFORCE).
*/
public void setColor(Color color) {
switch (color) {
case AIRFORCE:

View File

@@ -13,6 +13,10 @@
import java.util.prefs.Preferences;
/**
* The {@code JoinDialog} class represents a dialog for joining a network game.
* It allows users to input an IP address and port number, connect to a server, or navigate back to the previous view.
*/
public class JoinDialog extends NetworkDialog {
private InputButton ipInput;
private InputButton portInput;
@@ -24,6 +28,13 @@ public class JoinDialog extends NetworkDialog {
private Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
/**
* Constructs a {@code JoinDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The main view used for navigation and interaction with the dialog.
*/
public JoinDialog(MdgaApp app, Node node, MainView view) {
super(app, node, (NetworkSupport) app.getNetworkSupport());
@@ -46,6 +57,9 @@ public JoinDialog(MdgaApp app, Node node, MainView view) {
offset += 1.5f;
}
/**
* Called when the dialog is shown. Displays all input fields and buttons.
*/
@Override
protected void onShow() {
ipInput.show();
@@ -54,6 +68,9 @@ protected void onShow() {
backButton.show();
}
/**
* Called when the dialog is hidden. Hides all input fields and buttons.
*/
@Override
protected void onHide() {
ipInput.hide();
@@ -62,37 +79,62 @@ protected void onHide() {
backButton.hide();
}
/**
* Updates the state of the input fields. This method is called periodically to synchronize the dialog state.
*/
public void update() {
ipInput.update();
portInput.update();
}
/**
* Retrieves the currently entered IP address, saves it to preferences, and sets it as the hostname.
*
* @return The IP address as a string.
*/
public String getIpt() {
prefs.put("joinIp", ipInput.getString());
setHostname(ipInput.getString());
return ipInput.getString();
}
/**
* Resets the IP input field to its default value and updates preferences accordingly.
*/
public void resetIp() {
ipInput.reset();
prefs.put("joinIp", "");
}
/**
* Retrieves the currently entered port number, saves it to preferences, and sets it as the active port.
*
* @return The port number as a string.
*/
public String getPort() {
prefs.put("joinPort", portInput.getString());
setPortNumber(Integer.parseInt(portInput.getString()));
return portInput.getString();
}
/**
* Resets the port input field to its default value and updates preferences accordingly.
*/
public void resetPort() {
portInput.reset();
prefs.put("joinPort", "11111");
}
/**
* Connects to the server using the current IP address and port number.
*/
public void connectToServer() {
connectServer();
}
/**
* Disconnects from the server if a network connection exists.
*/
public void disconnect() {
NetworkSupport network = getNetwork();
if (network != null) {
@@ -104,4 +146,3 @@ public void disconnect() {
}
}
}

View File

@@ -8,6 +8,11 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
/**
* The {@code NetworkDialog} class serves as an abstract base class for dialogs
* that involve network-related functionalities, such as connecting to a server or hosting a game.
* It provides methods for initializing, connecting to, and managing a network server.
*/
public abstract class NetworkDialog extends Dialog {
private NetworkSupport network;
@@ -17,19 +22,41 @@ public abstract class NetworkDialog extends Dialog {
private MdgaServer serverInstance;
private Thread serverThread;
/**
* Constructs a {@code NetworkDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param network The network support instance for managing network interactions.
*/
public NetworkDialog(MdgaApp app, Node node, NetworkSupport network) {
super(app, node);
this.network = network;
}
/**
* Sets the hostname for the network connection.
*
* @param hostname The hostname or IP address of the server.
*/
public void setHostname(String hostname) {
this.hostname = hostname;
}
/**
* Sets the port number for the network connection.
*
* @param portNumber The port number to use for the connection.
*/
public void setPortNumber(int portNumber) {
this.portNumber = portNumber;
}
/**
* Initializes the network connection using the current hostname and port number.
*
* @return {@code null} if successful, otherwise throws an exception.
*/
protected Object initNetwork() {
try {
this.network.initNetwork(this.hostname, this.portNumber);
@@ -39,6 +66,9 @@ protected Object initNetwork() {
}
}
/**
* Starts the process of connecting to a server asynchronously.
*/
protected void connectServer() {
try {
connectionFuture = this.network.getApp().getExecutor().submit(this::initNetwork);
@@ -47,6 +77,9 @@ protected void connectServer() {
}
}
/**
* Starts hosting a server in a separate thread.
*/
protected void startServer() {
serverThread = new Thread(() -> {
try {
@@ -60,6 +93,9 @@ protected void startServer() {
serverThread.start();
}
/**
* Shuts down the hosted server and cleans up resources.
*/
public void shutdownServer() {
try {
@@ -85,6 +121,11 @@ public void shutdownServer() {
}
}
/**
* Updates the state of the connection process.
*
* @param delta The time elapsed since the last update call.
*/
public void update(float delta) {
if (this.connectionFuture != null && this.connectionFuture.isDone()) {
try {
@@ -97,6 +138,11 @@ public void update(float delta) {
}
}
/**
* Retrieves the {@code NetworkSupport} instance associated with this dialog.
*
* @return The {@code NetworkSupport} instance.
*/
public NetworkSupport getNetwork() {
return network;
}

View File

@@ -8,6 +8,10 @@
import pp.mdga.client.view.MainView;
import pp.mdga.client.view.MdgaView;
/**
* The {@code SettingsDialog} class represents a dialog for navigating to various settings sections,
* such as video and audio settings, or returning to the previous view.
*/
public class SettingsDialog extends Dialog {
private MenuButton videoButton;
private MenuButton audioButton;
@@ -15,6 +19,13 @@ public class SettingsDialog extends Dialog {
private final MdgaView view;
/**
* Constructs a {@code SettingsDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The view managing navigation and interaction with the settings dialog.
*/
public SettingsDialog(MdgaApp app, Node node, MdgaView view) {
super(app, node);
@@ -34,6 +45,9 @@ public SettingsDialog(MdgaApp app, Node node, MdgaView view) {
backButton.setPos(new Vector2f(0, 1.8f));
}
/**
* Called when the dialog is shown. Displays all buttons for video settings, audio settings, and back navigation.
*/
@Override
protected void onShow() {
videoButton.show();
@@ -41,6 +55,9 @@ protected void onShow() {
backButton.show();
}
/**
* Called when the dialog is hidden. Hides all buttons for video settings, audio settings, and back navigation.
*/
@Override
protected void onHide() {
videoButton.hide();

View File

@@ -14,6 +14,10 @@
import java.util.Random;
import java.util.random.RandomGenerator;
/**
* The {@code StartDialog} class represents the initial dialog in the application,
* allowing the user to input their name, host or join a game, or exit the application.
*/
public class StartDialog extends Dialog {
private InputButton nameInput;
@@ -23,6 +27,13 @@ public class StartDialog extends Dialog {
private final MainView view;
/**
* Constructs a {@code StartDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The main view used for navigation and interaction with the dialog.
*/
public StartDialog(MdgaApp app, Node node, MainView view) {
super(app, node);
@@ -48,6 +59,9 @@ public StartDialog(MdgaApp app, Node node, MainView view) {
endButton.setPos(new Vector2f(0, 1.8f));
}
/**
* Called when the dialog is shown. Displays the name input field and all buttons.
*/
@Override
protected void onShow() {
nameInput.show();
@@ -57,6 +71,9 @@ protected void onShow() {
endButton.show();
}
/**
* Called when the dialog is hidden. Hides the name input field and all buttons.
*/
@Override
protected void onHide ()
{
@@ -67,10 +84,18 @@ protected void onHide ()
endButton.hide();
}
/**
* Updates the state of the name input field. This method is called periodically to synchronize the dialog state.
*/
public void update() {
nameInput.update();
}
/**
* Retrieves the name entered by the user. If no name is provided, a random name is generated.
*
* @return The user's name or a randomly generated name.
*/
public String getName() {
String name = nameInput.getString();
@@ -206,7 +231,7 @@ public String getName() {
"FluffiKopf",
"DonutDöner",
"VollpfostenX",
"Schraubenschlüssel",
"Waschlappen",
"Witzepumper",
"ToastTraum",
"FroschFighter",
@@ -263,22 +288,22 @@ public String getName() {
"VulkanKeks",
"WasserToast",
"MenschSalat",
"KampfKohlenhydrate",
"KampfKohl",
"SockenZirkus",
"SchwimmBärchen",
"TanzenderDachgepäckträger",
"TanzenderPudel",
"PizzamarktMensch",
"ZahnarztZocker",
"RollerCoasterTester",
"WaschmaschinenPilot",
"RollerRudi",
"PupsPilot",
"WitzigeZwiebel",
"Pillenschlucker",
"ZwiebelReiter",
"HüpfenderKaktus",
"KochenderAsteroid",
"AsteroidenAlf",
"ChaosKarotte",
"WolkenFurz",
"SchnitzelPartikel",
"Krümelmonster",
"WackelBiene",
};

View File

@@ -11,6 +11,11 @@
import java.util.prefs.Preferences;
/**
* The {@code VideoSettingsDialog} class represents a dialog for configuring video settings,
* such as resolution and fullscreen mode. It also provides an option to restart the application
* when certain settings are changed.
*/
public class VideoSettingsDialog extends Dialog {
private static Preferences prefs = Preferences.userNodeForPackage(JoinDialog.class);
@@ -29,6 +34,13 @@ public class VideoSettingsDialog extends Dialog {
private boolean active = false;
/**
* Constructs a {@code VideoSettingsDialog}.
*
* @param app The main application managing the dialog.
* @param node The root node for attaching UI elements.
* @param view The view managing navigation and interaction with the video settings dialog.
*/
public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) {
super(app, node);
@@ -67,6 +79,9 @@ public VideoSettingsDialog(MdgaApp app, Node node, MdgaView view) {
backButton.setPos(new Vector2f(0, 1.8f));
}
/**
* Called when the dialog is shown. Displays all buttons and marks the dialog as active.
*/
@Override
protected void onShow() {
active = true;
@@ -83,6 +98,9 @@ protected void onShow() {
backButton.show();
}
/**
* Called when the dialog is hidden. Hides all buttons and marks the dialog as inactive.
*/
@Override
protected void onHide() {
active = false;
@@ -100,12 +118,23 @@ protected void onHide() {
restartButton.hide();
}
/**
* Updates the dialog's state. This method can be used for periodic updates while the dialog is active.
*/
public void update() {
if(!active) {
return;
}
}
/**
* Updates the resolution settings and optionally triggers the restart button if changes are detected.
*
* @param width The width of the resolution.
* @param height The height of the resolution.
* @param imageFactor The scaling factor for the resolution.
* @param isFullscreen {@code true} if fullscreen mode is enabled, {@code false} otherwise.
*/
public void updateResolution(int width, int height, float imageFactor, boolean isFullscreen) {
if(width != prefs.getInt("width", 1280) || height != prefs.getInt("height", 720) || isFullscreen != prefs.getBoolean("fullscreen", false)) {
restartButton.show();

View File

@@ -10,12 +10,25 @@
import pp.mdga.client.animation.ZoomControl;
import pp.mdga.game.Color;
/**
* The {@code ActionTextHandler} class manages the display of animated and stylized text messages in the game's UI.
* It supports dynamic text creation with spacing, color, and effects, such as dice rolls, player actions, and rankings.
*/
class ActionTextHandler {
private Node root;
private BitmapFont font;
private AppSettings appSettings;
private int ranking;
float paddingRanked = 100;
/**
* Constructs an {@code ActionTextHandler}.
*
* @param guiNode The GUI node where the text messages will be displayed.
* @param assetManager The asset manager used to load fonts and other assets.
* @param appSettings The application settings for positioning and sizing.
*/
ActionTextHandler(Node guiNode, AssetManager assetManager, AppSettings appSettings){
root = new Node("actionTextRoot");
guiNode.attachChild(root);
@@ -26,6 +39,16 @@ class ActionTextHandler {
ranking = 0;
}
/**
* Creates a {@code Node} containing text with specified spacing, size, and colors for each segment of the text.
*
* @param textArr An array of strings representing the text to be displayed.
* @param spacing The spacing between individual characters.
* @param size The size of the text.
* @param colorArr An array of {@code ColorRGBA} representing the color for each string in {@code textArr}.
* @return A {@code Node} containing the styled text with spacing and color applied.
* @throws RuntimeException if the lengths of {@code textArr} and {@code colorArr} do not match.
*/
private Node createTextWithSpacing(String[] textArr, float spacing, float size, ColorRGBA[] colorArr) {
if(textArr.length != colorArr.length) throw new RuntimeException("text and color are not the same length");
@@ -52,18 +75,55 @@ private Node createTextWithSpacing(String[] textArr, float spacing, float size,
return textNode;
}
/**
* Creates a {@code Node} containing text with specified spacing, size, and a single color.
*
* @param text The text to be displayed.
* @param spacing The spacing between individual characters.
* @param size The size of the text.
* @param color The color of the text.
* @return A {@code Node} containing the styled text.
*/
private Node createTextWithSpacing(String text, float spacing, float size, ColorRGBA color) {
return createTextWithSpacing(new String[]{text}, spacing, size, new ColorRGBA[]{color});
}
/**
* Calculates the center position of a rectangle given its width, height, and an origin position.
*
* @param width The width of the rectangle.
* @param height The height of the rectangle.
* @param pos The origin position of the rectangle.
* @return A {@code Vector3f} representing the center position.
*/
private Vector3f center(float width, float height, Vector3f pos){
return new Vector3f(pos.x+width/2, pos.y+height/2,0);
}
/**
* Creates and positions a single-line text at the top of the screen with a specified vertical offset.
*
* @param name The text to be displayed.
* @param spacing The spacing between individual characters.
* @param size The size of the text.
* @param color The color of the text.
* @param top The vertical offset from the top of the screen.
* @return A {@code Node} containing the styled text positioned at the top.
*/
private Node createTopText(String name, float spacing, float size, ColorRGBA color, float top){
return createTopText(new String[]{name}, spacing, size, new ColorRGBA[]{color}, top);
}
/**
* Creates and positions multi-line text at the top of the screen with specified vertical offset, spacing, and colors.
*
* @param name An array of strings representing the text to be displayed.
* @param spacing The spacing between individual characters.
* @param size The size of the text.
* @param color An array of {@code ColorRGBA} representing the color for each string in {@code name}.
* @param top The vertical offset from the top of the screen.
* @return A {@code Node} containing the styled text positioned at the top.
*/
private Node createTopText(String[] name, float spacing, float size, ColorRGBA color[], float top){
Node text = createTextWithSpacing(name, spacing, size, color);
text.setLocalTranslation(0, (appSettings.getHeight()/2f)*0.8f-top,0);
@@ -71,18 +131,44 @@ private Node createTopText(String[] name, float spacing, float size, ColorRGBA c
return text;
}
/**
* Calculates the center position of a rectangle with negative width offset.
*
* @param width The negative width of the rectangle.
* @param height The height of the rectangle.
* @param pos The origin position of the rectangle.
* @return A {@code Vector3f} representing the center position.
*/
private Vector3f centerText(float width, float height, Vector3f pos){
return center(-width, height, pos);
}
/**
* Displays a message indicating the active player.
*
* @param name The name of the active player.
* @param color The color representing the player's team.
*/
void activePlayer(String name, Color color){
createTopText(new String[]{name," ist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
/**
* Displays a message indicating that the current player is active.
*
* @param color The color representing the player's team.
*/
void ownActive(Color color){
createTopText(new String[]{"Du"," bist dran"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
/**
* Displays a dice roll result for a player.
*
* @param diceNum The number rolled on the dice.
* @param name The name of the player.
* @param color The color representing the player's team.
*/
void diceNum(int diceNum, String name, Color color){
createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0);
@@ -90,38 +176,84 @@ void diceNum(int diceNum, String name, Color color){
}
/**
* Displays a dice roll result with a multiplier for a player.
*
* @param diceNum The number rolled on the dice.
* @param mult The multiplier applied to the dice result.
* @param name The name of the player.
* @param color The color representing the player's team.
*/
void diceNumMult(int diceNum,int mult, String name, Color color){
createTopText(new String[]{name," würfelt:"}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0);
createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 100);
}
/**
* Displays the dice roll result for the current player.
*
* @param diceNum The number rolled on the dice.
*/
void ownDice(int diceNum){
createTopText(String.valueOf(diceNum), 10, 100, ColorRGBA.White, 0);
}
/**
* Displays the dice roll result with a multiplier for the current player.
*
* @param diceNum The number rolled on the dice.
* @param mult The multiplier applied to the dice result.
*/
void ownDiceMult(int diceNum, int mult){
createTopText(new String[]{String.valueOf(diceNum), " x" + mult + " = " + (diceNum*mult)}, 20, 100, new ColorRGBA[]{ColorRGBA.White,ColorRGBA.Red}, 0);
}
/**
* Displays a message indicating that a specified player received a bonus card.
*
* @param name The name of the player who received the bonus card.
* @param color The color representing the player's team.
*/
void drawCard(String name, Color color){
createTopText(new String[]{name," erhält eine Bonuskarte"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
/**
* Displays a message indicating that the current player received a bonus card.
*
* @param color The color representing the player's team.
*/
void drawCardOwn(Color color){
createTopText(new String[]{"Du"," erhälst eine Bonuskarte"}, 5,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
/**
* Displays a message indicating that a specified player has completed their turn or action.
*
* @param name The name of the player who finished.
* @param color The color representing the player's team.
*/
void finishText(String name, Color color){
createTopText(new String[]{name," ist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
/**
* Displays a message indicating that the current player has completed their turn or action.
*
* @param color The color representing the player's team.
*/
void finishTextOwn(Color color){
createTopText(new String[]{"Du", " bist fertig!"}, 7,70, new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, 0).addControl(new ZoomControl());
}
/**
* Converts a player's team color to a corresponding {@code ColorRGBA}.
*
* @param color The player's team color.
* @return The corresponding {@code ColorRGBA}.
* @throws RuntimeException if the color is invalid.
*/
private ColorRGBA playerColorToColorRGBA(Color color){
return switch (color){
case ARMY -> ColorRGBA.Green;
@@ -132,25 +264,41 @@ private ColorRGBA playerColorToColorRGBA(Color color){
};
}
/**
* Hides all text messages displayed by the handler and resets the ranking counter.
*/
void hide(){
ranking = 0;
root.detachAllChildren();
}
float paddingRanked = 100;
/**
* Displays a ranked dice roll result for a specified player.
*
* @param name The name of the player.
* @param color The color representing the player's team.
* @param eye The dice roll result.
*/
void rollRankingResult(String name, Color color, int eye){
createTopText(new String[]{name,": "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking);
ranking++;
}
/**
* Displays a ranked dice roll result for the current player.
*
* @param color The color representing the player's team.
* @param eye The dice roll result.
*/
void rollRankingResultOwn(Color color, int eye){
createTopText(new String[]{"Du",": "+eye}, 10,90,new ColorRGBA[]{playerColorToColorRGBA(color),ColorRGBA.White}, paddingRanked*ranking);
ranking++;
}
/**
* Displays a message prompting the player to roll the dice.
*/
void diceNow(){
createTopText("Klicke zum Würfeln", 5, 80, ColorRGBA.White, 0);
}
}

View File

@@ -122,13 +122,13 @@ public void noConfirm() {
confirmButton.hide();
}
public void needNoPower() {
public void showNoPower() {
confirmButton.hide();
noPowerButton.show();
}
public void noNoPower() {
noPowerButton.show();
public void hideNoPower() {
noPowerButton.hide();
}
public void enterInterrupt(Color color) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,4 +1,6 @@
world 0,0 90
treesBigBackground 0,0 90
treesSmallBackground 0,0 90
#Marine Pos
@@ -56,7 +58,8 @@ big_tent -10,-9 130
big_tent 9,-10 225
radar 0,10 -20
tank -1,-10 135
tank 0,-18 180
#tank 0,-18 180
tank_shoot 0,-18 180
tank 3,-18 180
tank -3,-18 180
@@ -270,3 +273,4 @@ treeBig 12,22 360

View File

@@ -1,7 +1,6 @@
// Samplers for textures
uniform sampler2D m_Texture;
uniform sampler2D m_OutlineDepthTexture;
uniform sampler2D m_DepthTexture;
// Input texture coordinates from the vertex shader
in vec2 texCoord;
@@ -15,26 +14,25 @@ uniform float m_OutlineWidth;
out vec4 fragColor;
void main() {
// Sample depth textures
// Sample depth textures at various offsets
vec4 depth = texture(m_OutlineDepthTexture, texCoord);
vec4 depth1 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth2 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth3 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(-m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth4 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(-m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth5 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(0.0, m_OutlineWidth)) / m_Resolution);
vec4 depth6 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(0.0, -m_OutlineWidth)) / m_Resolution);
vec4 depth7 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(m_OutlineWidth, 0.0)) / m_Resolution);
vec4 depth8 = texture(m_OutlineDepthTexture, ((texCoord * m_Resolution) + vec2(-m_OutlineWidth, 0.0)) / m_Resolution);
vec4 depth1 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth2 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth3 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth4 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth5 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, m_OutlineWidth)) / m_Resolution);
vec4 depth6 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, -m_OutlineWidth)) / m_Resolution);
vec4 depth7 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, 0.0)) / m_Resolution);
vec4 depth8 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, 0.0)) / m_Resolution);
// Sample the main texture
vec4 color = texture(m_Texture, texCoord);
// Determine whether to apply the outline color
if (depth == vec4(0.0) &&
(depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth ||
depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)) {
fragColor = m_OutlineColor; // Apply outline color
} else {
fragColor = color; // Use the original texture color
}
// Check if an outline should be applied
bool isEdge = (depth == vec4(0.0)) &&
(depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth ||
depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth);
// Output the final color
fragColor = isEdge ? m_OutlineColor : color;
}

View File

@@ -1,11 +1,10 @@
// Use 'in' instead of 'varying' for inputs from the vertex shader
// Input texture coordinates from the vertex shader
in vec2 texCoord;
// Declare a custom output variable for the fragment color
// Output variable for the fragment color
out vec4 fragColor;
// Uniform samplers
// Uniform samplers for textures
uniform sampler2D m_Texture;
uniform sampler2D m_NormalsTexture;
uniform sampler2D m_DepthTexture;
@@ -13,6 +12,7 @@ uniform sampler2D m_DepthTexture;
void main() {
// Sample the texture at the given texture coordinates
vec4 color = texture(m_Texture, texCoord);
// Assign the color to the output variable
fragColor = color;
}

View File

@@ -1,109 +1,78 @@
// Uniform samplers
uniform sampler2D m_Texture;
uniform sampler2D m_OutlineDepthTexture;
uniform sampler2D m_DepthTexture;
varying vec2 texCoord;
// Input texture coordinates from the vertex shader
in vec2 texCoord;
// Uniforms for resolution, outline color, and width
uniform vec2 m_Resolution;
uniform vec4 m_OutlineColor;
uniform float m_OutlineWidth;
// Output variable for fragment color
out vec4 fragColor;
void main() {
vec4 depth = texture2D(m_OutlineDepthTexture, texCoord);
vec4 depth1 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,m_OutlineWidth))/m_Resolution);
vec4 depth2 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,-m_OutlineWidth))/m_Resolution);
vec4 depth3 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,m_OutlineWidth))/m_Resolution);
vec4 depth4 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,-m_OutlineWidth))/m_Resolution);
vec4 depth5 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,m_OutlineWidth))/m_Resolution);
vec4 depth6 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,-m_OutlineWidth))/m_Resolution);
vec4 depth7 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(m_OutlineWidth,0.))/m_Resolution);
vec4 depth8 = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-m_OutlineWidth,0.))/m_Resolution);
vec4 color = texture2D(m_Texture, texCoord);
//如果是背景
float ratio=0.;
if(depth==vec4(0.) && (depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth||depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)){
float dist=m_OutlineWidth;
//距离边的像素
vec4 nearDepth;
if(depth1 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(i,i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth2 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(i,-i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth3 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-i,i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth4 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-i,-i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth5 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth6 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(0.,-i))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth7 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(i,0.))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}else
if(depth8 != depth){
for(float i=0.;i<m_OutlineWidth;i++){
nearDepth = texture2D(m_OutlineDepthTexture, ((texCoord*m_Resolution)+vec2(-i,0.))/m_Resolution);
if(nearDepth != depth){
dist = i;
break;
}
}
}
//0:场景颜色 1:outline颜色
ratio = clamp(1.- dist/m_OutlineWidth,0.,1.);
//float off = (1.-ratio*ratio)*(1.-ratio*ratio);
gl_FragColor = color*(1.-ratio) +m_OutlineColor*ratio;
//gl_FragColor = m_OutlineColor;
}else{
gl_FragColor = color;
}
//debug
//gl_FragColor = vec4(0.,(1.-ratio),0.,1.);
}
vec4 depth = texture(m_OutlineDepthTexture, texCoord);
vec4 depth1 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth2 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth3 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, m_OutlineWidth)) / m_Resolution);
vec4 depth4 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, -m_OutlineWidth)) / m_Resolution);
vec4 depth5 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, m_OutlineWidth)) / m_Resolution);
vec4 depth6 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, -m_OutlineWidth)) / m_Resolution);
vec4 depth7 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(m_OutlineWidth, 0.0)) / m_Resolution);
vec4 depth8 = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-m_OutlineWidth, 0.0)) / m_Resolution);
vec4 color = texture(m_Texture, texCoord);
float ratio = 0.0;
if (depth == vec4(0.0) &&
(depth1 != depth || depth2 != depth || depth3 != depth || depth4 != depth ||
depth5 != depth || depth6 != depth || depth7 != depth || depth8 != depth)) {
float dist = m_OutlineWidth;
vec4 nearDepth;
// Iterate to find the distance to the nearest edge
for (float i = 0.0; i < m_OutlineWidth; i++) {
if (depth1 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(i, i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth2 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(i, -i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth3 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-i, i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth4 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-i, -i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth5 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth6 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(0.0, -i)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth7 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(i, 0.0)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
} else if (depth8 != depth) {
nearDepth = texture(m_OutlineDepthTexture, (texCoord * m_Resolution + vec2(-i, 0.0)) / m_Resolution);
if (nearDepth != depth) { dist = i; break; }
}
}
// Calculate ratio for outline blending
ratio = clamp(1.0 - dist / m_OutlineWidth, 0.0, 1.0);
// Blend the outline color with the base color
fragColor = color * (1.0 - ratio) + m_OutlineColor * ratio;
} else {
// No outline, use the base texture color
fragColor = color;
}
// Optional: Debugging outline visualization
// fragColor = vec4(0.0, 1.0 - ratio, 0.0, 1.0);
}

View File

@@ -1,8 +1,8 @@
// Use 'in' for vertex attributes
// Vertex attributes
in vec4 inPosition;
in vec2 inTexCoord;
// Use 'out' for passing data to the fragment shader
// Output to fragment shader
out vec2 texCoord;
void main() {

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,554 @@
# Blender 3.6.5
# www.blender.org
o tank_top
v 0.136104 -4.382030 2.447842
v 0.136104 -4.313100 2.681757
v -0.107755 -4.382030 2.447842
v -0.107755 -4.313100 2.681757
v 0.136104 -5.171963 2.680617
v 0.136104 -5.103033 2.914532
v -0.107755 -5.171963 2.680617
v -0.107755 -5.103033 2.914532
v 0.104217 -1.266581 1.563035
v 0.104217 -1.215678 1.735777
v -0.075869 -1.266581 1.563035
v -0.075869 -1.215678 1.735777
v 0.104218 -5.321341 2.757877
v 0.104218 -5.270438 2.930620
v -0.075868 -5.321341 2.757877
v -0.075868 -5.270438 2.930620
v 0.170290 -1.697908 1.621255
v 0.170290 -1.609653 1.920754
v -0.141942 -1.697908 1.621255
v -0.141942 -1.609653 1.920754
v 0.229706 -1.077489 1.376490
v 0.229706 -0.955645 1.789975
v -0.201358 -1.077489 1.376490
v -0.201358 -0.955645 1.789975
v 0.514900 0.276165 1.806222
v 0.514900 0.588869 2.433130
v -0.442998 0.276164 1.806222
v -0.442998 0.588869 2.433130
v 0.514900 0.342851 1.772958
v 0.514900 0.655556 2.399867
v -0.442998 0.342851 1.772958
v -0.442998 0.655556 2.399867
v -0.571633 -0.809063 1.704162
v -0.571633 -0.809063 2.393599
v -0.723737 -0.809063 1.704162
v -0.723737 -0.809063 2.393599
v -0.571633 -0.656959 1.704161
v -0.571633 -0.656959 2.393600
v -0.723737 -0.656959 1.704161
v -0.723737 -0.656959 2.393600
v -0.950927 -0.897974 2.389141
v -0.950927 -0.803313 2.389141
v -0.861627 -0.897974 2.604730
v -0.861627 -0.803313 2.604730
v -0.646038 -0.897974 2.694029
v -0.646038 -0.803313 2.694029
v -0.430449 -0.897974 2.604729
v -0.430449 -0.803313 2.604730
v -0.341149 -0.897974 2.389141
v -0.341149 -0.803313 2.389141
v -0.430449 -0.897974 2.173552
v -0.430449 -0.803313 2.173552
v -0.646038 -0.897974 2.084251
v -0.646038 -0.803313 2.084251
v -0.861627 -0.897974 2.173552
v -0.861627 -0.803313 2.173552
v -0.799948 -0.357073 2.051851
v -0.784767 0.368207 2.051851
v -0.423762 0.888095 2.051851
v 0.525389 0.907164 2.051851
v 0.864924 0.401350 2.051851
v 0.849742 -0.323930 2.051851
v 0.387428 -1.063397 2.051851
v -0.368214 -1.078578 2.051851
v -1.026731 -0.445682 1.384443
v -1.007409 0.477460 1.384443
v -0.547920 1.139177 1.384443
v 0.660166 1.163448 1.384443
v 1.092329 0.519645 1.384443
v 1.073006 -0.403497 1.384443
v 0.484569 -1.344695 1.384443
v -0.477218 -1.364018 1.384443
v -1.077985 -0.465708 1.887314
v -1.057726 0.502151 1.887314
v -0.575980 1.195922 1.887314
v 0.690626 1.221368 1.887314
v 1.143722 0.546380 1.887314
v 1.123464 -0.421479 1.887314
v 0.506523 -1.408269 1.887314
v -0.501853 -1.428528 1.887314
v -1.085964 0.516008 1.535923
v -0.591726 1.227766 1.535923
v 0.707720 1.253873 1.535923
v 1.172564 0.561383 1.535923
v 1.151780 -0.431571 1.535923
v 0.518844 -1.443946 1.535923
v -0.515678 -1.464730 1.535923
v -1.106748 -0.476946 1.535923
v 0.612842 -0.142856 2.090828
v 0.612842 0.599197 2.091472
v -0.540940 -0.142856 2.090828
v -0.540940 0.599197 2.091472
v 0.612842 -0.142788 2.011893
v 0.612842 0.599266 2.012537
v -0.540940 -0.142788 2.011893
v -0.540940 0.599265 2.012537
v 0.445467 -0.035209 2.095495
v 0.445467 0.491550 2.095952
v -0.373565 -0.035210 2.095495
v -0.373565 0.491550 2.095952
vn -0.0000 0.9592 -0.2827
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.9592 0.2827
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.2827 -0.9592
vn -0.0000 0.2827 0.9592
vn -0.9960 -0.0855 0.0252
vn 0.9960 -0.0855 0.0252
vn -0.0000 -0.3670 -0.9302
vn -0.0000 0.1961 0.9806
vn -0.0000 -0.8949 0.4464
vn -0.0000 0.8949 -0.4464
vn -0.0000 -0.4464 -0.8949
vn -0.0000 0.4464 0.8949
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn -0.0000 -0.0000 1.0000
vn -0.9239 -0.0000 0.3827
vn -0.3827 -0.0000 0.9239
vn 0.3827 -0.0000 0.9239
vn 0.9239 -0.0000 0.3827
vn 0.9239 -0.0000 -0.3827
vn 0.3827 -0.0000 -0.9239
vn -0.3827 -0.0000 -0.9239
vn -0.9239 -0.0000 -0.3827
vn -0.5124 0.0107 0.8587
vn -0.3944 0.2739 0.8772
vn -0.0094 0.4678 0.8838
vn 0.3870 0.2598 0.8847
vn 0.5124 -0.0107 0.8587
vn 0.4253 -0.2659 0.8651
vn 0.0086 -0.4282 0.9037
vn -0.4187 -0.2505 0.8729
vn -0.8549 -0.5116 0.0863
vn 0.0200 -0.9946 0.1017
vn 0.8450 -0.5283 0.0833
vn 0.9965 -0.0209 0.0809
vn 0.8268 0.5550 0.0916
vn -0.0200 0.9956 0.0911
vn -0.8182 0.5681 0.0882
vn -0.9965 0.0209 0.0809
vn -0.8856 0.0185 -0.4640
vn -0.7133 0.4953 -0.4959
vn -0.0173 0.8609 -0.5085
vn 0.7140 0.4793 -0.5103
vn 0.8856 -0.0185 -0.4640
vn 0.7463 -0.4666 -0.4746
vn 0.0168 -0.8346 -0.5506
vn -0.7489 -0.4482 -0.4881
vn -0.0000 -0.0009 1.0000
vn -0.0000 0.0009 -1.0000
vn -0.0000 -1.0000 -0.0009
vn -0.0000 1.0000 0.0009
vt 0.302708 0.924112
vt 0.288040 0.909443
vt 0.302708 0.909443
vt 0.693823 0.755933
vt 0.708491 0.805468
vt 0.693821 0.805468
vt 0.933974 0.202686
vt 0.919330 0.188017
vt 0.933973 0.188017
vt 0.651344 0.857452
vt 0.636674 0.807918
vt 0.651343 0.807918
vt 0.134278 0.805715
vt 0.119609 0.855249
vt 0.119609 0.805715
vt 0.708491 0.751615
vt 0.693871 0.702081
vt 0.708540 0.702096
vt 0.766441 0.570082
vt 0.755608 0.559249
vt 0.766441 0.559249
vt 0.537931 0.359392
vt 0.548757 0.613657
vt 0.537924 0.613657
vt 0.781573 0.570082
vt 0.770759 0.559250
vt 0.781572 0.559250
vt 0.579073 0.613652
vt 0.568233 0.359392
vt 0.579066 0.359392
vt 0.563915 0.359392
vt 0.553082 0.613655
vt 0.553082 0.359392
vt 0.437696 0.622253
vt 0.448781 0.368077
vt 0.448529 0.622264
vt 0.615148 0.975854
vt 0.633896 0.957072
vt 0.633897 0.975854
vt 0.948652 0.917591
vt 0.926304 0.957587
vt 0.929870 0.917591
vt 0.106854 0.798464
vt 0.132782 0.772534
vt 0.132782 0.798464
vt 0.952676 0.496495
vt 0.930312 0.536496
vt 0.926746 0.496495
vt 0.938021 0.614748
vt 0.960377 0.574830
vt 0.956803 0.614748
vt 0.948620 0.913273
vt 0.926304 0.873209
vt 0.952234 0.873235
vt 0.288040 0.924112
vt 0.708492 0.755933
vt 0.919331 0.202686
vt 0.636675 0.857452
vt 0.134278 0.855249
vt 0.693822 0.751601
vt 0.755609 0.570082
vt 0.548764 0.359392
vt 0.770759 0.570082
vt 0.568241 0.613652
vt 0.563915 0.613655
vt 0.437949 0.368066
vt 0.615148 0.957072
vt 0.952234 0.957587
vt 0.106853 0.772534
vt 0.949094 0.536496
vt 0.934447 0.574830
vt 0.929838 0.913255
vt 0.040925 0.927951
vt 0.002159 0.870340
vt 0.040923 0.870330
vt 0.834247 0.352865
vt 0.838729 0.310724
vt 0.838729 0.352865
vt 0.508750 0.936735
vt 0.546231 0.879133
vt 0.546232 0.936754
vt 0.843047 0.310724
vt 0.847530 0.352865
vt 0.843047 0.352865
vt 0.697810 0.640142
vt 0.693822 0.697763
vt 0.693822 0.640142
vt 0.323097 0.582179
vt 0.319198 0.524554
vt 0.323154 0.524558
vt 0.768084 0.828693
vt 0.777234 0.870091
vt 0.768084 0.870092
vt 0.499788 0.814544
vt 0.508937 0.856016
vt 0.499788 0.856016
vt 0.218712 0.819723
vt 0.227862 0.861192
vt 0.218712 0.861192
vt 0.420031 0.812900
vt 0.429180 0.854372
vt 0.420031 0.854372
vt 0.785891 0.559250
vt 0.795040 0.568399
vt 0.785891 0.568399
vt 0.537924 0.700847
vt 0.547080 0.691706
vt 0.547071 0.700856
vt 0.966064 0.285237
vt 0.971814 0.298192
vt 0.966120 0.298192
vt 0.453257 0.607137
vt 0.458792 0.620111
vt 0.453099 0.620105
vt 0.453125 0.594168
vt 0.458949 0.607142
vt 0.961680 0.298192
vt 0.956052 0.285237
vt 0.961746 0.285237
vt 0.961746 0.311174
vt 0.955987 0.298192
vt 0.578003 0.963076
vt 0.583636 0.950107
vt 0.583697 0.963076
vt 0.713986 0.956996
vt 0.737947 0.947070
vt 0.747872 0.971032
vt 0.578003 0.937139
vt 0.577943 0.950107
vt 0.971758 0.311174
vt 0.966064 0.311174
vt 0.307851 0.954728
vt 0.317762 0.944793
vt 0.331777 0.978655
vt 0.921986 0.878344
vt 0.905406 0.827575
vt 0.921986 0.820132
vt 0.884350 0.795866
vt 0.893886 0.777817
vt 0.827291 0.793524
vt 0.817744 0.774692
vt 0.789645 0.814701
vt 0.806235 0.823505
vt 0.789645 0.872913
vt 0.833108 0.912166
vt 0.825506 0.933015
vt 0.878533 0.914030
vt 0.886125 0.935503
vt 0.036904 0.863435
vt 0.057955 0.805815
vt 0.058039 0.865604
vt 0.893784 0.134399
vt 0.914942 0.195798
vt 0.893712 0.196631
vt 0.011369 0.804651
vt 0.032412 0.863875
vt 0.011276 0.866012
vt 0.032503 0.745834
vt 0.011370 0.744910
vt 0.032435 0.705568
vt 0.011300 0.703600
vt 0.297104 0.788044
vt 0.318245 0.865182
vt 0.297086 0.866212
vt 0.057959 0.746102
vt 0.036897 0.705526
vt 0.058032 0.703600
vt 0.036826 0.746953
vt 0.067083 0.748470
vt 0.067078 0.803985
vt 0.067151 0.708957
vt 0.288040 0.863346
vt 0.288056 0.790674
vt 0.002181 0.709075
vt 0.002246 0.747480
vt 0.002245 0.803021
vt 0.002159 0.860067
vt 0.884977 0.194314
vt 0.885044 0.136458
vt 0.067157 0.859570
vt 0.709979 0.282916
vt 0.754532 0.352364
vt 0.709910 0.352320
vt 0.702128 0.640146
vt 0.706914 0.684776
vt 0.702166 0.684781
vt 0.415360 0.813107
vt 0.370725 0.882511
vt 0.370725 0.813107
vt 0.427272 0.808484
vt 0.432057 0.763854
vt 0.432020 0.808488
vt 0.542664 0.617975
vt 0.537927 0.687379
vt 0.537924 0.617975
vt 0.457848 0.589850
vt 0.453099 0.520446
vt 0.457847 0.520446
vt 0.919379 0.134399
vt 0.951006 0.183699
vt 0.919330 0.183667
vt 0.002161 0.927961
vt 0.834247 0.310724
vt 0.508749 0.879113
vt 0.847530 0.310724
vt 0.697810 0.697763
vt 0.319140 0.582175
vt 0.777234 0.828693
vt 0.508937 0.814544
vt 0.227862 0.819722
vt 0.429180 0.812900
vt 0.795040 0.559250
vt 0.537933 0.691697
vt 0.971758 0.285237
vt 0.458817 0.594174
vt 0.956052 0.311174
vt 0.723911 0.980958
vt 0.713986 0.971033
vt 0.723911 0.947070
vt 0.747872 0.956995
vt 0.737947 0.980958
vt 0.583697 0.937139
vt 0.331777 0.944776
vt 0.341688 0.954686
vt 0.341688 0.968720
vt 0.317762 0.978673
vt 0.307851 0.968762
vt 0.905406 0.871198
vt 0.806235 0.867128
vt 0.036821 0.805157
vt 0.915012 0.135139
vt 0.032502 0.804065
vt 0.318263 0.788989
vt 0.754601 0.282960
vt 0.706876 0.640142
vt 0.415360 0.882511
vt 0.427309 0.763850
vt 0.542666 0.687379
vt 0.453100 0.589850
vt 0.951055 0.134431
s 1
f 3/1/1 2/2/1 1/3/1
f 7/4/2 4/5/2 3/6/2
f 5/7/3 8/8/3 7/9/3
f 1/10/4 6/11/4 5/12/4
f 1/13/5 7/14/5 3/15/5
f 8/16/6 2/17/6 4/18/6
f 11/19/1 10/20/1 9/21/1
f 15/22/2 12/23/2 11/24/2
f 13/25/3 16/26/3 15/27/3
f 9/28/4 14/29/4 13/30/4
f 9/31/5 15/32/5 11/33/5
f 14/34/6 12/35/6 16/36/6
f 18/37/3 19/38/3 17/39/3
f 20/40/7 23/41/7 19/42/7
f 24/43/1 21/44/1 23/45/1
f 22/46/8 17/47/8 21/48/8
f 19/49/9 21/50/9 17/51/9
f 20/52/10 22/53/10 24/54/10
f 3/1/1 4/55/1 2/2/1
f 7/4/2 8/56/2 4/5/2
f 5/7/3 6/57/3 8/8/3
f 1/10/4 2/58/4 6/11/4
f 1/13/5 5/59/5 7/14/5
f 8/16/6 6/60/6 2/17/6
f 11/19/1 12/61/1 10/20/1
f 15/22/2 16/62/2 12/23/2
f 13/25/3 14/63/3 16/26/3
f 9/28/4 10/64/4 14/29/4
f 9/31/5 13/65/5 15/32/5
f 14/34/6 10/66/6 12/35/6
f 18/37/3 20/67/3 19/38/3
f 20/40/7 24/68/7 23/41/7
f 24/43/1 22/69/1 21/44/1
f 22/46/8 18/70/8 17/47/8
f 19/49/9 23/71/9 21/50/9
f 20/52/10 18/72/10 22/53/10
f 25/73/11 28/74/11 27/75/11
f 27/76/2 32/77/2 31/78/2
f 32/79/12 29/80/12 31/81/12
f 30/82/4 25/83/4 29/84/4
f 31/85/13 25/86/13 27/87/13
f 32/88/14 26/89/14 30/90/14
f 34/91/15 35/92/15 33/93/15
f 36/94/2 39/95/2 35/96/2
f 40/97/16 37/98/16 39/99/16
f 38/100/4 33/101/4 37/102/4
f 35/103/17 37/104/17 33/105/17
f 36/106/18 38/107/18 40/108/18
f 43/109/19 42/110/19 41/111/19
f 45/112/20 44/113/20 43/114/20
f 47/115/21 46/116/21 45/112/21
f 49/117/22 48/118/22 47/119/22
f 51/120/23 50/121/23 49/117/23
f 51/122/24 54/123/24 52/124/24
f 46/125/16 50/126/16 54/127/16
f 55/128/25 54/123/25 53/129/25
f 41/111/26 56/130/26 55/131/26
f 45/132/15 43/133/15 51/134/15
f 73/135/27 58/136/27 74/137/27
f 74/137/28 59/138/28 75/139/28
f 75/139/29 60/140/29 76/141/29
f 60/140/30 77/142/30 76/141/30
f 61/143/31 78/144/31 77/142/31
f 78/144/32 63/145/32 79/146/32
f 61/143/18 60/140/18 59/138/18
f 79/146/33 64/147/33 80/148/33
f 64/147/34 73/135/34 80/148/34
f 80/149/35 88/150/35 87/151/35
f 86/152/36 80/153/36 87/154/36
f 85/155/37 79/156/37 86/157/37
f 77/158/38 85/155/38 84/159/38
f 76/160/39 84/159/39 83/161/39
f 82/162/40 76/163/40 83/164/40
f 81/165/41 75/166/41 82/167/41
f 88/150/42 74/168/42 81/165/42
f 88/150/43 66/169/43 65/170/43
f 81/165/44 67/171/44 66/169/44
f 82/162/45 68/172/45 67/173/45
f 68/174/46 84/159/46 69/175/46
f 69/175/47 85/155/47 70/176/47
f 85/155/48 71/177/48 70/176/48
f 86/152/49 72/178/49 71/179/49
f 72/180/50 88/150/50 65/170/50
f 89/181/51 92/182/51 91/183/51
f 92/184/2 95/185/2 91/186/2
f 96/187/52 93/188/52 95/189/52
f 93/190/4 90/191/4 89/192/4
f 95/193/53 89/194/53 91/195/53
f 96/196/54 90/197/54 94/198/54
f 97/199/51 100/200/51 99/201/51
f 25/73/11 26/202/11 28/74/11
f 27/76/2 28/203/2 32/77/2
f 32/79/12 30/204/12 29/80/12
f 30/82/4 26/205/4 25/83/4
f 31/85/13 29/206/13 25/86/13
f 32/88/14 28/207/14 26/89/14
f 34/91/15 36/208/15 35/92/15
f 36/94/2 40/209/2 39/95/2
f 40/97/16 38/210/16 37/98/16
f 38/100/4 34/211/4 33/101/4
f 35/103/17 39/212/17 37/104/17
f 36/106/18 34/213/18 38/107/18
f 43/109/19 44/214/19 42/110/19
f 45/112/20 46/116/20 44/113/20
f 47/115/21 48/215/21 46/116/21
f 49/117/22 50/121/22 48/118/22
f 51/120/23 52/216/23 50/121/23
f 51/122/24 53/129/24 54/123/24
f 42/217/16 44/218/16 46/125/16
f 46/125/16 48/219/16 50/126/16
f 50/126/16 52/220/16 54/127/16
f 54/127/16 56/221/16 42/217/16
f 42/217/16 46/125/16 54/127/16
f 55/128/25 56/222/25 54/123/25
f 41/111/26 42/110/26 56/130/26
f 43/133/15 41/223/15 51/134/15
f 41/223/15 55/224/15 51/134/15
f 55/224/15 53/225/15 51/134/15
f 51/134/15 49/226/15 47/227/15
f 47/227/15 45/132/15 51/134/15
f 73/135/27 57/228/27 58/136/27
f 74/137/28 58/136/28 59/138/28
f 75/139/29 59/138/29 60/140/29
f 60/140/30 61/143/30 77/142/30
f 61/143/31 62/229/31 78/144/31
f 78/144/32 62/229/32 63/145/32
f 59/138/18 58/136/18 61/143/18
f 58/136/18 57/228/18 62/229/18
f 61/143/18 58/136/18 62/229/18
f 57/228/18 64/147/18 63/145/18
f 63/145/18 62/229/18 57/228/18
f 79/146/33 63/145/33 64/147/33
f 64/147/34 57/228/34 73/135/34
f 80/149/35 73/230/35 88/150/35
f 86/152/36 79/231/36 80/153/36
f 85/155/37 78/232/37 79/156/37
f 77/158/38 78/232/38 85/155/38
f 76/160/39 77/158/39 84/159/39
f 82/162/40 75/233/40 76/163/40
f 81/165/41 74/168/41 75/166/41
f 88/150/42 73/230/42 74/168/42
f 88/150/43 81/165/43 66/169/43
f 81/165/44 82/167/44 67/171/44
f 82/162/45 83/164/45 68/172/45
f 68/174/46 83/161/46 84/159/46
f 69/175/47 84/159/47 85/155/47
f 85/155/48 86/157/48 71/177/48
f 86/152/49 87/154/49 72/178/49
f 72/180/50 87/151/50 88/150/50
f 89/181/51 90/234/51 92/182/51
f 92/184/2 96/235/2 95/185/2
f 96/187/52 94/236/52 93/188/52
f 93/190/4 94/237/4 90/191/4
f 95/193/53 93/238/53 89/194/53
f 96/196/54 92/239/54 90/197/54
f 97/199/51 98/240/51 100/200/51

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 MiB

After

Width:  |  Height:  |  Size: 13 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 MiB

File diff suppressed because it is too large Load Diff

View File

@@ -275,6 +275,11 @@ public void received(ActivePlayerMessage msg){
state.received(msg);
}
@Override
public void received(PossiblePieceMessage msg){
state.received(msg);
}
/**
* This method returns the current state
*

View File

@@ -94,6 +94,7 @@ public void received(ServerStartGameMessage msg) {
@Override
public void received(LobbyPlayerJoinedMessage msg) {
if (msg.getPlayer().getName().equals(logic.getOwnPlayerName())) {
System.out.println(msg.getId());
logic.setOwnPlayerId(msg.getId());
}
if (msg.isHost() && msg.getId() == logic.getOwnPlayerId()) {

View File

@@ -7,6 +7,8 @@
public class AnimationState extends GameStates {
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
private final GameState parent;
public AnimationState(ClientState parent, ClientGameLogic logic) {
@@ -16,7 +18,7 @@ public AnimationState(ClientState parent, ClientGameLogic logic) {
@Override
public void enter() {
LOGGER.log(System.Logger.Level.INFO, "Entering AnimationState");
}
@Override

View File

@@ -8,10 +8,7 @@
import pp.mdga.client.gamestate.determinestartplayerstate.RollRankingDiceState;
import pp.mdga.client.gamestate.determinestartplayerstate.WaitRankingState;
import pp.mdga.message.client.AnimationEndMessage;
import pp.mdga.message.server.ActivePlayerMessage;
import pp.mdga.message.server.DieMessage;
import pp.mdga.message.server.RankingResponseMessage;
import pp.mdga.message.server.RankingRollAgainMessage;
import pp.mdga.message.server.*;
public class DetermineStartPlayerState extends GameStates {
@@ -61,8 +58,8 @@ public void setState(DetermineStartPlayerStates state) {
if(this.state != null){
this.state.exit();
}
state.enter();
this.state = state;
this.state.enter();
}
@Override
@@ -80,6 +77,11 @@ public void received(DieMessage msg){
state.received(msg);
}
@Override
public void received(DiceNowMessage msg){
state.received(msg);
}
@Override
public void received(RankingRollAgainMessage msg){
state.received(msg);

View File

@@ -15,6 +15,9 @@
import java.util.UUID;
public abstract class GameStates extends ClientState {
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
public GameStates(ClientState parent, ClientGameLogic logic) {
super(parent, logic);
}
@@ -27,6 +30,7 @@ protected void handlePowerCard(PlayCardMessage msg) {
} else {
Piece ownPiece = logic.getGame().getPieceThroughUUID(msg.getPieces().get(0).getUuid());
Piece enemyPiece = logic.getGame().getPieceThroughUUID(msg.getPieces().get(1).getUuid());
LOGGER.log(System.Logger.Level.INFO, "Swapping");
int ownIndex = logic.getGame().getBoard().getInfieldIndexOfPiece(ownPiece);
logic.addNotification(new SwapPieceNotification(ownPiece.getUuid(), enemyPiece.getUuid()));
logic.getGame().getBoard().getInfield()[logic.getGame().getBoard().getInfieldIndexOfPiece(enemyPiece)].setOccupant(ownPiece);
@@ -38,7 +42,7 @@ protected void handlePowerCard(PlayCardMessage msg) {
protected void throwPiece(Piece piece) {
logic.getGame().getBoard().getInfield()[logic.getGame().getBoard().getInfieldIndexOfPiece(piece)].clearOccupant();
logic.getGame().getPlayerByColor(piece.getColor()).addWaitingPiece(piece);
logic.addNotification(new ThrowPieceNotification(piece.getUuid()));
logic.addNotification(new ThrowPieceNotification(piece.getUuid(), piece.getColor()));
logic.getGame().getPlayerByColor(piece.getColor()).getPlayerStatistic().increasePiecesBeingThrown();
logic.getGame().getGameStatistics().increasePiecesBeingThrown();
piece.setState(PieceState.WAITING);

View File

@@ -63,7 +63,7 @@ public void received(ActivePlayerMessage msg) {
@Override
public void received(MoveMessage msg) {
Piece pieceToMove = logic.getGame().getPieceThroughUUID(msg.getIdentifier());
Piece pieceToMove = logic.getGame().getPieceThroughUUID(msg.getPiece().getUuid());
if (msg.isHomeMove()) {
logic.addNotification(new HomeMoveNotification(pieceToMove.getUuid(), msg.getTargetIndex()));
logic.getGame().getBoard().getInfield()[logic.getGame().getBoard().getInfieldIndexOfPiece(pieceToMove)].clearOccupant();

View File

@@ -132,6 +132,11 @@ public void received(DieMessage msg){
state.received(msg);
}
@Override
public void received(PossiblePieceMessage msg){
state.received(msg);
}
public ChoosePieceState getChoosePiece() {
return choosePieceState;
}

View File

@@ -11,6 +11,7 @@
public class WaitingState extends GameStates {
private final GameState parent;
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
public WaitingState(ClientState parent, ClientGameLogic logic) {
super(parent, logic);
@@ -46,7 +47,6 @@ public void received(DieMessage msg) {
logic.getGame().getPlayerByColor(logic.getGame().getActiveColor()).getPlayerStatistic().increaseDiced6();
logic.getGame().getGameStatistics().increaseDiced6();
}
parent.setState(parent.getAnimation());
}
@Override
@@ -62,35 +62,24 @@ public void received(PlayCardMessage msg) {
public void received(ActivePlayerMessage msg) {
logic.addNotification(new ActivePlayerNotification(msg.getColor()));
logic.getGame().setActiveColor(msg.getColor());
parent.setState(parent.getAnimation());
parent.setState(parent.getTurn());
}
@Override
public void received(MoveMessage msg) {
Piece pieceToMove = logic.getGame().getPieceThroughUUID(msg.getIdentifier());
Piece piece = logic.getGame().getPieceThroughUUID(msg.getPiece().getUuid());
logic.getGame().getBoard().getInfield()[logic.getGame().getBoard().getInfieldIndexOfPiece(piece)].clearOccupant();
if (msg.isHomeMove()) {
logic.addNotification(new HomeMoveNotification(pieceToMove.getUuid(), msg.getTargetIndex()));
logic.getGame().getBoard().getInfield()[logic.getGame().getBoard().getInfieldIndexOfPiece(pieceToMove)].clearOccupant();
logic.getGame().getPlayerByColor(pieceToMove.getColor()).setPieceInHome(msg.getTargetIndex(), pieceToMove);
for (int i = msg.getTargetIndex() + 1; i < 4; i++) {
if (!logic.getGame().getPlayerByColor(pieceToMove.getColor()).getHomeNodes()[i].isOccupied()) {
pieceToMove.setState(PieceState.HOME);
break;
}
pieceToMove.setState(PieceState.HOMEFINISHED);
}
logic.addNotification(new HomeMoveNotification(piece.getUuid(), msg.getTargetIndex()));
logic.getGame().getPlayerByColor(piece.getColor()).setPieceInHome(msg.getTargetIndex(), piece);
} else {
if (logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].isOccupied()) {
throwPiece(logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].getOccupant());
logic.getGame().getPlayerByColor(logic.getGame().getActiveColor()).getPlayerStatistic().increasePiecesThrown();
logic.getGame().getGameStatistics().increasePiecesThrown();
}
if (logic.getGame().getPlayerByColor(pieceToMove.getColor()).getStartNodeIndex() == logic.getGame().getBoard().getInfieldIndexOfPiece(pieceToMove)) {
logic.addNotification(new MovePieceNotification(pieceToMove.getUuid(), msg.getTargetIndex(), true));
logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].setOccupant(pieceToMove);
} else {
logic.addNotification(new MovePieceNotification(pieceToMove.getUuid(), logic.getGame().getBoard().getInfieldIndexOfPiece(pieceToMove), msg.getTargetIndex()));
logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].setOccupant(pieceToMove);
int i = logic.getGame().getBoard().getInfieldIndexOfPiece(piece);
this.LOGGER.log(System.Logger.Level.INFO, "Received MoveMessage with start index: " + i);
logic.addNotification(new MovePieceNotification(msg.getPiece().getUuid(), logic.getGame().getBoard().getInfieldIndexOfPiece(piece), msg.getTargetIndex()));
Piece occ = logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].moveOccupant(piece);
if (occ != null){
logic.getGame().getPlayerByColor(occ.getColor()).addWaitingPiece(occ);
logic.addNotification(new ThrowPieceNotification(occ.getUuid(), piece.getColor()));
}
}
parent.setState(parent.getAnimation());

View File

@@ -44,9 +44,9 @@ public DetermineStartPlayerState getParent(){
@Override
public void enter() {
for(Map.Entry<Integer, Player> entry : logic.getGame().getPlayers().entrySet()){
//logic.addNotification(new WaitMoveNotification(entry.getValue().getPieces()[0].getUuid()));
logic.addNotification(new MovePieceNotification(entry.getValue().getPieces()[0].getUuid(), entry.getValue().getStartNodeIndex(), true));
logic.getGame().getBoard().getInfield()[entry.getValue().getStartNodeIndex()].setOccupant(entry.getValue().getPieces()[0]);
entry.getValue().getWaitingArea()[0] = null;
animationCounter++;
if(entry.getKey() == logic.getOwnPlayerId()){
logic.addNotification(new AcquireCardNotification(entry.getValue().getHandCards().get(0).getCard()));

View File

@@ -10,6 +10,8 @@
public class RollRankingDiceState extends DetermineStartPlayerStates {
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
private final DetermineStartPlayerState parent;
public RollRankingDiceState(ClientState parent, ClientGameLogic logic) {
@@ -19,11 +21,13 @@ public RollRankingDiceState(ClientState parent, ClientGameLogic logic) {
@Override
public void enter() {
LOGGER.log(System.Logger.Level.INFO, "Entering RollRankingDiceState");
logic.addNotification(new DiceNowNotification());
}
@Override
public void exit() {
LOGGER.log(System.Logger.Level.INFO, "Exiting RollRankingDiceState");
}
@Override

View File

@@ -9,6 +9,8 @@
public class WaitRankingState extends DetermineStartPlayerStates {
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
private final DetermineStartPlayerState parent;
private boolean canChange = false;
@@ -27,11 +29,13 @@ private void changeToIntro(){
@Override
public void enter() {
LOGGER.log(System.Logger.Level.INFO, "Entering WaitRankingState");
}
@Override
public void exit() {
canChange = false;
LOGGER.log(System.Logger.Level.INFO, "Exiting WaitRankingState");
}
@Override

View File

@@ -47,6 +47,7 @@ public void received(DieMessage msg){
@Override
public void selectAnimationEnd(){
logic.send(new AnimationEndMessage());
parent.setState(parent.getChoosePiece());
}
// @Override

View File

@@ -16,6 +16,8 @@
public class NoPieceState extends ChoosePieceStates {
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
private final ChoosePieceState parent;
public NoPieceState(ClientState parent, ClientGameLogic logic) {
@@ -25,7 +27,7 @@ public NoPieceState(ClientState parent, ClientGameLogic logic) {
@Override
public void enter() {
LOGGER.log(System.Logger.Level.INFO, "Entering NoPieceState");
}
@Override
@@ -35,14 +37,16 @@ public void exit() {
@Override
public void received(SelectPieceMessage msg) {
parent.setState(parent.getSelectPiece());
ArrayList<Piece> pieces = msg.getPieces().stream().map(piece -> logic.getGame().getPieceThroughUUID(piece)).collect(Collectors.toCollection(ArrayList::new));
ArrayList<Piece> pieces = msg.getPieces().stream().map(piece -> logic.getGame().getPieceThroughUUID(piece.getUuid())).collect(Collectors.toCollection(ArrayList::new));
parent.getSelectPiece().setPossiblePieces(pieces);
LOGGER.log(System.Logger.Level.INFO, "Received " + msg.getPieces().size() + " pieces");
logic.addNotification(new SelectableMoveNotification(pieces.stream().map(Piece::getUuid).collect(Collectors.toCollection(ArrayList::new)), msg.getTargetIndex(), msg.getIsHomeMove()));
parent.setState(parent.getSelectPiece());
}
@Override
public void received(WaitPieceMessage msg){
LOGGER.log(System.Logger.Level.INFO, "Received WaitPieceMessage");
logic.addNotification(new WaitMoveNotification(msg.getPieceID()));
parent.setState(parent.getWaitingPiece());
}
@@ -56,6 +60,8 @@ public void received(StartPieceMessage msg){
listPiece.add(piece.getUuid());
listIndex.add(msg.getTargetIndex());
homeMove.add(false);
parent.getStartPiece().setMoveablePiece(piece);
LOGGER.log(System.Logger.Level.INFO, "Received start piece " + listPiece.get(0));
logic.addNotification(new SelectableMoveNotification(listPiece, listIndex, homeMove));
parent.setState(parent.getStartPiece());
}

View File

@@ -4,10 +4,12 @@
import pp.mdga.client.ClientState;
import pp.mdga.client.gamestate.turnstate.ChoosePieceState;
import pp.mdga.game.Piece;
import pp.mdga.message.client.RequestMoveMessage;
import pp.mdga.message.client.SelectedPiecesMessage;
import pp.mdga.message.server.MoveMessage;
import pp.mdga.notification.HomeMoveNotification;
import pp.mdga.notification.MovePieceNotification;
import pp.mdga.notification.ThrowPieceNotification;
import java.util.ArrayList;
@@ -15,6 +17,7 @@ public class SelectPieceState extends ChoosePieceStates {
private final ChoosePieceState parent;
private ArrayList<Piece> possiblePieces;
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
public SelectPieceState(ClientState parent, ClientGameLogic logic) {
super(parent, logic);
@@ -37,27 +40,28 @@ public void setPossiblePieces(ArrayList<Piece> possiblePieces) {
@Override
public void selectPiece(Piece piece) {
ArrayList<Piece> pieces = new ArrayList<>();
if(possiblePieces.contains(piece)){
pieces.add(piece);
logic.send(new SelectedPiecesMessage(pieces));
logic.send(new RequestMoveMessage(piece));
}
}
@Override
public void received(MoveMessage msg) {
Piece piece = logic.getGame().getPieceThroughUUID(msg.getIdentifier());
Piece piece = logic.getGame().getPieceThroughUUID(msg.getPiece().getUuid());
logic.getGame().getBoard().getInfield()[logic.getGame().getBoard().getInfieldIndexOfPiece(piece)].clearOccupant();
if (msg.isHomeMove()) {
logic.addNotification(new HomeMoveNotification(piece.getUuid(), msg.getTargetIndex()));
logic.getGame().getBoard().getInfield()[logic.getGame().getBoard().getInfieldIndexOfPiece(piece)].clearOccupant();
logic.getGame().getPlayerByColor(piece.getColor()).setPieceInHome(msg.getTargetIndex(), piece);
} else {
if (logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].isOccupied()) {
throwPiece(logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].getOccupant());
logic.getGame().getPlayerByColor(logic.getGame().getActiveColor()).getPlayerStatistic().increasePiecesThrown();
logic.getGame().getGameStatistics().increasePiecesThrown();
int i = logic.getGame().getBoard().getInfieldIndexOfPiece(piece);
LOGGER.log(System.Logger.Level.INFO, "Received MoveMessage with start index: " + i);
logic.addNotification(new MovePieceNotification(msg.getPiece().getUuid(), logic.getGame().getBoard().getInfieldIndexOfPiece(piece), msg.getTargetIndex()));
Piece occ = logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].moveOccupant(piece);
if (occ != null){
logic.getGame().getPlayerByColor(occ.getColor()).addWaitingPiece(occ);
logic.addNotification(new ThrowPieceNotification(occ.getUuid(), piece.getColor()));
}
logic.addNotification(new MovePieceNotification(piece.getUuid(), logic.getGame().getBoard().getInfieldIndexOfPiece(piece), msg.getTargetIndex()));
parent.getParent().setState(parent.getParent().getMovePiece());
}
parent.getParent().setState(parent.getParent().getMovePiece());
}

View File

@@ -4,13 +4,18 @@
import pp.mdga.client.ClientState;
import pp.mdga.client.gamestate.turnstate.ChoosePieceState;
import pp.mdga.game.Piece;
import pp.mdga.message.client.RequestMoveMessage;
import pp.mdga.message.client.SelectedPiecesMessage;
import pp.mdga.message.server.MoveMessage;
import pp.mdga.notification.MovePieceNotification;
import pp.mdga.notification.ThrowPieceNotification;
import java.util.ArrayList;
public class StartPieceState extends ChoosePieceStates {
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
private final ChoosePieceState parent;
private Piece moveablePiece;
@@ -27,20 +32,31 @@ public void enter() {
@Override
public void exit() {
moveablePiece = null;
}
public void setMoveablePiece(Piece moveablePiece) {
this.moveablePiece = moveablePiece;
}
@Override
public void selectPiece(Piece piece){
ArrayList<Piece> pieces = new ArrayList<>();
if(moveablePiece.equals(piece)){
pieces.add(piece);
logic.send(new SelectedPiecesMessage(pieces));
logic.send(new RequestMoveMessage(piece));
}
}
@Override
public void received(MoveMessage msg){
Piece piece = logic.getGame().getPieceThroughUUID(msg.getPiece().getUuid());
int i = logic.getGame().getBoard().getInfieldIndexOfPiece(piece);
LOGGER.log(System.Logger.Level.INFO, "Received MoveMessage with start index: " + i);
logic.addNotification(new MovePieceNotification(msg.getPiece().getUuid(), logic.getGame().getBoard().getInfieldIndexOfPiece(piece), msg.getTargetIndex()));
Piece occ = logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].moveOccupant(piece);
if (occ != null){
logic.getGame().getPlayerByColor(occ.getColor()).addWaitingPiece(occ);
logic.addNotification(new ThrowPieceNotification(occ.getUuid(), piece.getColor()));
}
parent.getParent().setState(parent.getParent().getMovePiece());
}
}

View File

@@ -5,8 +5,11 @@
import pp.mdga.client.gamestate.turnstate.ChoosePieceState;
import pp.mdga.game.Piece;
import pp.mdga.game.PieceState;
import pp.mdga.message.client.RequestMoveMessage;
import pp.mdga.message.client.SelectedPiecesMessage;
import pp.mdga.message.server.MoveMessage;
import pp.mdga.notification.MovePieceNotification;
import pp.mdga.notification.ThrowPieceNotification;
import java.util.ArrayList;
import java.util.List;
@@ -32,16 +35,20 @@ public void exit() {
@Override
public void selectPiece(Piece piece){
ArrayList<Piece> pieces = new ArrayList<>();
if(moveablePiece.equals(piece)){
pieces.add(piece);
logic.send(new SelectedPiecesMessage(pieces));
logic.send(new RequestMoveMessage(piece));
}
}
@Override
public void received(MoveMessage msg){
Piece pieceToMove = logic.getGame().getPieceThroughUUID(msg.getIdentifier());
Piece pieceToMove = logic.getGame().getPieceThroughUUID(msg.getPiece().getUuid());
Piece occ = logic.getGame().getBoard().getInfield()[msg.getTargetIndex()].moveOccupant(pieceToMove);
logic.addNotification(new MovePieceNotification(pieceToMove.getUuid(), logic.getGame().getPlayerByColor(logic.getGame().getActiveColor()).getStartNodeIndex(), true));
if (occ != null){
logic.getGame().getPlayerByColor(occ.getColor()).addWaitingPiece(occ);
logic.addNotification(new ThrowPieceNotification(occ.getUuid(), pieceToMove.getColor()));
}
pieceToMove.setState(PieceState.ACTIVE);
parent.getParent().setState(parent.getParent().getMovePiece());
}

View File

@@ -106,11 +106,12 @@ public void received(DiceNowMessage msg){
@Override
public void received(PossiblePieceMessage msg){
if (msg.getEnemyPossiblePieces().isEmpty()){
parent.getShield().setPossiblePieces(msg.getOwnPossiblePieces().stream().map(piece -> logic.getGame().getPieceThroughUUID(piece)).collect(Collectors.toCollection(ArrayList::new)));
parent.getShield().setPossiblePieces(msg.getOwnPossiblePieces().stream().map(piece -> logic.getGame().getPieceThroughUUID(piece.getUuid())).collect(Collectors.toCollection(ArrayList::new)));
parent.setState(parent.getShield());
} else {
parent.getSwap().setPossibleOwnPieces(msg.getOwnPossiblePieces().stream().map(piece -> logic.getGame().getPieceThroughUUID(piece)).collect(Collectors.toCollection(ArrayList::new)));
parent.getSwap().setPossibleEnemyPieces(msg.getEnemyPossiblePieces().stream().map(piece -> logic.getGame().getPieceThroughUUID(piece)).collect(Collectors.toCollection(ArrayList::new)));
System.out.println("Should enter Swap State");
parent.getSwap().setPossibleOwnPieces(msg.getOwnPossiblePieces().stream().map(piece -> logic.getGame().getPieceThroughUUID(piece.getUuid())).collect(Collectors.toCollection(ArrayList::new)));
parent.getSwap().setPossibleEnemyPieces(msg.getEnemyPossiblePieces().stream().map(piece -> logic.getGame().getPieceThroughUUID(piece.getUuid())).collect(Collectors.toCollection(ArrayList::new)));
parent.setState(parent.getSwap());
}
}

View File

@@ -13,6 +13,7 @@
public class ShieldState extends PowerCardStates {
private final PowerCardState parent;
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
private ArrayList<Piece> possiblePieces;

View File

@@ -5,6 +5,8 @@
import pp.mdga.client.gamestate.turnstate.PowerCardState;
import pp.mdga.game.Piece;
import pp.mdga.message.client.RequestPlayCardMessage;
import pp.mdga.message.client.SelectCardMessage;
import pp.mdga.message.client.SelectedPiecesMessage;
import pp.mdga.message.server.PlayCardMessage;
import pp.mdga.notification.SelectableSwapNotification;
@@ -13,6 +15,8 @@
public class SwapState extends PowerCardStates {
private final System.Logger LOGGER = System.getLogger(this.getClass().getName());
private final PowerCardState parent;
private ArrayList<Piece> possibleOwnPieces;
@@ -29,6 +33,7 @@ public SwapState(ClientState parent, ClientGameLogic logic) {
@Override
public void enter() {
LOGGER.log(System.Logger.Level.INFO, "Entering SwapState");
ArrayList<UUID> ownPieces = new ArrayList<>(possibleOwnPieces.stream().map(Piece::getUuid).toList());
ArrayList<UUID> enemyPieces = new ArrayList<>(possibleEnemyPieces.stream().map(Piece::getUuid).toList());
logic.addNotification(new SelectableSwapNotification(ownPieces, enemyPieces));
@@ -36,6 +41,7 @@ public void enter() {
@Override
public void exit() {
LOGGER.log(System.Logger.Level.INFO, "Exiting SwapState");
possibleOwnPieces = null;
possibleEnemyPieces = null;
}
@@ -56,7 +62,10 @@ public void selectPiece(Piece piece){
selectedEnemyPiece = piece;
}
if (selectedOwnPiece != null && selectedEnemyPiece != null){
logic.send(RequestPlayCardMessage.requestPlaySwap(selectedOwnPiece.getUuid(), selectedEnemyPiece.getUuid()));
ArrayList<Piece> temp = new ArrayList<>();
temp.add(selectedOwnPiece);
temp.add(selectedEnemyPiece);
logic.send(new SelectedPiecesMessage(temp));
}
}

View File

@@ -79,8 +79,11 @@ private StartNode createStartNode(int i) {
*/
public int getInfieldIndexOfPiece(Piece piece) {
for (int i = 0; i < infield.length; i++) {
if (infield[i].getOccupant() == piece) {
return i;
System.out.println(infield[i].getOccupant());
if(infield[i].isOccupied()) {
if (infield[i].getOccupant().equals(piece)) {
return i;
}
}
}
return -1;

View File

@@ -71,7 +71,7 @@ public class Game {
/**
* The dice modifier.
*/
private int diceModifier;
private int diceModifier = 1;
/**
* The number of eyes on the dice.

View File

@@ -49,6 +49,25 @@ public void setOccupant(Piece occupant) {
this.occupant = occupant;
}
/**
* This method handles the event when a new occupant is moved to the node,
* it then returns the old occupant.
*
* @param newOccupant the new occupant of the node
* @return the old occupant of the node
*/
public Piece moveOccupant(Piece newOccupant) {
if (occupant == null) {
setOccupant(newOccupant);
return null;
} else {
occupant.setShield(ShieldState.NONE);
occupant.setState(PieceState.WAITING);
setOccupant(newOccupant);
return occupant;
}
}
/**
* This method is used to clear the node of its occupant
*/

View File

@@ -2,6 +2,7 @@
import com.jme3.network.serializing.Serializable;
import java.util.Objects;
import java.util.UUID;
/**
@@ -159,7 +160,9 @@ public boolean equals(Object obj) {
if (obj instanceof Piece) {
Piece piece = (Piece) obj;
return this.hashCode() == piece.hashCode();
System.out.println("own UUID: " + this.uuid + ". Other UUID: " + piece.uuid);
return Objects.equals(this.uuid.toString(), piece.uuid.toString());
}
return false;

View File

@@ -152,6 +152,57 @@ public PowerCard getPowerCardByType(BonusCard bonusCard) {
return null;
}
public Piece getWaitingPiece(){
for (Piece piece : this.waitingArea) {
if (piece != null){
return piece;
}
}
return null;
}
/**
* This method returns a boolean based on if the Player has a piece in its waiting area
*
* @return the boolean if the waiting area contains a piece
*/
public boolean hasPieceInWaitingArea(){
for (Piece piece : this.waitingArea) {
if (piece != null){
return true;
}
}
return false;
}
public int getHomeIndexOfPiece(Piece piece) {
for (int i = 0; i < Resources.MAX_PIECES; i++) {
if (this.homeNodes[i].getOccupant().equals(piece)) {
return i;
}
}
return -1;
}
public boolean pieceInsideOfHome(Piece piece) {
for (Node node : this.homeNodes) {
if (piece.equals(node.getOccupant())) {
return true;
}
}
return false;
}
public boolean isHomeFinished(Piece piece) {
for (int i = getHomeIndexOfPiece(piece); i < Resources.MAX_PIECES; i++) {
if (!this.homeNodes[i].isOccupied()) {
return false;
}
}
return true;
}
/**
* This method returns the give name of the Player
*

View File

@@ -1,6 +1,7 @@
package pp.mdga.message.client;
import com.jme3.network.serializing.Serializable;
import pp.mdga.game.Piece;
import java.util.UUID;
@@ -12,22 +13,22 @@ public class RequestMoveMessage extends ClientMessage {
/**
* The identifier for the piece.
*/
private final UUID pieceIdentifier;
private final Piece piece;
/**
* Constructor for RequestMove
*
* @param pieceIdentifier the piece identifier
* @param piece the piece
*/
public RequestMoveMessage(UUID pieceIdentifier) {
this.pieceIdentifier = pieceIdentifier;
public RequestMoveMessage(Piece piece) {
this.piece = piece;
}
/**
* Default constructor for serialization purposes.
*/
private RequestMoveMessage() {
pieceIdentifier = null;
piece = null;
}
/**
@@ -35,8 +36,8 @@ private RequestMoveMessage() {
*
* @return the piece identifier
*/
public UUID getPieceIdentifier() {
return pieceIdentifier;
public Piece getPiece() {
return piece;
}
/**
@@ -46,7 +47,8 @@ public UUID getPieceIdentifier() {
*/
@Override
public String toString() {
return "RequestMove{pieceIdentifier = " + pieceIdentifier + '}';
assert piece != null;
return "RequestMove{pieceIdentifier = " + piece.toString() + '}';
}
/**

View File

@@ -1,6 +1,7 @@
package pp.mdga.message.server;
import com.jme3.network.serializing.Serializable;
import pp.mdga.game.Piece;
import java.util.UUID;
@@ -12,7 +13,7 @@ public class MoveMessage extends ServerMessage {
/**
* The identifier of the piece that should be moved.
*/
private final UUID pieceUUID;
private final Piece piece;
/**
* The index of the target node;
@@ -27,11 +28,13 @@ public class MoveMessage extends ServerMessage {
/**
* Constructs a new MoveMessage instance.
*
* @param identifier the identifier of the piece that should be moved
* @param piece the identifier of the piece that should be moved
* @param isHomeMove boolean flag declaring home move or not
* @param targetIndex the targetIndex
*/
public MoveMessage(UUID identifier, boolean isHomeMove, int targetIndex) {
public MoveMessage(Piece piece, boolean isHomeMove, int targetIndex) {
super();
this.pieceUUID = identifier;
this.piece = piece;
this.isHomeMove = isHomeMove;
this.targetIndex = targetIndex;
}
@@ -41,7 +44,7 @@ public MoveMessage(UUID identifier, boolean isHomeMove, int targetIndex) {
*/
private MoveMessage() {
super();
pieceUUID = null;
piece = null;
targetIndex = 0;
isHomeMove = false;
}
@@ -51,8 +54,8 @@ private MoveMessage() {
*
* @return the identifier of the piece that should be moved
*/
public UUID getIdentifier() {
return pieceUUID;
public Piece getPiece() {
return piece;
}
/**
@@ -90,6 +93,6 @@ public void accept(ServerInterpreter interpreter) {
*/
@Override
public String toString() {
return "MoveMessage{" + "pieceUUID=" + pieceUUID + ", targetIndex=" + targetIndex + ", isHomeMove=" + isHomeMove + '}';
return "MoveMessage{" + "pieceUUID=" + piece.getUuid() + ", targetIndex=" + targetIndex + ", isHomeMove=" + isHomeMove + '}';
}
}

View File

@@ -1,7 +1,9 @@
package pp.mdga.message.server;
import com.jme3.network.serializing.Serializable;
import pp.mdga.game.Piece;
import java.io.PipedOutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
@@ -14,12 +16,12 @@ public class PossiblePieceMessage extends ServerMessage {
/**
* The list of possible own pieces
*/
private final List<UUID> possibleOwnPieces;
private final List<Piece> possibleOwnPieces;
/**
* The list of possible enemy pieces
*/
private final List<UUID> possibleEnemyPieces;
private final List<Piece> possibleEnemyPieces;
/**
* Constructor for PossiblePiece
@@ -37,7 +39,7 @@ public PossiblePieceMessage() {
* @param possibleEnemyPieces the list of possible enemy pieces
* @return the swapped possible pieces
*/
public static PossiblePieceMessage swapPossiblePieces(ArrayList<UUID> possibleOwnPieces, ArrayList<UUID> possibleEnemyPieces) {
public static PossiblePieceMessage swapPossiblePieces(List<Piece> possibleOwnPieces, List<Piece> possibleEnemyPieces) {
PossiblePieceMessage possiblePieceMessage = new PossiblePieceMessage();
possiblePieceMessage.possibleOwnPieces.addAll(possibleOwnPieces);
possiblePieceMessage.possibleEnemyPieces.addAll(possibleEnemyPieces);
@@ -50,7 +52,7 @@ public static PossiblePieceMessage swapPossiblePieces(ArrayList<UUID> possibleOw
* @param possibleOwnPieces the list of possible own pieces
* @return the possible pieces for the shield
*/
public static PossiblePieceMessage shieldPossiblePieces(ArrayList<UUID> possibleOwnPieces) {
public static PossiblePieceMessage shieldPossiblePieces(List<Piece> possibleOwnPieces) {
PossiblePieceMessage possiblePieceMessage = new PossiblePieceMessage();
possiblePieceMessage.possibleOwnPieces.addAll(possibleOwnPieces);
return possiblePieceMessage;
@@ -61,7 +63,7 @@ public static PossiblePieceMessage shieldPossiblePieces(ArrayList<UUID> possible
*
* @param piece the piece to add
*/
public void addOwnPossiblePiece(UUID piece) {
public void addOwnPossiblePiece(Piece piece) {
this.possibleOwnPieces.add(piece);
}
@@ -70,7 +72,7 @@ public void addOwnPossiblePiece(UUID piece) {
*
* @param piece the piece to add
*/
public void addEnemyPossiblePiece(UUID piece) {
public void addEnemyPossiblePiece(Piece piece) {
this.possibleEnemyPieces.add(piece);
}
@@ -79,7 +81,7 @@ public void addEnemyPossiblePiece(UUID piece) {
*
* @return the list of possible pieces
*/
public List<UUID> getOwnPossiblePieces() {
public List<Piece> getOwnPossiblePieces() {
return possibleOwnPieces;
}
@@ -88,7 +90,7 @@ public List<UUID> getOwnPossiblePieces() {
*
* @return the list of possible enemy pieces
*/
public List<UUID> getEnemyPossiblePieces() {
public List<Piece> getEnemyPossiblePieces() {
return possibleEnemyPieces;
}

View File

@@ -3,7 +3,7 @@
import com.jme3.network.serializing.Serializable;
import java.util.List;
import java.util.UUID;
import pp.mdga.game.Piece;
/**
* A message sent by the server to the active player to select a piece to move.
@@ -13,7 +13,7 @@ public class SelectPieceMessage extends ServerMessage {
/**
* The list of pieces
*/
private final List<UUID> pieces;
private final List<Piece> pieces;
/**
* The list of booleans of isHomeMove of the pieces
@@ -32,7 +32,7 @@ public class SelectPieceMessage extends ServerMessage {
* @param isHomeMove the List of booleans of isHomeMove of the pieces
* @param targetIndex the List of indexes of target nodes of the pieces
*/
public SelectPieceMessage(List<UUID> pieces, List<Boolean> isHomeMove, List<Integer> targetIndex) {
public SelectPieceMessage(List<Piece> pieces, List<Boolean> isHomeMove, List<Integer> targetIndex) {
super();
this.pieces = pieces;
this.isHomeMove = isHomeMove;
@@ -54,7 +54,7 @@ public SelectPieceMessage() {
*
* @return the pieces
*/
public List<UUID> getPieces() {
public List<Piece> getPieces() {
return pieces;
}

View File

@@ -1,5 +1,7 @@
package pp.mdga.notification;
import pp.mdga.game.Color;
import java.util.UUID;
/**
@@ -10,12 +12,14 @@ public class ThrowPieceNotification extends Notification {
* The id of the piece that was thrown.
*/
private final UUID pieceId;
private final Color throwColor;
/**
* This constructor is used to create a new ThrowPieceNotification
*/
public ThrowPieceNotification(UUID pieceId) {
public ThrowPieceNotification(UUID pieceId, Color throwColor) {
this.pieceId = pieceId;
this.throwColor = throwColor;
}
/**
@@ -26,4 +30,8 @@ public ThrowPieceNotification(UUID pieceId) {
public UUID getPieceId() {
return pieceId;
}
public Color getThrowColor() {
return throwColor;
}
}

View File

@@ -1,9 +1,6 @@
package pp.mdga.server.automaton;
import pp.mdga.message.client.AnimationEndMessage;
import pp.mdga.message.client.DisconnectedMessage;
import pp.mdga.message.client.LeaveGameMessage;
import pp.mdga.message.client.RequestDieMessage;
import pp.mdga.message.client.*;
import pp.mdga.message.server.CeremonyMessage;
import pp.mdga.message.server.PauseGameMessage;
import pp.mdga.server.automaton.game.AnimationState;
@@ -88,6 +85,24 @@ public void received(LeaveGameMessage msg, int from) {
}
}
@Override
public void received(NoPowerCardMessage msg, int from){
this.currentState.received(msg, from);
}
public void received(SelectCardMessage msg, int from){
currentState.received(msg, from);
}
public void received(RequestMoveMessage msg, int from){
this.currentState.received(msg, from);
}
@Override
public void received(SelectedPiecesMessage msg, int from) {
this.currentState.received(msg, from);
}
/**
* This method will be called whenever the server received a RequestDieMessage message.
* It will also get the client id of the player who send this message.

View File

@@ -52,7 +52,7 @@ public void initializeGame() {
for (var player : this.logic.getGame().getPlayers().values()) {
player.initialize();
player.addHandCard(this.logic.getGame().draw());
Piece piece = player.getWaitingArea()[0];
Piece piece = player.getPieces()[0];
player.getWaitingArea()[0] = null;
piece.setState(PieceState.ACTIVE);
this.logic.getGame().getBoard().getInfield()[player.getStartNodeIndex()].setOccupant(piece);

View File

@@ -54,7 +54,6 @@ public void received(AnimationEndMessage msg, int from) {
this.messageReceived.add(from);
if (this.messageReceived.size() == this.logic.getGame().getPlayers().size()) {
this.gameAutomaton.setCurrentState(this.gameAutomaton.getTurnState());
this.gameAutomaton.getTurnState().setCurrentState(this.gameAutomaton.getTurnState().getPowerCardState());
}
}
}

View File

@@ -79,9 +79,6 @@ else if (maximumRoll < entry.getValue()) {
this.playersHaveToRoll.clear();
this.playersHaveToRoll.add(entry.getKey());
}
else {
this.logic.getServerSender().send(entry.getKey(), new EndOfTurnMessage());
}
}
for (int id: this.playersHaveToRoll) {

View File

@@ -1,10 +1,7 @@
package pp.mdga.server.automaton.game;
import pp.mdga.game.Player;
import pp.mdga.message.client.NoPowerCardMessage;
import pp.mdga.message.client.RequestDieMessage;
import pp.mdga.message.client.RequestMoveMessage;
import pp.mdga.message.client.SelectedPiecesMessage;
import pp.mdga.message.client.*;
import pp.mdga.server.ServerGameLogic;
import pp.mdga.server.automaton.GameState;
import pp.mdga.server.automaton.game.turn.ChoosePieceState;
@@ -111,6 +108,15 @@ public void received(RequestMoveMessage msg, int from) {
this.currentState.received(msg, from);
}
@Override
public void received(AnimationEndMessage msg, int from) {
this.currentState.received(msg, from);
}
public void received(SelectCardMessage msg, int from){
currentState.received(msg, from);
}
/**
* This method will be used to return currentState attribute of TurnState class.
*

View File

@@ -34,17 +34,17 @@ public ChoosePieceState(TurnState turnAutomaton, ServerGameLogic logic) {
this.waitingPieceState = new WaitingPieceState(this, logic);
this.startPieceState = new StartPieceState(this, logic);
this.selectPieceState = new SelectPieceState(this, logic);
this.setCurrentState(this.noPieceState);
}
@Override
public void enter() {
LOGGER.log(System.Logger.Level.DEBUG, "Exited ChoosePieceState state.");
LOGGER.log(System.Logger.Level.DEBUG, "Entered ChoosePieceState state.");
this.setCurrentState(this.noPieceState);
}
@Override
public void exit() {
LOGGER.log(System.Logger.Level.DEBUG, "Entered ChoosePieceState state.");
LOGGER.log(System.Logger.Level.DEBUG, "Exited ChoosePieceState state.");
}
/**

View File

@@ -1,13 +1,24 @@
package pp.mdga.server.automaton.game.turn;
import pp.mdga.game.Color;
import pp.mdga.game.Player;
import pp.mdga.message.client.AnimationEndMessage;
import pp.mdga.message.server.ActivePlayerMessage;
import pp.mdga.message.server.DiceNowMessage;
import pp.mdga.message.server.EndOfTurnMessage;
import pp.mdga.message.server.SpectatorMessage;
import pp.mdga.server.ServerGameLogic;
import pp.mdga.server.automaton.game.TurnState;
import java.util.HashSet;
import java.util.Set;
public class MovePieceState extends TurnAutomatonState {
/**
* Create LobbyState constants.
*/
private static final System.Logger LOGGER = System.getLogger(MovePieceState.class.getName());
private Set<Player> finishedAnimations = new HashSet<>();
/**
* Constructs a server state of the specified game logic.
@@ -24,8 +35,37 @@ public void enter() {
LOGGER.log(System.Logger.Level.DEBUG, "Entered MovePieceState state.");
}
private void setActivePlayer(Color color) {
if (!logic.getGame().getPlayerByColor(color.next()).isFinished()) {
logic.getGame().setActiveColor(logic.getGame().getActiveColor().next());
logic.getServerSender().broadcast(new ActivePlayerMessage(color.next()));
} else {
setActivePlayer(color.next());
}
}
@Override
public void received(AnimationEndMessage msg, int from){
finishedAnimations.add(logic.getGame().getPlayerById(from));
if (finishedAnimations.size() == logic.getGame().getPlayers().size()) {
if (logic.getGame().getPlayerByColor(logic.getGame().getActiveColor()).isFinished()){
logic.getServerSender().send(logic.getGame().getPlayerIdByColor(logic.getGame().getActiveColor()), new SpectatorMessage());
setActivePlayer(logic.getGame().getActiveColor());
this.turnAutomaton.getGameAutomaton().setCurrentState(this.turnAutomaton.getGameAutomaton().getTurnState());
} else if (logic.getGame().getDiceEyes() == 6){
logic.getServerSender().send(logic.getGame().getPlayerIdByColor(logic.getGame().getActiveColor()), new DiceNowMessage());
this.turnAutomaton.setCurrentState(this.turnAutomaton.getRollDiceState());
} else {
logic.getServerSender().send(logic.getGame().getPlayerIdByColor(logic.getGame().getActiveColor()), new EndOfTurnMessage());
setActivePlayer(logic.getGame().getActiveColor());
this.turnAutomaton.getGameAutomaton().setCurrentState(this.turnAutomaton.getGameAutomaton().getTurnState());
}
}
}
@Override
public void exit() {
finishedAnimations.clear();
LOGGER.log(System.Logger.Level.DEBUG, "Exited MovePieceState state.");
}
}

View File

@@ -32,6 +32,7 @@ public PlayPowerCardState(TurnState turnAutomaton, ServerGameLogic logic) {
@Override
public void enter() {
LOGGER.log(System.Logger.Level.DEBUG, "Entered PlayPowerCardState state.");
}

Some files were not shown because too many files have changed in this diff Show More