added animation of a shell to the game
- in BattleShipServer class added serialization of the AnimationMessage classs - added to VoidVisitor and Visitor the Shell class - edited the ServerGameLogic class to implement a new Animation state (see new Server-State-Chart) - added a new client state AnimationState (see new Client-State-Chart)
This commit is contained in:
@@ -29,7 +29,7 @@ robot.targets=2, 0,\
|
|||||||
2, 3
|
2, 3
|
||||||
#
|
#
|
||||||
# Delay in milliseconds between each shot fired by the RobotClient.
|
# Delay in milliseconds between each shot fired by the RobotClient.
|
||||||
robot.delay=500
|
robot.delay=2000
|
||||||
#
|
#
|
||||||
# The dimensions of the game map used in single mode.
|
# The dimensions of the game map used in single mode.
|
||||||
# 'map.width' defines the number of columns, and 'map.height' defines the number of rows.
|
# 'map.width' defines the number of columns, and 'map.height' defines the number of rows.
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
import com.jme3.app.DebugKeysAppState;
|
import com.jme3.app.DebugKeysAppState;
|
||||||
import com.jme3.app.SimpleApplication;
|
import com.jme3.app.SimpleApplication;
|
||||||
import com.jme3.app.StatsAppState;
|
import com.jme3.app.StatsAppState;
|
||||||
import com.jme3.audio.AudioNode;
|
|
||||||
import com.jme3.font.BitmapFont;
|
import com.jme3.font.BitmapFont;
|
||||||
import com.jme3.font.BitmapText;
|
import com.jme3.font.BitmapText;
|
||||||
import com.jme3.input.KeyInput;
|
import com.jme3.input.KeyInput;
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ public class GameSound extends AbstractAppState implements GameEventListener {
|
|||||||
private AudioNode splashSound;
|
private AudioNode splashSound;
|
||||||
private AudioNode shipDestroyedSound;
|
private AudioNode shipDestroyedSound;
|
||||||
private AudioNode explosionSound;
|
private AudioNode explosionSound;
|
||||||
|
private AudioNode shellFiredSound;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if sound is enabled in the preferences.
|
* Checks if sound is enabled in the preferences.
|
||||||
@@ -78,6 +79,7 @@ public void initialize(AppStateManager stateManager, Application app) {
|
|||||||
shipDestroyedSound = loadSound(app, "Sound/Effects/sunken.wav"); //NON-NLS
|
shipDestroyedSound = loadSound(app, "Sound/Effects/sunken.wav"); //NON-NLS
|
||||||
splashSound = loadSound(app, "Sound/Effects/splash.wav"); //NON-NLS
|
splashSound = loadSound(app, "Sound/Effects/splash.wav"); //NON-NLS
|
||||||
explosionSound = loadSound(app, "Sound/Effects/explosion.wav"); //NON-NLS
|
explosionSound = loadSound(app, "Sound/Effects/explosion.wav"); //NON-NLS
|
||||||
|
shellFiredSound = loadSound(app, "Sound/Effects/missle.wav"); //NON-NLS
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,8 +95,7 @@ private AudioNode loadSound(Application app, String name) {
|
|||||||
sound.setLooping(false);
|
sound.setLooping(false);
|
||||||
sound.setPositional(false);
|
sound.setPositional(false);
|
||||||
return sound;
|
return sound;
|
||||||
}
|
} catch (AssetLoadException | AssetNotFoundException ex) {
|
||||||
catch (AssetLoadException | AssetNotFoundException ex) {
|
|
||||||
LOGGER.log(Level.ERROR, ex.getMessage(), ex);
|
LOGGER.log(Level.ERROR, ex.getMessage(), ex);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -124,12 +125,21 @@ public void shipDestroyed() {
|
|||||||
shipDestroyedSound.playInstance();
|
shipDestroyedSound.playInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays sound effect when a shell has been fired.
|
||||||
|
*/
|
||||||
|
public void shellFired() {
|
||||||
|
if (isEnabled() && shellFiredSound != null)
|
||||||
|
shellFiredSound.playInstance();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void receivedEvent(SoundEvent event) {
|
public void receivedEvent(SoundEvent event) {
|
||||||
switch (event.sound()) {
|
switch (event.sound()) {
|
||||||
case EXPLOSION -> explosion();
|
case EXPLOSION -> explosion();
|
||||||
case SPLASH -> splash();
|
case SPLASH -> splash();
|
||||||
case DESTROYED_SHIP -> shipDestroyed();
|
case DESTROYED_SHIP -> shipDestroyed();
|
||||||
|
case SHELL_FIRED -> shellFired();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,6 +143,10 @@ public float getHeight() {
|
|||||||
return FIELD_SIZE * map.getHeight();
|
return FIELD_SIZE * map.getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static float getFieldSize() {
|
||||||
|
return FIELD_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts coordinates from view coordinates to model coordinates.
|
* Converts coordinates from view coordinates to model coordinates.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -7,14 +7,20 @@
|
|||||||
|
|
||||||
package pp.battleship.client.gui;
|
package pp.battleship.client.gui;
|
||||||
|
|
||||||
|
import com.jme3.material.Material;
|
||||||
|
import com.jme3.material.RenderState;
|
||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.shape.Sphere;
|
||||||
import pp.battleship.model.Battleship;
|
import pp.battleship.model.Battleship;
|
||||||
|
import pp.battleship.model.Shell;
|
||||||
import pp.battleship.model.Shot;
|
import pp.battleship.model.Shot;
|
||||||
import pp.util.Position;
|
import pp.util.Position;
|
||||||
|
|
||||||
|
import static com.jme3.material.Materials.UNSHADED;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronizes the visual representation of the ship map with the game model.
|
* Synchronizes the visual representation of the ship map with the game model.
|
||||||
* It handles the rendering of ships and shots on the map view, updating the view
|
* It handles the rendering of ships and shots on the map view, updating the view
|
||||||
@@ -109,6 +115,25 @@ public Spatial visit(Battleship ship) {
|
|||||||
return shipNode;
|
return shipNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a visual representation of a shell on the map.
|
||||||
|
* The shell is represented as a black ellipse.
|
||||||
|
*
|
||||||
|
* @param shell the Shell object representing the shell in the model
|
||||||
|
* @return a Spatial representing the shell on the map
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Spatial visit(Shell shell) {
|
||||||
|
Geometry ellipse = new Geometry("ellipse", new Sphere(50, 50, MapView.getFieldSize() / 2 * 0.8f));
|
||||||
|
Material mat = new Material(view.getApp().getAssetManager(), UNSHADED); //NON-NLS
|
||||||
|
mat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
|
||||||
|
mat.getAdditionalRenderState().setFaceCullMode(RenderState.FaceCullMode.Off);
|
||||||
|
mat.setColor("Color", ColorRGBA.Black);
|
||||||
|
ellipse.setMaterial(mat);
|
||||||
|
ellipse.addControl(new ShellMapControl(view, shell));
|
||||||
|
return ellipse;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a line geometry representing part of the ship's border.
|
* Creates a line geometry representing part of the ship's border.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -20,10 +20,7 @@
|
|||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
import com.jme3.scene.shape.Box;
|
import com.jme3.scene.shape.Box;
|
||||||
import pp.battleship.client.BattleshipApp;
|
import pp.battleship.client.BattleshipApp;
|
||||||
import pp.battleship.model.Battleship;
|
import pp.battleship.model.*;
|
||||||
import pp.battleship.model.Rotation;
|
|
||||||
import pp.battleship.model.ShipMap;
|
|
||||||
import pp.battleship.model.Shot;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static pp.util.FloatMath.HALF_PI;
|
import static pp.util.FloatMath.HALF_PI;
|
||||||
@@ -35,8 +32,7 @@
|
|||||||
* It extends the {@link ShipMapSynchronizer} to provide specific synchronization
|
* It extends the {@link ShipMapSynchronizer} to provide specific synchronization
|
||||||
* logic for the sea map.
|
* logic for the sea map.
|
||||||
*/
|
*/
|
||||||
class SeaSynchronizer extends ShipMapSynchronizer { ;
|
class SeaSynchronizer extends ShipMapSynchronizer {
|
||||||
|
|
||||||
private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
|
private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
|
||||||
private static final String KING_GEORGE_V_MODEL = "Models/KingGeorgeV/KingGeorgeV.j3o"; //NON-NLS
|
private static final String KING_GEORGE_V_MODEL = "Models/KingGeorgeV/KingGeorgeV.j3o"; //NON-NLS
|
||||||
private static final String DESTROYER_MODEL = "Models/Destroyer/Destroyer.j3o"; //NON-NLS
|
private static final String DESTROYER_MODEL = "Models/Destroyer/Destroyer.j3o"; //NON-NLS
|
||||||
@@ -44,8 +40,11 @@ class SeaSynchronizer extends ShipMapSynchronizer { ;
|
|||||||
private static final String TYPE_II_UBOAT_MODEL = "Models/TypeIIUboat/TypeIIUboat.j3o"; //NON-NLS
|
private static final String TYPE_II_UBOAT_MODEL = "Models/TypeIIUboat/TypeIIUboat.j3o"; //NON-NLS
|
||||||
private static final String TYPE_II_UBOAT_TEXTURE = "Models/TypeIIUboat/Type_II_U-boat_diff.jpg"; //NON-NLS
|
private static final String TYPE_II_UBOAT_TEXTURE = "Models/TypeIIUboat/Type_II_U-boat_diff.jpg"; //NON-NLS
|
||||||
private static final String ATLANTICA_MODEL = "Models/Atlantica/Atlantica.j3o"; //NON-NLS
|
private static final String ATLANTICA_MODEL = "Models/Atlantica/Atlantica.j3o"; //NON-NLS
|
||||||
|
private static final String ROCKET = "Models/Rocket/Rocket.j3o"; //NON-NLS
|
||||||
|
|
||||||
private static final String COLOR = "Color"; //NON-NLS
|
private static final String COLOR = "Color"; //NON-NLS
|
||||||
private static final String SHIP = "ship"; //NON-NLS
|
private static final String SHIP = "ship"; //NON-NLS
|
||||||
|
private static final String SHELL = "shell"; //NON-NLS
|
||||||
private static final ColorRGBA BOX_COLOR = ColorRGBA.Gray;
|
private static final ColorRGBA BOX_COLOR = ColorRGBA.Gray;
|
||||||
|
|
||||||
private final ShipMap map;
|
private final ShipMap map;
|
||||||
@@ -176,6 +175,36 @@ private ParticleEmitter createFireEffect(Shot shot, Node shipNode) {
|
|||||||
return fire;
|
return fire;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visits a {@link Shell} and creates a graphical representation of it.
|
||||||
|
* The shell is represented as a node with a model attached to it.
|
||||||
|
* The node is then positioned and controlled by a {@link ShellControl}.
|
||||||
|
*
|
||||||
|
* @param shell the shell to be represented
|
||||||
|
* @return the node containing the graphical representation of the shell
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Spatial visit(Shell shell) {
|
||||||
|
final Node node = new Node(SHELL);
|
||||||
|
node.attachChild(createShell());
|
||||||
|
node.setLocalTranslation(shell.getY() + 0.5f, 10f, shell.getX() + 0.5f);
|
||||||
|
node.addControl(new ShellControl());
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a graphical representation of a shell.
|
||||||
|
*
|
||||||
|
* @return the spatial representing the shell
|
||||||
|
*/
|
||||||
|
private Spatial createShell() {
|
||||||
|
final Spatial model = app.getAssetManager().loadModel(ROCKET);
|
||||||
|
model.scale(0.0025f);
|
||||||
|
model.rotate(PI, 0f, 0f);
|
||||||
|
model.setShadowMode(ShadowMode.CastAndReceive);
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a {@link Battleship} and creates a graphical representation of it.
|
* Visits a {@link Battleship} and creates a graphical representation of it.
|
||||||
* The representation is either a 3D model or a simple box depending on the
|
* The representation is either a 3D model or a simple box depending on the
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package pp.battleship.client.gui;
|
||||||
|
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.ViewPort;
|
||||||
|
import com.jme3.scene.control.AbstractControl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls the movement and rotation of a shell in the game.
|
||||||
|
* The shell moves downward at a constant speed and rotates around its Y-axis.
|
||||||
|
* When the shell reaches a certain Y-coordinate, it is removed from its parent node.
|
||||||
|
*/
|
||||||
|
public class ShellControl extends AbstractControl {
|
||||||
|
private final static float SHELL_SPEED = 7.5f;
|
||||||
|
private final static float SHELL_ROTATION_SPEED = 0.5f;
|
||||||
|
private final static float MIN_HEIGHT = 0.7f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the shell's position and rotation.
|
||||||
|
* If the shell's Y-coordinate is less than or equal to 1.0, it is detached from its parent node.
|
||||||
|
*
|
||||||
|
* @param tpf time per frame, used to ensure consistent movement speed across different frame rates
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void controlUpdate(float tpf) {
|
||||||
|
spatial.move(0, -SHELL_SPEED * tpf, 0);
|
||||||
|
spatial.rotate(0, SHELL_ROTATION_SPEED, 0);
|
||||||
|
if (spatial.getLocalTranslation().getY() <= MIN_HEIGHT) {
|
||||||
|
spatial.getParent().detachChild(spatial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the shell. This method is currently not used.
|
||||||
|
*
|
||||||
|
* @param rm the RenderManager
|
||||||
|
* @param vp the ViewPort
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
|
// nothing to do here
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package pp.battleship.client.gui;
|
||||||
|
|
||||||
|
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.util.Position;
|
||||||
|
|
||||||
|
import java.lang.System.Logger;
|
||||||
|
import java.lang.System.Logger.Level;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls the animation of a shell in the map view.
|
||||||
|
* This class handles the movement of a shell from its starting position to its target position
|
||||||
|
* using linear interpolation over a specified duration.
|
||||||
|
*/
|
||||||
|
public class ShellMapControl extends AbstractControl {
|
||||||
|
private static final Logger LOGGER = System.getLogger(ShellMapControl.class.getName());
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The duration of the shell animation in seconds.
|
||||||
|
*/
|
||||||
|
private final static float ANIMATION_DURATION = 0.8f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The end position of the shell in the map view.
|
||||||
|
*/
|
||||||
|
private final Position endPos;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The progress of the shell's movement, ranging from 0 to 1.
|
||||||
|
*/
|
||||||
|
private float progress = 0f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new instance of {@link ShellMapControl}.
|
||||||
|
*
|
||||||
|
* @param view the map view
|
||||||
|
* @param shell the shell to be controlled
|
||||||
|
*/
|
||||||
|
public ShellMapControl(MapView view, Shell shell) {
|
||||||
|
Vector3f endPos = new Vector3f(shell.getX(), 0, shell.getY());
|
||||||
|
this.endPos = view.modelToView(endPos.x, endPos.z);
|
||||||
|
LOGGER.log(Level.DEBUG, "ShellMapControl created with endPos: " + this.endPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the position of the shell in the view with linear interpolation.
|
||||||
|
* This method is called during the update phase.
|
||||||
|
*
|
||||||
|
* @param tpf the time per frame
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void controlUpdate(float tpf) {
|
||||||
|
// adjust speed by changing the multiplier
|
||||||
|
progress += tpf * ANIMATION_DURATION;
|
||||||
|
|
||||||
|
// progress is between 0 and 1
|
||||||
|
if (progress > 1f) {
|
||||||
|
progress = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
// linearly interpolate the current position between (0, 0) and endPos
|
||||||
|
float newX = (1 - progress) * 0 + progress * endPos.getX() + MapView.getFieldSize() / 2;
|
||||||
|
float newZ = (1 - progress) * 0 + progress * endPos.getY() + MapView.getFieldSize() / 2;
|
||||||
|
|
||||||
|
spatial.setLocalTranslation(newX, newZ, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called during the render phase.
|
||||||
|
* Currently, it does nothing.
|
||||||
|
*
|
||||||
|
* @param rm the RenderManager
|
||||||
|
* @param vp the ViewPort
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
|
// nothing to do here
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,250 @@
|
|||||||
|
#
|
||||||
|
# Generated by Sweet Home 3D - ven. janv. 02 20:37:08 CET 2015
|
||||||
|
# http://www.sweethome3d.com/
|
||||||
|
#
|
||||||
|
|
||||||
|
newmtl FrontColorNoCulling
|
||||||
|
illum 1
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 0.2 0.2 0.2
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl ForegroundColor
|
||||||
|
illum 1
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 0.2 0.2 0.2
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl white
|
||||||
|
illum 1
|
||||||
|
Ka 0.48235294 0.5019608 0.5803922
|
||||||
|
Kd 0.48235294 0.5019608 0.5803922
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl white_Cylinder_5
|
||||||
|
illum 1
|
||||||
|
Ka 0.47843137 0.49803922 0.5764706
|
||||||
|
Kd 0.47843137 0.49803922 0.5764706
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl white_Cylinder_10
|
||||||
|
illum 1
|
||||||
|
Ka 0.8784314 0.8745098 0.8901961
|
||||||
|
Kd 0.8784314 0.8745098 0.8901961
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl FrontColorNoCulling_11
|
||||||
|
illum 1
|
||||||
|
Ka 0.8784314 0.8745098 0.8901961
|
||||||
|
Kd 0.8784314 0.8745098 0.8901961
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl ForegroundColor_12
|
||||||
|
illum 1
|
||||||
|
Ka 0.8784314 0.8745098 0.8901961
|
||||||
|
Kd 0.8784314 0.8745098 0.8901961
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl white_Mesh_13
|
||||||
|
illum 1
|
||||||
|
Ka 0.6 0.6 0.6
|
||||||
|
Kd 0.6 0.6 0.6
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl Cube_1_1_1
|
||||||
|
illum 1
|
||||||
|
Ka 0.0 0.0 0.0
|
||||||
|
Kd 0.0 0.0 0.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cylinder_7_7
|
||||||
|
illum 1
|
||||||
|
Ka 0.4 0.4 0.4
|
||||||
|
Kd 0.4 0.4 0.4
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cylinder_10_10
|
||||||
|
illum 1
|
||||||
|
Ka 0.8 0.4 0.0
|
||||||
|
Kd 0.8 0.4 0.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cylinder_11_11
|
||||||
|
illum 2
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 0.2 0.2 0.2
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl 12_12
|
||||||
|
illum 1
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 0.2 0.2 0.2
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cube_1_1_1_Cube_1_1_1_38
|
||||||
|
illum 1
|
||||||
|
Ka 0.6 0.6 0.6
|
||||||
|
Kd 0.6 0.6 0.6
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl white_Cylinder_58
|
||||||
|
illum 1
|
||||||
|
Ka 0.1882353 0.27058825 0.58431375
|
||||||
|
Kd 0.1882353 0.27058825 0.58431375
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl white_Cylinder_59
|
||||||
|
illum 1
|
||||||
|
Ka 0.3137255 0.14901961 0.011764706
|
||||||
|
Kd 0.3137255 0.14901961 0.011764706
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 0.0
|
||||||
|
|
||||||
|
newmtl 1_1
|
||||||
|
illum 2
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 1.0 1.0 1.0
|
||||||
|
Ks 0.5 0.5 0.5
|
||||||
|
Ns 64.0
|
||||||
|
Ni 1.0
|
||||||
|
d 0.48000002
|
||||||
|
map_Kd Missile_AIM-120_D_[AMRAAM]_1_1.png
|
||||||
|
|
||||||
|
newmtl Cube_1_2_2
|
||||||
|
illum 1
|
||||||
|
Ka 0.8 0.4 0.0
|
||||||
|
Kd 0.8 0.4 0.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cylinder_4_4
|
||||||
|
illum 2
|
||||||
|
Ka 0.6 0.6 0.6
|
||||||
|
Kd 0.6 0.6 0.6
|
||||||
|
Ks 0.5 0.5 0.5
|
||||||
|
Ns 64.0
|
||||||
|
|
||||||
|
newmtl Cylinder_5_5
|
||||||
|
illum 2
|
||||||
|
Ka 0.8 0.8 0.0
|
||||||
|
Kd 0.8 0.8 0.0
|
||||||
|
Ks 0.5 0.5 0.5
|
||||||
|
Ns 64.0
|
||||||
|
|
||||||
|
newmtl Cylinder_6_6
|
||||||
|
illum 2
|
||||||
|
Ka 0.8784314 0.8745098 0.8901961
|
||||||
|
Kd 0.8784314 0.8745098 0.8901961
|
||||||
|
Ks 0.5 0.5 0.5
|
||||||
|
Ns 64.0
|
||||||
|
|
||||||
|
newmtl Cylinder_10_10_Cylinder_10_10_73
|
||||||
|
illum 1
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 0.2 0.2 0.2
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl 11_11
|
||||||
|
illum 1
|
||||||
|
Ka 0.6 0.6 0.6
|
||||||
|
Kd 0.6 0.6 0.6
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cube_1_1_1_Cube_1_1_1_76
|
||||||
|
illum 1
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 1.0 1.0 1.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
Ni 1.0
|
||||||
|
map_Kd Missile_AIM-120_D_[AMRAAM]_Cube_1_1_1_Cube_1_1_1_76.png
|
||||||
|
|
||||||
|
newmtl Cylinder_2_2
|
||||||
|
illum 2
|
||||||
|
Ka 0.6 0.6 0.6
|
||||||
|
Kd 0.6 0.6 0.6
|
||||||
|
Ks 0.5 0.5 0.5
|
||||||
|
Ns 64.0
|
||||||
|
|
||||||
|
newmtl Cylinder_3_3
|
||||||
|
illum 1
|
||||||
|
Ka 0.4 0.4 0.0
|
||||||
|
Kd 0.4 0.4 0.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cylinder_4_4_Cylinder_4_4_79
|
||||||
|
illum 1
|
||||||
|
Ka 0.0 0.0 0.0
|
||||||
|
Kd 0.0 0.0 0.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cube_1_5_5
|
||||||
|
illum 1
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 1.0 1.0 1.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
map_Kd Missile_AIM-120_D_[AMRAAM]_Cube_1_5_5.png
|
||||||
|
|
||||||
|
newmtl Cube_1_6_6
|
||||||
|
illum 1
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 1.0 1.0 1.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
Ni 1.0
|
||||||
|
map_Kd Missile_AIM-120_D_[AMRAAM]_Cube_1_6_6.png
|
||||||
|
|
||||||
|
newmtl Cylinder_1_1
|
||||||
|
illum 1
|
||||||
|
Ka 0.4 0.4 0.4
|
||||||
|
Kd 0.4 0.4 0.4
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cube_1_5_5_Cube_1_5_5_86
|
||||||
|
illum 1
|
||||||
|
Ka 0.2 0.2 0.2
|
||||||
|
Kd 0.2 0.2 0.2
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cube_1_6_6_Cube_1_6_6_87
|
||||||
|
illum 1
|
||||||
|
Ka 0.8 0.0 0.0
|
||||||
|
Kd 0.8 0.0 0.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cylinder_7_7_Cylinder_7_7_88
|
||||||
|
illum 1
|
||||||
|
Ka 0.8 0.4 0.0
|
||||||
|
Kd 0.8 0.4 0.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
|
|
||||||
|
newmtl Cylinder_8_8
|
||||||
|
illum 1
|
||||||
|
Ka 0.4 0.6 0.0
|
||||||
|
Kd 0.4 0.6 0.0
|
||||||
|
Ks 0.0 0.0 0.0
|
||||||
|
Ns 1.0
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 210 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 289 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
based on:
|
||||||
|
https://free3d.com/de/3d-model/aim-120d-missile-51025.html
|
||||||
|
License: Free Personal Use Only
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,159 @@
|
|||||||
|
package pp.battleship.game.client;
|
||||||
|
|
||||||
|
import pp.battleship.message.client.AnimationMessage;
|
||||||
|
import pp.battleship.message.server.EffectMessage;
|
||||||
|
import pp.battleship.model.Battleship;
|
||||||
|
import pp.battleship.model.IntPoint;
|
||||||
|
import pp.battleship.model.Shell;
|
||||||
|
import pp.battleship.model.ShipMap;
|
||||||
|
import pp.battleship.notification.Music;
|
||||||
|
import pp.battleship.notification.Sound;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the state of the game during an animation sequence.
|
||||||
|
* This state handles the progress and completion of the animation,
|
||||||
|
* updates the game state accordingly, and transitions to the next state.
|
||||||
|
*/
|
||||||
|
public class AnimationState extends ClientState {
|
||||||
|
/**
|
||||||
|
* Progress of the current animation, ranging from 0 to 1.
|
||||||
|
*/
|
||||||
|
private float animationProgress = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Duration of the animation in seconds.
|
||||||
|
*/
|
||||||
|
private final static float ANIMATION_DURATION = 0.375f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speed of the shell in the animation.
|
||||||
|
*/
|
||||||
|
private final static float SHELL_SPEED = 0.3f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The effect message received from the server.
|
||||||
|
*/
|
||||||
|
private final EffectMessage msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shell involved in the animation.
|
||||||
|
*/
|
||||||
|
private final Shell shell;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an AnimationState with the specified game logic, effect message, and shell.
|
||||||
|
*
|
||||||
|
* @param logic the game logic associated with this state
|
||||||
|
* @param msg the effect message received from the server
|
||||||
|
* @param shell the shell involved in the animation
|
||||||
|
*/
|
||||||
|
public AnimationState(ClientGameLogic logic, EffectMessage msg, Shell shell) {
|
||||||
|
super(logic);
|
||||||
|
this.msg = msg;
|
||||||
|
this.shell = shell;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends the animation state and transitions to the next state:<br>
|
||||||
|
* - Plays the appropriate sound.<br>
|
||||||
|
* - Updates the affected map.<br>
|
||||||
|
* - Adds destroyed ships to the opponent's map.<br>
|
||||||
|
* - Sends an `AnimationMessage` to the server.<br>
|
||||||
|
* - If the game is over, transitions to `GameOverState` and plays music.<br>
|
||||||
|
* - Otherwise, transitions to `BattleState`.
|
||||||
|
*/
|
||||||
|
public void endState() {
|
||||||
|
playSound(msg);
|
||||||
|
affectedMap(msg).add(msg.getShot());
|
||||||
|
affectedMap(msg).remove(shell);
|
||||||
|
|
||||||
|
if (destroyedOpponentShip(msg))
|
||||||
|
logic.getOpponentMap().add(msg.getDestroyedShip());
|
||||||
|
|
||||||
|
logic.send(new AnimationMessage());
|
||||||
|
if (msg.isGameOver()) {
|
||||||
|
for (Battleship ship : msg.getRemainingOpponentShips()) {
|
||||||
|
logic.getOpponentMap().add(ship);
|
||||||
|
}
|
||||||
|
logic.setState(new GameOverState(logic));
|
||||||
|
if (msg.isOwnShot())
|
||||||
|
logic.playMusic(Music.VICTORY_MUSIC);
|
||||||
|
else
|
||||||
|
logic.playMusic(Music.DEFEAT_MUSIC);
|
||||||
|
} else {
|
||||||
|
logic.setState(new BattleState(logic, msg.isMyTurn()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the battle state should be shown.
|
||||||
|
*
|
||||||
|
* @return true if the battle state should be shown, false otherwise
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean showBattle() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines which map (own or opponent's) should be affected by the shot based on the message.
|
||||||
|
*
|
||||||
|
* @param msg the effect message received from the server
|
||||||
|
* @return the map (either the opponent's or player's own map) that is affected by the shot
|
||||||
|
*/
|
||||||
|
private ShipMap affectedMap(EffectMessage msg) {
|
||||||
|
return msg.isOwnShot() ? logic.getOpponentMap() : logic.getOwnMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the opponent's ship was destroyed by the player's shot.
|
||||||
|
*
|
||||||
|
* @param msg the effect message received from the server
|
||||||
|
* @return true if the shot destroyed an opponent's ship, false otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
private boolean destroyedOpponentShip(EffectMessage msg) {
|
||||||
|
return msg.getDestroyedShip() != null && msg.isOwnShot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plays a sound based on the outcome of the shot. Different sounds are played for a miss, hit,
|
||||||
|
* or destruction of a ship.
|
||||||
|
*
|
||||||
|
* @param msg the effect message containing the result of the shot
|
||||||
|
*/
|
||||||
|
private void playSound(EffectMessage msg) {
|
||||||
|
if (!msg.getShot().isHit())
|
||||||
|
logic.playSound(Sound.SPLASH);
|
||||||
|
else if (msg.getDestroyedShip() == null)
|
||||||
|
logic.playSound(Sound.EXPLOSION);
|
||||||
|
else
|
||||||
|
logic.playSound(Sound.DESTROYED_SHIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a click on the opponent's map.
|
||||||
|
*
|
||||||
|
* @param pos the position where the click occurred
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void clickOpponentMap(IntPoint pos) {
|
||||||
|
if (!msg.isMyTurn())
|
||||||
|
logic.setInfoText("wait.its.not.your.turn");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the state of the animation. This method increments the animationProgress value
|
||||||
|
* until it exceeds a threshold, at which point the state ends.
|
||||||
|
*
|
||||||
|
* @param delta the time elapsed since the last update, in seconds
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void update(float delta) {
|
||||||
|
if (animationProgress > ANIMATION_DURATION) {
|
||||||
|
endState();
|
||||||
|
} else {
|
||||||
|
animationProgress += delta * SHELL_SPEED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,12 +10,10 @@
|
|||||||
import pp.battleship.message.client.ShootMessage;
|
import pp.battleship.message.client.ShootMessage;
|
||||||
import pp.battleship.message.server.EffectMessage;
|
import pp.battleship.message.server.EffectMessage;
|
||||||
import pp.battleship.model.IntPoint;
|
import pp.battleship.model.IntPoint;
|
||||||
|
import pp.battleship.model.Shell;
|
||||||
import pp.battleship.model.ShipMap;
|
import pp.battleship.model.ShipMap;
|
||||||
import pp.battleship.notification.Music;
|
|
||||||
import pp.battleship.notification.Sound;
|
import pp.battleship.notification.Sound;
|
||||||
|
|
||||||
import java.lang.System.Logger.Level;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the state of the client where players take turns to attack each other's ships.
|
* Represents the state of the client where players take turns to attack each other's ships.
|
||||||
*/
|
*/
|
||||||
@@ -33,11 +31,21 @@ public BattleState(ClientGameLogic logic, boolean myTurn) {
|
|||||||
this.myTurn = myTurn;
|
this.myTurn = myTurn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the battle state should be shown.
|
||||||
|
*
|
||||||
|
* @return true if the battle state should be shown, false otherwise
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean showBattle() {
|
public boolean showBattle() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a click on the opponent's map.
|
||||||
|
*
|
||||||
|
* @param pos the position where the click occurred
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void clickOpponentMap(IntPoint pos) {
|
public void clickOpponentMap(IntPoint pos) {
|
||||||
if (!myTurn)
|
if (!myTurn)
|
||||||
@@ -53,21 +61,16 @@ else if (logic.getOpponentMap().isValid(pos))
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void receivedEffect(EffectMessage msg) {
|
public void receivedEffect(EffectMessage msg) {
|
||||||
ClientGameLogic.LOGGER.log(Level.INFO, "report effect: {0}", msg); //NON-NLS
|
ClientGameLogic.LOGGER.log(System.Logger.Level.INFO, "report effect: {0}", msg); //NON-NLS
|
||||||
playSound(msg);
|
// Update turn and info text
|
||||||
myTurn = msg.isMyTurn();
|
myTurn = msg.isMyTurn();
|
||||||
logic.setInfoText(msg.getInfoTextKey());
|
logic.setInfoText(msg.getInfoTextKey());
|
||||||
affectedMap(msg).add(msg.getShot());
|
// Add the shell to the affected map
|
||||||
if (destroyedOpponentShip(msg))
|
Shell shell = new Shell(msg.getShot());
|
||||||
logic.getOpponentMap().add(msg.getDestroyedShip());
|
affectedMap(msg).add(shell);
|
||||||
if (msg.isGameOver()) {
|
// Change state to AnimationState
|
||||||
msg.getRemainingOpponentShips().forEach(logic.getOpponentMap()::add);
|
logic.playSound(Sound.SHELL_FIRED);
|
||||||
logic.setState(new GameOverState(logic));
|
logic.setState(new AnimationState(logic, msg, shell));
|
||||||
if (msg.isOwnShot())
|
|
||||||
logic.playMusic(Music.VICTORY_MUSIC);
|
|
||||||
else
|
|
||||||
logic.playMusic(Music.DEFEAT_MUSIC);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,29 +82,4 @@ public void receivedEffect(EffectMessage msg) {
|
|||||||
private ShipMap affectedMap(EffectMessage msg) {
|
private ShipMap affectedMap(EffectMessage msg) {
|
||||||
return msg.isOwnShot() ? logic.getOpponentMap() : logic.getOwnMap();
|
return msg.isOwnShot() ? logic.getOpponentMap() : logic.getOwnMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the opponent's ship was destroyed by the player's shot.
|
|
||||||
*
|
|
||||||
* @param msg the effect message received from the server
|
|
||||||
* @return true if the shot destroyed an opponent's ship, false otherwise
|
|
||||||
*/
|
|
||||||
private boolean destroyedOpponentShip(EffectMessage msg) {
|
|
||||||
return msg.getDestroyedShip() != null && msg.isOwnShot();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays a sound based on the outcome of the shot. Different sounds are played for a miss, hit,
|
|
||||||
* or destruction of a ship.
|
|
||||||
*
|
|
||||||
* @param msg the effect message containing the result of the shot
|
|
||||||
*/
|
|
||||||
private void playSound(EffectMessage msg) {
|
|
||||||
if (!msg.getShot().isHit())
|
|
||||||
logic.playSound(Sound.SPLASH);
|
|
||||||
else if (msg.getDestroyedShip() == null)
|
|
||||||
logic.playSound(Sound.EXPLOSION);
|
|
||||||
else
|
|
||||||
logic.playSound(Sound.DESTROYED_SHIP);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
package pp.battleship.game.server;
|
package pp.battleship.game.server;
|
||||||
|
|
||||||
import pp.battleship.BattleshipConfig;
|
import pp.battleship.BattleshipConfig;
|
||||||
|
import pp.battleship.message.client.AnimationMessage;
|
||||||
import pp.battleship.message.client.ClientInterpreter;
|
import pp.battleship.message.client.ClientInterpreter;
|
||||||
import pp.battleship.message.client.MapMessage;
|
import pp.battleship.message.client.MapMessage;
|
||||||
import pp.battleship.message.client.ShootMessage;
|
import pp.battleship.message.client.ShootMessage;
|
||||||
@@ -17,7 +18,6 @@
|
|||||||
import pp.battleship.message.server.StartBattleMessage;
|
import pp.battleship.message.server.StartBattleMessage;
|
||||||
import pp.battleship.model.Battleship;
|
import pp.battleship.model.Battleship;
|
||||||
import pp.battleship.model.IntPoint;
|
import pp.battleship.model.IntPoint;
|
||||||
import pp.battleship.model.Rotation;
|
|
||||||
|
|
||||||
import java.lang.System.Logger;
|
import java.lang.System.Logger;
|
||||||
import java.lang.System.Logger.Level;
|
import java.lang.System.Logger.Level;
|
||||||
@@ -36,6 +36,7 @@ public class ServerGameLogic implements ClientInterpreter {
|
|||||||
private final BattleshipConfig config;
|
private final BattleshipConfig config;
|
||||||
private final List<Player> players = new ArrayList<>(2);
|
private final List<Player> players = new ArrayList<>(2);
|
||||||
private final Set<Player> readyPlayers = new HashSet<>();
|
private final Set<Player> readyPlayers = new HashSet<>();
|
||||||
|
private final Set<Player> finishedAnimation = new HashSet<>();
|
||||||
private final ServerSender serverSender;
|
private final ServerSender serverSender;
|
||||||
private Player activePlayer;
|
private Player activePlayer;
|
||||||
private ServerState state = ServerState.WAIT;
|
private ServerState state = ServerState.WAIT;
|
||||||
@@ -226,9 +227,41 @@ private boolean overlaps(List<Battleship> ships) {
|
|||||||
public void received(ShootMessage msg, int from) {
|
public void received(ShootMessage msg, int from) {
|
||||||
if (state != ServerState.BATTLE)
|
if (state != ServerState.BATTLE)
|
||||||
LOGGER.log(Level.ERROR, "shoot not allowed in {0}", state); //NON-NLS
|
LOGGER.log(Level.ERROR, "shoot not allowed in {0}", state); //NON-NLS
|
||||||
else
|
else {
|
||||||
|
setState(ServerState.ANIMATION);
|
||||||
shoot(getPlayerById(from), msg.getPosition());
|
shoot(getPlayerById(from), msg.getPosition());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the reception of an {@link AnimationMessage}.
|
||||||
|
* Marks the player's animation as finished and transitions the game state if necessary.
|
||||||
|
*
|
||||||
|
* @param msg the received {@code AnimationMessage}
|
||||||
|
* @param from the ID of the sender client
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void received(AnimationMessage msg, int from) {
|
||||||
|
if (state != ServerState.ANIMATION)
|
||||||
|
LOGGER.log(Level.ERROR, "animation not allowed in {0}", state); //NON-NLS
|
||||||
|
else
|
||||||
|
finishedAnimation(getPlayerById(from));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the player's animation as finished and transitions the game state if necessary.
|
||||||
|
*
|
||||||
|
* @param player the player whose animation is finished
|
||||||
|
*/
|
||||||
|
private void finishedAnimation(Player player) {
|
||||||
|
if (!finishedAnimation.add(player)) {
|
||||||
|
LOGGER.log(Level.ERROR, "{0}'s animation was already finished", player);
|
||||||
|
}
|
||||||
|
if (finishedAnimation.size() == 2) {
|
||||||
|
finishedAnimation.clear();
|
||||||
|
setState(ServerState.BATTLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks the player as ready and sets their ships.
|
* Marks the player as ready and sets their ships.
|
||||||
@@ -265,8 +298,7 @@ void shoot(Player p, IntPoint pos) {
|
|||||||
send(activePlayer, EffectMessage.miss(true, pos));
|
send(activePlayer, EffectMessage.miss(true, pos));
|
||||||
send(otherPlayer, EffectMessage.miss(false, pos));
|
send(otherPlayer, EffectMessage.miss(false, pos));
|
||||||
activePlayer = otherPlayer;
|
activePlayer = otherPlayer;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// shot hit a ship
|
// shot hit a ship
|
||||||
selectedShip.hit(pos);
|
selectedShip.hit(pos);
|
||||||
if (otherPlayer.getMap().getRemainingShips().isEmpty()) {
|
if (otherPlayer.getMap().getRemainingShips().isEmpty()) {
|
||||||
@@ -274,13 +306,11 @@ void shoot(Player p, IntPoint pos) {
|
|||||||
send(activePlayer, EffectMessage.won(pos, selectedShip));
|
send(activePlayer, EffectMessage.won(pos, selectedShip));
|
||||||
send(otherPlayer, EffectMessage.lost(pos, selectedShip, activePlayer.getMap().getRemainingShips()));
|
send(otherPlayer, EffectMessage.lost(pos, selectedShip, activePlayer.getMap().getRemainingShips()));
|
||||||
setState(ServerState.GAME_OVER);
|
setState(ServerState.GAME_OVER);
|
||||||
}
|
} else if (selectedShip.isDestroyed()) {
|
||||||
else if (selectedShip.isDestroyed()) {
|
|
||||||
// ship has been destroyed, but game is not yet over
|
// ship has been destroyed, but game is not yet over
|
||||||
send(activePlayer, EffectMessage.shipDestroyed(true, pos, selectedShip));
|
send(activePlayer, EffectMessage.shipDestroyed(true, pos, selectedShip));
|
||||||
send(otherPlayer, EffectMessage.shipDestroyed(false, pos, selectedShip));
|
send(otherPlayer, EffectMessage.shipDestroyed(false, pos, selectedShip));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// ship has been hit, but it hasn't been destroyed
|
// ship has been hit, but it hasn't been destroyed
|
||||||
send(activePlayer, EffectMessage.hit(true, pos));
|
send(activePlayer, EffectMessage.hit(true, pos));
|
||||||
send(otherPlayer, EffectMessage.hit(false, pos));
|
send(otherPlayer, EffectMessage.hit(false, pos));
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ enum ServerState {
|
|||||||
*/
|
*/
|
||||||
BATTLE,
|
BATTLE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The server is waiting for clients to finish their animations.
|
||||||
|
*/
|
||||||
|
ANIMATION,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The game has ended because all the ships of one player have been destroyed.
|
* The game has ended because all the ships of one player have been destroyed.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -7,10 +7,7 @@
|
|||||||
|
|
||||||
package pp.battleship.game.singlemode;
|
package pp.battleship.game.singlemode;
|
||||||
|
|
||||||
import pp.battleship.message.client.ClientInterpreter;
|
import pp.battleship.message.client.*;
|
||||||
import pp.battleship.message.client.ClientMessage;
|
|
||||||
import pp.battleship.message.client.MapMessage;
|
|
||||||
import pp.battleship.message.client.ShootMessage;
|
|
||||||
import pp.battleship.model.Battleship;
|
import pp.battleship.model.Battleship;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -63,6 +60,19 @@ public void received(MapMessage msg, int from) {
|
|||||||
copiedMessage = new MapMessage(msg.getShips().stream().map(Copycat::copy).toList());
|
copiedMessage = new MapMessage(msg.getShips().stream().map(Copycat::copy).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the reception of a {@link AnimationMessage}.
|
||||||
|
* Since a {@code AnimationMessage} does not need to be copied, it is directly assigned.
|
||||||
|
*
|
||||||
|
* @param msg the received {@code AnimationMessage}
|
||||||
|
* @param from the identifier of the sender
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void received(AnimationMessage msg, int from) {
|
||||||
|
// copying is not necessary
|
||||||
|
copiedMessage = msg;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a copy of the provided {@link Battleship}.
|
* Creates a copy of the provided {@link Battleship}.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package pp.battleship.game.singlemode;
|
package pp.battleship.game.singlemode;
|
||||||
|
|
||||||
import pp.battleship.game.client.BattleshipClient;
|
import pp.battleship.game.client.BattleshipClient;
|
||||||
|
import pp.battleship.message.client.AnimationMessage;
|
||||||
import pp.battleship.message.client.MapMessage;
|
import pp.battleship.message.client.MapMessage;
|
||||||
import pp.battleship.message.client.ShootMessage;
|
import pp.battleship.message.client.ShootMessage;
|
||||||
import pp.battleship.message.server.EffectMessage;
|
import pp.battleship.message.server.EffectMessage;
|
||||||
@@ -121,6 +122,7 @@ public void received(StartBattleMessage msg) {
|
|||||||
@Override
|
@Override
|
||||||
public void received(EffectMessage msg) {
|
public void received(EffectMessage msg) {
|
||||||
LOGGER.log(Level.INFO, "Received EffectMessage: {0}", msg); //NON-NLS
|
LOGGER.log(Level.INFO, "Received EffectMessage: {0}", msg); //NON-NLS
|
||||||
|
connection.sendRobotMessage(new AnimationMessage());
|
||||||
if (msg.isMyTurn())
|
if (msg.isMyTurn())
|
||||||
shoot();
|
shoot();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package pp.battleship.message.client;
|
||||||
|
|
||||||
|
import com.jme3.network.serializing.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A message indicating an animation event is finished in the game. (Client → Server)
|
||||||
|
*/
|
||||||
|
@Serializable
|
||||||
|
public class AnimationMessage extends ClientMessage {
|
||||||
|
/**
|
||||||
|
* Constructs a new AnimationMessage instance.
|
||||||
|
*/
|
||||||
|
public AnimationMessage() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts a visitor for processing this message.
|
||||||
|
*
|
||||||
|
* @param interpreter the visitor to be used for processing
|
||||||
|
* @param from the connection ID of the sender
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void accept(ClientInterpreter interpreter, int from) {
|
||||||
|
interpreter.received(this, from);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,4 +26,12 @@ public interface ClientInterpreter {
|
|||||||
* @param from the connection ID from which the message was received
|
* @param from the connection ID from which the message was received
|
||||||
*/
|
*/
|
||||||
void received(MapMessage msg, int from);
|
void received(MapMessage msg, int from);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes a received AnimationMessage.
|
||||||
|
*
|
||||||
|
* @param msg the AnimationMessage to be processed
|
||||||
|
* @param from the connection ID from which the message was received
|
||||||
|
*/
|
||||||
|
void received(AnimationMessage msg, int from);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package pp.battleship.model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a shell in the Battleship game.
|
||||||
|
*/
|
||||||
|
public class Shell implements Item {
|
||||||
|
private final int x;
|
||||||
|
private final int y;
|
||||||
|
|
||||||
|
public Shell(Shot shot) {
|
||||||
|
this.x = shot.getX();
|
||||||
|
this.y = shot.getY();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T accept(Visitor<T> visitor) {
|
||||||
|
return visitor.visit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void accept(VoidVisitor visitor) {
|
||||||
|
visitor.visit(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -91,6 +91,15 @@ public void add(Shot shot) {
|
|||||||
addItem(shot);
|
addItem(shot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a shell to the map and triggers an item addition event.
|
||||||
|
*
|
||||||
|
* @param shell the shell to be added to the map
|
||||||
|
*/
|
||||||
|
public void add(Shell shell) {
|
||||||
|
addItem(shell);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an item from the map and triggers an item removal event.
|
* Removes an item from the map and triggers an item removal event.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -28,4 +28,12 @@ public interface Visitor<T> {
|
|||||||
* @return the result of visiting the Battleship element
|
* @return the result of visiting the Battleship element
|
||||||
*/
|
*/
|
||||||
T visit(Battleship ship);
|
T visit(Battleship ship);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visits a Shell element.
|
||||||
|
*
|
||||||
|
* @param shell the Shell element to visit
|
||||||
|
* @return the result of visiting the Shell element
|
||||||
|
*/
|
||||||
|
T visit(Shell shell);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,4 +25,11 @@ public interface VoidVisitor {
|
|||||||
* @param ship the Battleship element to visit
|
* @param ship the Battleship element to visit
|
||||||
*/
|
*/
|
||||||
void visit(Battleship ship);
|
void visit(Battleship ship);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visits a Shell element.
|
||||||
|
*
|
||||||
|
* @param shell the Shell element to visit
|
||||||
|
*/
|
||||||
|
void visit(Shell shell);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,5 +22,9 @@ public enum Sound {
|
|||||||
/**
|
/**
|
||||||
* Sound of a ship being destroyed.
|
* Sound of a ship being destroyed.
|
||||||
*/
|
*/
|
||||||
DESTROYED_SHIP
|
DESTROYED_SHIP,
|
||||||
|
/**
|
||||||
|
* Sound of a shot being fired.
|
||||||
|
*/
|
||||||
|
SHELL_FIRED
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
import pp.battleship.game.server.Player;
|
import pp.battleship.game.server.Player;
|
||||||
import pp.battleship.game.server.ServerGameLogic;
|
import pp.battleship.game.server.ServerGameLogic;
|
||||||
import pp.battleship.game.server.ServerSender;
|
import pp.battleship.game.server.ServerSender;
|
||||||
|
import pp.battleship.message.client.AnimationMessage;
|
||||||
import pp.battleship.message.client.ClientMessage;
|
import pp.battleship.message.client.ClientMessage;
|
||||||
import pp.battleship.message.client.MapMessage;
|
import pp.battleship.message.client.MapMessage;
|
||||||
import pp.battleship.message.client.ShootMessage;
|
import pp.battleship.message.client.ShootMessage;
|
||||||
@@ -56,8 +57,7 @@ public class BattleshipServer implements MessageListener<HostedConnection>, Conn
|
|||||||
try {
|
try {
|
||||||
manager.readConfiguration(new FileInputStream("logging.properties"));
|
manager.readConfiguration(new FileInputStream("logging.properties"));
|
||||||
LOGGER.log(Level.INFO, "Successfully read logging properties"); //NON-NLS
|
LOGGER.log(Level.INFO, "Successfully read logging properties"); //NON-NLS
|
||||||
}
|
} catch (IOException e) {
|
||||||
catch (IOException e) {
|
|
||||||
LOGGER.log(Level.INFO, e.getMessage());
|
LOGGER.log(Level.INFO, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,8 +92,7 @@ private void startServer() {
|
|||||||
myServer.start();
|
myServer.start();
|
||||||
registerListeners();
|
registerListeners();
|
||||||
LOGGER.log(Level.INFO, "Server started: {0}", myServer.isRunning()); //NON-NLS
|
LOGGER.log(Level.INFO, "Server started: {0}", myServer.isRunning()); //NON-NLS
|
||||||
}
|
} catch (IOException e) {
|
||||||
catch (IOException e) {
|
|
||||||
LOGGER.log(Level.ERROR, "Couldn't start server: {0}", e.getMessage()); //NON-NLS
|
LOGGER.log(Level.ERROR, "Couldn't start server: {0}", e.getMessage()); //NON-NLS
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -102,8 +101,7 @@ private void startServer() {
|
|||||||
private void processNextMessage() {
|
private void processNextMessage() {
|
||||||
try {
|
try {
|
||||||
pendingMessages.take().process(logic);
|
pendingMessages.take().process(logic);
|
||||||
}
|
} catch (InterruptedException ex) {
|
||||||
catch (InterruptedException ex) {
|
|
||||||
LOGGER.log(Level.INFO, "Interrupted while waiting for messages"); //NON-NLS
|
LOGGER.log(Level.INFO, "Interrupted while waiting for messages"); //NON-NLS
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
@@ -114,6 +112,7 @@ private void initializeSerializables() {
|
|||||||
Serializer.registerClass(StartBattleMessage.class);
|
Serializer.registerClass(StartBattleMessage.class);
|
||||||
Serializer.registerClass(MapMessage.class);
|
Serializer.registerClass(MapMessage.class);
|
||||||
Serializer.registerClass(ShootMessage.class);
|
Serializer.registerClass(ShootMessage.class);
|
||||||
|
Serializer.registerClass(AnimationMessage.class);
|
||||||
Serializer.registerClass(EffectMessage.class);
|
Serializer.registerClass(EffectMessage.class);
|
||||||
Serializer.registerClass(Battleship.class);
|
Serializer.registerClass(Battleship.class);
|
||||||
Serializer.registerClass(IntPoint.class);
|
Serializer.registerClass(IntPoint.class);
|
||||||
@@ -123,6 +122,7 @@ private void initializeSerializables() {
|
|||||||
private void registerListeners() {
|
private void registerListeners() {
|
||||||
myServer.addMessageListener(this, MapMessage.class);
|
myServer.addMessageListener(this, MapMessage.class);
|
||||||
myServer.addMessageListener(this, ShootMessage.class);
|
myServer.addMessageListener(this, ShootMessage.class);
|
||||||
|
myServer.addMessageListener(this, AnimationMessage.class);
|
||||||
myServer.addConnectionListener(this);
|
myServer.addConnectionListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user