diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java index 0e5a5611..0ae281c7 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/InputSynchronizer.java @@ -94,9 +94,7 @@ private void setupInput() { inputManager.addListener(actionListener, "Settings", "Forward", "RotateRightMouse", "Click", "Left", "Right", "Test"); inputManager.addListener(analogListener, "MouseLeft", "MouseRight", "MouseScrollUp", "MouseScrollDown"); } - - private boolean test = false; - + UUID p = null; /** * Handles action-based input events such as key presses and mouse clicks. */ @@ -151,8 +149,19 @@ else if(boardSelect != null) { if (name.equals("Right")) { isRotateRight = !isRotateRight; } - if(name.equals("Test") &&isPressed){ + if(name.equals("Testx") &&isPressed){ if(app.getView() instanceof GameView gameView){ + + if(p == null) { + p = UUID.randomUUID(); + gameView.getBoardHandler().addPlayer(Color.AIRFORCE,List.of(p,UUID.randomUUID(),UUID.randomUUID(),UUID.randomUUID())); + gameView.getBoardHandler().movePieceStartAnim(p,0); + gameView.getBoardHandler().movePieceAnim(p,0, 8); + } else { + gameView.getBoardHandler().throwBombAnim(p); + //gameView.getBoardHandler().movePieceStartAnim(p,0); + } + // gameView.getGuiHandler().rollRankingResult(Color.AIRFORCE, 1); // gameView.getGuiHandler().rollRankingResult(Color.ARMY, 2); // gameView.getGuiHandler().rollRankingResult(Color.NAVY, 3); @@ -162,9 +171,9 @@ else if(boardSelect != null) { // gameView.getBoardHandler().addPlayer(Color.AIRFORCE,List.of(p1,UUID.randomUUID(),UUID.randomUUID(),UUID.randomUUID())); // gameView.getBoardHandler().movePieceStartAnim(p1,0); - gameView.getGuiHandler().drawCard(Color.ARMY); - gameView.getGuiHandler().addCardOwn(BonusCard.SHIELD); - gameView.getGuiHandler().playCardOwn(BonusCard.SHIELD); + //gameView.getGuiHandler().drawCard(Color.ARMY); + //gameView.getGuiHandler().addCardOwn(BonusCard.SHIELD); + //gameView.getGuiHandler().playCardOwn(BonusCard.SHIELD); diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java index 2a66040a..04b9483f 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/NotificationSynchronizer.java @@ -138,7 +138,7 @@ private void handleGame(Notification notification) { } guiHandler.hideText(); } else if (notification instanceof ThrowPieceNotification n) { - boardHandler.throwPieceAnim(n.getPieceId()); + boardHandler.throwBombAnim(n.getPieceId()); } else if (notification instanceof NoShieldNotification n) { boardHandler.unshieldPiece(n.getPieceId()); } else if (notification instanceof PlayCardNotification n) { diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java index 264197d4..447bddb4 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/AcousticHandler.java @@ -109,6 +109,13 @@ public void playSound(MdgaSound sound) { case LEAVE: assets.add(new SoundAssetDelayVolume(SoundAsset.UI_SOUND2, 0.6f, 0.0f)); break; + case JET: + assets.add(new SoundAssetDelayVolume(SoundAsset.JET, 1.0f, 0.0f)); + break; + case EXPLOSION: + assets.add(new SoundAssetDelayVolume(SoundAsset.EXP, 1.0f, 4f)); + assets.add(new SoundAssetDelayVolume(SoundAsset.THUNDER, 1.0f, 4f)); + break; default: break; } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MdgaSound.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MdgaSound.java index 78898155..9bdfb54b 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MdgaSound.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/MdgaSound.java @@ -31,4 +31,6 @@ public enum MdgaSound { OTHER_CONNECTED, NOT_READY, LEAVE, + JET, + EXPLOSION, } diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/SoundAsset.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/SoundAsset.java index 24497300..ce90d750 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/SoundAsset.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/acoustic/SoundAsset.java @@ -28,6 +28,9 @@ enum SoundAsset { POWERUP("powerup.wav"), ROBOT_READY("robotReady.wav"), UNIT_READY("unitReady.wav"), + JET("jet-overhead.wav"), + EXP("exp.ogg"), + THUNDER("thunder.ogg"), CONNECTED("connected.wav"); private final String path; diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/Explosion.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/Explosion.java new file mode 100644 index 00000000..4fb01568 --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/Explosion.java @@ -0,0 +1,126 @@ +package pp.mdga.client.animation; + +import com.jme3.effect.ParticleEmitter; +import com.jme3.effect.ParticleMesh.Type; +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; + +public class Explosion { + + 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; + + /** + * Konstruktor für die Explosion. + * + * @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. + */ + public Explosion(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"); + mat.getAdditionalRenderState().setDepthWrite(false); + mat.getAdditionalRenderState().setDepthTest(false); + } + + /** + * Initialisiert den Partikel-Emitter für die Explosion. + */ + private void initializeEmitter() { + fire = new ParticleEmitter("Effect", Type.Triangle,50); + fire.setMaterial(mat); + fire.setStartColor(ColorRGBA.Yellow); + fire.setEndColor(ColorRGBA.Red); + fire.getParticleInfluencer().setInitialVelocity(new Vector3f(0.2f,0.2f,4f)); + 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(2.2f); + fire.setParticlesPerSec(0); + + fire.setLocalTranslation(location); + fire.move(0, 0, 45); + + smoke = new ParticleEmitter("Effect2", 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,0.7f)); + 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(5.5f); + smoke.setParticlesPerSec(0); + + smoke.setLocalTranslation(location); + smoke.move(0, 0, 45); + } + + /** + * Löst die Explosion aus. + */ + 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) {} + }); + } +} + diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/JetAnimation.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/JetAnimation.java new file mode 100644 index 00000000..fc339041 --- /dev/null +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/animation/JetAnimation.java @@ -0,0 +1,175 @@ +package pp.mdga.client.animation; + +import com.jme3.material.Material; +import com.jme3.math.FastMath; +import com.jme3.math.Vector3f; +import com.jme3.renderer.RenderManager; +import com.jme3.renderer.ViewPort; +import com.jme3.renderer.queue.RenderQueue; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.control.AbstractControl; +import pp.mdga.client.Asset; +import pp.mdga.client.MdgaApp; +import pp.mdga.client.acoustic.MdgaSound; +import pp.mdga.client.board.BoardHandler; +import pp.mdga.client.view.GameView; + +import java.util.UUID; + +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 Explosion explosion; + private final UUID id; + + /** + * Konstruktor für die ThrowAnimation-Klasse. + * + * @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 + */ + public JetAnimation(MdgaApp app, Node rootNode, UUID uuid, Vector3f targetPoint, float curveHeight, float animationDuration) { + Vector3f spawnPoint = targetPoint.add(170, 50, 50); + + Vector3f controlPoint = targetPoint.add(new Vector3f(0, 0, -45)); + + Vector3f despawnPoint = targetPoint.add(-100, -100, 40); + + this.app = app; + this.rootNode = rootNode; + this.spawnPoint = spawnPoint; + this.nodePoint = controlPoint; + this.despawnPoint = despawnPoint; + this.curveHeight = curveHeight; + this.animationDuration = animationDuration; + + id = uuid; + + explosion = new Explosion(app, rootNode, nodePoint); + } + + /** + * Startet die Animation. + */ + public void start() { + app.getAcousticHandler().playSound(MdgaSound.JET); + spawnJet(); + animateJet(); + } + + /** + * Spawnt den Jet an der spezifizierten Position. + */ + private void spawnJet() { + jetModel = app.getAssetManager().loadModel(Asset.jet.getModelPath()); + jetModel.setLocalTranslation(spawnPoint); + jetModel.scale(Asset.jet.getSize()); + jetModel.rotate(FastMath.HALF_PI, 0, 0); + jetModel.setShadowMode(RenderQueue.ShadowMode.CastAndReceive); + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md"); + mat.setTexture("DiffuseMap", app.getAssetManager().loadTexture(Asset.jet.getDiffPath())); + jetModel.setMaterial(mat); + + rootNode.attachChild(jetModel); + + app.getAcousticHandler().playSound(MdgaSound.EXPLOSION); + } + + /** + * Animiert den Jet entlang einer Kurve und lässt ihn anschließend verschwinden. + */ + private void animateJet() { + Vector3f controlPoint1 = spawnPoint.add(0, curveHeight, 0); + Vector3f controlPoint2 = nodePoint.add(0, curveHeight, 0); + + BezierCurve3f curve = new BezierCurve3f(spawnPoint, controlPoint1, controlPoint2, despawnPoint); + + app.getRootNode().addControl(new AbstractControl() { + private float elapsedTime = 0; + + @Override + protected void controlUpdate(float tpf) { + elapsedTime += tpf; + float progress = elapsedTime / animationDuration; + + if(elapsedTime > 4.2f) { + explosion.trigger(); + } + + if (progress > 1) { + rootNode.detachChild(jetModel); + this.spatial.removeControl(this); + } else { + Vector3f currentPos = curve.interpolate(progress); + Vector3f direction = curve.interpolateDerivative(progress).normalizeLocal(); + jetModel.setLocalTranslation(currentPos); + jetModel.lookAt(currentPos.add(direction), Vector3f.UNIT_Z); + jetModel.rotate(-FastMath.HALF_PI, 0, (float) Math.toRadians(-25)); + } + + if (elapsedTime > 6.0f) { + GameView gameView = (GameView) app.getView(); + BoardHandler boardHandler = gameView.getBoardHandler(); + + boardHandler.throwPieceAnim(id); + } + } + + @Override + protected void controlRender(RenderManager rm, ViewPort vp) { + // Wird hier nicht benötigt + } + }); + } + + /** + * Repräsentiert eine 3D-Bezier-Kurve mit vier Kontrollpunkten. + */ + private static class BezierCurve3f { + private final Vector3f p0, p1, p2, p3; + + public BezierCurve3f(Vector3f p0, Vector3f p1, Vector3f p2, Vector3f p3) { + this.p0 = p0; + this.p1 = p1; + this.p2 = p2; + this.p3 = p3; + } + + public Vector3f interpolate(float t) { + float u = 1 - t; + float tt = t * t; + float uu = u * u; + float uuu = uu * u; + float ttt = tt * t; + + Vector3f point = p0.mult(uuu); + point = point.add(p1.mult(3 * uu * t)); + point = point.add(p2.mult(3 * u * tt)); + point = point.add(p3.mult(ttt)); + return point; + } + + public Vector3f interpolateDerivative(float t) { + float u = 1 - t; + float tt = t * t; + + Vector3f derivative = p0.mult(-3 * u * u); + derivative = derivative.add(p1.mult(3 * u * u - 6 * u * t)); + derivative = derivative.add(p2.mult(6 * u * t - 3 * tt)); + derivative = derivative.add(p3.mult(3 * tt)); + return derivative; + } + } +} diff --git a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java index a00508e8..a63db8ec 100644 --- a/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java +++ b/Projekte/mdga/client/src/main/java/pp/mdga/client/board/BoardHandler.java @@ -10,9 +10,9 @@ import pp.mdga.client.Asset; import pp.mdga.client.MdgaApp; import pp.mdga.client.animation.MoveControl; +import pp.mdga.client.animation.JetAnimation; import pp.mdga.client.gui.DiceControl; import pp.mdga.game.Color; -import pp.mdga.game.Piece; import java.util.*; @@ -720,10 +720,20 @@ 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. + * + * @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(); } /** diff --git a/Projekte/mdga/client/src/main/resources/Sounds/exp.ogg b/Projekte/mdga/client/src/main/resources/Sounds/exp.ogg new file mode 100644 index 00000000..b9c570c7 Binary files /dev/null and b/Projekte/mdga/client/src/main/resources/Sounds/exp.ogg differ diff --git a/Projekte/mdga/client/src/main/resources/Sounds/jet-overhead.wav b/Projekte/mdga/client/src/main/resources/Sounds/jet-overhead.wav new file mode 100644 index 00000000..32fcee18 Binary files /dev/null and b/Projekte/mdga/client/src/main/resources/Sounds/jet-overhead.wav differ diff --git a/Projekte/mdga/client/src/main/resources/Sounds/thunder.ogg b/Projekte/mdga/client/src/main/resources/Sounds/thunder.ogg new file mode 100644 index 00000000..db30aaec Binary files /dev/null and b/Projekte/mdga/client/src/main/resources/Sounds/thunder.ogg differ