diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/GameSound.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/GameSound.java index 9785023..56d5df0 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/GameSound.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/GameSound.java @@ -7,6 +7,10 @@ package pp.battleship.client; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.prefs.Preferences; + import com.jme3.app.Application; import com.jme3.app.state.AbstractAppState; import com.jme3.app.state.AppStateManager; @@ -14,13 +18,9 @@ import com.jme3.asset.AssetLoadException; import com.jme3.asset.AssetNotFoundException; import com.jme3.audio.AudioData; import com.jme3.audio.AudioNode; + import pp.battleship.notification.GameEventListener; import pp.battleship.notification.SoundEvent; - -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.prefs.Preferences; - import static pp.util.PreferencesUtils.getPreferences; /** @@ -34,7 +34,6 @@ public class GameSound extends AbstractAppState implements GameEventListener { private AudioNode splashSound; private AudioNode shipDestroyedSound; private AudioNode explosionSound; - private AudioNode shellFlyingSound; /** * Checks if sound is enabled in the preferences. @@ -79,7 +78,6 @@ public class GameSound extends AbstractAppState implements GameEventListener { shipDestroyedSound = loadSound(app, "Sound/Effects/sunken.wav"); //NON-NLS splashSound = loadSound(app, "Sound/Effects/splash.wav"); //NON-NLS explosionSound = loadSound(app, "Sound/Effects/explosion.wav"); //NON-NLS - shellFlyingSound = loadSound(app, "Sound/Effects/shell_flying.wav"); } /** @@ -125,20 +123,6 @@ public class GameSound extends AbstractAppState implements GameEventListener { if (isEnabled() && shipDestroyedSound != null) shipDestroyedSound.playInstance(); } - /** - * Plays the shell flying sound effect. - */ - public void shellFly() { - if (isEnabled() && shellFlyingSound != null) {S - shellFlyingSound.playInstance(); - } - } - - /** - * Handles a recieved {@code SoundEvent} and plays the according sound. - * - * @param event the Sound event to be processed - */ @Override public void receivedEvent(SoundEvent event) { diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/EditorAppState.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/EditorAppState.java index 3341238..ee36529 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/EditorAppState.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/EditorAppState.java @@ -7,19 +7,19 @@ package pp.battleship.client.gui; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; + import com.jme3.input.controls.ActionListener; import com.jme3.math.Vector2f; import com.jme3.scene.Node; import com.jme3.system.AppSettings; import com.simsilica.lemur.Button; import com.simsilica.lemur.Container; -import pp.battleship.client.BattleshipAppState; - -import java.lang.System.Logger; -import java.lang.System.Logger.Level; import static pp.battleship.Resources.lookup; import static pp.battleship.client.BattleshipApp.CLICK; +import pp.battleship.client.BattleshipAppState; /** * EditorState manages the editor mode in the Battleship game, diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/MapViewSynchronizer.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/MapViewSynchronizer.java index 4d4d2e2..e38b332 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/MapViewSynchronizer.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/MapViewSynchronizer.java @@ -7,19 +7,20 @@ package pp.battleship.client.gui; +import com.jme3.material.Material; +import static com.jme3.material.Materials.UNSHADED; +import com.jme3.material.RenderState; +import com.jme3.material.RenderState.BlendMode; import com.jme3.math.ColorRGBA; import com.jme3.scene.Geometry; import com.jme3.scene.Node; -import com.jme3.material.Material; -import com.jme3.material.RenderState; -import com.jme3.material.RenderState.BlendMode; +import com.jme3.scene.Spatial; import com.jme3.scene.shape.Sphere; -import com.jme3.scene.Spatial; import pp.battleship.model.Battleship; +import pp.battleship.model.Shell; import pp.battleship.model.Shot; import pp.util.Position; -import static com.jme3.material.Materials.UNSHADED; /** * Synchronizes the visual representation of the ship map with the game model. @@ -148,6 +149,6 @@ class MapViewSynchronizer extends ShipMapSynchronizer { ellipse.setMaterial(mat); ellipse.addControl(new Shell2DControl(view, shell)); return ellipse; - } + } } diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ParticleEffectFactory.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ParticleEffectFactory.java index f5355a8..34d3c2b 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ParticleEffectFactory.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ParticleEffectFactory.java @@ -232,7 +232,7 @@ public class ParticleEffectFactory { ParticleEmitter smokeEmitter = new ParticleEmitter("SmokeEmitter", Type.Triangle, 300); smokeEmitter.setGravity(0, 0, 0); smokeEmitter.getParticleInfluencer().setVelocityVariation(1); - smokeEmitter.setLocalTranslation(0, 2f, 0); + smokeEmitter.setLocalTranslation(0, 2f, 0); //___________________ smokeEmitter.setLowLife(1); smokeEmitter.setHighLife(1); smokeEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0)); @@ -243,7 +243,7 @@ public class ParticleEffectFactory { return smokeEmitter; } - public ParticleEmitter createWaterSplash() { + public ParticleEmitter createWaterSplash() { //wird durch explosions animation ersetzt // Create a new particle emitter for the splash effect ParticleEmitter waterSplash = new ParticleEmitter("WaterSplash", Type.Triangle, 30); diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SeaSynchronizer.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SeaSynchronizer.java index d14279e..6fc2083 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SeaSynchronizer.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/SeaSynchronizer.java @@ -26,9 +26,9 @@ import com.jme3.scene.shape.Cylinder; import pp.battleship.client.BattleshipApp; import pp.battleship.model.Battleship; import pp.battleship.model.Rotation; +import pp.battleship.model.Shell; import pp.battleship.model.ShipMap; import pp.battleship.model.Shot; -import pp.battleship.model.Shell; import static pp.util.FloatMath.HALF_PI; import static pp.util.FloatMath.PI; @@ -52,6 +52,7 @@ class SeaSynchronizer extends ShipMapSynchronizer { private static final ColorRGBA BOX_COLOR = ColorRGBA.Gray; private static final ColorRGBA SPLASH_COLOR = new ColorRGBA(0f, 0f, 1f, 0.4f); private static final ColorRGBA HIT_COLOR = new ColorRGBA(1f, 0f, 0f, 0.4f); + private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md"; private final ShipMap map; private final BattleshipApp app; diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShellControl.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShellControl.java index a551f6a..7ed8c49 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShellControl.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/ShellControl.java @@ -4,8 +4,8 @@ import com.jme3.math.Vector3f; import com.jme3.renderer.RenderManager; import com.jme3.renderer.ViewPort; import com.jme3.scene.control.AbstractControl; -import pp.battleship.model.Shell; +import pp.battleship.model.Shell; import static pp.util.FloatMath.PI; /** diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/server/BattleshipSelfhostServer.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/server/BattleshipSelfhostServer.java index e3eab1c..e59a63a 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/server/BattleshipSelfhostServer.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/server/BattleshipSelfhostServer.java @@ -1,4 +1,4 @@ -/////////////////////////////////////// +//////////////////////////////////////// // Programming project code // UniBw M, 2022, 2023, 2024 // www.unibw.de/inf2 @@ -28,6 +28,7 @@ import pp.battleship.BattleshipConfig; import pp.battleship.game.server.Player; import pp.battleship.game.server.ServerGameLogic; import pp.battleship.game.server.ServerSender; +import pp.battleship.message.client.AnimationFinishedMessage; import pp.battleship.message.client.ClientMessage; import pp.battleship.message.client.MapMessage; import pp.battleship.message.client.ShootMessage; @@ -64,7 +65,7 @@ public class BattleshipSelfhostServer implements MessageListener isInBounds(ship) && ship.getPositions().stream().allMatch(belegt::add)); } - - // public void displayError(String message) { - // System.out.println("Fehlermeldung: " + message); - // // Optional: GUI-Element aktualisieren, um die Nachricht anzuzeigen - // } - + + /** + * Handles the reception of a AnimationFinishedMessage. + * + * @param msg the received MapMessage + * @param from the ID of the sender client + */ + @Override + public void received(AnimationFinishedMessage msg, int from) { + if (state != ServerState.ANIMATION) { + LOGGER.log(Level.ERROR, "animation finished not allowed in {0}", state); + } + else { + LOGGER.log(Level.DEBUG, "anim received from {0}", getPlayerById(from)); + Player player = getPlayerById(from); + if (!waitPlayers.add(player)) { + LOGGER.log(Level.ERROR, "{0} already sent animation finished", player); //NON-NLS + return; + } + if (waitPlayers.size() == 2) { + waitPlayers = new HashSet<>(); + setState(ServerState.BATTLE); + } + } } +} + + diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/game/server/ServerState.java b/Projekte/battleship/model/src/main/java/pp/battleship/game/server/ServerState.java index 507aa7a..e8ad028 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/game/server/ServerState.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/game/server/ServerState.java @@ -29,5 +29,10 @@ enum ServerState { /** * The game has ended because all the ships of one player have been destroyed. */ - GAME_OVER + GAME_OVER, + + /** + * The server is waiting for all clients to finish the shoot animation. + */ + ANIMATION } diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/game/singlemode/Copycat.java b/Projekte/battleship/model/src/main/java/pp/battleship/game/singlemode/Copycat.java index f6e89be..4b288b7 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/game/singlemode/Copycat.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/game/singlemode/Copycat.java @@ -7,6 +7,7 @@ package pp.battleship.game.singlemode; +import pp.battleship.message.client.AnimationFinishedMessage; import pp.battleship.message.client.ClientInterpreter; import pp.battleship.message.client.ClientMessage; import pp.battleship.message.client.MapMessage; @@ -72,4 +73,10 @@ class Copycat implements ClientInterpreter { private static Battleship copy(Battleship ship) { return new Battleship(ship.getLength(), ship.getX(), ship.getY(), ship.getRot()); } + + @Override + public void received(AnimationFinishedMessage msg, int from) { + copiedMessage = msg; + } + } diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/message/client/AnimationFinishedMessage.java b/Projekte/battleship/model/src/main/java/pp/battleship/message/client/AnimationFinishedMessage.java new file mode 100644 index 0000000..529ae5c --- /dev/null +++ b/Projekte/battleship/model/src/main/java/pp/battleship/message/client/AnimationFinishedMessage.java @@ -0,0 +1,25 @@ +package pp.battleship.message.client; + +import com.jme3.network.serializing.Serializable; + +/** + * Represents a message indicating that an animation has finished on the client side. + */ +@Serializable +public class AnimationFinishedMessage extends ClientMessage { + public AnimationFinishedMessage() { + super(); + } + + /** + * Accepts a visitor to process this message. + * + * @param interpreter the visitor to process this message + * @param from the connection ID from which the message was received + */ + @Override + public void accept(ClientInterpreter interpreter, int from) { + interpreter.received(this, from); + } +} + diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/message/client/ClientInterpreter.java b/Projekte/battleship/model/src/main/java/pp/battleship/message/client/ClientInterpreter.java index 6a50c1d..f1b332b 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/message/client/ClientInterpreter.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/message/client/ClientInterpreter.java @@ -26,4 +26,12 @@ public interface ClientInterpreter { * @param from the connection ID from which the message was received */ void received(MapMessage msg, int from); + + /** + * Processes a received AnimationFinishedMessage. + * + * @param msg the MapMessage to be processed + * @param from the connection ID from which the message was received + */ + void received(AnimationFinishedMessage msg, int from); } diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/model/Shell.java b/Projekte/battleship/model/src/main/java/pp/battleship/model/Shell.java new file mode 100644 index 0000000..60d8a51 --- /dev/null +++ b/Projekte/battleship/model/src/main/java/pp/battleship/model/Shell.java @@ -0,0 +1,126 @@ +package pp.battleship.model; + +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; + +/** + * The {@code Shell} class represents a projectile fired by a ship in the Battleship game. + * It models the position and rotation of the projectile and allows for its movement along + * a Bezier curve. + */ +public class Shell implements Item { + /** + * Initial position of the shell + */ + private final static Vector3f INIT_POS = new Vector3f(-3, 7, -3); + /** + * The overshot difference vector used to get a shallower flight path + */ + private final static Vector3f OVER_SHOT_DIFF = new Vector3f(-1, -1, -1); + // Target shot position + private final Vector3f shotPosition; + // Position on top of shotPosition used for Bezier curve + private final Vector3f overShotPosition; + // Current position of the shell + private Vector3f position; + // Current rotation of the shell + private final Quaternion rotation; + + /** + * Constructs a new {@code Shell} object using the given {@code Shot} target. + * The initial position, target position, and overshot position are calculated. + * + * @param shot The target {@code Shot} object containing the destination coordinates. + */ + public Shell(Shot shot) { + this.shotPosition = new Vector3f(shot.getX(), 0, shot.getY()); + this.overShotPosition = new Vector3f(shotPosition.x, INIT_POS.y, shotPosition.z).add(OVER_SHOT_DIFF); + this.position = INIT_POS; + this.rotation = new Quaternion(); + } + + /** + * Gets the current position of the shell. + * + * @return The current position as a {@code Vector3f}. + */ + public Vector3f getPosition() { + return this.position; + } + + /** + * Gets the current rotation of the shell. + * + * @return The current rotation as a {@code Quaternion}. + */ + public Quaternion getRotation() { + return this.rotation; + } + + /** + * Moves the shell along a Bezier curve based on the given time factor {@code t}. + * The position and rotation of the shell are updated. + * + * @param t The time factor between 0 and 1, representing the progress of the shell's flight. + */ + public void move(float t) { + if (t > 1f) t = 1f; + Vector3f newPosition = bezInt(INIT_POS, overShotPosition, shotPosition, t); + updateRotation(position, newPosition); + this.position = newPosition; + } + + /** + * Performs a quadratic Bezier interpolation between three points based on the time factor {@code t}. + * + * @param p1 The start position. + * @param p2 The overshot position. + * @param p3 The target position. + * @param t The time factor for interpolation. + * @return The interpolated position as a {@code Vector3f}. + */ + private Vector3f bezInt(Vector3f p1, Vector3f p2, Vector3f p3, float t) { + Vector3f inA = linInt(p1, p2, t); + Vector3f inB = linInt(p2, p3, t); + return linInt(inA, inB, t); + } + + /** + * Performs linear interpolation between two points {@code p1} and {@code p2} based on the time factor {@code t}. + * + * @param p1 The start position. + * @param p2 The end position. + * @param t The time factor for interpolation. + * @return The interpolated position as a {@code Vector3f}. + */ + private Vector3f linInt(Vector3f p1, Vector3f p2, float t) { + float x = p1.getX() + t * (p2.getX() - p1.getX()); + float y = p1.getY() + t * (p2.getY() - p1.getY()); + float z = p1.getZ() + t * (p2.getZ() - p1.getZ()); + return new Vector3f(x, y, z); + } + + /** + * Updates the rotation of the shell to face the new position along its flight path. + * + * @param oldPos The previous position of the shell. + * @param newPos The new position of the shell. + */ + private void updateRotation(Vector3f oldPos, Vector3f newPos) { + Vector3f direction = newPos.subtract(oldPos).normalize(); + if (direction.lengthSquared() > 0) { + this.rotation.lookAt(direction, Vector3f.UNIT_Y); + } + } + + @Override + public T accept(Visitor visitor) { + return visitor.visit(this); + } + + @Override + public void accept(VoidVisitor visitor) { + visitor.visit(this); + } +} + diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/model/ShipMap.java b/Projekte/battleship/model/src/main/java/pp/battleship/model/ShipMap.java index 59a2164..63c0ff7 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/model/ShipMap.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/model/ShipMap.java @@ -249,4 +249,8 @@ public class ShipMap { if (eventBroker != null) eventBroker.notifyListeners(event); } + + public void add(Shell shell) { + addItem(shell); + } } diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/model/Visitor.java b/Projekte/battleship/model/src/main/java/pp/battleship/model/Visitor.java index f490451..4d51d1f 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/model/Visitor.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/model/Visitor.java @@ -28,4 +28,13 @@ public interface Visitor { * @return the result of visiting the Battleship element */ T visit(Battleship ship); + + /** + * Visits a Shell element + * + * @param shell the Shell element to visit + * @return the result of visitung the Battleship element + */ + T visit(Shell shell); } + diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/model/VoidVisitor.java b/Projekte/battleship/model/src/main/java/pp/battleship/model/VoidVisitor.java index 871c063..cb556ab 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/model/VoidVisitor.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/model/VoidVisitor.java @@ -25,4 +25,11 @@ public interface VoidVisitor { * @param ship the Battleship element to visit */ void visit(Battleship ship); + + /** + * Visits a Shell element + * @param shell the Shell element to visit + */ + void visit(Shell shell); } + diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/notification/Sound.java b/Projekte/battleship/model/src/main/java/pp/battleship/notification/Sound.java index d82ab69..22066b0 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/notification/Sound.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/notification/Sound.java @@ -22,5 +22,9 @@ public enum Sound { /** * Sound of a ship being destroyed. */ - DESTROYED_SHIP + DESTROYED_SHIP, + /** + * Sound of flying Shell + */ + SHELL_FLYING } diff --git a/Projekte/battleship/server/src/main/java/pp/battleship/server/BattleshipServer.java b/Projekte/battleship/server/src/main/java/pp/battleship/server/BattleshipServer.java index cbad331..9da30a3 100644 --- a/Projekte/battleship/server/src/main/java/pp/battleship/server/BattleshipServer.java +++ b/Projekte/battleship/server/src/main/java/pp/battleship/server/BattleshipServer.java @@ -28,6 +28,7 @@ import pp.battleship.BattleshipConfig; import pp.battleship.game.server.Player; import pp.battleship.game.server.ServerGameLogic; import pp.battleship.game.server.ServerSender; +import pp.battleship.message.client.AnimationFinishedMessage; import pp.battleship.message.client.ClientMessage; import pp.battleship.message.client.MapMessage; import pp.battleship.message.client.ShootMessage; @@ -119,12 +120,14 @@ public class BattleshipServer implements MessageListener, Conn Serializer.registerClass(Battleship.class); Serializer.registerClass(IntPoint.class); Serializer.registerClass(Shot.class); + Serializer.registerClass(AnimationFinishedMessage.class); } private void registerListeners() { myServer.addMessageListener(this, MapMessage.class); myServer.addMessageListener(this, ShootMessage.class); myServer.addConnectionListener(this); + myServer.addMessageListener(this, AnimationFinishedMessage.class); } @Override