Added Ex. 10
added client hosts a server added javadoc comments
This commit is contained in:
@@ -14,9 +14,12 @@
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import static pp.util.PreferencesUtils.getPreferences;
|
||||
import static pp.util.Util.loadSound;
|
||||
|
||||
/**
|
||||
* An application state that plays sounds.
|
||||
*/
|
||||
public class GameMusic extends AbstractAppState implements GameEventListener {
|
||||
private static final Logger LOGGER = System.getLogger(GameMusic.class.getName());
|
||||
private static final Preferences PREFERENCES = getPreferences(GameMusic.class);
|
||||
private static final String ENABLED_PREF = "enabled";
|
||||
private static final String VOLUME_PREF = "volume";
|
||||
@@ -25,16 +28,30 @@ public class GameMusic extends AbstractAppState implements GameEventListener {
|
||||
|
||||
private AudioNode backgroundMusic;
|
||||
|
||||
/**
|
||||
* Returns whether the music is enabled in the user preferences.
|
||||
*
|
||||
* @return true if music is enabled, false otherwise
|
||||
*/
|
||||
public static boolean enabledInPreferences(){
|
||||
return PREFERENCES.getBoolean(ENABLED_PREF, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the music volume level stored in the user preferences.
|
||||
*
|
||||
* @return the volume level as a float (default is 0.5f)
|
||||
*/
|
||||
public static float volumeInPreferences(){
|
||||
return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the game music system
|
||||
*
|
||||
* @param stateManager the state manager of the game
|
||||
* @param app the main application
|
||||
*/
|
||||
@Override
|
||||
public void initialize(AppStateManager stateManager, Application app) {
|
||||
super.initialize(stateManager, app);
|
||||
@@ -43,19 +60,12 @@ public void initialize(AppStateManager stateManager, Application app) {
|
||||
if (isEnabled()) playMusic();
|
||||
}
|
||||
|
||||
private AudioNode loadSound(Application app, String name) {
|
||||
try {
|
||||
final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
|
||||
sound.setLooping(false);
|
||||
sound.setPositional(false);
|
||||
return sound;
|
||||
}
|
||||
catch (AssetLoadException | AssetNotFoundException ex) {
|
||||
LOGGER.log(Level.ERROR, ex.getMessage(), ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the enabled state of this AppState.
|
||||
* Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)}
|
||||
*
|
||||
* @param enabled {@code true} to enable the AppState, {@code false} to disable it.
|
||||
*/
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
if(enabled && !isEnabled()){
|
||||
@@ -68,6 +78,9 @@ else if(!enabled && isEnabled()){
|
||||
PREFERENCES.putBoolean(ENABLED_PREF, enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the background music.
|
||||
*/
|
||||
public void playMusic(){
|
||||
if (backgroundMusic != null){
|
||||
backgroundMusic.play();
|
||||
@@ -75,7 +88,7 @@ public void playMusic(){
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops background music
|
||||
* Stops background music.
|
||||
*/
|
||||
public void stopMusic(){
|
||||
if (backgroundMusic != null){
|
||||
@@ -83,6 +96,11 @@ public void stopMusic(){
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the volume of the background music and saves the volume setting in user preferences.
|
||||
*
|
||||
* @param volume the volume level to set (0.0f to 1.0f)
|
||||
*/
|
||||
public void setMusicVolume(float volume){
|
||||
if(backgroundMusic != null){
|
||||
backgroundMusic.setVolume(volume);
|
||||
|
||||
@@ -10,9 +10,6 @@
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.app.state.AbstractAppState;
|
||||
import com.jme3.app.state.AppStateManager;
|
||||
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;
|
||||
@@ -22,6 +19,7 @@
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
import static pp.util.PreferencesUtils.getPreferences;
|
||||
import static pp.util.Util.loadSound;
|
||||
|
||||
/**
|
||||
* An application state that plays sounds.
|
||||
@@ -81,26 +79,6 @@ public void initialize(AppStateManager stateManager, Application app) {
|
||||
explosionSound = loadSound(app, "Sound/Effects/explosion.wav"); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a sound from the specified file.
|
||||
*
|
||||
* @param app The application
|
||||
* @param name The name of the sound file.
|
||||
* @return The loaded AudioNode.
|
||||
*/
|
||||
private AudioNode loadSound(Application app, String name) {
|
||||
try {
|
||||
final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
|
||||
sound.setLooping(false);
|
||||
sound.setPositional(false);
|
||||
return sound;
|
||||
}
|
||||
catch (AssetLoadException | AssetNotFoundException ex) {
|
||||
LOGGER.log(Level.ERROR, ex.getMessage(), ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plays the splash sound effect.
|
||||
*/
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
|
||||
package pp.battleship.client;
|
||||
|
||||
import com.simsilica.lemur.Checkbox;
|
||||
import com.simsilica.lemur.Container;
|
||||
import com.simsilica.lemur.Label;
|
||||
import com.simsilica.lemur.TextField;
|
||||
import com.simsilica.lemur.component.SpringGridLayout;
|
||||
import pp.battleship.client.clienthost.BattleshipServerClient;
|
||||
import pp.dialog.Dialog;
|
||||
import pp.dialog.DialogBuilder;
|
||||
import pp.dialog.SimpleDialog;
|
||||
@@ -36,7 +38,10 @@ class NetworkDialog extends SimpleDialog {
|
||||
private String hostname;
|
||||
private int portNumber;
|
||||
private Future<Object> connectionFuture;
|
||||
private Future<Object> serverFuture;
|
||||
private Dialog progressDialog;
|
||||
private BattleshipServerClient server;
|
||||
private final Checkbox clientHostCheckbox;
|
||||
|
||||
/**
|
||||
* Constructs a new NetworkDialog.
|
||||
@@ -56,7 +61,8 @@ class NetworkDialog extends SimpleDialog {
|
||||
input.addChild(host, 1);
|
||||
input.addChild(new Label(lookup("port.number") + ": "));
|
||||
input.addChild(port, 1);
|
||||
|
||||
clientHostCheckbox = new Checkbox("Host Server");
|
||||
input.addChild(clientHostCheckbox);
|
||||
DialogBuilder.simple(app.getDialogManager())
|
||||
.setTitle(lookup("server.dialog"))
|
||||
.setExtension(d -> d.addChild(input))
|
||||
@@ -77,11 +83,22 @@ private void connect() {
|
||||
hostname = host.getText().trim().isEmpty() ? LOCALHOST : host.getText();
|
||||
portNumber = Integer.parseInt(port.getText());
|
||||
openProgressDialog();
|
||||
|
||||
if (clientHostCheckbox.isChecked()){
|
||||
serverFuture = network.getApp().getExecutor().submit(this::initServer);
|
||||
|
||||
while (server == null || !server.isReady()){
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
connectionFuture = network.getApp().getExecutor().submit(this::initNetwork);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
network.getApp().errorDialog(lookup("port.must.be.integer"));
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,6 +126,22 @@ private Object initNetwork() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to initialize the server hosted by the client.
|
||||
*
|
||||
* @throws RuntimeException If an error occurs when starting the server.
|
||||
*/
|
||||
private Object initServer(){
|
||||
try {
|
||||
server = new BattleshipServerClient();
|
||||
server.run();
|
||||
return null;
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(Level.ERROR, "Error while starting server", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called by {@linkplain pp.dialog.DialogManager#update(float)} for periodically
|
||||
* updating this dialog. T
|
||||
@@ -127,6 +160,17 @@ public void update(float delta) {
|
||||
LOGGER.log(Level.WARNING, "Interrupted!", e); //NON-NLS
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
|
||||
if (serverFuture != null && serverFuture.isDone()) {
|
||||
try {
|
||||
serverFuture.get();
|
||||
} catch (ExecutionException e) {
|
||||
LOGGER.log(Level.ERROR, "Failed to start server", e.getCause());
|
||||
} catch (InterruptedException e) {
|
||||
LOGGER.log(Level.WARNING, "Server thread was interrupted", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,10 +2,21 @@
|
||||
|
||||
import com.simsilica.lemur.Slider;
|
||||
|
||||
/**
|
||||
* Represents a volume slider for controlling the background music volume in the Battleship game.
|
||||
* This class extends the {@link Slider} class and interfaces with the {@link GameMusic} instance
|
||||
* to adjust the volume settings based on user input.
|
||||
*/
|
||||
public class VolumeSlider extends Slider {
|
||||
private GameMusic gameMusic;
|
||||
private final GameMusic gameMusic;
|
||||
private float volume;
|
||||
|
||||
/**
|
||||
* Constructs a new VolumeSlider instance and initializes it with the current volume level
|
||||
* from the game music preferences.
|
||||
*
|
||||
* @param gameMusic the instance of {@link GameMusic} to control music volume
|
||||
*/
|
||||
public VolumeSlider(GameMusic gameMusic){
|
||||
super();
|
||||
this.gameMusic = gameMusic;
|
||||
@@ -13,6 +24,11 @@ public VolumeSlider(GameMusic gameMusic){
|
||||
getModel().setPercent(volume);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the volume setting based on the current slider position.
|
||||
* If the slider's percent value has changed, it updates the music volume
|
||||
* in the associated {@link GameMusic} instance.
|
||||
*/
|
||||
public void update(){
|
||||
if(getModel().getPercent() != volume){
|
||||
this.volume = (float) getModel().getPercent();
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.battleship.client.clienthost;
|
||||
|
||||
import com.jme3.network.ConnectionListener;
|
||||
import com.jme3.network.HostedConnection;
|
||||
import com.jme3.network.Message;
|
||||
import com.jme3.network.MessageListener;
|
||||
import com.jme3.network.Network;
|
||||
import com.jme3.network.Server;
|
||||
import com.jme3.network.serializing.Serializer;
|
||||
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.ClientMessage;
|
||||
import pp.battleship.message.client.MapMessage;
|
||||
import pp.battleship.message.client.ShootMessage;
|
||||
import pp.battleship.message.server.EffectMessage;
|
||||
import pp.battleship.message.server.GameDetails;
|
||||
import pp.battleship.message.server.ServerMessage;
|
||||
import pp.battleship.message.server.StartBattleMessage;
|
||||
import pp.battleship.model.Battleship;
|
||||
import pp.battleship.model.IntPoint;
|
||||
import pp.battleship.model.Shot;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.LogManager;
|
||||
|
||||
/**
|
||||
* Server implementing the visitor pattern as MessageReceiver for ClientMessages
|
||||
*/
|
||||
public class BattleshipServerClient implements MessageListener<HostedConnection>, ConnectionListener, ServerSender {
|
||||
private static final Logger LOGGER = System.getLogger(BattleshipServerClient.class.getName());
|
||||
private static final File CONFIG_FILE = new File("server.properties");
|
||||
|
||||
private final BattleshipConfig config = new BattleshipConfig();
|
||||
private Server myServer;
|
||||
private final ServerGameLogic logic;
|
||||
private final BlockingQueue<ReceivedMessageClient> pendingMessages = new LinkedBlockingQueue<>();
|
||||
|
||||
static {
|
||||
// Configure logging
|
||||
LogManager manager = LogManager.getLogManager();
|
||||
try {
|
||||
manager.readConfiguration(new FileInputStream("logging.properties"));
|
||||
LOGGER.log(Level.INFO, "Successfully read logging properties"); //NON-NLS
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOGGER.log(Level.INFO, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the server and reads the configuration from the specified file.
|
||||
* Initializes the game logic and sets up logging.
|
||||
*/
|
||||
public BattleshipServerClient() {
|
||||
config.readFromIfExists(CONFIG_FILE);
|
||||
LOGGER.log(Level.INFO, "Configuration: {0}", config); //NON-NLS
|
||||
logic = new ServerGameLogic(this, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the server is ready.
|
||||
*
|
||||
* @return true if the server is running, false otherwise
|
||||
*/
|
||||
public boolean isReady() {
|
||||
return myServer != null && myServer.isRunning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server and continuously processes incoming messages.
|
||||
*/
|
||||
public void run() {
|
||||
startServer();
|
||||
while (true)
|
||||
processNextMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the server by creating a network server on the specified port.
|
||||
*/
|
||||
private void startServer() {
|
||||
try {
|
||||
LOGGER.log(Level.INFO, "Starting server..."); //NON-NLS
|
||||
myServer = Network.createServer(config.getPort());
|
||||
initializeSerializables();
|
||||
myServer.start();
|
||||
registerListeners();
|
||||
LOGGER.log(Level.INFO, "Server started: {0}", myServer.isRunning()); //NON-NLS
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOGGER.log(Level.ERROR, "Couldn't start server: {0}", e.getMessage()); //NON-NLS
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the next message in the queue.
|
||||
*/
|
||||
private void processNextMessage() {
|
||||
try {
|
||||
pendingMessages.take().process(logic);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
LOGGER.log(Level.INFO, "Interrupted while waiting for messages"); //NON-NLS
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers all serializable message classes for network transmission.
|
||||
*/
|
||||
private void initializeSerializables() {
|
||||
Serializer.registerClass(GameDetails.class);
|
||||
Serializer.registerClass(StartBattleMessage.class);
|
||||
Serializer.registerClass(MapMessage.class);
|
||||
Serializer.registerClass(ShootMessage.class);
|
||||
Serializer.registerClass(EffectMessage.class);
|
||||
Serializer.registerClass(Battleship.class);
|
||||
Serializer.registerClass(IntPoint.class);
|
||||
Serializer.registerClass(Shot.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the message and connection listeners for the server.
|
||||
*/
|
||||
private void registerListeners() {
|
||||
myServer.addMessageListener(this, MapMessage.class);
|
||||
myServer.addMessageListener(this, ShootMessage.class);
|
||||
myServer.addConnectionListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageReceived(HostedConnection source, Message message) {
|
||||
LOGGER.log(Level.INFO, "message received from {0}: {1}", source.getId(), message); //NON-NLS
|
||||
if (message instanceof ClientMessage clientMessage)
|
||||
pendingMessages.add(new ReceivedMessageClient(clientMessage, source.getId()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionAdded(Server server, HostedConnection hostedConnection) {
|
||||
LOGGER.log(Level.INFO, "new connection {0}", hostedConnection); //NON-NLS
|
||||
logic.addPlayer(hostedConnection.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connectionRemoved(Server server, HostedConnection hostedConnection) {
|
||||
LOGGER.log(Level.INFO, "connection closed: {0}", hostedConnection); //NON-NLS
|
||||
final Player player = logic.getPlayerById(hostedConnection.getId());
|
||||
if (player == null)
|
||||
LOGGER.log(Level.INFO, "closed connection does not belong to an active player"); //NON-NLS
|
||||
else { //NON-NLS
|
||||
LOGGER.log(Level.INFO, "closed connection belongs to {0}", player); //NON-NLS
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuts down the server and closes all active connections.
|
||||
*
|
||||
* @param exitValue the exit code to terminate the program with
|
||||
*/
|
||||
private void exit(int exitValue) { //NON-NLS
|
||||
LOGGER.log(Level.INFO, "close request"); //NON-NLS
|
||||
if (myServer != null)
|
||||
for (HostedConnection client : myServer.getConnections()) //NON-NLS
|
||||
if (client != null) client.close("Game over"); //NON-NLS
|
||||
System.exit(exitValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the specified message to the specified connection.
|
||||
*
|
||||
* @param id the connection id
|
||||
* @param message the message
|
||||
*/
|
||||
public void send(int id, ServerMessage message) {
|
||||
if (myServer == null || !myServer.isRunning()) {
|
||||
LOGGER.log(Level.ERROR, "no server running when trying to send {0}", message); //NON-NLS
|
||||
return;
|
||||
}
|
||||
final HostedConnection connection = myServer.getConnection(id);
|
||||
if (connection != null)
|
||||
connection.send(message);
|
||||
else
|
||||
LOGGER.log(Level.ERROR, "there is no connection with id={0}", id); //NON-NLS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
////////////////////////////////////////
|
||||
// Programming project code
|
||||
// UniBw M, 2022, 2023, 2024
|
||||
// www.unibw.de/inf2
|
||||
// (c) Mark Minas (mark.minas@unibw.de)
|
||||
////////////////////////////////////////
|
||||
|
||||
package pp.battleship.client.clienthost;
|
||||
|
||||
import pp.battleship.message.client.ClientInterpreter;
|
||||
import pp.battleship.message.client.ClientMessage;
|
||||
|
||||
record ReceivedMessageClient(ClientMessage message, int from) {
|
||||
void process(ClientInterpreter interpreter) {
|
||||
message.accept(interpreter, from);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
import com.jme3.math.Vector3f;
|
||||
|
||||
/**
|
||||
* Enum representing different ship models for the Battleship game.
|
||||
* Each ship model has a corresponding 3D model path, scale, translation, color texture, and optional bump texture.
|
||||
*/
|
||||
public enum ShipModel {
|
||||
SHIP1("Models/Ships/1/ship1.j3o",0.15f, new Vector3f(0f, 0f, 0f), "Models/Ships/1/ship1_color.png", null),
|
||||
SHIP2("Models/Ships/2/ship2.j3o",0.03f, new Vector3f(0f, 0.2f, 0f), "Models/Ships/2/ship2.jpg", null),
|
||||
@@ -14,6 +18,15 @@ public enum ShipModel {
|
||||
private final String colorPath;
|
||||
private final String bumpPath;
|
||||
|
||||
/**
|
||||
* Constructs a new ShipModel with the specified parameters.
|
||||
*
|
||||
* @param modelPath the path to the 3D model of the ship
|
||||
* @param modelScale the scale factor to be applied to the model
|
||||
* @param translation the translation to be applied to the model
|
||||
* @param colorPath the path to the color texture of the model
|
||||
* @param bumpPath the optional path to the bump texture of the model (may be null)
|
||||
*/
|
||||
ShipModel(String modelPath, float modelScale, Vector3f translation, String colorPath, String bumpPath){
|
||||
this.modelPath = modelPath;
|
||||
this.modelScale = modelScale;
|
||||
@@ -22,22 +35,47 @@ public enum ShipModel {
|
||||
this.bumpPath = bumpPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the bump texture of the ship model.
|
||||
*
|
||||
* @return the bump texture path, or null if no bump texture is defined
|
||||
*/
|
||||
public String getBumpPath() {
|
||||
return bumpPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the color texture of the ship model.
|
||||
*
|
||||
* @return the color texture path
|
||||
*/
|
||||
public String getColorPath() {
|
||||
return colorPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scale factor of the ship model.
|
||||
*
|
||||
* @return the scale factor
|
||||
*/
|
||||
public float getScale() {
|
||||
return modelScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path to the 3D model of the ship.
|
||||
*
|
||||
* @return the model path
|
||||
*/
|
||||
public String getPath() {
|
||||
return modelPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the translation to be applied to the ship model.
|
||||
*
|
||||
* @return the translation vector
|
||||
*/
|
||||
public Vector3f getTranslation() {
|
||||
return translation;
|
||||
}
|
||||
|
||||
@@ -155,7 +155,6 @@ public void received(MapMessage msg, int from) {
|
||||
|
||||
/**
|
||||
* Validates the placement of battleships on the map.
|
||||
*
|
||||
* Ensures that:
|
||||
* <ul>
|
||||
* <li>The number of ships matches the configuration.</li>
|
||||
|
||||
@@ -52,10 +52,14 @@ public ShipMapDTO(ShipMap map) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current ship map fits the game details provided.
|
||||
* Checks if the ship map is compatible with the provided game details.
|
||||
*
|
||||
* @param details the game details to be matched against
|
||||
* @return true if the ship map fits the game details, false otherwise
|
||||
* This method verifies that the ship map's dimensions match the game details,
|
||||
* that the total number of ships is correct, and that the ship placements
|
||||
* do not overlap and stay within the map boundaries.
|
||||
*
|
||||
* @param details the game details containing required dimensions and ship configurations
|
||||
* @return true if the ship map fits the game details; false otherwise
|
||||
*/
|
||||
public boolean fits(GameDetails details) {
|
||||
if (width != details.getWidth() || height != details.getHeight())
|
||||
|
||||
@@ -7,6 +7,14 @@
|
||||
|
||||
package pp.util;
|
||||
|
||||
import com.jme3.app.Application;
|
||||
import com.jme3.asset.AssetLoadException;
|
||||
import com.jme3.asset.AssetNotFoundException;
|
||||
import com.jme3.audio.AudioData;
|
||||
import com.jme3.audio.AudioNode;
|
||||
|
||||
import java.lang.System.Logger;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -17,6 +25,8 @@
|
||||
* A class with auxiliary functions.
|
||||
*/
|
||||
public class Util {
|
||||
private static final Logger LOGGER = System.getLogger(Util.class.getName());
|
||||
|
||||
private Util() { /* do not instantiate */ }
|
||||
|
||||
/**
|
||||
@@ -87,4 +97,24 @@ public static <T, E extends T> Set<T> add(Set<T> set, E element) {
|
||||
newSet.add(element);
|
||||
return newSet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a sound from the specified file.
|
||||
*
|
||||
* @param app The application
|
||||
* @param name The name of the sound file.
|
||||
* @return The loaded AudioNode.
|
||||
*/
|
||||
public static AudioNode loadSound(Application app, String name) {
|
||||
try {
|
||||
final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
|
||||
sound.setLooping(false);
|
||||
sound.setPositional(false);
|
||||
return sound;
|
||||
}
|
||||
catch (AssetLoadException | AssetNotFoundException ex) {
|
||||
LOGGER.log(Level.ERROR, ex.getMessage(), ex);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user