Compare commits
32 Commits
reworkedGu
...
b_schmidt_
Author | SHA1 | Date | |
---|---|---|---|
|
6c8da2f4a0 | ||
|
446cb43abb | ||
|
47610d9e11 | ||
|
563784dbab | ||
|
0e5ffcc501 | ||
|
1288d6d1ca | ||
|
520b98f693 | ||
|
8e02c3919e | ||
|
4df5a1a7d1 | ||
|
3bdc9c20dc | ||
|
f541f3edc0 | ||
|
c26d7b5422 | ||
|
2e294896d8 | ||
|
f02a3107a0 | ||
|
141d817421 | ||
|
fe1bf7746b | ||
|
2c7820491a | ||
|
2dd70c10b5 | ||
|
6475b63a22 | ||
|
ae41ec4007 | ||
|
b9309b6f5f | ||
|
ae5ba034fe | ||
|
682da199e8 | ||
|
8a7446d81c | ||
|
cb57187f98 | ||
|
958f4f6a13 | ||
|
af08a84dc3 | ||
|
53df6011db | ||
|
4bc3b3dc33 | ||
|
cf780f0995 | ||
|
05618c0793 | ||
|
3ee095215a |
1
.gitignore
vendored
@@ -4,7 +4,6 @@ build
|
|||||||
|
|
||||||
# VSC
|
# VSC
|
||||||
bin
|
bin
|
||||||
.vscode
|
|
||||||
|
|
||||||
# IntelliJ
|
# IntelliJ
|
||||||
*.iml
|
*.iml
|
||||||
|
@@ -1,18 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="MonopolyApp (Mac)" type="Application" factoryName="Application"
|
|
||||||
singleton="false">
|
|
||||||
<option name="MAIN_CLASS_NAME" value="pp.monopoly.client.MonopolyApp"/>
|
|
||||||
<module name="Projekte.monopoly.client.main"/>
|
|
||||||
<option name="VM_PARAMETERS" value="-XstartOnFirstThread"/>
|
|
||||||
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$"/>
|
|
||||||
<extension name="coverage">
|
|
||||||
<pattern>
|
|
||||||
<option name="PATTERN" value="pp.monopoly.client.*"/>
|
|
||||||
<option name="ENABLED" value="true"/>
|
|
||||||
</pattern>
|
|
||||||
</extension>
|
|
||||||
<method v="2">
|
|
||||||
<option name="Make" enabled="true"/>
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
@@ -1,18 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="MonopolyApp" type="Application" factoryName="Application" singleton="false"
|
|
||||||
nameIsGenerated="true">
|
|
||||||
<option name="MAIN_CLASS_NAME" value="pp.monopoly.client.MonopolyApp"/>
|
|
||||||
<module name="Projekte.monopoly.client.main"/>
|
|
||||||
<option name="VM_PARAMETERS" value="-Djava.util.logging.config.file=logging.properties"/>
|
|
||||||
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$"/>
|
|
||||||
<extension name="coverage">
|
|
||||||
<pattern>
|
|
||||||
<option name="PATTERN" value="pp.monopoly.client.*"/>
|
|
||||||
<option name="ENABLED" value="true"/>
|
|
||||||
</pattern>
|
|
||||||
</extension>
|
|
||||||
<method v="2">
|
|
||||||
<option name="Make" enabled="true"/>
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
@@ -1,17 +0,0 @@
|
|||||||
<component name="ProjectRunConfigurationManager">
|
|
||||||
<configuration default="false" name="MonopolyServer" type="Application" factoryName="Application"
|
|
||||||
nameIsGenerated="true">
|
|
||||||
<option name="MAIN_CLASS_NAME" value="pp.monopoly.server.MonopolyServer"/>
|
|
||||||
<module name="Projekte.monopoly.server.main"/>
|
|
||||||
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$"/>
|
|
||||||
<extension name="coverage">
|
|
||||||
<pattern>
|
|
||||||
<option name="PATTERN" value="pp.monopoly.server.*"/>
|
|
||||||
<option name="ENABLED" value="true"/>
|
|
||||||
</pattern>
|
|
||||||
</extension>
|
|
||||||
<method v="2">
|
|
||||||
<option name="Make" enabled="true"/>
|
|
||||||
</method>
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
@@ -7,14 +7,18 @@ description = 'Battleship Client'
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation project(":jme-common")
|
implementation project(":jme-common")
|
||||||
implementation project(":battleship:model")
|
implementation project(":battleship:model")
|
||||||
|
implementation 'org.jmonkeyengine:jme3-core:3.6.0-stable'
|
||||||
|
implementation 'org.jmonkeyengine:jme3-effects:3.6.0-stable'
|
||||||
|
|
||||||
|
|
||||||
implementation libs.jme3.desktop
|
implementation libs.jme3.desktop
|
||||||
implementation libs.jme3.effects
|
|
||||||
|
|
||||||
runtimeOnly libs.jme3.awt.dialogs
|
runtimeOnly libs.jme3.awt.dialogs
|
||||||
runtimeOnly libs.jme3.plugins
|
runtimeOnly libs.jme3.plugins
|
||||||
runtimeOnly libs.jme3.jogg
|
runtimeOnly libs.jme3.jogg
|
||||||
runtimeOnly libs.jme3.testdata
|
runtimeOnly libs.jme3.testdata
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
|
@@ -22,8 +22,8 @@ import com.simsilica.lemur.GuiGlobals;
|
|||||||
import com.simsilica.lemur.style.BaseStyles;
|
import com.simsilica.lemur.style.BaseStyles;
|
||||||
import pp.battleship.client.gui.BattleAppState;
|
import pp.battleship.client.gui.BattleAppState;
|
||||||
import pp.battleship.client.gui.EditorAppState;
|
import pp.battleship.client.gui.EditorAppState;
|
||||||
import pp.battleship.client.gui.GameMusic;
|
|
||||||
import pp.battleship.client.gui.SeaAppState;
|
import pp.battleship.client.gui.SeaAppState;
|
||||||
|
import pp.battleship.client.GameMusic;
|
||||||
import pp.battleship.game.client.BattleshipClient;
|
import pp.battleship.game.client.BattleshipClient;
|
||||||
import pp.battleship.game.client.ClientGameLogic;
|
import pp.battleship.game.client.ClientGameLogic;
|
||||||
import pp.battleship.game.client.ServerConnection;
|
import pp.battleship.game.client.ServerConnection;
|
||||||
@@ -269,6 +269,7 @@ public class BattleshipApp extends SimpleApplication implements BattleshipClient
|
|||||||
|
|
||||||
attachGameSound();
|
attachGameSound();
|
||||||
attachGameMusic();
|
attachGameMusic();
|
||||||
|
|
||||||
stateManager.attachAll(new EditorAppState(), new BattleAppState(), new SeaAppState());
|
stateManager.attachAll(new EditorAppState(), new BattleAppState(), new SeaAppState());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,12 +284,12 @@ public class BattleshipApp extends SimpleApplication implements BattleshipClient
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attaches the background music state and sets its initial enabled state.
|
* Attaches the music state and sets its initial enabled state.
|
||||||
*/
|
*/
|
||||||
private void attachGameMusic() {
|
private void attachGameMusic() {
|
||||||
final GameMusic gameSound = new GameMusic();
|
final GameMusic gameMusic = new GameMusic();
|
||||||
gameSound.setEnabled(GameMusic.enabledInPreferences());
|
gameMusic.setEnabled(GameMusic.enabledInPreferences());
|
||||||
stateManager.attach(gameSound);
|
stateManager.attach(gameMusic);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -1,10 +1,5 @@
|
|||||||
package pp.battleship.client.gui;
|
package pp.battleship.client;
|
||||||
|
|
||||||
import static pp.util.PreferencesUtils.getPreferences;
|
|
||||||
|
|
||||||
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.Application;
|
||||||
import com.jme3.app.state.AbstractAppState;
|
import com.jme3.app.state.AbstractAppState;
|
||||||
@@ -14,8 +9,15 @@ import com.jme3.asset.AssetNotFoundException;
|
|||||||
import com.jme3.audio.AudioData;
|
import com.jme3.audio.AudioData;
|
||||||
import com.jme3.audio.AudioNode;
|
import com.jme3.audio.AudioNode;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.System.Logger;
|
||||||
|
import java.lang.System.Logger.Level;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
|
import static pp.util.PreferencesUtils.getPreferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the background music beeing played. Is able to start and stop the music. Set the Volume of the Audio.
|
* An application state that plays music.
|
||||||
*/
|
*/
|
||||||
public class GameMusic extends AbstractAppState {
|
public class GameMusic extends AbstractAppState {
|
||||||
private static final Logger LOGGER = System.getLogger(GameMusic.class.getName());
|
private static final Logger LOGGER = System.getLogger(GameMusic.class.getName());
|
||||||
@@ -23,7 +25,9 @@ public class GameMusic extends AbstractAppState{
|
|||||||
private static final String ENABLED_PREF = "enabled"; //NON-NLS
|
private static final String ENABLED_PREF = "enabled"; //NON-NLS
|
||||||
private static final String VOLUME_PREF = "volume"; //NON-NLS
|
private static final String VOLUME_PREF = "volume"; //NON-NLS
|
||||||
|
|
||||||
private AudioNode music;
|
private AudioNode battleMusic;
|
||||||
|
private AudioNode menuMusicModern;
|
||||||
|
private AudioNode menuMusicTraditional;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if sound is enabled in the preferences.
|
* Checks if sound is enabled in the preferences.
|
||||||
@@ -33,18 +37,47 @@ public class GameMusic extends AbstractAppState{
|
|||||||
public static boolean enabledInPreferences() {
|
public static boolean enabledInPreferences() {
|
||||||
return PREFERENCES.getBoolean(ENABLED_PREF, true);
|
return PREFERENCES.getBoolean(ENABLED_PREF, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if sound is enabled in the preferences.
|
* Checks enabled volume in preferences.
|
||||||
|
*
|
||||||
|
* @return {@code float} if a volumePreference is set, the volume is set to the Value in PREFERENCES,
|
||||||
|
* {@code 0.25f} if no volumePreference is set in PREFERENCES, the Volume is set to a default of 0.25f
|
||||||
*
|
*
|
||||||
* @return float to which the volume is set
|
|
||||||
*/
|
*/
|
||||||
public static float volumeInPreferences() {
|
|
||||||
return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
|
public static float volumePreference() {
|
||||||
|
return PREFERENCES.getFloat(VOLUME_PREF, 0.25f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the sound effects for the game.
|
* Toggles the game sound on or off.
|
||||||
|
*/
|
||||||
|
public void toggleSound() {
|
||||||
|
setEnabled(!isEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 (isEnabled() == enabled) return;
|
||||||
|
else if (!isEnabled() && enabled) {
|
||||||
|
if (menuMusicModern != null) menuMusicModern.play();
|
||||||
|
} else if (isEnabled() && !enabled) {
|
||||||
|
if (menuMusicModern != null) menuMusicModern.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
super.setEnabled(enabled);
|
||||||
|
LOGGER.log(Level.INFO, "Music enabled: {0}", enabled); //NON-NLS
|
||||||
|
PREFERENCES.putBoolean(ENABLED_PREF, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the music.
|
||||||
* Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
|
* Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
|
||||||
*
|
*
|
||||||
* @param stateManager The state manager
|
* @param stateManager The state manager
|
||||||
@@ -53,16 +86,17 @@ public class GameMusic extends AbstractAppState{
|
|||||||
@Override
|
@Override
|
||||||
public void initialize(AppStateManager stateManager, Application app) {
|
public void initialize(AppStateManager stateManager, Application app) {
|
||||||
super.initialize(stateManager, app);
|
super.initialize(stateManager, app);
|
||||||
music = loadSound(app, "Sound/background.ogg");
|
menuMusicModern =loadSound(app, "Sound/BackgroundMusic/menu-music-modern.ogg");
|
||||||
setVolume(volumeInPreferences());
|
setVolume(volumePreference());
|
||||||
music.setLooping(true);
|
menuMusicModern.setLooping(true);
|
||||||
if (isEnabled() && music != null) {
|
if (isEnabled() && menuMusicModern != null) {
|
||||||
music.play();
|
menuMusicModern.play();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a sound from the specified file.
|
* Loads the music from the specified file.
|
||||||
*
|
*
|
||||||
* @param app The application
|
* @param app The application
|
||||||
* @param name The name of the sound file.
|
* @param name The name of the sound file.
|
||||||
@@ -82,41 +116,14 @@ public class GameMusic extends AbstractAppState{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the enabled state of this AppState.
|
* Sets the vol param to the level set in PREFERENCES
|
||||||
* Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)}
|
*
|
||||||
|
* @param vol Volume level of the music as indicated by the Volume control Slider
|
||||||
*
|
*
|
||||||
* @param enabled {@code true} to enable the AppState, {@code false} to disable it.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
if (isEnabled() == enabled) return;
|
|
||||||
|
|
||||||
if (music != null) {
|
|
||||||
if (enabled) {
|
|
||||||
music.play();
|
|
||||||
} else {
|
|
||||||
music.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
super.setEnabled(enabled);
|
|
||||||
LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); //NON-NLS
|
|
||||||
PREFERENCES.putBoolean(ENABLED_PREF, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the game sound on or off.
|
|
||||||
*/
|
|
||||||
public void toggleSound() {
|
|
||||||
setEnabled(!isEnabled());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the volume of music
|
|
||||||
* @param vol the volume to which the music should be set
|
|
||||||
*/
|
*/
|
||||||
public void setVolume(float vol){
|
public void setVolume(float vol){
|
||||||
music.setVolume(vol);
|
menuMusicModern.setVolume(vol);
|
||||||
PREFERENCES.putFloat(VOLUME_PREF, vol);
|
PREFERENCES.putFloat(VOLUME_PREF, vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@@ -34,7 +34,6 @@ 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 shellFlyingSound;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if sound is enabled in the preferences.
|
* 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
|
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
|
||||||
shellFlyingSound = loadSound(app, "Sound/Effects/shell_flying.wav");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -126,27 +124,12 @@ public class GameSound extends AbstractAppState implements GameEventListener {
|
|||||||
shipDestroyedSound.playInstance();
|
shipDestroyedSound.playInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays the shell flying sound effect.
|
|
||||||
*/
|
|
||||||
public void shellFly() {
|
|
||||||
if (isEnabled() && shellFlyingSound != null) {
|
|
||||||
shellFlyingSound.playInstance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a recieved {@code SoundEvent} and plays the according sound.
|
|
||||||
*
|
|
||||||
* @param event the Sound event to be processed
|
|
||||||
*/
|
|
||||||
@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_FLYING -> shellFly();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,22 +7,20 @@
|
|||||||
|
|
||||||
package pp.battleship.client;
|
package pp.battleship.client;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.prefs.Preferences;
|
|
||||||
|
|
||||||
import com.simsilica.lemur.Button;
|
import com.simsilica.lemur.Button;
|
||||||
import com.simsilica.lemur.Checkbox;
|
import com.simsilica.lemur.Checkbox;
|
||||||
import com.simsilica.lemur.Label;
|
import com.simsilica.lemur.Label;
|
||||||
import com.simsilica.lemur.style.ElementId;
|
import com.simsilica.lemur.style.ElementId;
|
||||||
|
import pp.battleship.client.gui.VolumeControl;
|
||||||
import static pp.battleship.Resources.lookup;
|
|
||||||
import pp.battleship.client.gui.GameMusic;
|
|
||||||
import pp.dialog.Dialog;
|
import pp.dialog.Dialog;
|
||||||
import pp.dialog.StateCheckboxModel;
|
import pp.dialog.StateCheckboxModel;
|
||||||
import pp.dialog.TextInputDialog;
|
import pp.dialog.TextInputDialog;
|
||||||
import pp.battleship.client.gui.VolumeSlider;
|
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
|
import static pp.battleship.Resources.lookup;
|
||||||
import static pp.util.PreferencesUtils.getPreferences;
|
import static pp.util.PreferencesUtils.getPreferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,29 +34,35 @@ class Menu extends Dialog {
|
|||||||
private final BattleshipApp app;
|
private final BattleshipApp app;
|
||||||
private final Button loadButton = new Button(lookup("menu.map.load"));
|
private final Button loadButton = new Button(lookup("menu.map.load"));
|
||||||
private final Button saveButton = new Button(lookup("menu.map.save"));
|
private final Button saveButton = new Button(lookup("menu.map.save"));
|
||||||
private final VolumeSlider slider;
|
private final VolumeControl volumeSlider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the Menu dialog for the Battleship application.+
|
* Constructs the Menu dialog for the Battleship application.
|
||||||
*
|
*
|
||||||
* @param app the BattleshipApp instance
|
* @param app the BattleshipApp instance
|
||||||
*/
|
*/
|
||||||
public Menu(BattleshipApp app) {
|
public Menu(BattleshipApp app) {
|
||||||
super(app.getDialogManager());
|
super(app.getDialogManager());
|
||||||
this.app = app;
|
this.app = app;
|
||||||
slider = new VolumeSlider(app.getStateManager().getState(GameMusic.class));
|
|
||||||
addChild(new Label(lookup("battleship.name"), new ElementId("header")));//NON-NLS
|
addChild(new Label(lookup("battleship.name"), new ElementId("header")));//NON-NLS
|
||||||
|
|
||||||
addChild(new Checkbox(lookup("menu.sound-enabled"), new StateCheckboxModel(app, GameSound.class)));
|
addChild(new Checkbox(lookup("menu.sound-enabled"), new StateCheckboxModel(app, GameSound.class)));
|
||||||
|
|
||||||
addChild(new Checkbox(lookup("menu.background-sound-enabled"), new StateCheckboxModel(app, GameMusic.class)));
|
addChild(new Checkbox(lookup("menu.background-sound-enabled"), new StateCheckboxModel(app, GameMusic.class)));
|
||||||
|
|
||||||
addChild(slider);
|
volumeSlider = new VolumeControl(app.getStateManager().getState(GameMusic.class));
|
||||||
|
|
||||||
addChild(loadButton).addClickCommands(s -> ifTopDialog(this::loadDialog));
|
addChild(volumeSlider);
|
||||||
addChild(saveButton).addClickCommands(s -> ifTopDialog(this::saveDialog));
|
|
||||||
addChild(new Button(lookup("menu.return-to-game"))).addClickCommands(s -> ifTopDialog(this::close));
|
|
||||||
addChild(new Button(lookup("menu.quit"))).addClickCommands(s -> ifTopDialog(app::closeApp));
|
|
||||||
|
|
||||||
|
addChild(loadButton)
|
||||||
|
.addClickCommands(s -> ifTopDialog(this::loadDialog));
|
||||||
|
addChild(saveButton)
|
||||||
|
.addClickCommands(s -> ifTopDialog(this::saveDialog));
|
||||||
|
addChild(new Button(lookup("menu.return-to-game")))
|
||||||
|
.addClickCommands(s -> ifTopDialog(this::close));
|
||||||
|
addChild(new Button(lookup("menu.quit")))
|
||||||
|
.addClickCommands(s -> ifTopDialog(app::closeApp));
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -71,11 +75,15 @@ class Menu extends Dialog {
|
|||||||
saveButton.setEnabled(app.getGameLogic().maySaveMap());
|
saveButton.setEnabled(app.getGameLogic().maySaveMap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the position of the volume control slider.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void update(float delta) {
|
public void update(float delta) {
|
||||||
slider.update();
|
volumeSlider.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* As an escape action, this method closes the menu if it is the top dialog.
|
* As an escape action, this method closes the menu if it is the top dialog.
|
||||||
*/
|
*/
|
||||||
|
@@ -7,22 +7,22 @@
|
|||||||
|
|
||||||
package pp.battleship.client;
|
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.server.BattleshipSelfhostServer;
|
||||||
|
import pp.dialog.Dialog;
|
||||||
|
import pp.dialog.DialogBuilder;
|
||||||
|
import pp.dialog.SimpleDialog;
|
||||||
|
|
||||||
import java.lang.System.Logger;
|
import java.lang.System.Logger;
|
||||||
import java.lang.System.Logger.Level;
|
import java.lang.System.Logger.Level;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
|
|
||||||
import com.simsilica.lemur.Button;
|
|
||||||
import com.simsilica.lemur.Container;
|
|
||||||
import com.simsilica.lemur.Label;
|
|
||||||
import com.simsilica.lemur.TextField;
|
|
||||||
import com.simsilica.lemur.component.SpringGridLayout;
|
|
||||||
|
|
||||||
import static pp.battleship.Resources.lookup;
|
import static pp.battleship.Resources.lookup;
|
||||||
import pp.battleship.server.BattleshipServer;
|
|
||||||
import pp.dialog.Dialog;
|
|
||||||
import pp.dialog.DialogBuilder;
|
|
||||||
import pp.dialog.SimpleDialog;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a dialog for setting up a network connection in the Battleship game.
|
* Represents a dialog for setting up a network connection in the Battleship game.
|
||||||
@@ -31,12 +31,11 @@ import pp.dialog.SimpleDialog;
|
|||||||
class NetworkDialog extends SimpleDialog {
|
class NetworkDialog extends SimpleDialog {
|
||||||
private static final Logger LOGGER = System.getLogger(NetworkDialog.class.getName());
|
private static final Logger LOGGER = System.getLogger(NetworkDialog.class.getName());
|
||||||
private static final String LOCALHOST = "localhost"; //NON-NLS
|
private static final String LOCALHOST = "localhost"; //NON-NLS
|
||||||
private static final String DEFAULT_PORT = "42069"; //NON-NLS
|
private static final String DEFAULT_PORT = "1234"; //NON-NLS
|
||||||
private final NetworkSupport network;
|
private final NetworkSupport network;
|
||||||
private final TextField host = new TextField(LOCALHOST);
|
private final TextField host = new TextField(LOCALHOST);
|
||||||
private final TextField port = new TextField(DEFAULT_PORT);
|
private final TextField port = new TextField(DEFAULT_PORT);
|
||||||
// private final Button serverButton = new Button(lookup("client.server-star"));
|
private static final Checkbox HOST = new Checkbox(lookup("server.host"));
|
||||||
private final Button serverButton = new Button(lookup("client.server-start"));
|
|
||||||
private String hostname;
|
private String hostname;
|
||||||
private int portNumber;
|
private int portNumber;
|
||||||
private Future<Object> connectionFuture;
|
private Future<Object> connectionFuture;
|
||||||
@@ -60,6 +59,7 @@ class NetworkDialog extends SimpleDialog {
|
|||||||
input.addChild(host, 1);
|
input.addChild(host, 1);
|
||||||
input.addChild(new Label(lookup("port.number") + ": "));
|
input.addChild(new Label(lookup("port.number") + ": "));
|
||||||
input.addChild(port, 1);
|
input.addChild(port, 1);
|
||||||
|
input.addChild(HOST).addClickCommands(s -> ifTopDialog(this::startClientServer));
|
||||||
|
|
||||||
DialogBuilder.simple(app.getDialogManager())
|
DialogBuilder.simple(app.getDialogManager())
|
||||||
.setTitle(lookup("server.dialog"))
|
.setTitle(lookup("server.dialog"))
|
||||||
@@ -69,9 +69,6 @@ class NetworkDialog extends SimpleDialog {
|
|||||||
.setOkClose(false)
|
.setOkClose(false)
|
||||||
.setNoClose(false)
|
.setNoClose(false)
|
||||||
.build(this);
|
.build(this);
|
||||||
|
|
||||||
//Add the button to start the sever
|
|
||||||
addChild(serverButton).addClickCommands(s -> ifTopDialog(this::startServerInThread));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -159,15 +156,15 @@ class NetworkDialog extends SimpleDialog {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the server in a separate thread.
|
* Starts the server of the client in a new thread and catches connectivity issues.
|
||||||
*/
|
*/
|
||||||
private void startServerInThread() {
|
private void startClientServer() {
|
||||||
serverButton.setEnabled(false);
|
HOST.setEnabled(false);
|
||||||
Thread serverThread = new Thread(() -> {
|
Thread serverThread = new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
BattleshipServer.main(null);
|
BattleshipSelfhostServer.main(null);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
serverButton.setEnabled(true);
|
HOST.setEnabled(true);
|
||||||
LOGGER.log(Level.ERROR, "Server could not be started", e);
|
LOGGER.log(Level.ERROR, "Server could not be started", e);
|
||||||
network.getApp().errorDialog("Could not start server: " + e.getMessage());
|
network.getApp().errorDialog("Could not start server: " + e.getMessage());
|
||||||
}
|
}
|
||||||
@@ -175,3 +172,4 @@ class NetworkDialog extends SimpleDialog {
|
|||||||
serverThread.start();
|
serverThread.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,7 +15,6 @@ 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 com.jme3.scene.shape.Sphere;
|
||||||
|
|
||||||
import pp.battleship.model.Battleship;
|
import pp.battleship.model.Battleship;
|
||||||
import pp.battleship.model.Shell;
|
import pp.battleship.model.Shell;
|
||||||
import pp.battleship.model.Shot;
|
import pp.battleship.model.Shot;
|
||||||
@@ -23,7 +22,6 @@ import pp.util.Position;
|
|||||||
|
|
||||||
import static com.jme3.material.Materials.UNSHADED;
|
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
|
||||||
@@ -151,4 +149,5 @@ class MapViewSynchronizer extends ShipMapSynchronizer {
|
|||||||
ellipse.addControl(new Shell2DControl(view, shell));
|
ellipse.addControl(new Shell2DControl(view, shell));
|
||||||
return ellipse;
|
return ellipse;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,285 +0,0 @@
|
|||||||
package pp.battleship.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.effect.ParticleEmitter;
|
|
||||||
import com.jme3.effect.ParticleMesh.Type;
|
|
||||||
import com.jme3.effect.shapes.EmitterSphereShape;
|
|
||||||
import com.jme3.material.Material;
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.math.FastMath;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import pp.battleship.client.BattleshipApp;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory class responsible for creating particle effects used in the game.
|
|
||||||
* This centralizes the creation of various types of particle emitters.
|
|
||||||
*/
|
|
||||||
public class ParticleEffectFactory {
|
|
||||||
private static final int COUNT_FACTOR = 1;
|
|
||||||
private static final float COUNT_FACTOR_F = 1f;
|
|
||||||
private static final boolean POINT_SPRITE = true;
|
|
||||||
private static final Type EMITTER_TYPE = POINT_SPRITE ? Type.Point : Type.Triangle;
|
|
||||||
|
|
||||||
private final BattleshipApp app;
|
|
||||||
|
|
||||||
ParticleEffectFactory(BattleshipApp app) {
|
|
||||||
this.app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a flame particle emitter.
|
|
||||||
*
|
|
||||||
* @return a configured flame particle emitter
|
|
||||||
*/
|
|
||||||
ParticleEmitter createFlame() {
|
|
||||||
ParticleEmitter flame = new ParticleEmitter("Flame", EMITTER_TYPE, 32 * COUNT_FACTOR);
|
|
||||||
flame.setSelectRandomImage(true);
|
|
||||||
flame.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (1f / COUNT_FACTOR_F)));
|
|
||||||
flame.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
|
|
||||||
flame.setStartSize(0.1f);
|
|
||||||
flame.setEndSize(0.5f);
|
|
||||||
flame.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
|
|
||||||
flame.setParticlesPerSec(0);
|
|
||||||
flame.setGravity(0, -5, 0);
|
|
||||||
flame.setLowLife(.4f);
|
|
||||||
flame.setHighLife(.5f);
|
|
||||||
flame.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 7, 0));
|
|
||||||
flame.getParticleInfluencer().setVelocityVariation(1f);
|
|
||||||
flame.setImagesX(2);
|
|
||||||
flame.setImagesY(2);
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
|
||||||
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flame.png"));
|
|
||||||
mat.setBoolean("PointSprite", POINT_SPRITE);
|
|
||||||
flame.setMaterial(mat);
|
|
||||||
|
|
||||||
return flame;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a flash particle emitter.
|
|
||||||
*
|
|
||||||
* @return a configured flash particle emitter
|
|
||||||
*/
|
|
||||||
ParticleEmitter createFlash() {
|
|
||||||
ParticleEmitter flash = new ParticleEmitter("Flash", EMITTER_TYPE, 24 * COUNT_FACTOR);
|
|
||||||
flash.setSelectRandomImage(true);
|
|
||||||
flash.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1f / COUNT_FACTOR_F));
|
|
||||||
flash.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
|
|
||||||
flash.setStartSize(.1f);
|
|
||||||
flash.setEndSize(0.5f);
|
|
||||||
flash.setShape(new EmitterSphereShape(Vector3f.ZERO, .05f));
|
|
||||||
flash.setParticlesPerSec(0);
|
|
||||||
flash.setGravity(0, 0, 0);
|
|
||||||
flash.setLowLife(.2f);
|
|
||||||
flash.setHighLife(.2f);
|
|
||||||
flash.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 5f, 0));
|
|
||||||
flash.getParticleInfluencer().setVelocityVariation(1);
|
|
||||||
flash.setImagesX(2);
|
|
||||||
flash.setImagesY(2);
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
|
||||||
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flash.png"));
|
|
||||||
mat.setBoolean("PointSprite", POINT_SPRITE);
|
|
||||||
flash.setMaterial(mat);
|
|
||||||
|
|
||||||
return flash;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a round spark particle emitter.
|
|
||||||
*
|
|
||||||
* @return a configured round spark particle emitter
|
|
||||||
*/
|
|
||||||
ParticleEmitter createRoundSpark() {
|
|
||||||
ParticleEmitter roundSpark = new ParticleEmitter("RoundSpark", EMITTER_TYPE, 20 * COUNT_FACTOR);
|
|
||||||
roundSpark.setStartColor(new ColorRGBA(1f, 0.29f, 0.34f, (float) (1.0 / COUNT_FACTOR_F)));
|
|
||||||
roundSpark.setEndColor(new ColorRGBA(0, 0, 0, 0.5f / COUNT_FACTOR_F));
|
|
||||||
roundSpark.setStartSize(0.2f);
|
|
||||||
roundSpark.setEndSize(0.8f);
|
|
||||||
roundSpark.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
|
|
||||||
roundSpark.setParticlesPerSec(0);
|
|
||||||
roundSpark.setGravity(0, -.5f, 0);
|
|
||||||
roundSpark.setLowLife(1.8f);
|
|
||||||
roundSpark.setHighLife(2f);
|
|
||||||
roundSpark.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 3, 0));
|
|
||||||
roundSpark.getParticleInfluencer().setVelocityVariation(.5f);
|
|
||||||
roundSpark.setImagesX(1);
|
|
||||||
roundSpark.setImagesY(1);
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
|
||||||
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/roundspark.png"));
|
|
||||||
mat.setBoolean("PointSprite", POINT_SPRITE);
|
|
||||||
roundSpark.setMaterial(mat);
|
|
||||||
|
|
||||||
return roundSpark;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a spark particle emitter.
|
|
||||||
*
|
|
||||||
* @return a configured spark particle emitter
|
|
||||||
*/
|
|
||||||
ParticleEmitter createSpark() {
|
|
||||||
ParticleEmitter spark = new ParticleEmitter("Spark", Type.Triangle, 30 * COUNT_FACTOR);
|
|
||||||
spark.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F));
|
|
||||||
spark.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
|
|
||||||
spark.setStartSize(.5f);
|
|
||||||
spark.setEndSize(.5f);
|
|
||||||
spark.setFacingVelocity(true);
|
|
||||||
spark.setParticlesPerSec(0);
|
|
||||||
spark.setGravity(0, 5, 0);
|
|
||||||
spark.setLowLife(1.1f);
|
|
||||||
spark.setHighLife(1.5f);
|
|
||||||
spark.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 20, 0));
|
|
||||||
spark.getParticleInfluencer().setVelocityVariation(1);
|
|
||||||
spark.setImagesX(1);
|
|
||||||
spark.setImagesY(1);
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
|
||||||
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/spark.png"));
|
|
||||||
spark.setMaterial(mat);
|
|
||||||
|
|
||||||
return spark;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a smoke trail particle emitter.
|
|
||||||
*
|
|
||||||
* @return a configured smoke trail particle emitter
|
|
||||||
*/
|
|
||||||
ParticleEmitter createSmokeTrail() {
|
|
||||||
ParticleEmitter smokeTrail = new ParticleEmitter("SmokeTrail", Type.Triangle, 22 * COUNT_FACTOR);
|
|
||||||
smokeTrail.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F));
|
|
||||||
smokeTrail.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
|
|
||||||
smokeTrail.setStartSize(.2f);
|
|
||||||
smokeTrail.setEndSize(1f);
|
|
||||||
smokeTrail.setFacingVelocity(true);
|
|
||||||
smokeTrail.setParticlesPerSec(0);
|
|
||||||
smokeTrail.setGravity(0, 1, 0);
|
|
||||||
smokeTrail.setLowLife(.4f);
|
|
||||||
smokeTrail.setHighLife(.5f);
|
|
||||||
smokeTrail.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 12, 0));
|
|
||||||
smokeTrail.getParticleInfluencer().setVelocityVariation(1);
|
|
||||||
smokeTrail.setImagesX(1);
|
|
||||||
smokeTrail.setImagesY(3);
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
|
||||||
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/smoketrail.png"));
|
|
||||||
smokeTrail.setMaterial(mat);
|
|
||||||
|
|
||||||
return smokeTrail;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a debris particle emitter.
|
|
||||||
*
|
|
||||||
* @return a configured debris particle emitter
|
|
||||||
*/
|
|
||||||
ParticleEmitter createDebris() {
|
|
||||||
ParticleEmitter debris = new ParticleEmitter("Debris", Type.Triangle, 15 * COUNT_FACTOR);
|
|
||||||
debris.setSelectRandomImage(true);
|
|
||||||
debris.setRandomAngle(true);
|
|
||||||
debris.setRotateSpeed(FastMath.TWO_PI * 4);
|
|
||||||
debris.setStartColor(new ColorRGBA(1f, 0.59f, 0.28f, 1.0f / COUNT_FACTOR_F));
|
|
||||||
debris.setEndColor(new ColorRGBA(.5f, 0.5f, 0.5f, 0f));
|
|
||||||
debris.setStartSize(.10f);
|
|
||||||
debris.setEndSize(.15f);
|
|
||||||
debris.setParticlesPerSec(0);
|
|
||||||
debris.setGravity(0, 12f, 0);
|
|
||||||
debris.setLowLife(1.4f);
|
|
||||||
debris.setHighLife(1.5f);
|
|
||||||
debris.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 15, 0));
|
|
||||||
debris.getParticleInfluencer().setVelocityVariation(.60f);
|
|
||||||
debris.setImagesX(3);
|
|
||||||
debris.setImagesY(3);
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
|
||||||
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/Debris.png"));
|
|
||||||
debris.setMaterial(mat);
|
|
||||||
|
|
||||||
return debris;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a shockwave particle emitter.
|
|
||||||
*
|
|
||||||
* @return a configured shockwave particle emitter
|
|
||||||
*/
|
|
||||||
ParticleEmitter createShockwave() {
|
|
||||||
ParticleEmitter shockwave = new ParticleEmitter("Shockwave", Type.Triangle, 1 * COUNT_FACTOR);
|
|
||||||
shockwave.setFaceNormal(Vector3f.UNIT_Y);
|
|
||||||
shockwave.setStartColor(new ColorRGBA(.48f, 0.17f, 0.01f, .8f / COUNT_FACTOR_F));
|
|
||||||
shockwave.setEndColor(new ColorRGBA(.48f, 0.17f, 0.01f, 0f));
|
|
||||||
shockwave.setStartSize(0f);
|
|
||||||
shockwave.setEndSize(3f);
|
|
||||||
shockwave.setParticlesPerSec(0);
|
|
||||||
shockwave.setGravity(0, 0, 0);
|
|
||||||
shockwave.setLowLife(0.5f);
|
|
||||||
shockwave.setHighLife(0.5f);
|
|
||||||
shockwave.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0));
|
|
||||||
shockwave.getParticleInfluencer().setVelocityVariation(0f);
|
|
||||||
shockwave.setImagesX(1);
|
|
||||||
shockwave.setImagesY(1);
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
|
||||||
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/shockwave.png"));
|
|
||||||
shockwave.setMaterial(mat);
|
|
||||||
|
|
||||||
return shockwave;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a moving smoke emitter.
|
|
||||||
*
|
|
||||||
* @return a configured smoke emitter
|
|
||||||
*/
|
|
||||||
ParticleEmitter createMovingSmokeEmitter() {
|
|
||||||
ParticleEmitter smokeEmitter = new ParticleEmitter("SmokeEmitter", Type.Triangle, 300);
|
|
||||||
smokeEmitter.setGravity(0, 0, 0);
|
|
||||||
smokeEmitter.getParticleInfluencer().setVelocityVariation(1);
|
|
||||||
smokeEmitter.setLowLife(1);
|
|
||||||
smokeEmitter.setHighLife(1);
|
|
||||||
smokeEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, .5f, 0));
|
|
||||||
smokeEmitter.setImagesX(15); // Assuming the smoke texture is a sprite sheet with 15 frames
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
|
||||||
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Smoke/Smoke.png"));
|
|
||||||
smokeEmitter.setMaterial(mat);
|
|
||||||
|
|
||||||
return smokeEmitter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a one-time water splash particle emitter.
|
|
||||||
*
|
|
||||||
* @return a configured one-time water splash particle emitter
|
|
||||||
*/
|
|
||||||
public ParticleEmitter createWaterSplash() {
|
|
||||||
// Create a new particle emitter for the splash effect
|
|
||||||
ParticleEmitter waterSplash = new ParticleEmitter("WaterSplash", Type.Triangle, 30);
|
|
||||||
|
|
||||||
// Set the shape of the emitter, making particles emit from a point or small area
|
|
||||||
waterSplash.setShape(new EmitterSphereShape(Vector3f.ZERO, 0.2f));
|
|
||||||
|
|
||||||
// Start and end colors for water (blue, fading out)
|
|
||||||
waterSplash.setStartColor(new ColorRGBA(0.4f, 0.4f, 1f, 1f)); // Light blue at start
|
|
||||||
waterSplash.setEndColor(new ColorRGBA(0.4f, 0.4f, 1f, 0f)); // Transparent at the end
|
|
||||||
|
|
||||||
// Particle size: small at start, larger before fading out
|
|
||||||
waterSplash.setStartSize(0.1f);
|
|
||||||
waterSplash.setEndSize(0.3f);
|
|
||||||
|
|
||||||
// Particle lifespan (how long particles live)
|
|
||||||
waterSplash.setLowLife(0.5f);
|
|
||||||
waterSplash.setHighLife(1f);
|
|
||||||
|
|
||||||
// Gravity: Pull the water particles downwards
|
|
||||||
waterSplash.setGravity(0, -9.81f, 0); // Earth's gravity simulation
|
|
||||||
|
|
||||||
// Velocity: Give particles an initial burst upward (simulates splash)
|
|
||||||
waterSplash.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 3, 0));
|
|
||||||
waterSplash.getParticleInfluencer().setVelocityVariation(0.6f); // Add randomness to splash
|
|
||||||
|
|
||||||
// Set how many particles are emitted per second (0 to emit all particles at once)
|
|
||||||
waterSplash.setParticlesPerSec(0);
|
|
||||||
|
|
||||||
// Load a texture for the water splash (assuming a texture exists at this path)
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
|
||||||
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Splash/splash.png"));
|
|
||||||
waterSplash.setMaterial(mat);
|
|
||||||
|
|
||||||
return waterSplash;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -8,9 +8,13 @@
|
|||||||
package pp.battleship.client.gui;
|
package pp.battleship.client.gui;
|
||||||
|
|
||||||
import com.jme3.effect.ParticleEmitter;
|
import com.jme3.effect.ParticleEmitter;
|
||||||
|
import com.jme3.effect.ParticleMesh.Type;
|
||||||
|
import com.jme3.effect.shapes.EmitterSphereShape;
|
||||||
import com.jme3.material.Material;
|
import com.jme3.material.Material;
|
||||||
import com.jme3.material.RenderState.BlendMode;
|
import com.jme3.material.RenderState.BlendMode;
|
||||||
import com.jme3.math.ColorRGBA;
|
import com.jme3.math.ColorRGBA;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
||||||
import com.jme3.scene.Geometry;
|
import com.jme3.scene.Geometry;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
@@ -30,16 +34,16 @@ import static pp.util.FloatMath.PI;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@code SeaSynchronizer} class is responsible for synchronizing the graphical
|
* The {@code SeaSynchronizer} class is responsible for synchronizing the graphical
|
||||||
* representation of the ships and shots on the sea map with the underlying data model.
|
* representation of the ships and shots on the sea map with the underlying model.
|
||||||
* 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 BOAT_SMALL_MODEL = "Models/BoatSmall/12219_boat_v2_L2.j3o"; //NON-NLS
|
private static final String SMALL_BOAT_MODEL = "Models/BoatSmall/12219_boat_v2_L2.j3o";
|
||||||
private static final String CV_MODEL = "Models/CV/CV.j3o"; //NON-NLS
|
|
||||||
private static final String BATTLE_MODEL = "Models/Battle/Battle.j3o"; //NON-NLS
|
private static final String BATTLE_MODEL = "Models/Battle/Battle.j3o"; //NON-NLS
|
||||||
|
private static final String CV_MODEL = "Models/CV/CV.j3o"; //NON-NLS
|
||||||
private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md";
|
private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md";
|
||||||
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
|
||||||
@@ -50,7 +54,273 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
|
|
||||||
private final ShipMap map;
|
private final ShipMap map;
|
||||||
private final BattleshipApp app;
|
private final BattleshipApp app;
|
||||||
private final ParticleEffectFactory particleFactory;
|
private Shell shell;
|
||||||
|
private Spatial shellModel;
|
||||||
|
|
||||||
|
private ParticleEmitter flame, flash, spark, roundspark, smoketrail, debris,
|
||||||
|
shockwave;
|
||||||
|
|
||||||
|
|
||||||
|
private static final int COUNT_FACTOR = 1;
|
||||||
|
private static final float COUNT_FACTOR_F = 1f;
|
||||||
|
|
||||||
|
private static final boolean POINT_SPRITE = true;
|
||||||
|
private static final Type EMITTER_TYPE = POINT_SPRITE ? Type.Point : Type.Triangle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Flame texture with indefinite Lifetime
|
||||||
|
* @return flame texture
|
||||||
|
*/
|
||||||
|
private ParticleEmitter createFlame(){
|
||||||
|
flame = new ParticleEmitter("Flame", EMITTER_TYPE, 32 * COUNT_FACTOR);
|
||||||
|
flame.setSelectRandomImage(true);
|
||||||
|
flame.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (1f / COUNT_FACTOR_F)));
|
||||||
|
flame.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
|
||||||
|
flame.setStartSize(0.1f);
|
||||||
|
flame.setEndSize(0.5f);
|
||||||
|
flame.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
|
||||||
|
flame.setParticlesPerSec(0);
|
||||||
|
flame.setGravity(0, -5, 0);
|
||||||
|
flame.setLowLife(.4f);
|
||||||
|
flame.setHighLife(.5f);
|
||||||
|
flame.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 7, 0));
|
||||||
|
flame.getParticleInfluencer().setVelocityVariation(1f);
|
||||||
|
flame.setImagesX(2);
|
||||||
|
flame.setImagesY(2);
|
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flame.png"));
|
||||||
|
mat.setBoolean("PointSprite", POINT_SPRITE);
|
||||||
|
flame.setMaterial(mat);
|
||||||
|
|
||||||
|
return flame;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Flash texture
|
||||||
|
* @return flash texture
|
||||||
|
*/
|
||||||
|
private ParticleEmitter createFlash(){
|
||||||
|
flash = new ParticleEmitter("Flash", EMITTER_TYPE, 24 * COUNT_FACTOR);
|
||||||
|
flash.setSelectRandomImage(true);
|
||||||
|
flash.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1f / COUNT_FACTOR_F));
|
||||||
|
flash.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
|
||||||
|
flash.setStartSize(.1f);
|
||||||
|
flash.setEndSize(0.5f);
|
||||||
|
flash.setShape(new EmitterSphereShape(Vector3f.ZERO, .05f));
|
||||||
|
flash.setParticlesPerSec(0);
|
||||||
|
flash.setGravity(0, 0, 0);
|
||||||
|
flash.setLowLife(.2f);
|
||||||
|
flash.setHighLife(.2f);
|
||||||
|
flash.getParticleInfluencer()
|
||||||
|
.setInitialVelocity(new Vector3f(0, 5f, 0));
|
||||||
|
flash.getParticleInfluencer().setVelocityVariation(1);
|
||||||
|
flash.setImagesX(2);
|
||||||
|
flash.setImagesY(2);
|
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flash.png"));
|
||||||
|
mat.setBoolean("PointSprite", POINT_SPRITE);
|
||||||
|
flash.setMaterial(mat);
|
||||||
|
|
||||||
|
return flash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates small spark particles, that dissipate into the air
|
||||||
|
* @return spark texture
|
||||||
|
*/
|
||||||
|
private ParticleEmitter createRoundSpark(){
|
||||||
|
roundspark = new ParticleEmitter("RoundSpark", EMITTER_TYPE, 20 * COUNT_FACTOR);
|
||||||
|
roundspark.setStartColor(new ColorRGBA(1f, 0.29f, 0.34f, (float) (1.0 / COUNT_FACTOR_F)));
|
||||||
|
roundspark.setEndColor(new ColorRGBA(0, 0, 0, 0.5f / COUNT_FACTOR_F));
|
||||||
|
roundspark.setStartSize(0.2f);
|
||||||
|
roundspark.setEndSize(0.8f);
|
||||||
|
roundspark.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
|
||||||
|
roundspark.setParticlesPerSec(0);
|
||||||
|
roundspark.setGravity(0, -.5f, 0);
|
||||||
|
roundspark.setLowLife(1.8f);
|
||||||
|
roundspark.setHighLife(2f);
|
||||||
|
roundspark.getParticleInfluencer()
|
||||||
|
.setInitialVelocity(new Vector3f(0, 3, 0));
|
||||||
|
roundspark.getParticleInfluencer().setVelocityVariation(.5f);
|
||||||
|
roundspark.setImagesX(1);
|
||||||
|
roundspark.setImagesY(1);
|
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/roundspark.png"));
|
||||||
|
mat.setBoolean("PointSprite", POINT_SPRITE);
|
||||||
|
roundspark.setMaterial(mat);
|
||||||
|
|
||||||
|
return roundspark;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates small, thin smoke trails that enhance the flying debris
|
||||||
|
* @return crates a thin smoke trail texture
|
||||||
|
*/
|
||||||
|
private ParticleEmitter createSpark(){
|
||||||
|
spark = new ParticleEmitter("Spark", Type.Triangle, 30 * COUNT_FACTOR);
|
||||||
|
spark.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F));
|
||||||
|
spark.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
|
||||||
|
spark.setStartSize(.5f);
|
||||||
|
spark.setEndSize(.5f);
|
||||||
|
spark.setFacingVelocity(true);
|
||||||
|
spark.setParticlesPerSec(0);
|
||||||
|
spark.setGravity(0, 5, 0);
|
||||||
|
spark.setLowLife(1.1f);
|
||||||
|
spark.setHighLife(1.5f);
|
||||||
|
spark.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 20, 0));
|
||||||
|
spark.getParticleInfluencer().setVelocityVariation(1);
|
||||||
|
spark.setImagesX(1);
|
||||||
|
spark.setImagesY(1);
|
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/spark.png"));
|
||||||
|
spark.setMaterial(mat);
|
||||||
|
|
||||||
|
return spark;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a dynamic smoke trail
|
||||||
|
* @return smoke texture
|
||||||
|
*/
|
||||||
|
private ParticleEmitter createSmokeTrail(){
|
||||||
|
smoketrail = new ParticleEmitter("SmokeTrail", Type.Triangle, 22 * COUNT_FACTOR);
|
||||||
|
smoketrail.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F));
|
||||||
|
smoketrail.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
|
||||||
|
smoketrail.setStartSize(.2f);
|
||||||
|
smoketrail.setEndSize(1f);
|
||||||
|
|
||||||
|
// smoketrail.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
|
||||||
|
smoketrail.setFacingVelocity(true);
|
||||||
|
smoketrail.setParticlesPerSec(0);
|
||||||
|
smoketrail.setGravity(0, 1, 0);
|
||||||
|
smoketrail.setLowLife(.4f);
|
||||||
|
smoketrail.setHighLife(.5f);
|
||||||
|
smoketrail.getParticleInfluencer()
|
||||||
|
.setInitialVelocity(new Vector3f(0, 12, 0));
|
||||||
|
smoketrail.getParticleInfluencer().setVelocityVariation(1);
|
||||||
|
smoketrail.setImagesX(1);
|
||||||
|
smoketrail.setImagesY(3);
|
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/smoketrail.png"));
|
||||||
|
smoketrail.setMaterial(mat);
|
||||||
|
|
||||||
|
return smoketrail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates flying debris particles
|
||||||
|
* @return debris texture
|
||||||
|
*/
|
||||||
|
private ParticleEmitter createDebris(){
|
||||||
|
debris = new ParticleEmitter("Debris", Type.Triangle, 15 * COUNT_FACTOR);
|
||||||
|
debris.setSelectRandomImage(true);
|
||||||
|
debris.setRandomAngle(true);
|
||||||
|
debris.setRotateSpeed(FastMath.TWO_PI * 4);
|
||||||
|
debris.setStartColor(new ColorRGBA(1f, 0.59f, 0.28f, 1.0f / COUNT_FACTOR_F));
|
||||||
|
debris.setEndColor(new ColorRGBA(.5f, 0.5f, 0.5f, 0f));
|
||||||
|
debris.setStartSize(.10f);
|
||||||
|
debris.setEndSize(.15f);
|
||||||
|
|
||||||
|
// debris.setShape(new EmitterSphereShape(Vector3f.ZERO, .05f));
|
||||||
|
debris.setParticlesPerSec(0);
|
||||||
|
debris.setGravity(0, 12f, 0);
|
||||||
|
debris.setLowLife(1.4f);
|
||||||
|
debris.setHighLife(1.5f);
|
||||||
|
debris.getParticleInfluencer()
|
||||||
|
.setInitialVelocity(new Vector3f(0, 15, 0));
|
||||||
|
debris.getParticleInfluencer().setVelocityVariation(.60f);
|
||||||
|
debris.setImagesX(3);
|
||||||
|
debris.setImagesY(3);
|
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/Debris.png"));
|
||||||
|
debris.setMaterial(mat);
|
||||||
|
|
||||||
|
return debris;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an expanding circular shockwave
|
||||||
|
* @return shockwave texture
|
||||||
|
*/
|
||||||
|
private ParticleEmitter createShockwave(){
|
||||||
|
shockwave = new ParticleEmitter("Shockwave", Type.Triangle, 1 * COUNT_FACTOR);
|
||||||
|
// shockwave.setRandomAngle(true);
|
||||||
|
shockwave.setFaceNormal(Vector3f.UNIT_Y);
|
||||||
|
shockwave.setStartColor(new ColorRGBA(.48f, 0.17f, 0.01f, .8f / COUNT_FACTOR_F));
|
||||||
|
shockwave.setEndColor(new ColorRGBA(.48f, 0.17f, 0.01f, 0f));
|
||||||
|
|
||||||
|
shockwave.setStartSize(0f);
|
||||||
|
shockwave.setEndSize(3f);
|
||||||
|
|
||||||
|
shockwave.setParticlesPerSec(0);
|
||||||
|
shockwave.setGravity(0, 0, 0);
|
||||||
|
shockwave.setLowLife(0.5f);
|
||||||
|
shockwave.setHighLife(0.5f);
|
||||||
|
shockwave.getParticleInfluencer()
|
||||||
|
.setInitialVelocity(new Vector3f(0, 0, 0));
|
||||||
|
shockwave.getParticleInfluencer().setVelocityVariation(0f);
|
||||||
|
shockwave.setImagesX(1);
|
||||||
|
shockwave.setImagesY(1);
|
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/shockwave.png"));
|
||||||
|
shockwave.setMaterial(mat);
|
||||||
|
|
||||||
|
return shockwave;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an animated smoke column.
|
||||||
|
* @return moving smoke texture
|
||||||
|
*/
|
||||||
|
private ParticleEmitter createMovingSmokeEmitter() {
|
||||||
|
ParticleEmitter smokeEmitter = new ParticleEmitter("SmokeEmitter", Type.Triangle, 300);
|
||||||
|
smokeEmitter.setGravity(0, 0, 0);
|
||||||
|
smokeEmitter.getParticleInfluencer().setVelocityVariation(1);
|
||||||
|
smokeEmitter.setLowLife(1);
|
||||||
|
smokeEmitter.setHighLife(1);
|
||||||
|
smokeEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, .5f, 0));
|
||||||
|
smokeEmitter.setImagesX(15); // Assuming the smoke texture is a sprite sheet with 15 frames
|
||||||
|
|
||||||
|
// Set the material for the emitter
|
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Smoke/Smoke.png"));
|
||||||
|
smokeEmitter.setMaterial(mat);
|
||||||
|
|
||||||
|
return smokeEmitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a small burst of bubbles.
|
||||||
|
*
|
||||||
|
* @return bubbling water texture.
|
||||||
|
*/
|
||||||
|
public ParticleEmitter createWaterSplash() {
|
||||||
|
ParticleEmitter waterSplash = new ParticleEmitter("WaterSplash", Type.Triangle, 30);
|
||||||
|
|
||||||
|
waterSplash.setShape(new EmitterSphereShape(Vector3f.ZERO, 0.2f));
|
||||||
|
|
||||||
|
waterSplash.setStartColor(new ColorRGBA(0.4f, 0.4f, 1f, 1f)); // Light blue at start
|
||||||
|
waterSplash.setEndColor(new ColorRGBA(0.4f, 0.4f, 1f, 0f)); // Transparent at the end
|
||||||
|
|
||||||
|
waterSplash.setStartSize(0.05f);
|
||||||
|
waterSplash.setEndSize(0.1f);
|
||||||
|
|
||||||
|
waterSplash.setLowLife(0.5f);
|
||||||
|
waterSplash.setHighLife(1f);
|
||||||
|
|
||||||
|
|
||||||
|
waterSplash.setGravity(0, 0, 0); // No gravity to simulate a horizontal water splash
|
||||||
|
|
||||||
|
waterSplash.getParticleInfluencer().setInitialVelocity(new Vector3f(1f, 0f, 1f)); // Horizontal spread
|
||||||
|
waterSplash.getParticleInfluencer().setVelocityVariation(1f); // Add randomness to splash
|
||||||
|
waterSplash.setParticlesPerSec(0);
|
||||||
|
|
||||||
|
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
|
||||||
|
mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Splash/splash.png"));
|
||||||
|
waterSplash.setMaterial(mat);
|
||||||
|
|
||||||
|
return waterSplash;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a {@code SeaSynchronizer} object with the specified application, root node, and ship map.
|
* Constructs a {@code SeaSynchronizer} object with the specified application, root node, and ship map.
|
||||||
@@ -63,7 +333,6 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
super(app.getGameLogic().getOwnMap(), root);
|
super(app.getGameLogic().getOwnMap(), root);
|
||||||
this.app = app;
|
this.app = app;
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.particleFactory = new ParticleEffectFactory(app);
|
|
||||||
addExisting();
|
addExisting();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,49 +349,13 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
return shot.isHit() ? handleHit(shot) : handleMiss(shot);
|
return shot.isHit() ? handleHit(shot) : handleMiss(shot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a miss by representing it with a blue cylinder
|
|
||||||
* and attaching a water splash effect to it.
|
|
||||||
* @param shot the shot to be processed
|
|
||||||
* @return a Spatial simulating a miss with water splash effect
|
|
||||||
*/
|
|
||||||
private Spatial handleMiss(Shot shot) {
|
|
||||||
Node shotNode = new Node("ShotNode");
|
|
||||||
Geometry shotCylinder = createCylinder(shot);
|
|
||||||
shotNode.attachChild(shotCylinder);
|
|
||||||
ParticleEmitter waterSplash = particleFactory.createWaterSplash();
|
|
||||||
waterSplash.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f);
|
|
||||||
shotNode.attachChild(waterSplash);
|
|
||||||
waterSplash.emitAllParticles();
|
|
||||||
|
|
||||||
return shotNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the sinking animation and removal of ship if destroyed
|
|
||||||
* @param ship the ship to be sunk
|
|
||||||
*/
|
|
||||||
private void sinkAndRemoveShip(Battleship ship) {
|
|
||||||
Battleship wilkeningklaunichtmeinencode = ship;
|
|
||||||
final Node shipNode = (Node) getSpatial(wilkeningklaunichtmeinencode);
|
|
||||||
if (shipNode == null) return;
|
|
||||||
|
|
||||||
// Add sinking control to animate the sinking
|
|
||||||
shipNode.addControl(new SinkingControl(shipNode));
|
|
||||||
|
|
||||||
// Add particle effects
|
|
||||||
ParticleEmitter bubbles = particleFactory.createWaterSplash();
|
|
||||||
bubbles.setLocalTranslation(shipNode.getLocalTranslation());
|
|
||||||
shipNode.attachChild(bubbles);
|
|
||||||
bubbles.emitAllParticles();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles a hit by attaching its representation to the node that
|
* Handles a hit by attaching its representation to the node that
|
||||||
* contains the ship model as a child so that it moves with the ship.
|
* contains the ship model as a child so that it moves with the ship.
|
||||||
*
|
*
|
||||||
* @param shot a hit
|
* @param shot a hit
|
||||||
* @return always null to prevent the representation from being attached to the items node as well
|
* @return always null to prevent the representation from being attached
|
||||||
|
* to the items node as well
|
||||||
*/
|
*/
|
||||||
private Spatial handleHit(Shot shot) {
|
private Spatial handleHit(Shot shot) {
|
||||||
final Battleship ship = requireNonNull(map.findShipAt(shot), "Missing ship");
|
final Battleship ship = requireNonNull(map.findShipAt(shot), "Missing ship");
|
||||||
@@ -134,14 +367,14 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
Node hitEffectNode = new Node("HitEffectNode");
|
Node hitEffectNode = new Node("HitEffectNode");
|
||||||
|
|
||||||
// Create particle effects
|
// Create particle effects
|
||||||
ParticleEmitter flame = particleFactory.createFlame();
|
ParticleEmitter flame = createFlame();
|
||||||
ParticleEmitter flash = particleFactory.createFlash();
|
ParticleEmitter flash = createFlash();
|
||||||
ParticleEmitter spark = particleFactory.createSpark();
|
ParticleEmitter spark = createSpark();
|
||||||
ParticleEmitter roundSpark = particleFactory.createRoundSpark();
|
ParticleEmitter roundSpark = createRoundSpark();
|
||||||
ParticleEmitter smokeTrail = particleFactory.createSmokeTrail();
|
ParticleEmitter smokeTrail = createSmokeTrail();
|
||||||
ParticleEmitter debris = particleFactory.createDebris();
|
ParticleEmitter debris = createDebris();
|
||||||
ParticleEmitter shockwave = particleFactory.createShockwave();
|
ParticleEmitter shockwave = createShockwave();
|
||||||
ParticleEmitter movingSmoke = particleFactory.createMovingSmokeEmitter();
|
ParticleEmitter movingSmoke = createMovingSmokeEmitter(); // New moving smoke emitter
|
||||||
|
|
||||||
// Attach all effects to the hitEffectNode
|
// Attach all effects to the hitEffectNode
|
||||||
hitEffectNode.attachChild(flame);
|
hitEffectNode.attachChild(flame);
|
||||||
@@ -170,14 +403,36 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
flame.emitAllParticles();
|
flame.emitAllParticles();
|
||||||
roundSpark.emitAllParticles();
|
roundSpark.emitAllParticles();
|
||||||
|
|
||||||
//Checks if ship is destroyed and triggers animation accordingly
|
|
||||||
if (ship.isDestroyed()) {
|
if (ship.isDestroyed()) {
|
||||||
sinkAndRemoveShip(ship);
|
// Add ShipSinkingControl to the shipNode
|
||||||
|
ShipSinkingControl sinkingControl = new ShipSinkingControl(2f, 5f, -5f, 60f);
|
||||||
|
shipNode.addControl(sinkingControl);
|
||||||
|
sinkingControl.startSinking(); // Start the sinking process
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles a miss by representing it with a blue cylinder
|
||||||
|
* and attaching a water splash effect to it.
|
||||||
|
* @param shot the shot to be processed
|
||||||
|
* @return a Spatial simulating a miss with water splash effect
|
||||||
|
*/
|
||||||
|
private Spatial handleMiss(Shot shot) {
|
||||||
|
Node shotNode = new Node("ShotNode");
|
||||||
|
Geometry shotCylinder = createCylinder(shot);
|
||||||
|
shotNode.attachChild(shotCylinder);
|
||||||
|
ParticleEmitter waterSplash = createWaterSplash();
|
||||||
|
waterSplash.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f);
|
||||||
|
shotNode.attachChild(waterSplash);
|
||||||
|
waterSplash.emitAllParticles();
|
||||||
|
|
||||||
|
return shotNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a cylinder geometry representing the specified shot.
|
* Creates a cylinder geometry representing the specified shot.
|
||||||
* The appearance of the cylinder depends on whether the shot is a hit or a miss.
|
* The appearance of the cylinder depends on whether the shot is a hit or a miss.
|
||||||
@@ -229,11 +484,17 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
*/
|
*/
|
||||||
private Spatial createShip(Battleship ship) {
|
private Spatial createShip(Battleship ship) {
|
||||||
switch (ship.getLength()) {
|
switch (ship.getLength()) {
|
||||||
case 4: return createBattleship(ship);
|
case 4:
|
||||||
case 3: return createCV(ship);
|
return createBattleship(ship);
|
||||||
case 2: return createBattle(ship);
|
case 3:
|
||||||
case 1: return createSmallship(ship);
|
return createCV(ship);
|
||||||
default: return createBox(ship);
|
case 2:
|
||||||
|
return createBattle(ship);
|
||||||
|
case 1:
|
||||||
|
return createSmallShip(ship);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return createBox(ship);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -277,14 +538,13 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
* Creates a detailed 3D model to represent a "King George V" battleship.
|
* Creates a detailed 3D model to represent a "King George V" battleship.
|
||||||
*
|
*
|
||||||
* @param ship the battleship to be represented
|
* @param ship the battleship to be represented
|
||||||
* @return the spatial representing the "King George V" battleship
|
* @return the spatial representing the "King George V" battleship.
|
||||||
*/
|
*/
|
||||||
private Spatial createBattleship(Battleship ship) {
|
private Spatial createBattleship(Battleship ship) {
|
||||||
final Spatial model = app.getAssetManager().loadModel(KING_GEORGE_V_MODEL);
|
final Spatial model = app.getAssetManager().loadModel(KING_GEORGE_V_MODEL);
|
||||||
|
|
||||||
model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
|
model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
|
||||||
model.scale(1.48f);
|
model.scale(1.48f);
|
||||||
// model.scale(0.0007f);
|
|
||||||
model.setShadowMode(ShadowMode.CastAndReceive);
|
model.setShadowMode(ShadowMode.CastAndReceive);
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
@@ -294,10 +554,10 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
* Creates a detailed 3D model to represent a small tugboat.
|
* Creates a detailed 3D model to represent a small tugboat.
|
||||||
*
|
*
|
||||||
* @param ship the battleship to be represented
|
* @param ship the battleship to be represented
|
||||||
* @return the spatial representing a small tug boat
|
* @return the spatial representing the small tugboat.
|
||||||
*/
|
*/
|
||||||
private Spatial createSmallship(Battleship ship) {
|
private Spatial createSmallShip(Battleship ship) {
|
||||||
final Spatial model = app.getAssetManager().loadModel(BOAT_SMALL_MODEL);
|
final Spatial model = app.getAssetManager().loadModel(SMALL_BOAT_MODEL);
|
||||||
|
|
||||||
model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
|
model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
|
||||||
model.scale(0.0005f);
|
model.scale(0.0005f);
|
||||||
@@ -307,10 +567,27 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a detailed 3D model to represent a "German WWII UBoat".
|
* Creates a detailed 3D model to represent a U-Boat .
|
||||||
*
|
*
|
||||||
* @param ship the battleship to be represented
|
* @param ship the battleship to be represented
|
||||||
* @return the spatial representing the "German WWII UBoat"
|
* @return the spatial representing the U-Boat.
|
||||||
|
*/
|
||||||
|
private Spatial createBattle(Battleship ship) {
|
||||||
|
final Spatial model = app.getAssetManager().loadModel(BATTLE_MODEL);
|
||||||
|
|
||||||
|
model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
|
||||||
|
// model.move(0f, -0.05f, 0f);
|
||||||
|
model.scale(0.27f);
|
||||||
|
model.setShadowMode(ShadowMode.CastAndReceive);
|
||||||
|
|
||||||
|
return model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a detailed 3D model to represent an aircraft carrier
|
||||||
|
*
|
||||||
|
* @param ship the battleship to be represented
|
||||||
|
* @return the spatial representing the aircraft carrier.
|
||||||
*/
|
*/
|
||||||
private Spatial createCV(Battleship ship) {
|
private Spatial createCV(Battleship ship) {
|
||||||
final Spatial model = app.getAssetManager().loadModel(CV_MODEL);
|
final Spatial model = app.getAssetManager().loadModel(CV_MODEL);
|
||||||
@@ -323,23 +600,6 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a detailed 3D model to represent a battleship.
|
|
||||||
*
|
|
||||||
* @param ship the battleship to be represented
|
|
||||||
* @return the spatial representing a battleship
|
|
||||||
*/
|
|
||||||
private Spatial createBattle(Battleship ship) {
|
|
||||||
final Spatial model = app.getAssetManager().loadModel(BATTLE_MODEL);
|
|
||||||
|
|
||||||
model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
|
|
||||||
model.move(0f, -0.06f, 0f);
|
|
||||||
model.scale(0.27f);
|
|
||||||
model.setShadowMode(ShadowMode.CastAndReceive);
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the rotation angle for the specified rotation.
|
* Calculates the rotation angle for the specified rotation.
|
||||||
*
|
*
|
||||||
@@ -375,4 +635,5 @@ class SeaSynchronizer extends ShipMapSynchronizer {
|
|||||||
model.addControl(new ShellControl(shell));
|
model.addControl(new ShellControl(shell));
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -42,16 +42,9 @@ public class Shell2DControl extends AbstractControl {
|
|||||||
spatial.setLocalTranslation(viewPos.getX() + MapView.FIELD_SIZE / 2, viewPos.getY() + MapView.FIELD_SIZE / 2, 0);
|
spatial.setLocalTranslation(viewPos.getX() + MapView.FIELD_SIZE / 2, viewPos.getY() + MapView.FIELD_SIZE / 2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called during the rendering phase, but it does not perform any
|
|
||||||
* operations in this implementation as the control only influences the spatial's
|
|
||||||
* transformation, not its rendering process.
|
|
||||||
*
|
|
||||||
* @param rm the RenderManager rendering the controlled Spatial (not null)
|
|
||||||
* @param vp the ViewPort being rendered (not null)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
// No rendering-specific behavior required for this control
|
// No rendering-specific behavior required for this control
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -43,16 +43,9 @@ public class ShellControl extends AbstractControl {
|
|||||||
spatial.rotate(PI/2,0,0);
|
spatial.rotate(PI/2,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called during the rendering phase, but it does not perform any
|
|
||||||
* operations in this implementation as the control only influences the spatial's
|
|
||||||
* transformation, not its rendering process.
|
|
||||||
*
|
|
||||||
* @param rm the RenderManager rendering the controlled Spatial (not null)
|
|
||||||
* @param vp the ViewPort being rendered (not null)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
// No rendering-specific behavior required for this control
|
// No rendering-specific behavior required for this control
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -0,0 +1,116 @@
|
|||||||
|
package pp.battleship.client.gui;
|
||||||
|
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
|
import com.jme3.math.Quaternion;
|
||||||
|
import com.jme3.math.Vector3f;
|
||||||
|
import com.jme3.renderer.RenderManager;
|
||||||
|
import com.jme3.renderer.ViewPort;
|
||||||
|
import com.jme3.scene.Node;
|
||||||
|
import com.jme3.scene.Spatial;
|
||||||
|
import com.jme3.scene.control.AbstractControl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls the burning, tilting, and sinking behavior of a battleship.
|
||||||
|
* The ship will burn and tilt for a specified duration, then sink below the water surface.
|
||||||
|
*/
|
||||||
|
public class ShipSinkingControl extends AbstractControl {
|
||||||
|
|
||||||
|
private float elapsedTime = 0f;
|
||||||
|
private final float burnTiltDuration;
|
||||||
|
private final float sinkDuration;
|
||||||
|
private final float sinkDepth;
|
||||||
|
private final float tiltAngle;
|
||||||
|
private final Vector3f initialPosition;
|
||||||
|
private boolean sinkingStarted = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new ShipSinkingControl instance.
|
||||||
|
*
|
||||||
|
* @param burnTiltDuration Time in seconds for the ship to burn and tilt on the surface
|
||||||
|
* @param sinkDuration Time in seconds for the ship to fully sink
|
||||||
|
* @param sinkDepth Depth below the water to sink the ship
|
||||||
|
* @param tiltAngle Final tilt angle in degrees
|
||||||
|
*/
|
||||||
|
public ShipSinkingControl(float burnTiltDuration, float sinkDuration, float sinkDepth, float tiltAngle) {
|
||||||
|
this.burnTiltDuration = burnTiltDuration;
|
||||||
|
this.sinkDuration = sinkDuration;
|
||||||
|
this.sinkDepth = sinkDepth;
|
||||||
|
this.tiltAngle = tiltAngle;
|
||||||
|
this.initialPosition = new Vector3f(); // Placeholder; will be set in controlUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides controlUpdate in AbstractControl
|
||||||
|
* regulates the burn and tilt timeframe
|
||||||
|
* @param tpf time per frame (in seconds)
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void controlUpdate(float tpf) {
|
||||||
|
if (spatial == null) return;
|
||||||
|
|
||||||
|
elapsedTime += tpf;
|
||||||
|
|
||||||
|
if (elapsedTime < burnTiltDuration) {
|
||||||
|
float progress = elapsedTime / burnTiltDuration;
|
||||||
|
|
||||||
|
float angleInRadians = FastMath.DEG_TO_RAD * FastMath.interpolateLinear(progress, 0f, -tiltAngle);
|
||||||
|
Quaternion tiltRotation = new Quaternion().fromAngles(angleInRadians, 0, 0);
|
||||||
|
spatial.setLocalRotation(tiltRotation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start sinking if it hasn't started yet
|
||||||
|
if (!sinkingStarted) {
|
||||||
|
sinkingStarted = true;
|
||||||
|
// Save the initial position when sinking starts
|
||||||
|
initialPosition.set(spatial.getLocalTranslation());
|
||||||
|
|
||||||
|
// Remove the hitEffectNode
|
||||||
|
Node parentNode = (Node) spatial;
|
||||||
|
Spatial hitEffects = parentNode.getChild("HitEffectNode");
|
||||||
|
if (hitEffects != null) {
|
||||||
|
parentNode.detachChild(hitEffects);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the progress of the sinking (0 to 1)
|
||||||
|
float progress = Math.min((elapsedTime - burnTiltDuration) / sinkDuration, 1f);
|
||||||
|
|
||||||
|
// Apply the tilt angle (remains constant during sinking)
|
||||||
|
Quaternion tiltRotation = new Quaternion().fromAngles(-FastMath.DEG_TO_RAD * tiltAngle, 0, 0);
|
||||||
|
spatial.setLocalRotation(tiltRotation);
|
||||||
|
|
||||||
|
// Sink the ship by interpolating the Y position
|
||||||
|
float currentY = FastMath.interpolateLinear(progress, initialPosition.y, sinkDepth);
|
||||||
|
spatial.setLocalTranslation(initialPosition.x, currentY, initialPosition.z);
|
||||||
|
|
||||||
|
if (currentY <= sinkDepth) {
|
||||||
|
Node parentNode = (Node) spatial.getParent();
|
||||||
|
if (parentNode != null) {
|
||||||
|
parentNode.detachChild(spatial);
|
||||||
|
}
|
||||||
|
spatial.removeControl(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called during the rendering phase, but it does not perform any
|
||||||
|
* operations in this implementation as the control only influences the spatial's
|
||||||
|
* transformation, not its rendering process.
|
||||||
|
*
|
||||||
|
* @param rm the RenderManager rendering the controlled Spatial (not null)
|
||||||
|
* @param vp the ViewPort being rendered (not null)
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void controlRender(RenderManager rm, ViewPort vp) {
|
||||||
|
// No rendering logic is needed for this control
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the sinking process for the ship.
|
||||||
|
*/
|
||||||
|
public void startSinking() {
|
||||||
|
// Nothing to do here as the control update handles the timing and sequence
|
||||||
|
}
|
||||||
|
}
|
@@ -1,61 +0,0 @@
|
|||||||
package pp.battleship.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.scene.control.AbstractControl;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.renderer.RenderManager;
|
|
||||||
import com.jme3.renderer.ViewPort;
|
|
||||||
import com.jme3.scene.Node;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Control that handles the sinking effect for destroyed ships.
|
|
||||||
* It will gradually move the ship downwards and then remove it from the scene.
|
|
||||||
*/
|
|
||||||
class SinkingControl extends AbstractControl {
|
|
||||||
private static final float SINK_DURATION = 5f; // Duration of the sinking animation
|
|
||||||
private static final float SINK_SPEED = 0.1f; // Speed at which the ship sinks
|
|
||||||
private float elapsedTime = 0;
|
|
||||||
|
|
||||||
private final Node shipNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a {@code SinkingControl} object with the shipNode to be to be sunk
|
|
||||||
* @param shipNode the node to handeld
|
|
||||||
*/
|
|
||||||
public SinkingControl(Node shipNode) {
|
|
||||||
this.shipNode = shipNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updated the Map to sink the ship
|
|
||||||
*
|
|
||||||
* @param tpf time per frame
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void controlUpdate(float tpf) {
|
|
||||||
// Update the sinking effect
|
|
||||||
elapsedTime += tpf;
|
|
||||||
|
|
||||||
// Move the ship down over time
|
|
||||||
Vector3f currentPos = shipNode.getLocalTranslation();
|
|
||||||
shipNode.setLocalTranslation(currentPos.x, currentPos.y - SINK_SPEED * tpf, currentPos.z);
|
|
||||||
|
|
||||||
// Check if sinking duration has passed
|
|
||||||
if (elapsedTime >= SINK_DURATION) {
|
|
||||||
// Remove the ship from the scene
|
|
||||||
shipNode.removeFromParent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called during the rendering phase, but it does not perform any
|
|
||||||
* operations in this implementation as the control only influences the spatial's
|
|
||||||
* transformation, not its rendering process.
|
|
||||||
*
|
|
||||||
* @param rm the RenderManager rendering the controlled Spatial (not null)
|
|
||||||
* @param vp the ViewPort being rendered (not null)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
||||||
// No rendering-related code needed
|
|
||||||
}
|
|
||||||
}
|
|
@@ -0,0 +1,34 @@
|
|||||||
|
package pp.battleship.client.gui;
|
||||||
|
|
||||||
|
import com.simsilica.lemur.Slider;
|
||||||
|
import pp.battleship.client.GameMusic;
|
||||||
|
|
||||||
|
|
||||||
|
public class VolumeControl extends Slider {
|
||||||
|
|
||||||
|
private final GameMusic menuMusicModern;
|
||||||
|
|
||||||
|
private double vol;
|
||||||
|
|
||||||
|
public VolumeControl(GameMusic music) {
|
||||||
|
super();
|
||||||
|
this.menuMusicModern = music;
|
||||||
|
vol = GameMusic.volumePreference();
|
||||||
|
getModel().setPercent(vol);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the volume of the music to the appropriate level set by the User through the Volume control Slider
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public void update() {
|
||||||
|
if (vol != getModel().getPercent()) {
|
||||||
|
vol = getModel().getPercent();
|
||||||
|
menuMusicModern.setVolume( (float) vol);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,36 +0,0 @@
|
|||||||
package pp.battleship.client.gui;
|
|
||||||
|
|
||||||
import com.simsilica.lemur.Slider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The VolumeSlider class represents the Volume Slider in the Menu.
|
|
||||||
* It extends the Slider class and provides functionalities for setting the music volume,
|
|
||||||
* with the help of the Slider in the GUI
|
|
||||||
*/
|
|
||||||
public class VolumeSlider extends Slider {
|
|
||||||
|
|
||||||
private final GameMusic music;
|
|
||||||
private double vol;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the Volume Slider for the Menu dialog
|
|
||||||
* @param music the music instance
|
|
||||||
*/
|
|
||||||
public VolumeSlider(GameMusic music) {
|
|
||||||
super();
|
|
||||||
this.music = music;
|
|
||||||
vol = GameMusic.volumeInPreferences();
|
|
||||||
getModel().setPercent(vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* when triggered it updates the volume to the value set with the slider
|
|
||||||
*/
|
|
||||||
public void update() {
|
|
||||||
if (vol != getModel().getPercent()) {
|
|
||||||
vol = getModel().getPercent();
|
|
||||||
music.setVolume( (float) vol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -5,7 +5,7 @@
|
|||||||
// (c) Mark Minas (mark.minas@unibw.de)
|
// (c) Mark Minas (mark.minas@unibw.de)
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
|
||||||
package pp.battleship.server;
|
package pp.battleship.client.server;
|
||||||
|
|
||||||
import com.jme3.network.ConnectionListener;
|
import com.jme3.network.ConnectionListener;
|
||||||
import com.jme3.network.HostedConnection;
|
import com.jme3.network.HostedConnection;
|
||||||
@@ -42,14 +42,14 @@ import java.util.logging.LogManager;
|
|||||||
/**
|
/**
|
||||||
* Server implementing the visitor pattern as MessageReceiver for ClientMessages
|
* Server implementing the visitor pattern as MessageReceiver for ClientMessages
|
||||||
*/
|
*/
|
||||||
public class BattleshipServer implements MessageListener<HostedConnection>, ConnectionListener, ServerSender {
|
public class BattleshipSelfhostServer implements MessageListener<HostedConnection>, ConnectionListener, ServerSender {
|
||||||
private static final Logger LOGGER = System.getLogger(BattleshipServer.class.getName());
|
private static final Logger LOGGER = System.getLogger(BattleshipSelfhostServer.class.getName());
|
||||||
private static final File CONFIG_FILE = new File("server.properties");
|
private static final File CONFIG_FILE = new File("server.properties");
|
||||||
|
|
||||||
private final BattleshipConfig config = new BattleshipConfig();
|
private final BattleshipConfig config = new BattleshipConfig();
|
||||||
private Server myServer;
|
private Server myServer;
|
||||||
private final ServerGameLogic logic;
|
private final ServerGameLogic logic;
|
||||||
private final BlockingQueue<ReceivedMessage> pendingMessages = new LinkedBlockingQueue<>();
|
private final BlockingQueue<ReceivedMessageSelfhost> pendingMessages = new LinkedBlockingQueue<>();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// Configure logging
|
// Configure logging
|
||||||
@@ -64,16 +64,16 @@ public class BattleshipServer implements MessageListener<HostedConnection>, Conn
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Starts the Battleships server.
|
* Starts the server.
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
new BattleshipServer().run();
|
new BattleshipSelfhostServer().run();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the server.
|
* Creates the server.
|
||||||
*/
|
*/
|
||||||
BattleshipServer() {
|
BattleshipSelfhostServer() {
|
||||||
config.readFromIfExists(CONFIG_FILE);
|
config.readFromIfExists(CONFIG_FILE);
|
||||||
LOGGER.log(Level.INFO, "Configuration: {0}", config); //NON-NLS
|
LOGGER.log(Level.INFO, "Configuration: {0}", config); //NON-NLS
|
||||||
logic = new ServerGameLogic(this, config);
|
logic = new ServerGameLogic(this, config);
|
||||||
@@ -133,7 +133,7 @@ public class BattleshipServer implements MessageListener<HostedConnection>, Conn
|
|||||||
public void messageReceived(HostedConnection source, Message message) {
|
public void messageReceived(HostedConnection source, Message message) {
|
||||||
LOGGER.log(Level.INFO, "message received from {0}: {1}", source.getId(), message); //NON-NLS
|
LOGGER.log(Level.INFO, "message received from {0}: {1}", source.getId(), message); //NON-NLS
|
||||||
if (message instanceof ClientMessage clientMessage)
|
if (message instanceof ClientMessage clientMessage)
|
||||||
pendingMessages.add(new ReceivedMessage(clientMessage, source.getId()));
|
pendingMessages.add(new ReceivedMessageSelfhost(clientMessage, source.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
@@ -5,12 +5,12 @@
|
|||||||
// (c) Mark Minas (mark.minas@unibw.de)
|
// (c) Mark Minas (mark.minas@unibw.de)
|
||||||
////////////////////////////////////////
|
////////////////////////////////////////
|
||||||
|
|
||||||
package pp.battleship.server;
|
package pp.battleship.client.server;
|
||||||
|
|
||||||
import pp.battleship.message.client.ClientInterpreter;
|
import pp.battleship.message.client.ClientInterpreter;
|
||||||
import pp.battleship.message.client.ClientMessage;
|
import pp.battleship.message.client.ClientMessage;
|
||||||
|
|
||||||
record ReceivedMessage(ClientMessage message, int from) {
|
record ReceivedMessageSelfhost(ClientMessage message, int from) {
|
||||||
void process(ClientInterpreter interpreter) {
|
void process(ClientInterpreter interpreter) {
|
||||||
message.accept(interpreter, from);
|
message.accept(interpreter, from);
|
||||||
}
|
}
|
@@ -0,0 +1,16 @@
|
|||||||
|
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
|
||||||
|
# File Created: 29.03.2012 14:25:39
|
||||||
|
|
||||||
|
newmtl default
|
||||||
|
Ns 35.0000
|
||||||
|
Ni 1.5000
|
||||||
|
d 1.0000
|
||||||
|
Tr 0.0000
|
||||||
|
Tf 1.0000 1.0000 1.0000
|
||||||
|
illum 2
|
||||||
|
Ka 1.0000 1.0000 1.0000
|
||||||
|
Kd 1.0000 1.0000 1.0000
|
||||||
|
Ks 0.5400 0.5400 0.5400
|
||||||
|
Ke 0.0000 0.0000 0.0000
|
||||||
|
map_Ka 14084_WWII_ship_German_Type_II_U-boat_diff.jpg
|
||||||
|
map_Kd 14084_WWII_ship_German_Type_II_U-boat_diff.jpg
|
Before Width: | Height: | Size: 7.1 MiB After Width: | Height: | Size: 7.2 MiB |
@@ -0,0 +1,104 @@
|
|||||||
|
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
|
||||||
|
# File Created: 16.12.2011 14:18:52
|
||||||
|
|
||||||
|
newmtl white
|
||||||
|
Ns 53.0000
|
||||||
|
Ni 1.5000
|
||||||
|
d 1.0000
|
||||||
|
Tr 0.0000
|
||||||
|
Tf 1.0000 1.0000 1.0000
|
||||||
|
illum 2
|
||||||
|
Ka 0.6667 0.6667 0.6667
|
||||||
|
Kd 0.6667 0.6667 0.6667
|
||||||
|
Ks 0.1800 0.1800 0.1800
|
||||||
|
Ke 0.0000 0.0000 0.0000
|
||||||
|
|
||||||
|
newmtl boat_elements_black
|
||||||
|
Ns 55.0000
|
||||||
|
Ni 1.5000
|
||||||
|
d 1.0000
|
||||||
|
Tr 0.0000
|
||||||
|
Tf 1.0000 1.0000 1.0000
|
||||||
|
illum 2
|
||||||
|
Ka 0.0000 0.0000 0.0000
|
||||||
|
Kd 0.0000 0.0000 0.0000
|
||||||
|
Ks 0.3600 0.3600 0.3600
|
||||||
|
Ke 0.0000 0.0000 0.0000
|
||||||
|
|
||||||
|
newmtl boat_glass
|
||||||
|
Ns 60.0000
|
||||||
|
Ni 7.0000
|
||||||
|
d 0.4000
|
||||||
|
Tr 0.6000
|
||||||
|
Tf 0.4000 0.4000 0.4000
|
||||||
|
illum 2
|
||||||
|
Ka 0.1059 0.1569 0.1451
|
||||||
|
Kd 0.1059 0.1569 0.1451
|
||||||
|
Ks 0.6750 0.6750 0.6750
|
||||||
|
Ke 0.0000 0.0000 0.0000
|
||||||
|
|
||||||
|
newmtl boat_screw_hooks_bronze
|
||||||
|
Ns 80.0000
|
||||||
|
Ni 1.5000
|
||||||
|
d 1.0000
|
||||||
|
Tr 0.0000
|
||||||
|
Tf 1.0000 1.0000 1.0000
|
||||||
|
illum 2
|
||||||
|
Ka 0.2941 0.2157 0.0510
|
||||||
|
Kd 0.2941 0.2157 0.0510
|
||||||
|
Ks 0.7200 0.7200 0.7200
|
||||||
|
Ke 0.0000 0.0000 0.0000
|
||||||
|
|
||||||
|
newmtl boat_silver
|
||||||
|
Ns 80.0000
|
||||||
|
Ni 1.5000
|
||||||
|
d 1.0000
|
||||||
|
Tr 0.0000
|
||||||
|
Tf 1.0000 1.0000 1.0000
|
||||||
|
illum 2
|
||||||
|
Ka 0.3333 0.3333 0.3333
|
||||||
|
Kd 0.3333 0.3333 0.3333
|
||||||
|
Ks 0.7200 0.7200 0.7200
|
||||||
|
Ke 0.0000 0.0000 0.0000
|
||||||
|
|
||||||
|
newmtl boat_buffer
|
||||||
|
Ns 10.0000
|
||||||
|
Ni 1.5000
|
||||||
|
d 1.0000
|
||||||
|
Tr 0.0000
|
||||||
|
Tf 1.0000 1.0000 1.0000
|
||||||
|
illum 2
|
||||||
|
Ka 1.0000 1.0000 1.0000
|
||||||
|
Kd 1.0000 1.0000 1.0000
|
||||||
|
Ks 0.2700 0.2700 0.2700
|
||||||
|
Ke 0.0000 0.0000 0.0000
|
||||||
|
map_Ka boat_buffer_diffuse.jpg
|
||||||
|
map_Kd boat_buffer_diffuse.jpg
|
||||||
|
|
||||||
|
newmtl boat_roof_accessory
|
||||||
|
Ns 15.0000
|
||||||
|
Ni 1.5000
|
||||||
|
d 1.0000
|
||||||
|
Tr 0.0000
|
||||||
|
Tf 1.0000 1.0000 1.0000
|
||||||
|
illum 2
|
||||||
|
Ka 1.0000 1.0000 1.0000
|
||||||
|
Kd 1.0000 1.0000 1.0000
|
||||||
|
Ks 0.3600 0.3600 0.3600
|
||||||
|
Ke 0.0000 0.0000 0.0000
|
||||||
|
map_Ka boat_roof_accessory_diffuse.jpg
|
||||||
|
map_Kd boat_roof_accessory_diffuse.jpg
|
||||||
|
|
||||||
|
newmtl boat_body
|
||||||
|
Ns 55.0000
|
||||||
|
Ni 1.5000
|
||||||
|
d 1.0000
|
||||||
|
Tr 0.0000
|
||||||
|
Tf 1.0000 1.0000 1.0000
|
||||||
|
illum 2
|
||||||
|
Ka 1.0000 1.0000 1.0000
|
||||||
|
Kd 1.0000 1.0000 1.0000
|
||||||
|
Ks 0.3600 0.3600 0.3600
|
||||||
|
Ke 0.0000 0.0000 0.0000
|
||||||
|
map_Ka boat_body_diffuse.jpg
|
||||||
|
map_Kd boat_body_diffuse.jpg
|
Before Width: | Height: | Size: 18 MiB After Width: | Height: | Size: 17 MiB |
After Width: | Height: | Size: 78 B |
@@ -0,0 +1,180 @@
|
|||||||
|
newmtl Model001_Material001
|
||||||
|
map_Kd Yorktown_paint6.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material002
|
||||||
|
map_Kd diff_null_7.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material003
|
||||||
|
map_Kd diff_null_14.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material004
|
||||||
|
map_Kd diff_null_Color005.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material005
|
||||||
|
map_Kd diff_null_8.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material006
|
||||||
|
map_Kd diff_null_Color007.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material007
|
||||||
|
map_Kd diff_null_17.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material008
|
||||||
|
map_Kd diff_null_FrontColor.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material009
|
||||||
|
map_Kd diff_null_BackColor.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material010
|
||||||
|
map_Kd diff_null_4.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material011
|
||||||
|
map_Kd Color_004.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material012
|
||||||
|
map_Kd diff_null_Gray1.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material013
|
||||||
|
map_Kd diff_null_3.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material014
|
||||||
|
map_Kd Metal_Rough.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material015
|
||||||
|
map_Kd diff_null_12.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material016
|
||||||
|
map_Kd diff_null_19.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material017
|
||||||
|
map_Kd diff_null_Color003.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material018
|
||||||
|
map_Kd diff_null_mat1.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material019
|
||||||
|
map_Kd diff_null_1.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material020
|
||||||
|
map_Kd diff_null_2.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material021
|
||||||
|
map_Kd diff_null_Black1.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material022
|
||||||
|
map_Kd diff_null_Model001Materia.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material023
|
||||||
|
map_Kd diff_null_Model001Mate1.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material024
|
||||||
|
map_Kd diff_null_13.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material025
|
||||||
|
map_Kd _6.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material026
|
||||||
|
map_Kd Metal_Aluminum_Anodized.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material027
|
||||||
|
map_Kd diff_null_Color006.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material028
|
||||||
|
map_Kd Blinds_Vertical_Stripe_Gray.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material029
|
||||||
|
map_Kd diff_null_15.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material030
|
||||||
|
map_Kd diff_null_Color002.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material031
|
||||||
|
map_Kd Cladding_Siding_Tan.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material032
|
||||||
|
map_Kd diff_null_Color008.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material033
|
||||||
|
map_Kd diff_null_16.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material034
|
||||||
|
map_Kd _2.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material035
|
||||||
|
map_Kd Blinds_Roman_Hobbled_Blue.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material036
|
||||||
|
map_Kd Blinds_Wood_White.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material037
|
||||||
|
map_Kd Metal_Seamed.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material038
|
||||||
|
map_Kd image_16.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material039
|
||||||
|
map_Kd diff_null_9.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material040
|
||||||
|
map_Kd image_15.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material041
|
||||||
|
map_Kd _Blinds_Roman_Hobbled_Blue_1.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material042
|
||||||
|
map_Kd diff_null_10.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material043
|
||||||
|
map_Kd diff_null_Material5.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material044
|
||||||
|
map_Kd diff_null_Material1.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
||||||
|
newmtl Model001_Material045
|
||||||
|
map_Kd diff_null_11.png
|
||||||
|
map_bump bumpmap_flat.png
|
||||||
|
|
BIN
Projekte/battleship/converter/12219_boat_v2_L2.j3o
Normal file
BIN
Projekte/battleship/converter/Battle.j3o
Normal file
BIN
Projekte/battleship/converter/CV.j3o
Normal file
BIN
Projekte/battleship/converter/KingGeorgeV.j3o
Normal file
@@ -42,15 +42,13 @@ public class ModelExporter extends SimpleApplication {
|
|||||||
@Override
|
@Override
|
||||||
public void simpleInitApp() {
|
public void simpleInitApp() {
|
||||||
export("Models/KingGeorgeV/King_George_V.obj", "KingGeorgeV.j3o");//NON-NLS
|
export("Models/KingGeorgeV/King_George_V.obj", "KingGeorgeV.j3o");//NON-NLS
|
||||||
export("Models/BoatSmall/12219_boat_v2_L2.obj", "BoatSmall.j3o"); //NON-NLS
|
export("Models/BoatSmall/12219_boat_v2_L2.obj", "12219_boat_v2_L2.j3o");
|
||||||
export("Models/Battle/14084_WWII_Ship_German_Type_II_U-boat_v2_L1.obj", "Battle.j3o"); //NON-NLS
|
export("Models/Battle/14084_WWII_Ship_German_Type_II_U-boat_v2_L1.obj", "Battle.j3o"); //NON-NLS
|
||||||
export("Models/CV/essex_scb-125_generic.obj", "CV.j3o"); //NON-NLS
|
export("Models/CV/essex_scb-125_generic.obj", "CV.j3o"); //NON-NLS
|
||||||
export("Models/Figures/Würfel_blau.obj", "Würfel_blau.j30");
|
export("Models/Shell/Shell/45.obj", "Shell.j3o");
|
||||||
export("Models/Figures/Würfel_gelb.obj", "Würfel_gelb.j30");
|
|
||||||
export("Models/Figures/Würfel_grün.obj", "Würfel_grün.j30");
|
|
||||||
export("Models/Figures/Würfel_rosa.obj", "Würfel_rosa.j30");
|
|
||||||
export("Models/Figures/Würfel_rot.obj", "Würfel_rot.j30");
|
|
||||||
export("Models/Figures/Würfel_schwarz.obj", "Würfel_schwarz.j30");
|
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 78 B |
387991
Projekte/battleship/converter/src/main/resources/Models/Shell/Shell/45.obj
Normal file
After Width: | Height: | Size: 127 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 147 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 101 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 35 KiB |
After Width: | Height: | Size: 3.0 MiB |
After Width: | Height: | Size: 246 KiB |
After Width: | Height: | Size: 330 KiB |
@@ -7,11 +7,12 @@
|
|||||||
|
|
||||||
package pp.battleship;
|
package pp.battleship;
|
||||||
|
|
||||||
import static java.lang.Math.max;
|
import pp.util.config.Config;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import pp.util.config.Config;
|
import static java.lang.Math.max;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides access to the configuration settings for the Battleship game.
|
* Provides access to the configuration settings for the Battleship game.
|
||||||
@@ -30,7 +31,7 @@ public class BattleshipConfig extends Config {
|
|||||||
* The default port number for the Battleship server.
|
* The default port number for the Battleship server.
|
||||||
*/
|
*/
|
||||||
@Property("port")
|
@Property("port")
|
||||||
private int port = 12234;
|
private int port = 1234;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The width of the game map in terms of grid units.
|
* The width of the game map in terms of grid units.
|
||||||
|
@@ -10,8 +10,8 @@ package pp.battleship.game.client;
|
|||||||
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.model.Shell;
|
||||||
import pp.battleship.notification.Sound;
|
import pp.battleship.notification.Sound;
|
||||||
|
|
||||||
import java.lang.System.Logger.Level;
|
import java.lang.System.Logger.Level;
|
||||||
@@ -54,13 +54,13 @@ class BattleState extends ClientState {
|
|||||||
@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(Level.INFO, "report effect: {0}", msg); //NON-NLS
|
||||||
|
|
||||||
myTurn = msg.isMyTurn();
|
myTurn = msg.isMyTurn();
|
||||||
logic.setInfoText(msg.getInfoTextKey());
|
logic.setInfoText(msg.getInfoTextKey());
|
||||||
Shell shell = new Shell(msg.getShot());
|
Shell shell = new Shell(msg.getShot());
|
||||||
affectedMap(msg).add(shell);
|
affectedMap(msg).add(shell);
|
||||||
logic.playSound(Sound.SHELL_FLYING);
|
logic.playSound(Sound.SHELL_FLYING);
|
||||||
logic.setState(new ShootingState(logic, shell, myTurn, msg));
|
logic.setState(new ShootingState(logic, shell, myTurn, msg));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -8,10 +8,10 @@
|
|||||||
package pp.battleship.game.server;
|
package pp.battleship.game.server;
|
||||||
|
|
||||||
import pp.battleship.BattleshipConfig;
|
import pp.battleship.BattleshipConfig;
|
||||||
import pp.battleship.message.client.AnimationFinishedMessage;
|
|
||||||
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;
|
||||||
|
import pp.battleship.message.client.AnimationFinishedMessage;
|
||||||
import pp.battleship.message.server.EffectMessage;
|
import pp.battleship.message.server.EffectMessage;
|
||||||
import pp.battleship.message.server.GameDetails;
|
import pp.battleship.message.server.GameDetails;
|
||||||
import pp.battleship.message.server.ServerMessage;
|
import pp.battleship.message.server.ServerMessage;
|
||||||
@@ -36,10 +36,10 @@ 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 Set<Player> waitPlayers = 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;
|
||||||
private Set<Player> waitPlayers = new HashSet<>();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a ServerGameLogic with the specified sender and configuration.
|
* Constructs a ServerGameLogic with the specified sender and configuration.
|
||||||
@@ -136,6 +136,7 @@ public class ServerGameLogic implements ClientInterpreter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the reception of a MapMessage.
|
* Handles the reception of a MapMessage.
|
||||||
|
* Also tests valid ship placement on the Map.
|
||||||
*
|
*
|
||||||
* @param msg the received MapMessage
|
* @param msg the received MapMessage
|
||||||
* @param from the ID of the sender client
|
* @param from the ID of the sender client
|
||||||
@@ -153,8 +154,85 @@ public class ServerGameLogic implements ClientInterpreter {
|
|||||||
send(players.get(from), new GameDetails(config));
|
send(players.get(from), new GameDetails(config));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the reception of a ShootMessage.
|
||||||
|
*
|
||||||
|
* @param msg the received ShootMessage
|
||||||
|
* @param from the ID of the sender client
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void received(ShootMessage msg, int from) {
|
||||||
|
if (state != ServerState.BATTLE)
|
||||||
|
LOGGER.log(Level.ERROR, "shoot not allowed in {0}", state); //NON-NLS
|
||||||
|
else{
|
||||||
|
setState(ServerState.ANIMATION);
|
||||||
|
shoot(getPlayerById(from), msg.getPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the player as ready and sets their ships.
|
||||||
|
* Transitions the state to PLAY if both players are ready.
|
||||||
|
*
|
||||||
|
* @param player the player who is ready
|
||||||
|
* @param ships the list of ships placed by the player
|
||||||
|
*/
|
||||||
|
void playerReady(Player player, List<Battleship> ships) {
|
||||||
|
if (!readyPlayers.add(player)) {
|
||||||
|
LOGGER.log(Level.ERROR, "{0} was already ready", player); //NON-NLS
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ships.forEach(player.getMap()::add);
|
||||||
|
if (readyPlayers.size() == 2) {
|
||||||
|
for (Player p : players)
|
||||||
|
send(p, new StartBattleMessage(p == activePlayer));
|
||||||
|
setState(ServerState.BATTLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the shooting action by the player.
|
||||||
|
*
|
||||||
|
* @param p the player who shot
|
||||||
|
* @param pos the position of the shot
|
||||||
|
*/
|
||||||
|
void shoot(Player p, IntPoint pos) {
|
||||||
|
if (p != activePlayer) return;
|
||||||
|
|
||||||
|
final Player otherPlayer = getOpponent(activePlayer);
|
||||||
|
final Battleship selectedShip = otherPlayer.getMap().findShipAt(pos);
|
||||||
|
if (selectedShip == null) {
|
||||||
|
// shot missed
|
||||||
|
send(activePlayer, EffectMessage.miss(true, pos));
|
||||||
|
send(otherPlayer, EffectMessage.miss(false, pos));
|
||||||
|
activePlayer = otherPlayer;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// shot hit a ship
|
||||||
|
selectedShip.hit(pos);
|
||||||
|
if (otherPlayer.getMap().getRemainingShips().isEmpty()) {
|
||||||
|
// game is over
|
||||||
|
send(activePlayer, EffectMessage.won(pos, selectedShip));
|
||||||
|
send(otherPlayer, EffectMessage.lost(pos, selectedShip, activePlayer.getMap().getRemainingShips()));
|
||||||
|
setState(ServerState.GAME_OVER);
|
||||||
|
}
|
||||||
|
else if (selectedShip.isDestroyed()) {
|
||||||
|
// ship has been destroyed, but game is not yet over
|
||||||
|
send(activePlayer, EffectMessage.shipDestroyed(true, pos, selectedShip));
|
||||||
|
send(otherPlayer, EffectMessage.shipDestroyed(false, pos, selectedShip));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// ship has been hit, but it hasn't been destroyed
|
||||||
|
send(activePlayer, EffectMessage.hit(true, pos));
|
||||||
|
send(otherPlayer, EffectMessage.hit(false, pos));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Handles the reception of a AnimationFinishedMessage.
|
* Handles the reception of a AnimationFinishedMessage.
|
||||||
*
|
*
|
||||||
@@ -212,79 +290,4 @@ public class ServerGameLogic implements ClientInterpreter {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the reception of a ShootMessage.
|
|
||||||
*
|
|
||||||
* @param msg the received ShootMessage
|
|
||||||
* @param from the ID of the sender client
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void received(ShootMessage msg, int from) {
|
|
||||||
if (state != ServerState.BATTLE)
|
|
||||||
LOGGER.log(Level.ERROR, "shoot not allowed in {0}", state); //NON-NLS
|
|
||||||
else{
|
|
||||||
setState(ServerState.ANIMATION);
|
|
||||||
shoot(getPlayerById(from), msg.getPosition());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the player as ready and sets their ships.
|
|
||||||
* Transitions the state to PLAY if both players are ready.
|
|
||||||
*
|
|
||||||
* @param player the player who is ready
|
|
||||||
* @param ships the list of ships placed by the player
|
|
||||||
*/
|
|
||||||
void playerReady(Player player, List<Battleship> ships) {
|
|
||||||
if (!readyPlayers.add(player)) {
|
|
||||||
LOGGER.log(Level.ERROR, "{0} was already ready", player); //NON-NLS
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ships.forEach(player.getMap()::add);
|
|
||||||
if (readyPlayers.size() == 2) {
|
|
||||||
for (Player p : players)
|
|
||||||
send(p, new StartBattleMessage(p == activePlayer));
|
|
||||||
setState(ServerState.BATTLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the shooting action by the player.
|
|
||||||
*
|
|
||||||
* @param p the player who shot
|
|
||||||
* @param pos the position of the shot
|
|
||||||
*/
|
|
||||||
void shoot(Player p, IntPoint pos) {
|
|
||||||
if (p != activePlayer) return;
|
|
||||||
final Player otherPlayer = getOpponent(activePlayer);
|
|
||||||
final Battleship selectedShip = otherPlayer.getMap().findShipAt(pos);
|
|
||||||
if (selectedShip == null) {
|
|
||||||
// shot missed
|
|
||||||
send(activePlayer, EffectMessage.miss(true, pos));
|
|
||||||
send(otherPlayer, EffectMessage.miss(false, pos));
|
|
||||||
activePlayer = otherPlayer;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// shot hit a ship
|
|
||||||
selectedShip.hit(pos);
|
|
||||||
if (otherPlayer.getMap().getRemainingShips().isEmpty()) {
|
|
||||||
// game is over
|
|
||||||
send(activePlayer, EffectMessage.won(pos, selectedShip));
|
|
||||||
send(otherPlayer, EffectMessage.lost(pos, selectedShip, activePlayer.getMap().getRemainingShips()));
|
|
||||||
setState(ServerState.GAME_OVER);
|
|
||||||
}
|
|
||||||
else if (selectedShip.isDestroyed()) {
|
|
||||||
// ship has been destroyed, but game is not yet over
|
|
||||||
send(activePlayer, EffectMessage.shipDestroyed(true, pos, selectedShip));
|
|
||||||
send(otherPlayer, EffectMessage.shipDestroyed(false, pos, selectedShip));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// ship has been hit, but it hasn't been destroyed
|
|
||||||
send(activePlayer, EffectMessage.hit(true, pos));
|
|
||||||
send(otherPlayer, EffectMessage.hit(false, pos));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -31,8 +31,9 @@ enum ServerState {
|
|||||||
*/
|
*/
|
||||||
ANIMATION,
|
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.
|
||||||
*/
|
*/
|
||||||
GAME_OVER
|
GAME_OVER,
|
||||||
}
|
}
|
||||||
|
@@ -7,11 +7,11 @@
|
|||||||
|
|
||||||
package pp.battleship.game.singlemode;
|
package pp.battleship.game.singlemode;
|
||||||
|
|
||||||
import pp.battleship.message.client.AnimationFinishedMessage;
|
|
||||||
import pp.battleship.message.client.ClientInterpreter;
|
import pp.battleship.message.client.ClientInterpreter;
|
||||||
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;
|
||||||
|
import pp.battleship.message.client.AnimationFinishedMessage;
|
||||||
import pp.battleship.model.Battleship;
|
import pp.battleship.model.Battleship;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,6 +69,7 @@ class Copycat implements ClientInterpreter {
|
|||||||
copiedMessage = msg;
|
copiedMessage = msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a copy of the provided {@link Battleship}.
|
* Creates a copy of the provided {@link Battleship}.
|
||||||
*
|
*
|
||||||
@@ -78,4 +79,5 @@ class Copycat implements ClientInterpreter {
|
|||||||
private static Battleship copy(Battleship ship) {
|
private static Battleship copy(Battleship ship) {
|
||||||
return new Battleship(ship.getLength(), ship.getX(), ship.getY(), ship.getRot());
|
return new Battleship(ship.getLength(), ship.getX(), ship.getY(), ship.getRot());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
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.AnimationFinishedMessage;
|
|
||||||
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.client.AnimationFinishedMessage;
|
||||||
import pp.battleship.message.server.EffectMessage;
|
import pp.battleship.message.server.EffectMessage;
|
||||||
import pp.battleship.message.server.GameDetails;
|
import pp.battleship.message.server.GameDetails;
|
||||||
import pp.battleship.message.server.ServerInterpreter;
|
import pp.battleship.message.server.ServerInterpreter;
|
||||||
@@ -72,7 +72,6 @@ class RobotClient implements ServerInterpreter {
|
|||||||
* Makes the RobotClient take a shot by sending a ShootMessage with the target position.
|
* Makes the RobotClient take a shot by sending a ShootMessage with the target position.
|
||||||
*/
|
*/
|
||||||
private void robotShot() {
|
private void robotShot() {
|
||||||
|
|
||||||
connection.sendRobotMessage(new ShootMessage(getShotPosition()));
|
connection.sendRobotMessage(new ShootMessage(getShotPosition()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,8 +30,8 @@ public interface ClientInterpreter {
|
|||||||
/**
|
/**
|
||||||
* Processes a received AnimationFinishedMessage.
|
* Processes a received AnimationFinishedMessage.
|
||||||
*
|
*
|
||||||
* @param msg the MapMessage to be processed
|
* @param animationFinishedMessage the MapMessage to be processed
|
||||||
* @param from the connection ID from which the message was received
|
* @param from the connection ID from which the message was received
|
||||||
*/
|
*/
|
||||||
void received(AnimationFinishedMessage msg, int from);
|
void received(AnimationFinishedMessage animationFinishedMessage, int from);
|
||||||
}
|
}
|
||||||
|
@@ -123,3 +123,4 @@ public class Shell implements Item {
|
|||||||
visitor.visit(this);
|
visitor.visit(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -78,15 +78,6 @@ public class ShipMap {
|
|||||||
addItem(ship);
|
addItem(ship);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers a shot on the map and triggers an item addition event.
|
|
||||||
*
|
|
||||||
* @param shell the shell to be registered on the map
|
|
||||||
*/
|
|
||||||
public void add(Shell shell) {
|
|
||||||
addItem(shell);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers a shot on the map, updates the state of the affected ship (if any),
|
* Registers a shot on the map, updates the state of the affected ship (if any),
|
||||||
* and triggers an item addition event.
|
* and triggers an item addition event.
|
||||||
@@ -100,6 +91,16 @@ public class ShipMap {
|
|||||||
addItem(shot);
|
addItem(shot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a shot on the map and triggers an item addition event.
|
||||||
|
*
|
||||||
|
* @param shell the shell to be registered on 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,12 +28,14 @@ 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
|
* Visits a Shell element
|
||||||
*
|
*
|
||||||
* @param shell the Shell element to visit
|
* @param shell the Shell element to visit
|
||||||
* @return the result of visitung the Battleship element
|
* @return the result of visitung the Battleship element
|
||||||
*/
|
*/
|
||||||
|
|
||||||
T visit(Shell shell);
|
T visit(Shell shell);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,8 +27,9 @@ public interface VoidVisitor {
|
|||||||
void visit(Battleship ship);
|
void visit(Battleship ship);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Visits a Shell element
|
* * Visits a Shell element
|
||||||
* @param shell the Shell element to visit
|
* @param shell the Shell element to visit
|
||||||
*/
|
*/
|
||||||
void visit(Shell shell);
|
void visit(Shell shell);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -27,4 +27,5 @@ public enum Sound {
|
|||||||
* Sound of a shell flying
|
* Sound of a shell flying
|
||||||
*/
|
*/
|
||||||
SHELL_FLYING
|
SHELL_FLYING
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -25,11 +25,12 @@ button.cancel=Cancel
|
|||||||
server.dialog=Server
|
server.dialog=Server
|
||||||
host.name=Host
|
host.name=Host
|
||||||
port.number=Port
|
port.number=Port
|
||||||
|
server.host= Self-Host Game
|
||||||
wait.its.not.your.turn=Wait, it's not your turn!!
|
wait.its.not.your.turn=Wait, it's not your turn!!
|
||||||
menu.quit=Quit game
|
menu.quit=Quit game
|
||||||
menu.return-to-game=Return to game
|
menu.return-to-game=Return to game
|
||||||
menu.sound-enabled=Sound switched on
|
menu.sound-enabled=Sound switched on
|
||||||
menu.background-sound-enabled=Background music switched on
|
menu.background-sound-enabled=Music switched on
|
||||||
menu.map.load=Load map from file...
|
menu.map.load=Load map from file...
|
||||||
menu.map.save=Save map in file...
|
menu.map.save=Save map in file...
|
||||||
label.file=File:
|
label.file=File:
|
||||||
@@ -38,4 +39,3 @@ dialog.error=Error
|
|||||||
dialog.question=Question
|
dialog.question=Question
|
||||||
port.must.be.integer=Port must be an integer number
|
port.must.be.integer=Port must be an integer number
|
||||||
map.doesnt.fit=The map doesn't fit to this game
|
map.doesnt.fit=The map doesn't fit to this game
|
||||||
client.server-start=Start server
|
|
||||||
|
@@ -25,11 +25,12 @@ button.cancel=Abbruch
|
|||||||
server.dialog=Server
|
server.dialog=Server
|
||||||
host.name=Host
|
host.name=Host
|
||||||
port.number=Port
|
port.number=Port
|
||||||
|
server.host= Spiel selbst hosten
|
||||||
wait.its.not.your.turn=Warte, Du bist nicht dran!!
|
wait.its.not.your.turn=Warte, Du bist nicht dran!!
|
||||||
menu.quit=Spiel beenden
|
menu.quit=Spiel beenden
|
||||||
menu.return-to-game=Zurück zum Spiel
|
menu.return-to-game=Zurück zum Spiel
|
||||||
menu.sound-enabled=Sound eingeschaltet
|
menu.sound-enabled=Sound eingeschaltet
|
||||||
menu.background-sound-enabled=Hintergrundmusik eingeschaltet
|
menu.background-sound-enabled=Musik eingeschaltet
|
||||||
menu.map.load=Karte von Datei laden...
|
menu.map.load=Karte von Datei laden...
|
||||||
menu.map.save=Karte in Datei speichern...
|
menu.map.save=Karte in Datei speichern...
|
||||||
label.file=Datei:
|
label.file=Datei:
|
||||||
@@ -38,4 +39,3 @@ dialog.error=Fehler
|
|||||||
dialog.question=Frage
|
dialog.question=Frage
|
||||||
port.must.be.integer=Der Port muss eine ganze Zahl sein
|
port.must.be.integer=Der Port muss eine ganze Zahl sein
|
||||||
map.doesnt.fit=Diese Karte passt nicht zu diesem Spiel
|
map.doesnt.fit=Diese Karte passt nicht zu diesem Spiel
|
||||||
client.server-start=Server starten
|
|
||||||
|
@@ -10,7 +10,7 @@
|
|||||||
# This file defines the configuration settings for the Battleship server.
|
# This file defines the configuration settings for the Battleship server.
|
||||||
#
|
#
|
||||||
# The port number on which the server will listen for incoming connections.
|
# The port number on which the server will listen for incoming connections.
|
||||||
port=42069
|
port=1234
|
||||||
#
|
#
|
||||||
# The dimensions of the game map.
|
# The dimensions of the game map.
|
||||||
# '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.
|
||||||
|
@@ -18,10 +18,10 @@ import pp.battleship.BattleshipConfig;
|
|||||||
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.AnimationFinishedMessage;
|
|
||||||
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;
|
||||||
|
import pp.battleship.message.client.AnimationFinishedMessage;
|
||||||
import pp.battleship.message.server.EffectMessage;
|
import pp.battleship.message.server.EffectMessage;
|
||||||
import pp.battleship.message.server.GameDetails;
|
import pp.battleship.message.server.GameDetails;
|
||||||
import pp.battleship.message.server.ServerMessage;
|
import pp.battleship.message.server.ServerMessage;
|
||||||
@@ -81,9 +81,10 @@ public class BattleshipServer implements MessageListener<HostedConnection>, Conn
|
|||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
startServer();
|
startServer();
|
||||||
while (true)
|
while (true) {
|
||||||
processNextMessage();
|
processNextMessage();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void startServer() {
|
private void startServer() {
|
||||||
try {
|
try {
|
||||||
@@ -120,6 +121,7 @@ public class BattleshipServer implements MessageListener<HostedConnection>, Conn
|
|||||||
Serializer.registerClass(Battleship.class);
|
Serializer.registerClass(Battleship.class);
|
||||||
Serializer.registerClass(IntPoint.class);
|
Serializer.registerClass(IntPoint.class);
|
||||||
Serializer.registerClass(Shot.class);
|
Serializer.registerClass(Shot.class);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerListeners() {
|
private void registerListeners() {
|
||||||
|
@@ -111,7 +111,7 @@ public class DialogManager {
|
|||||||
*
|
*
|
||||||
* @param dialog the dialog to open
|
* @param dialog the dialog to open
|
||||||
*/
|
*/
|
||||||
public void open(Dialog dialog) {
|
void open(Dialog dialog) {
|
||||||
dialogStack.push(dialog);
|
dialogStack.push(dialog);
|
||||||
dialog.update();
|
dialog.update();
|
||||||
app.getGuiNode().attachChild(dialog);
|
app.getGuiNode().attachChild(dialog);
|
||||||
@@ -133,7 +133,7 @@ public class DialogManager {
|
|||||||
* @param dialog the dialog to close
|
* @param dialog the dialog to close
|
||||||
* @throws IllegalArgumentException if the specified dialog is not the top dialog
|
* @throws IllegalArgumentException if the specified dialog is not the top dialog
|
||||||
*/
|
*/
|
||||||
public void close(Dialog dialog) {
|
void close(Dialog dialog) {
|
||||||
if (!isTop(dialog))
|
if (!isTop(dialog))
|
||||||
throw new IllegalArgumentException(dialog + " is not the top dialog");
|
throw new IllegalArgumentException(dialog + " is not the top dialog");
|
||||||
dialogStack.pop();
|
dialogStack.pop();
|
||||||
|
@@ -1,10 +1,7 @@
|
|||||||
// Styling of Lemur components
|
// Styling of Lemur components
|
||||||
// For documentation, see:
|
// For documentation, see
|
||||||
// https://github.com/jMonkeyEngine-Contributions/Lemur/wiki/Styling
|
// https://github.com/jMonkeyEngine-Contributions/Lemur/wiki/Styling
|
||||||
|
|
||||||
import com.jme3.math.ColorRGBA
|
|
||||||
import com.simsilica.lemur.*
|
|
||||||
import com.simsilica.lemur.component.QuadBackgroundComponent
|
|
||||||
import com.simsilica.lemur.Button
|
import com.simsilica.lemur.Button
|
||||||
import com.simsilica.lemur.Button.ButtonAction
|
import com.simsilica.lemur.Button.ButtonAction
|
||||||
import com.simsilica.lemur.Command
|
import com.simsilica.lemur.Command
|
||||||
@@ -13,21 +10,14 @@ import com.simsilica.lemur.Insets3f
|
|||||||
import com.simsilica.lemur.component.QuadBackgroundComponent
|
import com.simsilica.lemur.component.QuadBackgroundComponent
|
||||||
import com.simsilica.lemur.component.TbtQuadBackgroundComponent
|
import com.simsilica.lemur.component.TbtQuadBackgroundComponent
|
||||||
|
|
||||||
def bgColor = color(1, 1, 1, 1)
|
def bgColor = color(0.25, 0.5, 0.5, 1)
|
||||||
def buttonEnabledColor = color(0, 0, 0, 1)
|
def buttonEnabledColor = color(0.8, 0.9, 1, 1)
|
||||||
def buttonDisabledColor = color(0.8, 0.9, 1, 0.2)
|
def buttonDisabledColor = color(0.8, 0.9, 1, 0.2)
|
||||||
def buttonBgColor = color(1, 1, 1, 1)
|
def buttonBgColor = color(0, 0.75, 0.75, 1)
|
||||||
def sliderColor = color(0.6, 0.8, 0.8, 1)
|
def sliderColor = color(0.6, 0.8, 0.8, 1)
|
||||||
def sliderBgColor = color(0.5, 0.75, 0.75, 1)
|
def sliderBgColor = color(0.5, 0.75, 0.75, 1)
|
||||||
def gradientColor = color(0.5, 0.75, 0.85, 0.5)
|
def gradientColor = color(0.5, 0.75, 0.85, 0.5)
|
||||||
def tabbuttonEnabledColor = color(0.4, 0.45, 0.5, 1)
|
def tabbuttonEnabledColor = color(0.4, 0.45, 0.5, 1)
|
||||||
def solidWhiteBackground = new QuadBackgroundComponent(new ColorRGBA(1, 1, 1, 1))
|
|
||||||
def greyBackground = new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 1.0f));
|
|
||||||
def lightGreyBackground = new QuadBackgroundComponent(new ColorRGBA(0.4f, 0.4f, 0.4f, 1.0f));
|
|
||||||
def lightGrey = color(0.6, 0.6, 0.6, 1.0)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def gradient = TbtQuadBackgroundComponent.create(
|
def gradient = TbtQuadBackgroundComponent.create(
|
||||||
texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png",
|
texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png",
|
||||||
@@ -38,14 +28,6 @@ def gradient = TbtQuadBackgroundComponent.create(
|
|||||||
def doubleGradient = new QuadBackgroundComponent(gradientColor)
|
def doubleGradient = new QuadBackgroundComponent(gradientColor)
|
||||||
doubleGradient.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
|
doubleGradient.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
|
||||||
generateMips: false)
|
generateMips: false)
|
||||||
//doubleGradient.color = color(0, 0, 0, 1)
|
|
||||||
|
|
||||||
def orangeBorder = TbtQuadBackgroundComponent.create(
|
|
||||||
texture(name: "/com/simsilica/lemur/icons/border.png", // Replace with an appropriate texture if needed
|
|
||||||
generateMips: false),
|
|
||||||
1, 1, 1, 126, 126,
|
|
||||||
1f, false)
|
|
||||||
orangeBorder.color = color(1, 0.5, 0, 1) // Orange color
|
|
||||||
|
|
||||||
selector("pp") {
|
selector("pp") {
|
||||||
font = font("Interface/Fonts/Metropolis/Metropolis-Regular-32.fnt")
|
font = font("Interface/Fonts/Metropolis/Metropolis-Regular-32.fnt")
|
||||||
@@ -56,63 +38,18 @@ selector("label", "pp") {
|
|||||||
color = buttonEnabledColor
|
color = buttonEnabledColor
|
||||||
}
|
}
|
||||||
|
|
||||||
selector("label-Bold", "pp") {
|
|
||||||
insets = new Insets3f(2, 2, 2, 2)
|
|
||||||
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-32.fnt")
|
|
||||||
fontSize = 30
|
|
||||||
color = buttonEnabledColor
|
|
||||||
textHAlignment = HAlignment.Center
|
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
selector("label-toolbar", "pp") {
|
|
||||||
insets = new Insets3f(2, 2, 2, 2)
|
|
||||||
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-32.fnt")
|
|
||||||
fontSize = 30
|
|
||||||
color = new ColorRGBA(ColorRGBA.White)
|
|
||||||
textHAlignment = HAlignment.Center
|
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
|
|
||||||
}
|
|
||||||
selector("label-Text", "pp") {
|
|
||||||
insets = new Insets3f(2, 2, 2, 2)
|
|
||||||
fontSize = 25
|
|
||||||
color = buttonEnabledColor
|
|
||||||
}
|
|
||||||
|
|
||||||
selector("label-account", "pp") {
|
|
||||||
insets = new Insets3f(2, 2, 2, 2)
|
|
||||||
fontSize = 25
|
|
||||||
color = new ColorRGBA(ColorRGBA.White)
|
|
||||||
textHAlignment = HAlignment.Center
|
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
}
|
|
||||||
|
|
||||||
selector("card-label", "pp") {
|
|
||||||
insets = new Insets3f(2, 2, 2, 2)
|
|
||||||
color = ColorRGBA.Black
|
|
||||||
}
|
|
||||||
|
|
||||||
selector("header", "pp") {
|
selector("header", "pp") {
|
||||||
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-42.fnt")
|
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-42.fnt")
|
||||||
insets = new Insets3f(2, 2, 2, 2)
|
insets = new Insets3f(2, 2, 2, 2)
|
||||||
color = color(1, 0.5, 0, 1)
|
color = color(1, 0.5, 0, 1)
|
||||||
textHAlignment = HAlignment.Center
|
textHAlignment = HAlignment.Center
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selector("container", "pp") {
|
selector("container", "pp") {
|
||||||
background = solidWhiteBackground.clone()
|
|
||||||
background.setColor(bgColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
selector("toolbar") {
|
|
||||||
background = gradient.clone()
|
background = gradient.clone()
|
||||||
background.setColor(bgColor)
|
background.setColor(bgColor)
|
||||||
//color = (new ColorRGBA(0.4157f, 0.4235f, 0.4392f, 1.0f))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selector("slider", "pp") {
|
selector("slider", "pp") {
|
||||||
background = gradient.clone()
|
background = gradient.clone()
|
||||||
background.setColor(bgColor)
|
background.setColor(bgColor)
|
||||||
@@ -180,24 +117,17 @@ selector("title", "pp") {
|
|||||||
background.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
|
background.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
|
||||||
generateMips: false)
|
generateMips: false)
|
||||||
insets = new Insets3f(2, 2, 2, 2)
|
insets = new Insets3f(2, 2, 2, 2)
|
||||||
textHAlignment = HAlignment.Center
|
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
|
|
||||||
buttonCommands = stdButtonCommands
|
buttonCommands = stdButtonCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
selector("button", "pp") {
|
selector("button", "pp") {
|
||||||
def outerBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)) // Orange border
|
background = gradient.clone()
|
||||||
def innerBackground = new QuadBackgroundComponent(buttonBgColor) // Inner button background
|
color = buttonEnabledColor
|
||||||
|
background.setColor(buttonBgColor)
|
||||||
|
insets = new Insets3f(2, 2, 2, 2)
|
||||||
|
|
||||||
// Apply the outer border as the main background
|
|
||||||
background = outerBackground
|
|
||||||
|
|
||||||
// Use insets to create a margin/padding effect for the inner background
|
|
||||||
insets = new Insets3f(3, 3, 3, 3) // Adjust the border thickness
|
|
||||||
textHAlignment = HAlignment.Center
|
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
buttonCommands = stdButtonCommands
|
buttonCommands = stdButtonCommands
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +176,6 @@ selector("slider.down.button", "pp") {
|
|||||||
|
|
||||||
selector("checkbox", "pp") {
|
selector("checkbox", "pp") {
|
||||||
color = buttonEnabledColor
|
color = buttonEnabledColor
|
||||||
fontSize = 20
|
|
||||||
}
|
}
|
||||||
|
|
||||||
selector("rollup", "pp") {
|
selector("rollup", "pp") {
|
||||||
@@ -263,106 +192,10 @@ selector("tabbedPanel.container", "pp") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
selector("tab.button", "pp") {
|
selector("tab.button", "pp") {
|
||||||
background = solidWhiteBackground.clone()
|
background = gradient.clone()
|
||||||
background.setColor(bgColor)
|
background.setColor(bgColor)
|
||||||
color = tabbuttonEnabledColor
|
color = tabbuttonEnabledColor
|
||||||
insets = new Insets3f(4, 2, 0, 2)
|
insets = new Insets3f(4, 2, 0, 2)
|
||||||
|
|
||||||
buttonCommands = stdButtonCommands
|
buttonCommands = stdButtonCommands
|
||||||
}
|
}
|
||||||
selector("settings-title", "pp") {
|
|
||||||
def outerBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)) // Grey inner border
|
|
||||||
def innerBackground = new QuadBackgroundComponent(buttonBgColor) // White outer border background
|
|
||||||
|
|
||||||
background = outerBackground
|
|
||||||
fontSize = 40
|
|
||||||
insets = new Insets3f(3, 3, 3, 3)
|
|
||||||
textHAlignment = HAlignment.Center
|
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
}
|
|
||||||
selector("warning-title", "pp") {
|
|
||||||
def outerBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)) // Grey inner border
|
|
||||||
def innerBackground = new QuadBackgroundComponent(buttonBgColor) // White outer border background
|
|
||||||
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-42.fnt")
|
|
||||||
background = outerBackground
|
|
||||||
fontSize = 40
|
|
||||||
insets = new Insets3f(3, 3, 3, 3)
|
|
||||||
textHAlignment = HAlignment.Center
|
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
}
|
|
||||||
|
|
||||||
selector("menu-button", "pp") {
|
|
||||||
fontSize = 40 // Set font size
|
|
||||||
textHAlignment = HAlignment.Center
|
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
buttonCommands = stdButtonCommands
|
|
||||||
}
|
|
||||||
|
|
||||||
// Style for Selector text
|
|
||||||
selector("selector.item.label") {
|
|
||||||
color = color(0, 0, 0, 1) // Black text
|
|
||||||
fontSize = 16 // Optional: Adjust the text size if needed
|
|
||||||
textHAlignment = HAlignment.Left // Optional: Align text to the left
|
|
||||||
insets = new Insets3f(2, 2, 2, 2) // Optional: Add padding around text
|
|
||||||
}
|
|
||||||
// Style the popup container background
|
|
||||||
selector("selector.popup") {
|
|
||||||
background = new QuadBackgroundComponent(new ColorRGBA(1, 1, 1, 0.8f)) // Translucent white background
|
|
||||||
insets = new Insets3f(5, 5, 5, 5) // Padding inside the popup container
|
|
||||||
}
|
|
||||||
|
|
||||||
// Style the text of dropdown options
|
|
||||||
selector("selector.item.label") {
|
|
||||||
color = color(0, 0, 0, 1) // Black text
|
|
||||||
fontSize = 16 // Optional: Adjust font size
|
|
||||||
textHAlignment = HAlignment.Left // Align text to the left
|
|
||||||
insets = new Insets3f(2, 5, 2, 5) // Add padding for each option
|
|
||||||
}
|
|
||||||
|
|
||||||
// Style the hover state of dropdown options
|
|
||||||
selector("selector.item.label", "hover") {
|
|
||||||
color = color(1, 1, 1, 1) // White text when hovered
|
|
||||||
background = new QuadBackgroundComponent(new ColorRGBA(0.2f, 0.6f, 1.0f, 0.9f)) // Highlighted background
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def enabledCommandToolbar = new Command<Button>() {
|
|
||||||
void execute(Button source) {
|
|
||||||
if (source.isEnabled()){
|
|
||||||
source.setColor(ColorRGBA.White)
|
|
||||||
def orangeBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)); // Orange background
|
|
||||||
source.setBackground(orangeBackground);
|
|
||||||
} else{
|
|
||||||
source.setColor(ColorRGBA.White)
|
|
||||||
def grayBackground = new QuadBackgroundComponent(ColorRGBA.Gray); // Gray background
|
|
||||||
source.setBackground(grayBackground);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def stdButtonCommandsToolbar = [
|
|
||||||
(ButtonAction.Down) : [pressedCommand],
|
|
||||||
(ButtonAction.Up) : [pressedCommand],
|
|
||||||
(ButtonAction.Enabled) : [enabledCommandToolbar],
|
|
||||||
(ButtonAction.Disabled): [enabledCommandToolbar]
|
|
||||||
]
|
|
||||||
|
|
||||||
selector("button-toolbar", "pp") {
|
|
||||||
def outerBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)) // Orange border
|
|
||||||
def innerBackground = new QuadBackgroundComponent(buttonBgColor) // Inner button background
|
|
||||||
|
|
||||||
// Apply the outer border as the main background
|
|
||||||
background = outerBackground
|
|
||||||
|
|
||||||
// Use insets to create a margin/padding effect for the inner background
|
|
||||||
insets = new Insets3f(3, 3, 3, 3) // Adjust the border thickness
|
|
||||||
textHAlignment = HAlignment.Center
|
|
||||||
textVAlignment = VAlignment.Center
|
|
||||||
buttonCommands = stdButtonCommandsToolbar
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -1,38 +0,0 @@
|
|||||||
plugins {
|
|
||||||
id 'buildlogic.jme-application-conventions'
|
|
||||||
id 'com.github.johnrengelman.shadow' version '8.1.1'
|
|
||||||
}
|
|
||||||
|
|
||||||
description = 'Monopoly Client'
|
|
||||||
|
|
||||||
dependencies {
|
|
||||||
implementation project(":jme-common")
|
|
||||||
implementation project(":monopoly:model")
|
|
||||||
implementation project(":monopoly:server")
|
|
||||||
|
|
||||||
implementation 'com.simsilica:lemur-proto:1.13.0'
|
|
||||||
implementation libs.jme3.desktop
|
|
||||||
implementation libs.lemur
|
|
||||||
implementation libs.lemurproto
|
|
||||||
|
|
||||||
implementation libs.selenium
|
|
||||||
|
|
||||||
runtimeOnly libs.jme3.awt.dialogs
|
|
||||||
runtimeOnly libs.jme3.plugins
|
|
||||||
runtimeOnly libs.jme3.jogg
|
|
||||||
runtimeOnly libs.jme3.testdata
|
|
||||||
}
|
|
||||||
|
|
||||||
application {
|
|
||||||
mainClass = 'pp.monopoly.client.MonopolyApp'
|
|
||||||
applicationName = 'monopoly'
|
|
||||||
}
|
|
||||||
|
|
||||||
shadowJar {
|
|
||||||
manifest {
|
|
||||||
attributes(
|
|
||||||
'Main-Class': 'pp.monopoly.client.MonopolyApp'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@@ -1,39 +0,0 @@
|
|||||||
########################################
|
|
||||||
## Programming project code
|
|
||||||
## UniBw M, 2022, 2023, 2024
|
|
||||||
## www.unibw.de/inf2
|
|
||||||
## (c) Mark Minas (mark.minas@unibw.de)
|
|
||||||
########################################
|
|
||||||
#
|
|
||||||
# Monopoly client configuration
|
|
||||||
#
|
|
||||||
# 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=10
|
|
||||||
map.height=10
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# Screen settings
|
|
||||||
#
|
|
||||||
# Color of the text displayed at the top of the overlay.
|
|
||||||
# The format is (red, green, blue, alpha) where each value ranges from 0 to 1.
|
|
||||||
overlay.top.color=1, 1, 1, 1
|
|
||||||
#
|
|
||||||
# Application settings configuration
|
|
||||||
# Determines whether the settings window is shown at startup.
|
|
||||||
settings.show=false
|
|
||||||
#
|
|
||||||
# Specifies the width of the application window in pixels.
|
|
||||||
settings.resolution.width=1200
|
|
||||||
#
|
|
||||||
# Specifies the height of the application window in pixels.
|
|
||||||
settings.resolution.height=800
|
|
||||||
#
|
|
||||||
# Determines whether the application runs in full-screen mode.
|
|
||||||
settings.full-screen=false
|
|
||||||
#
|
|
||||||
# Enables or disables gamma correction to improve color accuracy.
|
|
||||||
settings.use-gamma-correction=true
|
|
||||||
#
|
|
||||||
# Indicates whether the statistics window is displayed during gameplay.
|
|
||||||
statistics.show=false
|
|
@@ -1,8 +0,0 @@
|
|||||||
handlers=java.util.logging.ConsoleHandler
|
|
||||||
.level=INFO
|
|
||||||
pp.level=FINE
|
|
||||||
com.jme3.network.level=INFO
|
|
||||||
;com.jme3.util.TangentBinormalGenerator.level=SEVERE
|
|
||||||
java.util.logging.ConsoleHandler.level=FINER
|
|
||||||
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
|
|
||||||
;java.util.logging.SimpleFormatter.format=[%4$s %2$s] %5$s%n
|
|
@@ -1,217 +0,0 @@
|
|||||||
package pp.monopoly.client;
|
|
||||||
|
|
||||||
import com.jme3.app.Application;
|
|
||||||
import com.jme3.app.state.AppStateManager;
|
|
||||||
import com.jme3.asset.AssetManager;
|
|
||||||
import com.jme3.light.AmbientLight;
|
|
||||||
import com.jme3.light.DirectionalLight;
|
|
||||||
import com.jme3.material.Material;
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.math.FastMath;
|
|
||||||
import com.jme3.math.Quaternion;
|
|
||||||
import com.jme3.math.Vector2f;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.renderer.Camera;
|
|
||||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
|
||||||
import com.jme3.scene.Geometry;
|
|
||||||
import com.jme3.scene.Node;
|
|
||||||
import com.jme3.scene.Spatial;
|
|
||||||
import com.jme3.scene.shape.Box;
|
|
||||||
import com.jme3.shadow.DirectionalLightShadowRenderer;
|
|
||||||
import com.jme3.shadow.EdgeFilteringMode;
|
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
import com.jme3.util.SkyFactory;
|
|
||||||
import com.jme3.util.TangentBinormalGenerator;
|
|
||||||
import pp.monopoly.model.Board;
|
|
||||||
import pp.monopoly.client.gui.BobTheBuilder;
|
|
||||||
import pp.monopoly.client.gui.Toolbar;
|
|
||||||
|
|
||||||
import static pp.util.FloatMath.PI;
|
|
||||||
import static pp.util.FloatMath.TWO_PI;
|
|
||||||
import static pp.util.FloatMath.cos;
|
|
||||||
import static pp.util.FloatMath.sin;
|
|
||||||
import static pp.util.FloatMath.sqrt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages the rendering and visual aspects of the sea and sky in the Battleship game.
|
|
||||||
* This state is responsible for setting up and updating the sea, sky, and lighting
|
|
||||||
* conditions, and controls the camera to create a dynamic view of the game environment.
|
|
||||||
*/
|
|
||||||
public class BoardAppState extends MonopolyAppState {
|
|
||||||
/**
|
|
||||||
* The path to the unshaded texture material.
|
|
||||||
*/
|
|
||||||
private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The path to the sea texture material.
|
|
||||||
*/
|
|
||||||
private static final String BoardTexture = "Pictures/board2.png"; //NON-NLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root node for all visual elements in this state.
|
|
||||||
*/
|
|
||||||
private final Node viewNode = new Node("view"); //NON-NLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The node containing the scene elements, such as the sea surface.
|
|
||||||
*/
|
|
||||||
private final Node sceneNode = new Node("scene"); //NON-NLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronizes the buildings's visual representation with the game logic.
|
|
||||||
*/
|
|
||||||
private BobTheBuilder bobTheBuilder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The pop-up manager for displaying messages and notifications.
|
|
||||||
*/
|
|
||||||
private PopUpManager popUpManager;;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the state by setting up the sky, lights, and other visual components.
|
|
||||||
* This method is called when the state is first attached to the state manager.
|
|
||||||
*
|
|
||||||
* @param stateManager the state manager
|
|
||||||
* @param application the application
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void initialize(AppStateManager stateManager, Application application) {
|
|
||||||
super.initialize(stateManager, application);
|
|
||||||
popUpManager = new PopUpManager(getApp());
|
|
||||||
viewNode.attachChild(sceneNode);
|
|
||||||
setupLights();
|
|
||||||
setupSky();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables the sea and sky state, setting up the scene and registering any necessary listeners.
|
|
||||||
* This method is called when the state is set to active.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void enableState() {
|
|
||||||
getApp().getRootNode().detachAllChildren();
|
|
||||||
getApp().getGuiNode().detachAllChildren();
|
|
||||||
|
|
||||||
new Toolbar(getApp()).open();
|
|
||||||
sceneNode.detachAllChildren();
|
|
||||||
setupScene();
|
|
||||||
if (bobTheBuilder == null) {
|
|
||||||
bobTheBuilder = new BobTheBuilder(getApp(), getApp().getRootNode());
|
|
||||||
System.out.println("LISTENER IS REGISTEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
|
|
||||||
getGameLogic().addListener(bobTheBuilder);
|
|
||||||
}
|
|
||||||
getApp().getRootNode().attachChild(viewNode);
|
|
||||||
}
|
|
||||||
//TODO remove this only for camera testing
|
|
||||||
private static final float ABOVE_SEA_LEVEL = 10f;
|
|
||||||
private static final float INCLINATION = 2.5f;
|
|
||||||
private float cameraAngle;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adjusts the camera position and orientation to create a circular motion around
|
|
||||||
* the center of the map. This provides a dynamic view of the sea and surrounding environment.
|
|
||||||
*/
|
|
||||||
private void adjustCamera() {
|
|
||||||
final Board board = getGameLogic().getBoard();
|
|
||||||
final float mx = 0.5f * board.getWidth();
|
|
||||||
final float my = 0.5f * board.getHeight();
|
|
||||||
final float radius = 2f * sqrt(mx * mx + my + my);
|
|
||||||
final float cos = radius * cos(cameraAngle);
|
|
||||||
final float sin = radius * sin(cameraAngle);
|
|
||||||
final float x = mx - cos;
|
|
||||||
final float y = my - sin;
|
|
||||||
final Camera camera = getApp().getCamera();
|
|
||||||
camera.setLocation(new Vector3f(x, ABOVE_SEA_LEVEL, y));
|
|
||||||
camera.lookAt(new Vector3f(0,0, 0),
|
|
||||||
Vector3f.UNIT_Y);
|
|
||||||
camera.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disables the sea and sky state, removing visual elements from the scene and unregistering listeners.
|
|
||||||
* This method is called when the state is set to inactive.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void disableState() {
|
|
||||||
getApp().getRootNode().detachChild(viewNode);
|
|
||||||
if (bobTheBuilder != null) {
|
|
||||||
getGameLogic().removeListener(bobTheBuilder);
|
|
||||||
bobTheBuilder = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the state each frame, moving the camera to simulate it circling around the map.
|
|
||||||
*
|
|
||||||
* @param tpf the time per frame (seconds)
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void update(float tpf) {
|
|
||||||
super.update(tpf);
|
|
||||||
//TODO remove this only for camera testing
|
|
||||||
cameraAngle += TWO_PI * 0.05f * tpf;
|
|
||||||
adjustCamera();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the lighting for the scene, including directional and ambient lights.
|
|
||||||
* Also configures shadows to enhance the visual depth of the scene.
|
|
||||||
*/
|
|
||||||
private void setupLights() {
|
|
||||||
final AssetManager assetManager = getApp().getAssetManager();
|
|
||||||
final DirectionalLightShadowRenderer shRend = new DirectionalLightShadowRenderer(assetManager, 2048, 3);
|
|
||||||
shRend.setLambda(0.55f);
|
|
||||||
shRend.setShadowIntensity(0.6f);
|
|
||||||
shRend.setEdgeFilteringMode(EdgeFilteringMode.Bilinear);
|
|
||||||
getApp().getViewPort().addProcessor(shRend);
|
|
||||||
|
|
||||||
final DirectionalLight sun = new DirectionalLight();
|
|
||||||
sun.setDirection(new Vector3f(-1f, -0.7f, -1f).normalizeLocal());
|
|
||||||
viewNode.addLight(sun);
|
|
||||||
shRend.setLight(sun);
|
|
||||||
|
|
||||||
final AmbientLight ambientLight = new AmbientLight(new ColorRGBA(1f, 1f, 1f, 1f));
|
|
||||||
viewNode.addLight(ambientLight);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the sky in the scene using a skybox with textures for all six directions.
|
|
||||||
* This creates a realistic and immersive environment for the sea.
|
|
||||||
*/
|
|
||||||
private void setupSky() {
|
|
||||||
final AssetManager assetManager = getApp().getAssetManager();
|
|
||||||
final Texture west = assetManager.loadTexture("Pictures/Backdrop/left.jpg"); //NON-NLS
|
|
||||||
final Texture east = assetManager.loadTexture("Pictures/Backdrop/right.jpg"); //NON-NLS
|
|
||||||
final Texture north = assetManager.loadTexture("Pictures/Backdrop/front.jpg"); //NON-NLS
|
|
||||||
final Texture south = assetManager.loadTexture("Pictures/Backdrop/back.jpg"); //NON-NLS
|
|
||||||
final Texture up = assetManager.loadTexture("Pictures/Backdrop/up.jpg"); //NON-NLS
|
|
||||||
final Texture down = assetManager.loadTexture("Pictures/Backdrop/down.jpg"); //NON-NLS
|
|
||||||
final Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
|
|
||||||
// sky.rotate(0, PI, 0);
|
|
||||||
viewNode.attachChild(sky);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the sea surface in the scene. This includes creating the sea mesh,
|
|
||||||
* applying textures, and enabling shadows.
|
|
||||||
*/
|
|
||||||
private void setupScene() {
|
|
||||||
final Board board = getGameLogic().getBoard();
|
|
||||||
final float x = board.getWidth();
|
|
||||||
final float y = board.getHeight();
|
|
||||||
final Box seaMesh = new Box(y, 0.1f, x);
|
|
||||||
final Geometry seaGeo = new Geometry("sea", seaMesh); //NONs-NLS
|
|
||||||
seaGeo.setLocalTranslation(new Vector3f(0, -0.1f, 0));
|
|
||||||
Quaternion rotation = new com.jme3.math.Quaternion();
|
|
||||||
rotation.fromAngleAxis(FastMath.HALF_PI, com.jme3.math.Vector3f.UNIT_Y);
|
|
||||||
seaGeo.setLocalRotation(rotation);
|
|
||||||
final Material seaMat = new Material(getApp().getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
|
|
||||||
Texture texture = getApp().getAssetManager().loadTexture("Pictures/board2.png");
|
|
||||||
seaMat.setTexture("DiffuseMap", texture);
|
|
||||||
seaGeo.setMaterial(seaMat);
|
|
||||||
seaGeo.setShadowMode(ShadowMode.CastAndReceive);
|
|
||||||
TangentBinormalGenerator.generate(seaGeo);
|
|
||||||
sceneNode.attachChild(seaGeo);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,99 +0,0 @@
|
|||||||
////////////////////////////////////////
|
|
||||||
// Programming project code
|
|
||||||
// UniBw M, 2022, 2023, 2024
|
|
||||||
// www.unibw.de/inf2
|
|
||||||
// (c) Mark Minas (mark.minas@unibw.de)
|
|
||||||
////////////////////////////////////////
|
|
||||||
|
|
||||||
package pp.monopoly.client;
|
|
||||||
|
|
||||||
import java.lang.System.Logger;
|
|
||||||
|
|
||||||
import java.lang.System.Logger.Level;
|
|
||||||
|
|
||||||
import com.jme3.input.controls.ActionListener;
|
|
||||||
import com.jme3.scene.Node;
|
|
||||||
|
|
||||||
import pp.monopoly.client.gui.TestWorld;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the state responsible for managing the battle interface within the Battleship game.
|
|
||||||
* This state handles the display and interaction of the battle map, including the opponent's map.
|
|
||||||
* It manages GUI components, input events, and the layout of the interface when this state is enabled.
|
|
||||||
*/
|
|
||||||
public class GameAppState extends MonopolyAppState {
|
|
||||||
private static final Logger LOGGER = System.getLogger(MonopolyAppState.class.getName());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A listener for handling click events in the battle interface.
|
|
||||||
* When a click is detected, it triggers the corresponding actions on the opponent's map.
|
|
||||||
*/
|
|
||||||
private final ActionListener clickListener = (name, isPressed, tpf) -> click(isPressed);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The root node for all GUI components in the battle state.
|
|
||||||
*/
|
|
||||||
private final Node battleNode = new Node("Game"); //NON-NLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A view representing the opponent's map in the GUI.
|
|
||||||
*/
|
|
||||||
private TestWorld testWorld;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables the battle state by initializing, laying out, and adding GUI components.
|
|
||||||
* Attaches the components to the GUI node and registers input listeners.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void enableState() {
|
|
||||||
LOGGER.log(Level.DEBUG, "Enabling game state");
|
|
||||||
battleNode.detachAllChildren();
|
|
||||||
initializeGuiComponents();
|
|
||||||
addGuiComponents();
|
|
||||||
getApp().getGuiNode().attachChild(battleNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disables the battle state by removing GUI components and unregistering input listeners.
|
|
||||||
* Also handles cleanup of resources, such as the opponent's map view.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void disableState() {
|
|
||||||
getApp().getGuiNode().detachChild(battleNode);
|
|
||||||
getApp().getInputManager().removeListener(clickListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the GUI components used in the battle state.
|
|
||||||
* Creates the opponent's map view and adds a grid overlay to it.
|
|
||||||
*/
|
|
||||||
private void initializeGuiComponents() {
|
|
||||||
|
|
||||||
// Initialisiere TestWorld mit Spielern
|
|
||||||
testWorld = new TestWorld(getApp());
|
|
||||||
testWorld.initializeScene();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the initialized GUI components to the battle node.
|
|
||||||
* Currently, it attaches the opponent's map view to the node.
|
|
||||||
*/
|
|
||||||
private void addGuiComponents() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles click events in the battle interface. If the event indicates a click (not a release),
|
|
||||||
* it translates the cursor position to the model's coordinate system and triggers the game logic
|
|
||||||
* for interacting with the opponent's map.
|
|
||||||
*
|
|
||||||
* @param isPressed whether the mouse button is currently pressed (true) or released (false)
|
|
||||||
*/
|
|
||||||
private void click(boolean isPressed) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(float tpf) {
|
|
||||||
super.update(tpf);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,195 +0,0 @@
|
|||||||
package pp.monopoly.client;
|
|
||||||
|
|
||||||
import static pp.util.PreferencesUtils.getPreferences;
|
|
||||||
|
|
||||||
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;
|
|
||||||
import com.jme3.asset.AssetLoadException;
|
|
||||||
import com.jme3.asset.AssetNotFoundException;
|
|
||||||
import com.jme3.audio.AudioData;
|
|
||||||
import com.jme3.audio.AudioNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the background and secondary music in the game.
|
|
||||||
* Allows playing, stopping, and toggling between background music and a secondary track.
|
|
||||||
*/
|
|
||||||
public class GameMusic extends AbstractAppState {
|
|
||||||
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"; // NON-NLS
|
|
||||||
private static final String VOLUME_PREF = "volume"; // NON-NLS
|
|
||||||
|
|
||||||
private AudioNode mainMusic;
|
|
||||||
private AudioNode secondaryMusic;
|
|
||||||
private boolean isMainMusicPlaying = false;
|
|
||||||
private boolean isSecondaryMusicPlaying = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the GameMusic app state and loads the background music.
|
|
||||||
*
|
|
||||||
* @param stateManager The state manager
|
|
||||||
* @param app The application instance
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void initialize(AppStateManager stateManager, Application app) {
|
|
||||||
super.initialize(stateManager, app);
|
|
||||||
mainMusic = loadSound(app, "Sound/background.ogg");
|
|
||||||
secondaryMusic = loadSound(app, "Sound/ChooseYourCharakter.ogg");
|
|
||||||
setVolume(volumeInPreferences());
|
|
||||||
if (isEnabled()) {
|
|
||||||
playMainMusic();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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(true);
|
|
||||||
sound.setPositional(false);
|
|
||||||
return sound;
|
|
||||||
}
|
|
||||||
catch (AssetLoadException | AssetNotFoundException ex) {
|
|
||||||
LOGGER.log(Level.ERROR, ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays the main music.
|
|
||||||
*/
|
|
||||||
private void playMainMusic() {
|
|
||||||
if (!isEnabled()) {
|
|
||||||
return; // Sound is disabled
|
|
||||||
}
|
|
||||||
if (mainMusic != null && !isMainMusicPlaying) {
|
|
||||||
mainMusic.play();
|
|
||||||
isMainMusicPlaying = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the main music.
|
|
||||||
*/
|
|
||||||
private void stopMainMusic() {
|
|
||||||
if (mainMusic != null && isMainMusicPlaying) {
|
|
||||||
mainMusic.stop();
|
|
||||||
isMainMusicPlaying = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plays the secondary music and stops the main music.
|
|
||||||
*
|
|
||||||
* @param app The application instance
|
|
||||||
* @param secondaryMusicFile The file path of the secondary audio file
|
|
||||||
*/
|
|
||||||
private void playSecondaryMusic() {
|
|
||||||
if(!isEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isSecondaryMusicPlaying) {
|
|
||||||
return; // Secondary music is already playing
|
|
||||||
}
|
|
||||||
|
|
||||||
stopMainMusic();
|
|
||||||
|
|
||||||
if (secondaryMusic != null) {
|
|
||||||
secondaryMusic.setVolume(volumeInPreferences());
|
|
||||||
secondaryMusic.play();
|
|
||||||
isSecondaryMusicPlaying = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the secondary music.
|
|
||||||
*/
|
|
||||||
private void stopSecondaryMusic() {
|
|
||||||
if (secondaryMusic != null && isSecondaryMusicPlaying) {
|
|
||||||
secondaryMusic.stop();
|
|
||||||
isSecondaryMusicPlaying = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles between the background music and the secondary track.
|
|
||||||
* If the secondary track is playing, it stops and resumes the background music.
|
|
||||||
* If the background music is playing, it pauses and plays the secondary track.
|
|
||||||
*
|
|
||||||
* @param app The application instance
|
|
||||||
* @param secondaryMusicFile The file path of the secondary audio file
|
|
||||||
*/
|
|
||||||
public void toggleMusic() {
|
|
||||||
if(!isEnabled()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isSecondaryMusicPlaying) {
|
|
||||||
stopSecondaryMusic();
|
|
||||||
playMainMusic();
|
|
||||||
} else {
|
|
||||||
playSecondaryMusic();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the audio volume for both the main and secondary tracks.
|
|
||||||
*
|
|
||||||
* @param vol The volume level (0.0f to 1.0f)
|
|
||||||
*/
|
|
||||||
public void setVolume(float vol) {
|
|
||||||
if (mainMusic != null) mainMusic.setVolume(vol);
|
|
||||||
if (secondaryMusic != null) secondaryMusic.setVolume(vol);
|
|
||||||
PREFERENCES.putFloat(VOLUME_PREF, vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables the sound system.
|
|
||||||
* When disabled, all music stops.
|
|
||||||
*
|
|
||||||
* @param enabled {@code true} to enable, {@code false} to disable
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
if (isEnabled() == enabled) return;
|
|
||||||
|
|
||||||
if (enabled) {
|
|
||||||
playMainMusic();
|
|
||||||
} else {
|
|
||||||
stopMainMusic();
|
|
||||||
stopSecondaryMusic();
|
|
||||||
}
|
|
||||||
|
|
||||||
super.setEnabled(enabled);
|
|
||||||
LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); // NON-NLS
|
|
||||||
PREFERENCES.putBoolean(ENABLED_PREF, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the current sound volume preference.
|
|
||||||
*
|
|
||||||
* @return The volume level (0.0f to 1.0f)
|
|
||||||
*/
|
|
||||||
public static float volumeInPreferences() {
|
|
||||||
return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if sound is enabled in the preferences.
|
|
||||||
*
|
|
||||||
* @return {@code true} if sound is enabled, {@code false} otherwise
|
|
||||||
*/
|
|
||||||
public static boolean enabledInPreferences() {
|
|
||||||
return PREFERENCES.getBoolean(ENABLED_PREF, true);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,263 +0,0 @@
|
|||||||
////////////////////////////////////////
|
|
||||||
// Programming project code
|
|
||||||
// UniBw M, 2022, 2023, 2024
|
|
||||||
// www.unibw.de/inf2
|
|
||||||
// (c) Mark Minas (mark.minas@unibw.de)
|
|
||||||
////////////////////////////////////////
|
|
||||||
|
|
||||||
package pp.monopoly.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;
|
|
||||||
import com.jme3.asset.AssetLoadException;
|
|
||||||
import com.jme3.asset.AssetNotFoundException;
|
|
||||||
import com.jme3.audio.AudioData;
|
|
||||||
import com.jme3.audio.AudioNode;
|
|
||||||
|
|
||||||
import pp.monopoly.notification.GameEventListener;
|
|
||||||
import pp.monopoly.notification.SoundEvent;
|
|
||||||
import static pp.util.PreferencesUtils.getPreferences;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An application state that plays sounds.
|
|
||||||
*/
|
|
||||||
public class GameSound extends AbstractAppState implements GameEventListener {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logger instance for logging messages related to GameSound.
|
|
||||||
*/
|
|
||||||
private static final Logger LOGGER = System.getLogger(GameSound.class.getName());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preferences instance for managing GameSound-related settings.
|
|
||||||
*/
|
|
||||||
private static final Preferences PREFERENCES = getPreferences(GameSound.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preference key for enabling or disabling GameSound.
|
|
||||||
*/
|
|
||||||
private static final String ENABLED_PREF = "enabled"; //NON-NLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preference key for storing the volume level of GameSound.
|
|
||||||
*/
|
|
||||||
private static final String VOLUME_PREF = "volume"; //NON-NLS
|
|
||||||
|
|
||||||
private AudioNode passStartSound;
|
|
||||||
private AudioNode eventCardSound;
|
|
||||||
private AudioNode gulagSound;
|
|
||||||
private AudioNode diceRollSound;
|
|
||||||
private AudioNode moneyCollectSound;
|
|
||||||
private AudioNode moneyLostSound;
|
|
||||||
private AudioNode tradeAcceptedSound;
|
|
||||||
private AudioNode tradeRejectedSound;
|
|
||||||
private AudioNode winnerSound;
|
|
||||||
private AudioNode looserSound;
|
|
||||||
private AudioNode buttonSound;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if sound is enabled in the preferences.
|
|
||||||
*
|
|
||||||
* @return {@code true} if sound is enabled, {@code false} otherwise.
|
|
||||||
*/
|
|
||||||
public static boolean enabledInPreferences() {
|
|
||||||
return PREFERENCES.getBoolean(ENABLED_PREF, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the game sound on or off.
|
|
||||||
*/
|
|
||||||
public void toggleSound() {
|
|
||||||
setEnabled(!isEnabled());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if sound is enabled in the preferences.
|
|
||||||
*
|
|
||||||
* @return float to which the volume is set
|
|
||||||
*/
|
|
||||||
public static float volumeInPreferences() {
|
|
||||||
return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 (isEnabled() == enabled) return;
|
|
||||||
super.setEnabled(enabled);
|
|
||||||
LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); //NON-NLS
|
|
||||||
PREFERENCES.putBoolean(ENABLED_PREF, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the sound effects for the game.
|
|
||||||
* Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
|
|
||||||
*
|
|
||||||
* @param stateManager The state manager
|
|
||||||
* @param app The application
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void initialize(AppStateManager stateManager, Application app) {
|
|
||||||
super.initialize(stateManager, app);
|
|
||||||
passStartSound = loadSound(app, "Sound/Effects/passStart.ogg");
|
|
||||||
eventCardSound = loadSound(app, "Sound/Effects/eventCard.ogg");
|
|
||||||
gulagSound = loadSound(app, "Sound/Effects/gulag.ogg");
|
|
||||||
diceRollSound = loadSound(app, "Sound/Effects/diceRoll.ogg");
|
|
||||||
moneyCollectSound = loadSound(app, "Sound/Effects/moneyCollect.ogg");
|
|
||||||
moneyLostSound = loadSound(app, "Sound/Effects/moneyLost.ogg");
|
|
||||||
tradeAcceptedSound = loadSound(app, "Sound/Effects/tradeAccepted.ogg");
|
|
||||||
tradeRejectedSound = loadSound(app, "Sound/Effects/tradeRejected.ogg");
|
|
||||||
winnerSound = loadSound(app, "Sound/Effects/winner.ogg");
|
|
||||||
looserSound = loadSound(app, "Sound/Effects/loser.ogg");
|
|
||||||
buttonSound = loadSound(app, "Sound/Effects/button.ogg");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 passStart sound effect.
|
|
||||||
*/
|
|
||||||
public void passStart() {
|
|
||||||
if (isEnabled() && passStartSound != null)
|
|
||||||
passStartSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the eventCard sound effect.
|
|
||||||
*/
|
|
||||||
public void eventCard() {
|
|
||||||
if (isEnabled() && eventCardSound != null)
|
|
||||||
eventCardSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the gulag sound effect.
|
|
||||||
*/
|
|
||||||
public void gulag() {
|
|
||||||
if (isEnabled() && gulagSound != null)
|
|
||||||
gulagSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the diceRoll sound effect.
|
|
||||||
*/
|
|
||||||
public void diceRoll() {
|
|
||||||
if (isEnabled() && diceRollSound != null)
|
|
||||||
diceRollSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the moneyCollect sound effect.
|
|
||||||
*/
|
|
||||||
public void moneyCollect() {
|
|
||||||
if (isEnabled() && moneyCollectSound != null)
|
|
||||||
moneyCollectSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the moneyLost sound effect.
|
|
||||||
*/
|
|
||||||
public void moneyLost() {
|
|
||||||
if (isEnabled() && moneyLostSound != null)
|
|
||||||
moneyLostSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the tradeAccepted sound effect.
|
|
||||||
*/
|
|
||||||
public void tradeAccepted() {
|
|
||||||
if (isEnabled() && tradeAcceptedSound != null)
|
|
||||||
tradeAcceptedSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the tradeRejected sound effect.
|
|
||||||
*/
|
|
||||||
public void tradeRejected() {
|
|
||||||
if (isEnabled() && tradeRejectedSound != null)
|
|
||||||
tradeRejectedSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the winner sound effect.
|
|
||||||
*/
|
|
||||||
public void winner() {
|
|
||||||
if (isEnabled() && winnerSound != null)
|
|
||||||
winnerSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the looser sound effect.
|
|
||||||
*/
|
|
||||||
public void looser() {
|
|
||||||
if (isEnabled() && looserSound != null)
|
|
||||||
looserSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Plays the button sound effect.
|
|
||||||
*/
|
|
||||||
public void button() {
|
|
||||||
if (isEnabled() && buttonSound != null)
|
|
||||||
buttonSound.playInstance();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Sets the volume of the sounds
|
|
||||||
* @param vol the volume to which the sounds should be set
|
|
||||||
*/
|
|
||||||
public void setVolume(float vol){
|
|
||||||
passStartSound.setVolume(vol);
|
|
||||||
eventCardSound.setVolume(vol);
|
|
||||||
gulagSound.setVolume(vol);
|
|
||||||
diceRollSound.setVolume(vol);
|
|
||||||
moneyCollectSound.setVolume(vol);
|
|
||||||
moneyLostSound.setVolume(vol);
|
|
||||||
tradeAcceptedSound.setVolume(vol);
|
|
||||||
tradeRejectedSound.setVolume(vol);
|
|
||||||
winnerSound.setVolume(vol);
|
|
||||||
looserSound.setVolume(vol);
|
|
||||||
buttonSound.setVolume(vol);
|
|
||||||
|
|
||||||
PREFERENCES.putFloat(VOLUME_PREF, vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides {@link SoundEvent#notifyListener(GameEventListener)}
|
|
||||||
* @param event the received event
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(SoundEvent event) {
|
|
||||||
switch (event.sound()) {
|
|
||||||
case PASS_START -> passStart();
|
|
||||||
case EVENT_CARD -> eventCard();
|
|
||||||
case GULAG -> gulag();
|
|
||||||
case DICE_ROLL -> diceRoll();
|
|
||||||
case MONEY_COLLECTED -> moneyCollect();
|
|
||||||
case MONEY_LOST -> moneyLost();
|
|
||||||
case TRADE_ACCEPTED -> tradeAccepted();
|
|
||||||
case TRADE_REJECTED -> tradeRejected();
|
|
||||||
case WINNER -> winner();
|
|
||||||
case LOSER -> looser();
|
|
||||||
case BUTTON -> button();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,488 +0,0 @@
|
|||||||
////////////////////////////////////////
|
|
||||||
// Programming project code
|
|
||||||
// UniBw M, 2022, 2023, 2024
|
|
||||||
// www.unibw.de/inf2
|
|
||||||
// (c) Mark Minas (mark.minas@unibw.de)
|
|
||||||
////////////////////////////////////////
|
|
||||||
|
|
||||||
package pp.monopoly.client;
|
|
||||||
|
|
||||||
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.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.logging.LogManager;
|
|
||||||
|
|
||||||
import com.jme3.app.DebugKeysAppState;
|
|
||||||
import com.jme3.app.SimpleApplication;
|
|
||||||
import com.jme3.app.StatsAppState;
|
|
||||||
import com.jme3.font.BitmapFont;
|
|
||||||
import com.jme3.font.BitmapText;
|
|
||||||
import com.jme3.input.KeyInput;
|
|
||||||
import com.jme3.input.MouseInput;
|
|
||||||
import com.jme3.input.controls.ActionListener;
|
|
||||||
import com.jme3.input.controls.KeyTrigger;
|
|
||||||
import com.jme3.input.controls.MouseButtonTrigger;
|
|
||||||
import com.jme3.system.AppSettings;
|
|
||||||
import com.simsilica.lemur.GuiGlobals;
|
|
||||||
import com.simsilica.lemur.style.BaseStyles;
|
|
||||||
|
|
||||||
import pp.dialog.DialogBuilder;
|
|
||||||
import pp.dialog.DialogManager;
|
|
||||||
import pp.graphics.Draw;
|
|
||||||
import static pp.monopoly.Resources.lookup;
|
|
||||||
import pp.monopoly.client.gui.SettingsMenu;
|
|
||||||
import pp.monopoly.client.gui.StartMenu;
|
|
||||||
import pp.monopoly.game.client.ClientGameLogic;
|
|
||||||
import pp.monopoly.game.client.MonopolyClient;
|
|
||||||
import pp.monopoly.game.client.ServerConnection;
|
|
||||||
import pp.monopoly.message.client.NotificationAnswer;
|
|
||||||
import pp.monopoly.notification.ClientStateEvent;
|
|
||||||
import pp.monopoly.notification.GameEventListener;
|
|
||||||
import pp.monopoly.notification.InfoTextEvent;
|
|
||||||
import pp.monopoly.notification.Sound;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main class for the Monopoly client application.
|
|
||||||
* It manages the initialization, input setup, GUI setup, and game states for the client.
|
|
||||||
*/
|
|
||||||
public class MonopolyApp extends SimpleApplication implements MonopolyClient, GameEventListener {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Logger for logging messages within the application.
|
|
||||||
*/
|
|
||||||
private static final Logger LOGGER = System.getLogger(MonopolyApp.class.getName());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Path to the styles script for GUI elements.
|
|
||||||
*/
|
|
||||||
private static final String STYLES_SCRIPT = "Interface/Lemur/pp-styles.groovy"; //NON-NLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Path to the font resource used in the GUI.
|
|
||||||
*/
|
|
||||||
private static final String FONT = "Interface/Fonts/Default.fnt"; //NON-NLS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Path to the client configuration file, if one exists.
|
|
||||||
*/
|
|
||||||
private static final File CONFIG_FILE = new File("client.properties");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Input mapping name for mouse clicks.
|
|
||||||
*/
|
|
||||||
public static final String CLICK = "CLICK";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Input mapping name for the Escape key.
|
|
||||||
*/
|
|
||||||
private static final String ESC = "ESC";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manager for handling dialogs within the application.
|
|
||||||
*/
|
|
||||||
private final DialogManager dialogManager = new DialogManager(this);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The server connection instance, used for communicating with the game server.
|
|
||||||
*/
|
|
||||||
private final ServerConnection serverConnection;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of the {@link Draw} class for rendering graphics.
|
|
||||||
*/
|
|
||||||
private Draw draw;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text display at the top of the GUI for showing information to the user.
|
|
||||||
*/
|
|
||||||
private BitmapText topText;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executor service for handling asynchronous tasks within the application.
|
|
||||||
*/
|
|
||||||
private ExecutorService executor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handler for managing the client's game logic.
|
|
||||||
*/
|
|
||||||
private final ClientGameLogic logic;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configuration settings for the Battleship client application.
|
|
||||||
*/
|
|
||||||
private final MonopolyAppConfig config;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener for handling actions triggered by the Escape key.
|
|
||||||
*/
|
|
||||||
private final ActionListener escapeListener = (name, isPressed, tpf) -> escape(isPressed);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener for handeling Demo Mode (Minas mode)
|
|
||||||
*/
|
|
||||||
private final ActionListener f8Listener = (name, isPressed, tpf) -> handleF8(isPressed);
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the Monopoly application.
|
|
||||||
*
|
|
||||||
* @param args Command-line arguments for launching the application.
|
|
||||||
*/
|
|
||||||
public static void main(String[] args) {
|
|
||||||
new MonopolyApp().start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new {@code MonopolyApp} instance.
|
|
||||||
* Initializes the configuration, server connection, and game logic listeners.
|
|
||||||
*/
|
|
||||||
public MonopolyApp() {
|
|
||||||
config = new MonopolyAppConfig();
|
|
||||||
config.readFromIfExists(CONFIG_FILE);
|
|
||||||
serverConnection = makeServerConnection();
|
|
||||||
logic = new ClientGameLogic(serverConnection);
|
|
||||||
logic.addListener(this);
|
|
||||||
setShowSettings(config.getShowSettings());
|
|
||||||
setSettings(makeSettings());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates and configures application settings from the client configuration.
|
|
||||||
*
|
|
||||||
* @return A configured {@link AppSettings} object.
|
|
||||||
*/
|
|
||||||
private AppSettings makeSettings() {
|
|
||||||
final AppSettings settings = new AppSettings(true);
|
|
||||||
settings.setTitle(lookup("monopoly.name"));
|
|
||||||
settings.setResolution(config.getResolutionWidth(), config.getResolutionHeight());
|
|
||||||
settings.setFullscreen(config.fullScreen());
|
|
||||||
settings.setUseRetinaFrameBuffer(config.useRetinaFrameBuffer());
|
|
||||||
settings.setGammaCorrection(config.useGammaCorrection());
|
|
||||||
return settings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory method for creating a server connection based on the current
|
|
||||||
* client configuration.
|
|
||||||
*
|
|
||||||
* @return A {@link ServerConnection} instance, which could be a real or mock server.
|
|
||||||
*/
|
|
||||||
private ServerConnection makeServerConnection() {
|
|
||||||
return new NetworkSupport(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the dialog manager responsible for managing in-game dialogs.
|
|
||||||
*
|
|
||||||
* @return The {@link DialogManager} instance.
|
|
||||||
*/
|
|
||||||
public DialogManager getDialogManager() {
|
|
||||||
return dialogManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the game logic handler for the client.
|
|
||||||
*
|
|
||||||
* @return The {@link ClientGameLogic} instance.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public ClientGameLogic getGameLogic() {
|
|
||||||
return logic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current configuration settings for the Monopoly client.
|
|
||||||
*
|
|
||||||
* @return The {@link pp.monopoly.game.client.MonopolyClientConfig} instance.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public MonopolyAppConfig getConfig() {
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the application.
|
|
||||||
* Sets up input mappings, GUI, game states, and connects to the server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void simpleInitApp() {
|
|
||||||
setPauseOnLostFocus(false);
|
|
||||||
draw = new Draw(assetManager);
|
|
||||||
setupInput();
|
|
||||||
setupStates();
|
|
||||||
setupGui();
|
|
||||||
new StartMenu(this).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up the graphical user interface (GUI) for the application.
|
|
||||||
*/
|
|
||||||
private void setupGui() {
|
|
||||||
GuiGlobals.initialize(this);
|
|
||||||
BaseStyles.loadStyleResources(STYLES_SCRIPT);
|
|
||||||
BaseStyles.loadGlassStyle();
|
|
||||||
GuiGlobals.getInstance().getStyles().setDefaultStyle("pp"); //NON-NLS
|
|
||||||
final BitmapFont normalFont = assetManager.loadFont(FONT); //NON-NLS
|
|
||||||
topText = new BitmapText(normalFont);
|
|
||||||
final int height = context.getSettings().getHeight();
|
|
||||||
topText.setLocalTranslation(10f, height - 10f, 0f);
|
|
||||||
topText.setColor(config.getTopColor());
|
|
||||||
guiNode.attachChild(topText);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configures input mappings and sets up listeners for user interactions.
|
|
||||||
*/
|
|
||||||
private void setupInput() {
|
|
||||||
inputManager.deleteMapping(INPUT_MAPPING_EXIT);
|
|
||||||
inputManager.setCursorVisible(false);
|
|
||||||
inputManager.addMapping(ESC, new KeyTrigger(KeyInput.KEY_ESCAPE));
|
|
||||||
inputManager.addMapping(CLICK, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
|
|
||||||
inputManager.addMapping("F8", new KeyTrigger(KeyInput.KEY_F8));
|
|
||||||
inputManager.addListener(f8Listener, "F8");
|
|
||||||
inputManager.addListener(escapeListener, ESC);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the action on alt m for demo mode
|
|
||||||
* @param isPressed {@code true} is alt + m is pressed, {@code false} otherwise
|
|
||||||
*/
|
|
||||||
private void handleF8(boolean isPressed) {
|
|
||||||
if (isPressed) {
|
|
||||||
LOGGER.log(Level.INFO, "F detected."); // Debug logging
|
|
||||||
getGameLogic().send(new NotificationAnswer("hack"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes and attaches the necessary application states for the game.
|
|
||||||
*/
|
|
||||||
private void setupStates() {
|
|
||||||
if (config.getShowStatistics()) {
|
|
||||||
final BitmapFont normalFont = assetManager.loadFont(FONT); //NON-NLS
|
|
||||||
final StatsAppState stats = new StatsAppState(guiNode, normalFont);
|
|
||||||
stateManager.attach(stats);
|
|
||||||
}
|
|
||||||
flyCam.setEnabled(false);
|
|
||||||
stateManager.detach(stateManager.getState(StatsAppState.class));
|
|
||||||
stateManager.detach(stateManager.getState(DebugKeysAppState.class));
|
|
||||||
|
|
||||||
attachGameSound();
|
|
||||||
attachGameMusic();
|
|
||||||
stateManager.attach(new BoardAppState());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attaches the game sound state and sets its initial enabled state.
|
|
||||||
*/
|
|
||||||
private void attachGameSound() {
|
|
||||||
final GameSound gameSound = new GameSound();
|
|
||||||
logic.addListener(gameSound);
|
|
||||||
gameSound.setEnabled(GameSound.enabledInPreferences());
|
|
||||||
stateManager.attach(gameSound);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attaches the background music state and sets its initial enabled state.
|
|
||||||
*/
|
|
||||||
private void attachGameMusic() {
|
|
||||||
final GameMusic gameSound = new GameMusic();
|
|
||||||
gameSound.setEnabled(GameMusic.enabledInPreferences());
|
|
||||||
stateManager.attach(gameSound);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the application state every frame.
|
|
||||||
* This method is called once per frame during the game loop.
|
|
||||||
*
|
|
||||||
* @param tpf Time per frame in seconds.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void simpleUpdate(float tpf) {
|
|
||||||
super.simpleUpdate(tpf);
|
|
||||||
dialogManager.update(tpf);
|
|
||||||
logic.update(tpf);
|
|
||||||
stateManager.update(tpf);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the Escape key action to either close the top dialog or show the main menu.
|
|
||||||
*
|
|
||||||
* @param isPressed Indicates whether the Escape key is pressed.
|
|
||||||
*/
|
|
||||||
public void escape(boolean isPressed) {
|
|
||||||
if (!isPressed) return;
|
|
||||||
if (dialogManager.showsDialog())
|
|
||||||
dialogManager.escape();
|
|
||||||
else
|
|
||||||
new SettingsMenu(this).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the {@link Draw} instance used for rendering graphical elements in the game.
|
|
||||||
*
|
|
||||||
* @return The {@link Draw} instance.
|
|
||||||
*/
|
|
||||||
public Draw getDraw() {
|
|
||||||
return draw;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to connect
|
|
||||||
*/
|
|
||||||
public void connect() {
|
|
||||||
serverConnection.connect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a request to close the application.
|
|
||||||
* If the request is initiated by pressing ESC, this parameter is true.
|
|
||||||
*
|
|
||||||
* @param esc If true, the request is due to the ESC key being pressed.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void requestClose(boolean esc) { /* do nothing */ }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the application, displaying a confirmation dialog if the client is connected to a server.
|
|
||||||
*/
|
|
||||||
public void closeApp() {
|
|
||||||
if (serverConnection.isConnected())
|
|
||||||
confirmDialog(lookup("confirm.leaving"), this::close);
|
|
||||||
else
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the application, disconnecting from the server and stopping the application.
|
|
||||||
*/
|
|
||||||
private void close() {
|
|
||||||
serverConnection.disconnect();
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the informational text displayed in the GUI.
|
|
||||||
*
|
|
||||||
* @param text The information text to display.
|
|
||||||
*/
|
|
||||||
public void setInfoText(String text) {
|
|
||||||
LOGGER.log(Level.DEBUG, "setInfoText {0}", text); //NON-NLS
|
|
||||||
topText.setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the informational text in the GUI based on the key received in an {@link InfoTextEvent}.
|
|
||||||
*
|
|
||||||
* @param event The {@link InfoTextEvent} containing the key for the text to display.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(InfoTextEvent event) {
|
|
||||||
LOGGER.log(Level.DEBUG, "received info text {0}", event.key()); //NON-NLS
|
|
||||||
setInfoText(lookup(event.key()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles client state events to update the game states accordingly.
|
|
||||||
*
|
|
||||||
* @param event The {@link ClientStateEvent} representing the state change.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(ClientStateEvent event) {
|
|
||||||
stateManager.getState(BoardAppState.class).setEnabled(logic.isTurn());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the executor service used for handling multithreaded tasks.
|
|
||||||
*
|
|
||||||
* @return The {@link ExecutorService} instance.
|
|
||||||
*/
|
|
||||||
public ExecutorService getExecutor() {
|
|
||||||
if (executor == null)
|
|
||||||
executor = Executors.newCachedThreadPool();
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stops the application, shutting down the executor service and halting execution.
|
|
||||||
*
|
|
||||||
* @param waitFor If true, waits for the application to stop before returning.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void stop(boolean waitFor) {
|
|
||||||
if (executor != null) executor.shutdownNow();
|
|
||||||
super.stop(waitFor);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays a confirmation dialog with a specified question and action for the "Yes" button.
|
|
||||||
*
|
|
||||||
* @param question The question to display in the dialog.
|
|
||||||
* @param yesAction The action to perform if "Yes" is selected.
|
|
||||||
*/
|
|
||||||
public void confirmDialog(String question, Runnable yesAction) {
|
|
||||||
DialogBuilder.simple(dialogManager)
|
|
||||||
.setTitle(lookup("dialog.question"))
|
|
||||||
.setText(question)
|
|
||||||
.setOkButton(lookup("button.yes"), d -> {
|
|
||||||
getGameLogic().playSound(Sound.BUTTON); // Play sound
|
|
||||||
yesAction.run(); // Execute the original yesAction
|
|
||||||
})
|
|
||||||
.setNoButton(lookup("button.no"), d -> getGameLogic().playSound(Sound.BUTTON))
|
|
||||||
.build()
|
|
||||||
.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays an error dialog with the specified error message.
|
|
||||||
*
|
|
||||||
* @param errorMessage The error message to display in the dialog.
|
|
||||||
*/
|
|
||||||
public void errorDialog(String errorMessage) {
|
|
||||||
DialogBuilder.simple(dialogManager)
|
|
||||||
.setTitle(lookup("dialog.error"))
|
|
||||||
.setText(errorMessage)
|
|
||||||
.setOkButton(lookup("button.ok"), d -> getGameLogic().playSound(Sound.BUTTON))
|
|
||||||
.build()
|
|
||||||
.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Disconnects the current server connection.
|
|
||||||
*
|
|
||||||
* This method delegates the disconnection operation to the `disconnect` method of the
|
|
||||||
* `serverConnection` object.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void disconnect() {
|
|
||||||
serverConnection.disconnect();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the unique identifier associated with the server connection.
|
|
||||||
*
|
|
||||||
* Checks if a Server is connected and returns 0 if there is no connection
|
|
||||||
*
|
|
||||||
* @return the ID of the connected Server instance.
|
|
||||||
*/
|
|
||||||
public int getId() {
|
|
||||||
if (serverConnection != null && serverConnection instanceof NetworkSupport) return ((NetworkSupport) serverConnection).getId();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,190 +0,0 @@
|
|||||||
package pp.monopoly.client;
|
|
||||||
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
|
|
||||||
import pp.monopoly.game.client.MonopolyClientConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides access to the Monopoly application configuration.
|
|
||||||
* Extends {@link MonopolyClientConfig} to include additional properties specific to the client,
|
|
||||||
* particularly those related to screen settings and visual customization.
|
|
||||||
* <p>
|
|
||||||
* <b>Note:</b> Attributes of this class should not be marked as {@code final}
|
|
||||||
* to ensure proper functionality when reading from a properties file.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class MonopolyAppConfig extends MonopolyClientConfig {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a string value found in the properties file into an object of the specified type.
|
|
||||||
* Extends the superclass method to support conversion to {@link ColorRGBA}.
|
|
||||||
*
|
|
||||||
* @param value the string value to be converted
|
|
||||||
* @param targetType the target type into which the value string is converted
|
|
||||||
* @return the converted object of the specified type
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Object convertToType(String value, Class<?> targetType) {
|
|
||||||
if (targetType == ColorRGBA.class)
|
|
||||||
return makeColorRGBA(value);
|
|
||||||
return super.convertToType(value, targetType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the specified string value to a corresponding {@link ColorRGBA} object.
|
|
||||||
*
|
|
||||||
* @param value the color in the format "red, green, blue, alpha" with all values in the range [0..1]
|
|
||||||
* @return a {@link ColorRGBA} object representing the color
|
|
||||||
* @throws IllegalArgumentException if the input string is not in the expected format
|
|
||||||
*/
|
|
||||||
private static ColorRGBA makeColorRGBA(String value) {
|
|
||||||
String[] split = value.split(",", -1);
|
|
||||||
try {
|
|
||||||
if (split.length == 4)
|
|
||||||
return new ColorRGBA(Float.parseFloat(split[0]),
|
|
||||||
Float.parseFloat(split[1]),
|
|
||||||
Float.parseFloat(split[2]),
|
|
||||||
Float.parseFloat(split[3]));
|
|
||||||
}
|
|
||||||
catch (NumberFormatException e) {
|
|
||||||
// deliberately left empty
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException(value + " should consist of exactly 4 numbers");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The width of the game view resolution in pixels.
|
|
||||||
*/
|
|
||||||
@Property("settings.resolution.width") //NON-NLS
|
|
||||||
private int resolutionWidth = 1200;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The height of the game view resolution in pixels.
|
|
||||||
*/
|
|
||||||
@Property("settings.resolution.height") //NON-NLS
|
|
||||||
private int resolutionHeight = 800;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether the game should start in full-screen mode.
|
|
||||||
*/
|
|
||||||
@Property("settings.full-screen") //NON-NLS
|
|
||||||
private boolean fullScreen = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether gamma correction should be enabled.
|
|
||||||
* If enabled, the main framebuffer is configured for sRGB colors,
|
|
||||||
* and sRGB images are linearized.
|
|
||||||
* <p>
|
|
||||||
* Requires a GPU that supports GL_ARB_framebuffer_sRGB; otherwise, this setting will be ignored.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
@Property("settings.use-gamma-correction") //NON-NLS
|
|
||||||
private boolean useGammaCorrection = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether full resolution framebuffers should be used on Retina displays.
|
|
||||||
* This setting is ignored on non-Retina platforms.
|
|
||||||
*/
|
|
||||||
@Property("settings.use-retina-framebuffer") //NON-NLS
|
|
||||||
private boolean useRetinaFrameBuffer = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether the settings window should be shown for configuring the game.
|
|
||||||
*/
|
|
||||||
@Property("settings.show") //NON-NLS
|
|
||||||
private boolean showSettings = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specifies whether the JME statistics window should be shown in the lower left corner of the screen.
|
|
||||||
*/
|
|
||||||
@Property("statistics.show") //NON-NLS
|
|
||||||
private boolean showStatistics = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The color of the top text during gameplay, represented as a {@link ColorRGBA} object.
|
|
||||||
*/
|
|
||||||
@Property("overlay.top.color") //NON-NLS
|
|
||||||
private ColorRGBA topColor = ColorRGBA.White;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a default {@code MonopolyAppConfig} with predefined values.
|
|
||||||
*/
|
|
||||||
public MonopolyAppConfig() {
|
|
||||||
// Default constructor
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the width of the game view resolution in pixels.
|
|
||||||
*
|
|
||||||
* @return the width of the game view resolution in pixels
|
|
||||||
*/
|
|
||||||
public int getResolutionWidth() {
|
|
||||||
return resolutionWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the height of the game view resolution in pixels.
|
|
||||||
*
|
|
||||||
* @return the height of the game view resolution in pixels
|
|
||||||
*/
|
|
||||||
public int getResolutionHeight() {
|
|
||||||
return resolutionHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the game should start in full-screen mode.
|
|
||||||
*
|
|
||||||
* @return {@code true} if the game should start in full-screen mode; {@code false} otherwise
|
|
||||||
*/
|
|
||||||
public boolean fullScreen() {
|
|
||||||
return fullScreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether gamma correction is enabled.
|
|
||||||
* If enabled, the main framebuffer is configured for sRGB colors,
|
|
||||||
* and sRGB images are linearized.
|
|
||||||
*
|
|
||||||
* @return {@code true} if gamma correction is enabled; {@code false} otherwise
|
|
||||||
*/
|
|
||||||
public boolean useGammaCorrection() {
|
|
||||||
return useGammaCorrection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether full resolution framebuffers should be used on Retina displays.
|
|
||||||
* This setting is ignored on non-Retina platforms.
|
|
||||||
*
|
|
||||||
* @return {@code true} if full resolution framebuffers should be used on Retina displays; {@code false} otherwise
|
|
||||||
*/
|
|
||||||
public boolean useRetinaFrameBuffer() {
|
|
||||||
return useRetinaFrameBuffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the settings window should be shown for configuring the game.
|
|
||||||
*
|
|
||||||
* @return {@code true} if the settings window should be shown; {@code false} otherwise
|
|
||||||
*/
|
|
||||||
public boolean getShowSettings() {
|
|
||||||
return showSettings;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the JME statistics window should be shown in the lower left corner of the screen.
|
|
||||||
*
|
|
||||||
* @return {@code true} if the statistics window should be shown; {@code false} otherwise
|
|
||||||
*/
|
|
||||||
public boolean getShowStatistics() {
|
|
||||||
return showStatistics;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the color of the top text during gameplay as a {@link ColorRGBA} object.
|
|
||||||
*
|
|
||||||
* @return the color of the top text during gameplay
|
|
||||||
*/
|
|
||||||
public ColorRGBA getTopColor() {
|
|
||||||
return topColor;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,84 +0,0 @@
|
|||||||
package pp.monopoly.client;
|
|
||||||
|
|
||||||
import com.jme3.app.Application;
|
|
||||||
import com.jme3.app.state.AbstractAppState;
|
|
||||||
import com.jme3.app.state.AppStateManager;
|
|
||||||
|
|
||||||
import pp.monopoly.game.client.ClientGameLogic;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract class representing a state in the Monopoly game.
|
|
||||||
* Extends the AbstractAppState from jMonkeyEngine to manage state behavior.
|
|
||||||
*/
|
|
||||||
public abstract class MonopolyAppState extends AbstractAppState {
|
|
||||||
private MonopolyApp app;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new MonopolyAppState that is initially disabled.
|
|
||||||
*/
|
|
||||||
protected MonopolyAppState() {
|
|
||||||
setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the state manager and application.
|
|
||||||
*
|
|
||||||
* @param stateManager The state manager
|
|
||||||
* @param application The application instance
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void initialize(AppStateManager stateManager, Application application) {
|
|
||||||
super.initialize(stateManager, application);
|
|
||||||
this.app = (MonopolyApp) application;
|
|
||||||
if (isEnabled()) {
|
|
||||||
enableState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the MonopolyApp instance associated with this MonopolyAppState.
|
|
||||||
*
|
|
||||||
* @return The MonopolyApp instance.
|
|
||||||
*/
|
|
||||||
public MonopolyApp getApp() {
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the client game logic handler.
|
|
||||||
*
|
|
||||||
* @return the client game logic handler
|
|
||||||
*/
|
|
||||||
public ClientGameLogic getGameLogic() {
|
|
||||||
return app.getGameLogic();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the enabled state of the MonopolyAppState.
|
|
||||||
* If the new state is the same as the current state, the method returns.
|
|
||||||
*
|
|
||||||
* @param enabled The new enabled state.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
if (isEnabled() == enabled) return;
|
|
||||||
super.setEnabled(enabled);
|
|
||||||
if (app != null) {
|
|
||||||
if (enabled) {
|
|
||||||
enableState();
|
|
||||||
} else {
|
|
||||||
disableState();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the state is enabled. Override to define specific behavior.
|
|
||||||
*/
|
|
||||||
protected abstract void enableState();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the state is disabled. Override to define specific behavior.
|
|
||||||
*/
|
|
||||||
protected abstract void disableState();
|
|
||||||
}
|
|
@@ -1,164 +0,0 @@
|
|||||||
////////////////////////////////////////
|
|
||||||
// Programming project code
|
|
||||||
// UniBw M, 2022, 2023, 2024
|
|
||||||
// www.unibw.de/inf2
|
|
||||||
// (c) Mark Minas (mark.minas@unibw.de)
|
|
||||||
////////////////////////////////////////
|
|
||||||
|
|
||||||
package pp.monopoly.client;
|
|
||||||
|
|
||||||
import com.jme3.network.Client;
|
|
||||||
import com.jme3.network.ClientStateListener;
|
|
||||||
import com.jme3.network.Message;
|
|
||||||
import com.jme3.network.MessageListener;
|
|
||||||
import com.jme3.network.Network;
|
|
||||||
|
|
||||||
import pp.monopoly.client.gui.CreateGameMenu;
|
|
||||||
import pp.monopoly.game.client.ServerConnection;
|
|
||||||
import pp.monopoly.message.client.ClientMessage;
|
|
||||||
import pp.monopoly.message.server.ServerMessage;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.System.Logger;
|
|
||||||
import java.lang.System.Logger.Level;
|
|
||||||
|
|
||||||
import static pp.monopoly.Resources.lookup;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages the network connection for the Monopoly application.
|
|
||||||
* Handles connecting to and disconnecting from the server, and sending messages.
|
|
||||||
*/
|
|
||||||
public class NetworkSupport implements MessageListener<Client>, ClientStateListener, ServerConnection {
|
|
||||||
private static final Logger LOGGER = System.getLogger(NetworkSupport.class.getName());
|
|
||||||
private final MonopolyApp app;
|
|
||||||
private Client client;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a NetworkSupport instance for the given Monopoly application.
|
|
||||||
*
|
|
||||||
* @param app The Monopoly application instance.
|
|
||||||
*/
|
|
||||||
public NetworkSupport(MonopolyApp app) {
|
|
||||||
this.app = app;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the client connections Id
|
|
||||||
*
|
|
||||||
* @return the client id
|
|
||||||
*/
|
|
||||||
public int getId() {
|
|
||||||
if (client == null) return 0;
|
|
||||||
return client.getId();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the Monopoly application instance.
|
|
||||||
*
|
|
||||||
* @return Monopoly application instance
|
|
||||||
*/
|
|
||||||
public MonopolyApp getApp() {
|
|
||||||
return app;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if there is a connection to the game server.
|
|
||||||
*
|
|
||||||
* @return true if there is a connection to the game server, false otherwise.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean isConnected() {
|
|
||||||
return client != null && client.isConnected();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to join the game if there is no connection yet.
|
|
||||||
* Opens a dialog for the user to enter the host and port information.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void connect() {
|
|
||||||
if (client == null)
|
|
||||||
new CreateGameMenu(this).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the client connection.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void disconnect() {
|
|
||||||
if (client == null) return;
|
|
||||||
client.close();
|
|
||||||
client = null;
|
|
||||||
LOGGER.log(Level.INFO, "client closed"); //NON-NLS
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initializes the network connection.
|
|
||||||
*
|
|
||||||
* @param host The server's address.
|
|
||||||
* @param port The server's port.
|
|
||||||
* @throws IOException If an I/O error occurs when creating the client.
|
|
||||||
*/
|
|
||||||
public void initNetwork(String host, int port) throws IOException {
|
|
||||||
if (client != null)
|
|
||||||
throw new IllegalStateException("trying to join a game again");
|
|
||||||
client = Network.connectToServer(host, port);
|
|
||||||
client.start();
|
|
||||||
client.addMessageListener(this);
|
|
||||||
client.addClientStateListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a message is received from the server.
|
|
||||||
*
|
|
||||||
* @param client The client instance that received the message.
|
|
||||||
* @param message The message received from the server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void messageReceived(Client client, Message message) {
|
|
||||||
LOGGER.log(Level.INFO, "message received from server: {0}", message); //NON-NLS
|
|
||||||
if (message instanceof ServerMessage serverMessage)
|
|
||||||
app.enqueue(() -> serverMessage.accept(app.getGameLogic()));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the client has successfully connected to the server.
|
|
||||||
*
|
|
||||||
* @param client The client that connected to the server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void clientConnected(Client client) {
|
|
||||||
LOGGER.log(Level.INFO, "Client connected: {0}", client); //NON-NLS
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the client is disconnected from the server.
|
|
||||||
*
|
|
||||||
* @param client The client that was disconnected.
|
|
||||||
* @param disconnectInfo Information about the disconnection.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void clientDisconnected(Client client, DisconnectInfo disconnectInfo) {
|
|
||||||
LOGGER.log(Level.INFO, "Client {0} disconnected: {1}", client, disconnectInfo); //NON-NLS
|
|
||||||
if (this.client != client)
|
|
||||||
throw new IllegalArgumentException("parameter value must be client");
|
|
||||||
LOGGER.log(Level.INFO, "client still connected: {0}", client.isConnected()); //NON-NLS
|
|
||||||
this.client = null;
|
|
||||||
disconnect();
|
|
||||||
app.enqueue(() -> app.setInfoText(lookup("lost.connection.to.server")));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the specified message to the server.
|
|
||||||
*
|
|
||||||
* @param message The message to be sent to the server.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void send(ClientMessage message) {
|
|
||||||
LOGGER.log(Level.INFO, "sending {0}", message); //NON-NLS
|
|
||||||
if (client == null)
|
|
||||||
app.errorDialog(lookup("lost.connection.to.server"));
|
|
||||||
else
|
|
||||||
client.send(message);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,97 +0,0 @@
|
|||||||
package pp.monopoly.client;
|
|
||||||
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import pp.monopoly.client.gui.popups.AcceptTrade;
|
|
||||||
import pp.monopoly.client.gui.popups.BuildingPropertyCard;
|
|
||||||
import pp.monopoly.client.gui.popups.ConfirmTrade;
|
|
||||||
import pp.monopoly.client.gui.popups.EventCardPopup;
|
|
||||||
import pp.monopoly.client.gui.popups.FoodFieldCard;
|
|
||||||
import pp.monopoly.client.gui.popups.GateFieldCard;
|
|
||||||
import pp.monopoly.client.gui.popups.Gulag;
|
|
||||||
import pp.monopoly.client.gui.popups.GulagInfo;
|
|
||||||
import pp.monopoly.client.gui.popups.LooserPopUp;
|
|
||||||
import pp.monopoly.client.gui.popups.NoMoneyWarning;
|
|
||||||
import pp.monopoly.client.gui.popups.ReceivedRent;
|
|
||||||
import pp.monopoly.client.gui.popups.RejectTrade;
|
|
||||||
import pp.monopoly.client.gui.popups.Rent;
|
|
||||||
import pp.monopoly.client.gui.popups.TimeOut;
|
|
||||||
import pp.monopoly.client.gui.popups.WinnerPopUp;
|
|
||||||
import pp.monopoly.message.server.NotificationMessage;
|
|
||||||
import pp.monopoly.message.server.TradeReply;
|
|
||||||
import pp.monopoly.model.fields.BuildingProperty;
|
|
||||||
import pp.monopoly.model.fields.FoodField;
|
|
||||||
import pp.monopoly.model.fields.GateField;
|
|
||||||
import pp.monopoly.notification.EventCardEvent;
|
|
||||||
import pp.monopoly.notification.GameEventListener;
|
|
||||||
import pp.monopoly.notification.PopUpEvent;
|
|
||||||
|
|
||||||
public class PopUpManager implements GameEventListener {
|
|
||||||
|
|
||||||
private final MonopolyApp app;
|
|
||||||
|
|
||||||
public PopUpManager(MonopolyApp app) {
|
|
||||||
this.app = app;
|
|
||||||
app.getGameLogic().addListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(PopUpEvent event) {
|
|
||||||
if (event.msg().equals("Buy")) {
|
|
||||||
Timer timer = new Timer();
|
|
||||||
timer.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
app.enqueue(() -> {
|
|
||||||
int field = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
|
|
||||||
Object fieldObject = app.getGameLogic().getBoardManager().getFieldAtIndex(field);
|
|
||||||
|
|
||||||
if (fieldObject instanceof BuildingProperty) {
|
|
||||||
new BuildingPropertyCard(app).open();
|
|
||||||
} else if (fieldObject instanceof GateField) {
|
|
||||||
new GateFieldCard(app).open();
|
|
||||||
} else if (fieldObject instanceof FoodField) {
|
|
||||||
new FoodFieldCard(app).open();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 2500);
|
|
||||||
} else if (event.msg().equals("Winner")) {
|
|
||||||
new WinnerPopUp(app).open();
|
|
||||||
} else if (event.msg().equals("Looser")) {
|
|
||||||
new LooserPopUp(app).open();
|
|
||||||
} else if (event.msg().equals("timeout")) {
|
|
||||||
new TimeOut(app).open();
|
|
||||||
} else if (event.msg().equals("tradeRequest")) {
|
|
||||||
new ConfirmTrade(app).open();
|
|
||||||
} else if (event.msg().equals("goingToJail")) {
|
|
||||||
new Gulag(app).open();
|
|
||||||
} else if (event.msg().equals("NoMoneyWarning")) {
|
|
||||||
new NoMoneyWarning(app).open();
|
|
||||||
} else if(event.msg().equals("rent")) {
|
|
||||||
new Rent(app, ( (NotificationMessage) event.message()).getRentOwner(), ( (NotificationMessage) event.message()).getRentAmount() ).open();
|
|
||||||
} else if (event.msg().equals("jailtryagain")) {
|
|
||||||
new GulagInfo(app, 1).open();
|
|
||||||
} else if (event.msg().equals("jailpay")) {
|
|
||||||
new GulagInfo(app, 3).open();
|
|
||||||
} else if (event.msg().equals("tradepos")) {
|
|
||||||
new AcceptTrade(app, (TradeReply) event.message()).open();
|
|
||||||
} else if (event.msg().equals("tradeneg")) {
|
|
||||||
new RejectTrade(app, (TradeReply) event.message()).open();
|
|
||||||
} else if (event.msg().equals("ReceivedRent")) {
|
|
||||||
new ReceivedRent(app, ( (NotificationMessage) event.message()).getRentOwner(), ( (NotificationMessage) event.message()).getRentAmount() ).open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(EventCardEvent event) {
|
|
||||||
Timer timer = new Timer();
|
|
||||||
timer.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
app.enqueue(() -> new EventCardPopup(app, event.description()).open());
|
|
||||||
}
|
|
||||||
}, 2500);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,159 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import static com.jme3.material.Materials.LIGHTING;
|
|
||||||
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import com.jme3.material.Material;
|
|
||||||
import com.jme3.material.RenderState.BlendMode;
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
|
|
||||||
import com.jme3.scene.Geometry;
|
|
||||||
import com.jme3.scene.Node;
|
|
||||||
import com.jme3.scene.Spatial;
|
|
||||||
import com.jme3.scene.shape.Box;
|
|
||||||
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.monopoly.game.server.Player;
|
|
||||||
import pp.monopoly.model.Figure;
|
|
||||||
import pp.monopoly.model.Hotel;
|
|
||||||
import pp.monopoly.model.House;
|
|
||||||
import pp.monopoly.model.Item;
|
|
||||||
import pp.monopoly.notification.UpdatePlayerView;
|
|
||||||
|
|
||||||
public class BobTheBuilder extends GameBoardSynchronizer {
|
|
||||||
|
|
||||||
private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
|
|
||||||
private static final String COLOR = "Color"; //NON-NLS
|
|
||||||
private static final String FIGURE = "figure"; //NON-NLS
|
|
||||||
private static final String HOUSE = "house"; //NON-NLS
|
|
||||||
private static final String HOTEL = "hotel"; //NON-NLS
|
|
||||||
|
|
||||||
private final MonopolyApp app;
|
|
||||||
|
|
||||||
public BobTheBuilder(MonopolyApp app, Node root) {
|
|
||||||
super(app.getGameLogic().getBoard(), root);
|
|
||||||
this.app = app;
|
|
||||||
addExisting();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Spatial visit(Figure figure) {
|
|
||||||
final Node node = new Node(FIGURE);
|
|
||||||
node.attachChild(createFigure(figure));
|
|
||||||
|
|
||||||
// Setze die Position basierend auf der Feld-ID
|
|
||||||
node.setLocalTranslation(figure.getPos());
|
|
||||||
|
|
||||||
// Setze die Rotation basierend auf der Feld-ID
|
|
||||||
node.setLocalRotation(figure.getRot().toQuaternion());
|
|
||||||
// node.addControl(new FigureControl(figure));
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Spatial visit(Hotel hotel) {
|
|
||||||
final Node node = new Node(HOTEL);
|
|
||||||
node.attachChild(createHotel(hotel));
|
|
||||||
|
|
||||||
// Setze die Position basierend auf der Feld-ID
|
|
||||||
node.setLocalTranslation(hotel.getPos());
|
|
||||||
|
|
||||||
// Setze die Rotation basierend auf der Feld-ID
|
|
||||||
node.setLocalRotation(hotel.getRot().toQuaternion());
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Spatial visit(House house) {
|
|
||||||
final Node node = new Node(HOUSE);
|
|
||||||
node.attachChild(createHouse(house));
|
|
||||||
|
|
||||||
// Setze die Position basierend auf der Feld-ID
|
|
||||||
node.setLocalTranslation(house.getPos());
|
|
||||||
|
|
||||||
// Setze die Rotation basierend auf der Feld-ID
|
|
||||||
node.setLocalRotation(house.getAlignment());
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Spatial createFigure(Figure figure) {
|
|
||||||
// Lade das Modell
|
|
||||||
Spatial model = app.getAssetManager().loadModel("models/" + "Spielfiguren/" + figure.getType() + "/" + figure.getType() + ".j3o");
|
|
||||||
|
|
||||||
// Skaliere und positioniere das Modell
|
|
||||||
model.scale(0.5f);
|
|
||||||
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Spatial createHotel(Hotel hotel) {
|
|
||||||
Spatial model = app.getAssetManager().loadModel("models/Hotel/Hotel.j3o");
|
|
||||||
model.scale(0.2f);
|
|
||||||
model.setShadowMode(ShadowMode.CastAndReceive);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Spatial createHouse(House house) {
|
|
||||||
Spatial model = app.getAssetManager().loadModel("models/Haus/"+house.getStage()+"Haus.j3o");
|
|
||||||
model.scale(0.5f);
|
|
||||||
model.setShadowMode(ShadowMode.CastAndReceive);
|
|
||||||
return model;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a simple box to represent a battleship that is not of the "King George V" type.
|
|
||||||
*
|
|
||||||
* @param ship the battleship to be represented
|
|
||||||
* @return the geometry representing the battleship as a box
|
|
||||||
*/
|
|
||||||
private Spatial createBox(Item item) {
|
|
||||||
final Box box = new Box(3,
|
|
||||||
3f,
|
|
||||||
3);
|
|
||||||
final Geometry geometry = new Geometry(FIGURE, box);
|
|
||||||
geometry.setMaterial(createColoredMaterial(ColorRGBA.Blue));
|
|
||||||
geometry.setShadowMode(ShadowMode.CastAndReceive);
|
|
||||||
geometry.setLocalTranslation(0, 2, 0);
|
|
||||||
|
|
||||||
return geometry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new {@link Material} with the specified color.
|
|
||||||
* If the color includes transparency (i.e., alpha value less than 1),
|
|
||||||
* the material's render state is set to use alpha blending, allowing for
|
|
||||||
* semi-transparent rendering.
|
|
||||||
*
|
|
||||||
* @param color the {@link ColorRGBA} to be applied to the material. If the alpha value
|
|
||||||
* of the color is less than 1, the material will support transparency.
|
|
||||||
* @return a {@link Material} instance configured with the specified color and,
|
|
||||||
* if necessary, alpha blending enabled.
|
|
||||||
*/
|
|
||||||
private Material createColoredMaterial(ColorRGBA color) {
|
|
||||||
final Material material = new Material(app.getAssetManager(), UNSHADED);
|
|
||||||
if (color.getAlpha() < 1f)
|
|
||||||
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
|
|
||||||
material.setColor(COLOR, color);
|
|
||||||
return material;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(UpdatePlayerView event) {
|
|
||||||
board.removePlayers();
|
|
||||||
|
|
||||||
//TODO transition animation
|
|
||||||
for (Player player : app.getGameLogic().getPlayerHandler().getPlayers()) {
|
|
||||||
board.add(player.getFigure());
|
|
||||||
}
|
|
||||||
for (Item item : board.getItems().stream().filter(p -> p instanceof Figure).collect(Collectors.toList())) {
|
|
||||||
add(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,221 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.material.Material;
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.scene.Geometry;
|
|
||||||
import com.jme3.scene.shape.Quad;
|
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
import com.simsilica.lemur.*;
|
|
||||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
|
|
||||||
import com.simsilica.lemur.component.SpringGridLayout;
|
|
||||||
import com.simsilica.lemur.style.ElementId;
|
|
||||||
import pp.dialog.Dialog;
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.monopoly.client.gui.popups.BuyHouse;
|
|
||||||
import pp.monopoly.client.gui.popups.RepayMortage;
|
|
||||||
import pp.monopoly.client.gui.popups.SellHouse;
|
|
||||||
import pp.monopoly.client.gui.popups.TakeMortage;
|
|
||||||
import pp.monopoly.notification.Sound;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the building administration menu in the Monopoly application.
|
|
||||||
* <p>
|
|
||||||
* Provides options to manage properties, including building houses, demolishing houses,
|
|
||||||
* taking mortgages, repaying mortgages, and viewing an overview of owned properties.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class BuildingAdminMenu extends Dialog {
|
|
||||||
/** Reference to the Monopoly application instance. */
|
|
||||||
private final MonopolyApp app;
|
|
||||||
|
|
||||||
/** Main container for the menu's UI components. */
|
|
||||||
private final Container mainContainer;
|
|
||||||
|
|
||||||
/** Background geometry for the menu. */
|
|
||||||
private Geometry background;
|
|
||||||
|
|
||||||
/** Button to navigate back to the previous menu. */
|
|
||||||
private final Button backButton = new Button("Zurück");
|
|
||||||
|
|
||||||
/** Button to build houses on properties. */
|
|
||||||
private final Button buildButton = new Button("Bauen");
|
|
||||||
|
|
||||||
/** Button to demolish houses on properties. */
|
|
||||||
private final Button demolishButton = new Button("Abriss");
|
|
||||||
|
|
||||||
/** Button to take out a mortgage on properties. */
|
|
||||||
private final Button takeMortgageButton = new Button("Hypothek aufnehmen");
|
|
||||||
|
|
||||||
/** Button to repay a mortgage on properties. */
|
|
||||||
private final Button payMortgageButton = new Button("Hypothek bezahlen");
|
|
||||||
|
|
||||||
/** Button to open the property overview menu. */
|
|
||||||
private final Button overviewButton = new Button("Übersicht");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the building administration menu.
|
|
||||||
*
|
|
||||||
* @param app the Monopoly application instance
|
|
||||||
*/
|
|
||||||
public BuildingAdminMenu(MonopolyApp app) {
|
|
||||||
super(app.getDialogManager());
|
|
||||||
this.app = app;
|
|
||||||
|
|
||||||
// Background Image
|
|
||||||
addBackgroundImage();
|
|
||||||
|
|
||||||
// Main container for the UI components
|
|
||||||
mainContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
|
|
||||||
mainContainer.setPreferredSize(new Vector3f(800, 600, 0));
|
|
||||||
mainContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(1, 1, 1, 0.7f))); // Translucent white background
|
|
||||||
|
|
||||||
// Add header
|
|
||||||
mainContainer.addChild(createHeaderContainer());
|
|
||||||
// Add content
|
|
||||||
mainContainer.addChild(createContent());
|
|
||||||
// Attach main container to GUI node
|
|
||||||
app.getGuiNode().attachChild(mainContainer);
|
|
||||||
mainContainer.setLocalTranslation(
|
|
||||||
(app.getCamera().getWidth() - mainContainer.getPreferredSize().x) / 2,
|
|
||||||
(app.getCamera().getHeight() + mainContainer.getPreferredSize().y) / 2,
|
|
||||||
7
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the header container.
|
|
||||||
*
|
|
||||||
* @return The header container.
|
|
||||||
*/
|
|
||||||
private Container createHeaderContainer() {
|
|
||||||
Container headerContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
|
|
||||||
headerContainer.setPreferredSize(new Vector3f(800, 100, 0));
|
|
||||||
Label headerLabel = headerContainer.addChild(new Label("Grundstücke Verwalten", new ElementId("header")));
|
|
||||||
headerLabel.setFontSize(45);
|
|
||||||
headerLabel.setInsets(new Insets3f(10, 10, 10, 10));
|
|
||||||
return headerContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the main content container with columns for Overview, Build, and Mortgage.
|
|
||||||
*
|
|
||||||
* @return The content container.
|
|
||||||
*/
|
|
||||||
private Container createContent() {
|
|
||||||
Container contentContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
|
|
||||||
contentContainer.setPreferredSize(new Vector3f(800, 500, 0));
|
|
||||||
|
|
||||||
// Overview Column
|
|
||||||
Container overviewColumn = new Container(new SpringGridLayout(Axis.Y, Axis.X));
|
|
||||||
overviewColumn.addChild(new Label("Übersicht:")).setFontSize(30);
|
|
||||||
|
|
||||||
//Building Overview Button
|
|
||||||
overviewButton.setPreferredSize(new Vector3f(200, 50, 0));
|
|
||||||
overviewButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
new PropertyOverviewMenu(app).open();
|
|
||||||
}));
|
|
||||||
overviewColumn.addChild(overviewButton);
|
|
||||||
|
|
||||||
// Back Button
|
|
||||||
backButton.setPreferredSize(new Vector3f(200, 50, 0));
|
|
||||||
backButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
close();
|
|
||||||
}));
|
|
||||||
overviewColumn.addChild(backButton);
|
|
||||||
|
|
||||||
contentContainer.addChild(overviewColumn);
|
|
||||||
|
|
||||||
|
|
||||||
// Build Column
|
|
||||||
Container buildColumn = new Container(new SpringGridLayout(Axis.Y, Axis.X));
|
|
||||||
buildColumn.addChild(new Label("Bauen:")).setFontSize(30);
|
|
||||||
|
|
||||||
// Build Houses Button
|
|
||||||
buildButton.setPreferredSize(new Vector3f(200, 50, 0));
|
|
||||||
buildButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
new BuyHouse(app).open();
|
|
||||||
}));
|
|
||||||
buildColumn.addChild(buildButton);
|
|
||||||
|
|
||||||
//Demolish Houses Button
|
|
||||||
demolishButton.setPreferredSize(new Vector3f(200, 50, 0));
|
|
||||||
demolishButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
new SellHouse(app).open();
|
|
||||||
}));
|
|
||||||
buildColumn.addChild(demolishButton);
|
|
||||||
|
|
||||||
contentContainer.addChild(buildColumn);
|
|
||||||
|
|
||||||
|
|
||||||
// Mortgage Column
|
|
||||||
Container mortgageColumn = new Container(new SpringGridLayout(Axis.Y, Axis.X));
|
|
||||||
mortgageColumn.addChild(new Label("Hypotheken:")).setFontSize(30);
|
|
||||||
|
|
||||||
// Lend Mortgage Button
|
|
||||||
takeMortgageButton.setPreferredSize(new Vector3f(200, 50, 0));
|
|
||||||
takeMortgageButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
new TakeMortage(app).open();
|
|
||||||
}));
|
|
||||||
mortgageColumn.addChild(takeMortgageButton);
|
|
||||||
|
|
||||||
//Repay Mortgage Button
|
|
||||||
payMortgageButton.setPreferredSize(new Vector3f(200, 50, 0));
|
|
||||||
payMortgageButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
new RepayMortage(app).open();
|
|
||||||
}));
|
|
||||||
mortgageColumn.addChild(payMortgageButton);
|
|
||||||
|
|
||||||
contentContainer.addChild(mortgageColumn);
|
|
||||||
|
|
||||||
return contentContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a background image to the dialog.
|
|
||||||
*/
|
|
||||||
private void addBackgroundImage() {
|
|
||||||
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
|
|
||||||
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
|
|
||||||
background = new Geometry("Background", quad);
|
|
||||||
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
|
||||||
backgroundMaterial.setTexture("ColorMap", backgroundImage);
|
|
||||||
background.setMaterial(backgroundMaterial);
|
|
||||||
background.setLocalTranslation(0, 0, 6); // Position behind other GUI elements
|
|
||||||
app.getGuiNode().attachChild(background);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the building administration menu and detaches its elements from the GUI.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
app.getGuiNode().detachChild(mainContainer);
|
|
||||||
app.getGuiNode().detachChild(background);
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the settings menu when the escape key is pressed.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void escape() {
|
|
||||||
new SettingsMenu(app).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Periodic updates for the menu, if required.
|
|
||||||
*
|
|
||||||
* @param delta Time since the last update in seconds.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void update(float delta) {
|
|
||||||
// Periodic updates if necessary
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,66 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.renderer.Camera;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls the movement of the camera within the scene.
|
|
||||||
*/
|
|
||||||
public class CameraController {
|
|
||||||
private final Camera camera;
|
|
||||||
private final float height = 25; // Height of the camera above the game board
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for the CameraController.
|
|
||||||
*
|
|
||||||
* @param camera The camera to be controlled
|
|
||||||
*/
|
|
||||||
public CameraController(Camera camera) {
|
|
||||||
this.camera = camera;
|
|
||||||
setPosition(0);
|
|
||||||
camera.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the camera's position and orientation.
|
|
||||||
*
|
|
||||||
* @param tpf Time per frame
|
|
||||||
*/
|
|
||||||
public void update(float tpf) {
|
|
||||||
camera.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the camera's position based on the field ID.
|
|
||||||
*
|
|
||||||
* @param fieldID The ID of the field to which the camera should move
|
|
||||||
*/
|
|
||||||
public void setPosition(int fieldID) {
|
|
||||||
camera.setLocation(fieldIdToVector(fieldID));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the camera's position using specific coordinates.
|
|
||||||
*
|
|
||||||
* @param x The X-coordinate of the new camera position
|
|
||||||
* @param y The Y-coordinate of the new camera position
|
|
||||||
*/
|
|
||||||
public void setPosition(float x, float y) {
|
|
||||||
camera.setLocation(new Vector3f(x, height, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Maps a field ID to its corresponding position in the game world.
|
|
||||||
*
|
|
||||||
* @param fieldID The ID of the field
|
|
||||||
* @return The position of the field as a {@link Vector3f}
|
|
||||||
* @throws IllegalArgumentException If the field ID is invalid
|
|
||||||
*/
|
|
||||||
private Vector3f fieldIdToVector(int fieldID) {
|
|
||||||
if (fieldID <= 10) return new Vector3f(30, height, 0);
|
|
||||||
if (fieldID <= 20) return new Vector3f(0, height, 30);
|
|
||||||
if (fieldID <= 30) return new Vector3f(-30, height, 0);
|
|
||||||
if (fieldID <= 40) return new Vector3f(0, height, -30);
|
|
||||||
else throw new IllegalArgumentException();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,60 +0,0 @@
|
|||||||
//package pp.monopoly.client.gui;
|
|
||||||
//
|
|
||||||
//import com.jme3.input.InputManager;
|
|
||||||
//import com.jme3.input.KeyInput;
|
|
||||||
//import com.jme3.input.controls.ActionListener;
|
|
||||||
//import com.jme3.input.controls.KeyTrigger;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Handhabt die Eingaben für die Kamera.
|
|
||||||
// */
|
|
||||||
//public class CameraInputHandler {
|
|
||||||
//
|
|
||||||
// private CameraController cameraController; // Kamera-Controller
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Konstruktor für den CameraInputHandler.
|
|
||||||
// *
|
|
||||||
// * @param cameraController Der Kamera-Controller, der gesteuert werden soll.
|
|
||||||
// * @param inputManager Der InputManager, um Eingaben zu registrieren.
|
|
||||||
// */
|
|
||||||
// public CameraInputHandler(CameraController cameraController, InputManager inputManager) {
|
|
||||||
// if (cameraController == null || inputManager == null) {
|
|
||||||
// throw new IllegalArgumentException("CameraController und InputManager dürfen nicht null sein");
|
|
||||||
// }
|
|
||||||
// this.cameraController = cameraController;
|
|
||||||
//
|
|
||||||
// // Mappings für Kamerasteuerung
|
|
||||||
// inputManager.addMapping("FocusCurrentPlayer", new KeyTrigger(KeyInput.KEY_1)); // Modus 1
|
|
||||||
// inputManager.addMapping("FocusSelf", new KeyTrigger(KeyInput.KEY_2)); // Modus 2
|
|
||||||
// inputManager.addMapping("FreeCam", new KeyTrigger(KeyInput.KEY_3)); // Modus 3
|
|
||||||
//
|
|
||||||
// // Listener für die Kameramodi
|
|
||||||
// inputManager.addListener(actionListener, "FocusCurrentPlayer", "FocusSelf", "FreeCam");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * ActionListener für die Kamerasteuerung.
|
|
||||||
// */
|
|
||||||
// private final ActionListener actionListener = (name, isPressed, tpf) -> {
|
|
||||||
// if (!isPressed) return;
|
|
||||||
//
|
|
||||||
// // Umschalten der Kamera-Modi basierend auf der Eingabe
|
|
||||||
// switch (name) {
|
|
||||||
// case "FocusCurrentPlayer" -> {
|
|
||||||
// cameraController.setMode(CameraController.CameraMode.FOCUS_CURRENT_PLAYER);
|
|
||||||
// System.out.println("Kameramodus: Fokus auf aktuellen Spieler");
|
|
||||||
// }
|
|
||||||
// case "FocusSelf" -> {
|
|
||||||
// cameraController.setMode(CameraController.CameraMode.FOCUS_SELF);
|
|
||||||
// System.out.println("Kameramodus: Fokus auf eigene Figur");
|
|
||||||
// }
|
|
||||||
// case "FreeCam" -> {
|
|
||||||
// cameraController.setMode(CameraController.CameraMode.FREECAM);
|
|
||||||
// System.out.println("Kameramodus: Freie Kamera");
|
|
||||||
// }
|
|
||||||
// default -> System.err.println("Unbekannter Kameramodus: " + name);
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
//}
|
|
||||||
//
|
|
@@ -1,224 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import com.jme3.material.Material;
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.scene.Geometry;
|
|
||||||
import com.jme3.scene.shape.Quad;
|
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
import com.simsilica.lemur.*;
|
|
||||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
|
|
||||||
import com.simsilica.lemur.component.SpringGridLayout;
|
|
||||||
import com.simsilica.lemur.core.VersionedList;
|
|
||||||
import com.simsilica.lemur.core.VersionedReference;
|
|
||||||
import com.simsilica.lemur.style.ElementId;
|
|
||||||
|
|
||||||
import pp.dialog.Dialog;
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.monopoly.game.server.Player;
|
|
||||||
import pp.monopoly.message.client.ViewAssetsRequest;
|
|
||||||
import pp.monopoly.model.TradeHandler;
|
|
||||||
import pp.monopoly.notification.Sound;
|
|
||||||
|
|
||||||
public class ChoosePartner extends Dialog {
|
|
||||||
private final MonopolyApp app;
|
|
||||||
private Selector<String> playerSelector;
|
|
||||||
private final Button cancelButton = new Button("Abbrechen");
|
|
||||||
private final Button confirmButton = new Button("Bestätigen");
|
|
||||||
private final Container mainContainer;
|
|
||||||
private Container lowerLeftMenu;
|
|
||||||
private Container lowerRightMenu;
|
|
||||||
private Geometry background;
|
|
||||||
private TradeHandler tradeHandler;
|
|
||||||
private VersionedReference<Set<Integer>> selectionRef; // Reference to track selector changes
|
|
||||||
private String lastSelected = ""; // To keep track of the last selected value
|
|
||||||
|
|
||||||
QuadBackgroundComponent translucentWhiteBackground =
|
|
||||||
new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the ChoosePartner dialog.
|
|
||||||
*
|
|
||||||
* @param app The Monopoly application instance.
|
|
||||||
*/
|
|
||||||
public ChoosePartner(MonopolyApp app) {
|
|
||||||
super(app.getDialogManager());
|
|
||||||
this.app = app;
|
|
||||||
tradeHandler = new TradeHandler(app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()));
|
|
||||||
app.getGameLogic().send(new ViewAssetsRequest());
|
|
||||||
|
|
||||||
// Background Image
|
|
||||||
addBackgroundImage();
|
|
||||||
|
|
||||||
// Main container for the UI components
|
|
||||||
mainContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
|
|
||||||
mainContainer.setPreferredSize(new Vector3f(1000, 600, 0));
|
|
||||||
mainContainer.setBackground(translucentWhiteBackground);
|
|
||||||
|
|
||||||
// Add title with background
|
|
||||||
Label headerLabel = mainContainer.addChild(new Label("Wähle deinen Handelspartner:", new ElementId("label-Bold")));
|
|
||||||
headerLabel.setFontSize(40);
|
|
||||||
headerLabel.setBackground(new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f)));
|
|
||||||
|
|
||||||
// Dropdown for player selection
|
|
||||||
mainContainer.addChild(createDropdown());
|
|
||||||
|
|
||||||
// Add buttons
|
|
||||||
mainContainer.addChild(createButtonContainer());
|
|
||||||
|
|
||||||
// Attach main container to GUI node
|
|
||||||
app.getGuiNode().attachChild(mainContainer);
|
|
||||||
mainContainer.setLocalTranslation(
|
|
||||||
(app.getCamera().getWidth() - mainContainer.getPreferredSize().x) / 2,
|
|
||||||
(app.getCamera().getHeight() + mainContainer.getPreferredSize().y) / 2,
|
|
||||||
4
|
|
||||||
);
|
|
||||||
|
|
||||||
// Initialize selection reference for tracking changes
|
|
||||||
selectionRef = playerSelector.getSelectionModel().createReference();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the dropdown menu for selecting a partner.
|
|
||||||
*
|
|
||||||
* @return The dropdown container.
|
|
||||||
*/
|
|
||||||
private Container createDropdown() {
|
|
||||||
Container dropdownContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
|
|
||||||
dropdownContainer.setPreferredSize(new Vector3f(100, 80, 0));
|
|
||||||
dropdownContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(ColorRGBA.Black)));
|
|
||||||
|
|
||||||
VersionedList<String> playerOptions = new VersionedList<>();
|
|
||||||
|
|
||||||
for (Player player : app.getGameLogic().getPlayerHandler().getPlayers()) {
|
|
||||||
if (player.getId() != app.getId()) {
|
|
||||||
playerOptions.add(player.getName() + " (ID: " + player.getId() + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
playerSelector = new Selector<>(playerOptions, "glass");
|
|
||||||
dropdownContainer.addChild(playerSelector);
|
|
||||||
Vector3f dimens = dropdownContainer.getPreferredSize();
|
|
||||||
Vector3f dimens2 = playerSelector.getPopupContainer().getPreferredSize();
|
|
||||||
dimens2.setX(dimens.getX());
|
|
||||||
playerSelector.getPopupContainer().setPreferredSize(new Vector3f(200, 200, 3));
|
|
||||||
playerSelector.setLocalTranslation(0, 0, 5);
|
|
||||||
|
|
||||||
// Set initial selection
|
|
||||||
if (!playerOptions.isEmpty()) {
|
|
||||||
onDropdownSelectionChanged(playerOptions.get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
return dropdownContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the button container with cancel and confirm buttons.
|
|
||||||
*
|
|
||||||
* @return The button container.
|
|
||||||
*/
|
|
||||||
private Container createButtonContainer() {
|
|
||||||
Container buttonContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
|
|
||||||
buttonContainer.setBackground(translucentWhiteBackground);
|
|
||||||
|
|
||||||
// "Abbrechen" button
|
|
||||||
lowerLeftMenu = new Container();
|
|
||||||
cancelButton.setPreferredSize(new Vector3f(200, 60, 0));
|
|
||||||
cancelButton.setFontSize(30);
|
|
||||||
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
close();
|
|
||||||
}));
|
|
||||||
lowerLeftMenu.addChild(cancelButton);
|
|
||||||
|
|
||||||
// Position the container near the bottom-left corner
|
|
||||||
lowerLeftMenu.setLocalTranslation(new Vector3f(120, 170, 5)); // Adjust X and Y to align with the bottom-left corner
|
|
||||||
app.getGuiNode().attachChild(lowerLeftMenu);
|
|
||||||
|
|
||||||
// "Bestätigen" button
|
|
||||||
lowerRightMenu = new Container();
|
|
||||||
confirmButton.setPreferredSize(new Vector3f(200, 60, 0));
|
|
||||||
confirmButton.setFontSize(30);
|
|
||||||
confirmButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
close();
|
|
||||||
new TradeMenu(app, tradeHandler).open();
|
|
||||||
}));
|
|
||||||
lowerRightMenu.addChild(confirmButton);
|
|
||||||
|
|
||||||
// Position the container near the bottom-right corner
|
|
||||||
lowerRightMenu.setLocalTranslation(new Vector3f(app.getCamera().getWidth() - 320, 170, 5)); // X: 220px from the right, Y: 50px above the bottom
|
|
||||||
app.getGuiNode().attachChild(lowerRightMenu);
|
|
||||||
|
|
||||||
return buttonContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a background image to the dialog.
|
|
||||||
*/
|
|
||||||
private void addBackgroundImage() {
|
|
||||||
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
|
|
||||||
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
|
|
||||||
background = new Geometry("Background", quad);
|
|
||||||
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
|
||||||
backgroundMaterial.setTexture("ColorMap", backgroundImage);
|
|
||||||
background.setMaterial(backgroundMaterial);
|
|
||||||
background.setLocalTranslation(0, 0, 3); // Position behind other GUI elements
|
|
||||||
app.getGuiNode().attachChild(background);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the escape action for the dialog.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void escape() {
|
|
||||||
new SettingsMenu(app).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the dialog periodically, called by the dialog manager.
|
|
||||||
*
|
|
||||||
* @param delta The time elapsed since the last update.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void update(float delta) {
|
|
||||||
// Check if the selection has changed
|
|
||||||
if (selectionRef.update()) {
|
|
||||||
String selected = playerSelector.getSelectedItem();
|
|
||||||
if (!selected.equals(lastSelected)) {
|
|
||||||
lastSelected = selected;
|
|
||||||
onDropdownSelectionChanged(selected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
app.getGuiNode().detachChild(playerSelector);
|
|
||||||
app.getGuiNode().detachChild(lowerLeftMenu);
|
|
||||||
app.getGuiNode().detachChild(lowerRightMenu);
|
|
||||||
app.getGuiNode().detachChild(mainContainer);
|
|
||||||
app.getGuiNode().detachChild(background);
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback for when the dropdown selection changes.
|
|
||||||
*/
|
|
||||||
private void onDropdownSelectionChanged(String selected) {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
int idStart = selected.indexOf("(ID: ") + 5; // Find start of the ID
|
|
||||||
int idEnd = selected.indexOf(")", idStart); // Find end of the ID
|
|
||||||
String idStr = selected.substring(idStart, idEnd); // Extract the ID as a string
|
|
||||||
int playerId = Integer.parseInt(idStr); // Convert the ID to an integer
|
|
||||||
|
|
||||||
// Find the player by ID
|
|
||||||
Player selectedPlayer = app.getGameLogic().getPlayerHandler().getPlayerById(playerId);
|
|
||||||
|
|
||||||
if (selectedPlayer != null) {
|
|
||||||
tradeHandler.setReceiver(selectedPlayer); // Set the receiver in TradeHandler
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,226 +0,0 @@
|
|||||||
////////////////////////////////////////
|
|
||||||
// Programming project code
|
|
||||||
// UniBw M, 2022, 2023, 2024
|
|
||||||
// www.unibw.de/inf2
|
|
||||||
// (c) Mark Minas (mark.minas@unibw.de)
|
|
||||||
////////////////////////////////////////
|
|
||||||
|
|
||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import java.lang.System.Logger;
|
|
||||||
import java.lang.System.Logger.Level;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
import com.jme3.material.Material;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.scene.Geometry;
|
|
||||||
import com.jme3.scene.shape.Quad;
|
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
import com.simsilica.lemur.Button;
|
|
||||||
import com.simsilica.lemur.Container;
|
|
||||||
import com.simsilica.lemur.Label;
|
|
||||||
import com.simsilica.lemur.TextField;
|
|
||||||
import com.simsilica.lemur.component.SpringGridLayout;
|
|
||||||
|
|
||||||
import static pp.monopoly.Resources.lookup;
|
|
||||||
|
|
||||||
import com.simsilica.lemur.style.ElementId;
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.monopoly.client.NetworkSupport;
|
|
||||||
import pp.monopoly.notification.Sound;
|
|
||||||
import pp.monopoly.server.MonopolyServer;
|
|
||||||
import pp.dialog.Dialog;
|
|
||||||
import pp.dialog.DialogBuilder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a dialog for setting up a network connection in the Battleship game.
|
|
||||||
* Allows users to specify the host and port for connecting to a game server.
|
|
||||||
*/
|
|
||||||
public class CreateGameMenu extends Dialog {
|
|
||||||
private static final Logger LOGGER = System.getLogger(CreateGameMenu.class.getName());
|
|
||||||
private static final String LOCALHOST = "localhost"; //NON-NLS
|
|
||||||
private static final String DEFAULT_PORT = "42069"; //NON-NLS
|
|
||||||
private final NetworkSupport network;
|
|
||||||
private final TextField host = new TextField(LOCALHOST);
|
|
||||||
private final TextField port = new TextField(DEFAULT_PORT);
|
|
||||||
private final Button serverButton = new Button("Selber hosten");
|
|
||||||
private final Button cancelButton = new Button("Abbrechen");
|
|
||||||
private final Button joinButton = new Button("Beitreten");
|
|
||||||
private String hostname;
|
|
||||||
private int portNumber;
|
|
||||||
private Future<Object> connectionFuture;
|
|
||||||
private Dialog progressDialog;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new CreateGameMenu.
|
|
||||||
*
|
|
||||||
* @param network The NetworkSupport instance to be used for network operations.
|
|
||||||
*/
|
|
||||||
public CreateGameMenu(NetworkSupport network) {
|
|
||||||
super(network.getApp().getDialogManager());
|
|
||||||
this.network = network;
|
|
||||||
host.setSingleLine(true);
|
|
||||||
host.setPreferredWidth(400f);
|
|
||||||
port.setSingleLine(true);
|
|
||||||
|
|
||||||
final MonopolyApp app = network.getApp();
|
|
||||||
|
|
||||||
int screenWidth = app.getContext().getSettings().getWidth();
|
|
||||||
int screenHeight = app.getContext().getSettings().getHeight();
|
|
||||||
|
|
||||||
// Set up the background image
|
|
||||||
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
|
|
||||||
Quad quad = new Quad(screenWidth, screenHeight);
|
|
||||||
Geometry background = new Geometry("Background", quad);
|
|
||||||
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
|
||||||
backgroundMaterial.setTexture("ColorMap", backgroundImage);
|
|
||||||
background.setMaterial(backgroundMaterial);
|
|
||||||
background.setLocalTranslation(0, 0, -1); // Ensure it is behind other GUI elements
|
|
||||||
app.getGuiNode().attachChild(background);
|
|
||||||
|
|
||||||
addChild(new Label("Spiel erstellen", new ElementId("header"))); //NON-NLS
|
|
||||||
final Container input = new Container(new SpringGridLayout());
|
|
||||||
input.addChild(new Label(lookup("host.name") + ": "));
|
|
||||||
input.addChild(host, 1);
|
|
||||||
input.addChild(new Label(lookup("port.number") + ": "));
|
|
||||||
input.addChild(port, 1);
|
|
||||||
|
|
||||||
addChild(input);
|
|
||||||
// "Abbrechen"-Button
|
|
||||||
cancelButton.setPreferredSize(new Vector3f(120, 40, 0));
|
|
||||||
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
this.close();
|
|
||||||
new StartMenu(network.getApp()).open();
|
|
||||||
}));
|
|
||||||
addChild(cancelButton);
|
|
||||||
|
|
||||||
// "Selber hosten"-Button
|
|
||||||
serverButton.addClickCommands(s -> ifTopDialog( () -> {
|
|
||||||
network.getApp().getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
startServerInThread();
|
|
||||||
} ));
|
|
||||||
addChild(serverButton);
|
|
||||||
|
|
||||||
// "Beitreten"-Button
|
|
||||||
joinButton.setPreferredSize(new Vector3f(120, 40, 0));
|
|
||||||
joinButton.addClickCommands(s -> ifTopDialog( () -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
connect();
|
|
||||||
}));
|
|
||||||
addChild(joinButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the action for the connect button in the connection dialog.
|
|
||||||
* Tries to parse the port number and initiate connection to the server.
|
|
||||||
*/
|
|
||||||
private void connect() {
|
|
||||||
LOGGER.log(Level.INFO, "connect to host={0}, port={1}", host, port); //NON-NLS
|
|
||||||
try {
|
|
||||||
hostname = host.getText().trim().isEmpty() ? LOCALHOST : host.getText();
|
|
||||||
portNumber = Integer.parseInt(port.getText());
|
|
||||||
openProgressDialog();
|
|
||||||
connectionFuture = network.getApp().getExecutor().submit(this::initNetwork);
|
|
||||||
}
|
|
||||||
catch (NumberFormatException e) {
|
|
||||||
network.getApp().errorDialog(lookup("port.must.be.integer"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a dialog indicating that the connection is in progress.
|
|
||||||
*/
|
|
||||||
private void openProgressDialog() {
|
|
||||||
progressDialog = DialogBuilder.simple(network.getApp().getDialogManager())
|
|
||||||
.setText(lookup("label.connecting"))
|
|
||||||
.build();
|
|
||||||
progressDialog.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to initialize the network connection.
|
|
||||||
*
|
|
||||||
* @throws RuntimeException If an error occurs when creating the client.
|
|
||||||
*/
|
|
||||||
private Object initNetwork() {
|
|
||||||
try {
|
|
||||||
network.initNetwork(hostname, portNumber);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void escape() {
|
|
||||||
new SettingsMenu(network.getApp()).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called by {@linkplain pp.dialog.DialogManager#update(float)} for periodically
|
|
||||||
* updating this dialog. T
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void update(float delta) {
|
|
||||||
if (connectionFuture != null && connectionFuture.isDone())
|
|
||||||
try {
|
|
||||||
connectionFuture.get();
|
|
||||||
success();
|
|
||||||
}
|
|
||||||
catch (ExecutionException e) {
|
|
||||||
failure(e.getCause());
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Interrupted!", e); //NON-NLS
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a successful connection to the game server.
|
|
||||||
*/
|
|
||||||
private void success() {
|
|
||||||
connectionFuture = null;
|
|
||||||
progressDialog.close();
|
|
||||||
this.close();
|
|
||||||
new LobbyMenu(network.getApp()).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles a failed connection attempt.
|
|
||||||
*
|
|
||||||
* @param e The cause of the failure.
|
|
||||||
*/
|
|
||||||
private void failure(Throwable e) {
|
|
||||||
connectionFuture = null;
|
|
||||||
progressDialog.close();
|
|
||||||
network.getApp().errorDialog(lookup("server.connection.failed"));
|
|
||||||
network.getApp().setInfoText(e.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts the server in a separate thread.
|
|
||||||
*/
|
|
||||||
private void startServerInThread() {
|
|
||||||
serverButton.setEnabled(false);
|
|
||||||
Thread serverThread = new Thread(() -> {
|
|
||||||
try {
|
|
||||||
MonopolyServer.main(null);
|
|
||||||
} catch (Exception e) {
|
|
||||||
serverButton.setEnabled(true);
|
|
||||||
LOGGER.log(Level.ERROR, "Server could not be started", e);
|
|
||||||
network.getApp().errorDialog("Could not start server: " + e.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
serverThread.start();
|
|
||||||
try {
|
|
||||||
Thread.sleep(2000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
connect();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,87 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.scene.Node;
|
|
||||||
import com.jme3.scene.Spatial;
|
|
||||||
import pp.monopoly.model.Item;
|
|
||||||
import pp.monopoly.model.Visitor;
|
|
||||||
import pp.monopoly.notification.DiceRollEvent;
|
|
||||||
import pp.monopoly.notification.GameEventListener;
|
|
||||||
import pp.monopoly.notification.ItemAddedEvent;
|
|
||||||
import pp.monopoly.notification.ItemRemovedEvent;
|
|
||||||
import pp.monopoly.notification.UpdatePlayerView;
|
|
||||||
import pp.monopoly.model.Board;
|
|
||||||
import pp.monopoly.model.Figure;
|
|
||||||
import pp.view.ModelViewSynchronizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract base class for synchronizing the visual representation of a {@link Board} with its model state.
|
|
||||||
* This class handles the addition and removal of items from the board, ensuring that changes in the model
|
|
||||||
* are accurately reflected in the view.
|
|
||||||
* <p>
|
|
||||||
* Subclasses are responsible for providing the specific implementation of how each item in the map
|
|
||||||
* is represented visually by implementing the {@link Visitor} interface.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
abstract class GameBoardSynchronizer extends ModelViewSynchronizer<Item> implements Visitor<Spatial>, GameEventListener {
|
|
||||||
// The board that this synchronizer is responsible for
|
|
||||||
protected final Board board;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new GameBoardSynchronizer.
|
|
||||||
* Initializes the synchronizer with the provided board and the root node for attaching view representations.
|
|
||||||
*
|
|
||||||
* @param map the board to be synchronized
|
|
||||||
* @param root the root node to which the view representations of the board items are attached
|
|
||||||
*/
|
|
||||||
protected GameBoardSynchronizer(Board board, Node root) {
|
|
||||||
super(root);
|
|
||||||
this.board = board;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translates a model item into its corresponding visual representation.
|
|
||||||
* The specific visual representation is determined by the concrete implementation of the {@link Visitor} interface.
|
|
||||||
*
|
|
||||||
* @param item the item from the model to be translated
|
|
||||||
* @return the visual representation of the item as a {@link Spatial}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected Spatial translate(Item item) {
|
|
||||||
return item.accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the existing items from the board to the view.
|
|
||||||
* This method should be called during initialization to ensure that all current items in the board
|
|
||||||
* are visually represented.
|
|
||||||
*/
|
|
||||||
protected void addExisting() {
|
|
||||||
board.getItems().forEach(this::add);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the event when an item is removed from the ship map.
|
|
||||||
* Removes the visual representation of the item from the view if it belongs to the synchronized ship map.
|
|
||||||
*
|
|
||||||
* @param event the event indicating that an item has been removed from the ship map
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(ItemRemovedEvent event) {
|
|
||||||
if (board == event.board())
|
|
||||||
delete(event.item());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles the event when an item is added to the ship map.
|
|
||||||
* Adds the visual representation of the new item to the view if it belongs to the synchronized ship map.
|
|
||||||
*
|
|
||||||
* @param event the event indicating that an item has been added to the ship map
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(ItemAddedEvent event) {
|
|
||||||
if (board == event.board()){
|
|
||||||
add(event.item());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,39 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
import com.simsilica.lemur.Button;
|
|
||||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
|
|
||||||
import com.simsilica.lemur.style.ElementId;
|
|
||||||
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
|
|
||||||
public class ImageButton extends Button {
|
|
||||||
|
|
||||||
private final String file;
|
|
||||||
private static MonopolyApp app;
|
|
||||||
|
|
||||||
public ImageButton( String s, String file, MonopolyApp app ) {
|
|
||||||
this(s, true, new ElementId(ELEMENT_ID), null, file, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageButton( String s, String style, String file, MonopolyApp app ) {
|
|
||||||
this(s, true, new ElementId(ELEMENT_ID), style, file, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageButton( String s, ElementId elementId, String file, MonopolyApp app ) {
|
|
||||||
this(s, true, elementId, null, file, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ImageButton( String s, ElementId elementId, String style, String file, MonopolyApp app ) {
|
|
||||||
this(s, true, elementId, style, file, app);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ImageButton( String s, boolean applyStyles, ElementId elementId, String style, String file, MonopolyApp app ) {
|
|
||||||
super(s, false, elementId, style);
|
|
||||||
this.file = file;
|
|
||||||
ImageButton.app = app;
|
|
||||||
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/Buttons/"+file+".png");
|
|
||||||
setBackground(new QuadBackgroundComponent(backgroundImage));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -1,321 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.material.Material;
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.scene.Geometry;
|
|
||||||
import com.jme3.scene.shape.Quad;
|
|
||||||
import com.jme3.scene.shape.Sphere;
|
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
import com.simsilica.lemur.Axis;
|
|
||||||
import com.simsilica.lemur.Button;
|
|
||||||
import com.simsilica.lemur.Container;
|
|
||||||
import com.simsilica.lemur.Insets3f;
|
|
||||||
import com.simsilica.lemur.Label;
|
|
||||||
import com.simsilica.lemur.Selector;
|
|
||||||
import com.simsilica.lemur.TextField;
|
|
||||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
|
|
||||||
import com.simsilica.lemur.component.SpringGridLayout;
|
|
||||||
|
|
||||||
import com.simsilica.lemur.core.VersionedList;
|
|
||||||
import com.simsilica.lemur.core.VersionedReference;
|
|
||||||
import com.simsilica.lemur.style.ElementId;
|
|
||||||
|
|
||||||
import pp.dialog.Dialog;
|
|
||||||
import pp.monopoly.client.GameMusic;
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.monopoly.game.server.Player;
|
|
||||||
import pp.monopoly.message.client.PlayerReady;
|
|
||||||
import pp.monopoly.notification.Sound;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the lobby menu in the Monopoly application.
|
|
||||||
* <p>
|
|
||||||
* Provides functionality for player configuration, including input for starting capital,
|
|
||||||
* player name, and figure selection, as well as options to ready up or exit the game.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class LobbyMenu extends Dialog {
|
|
||||||
|
|
||||||
/** Reference to the Monopoly application instance. */
|
|
||||||
private final MonopolyApp app;
|
|
||||||
|
|
||||||
/** Main container for the lobby menu UI. */
|
|
||||||
private final Container menuContainer;
|
|
||||||
|
|
||||||
/** Background geometry for the menu. */
|
|
||||||
private Geometry background;
|
|
||||||
|
|
||||||
/** Colored circle displayed between input fields and dropdown menus. */
|
|
||||||
private Geometry circle;
|
|
||||||
|
|
||||||
/** Container for the lower-left section of the menu. */
|
|
||||||
private Container lowerLeftMenu;
|
|
||||||
|
|
||||||
/** Container for the lower-right section of the menu. */
|
|
||||||
private Container lowerRightMenu;
|
|
||||||
|
|
||||||
/** Text field for entering the player's name. */
|
|
||||||
private TextField playerInputField;
|
|
||||||
|
|
||||||
/** Text field for entering the starting capital. */
|
|
||||||
private TextField startingCapital = new TextField("15000");
|
|
||||||
|
|
||||||
/** Selected player figure. */
|
|
||||||
private String figure;
|
|
||||||
|
|
||||||
private VersionedReference<Set<Integer>> selectionRef;
|
|
||||||
private Selector<String> figureDropdown;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the lobby menu for player configuration.
|
|
||||||
*
|
|
||||||
* @param app the Monopoly application instance
|
|
||||||
*/
|
|
||||||
public LobbyMenu(MonopolyApp app) {
|
|
||||||
super(app.getDialogManager());
|
|
||||||
this.app = app;
|
|
||||||
|
|
||||||
GameMusic music = app.getStateManager().getState(GameMusic.class);
|
|
||||||
music.toggleMusic();
|
|
||||||
|
|
||||||
playerInputField = new TextField("Spieler "+(app.getId()+1));
|
|
||||||
// Hintergrundbild laden und hinzufügen
|
|
||||||
addBackgroundImage();
|
|
||||||
|
|
||||||
QuadBackgroundComponent translucentWhiteBackground =
|
|
||||||
new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f));
|
|
||||||
|
|
||||||
menuContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
|
|
||||||
menuContainer.setPreferredSize(new Vector3f(1000, 600, 0)); // Fixed size of the container
|
|
||||||
menuContainer.setBackground(translucentWhiteBackground);
|
|
||||||
|
|
||||||
// Create a smaller horizontal container for the label, input field, and spacers
|
|
||||||
Container horizontalContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
|
|
||||||
horizontalContainer.setPreferredSize(new Vector3f(600, 40, 0)); // Adjust container size
|
|
||||||
horizontalContainer.setBackground(null);
|
|
||||||
|
|
||||||
Label title = horizontalContainer.addChild(new Label("Startkapital:", new ElementId("label-Bold")));
|
|
||||||
title.setFontSize(40);
|
|
||||||
|
|
||||||
// Add a spacer between the title and the input field
|
|
||||||
Label spacerBeforeInput = horizontalContainer.addChild(new Label("")); // Invisible spacer
|
|
||||||
spacerBeforeInput.setPreferredSize(new Vector3f(20, 1, 0)); // Width of the spacer
|
|
||||||
|
|
||||||
// Add an input field (TextField)
|
|
||||||
horizontalContainer.addChild(startingCapital);
|
|
||||||
startingCapital.setPreferredWidth(100); // Set the width of the input field
|
|
||||||
startingCapital.setPreferredSize(new Vector3f(150, 50, 0));
|
|
||||||
startingCapital.setInsets(new Insets3f(5, 10, 5, 10)); // Add padding around the text inside the field
|
|
||||||
|
|
||||||
// Add a spacer after the input field
|
|
||||||
Label spacerAfterInput = horizontalContainer.addChild(new Label("")); // Invisible spacer
|
|
||||||
spacerAfterInput.setPreferredSize(new Vector3f(20, 1, 0)); // Width of the spacer
|
|
||||||
|
|
||||||
menuContainer.setLocalTranslation(
|
|
||||||
(app.getCamera().getWidth() - menuContainer.getPreferredSize().x) / 2,
|
|
||||||
(app.getCamera().getHeight() + menuContainer.getPreferredSize().y) / 2,
|
|
||||||
1
|
|
||||||
);
|
|
||||||
app.getGuiNode().attachChild(menuContainer);
|
|
||||||
|
|
||||||
// Dropdowns and Labels
|
|
||||||
Container dropdownContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
|
|
||||||
dropdownContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(ColorRGBA.Black)));
|
|
||||||
dropdownContainer.setPreferredSize(new Vector3f(800, 200, 0));
|
|
||||||
dropdownContainer.setBackground(null);
|
|
||||||
dropdownContainer.setInsets(new Insets3f(10, 0, 0, 0));
|
|
||||||
// Player Input Field
|
|
||||||
Container playerInputContainer = dropdownContainer.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X)));
|
|
||||||
playerInputContainer.addChild(new Label("Spieler:"));
|
|
||||||
playerInputContainer.setBackground(null);
|
|
||||||
|
|
||||||
|
|
||||||
playerInputField.setPreferredSize(new Vector3f(100, 20, 0));
|
|
||||||
playerInputField.setInsets(new Insets3f(5, 10, 5, 10)); // Add padding for the text inside the field
|
|
||||||
playerInputField.setBackground(new QuadBackgroundComponent(ColorRGBA.Black));
|
|
||||||
playerInputContainer.addChild(playerInputField);
|
|
||||||
// Spacer (Center Circle Area)
|
|
||||||
Label spacer = dropdownContainer.addChild(new Label(""));
|
|
||||||
spacer.setPreferredSize(new Vector3f(200, 200, 0)); // Adjust this to fit the center graphic
|
|
||||||
|
|
||||||
// Figur Dropdown
|
|
||||||
Container figureDropdownContainer = dropdownContainer.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X)));
|
|
||||||
figureDropdownContainer.addChild(new Label("Figur:"));
|
|
||||||
figureDropdownContainer.setBackground(null);
|
|
||||||
|
|
||||||
VersionedList<String> figures = new VersionedList<>();
|
|
||||||
figures.add("Computer");
|
|
||||||
figures.add("Flugzeug");
|
|
||||||
figures.add("Jägermeister");
|
|
||||||
figures.add("Katze");
|
|
||||||
figures.add("OOP");
|
|
||||||
figures.add("Handyholster");
|
|
||||||
|
|
||||||
|
|
||||||
figureDropdown = new Selector<>(figures, "glass");
|
|
||||||
figureDropdown.setBackground(new QuadBackgroundComponent(ColorRGBA.DarkGray));
|
|
||||||
figureDropdown.setPreferredSize(new Vector3f(100, 20, 0));
|
|
||||||
figureDropdownContainer.addChild(figureDropdown);
|
|
||||||
Vector3f dimens = dropdownContainer.getPreferredSize();
|
|
||||||
Vector3f dimens2 = figureDropdown.getPopupContainer().getPreferredSize();
|
|
||||||
dimens2.setX( dimens.getX() );
|
|
||||||
figureDropdown.getPopupContainer().setPreferredSize(new Vector3f(200,200,5));
|
|
||||||
|
|
||||||
// Create selection ref for updating
|
|
||||||
selectionRef = figureDropdown.getSelectionModel().createReference();
|
|
||||||
// Set default
|
|
||||||
figureDropdown.getSelectionModel().setSelection(0);
|
|
||||||
onDropdownSelectionChanged(figureDropdown);
|
|
||||||
|
|
||||||
Container buttonContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
|
|
||||||
buttonContainer.setPreferredSize(new Vector3f(100, 40, 0));
|
|
||||||
buttonContainer.setInsets(new Insets3f(20, 0, 10, 0)); // Add spacing above the buttons
|
|
||||||
buttonContainer.setBackground(null);
|
|
||||||
// Lower-left container for "Abbrechen" button
|
|
||||||
lowerLeftMenu = new Container();
|
|
||||||
Button cancelButton = new Button("Beenden");
|
|
||||||
cancelButton.setPreferredSize(new Vector3f(200, 60, 0)); // Set size to match the appearance in the image
|
|
||||||
cancelButton.setFontSize(18); // Adjust font size
|
|
||||||
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.closeApp();
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
}));
|
|
||||||
lowerLeftMenu.addChild(cancelButton);
|
|
||||||
|
|
||||||
// Position the container near the bottom-left corner
|
|
||||||
lowerLeftMenu.setLocalTranslation(new Vector3f(120, 170, 3)); // Adjust X and Y to align with the bottom-left corner
|
|
||||||
app.getGuiNode().attachChild(lowerLeftMenu);
|
|
||||||
|
|
||||||
// Lower-right container for "Bereit" button
|
|
||||||
lowerRightMenu = new Container();
|
|
||||||
Button readyButton = new Button("Bereit");
|
|
||||||
readyButton.setPreferredSize(new Vector3f(200, 60, 0)); // Set size to match the appearance in the image
|
|
||||||
readyButton.setFontSize(18); // Adjust font size
|
|
||||||
readyButton.setBackground(new QuadBackgroundComponent(ColorRGBA.Green)); // Add color to match the style
|
|
||||||
readyButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
music.toggleMusic();
|
|
||||||
toggleReady();
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
readyButton.setBackground(new QuadBackgroundComponent(ColorRGBA.DarkGray));
|
|
||||||
}));
|
|
||||||
lowerRightMenu.addChild(readyButton);
|
|
||||||
|
|
||||||
// Position the container near the bottom-right corner
|
|
||||||
lowerRightMenu.setLocalTranslation(new Vector3f(app.getCamera().getWidth() - 320, 170, 3)); // X: 220px from the right, Y: 50px above the bottom
|
|
||||||
app.getGuiNode().attachChild(lowerRightMenu);
|
|
||||||
|
|
||||||
// Add a colored circle between the input field and the dropdown menu
|
|
||||||
circle = createCircle(); // 50 is the diameter, Red is the color
|
|
||||||
circle.setLocalTranslation(new Vector3f(
|
|
||||||
(app.getCamera().getWidth()) / 2, // Center horizontally
|
|
||||||
(app.getCamera().getHeight() / 2) - 90, // Adjust Y position
|
|
||||||
2 // Ensure it's in front of the background but behind the dropdown
|
|
||||||
));
|
|
||||||
app.getGuiNode().attachChild(circle); // Attach to the GUI node
|
|
||||||
|
|
||||||
// Zentrierung des Containers
|
|
||||||
menuContainer.setLocalTranslation(
|
|
||||||
(app.getCamera().getWidth() - menuContainer.getPreferredSize().x) / 2,
|
|
||||||
(app.getCamera().getHeight() + menuContainer.getPreferredSize().y) / 2,
|
|
||||||
1 // Höhere Z-Ebene für den Vordergrund
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
app.getGuiNode().attachChild(menuContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a background image to the lobby menu.
|
|
||||||
*/
|
|
||||||
private void addBackgroundImage() {
|
|
||||||
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/lobby.png");
|
|
||||||
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
|
|
||||||
background = new Geometry("Background", quad);
|
|
||||||
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
|
||||||
backgroundMaterial.setTexture("ColorMap", backgroundImage);
|
|
||||||
background.setMaterial(backgroundMaterial);
|
|
||||||
background.setLocalTranslation(0, 0, -1); // Hintergrundebene
|
|
||||||
|
|
||||||
app.getGuiNode().attachChild(background);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a circle graphic element for the menu.
|
|
||||||
*
|
|
||||||
* @return the created circle geometry
|
|
||||||
*/
|
|
||||||
private Geometry createCircle() {
|
|
||||||
|
|
||||||
Sphere sphere = new Sphere(90,90,60.0f);
|
|
||||||
Geometry circleGeometry = new Geometry("Circle", sphere);
|
|
||||||
|
|
||||||
// Create a material with a solid color
|
|
||||||
Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
|
||||||
material.setColor("Color", Player.getColor(app.getId()).getColor()); // Set the desired color
|
|
||||||
circleGeometry.setMaterial(material);
|
|
||||||
|
|
||||||
return circleGeometry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the player's ready state and sends the configuration to the server.
|
|
||||||
*/
|
|
||||||
private void toggleReady() {
|
|
||||||
app.getGameLogic().send(new PlayerReady(true, playerInputField.getText(), figure, Integer.parseInt(startingCapital.getText())));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the settings menu when the escape key is pressed.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void escape() {
|
|
||||||
new SettingsMenu(app).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(float tpf) {
|
|
||||||
if (selectionRef.update()) {
|
|
||||||
onDropdownSelectionChanged(figureDropdown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the selected figure based on the dropdown menu selection.
|
|
||||||
*
|
|
||||||
* @param selected the selected figure
|
|
||||||
*/
|
|
||||||
private void onDropdownSelectionChanged(Selector<String> selector) {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
switch (selector.getSelectedItem()) {
|
|
||||||
case "Jägermeister":
|
|
||||||
figure = "Jaegermeister";
|
|
||||||
break;
|
|
||||||
case "Handyholster":
|
|
||||||
figure = "Holster";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
figure = selector.getSelectedItem();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
System.out.println("FIGUR:::::"+figure);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Functional interface for handling selection changes in dropdown menus.
|
|
||||||
*
|
|
||||||
* @param <T> the type of the selection
|
|
||||||
*/
|
|
||||||
@FunctionalInterface
|
|
||||||
private interface SelectionActionListener<T> {
|
|
||||||
/**
|
|
||||||
* Triggered when the selection changes.
|
|
||||||
*
|
|
||||||
* @param selection the new selection
|
|
||||||
*/
|
|
||||||
void onSelectionChanged(T selection);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,271 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.renderer.RenderManager;
|
|
||||||
import com.jme3.renderer.ViewPort;
|
|
||||||
import com.jme3.scene.control.AbstractControl;
|
|
||||||
import com.simsilica.lemur.*;
|
|
||||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
|
|
||||||
import com.simsilica.lemur.component.SpringGridLayout;
|
|
||||||
import com.simsilica.lemur.style.ElementId;
|
|
||||||
|
|
||||||
import pp.dialog.Dialog;
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.monopoly.game.server.Player;
|
|
||||||
import pp.monopoly.model.fields.BuildingProperty;
|
|
||||||
import pp.monopoly.model.fields.FoodField;
|
|
||||||
import pp.monopoly.model.fields.GateField;
|
|
||||||
import pp.monopoly.model.fields.PropertyField;
|
|
||||||
import pp.monopoly.notification.Sound;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PropertyOverviewMenu is a dialog for displaying the player's properties in the game.
|
|
||||||
*/
|
|
||||||
public class PropertyOverviewMenu extends Dialog {
|
|
||||||
private final MonopolyApp app;
|
|
||||||
private final Container mainContainer;
|
|
||||||
private final Container displayContainer;
|
|
||||||
private final Slider horizontalSlider;
|
|
||||||
private final List<Container> cards;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the PropertyOverviewMenu dialog.
|
|
||||||
*
|
|
||||||
* @param app The Monopoly application instance.
|
|
||||||
*/
|
|
||||||
public PropertyOverviewMenu(MonopolyApp app) {
|
|
||||||
super(app.getDialogManager());
|
|
||||||
this.app = app;
|
|
||||||
|
|
||||||
// Make the menu fullscreen
|
|
||||||
Vector3f screenSize = new Vector3f(app.getCamera().getWidth(), app.getCamera().getHeight(), 0);
|
|
||||||
|
|
||||||
// Main container for the menu layout
|
|
||||||
mainContainer = new Container();
|
|
||||||
mainContainer.setPreferredSize(screenSize); // Make fullscreen
|
|
||||||
mainContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.8f))); // Semi-transparent background
|
|
||||||
|
|
||||||
// Header label
|
|
||||||
Label headerLabel = mainContainer.addChild(new Label("Meine Grundstücke", new ElementId("header")));
|
|
||||||
headerLabel.setFontSize(40);
|
|
||||||
|
|
||||||
// Central display container (to hold the "Gebäude" cards)
|
|
||||||
displayContainer = mainContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y, FillMode.Even, FillMode.None))); // Horizontal layout
|
|
||||||
displayContainer.setPreferredSize(new Vector3f(screenSize.x - 300, 550, 0)); // Take up the remaining width
|
|
||||||
displayContainer.setBackground(new QuadBackgroundComponent(ColorRGBA.White));
|
|
||||||
displayContainer.setLocalTranslation(0, 0, 11);
|
|
||||||
|
|
||||||
// Add some placeholder "Gebäude" cards to the display container
|
|
||||||
cards = new ArrayList<>();
|
|
||||||
populatePlayerProperties();
|
|
||||||
|
|
||||||
|
|
||||||
// Initially add only visible cards to the displayContainer
|
|
||||||
refreshVisibleCards(0);
|
|
||||||
|
|
||||||
// Horizontal slider for scrolling through cards
|
|
||||||
horizontalSlider = mainContainer.addChild(new Slider(Axis.X));
|
|
||||||
horizontalSlider.setPreferredSize(new Vector3f(screenSize.x - 300, 20, 5));
|
|
||||||
horizontalSlider.setModel(new DefaultRangedValueModel(0, Math.max(0, cards.size() - 5), 0));
|
|
||||||
horizontalSlider.addControl(new SliderValueChangeListener());
|
|
||||||
|
|
||||||
// Add the "Zurück" button at the bottom
|
|
||||||
Button backButton = mainContainer.addChild(new Button("Zurück", new ElementId("button")));
|
|
||||||
backButton.setPreferredSize(new Vector3f(200, 60, 0));
|
|
||||||
backButton.addClickCommands(source -> ifTopDialog( () -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
close();
|
|
||||||
|
|
||||||
}));
|
|
||||||
|
|
||||||
// Attach the main container to the GUI node
|
|
||||||
app.getGuiNode().attachChild(mainContainer);
|
|
||||||
mainContainer.setLocalTranslation(
|
|
||||||
0,
|
|
||||||
app.getCamera().getHeight(), // Align to the top
|
|
||||||
10
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fetches the player's properties and populates them into the cards list.
|
|
||||||
*/
|
|
||||||
private void populatePlayerProperties() {
|
|
||||||
// Fetch the current player
|
|
||||||
Player currentPlayer = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId());
|
|
||||||
|
|
||||||
// Fetch the player's properties using their indices
|
|
||||||
List<PropertyField> fields = new ArrayList<>();
|
|
||||||
for (Integer i : currentPlayer.getProperties()) {
|
|
||||||
fields.add((PropertyField) app.getGameLogic().getBoardManager().getFieldAtIndex(i));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iterate through the fetched properties
|
|
||||||
for (PropertyField property : fields.stream().sorted(Comparator.comparingInt(PropertyField::getId)).collect(Collectors.toList())) {
|
|
||||||
if (property instanceof BuildingProperty) {
|
|
||||||
BuildingProperty building = (BuildingProperty) property;
|
|
||||||
cards.add(createBuildingCard(building));
|
|
||||||
} else if (property instanceof FoodField) {
|
|
||||||
FoodField foodField = (FoodField) property;
|
|
||||||
cards.add(createFoodFieldCard(foodField));
|
|
||||||
} else if (property instanceof GateField) {
|
|
||||||
GateField gateField = (GateField) property;
|
|
||||||
cards.add(createGateFieldCard(gateField));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a card for BuildingProperty with detailed rent and cost information.
|
|
||||||
*/
|
|
||||||
private Container createBuildingCard(BuildingProperty field) {
|
|
||||||
Container card = new Container();
|
|
||||||
card.setPreferredSize(new Vector3f(200, 300, 2)); // Match the size of the BuildingPropertyCard
|
|
||||||
card.setBackground(new QuadBackgroundComponent(field.getColor().getColor()));
|
|
||||||
card.setInsets(new Insets3f(5, 5, 5, 5)); // Add 5-pixel inset
|
|
||||||
|
|
||||||
card.addChild(new Label(field.getName(), new ElementId("label-Bold"))).setFontSize(25);
|
|
||||||
|
|
||||||
// Add property details
|
|
||||||
Container propertyValuesContainer = card.addChild(new Container());
|
|
||||||
propertyValuesContainer.addChild(new Label("Grundstückswert: " + field.getPrice()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
|
|
||||||
propertyValuesContainer.addChild(new Label("Miete allein: " + field.getAllRent().get(0)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("- mit 1 Haus: " + field.getAllRent().get(1)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("- mit 2 Häuser: " + field.getAllRent().get(2)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("- mit 3 Häuser: " + field.getAllRent().get(3)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("- mit 4 Häuser: " + field.getAllRent().get(4)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("- mit 1 Hotel: " + field.getAllRent().get(5)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("- 1 Haus kostet: " + field.getHousePrice()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
|
|
||||||
Label hypo = new Label("Hypothek: " + field.getHypo()+ " EUR", new ElementId("label-Text"));
|
|
||||||
hypo.setFontSize(14);
|
|
||||||
if (field.isMortgaged()) hypo.setColor(ColorRGBA.Red);
|
|
||||||
propertyValuesContainer.addChild(hypo);
|
|
||||||
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a card for FoodField with dynamic pricing and rent details.
|
|
||||||
*/
|
|
||||||
private Container createFoodFieldCard(FoodField field) {
|
|
||||||
Container card = new Container();
|
|
||||||
card.setPreferredSize(new Vector3f(200, 300, 2)); // Adjust card size
|
|
||||||
card.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f))); // Dark background for card
|
|
||||||
card.setInsets(new Insets3f(5, 5, 5, 5)); // Add 5-pixel inset
|
|
||||||
|
|
||||||
// Title Section
|
|
||||||
Label title = card.addChild(new Label(field.getName(), new ElementId("label-Bold")));
|
|
||||||
title.setFontSize(30);
|
|
||||||
title.setColor(ColorRGBA.White); // Ensure readability on dark background
|
|
||||||
|
|
||||||
// Property Values Section
|
|
||||||
Container propertyValuesContainer = card.addChild(new Container());
|
|
||||||
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); // Grey background
|
|
||||||
propertyValuesContainer.addChild(new Label("Preis: " + field.getPrice()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
|
|
||||||
propertyValuesContainer.addChild(new Label("Wenn man Besitzer der", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label(field.getName() + " ist, so ist", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("die Miete 40x Würfelwert.", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
|
|
||||||
propertyValuesContainer.addChild(new Label("Besitzer beider", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("Restaurants zahlt", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("100x Würfelwert.", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
|
|
||||||
propertyValuesContainer.addChild(new Label("Hypothek: " + field.getHypo()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a card for GateField with rent details for owning multiple gates.
|
|
||||||
*/
|
|
||||||
private Container createGateFieldCard(GateField field) {
|
|
||||||
Container card = new Container();
|
|
||||||
card.setPreferredSize(new Vector3f(200, 300, 2)); // Adjust card size
|
|
||||||
card.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Light grey background
|
|
||||||
card.setInsets(new Insets3f(5, 5, 5, 5)); // Add 5-pixel inset
|
|
||||||
|
|
||||||
// Title Section
|
|
||||||
Label title = card.addChild(new Label(field.getName(), new ElementId("label-Bold")));
|
|
||||||
title.setFontSize(30);
|
|
||||||
title.setColor(ColorRGBA.Black);
|
|
||||||
|
|
||||||
// Property Values Section
|
|
||||||
Container propertyValuesContainer = card.addChild(new Container());
|
|
||||||
propertyValuesContainer.addChild(new Label("Preis: " + field.getPrice()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("Miete: 250 EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("2 Bahnhof besitzt: 500 EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("3 Bahnhof besitzt: 1000 EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("4 Bahnhof besitzt: 2000 EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text"))).setFontSize(14);
|
|
||||||
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); // Dark grey background
|
|
||||||
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the visible cards in the displayContainer based on the slider value.
|
|
||||||
*
|
|
||||||
* @param startIndex The starting index of the visible cards.
|
|
||||||
*/
|
|
||||||
private void refreshVisibleCards(int startIndex) {
|
|
||||||
displayContainer.clearChildren(); // Remove all current children
|
|
||||||
int maxVisible = 5; // Number of cards to display at once
|
|
||||||
for (int i = startIndex; i < startIndex + maxVisible && i < cards.size(); i++) {
|
|
||||||
displayContainer.addChild(cards.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Custom listener for slider value changes.
|
|
||||||
* Extends {@link AbstractControl} to respond to updates in the slider's value and refresh
|
|
||||||
* the visible cards accordingly.
|
|
||||||
*/
|
|
||||||
private class SliderValueChangeListener extends AbstractControl {
|
|
||||||
@Override
|
|
||||||
protected void controlUpdate(float tpf) {
|
|
||||||
// Get the slider's current value and refresh visible cards
|
|
||||||
int sliderValue = (int) ((Slider) getSpatial()).getModel().getValue();
|
|
||||||
refreshVisibleCards(sliderValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Overrides the rendering logic for the control.
|
|
||||||
* <p>
|
|
||||||
* This implementation does not require any rendering operations, so the method is left empty.
|
|
||||||
*
|
|
||||||
* @param renderManager the {@link RenderManager} handling the rendering process.
|
|
||||||
* @param viewPort the {@link ViewPort} associated with the rendering context.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void controlRender(RenderManager renderManager, ViewPort viewPort) {
|
|
||||||
// No rendering logic needed
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the dialog and detaches it from the GUI node.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
app.getGuiNode().detachChild(mainContainer);
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,106 +0,0 @@
|
|||||||
////////////////////////////////////////
|
|
||||||
// Programming project code
|
|
||||||
// UniBw M, 2022, 2023, 2024
|
|
||||||
// www.unibw.de/inf2
|
|
||||||
// (c) Mark Minas (mark.minas@unibw.de)
|
|
||||||
////////////////////////////////////////
|
|
||||||
|
|
||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import java.util.prefs.Preferences;
|
|
||||||
|
|
||||||
import com.simsilica.lemur.Button;
|
|
||||||
import com.simsilica.lemur.Checkbox;
|
|
||||||
import com.simsilica.lemur.Label;
|
|
||||||
import com.simsilica.lemur.style.ElementId;
|
|
||||||
|
|
||||||
import pp.monopoly.client.GameMusic;
|
|
||||||
import pp.monopoly.client.GameSound;
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.dialog.Dialog;
|
|
||||||
import pp.dialog.StateCheckboxModel;
|
|
||||||
import pp.monopoly.notification.Sound;
|
|
||||||
|
|
||||||
import static pp.util.PreferencesUtils.getPreferences;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Menu class represents the main menu in the Monopoly game application.
|
|
||||||
* It extends the Dialog class and provides functionalities for Volume adjustment, Sound adjustment,
|
|
||||||
* returning to the game, and quitting the application.
|
|
||||||
*/
|
|
||||||
public class SettingsMenu extends Dialog {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preferences instance for storing and retrieving settings specific to the SettingsMenu.
|
|
||||||
*/
|
|
||||||
private static final Preferences PREFERENCES = getPreferences(SettingsMenu.class);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to the main Monopoly application instance.
|
|
||||||
*/
|
|
||||||
private final MonopolyApp app;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Slider control for adjusting the music volume.
|
|
||||||
*/
|
|
||||||
private final VolumeSlider musicSlider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Slider control for adjusting the sound effects volume.
|
|
||||||
*/
|
|
||||||
private final SoundSlider soundSlider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the Menu dialog for the Battleship application.
|
|
||||||
*
|
|
||||||
* @param app the MonopolyApp instance
|
|
||||||
*/
|
|
||||||
public SettingsMenu(MonopolyApp app) {
|
|
||||||
super(app.getDialogManager());
|
|
||||||
this.app = app;
|
|
||||||
musicSlider = new VolumeSlider(app.getStateManager().getState(GameMusic.class));
|
|
||||||
soundSlider = new SoundSlider(app.getStateManager().getState(GameSound.class));
|
|
||||||
addChild(new Label("Einstellungen", new ElementId("settings-title"))); //NON-NLS
|
|
||||||
addChild(new Label("Sound Effekte", new ElementId("label"))); //NON-NLS
|
|
||||||
|
|
||||||
addChild(soundSlider);
|
|
||||||
|
|
||||||
addChild(new Checkbox("Soundeffekte an / aus", new StateCheckboxModel(app, GameSound.class)));
|
|
||||||
|
|
||||||
addChild(new Label("Hintergrund Musik", new ElementId("label"))); //NON-NLS
|
|
||||||
addChild(new Checkbox("Musik an / aus", new StateCheckboxModel(app, GameMusic.class)));
|
|
||||||
|
|
||||||
addChild(musicSlider);
|
|
||||||
|
|
||||||
addChild(new Button("Zurück zum Spiel", new ElementId("button"))).addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
this.close(); // Close the StartMenu dialog
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
}));
|
|
||||||
addChild(new Button("Beenden", new ElementId("button"))).addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
app.closeApp();
|
|
||||||
}));
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the state of the load and save buttons based on the game logic.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void update() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void update(float delta) {
|
|
||||||
musicSlider.update();
|
|
||||||
soundSlider.update();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* As an escape action, this method closes the menu if it is the top dialog.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void escape() {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,38 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import com.simsilica.lemur.Slider;
|
|
||||||
import pp.monopoly.client.GameSound;
|
|
||||||
|
|
||||||
public class SoundSlider extends Slider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages sound effects for the game.
|
|
||||||
*/
|
|
||||||
private final pp.monopoly.client.GameSound sound;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Volume level for the game sounds.
|
|
||||||
*/
|
|
||||||
private double vol;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the Volume Slider for the Menu dialog
|
|
||||||
* @param sound the Effects sound instance
|
|
||||||
*/
|
|
||||||
public SoundSlider(GameSound sound) {
|
|
||||||
super();
|
|
||||||
this.sound = sound;
|
|
||||||
vol = GameSound.volumeInPreferences();
|
|
||||||
getModel().setPercent(vol);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When triggered it updates the volume to the value set with the slider
|
|
||||||
*/
|
|
||||||
public void update() {
|
|
||||||
if (vol != getModel().getPercent()) {
|
|
||||||
vol = getModel().getPercent();
|
|
||||||
sound.setVolume( (float) vol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,124 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.material.Material;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.scene.Geometry;
|
|
||||||
import com.jme3.scene.shape.Quad;
|
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
import com.simsilica.lemur.Axis;
|
|
||||||
import com.simsilica.lemur.Button;
|
|
||||||
import com.simsilica.lemur.Container;
|
|
||||||
import com.simsilica.lemur.HAlignment;
|
|
||||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
|
|
||||||
import com.simsilica.lemur.component.SpringGridLayout;
|
|
||||||
|
|
||||||
import pp.dialog.Dialog;
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.monopoly.notification.Sound;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the startup menu dialog for the Monopoly application.
|
|
||||||
*/
|
|
||||||
public class StartMenu extends Dialog {
|
|
||||||
private final MonopolyApp app;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the Startup Menu dialog for the Monopoly application.
|
|
||||||
*
|
|
||||||
* @param app the MonopolyApp instance
|
|
||||||
*/
|
|
||||||
public StartMenu(MonopolyApp app) {
|
|
||||||
super(app.getDialogManager());
|
|
||||||
this.app = app;
|
|
||||||
|
|
||||||
int screenWidth = app.getContext().getSettings().getWidth();
|
|
||||||
int screenHeight = app.getContext().getSettings().getHeight();
|
|
||||||
|
|
||||||
// Set up the background image
|
|
||||||
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
|
|
||||||
Quad quad = new Quad(screenWidth, screenHeight);
|
|
||||||
Geometry background = new Geometry("Background", quad);
|
|
||||||
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
|
|
||||||
backgroundMaterial.setTexture("ColorMap", backgroundImage);
|
|
||||||
background.setMaterial(backgroundMaterial);
|
|
||||||
background.setLocalTranslation(0, 0, -1);
|
|
||||||
app.getGuiNode().attachChild(background);
|
|
||||||
|
|
||||||
Container centerMenu = new Container(new SpringGridLayout(Axis.Y, Axis.X));
|
|
||||||
|
|
||||||
Button startButton = new Button("Spielen");
|
|
||||||
startButton.setPreferredSize(new Vector3f(190, 60, 0));
|
|
||||||
startButton.setFontSize(40);
|
|
||||||
startButton.setTextHAlignment(HAlignment.Center);
|
|
||||||
|
|
||||||
startButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
close();
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
app.connect();
|
|
||||||
}));
|
|
||||||
centerMenu.addChild(startButton);
|
|
||||||
|
|
||||||
|
|
||||||
centerMenu.setLocalTranslation(new Vector3f(screenWidth / 2f - centerMenu.getPreferredSize().x / 2f,
|
|
||||||
screenHeight / 2f - 280 + centerMenu.getPreferredSize().y / 2f,
|
|
||||||
0));
|
|
||||||
app.getGuiNode().attachChild(centerMenu);
|
|
||||||
|
|
||||||
// Load the Monopoly logo as a texture
|
|
||||||
Texture logoTexture = app.getAssetManager().loadTexture("Pictures/logo-monopoly.png");
|
|
||||||
|
|
||||||
// Create a container for the logo
|
|
||||||
Container logoContainer = new Container();
|
|
||||||
QuadBackgroundComponent logoBackground = new QuadBackgroundComponent(logoTexture);
|
|
||||||
logoContainer.setBackground(logoBackground);
|
|
||||||
|
|
||||||
|
|
||||||
float logoWidth = 512;
|
|
||||||
float logoHeight = 128;
|
|
||||||
logoContainer.setPreferredSize(new Vector3f(logoWidth, logoHeight, 0));
|
|
||||||
|
|
||||||
logoContainer.setLocalTranslation(new Vector3f(
|
|
||||||
screenWidth / 2f - logoWidth / 2f,
|
|
||||||
screenHeight / 2f + 200,
|
|
||||||
0
|
|
||||||
));
|
|
||||||
|
|
||||||
app.getGuiNode().attachChild(logoContainer);
|
|
||||||
|
|
||||||
Texture unibwTexture = app.getAssetManager().loadTexture("Pictures/logo-unibw.png");
|
|
||||||
|
|
||||||
Container unibwContainer = new Container();
|
|
||||||
QuadBackgroundComponent unibwBackground = new QuadBackgroundComponent(unibwTexture);
|
|
||||||
unibwContainer.setBackground(unibwBackground);
|
|
||||||
|
|
||||||
float unibwWidth = 512;
|
|
||||||
float unibwHeight = 128;
|
|
||||||
unibwContainer.setPreferredSize(new Vector3f(unibwWidth, unibwHeight, 0));
|
|
||||||
|
|
||||||
unibwContainer.setLocalTranslation(new Vector3f(
|
|
||||||
screenWidth / 2f - unibwWidth / 2f,
|
|
||||||
screenHeight / 2f + 100,
|
|
||||||
0
|
|
||||||
));
|
|
||||||
|
|
||||||
// Attach the container to the GUI node
|
|
||||||
app.getGuiNode().attachChild(unibwContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the settings menu when the escape key is pressed.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void escape() {
|
|
||||||
new SettingsMenu(app).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the startup menu and detaches all GUI elements.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
app.getGuiNode().detachAllChildren();
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,565 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import com.jme3.light.AmbientLight;
|
|
||||||
import com.jme3.light.DirectionalLight;
|
|
||||||
import com.jme3.material.Material;
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.math.FastMath;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.renderer.RenderManager;
|
|
||||||
import com.jme3.renderer.ViewPort;
|
|
||||||
import com.jme3.scene.control.AbstractControl;
|
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.monopoly.client.gui.popups.AcceptTrade;
|
|
||||||
import pp.monopoly.client.gui.popups.BuildingPropertyCard;
|
|
||||||
import pp.monopoly.client.gui.popups.ConfirmTrade;
|
|
||||||
import pp.monopoly.client.gui.popups.EventCardPopup;
|
|
||||||
import pp.monopoly.client.gui.popups.FoodFieldCard;
|
|
||||||
import pp.monopoly.client.gui.popups.GateFieldCard;
|
|
||||||
import pp.monopoly.client.gui.popups.Gulag;
|
|
||||||
import pp.monopoly.client.gui.popups.GulagInfo;
|
|
||||||
import pp.monopoly.client.gui.popups.LooserPopUp;
|
|
||||||
import pp.monopoly.client.gui.popups.NoMoneyWarning;
|
|
||||||
import pp.monopoly.client.gui.popups.ReceivedRent;
|
|
||||||
import pp.monopoly.client.gui.popups.RejectTrade;
|
|
||||||
import pp.monopoly.client.gui.popups.Rent;
|
|
||||||
import pp.monopoly.client.gui.popups.TimeOut;
|
|
||||||
import pp.monopoly.client.gui.popups.WinnerPopUp;
|
|
||||||
import pp.monopoly.game.server.Player;
|
|
||||||
import pp.monopoly.game.server.PlayerHandler;
|
|
||||||
import pp.monopoly.message.server.NotificationMessage;
|
|
||||||
import pp.monopoly.message.server.TradeReply;
|
|
||||||
import pp.monopoly.model.fields.BuildingProperty;
|
|
||||||
import pp.monopoly.model.fields.FoodField;
|
|
||||||
import pp.monopoly.model.fields.GateField;
|
|
||||||
import pp.monopoly.notification.EventCardEvent;
|
|
||||||
import pp.monopoly.notification.GameEventListener;
|
|
||||||
import pp.monopoly.notification.PopUpEvent;
|
|
||||||
import pp.monopoly.notification.UpdatePlayerView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TestWorld zeigt eine einfache Szene mit Spielfeld und Spielfiguren.
|
|
||||||
*/
|
|
||||||
public class TestWorld implements GameEventListener {
|
|
||||||
|
|
||||||
private final MonopolyApp app;
|
|
||||||
private PlayerHandler playerHandler;
|
|
||||||
private CameraController cameraController;
|
|
||||||
private Toolbar toolbar;
|
|
||||||
private List<String> existingHouses = new ArrayList<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Konstruktor für die TestWorld.
|
|
||||||
*
|
|
||||||
* @param app Die Hauptanwendung
|
|
||||||
*/
|
|
||||||
public TestWorld(MonopolyApp app) {
|
|
||||||
this.app = app;
|
|
||||||
this.playerHandler = app.getGameLogic().getPlayerHandler();
|
|
||||||
app.getGameLogic().addListener(this);
|
|
||||||
cameraController = new CameraController(app.getCamera());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialisiert die Szene mit Spielfeld und Figuren.
|
|
||||||
*/
|
|
||||||
public void initializeScene() {
|
|
||||||
// Entferne bestehende Inhalte
|
|
||||||
app.getGuiNode().detachAllChildren();
|
|
||||||
app.getRootNode().detachAllChildren();
|
|
||||||
|
|
||||||
System.out.println("Szene initialisiert.");
|
|
||||||
|
|
||||||
//Füge Inhalte ein
|
|
||||||
setSkyColor();
|
|
||||||
createBoard();
|
|
||||||
addLighting();
|
|
||||||
createPlayerFigures();
|
|
||||||
toolbar = new Toolbar(app);
|
|
||||||
toolbar.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setzt die Hintergrundfarbe der Szene auf hellblau.
|
|
||||||
*/
|
|
||||||
private void setSkyColor() {
|
|
||||||
app.getViewPort().setBackgroundColor(new com.jme3.math.ColorRGBA(0.5f, 0.7f, 1.0f, 1.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Erstellt das Spielfeld und fügt es zur Szene hinzu.
|
|
||||||
*/
|
|
||||||
private void createBoard() {
|
|
||||||
try {
|
|
||||||
com.jme3.scene.shape.Box box = new com.jme3.scene.shape.Box(10, 0.1f, 10);
|
|
||||||
com.jme3.scene.Geometry geom = new com.jme3.scene.Geometry("Board", box);
|
|
||||||
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
|
|
||||||
Texture texture = app.getAssetManager().loadTexture("Pictures/board2.png");
|
|
||||||
mat.setTexture("DiffuseMap", texture);
|
|
||||||
geom.setMaterial(mat);
|
|
||||||
|
|
||||||
geom.setLocalTranslation(0, -0.1f, 0);
|
|
||||||
|
|
||||||
com.jme3.math.Quaternion rotation = new com.jme3.math.Quaternion();
|
|
||||||
rotation.fromAngleAxis(FastMath.HALF_PI, com.jme3.math.Vector3f.UNIT_Y);
|
|
||||||
geom.setLocalRotation(rotation);
|
|
||||||
|
|
||||||
app.getRootNode().attachChild(geom);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Fehler beim Erstellen des Spielfelds: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addLighting() {
|
|
||||||
// Direktionales Licht
|
|
||||||
DirectionalLight sun = new DirectionalLight();
|
|
||||||
sun.setColor(ColorRGBA.White);
|
|
||||||
sun.setDirection(new Vector3f(-0.5f, -0.7f, -1.0f).normalizeLocal());
|
|
||||||
app.getRootNode().addLight(sun);
|
|
||||||
|
|
||||||
// Umgebungslicht
|
|
||||||
AmbientLight ambient = new AmbientLight();
|
|
||||||
ambient.setColor(new ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f));
|
|
||||||
app.getRootNode().addLight(ambient);
|
|
||||||
}
|
|
||||||
|
|
||||||
private com.jme3.math.Quaternion calculateRotationForField(int fieldID) {
|
|
||||||
com.jme3.math.Quaternion rotation = new com.jme3.math.Quaternion();
|
|
||||||
|
|
||||||
// Berechne die Rotation basierend auf der Feld-ID
|
|
||||||
if (fieldID >= 0 && fieldID <= 9) {
|
|
||||||
// Untere Seite (0-9)
|
|
||||||
rotation.fromAngleAxis(0, Vector3f.UNIT_Y); // Richtung: nach oben
|
|
||||||
} else if (fieldID >= 10 && fieldID <= 19) {
|
|
||||||
// Rechte Seite (10-19)
|
|
||||||
rotation.fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y); // Richtung: nach links
|
|
||||||
} else if (fieldID >= 20 && fieldID <= 29) {
|
|
||||||
// Obere Seite (20-29)
|
|
||||||
rotation.fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y); // Richtung: nach unten
|
|
||||||
} else if (fieldID >= 30 && fieldID <= 39) {
|
|
||||||
// Linke Seite (30-39)
|
|
||||||
rotation.fromAngleAxis(3 * FastMath.HALF_PI, Vector3f.UNIT_Y); // Richtung: nach rechts
|
|
||||||
}
|
|
||||||
|
|
||||||
// Korrigiere die Richtung für die Quadranten 10–19 und 30–39 (gegenüberliegende Richtung)
|
|
||||||
if ((fieldID >= 10 && fieldID <= 19) || (fieldID >= 30 && fieldID <= 39)) {
|
|
||||||
com.jme3.math.Quaternion oppositeDirection = new com.jme3.math.Quaternion();
|
|
||||||
oppositeDirection.fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y); // 180° drehen
|
|
||||||
rotation = rotation.multLocal(oppositeDirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Füge zusätzliche 90° nach links hinzu
|
|
||||||
com.jme3.math.Quaternion leftTurn = new com.jme3.math.Quaternion();
|
|
||||||
leftTurn.fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y); // 90° nach links
|
|
||||||
rotation = rotation.multLocal(leftTurn);
|
|
||||||
|
|
||||||
return rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Erstellt die Spielfiguren basierend auf der bereits bekannten Spielerliste.
|
|
||||||
*/
|
|
||||||
private void createPlayerFigures() {
|
|
||||||
for (Player player : playerHandler.getPlayers()) {
|
|
||||||
try {
|
|
||||||
// Lade das Modell
|
|
||||||
com.jme3.scene.Spatial model = app.getAssetManager().loadModel(
|
|
||||||
"models/" + "Spielfiguren/" + player.getFigure().getType() + "/" + player.getFigure().getType() + ".j3o");
|
|
||||||
|
|
||||||
// Skaliere und positioniere das Modell
|
|
||||||
model.setLocalScale(0.5f);
|
|
||||||
Vector3f startPosition = calculateFieldPosition(player.getFieldID(), player.getId());
|
|
||||||
model.setLocalTranslation(startPosition);
|
|
||||||
|
|
||||||
// Setze die Rotation basierend auf der Feld-ID
|
|
||||||
model.setLocalRotation(calculateRotationForField(player.getFieldID()));
|
|
||||||
|
|
||||||
model.setName("PlayerFigure_" + player.getId());
|
|
||||||
|
|
||||||
// Füge das Modell zur Szene hinzu
|
|
||||||
app.getRootNode().attachChild(model);
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Fehler beim Laden des Modells für Spieler " + player.getId() + ": " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector3f calculateFieldPosition(int fieldID, int playerIndex) {
|
|
||||||
float offset = 0.1f;
|
|
||||||
float baseX = 0.0f;
|
|
||||||
float baseZ = 0.0f;
|
|
||||||
|
|
||||||
switch (fieldID) {
|
|
||||||
case 0: baseX = -9.1f; baseZ = -9.1f; break;
|
|
||||||
case 1: baseX = -6.5f; baseZ = -9.1f; break;
|
|
||||||
case 2: baseX = -4.9f; baseZ = -9.1f; break;
|
|
||||||
case 3: baseX = -3.3f; baseZ = -9.1f; break;
|
|
||||||
case 4: baseX = -1.6f; baseZ = -9.1f; break;
|
|
||||||
case 5: baseX = 0.0f; baseZ = -9.1f; break;
|
|
||||||
case 6: baseX = 1.6f; baseZ = -9.1f; break;
|
|
||||||
case 7: baseX = 3.3f; baseZ = -9.1f; break;
|
|
||||||
case 8: baseX = 4.9f; baseZ = -9.1f; break;
|
|
||||||
case 9: baseX = 6.5f; baseZ = -9.1f; break;
|
|
||||||
case 10: baseX = 9.1f; baseZ = -9.1f; break;
|
|
||||||
case 11: baseX = 9.1f; baseZ = -6.5f; break;
|
|
||||||
case 12: baseX = 9.1f; baseZ = -4.9f; break;
|
|
||||||
case 13: baseX = 9.1f; baseZ = -3.3f; break;
|
|
||||||
case 14: baseX = 9.1f; baseZ = -1.6f; break;
|
|
||||||
case 15: baseX = 9.1f; baseZ = 0.0f; break;
|
|
||||||
case 16: baseX = 9.1f; baseZ = 1.6f; break;
|
|
||||||
case 17: baseX = 9.1f; baseZ = 3.3f; break;
|
|
||||||
case 18: baseX = 9.1f; baseZ = 4.9f; break;
|
|
||||||
case 19: baseX = 9.1f; baseZ = 6.5f; break;
|
|
||||||
case 20: baseX = 9.1f; baseZ = 9.1f; break;
|
|
||||||
case 21: baseX = 6.5f; baseZ = 9.1f; break;
|
|
||||||
case 22: baseX = 4.9f; baseZ = 9.1f; break;
|
|
||||||
case 23: baseX = 3.3f; baseZ = 9.1f; break;
|
|
||||||
case 24: baseX = 1.6f; baseZ = 9.1f; break;
|
|
||||||
case 25: baseX = 0.0f; baseZ = 9.1f; break;
|
|
||||||
case 26: baseX = -1.6f; baseZ = 9.1f; break;
|
|
||||||
case 27: baseX = -3.3f; baseZ = 9.1f; break;
|
|
||||||
case 28: baseX = -4.9f; baseZ = 9.1f; break;
|
|
||||||
case 29: baseX = -6.5f; baseZ = 9.1f; break;
|
|
||||||
case 30: baseX = -9.1f; baseZ = 9.1f; break;
|
|
||||||
case 31: baseX = -9.1f; baseZ = 6.5f; break;
|
|
||||||
case 32: baseX = -9.1f; baseZ = 4.9f; break;
|
|
||||||
case 33: baseX = -9.1f; baseZ = 3.3f; break;
|
|
||||||
case 34: baseX = -9.1f; baseZ = 1.6f; break;
|
|
||||||
case 35: baseX = -9.1f; baseZ = 0.0f; break;
|
|
||||||
case 36: baseX = -9.1f; baseZ = -1.6f; break;
|
|
||||||
case 37: baseX = -9.1f; baseZ = -3.3f; break;
|
|
||||||
case 38: baseX = -9.1f; baseZ = -4.9f; break;
|
|
||||||
case 39: baseX = -9.1f; baseZ = -6.5f; break;
|
|
||||||
default: throw new IllegalArgumentException("Ungültige Feld-ID: " + fieldID);
|
|
||||||
}
|
|
||||||
|
|
||||||
float xOffset = (playerIndex % 2) * offset;
|
|
||||||
float zOffset = (playerIndex / 2) * offset;
|
|
||||||
|
|
||||||
return new Vector3f(baseX + xOffset, 0, baseZ + zOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void movePlayerFigure(Player player) {
|
|
||||||
int playerIndexOnField = calculatePlayerIndexOnField(player.getFieldID(), player.getId());
|
|
||||||
String figureName = "PlayerFigure_" + player.getId();
|
|
||||||
com.jme3.scene.Spatial figure = app.getRootNode().getChild(figureName);
|
|
||||||
|
|
||||||
if (figure != null) {
|
|
||||||
// Füge einen Delay hinzu (z.B. 3 Sekunden)
|
|
||||||
Timer timer = new Timer();
|
|
||||||
timer.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
app.enqueue(() -> {
|
|
||||||
// Setze die Position
|
|
||||||
Vector3f targetPosition = calculateFieldPosition(player.getFieldID(), player.getId());
|
|
||||||
figure.setLocalTranslation(targetPosition);
|
|
||||||
|
|
||||||
// Aktualisiere die Rotation basierend auf der Feld-ID
|
|
||||||
figure.setLocalRotation(calculateRotationForField(player.getFieldID()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 3000); // 3000 Millisekunden Delay
|
|
||||||
} else {
|
|
||||||
System.err.println("Figur für Spieler " + player.getId() + " nicht gefunden.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getFieldIDFromPosition(Vector3f position) {
|
|
||||||
for (int fieldID = 0; fieldID < 40; fieldID++) {
|
|
||||||
Vector3f fieldPosition = calculateFieldPosition(fieldID, 0);
|
|
||||||
if (fieldPosition.distance(position) < 0.5f) { // Toleranz für Positionserkennung
|
|
||||||
return fieldID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("Position entspricht keinem gültigen Feld: " + position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Berechnet den Eckpunkt basierend auf Start- und Zielposition.
|
|
||||||
*
|
|
||||||
* @param startPosition Die Startposition der Figur.
|
|
||||||
* @param targetPosition Die Zielposition der Figur.
|
|
||||||
* @return Die Position der Ecke, die passiert werden muss.
|
|
||||||
*/
|
|
||||||
private Vector3f calculateCornerPosition(Vector3f startPosition, Vector3f targetPosition) {
|
|
||||||
// Ziel: Immer entlang der Spielfeldkante navigieren
|
|
||||||
float deltaX = targetPosition.x - startPosition.x;
|
|
||||||
float deltaZ = targetPosition.z - startPosition.z;
|
|
||||||
|
|
||||||
// Überprüfen, ob Bewegung entlang X oder Z-Koordinate zuerst erfolgen soll
|
|
||||||
if (deltaX != 0 && deltaZ != 0) {
|
|
||||||
if (Math.abs(deltaX) > Math.abs(deltaZ)) {
|
|
||||||
// Bewegung entlang X zuerst
|
|
||||||
return new Vector3f(targetPosition.x, 0, startPosition.z);
|
|
||||||
} else {
|
|
||||||
// Bewegung entlang Z zuerst
|
|
||||||
return new Vector3f(startPosition.x, 0, targetPosition.z);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Bewegung ist bereits entlang einer Achse (keine Ecke erforderlich)
|
|
||||||
return targetPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Vector3f> calculatePath(int startFieldID, int targetFieldID, int playerIndex) {
|
|
||||||
List<Vector3f> pathPoints = new ArrayList<>();
|
|
||||||
|
|
||||||
// Bewegung im Uhrzeigersinn
|
|
||||||
if (startFieldID < targetFieldID) {
|
|
||||||
for (int i = startFieldID; i <= targetFieldID; i++) {
|
|
||||||
// Füge Ecken hinzu, falls sie überschritten werden
|
|
||||||
if (i == 10 || i == 20 || i == 30 || i == 0) {
|
|
||||||
pathPoints.add(calculateFieldPosition(i, playerIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Bewegung über das Ende des Spielfelds hinaus (z.B. von 39 zu 5)
|
|
||||||
for (int i = startFieldID; i < 40; i++) {
|
|
||||||
if (i == 10 || i == 20 || i == 30 || i == 0) {
|
|
||||||
pathPoints.add(calculateFieldPosition(i, playerIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (int i = 0; i <= targetFieldID; i++) {
|
|
||||||
if (i == 10 || i == 20 || i == 30 || i == 0) {
|
|
||||||
pathPoints.add(calculateFieldPosition(i, playerIndex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Füge das Ziel hinzu
|
|
||||||
pathPoints.add(calculateFieldPosition(targetFieldID, playerIndex));
|
|
||||||
|
|
||||||
return pathPoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private int calculatePlayerIndexOnField(int fieldID, int playerID) {
|
|
||||||
List<Player> playersOnField = playerHandler.getPlayers().stream()
|
|
||||||
.filter(p -> p.getFieldID() == fieldID)
|
|
||||||
.toList();
|
|
||||||
|
|
||||||
for (int i = 0; i < playersOnField.size(); i++) {
|
|
||||||
if (playersOnField.get(i).getId() == playerID) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void animateMovementAlongPath(com.jme3.scene.Spatial figure, List<Vector3f> pathPoints) {
|
|
||||||
float animationDurationPerSegment = 2.5f; // Langsamere Animation (2.5 Sekunden pro Segment)
|
|
||||||
int[] currentSegment = {0};
|
|
||||||
|
|
||||||
app.enqueue(() -> {
|
|
||||||
figure.addControl(new AbstractControl() {
|
|
||||||
private float elapsedTime = 0.0f;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void controlUpdate(float tpf) {
|
|
||||||
if (currentSegment[0] >= pathPoints.size() - 1) {
|
|
||||||
this.setEnabled(false); // Animation abgeschlossen
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
elapsedTime += tpf;
|
|
||||||
float progress = Math.min(elapsedTime / animationDurationPerSegment, 1.0f);
|
|
||||||
|
|
||||||
Vector3f start = pathPoints.get(currentSegment[0]);
|
|
||||||
Vector3f end = pathPoints.get(currentSegment[0] + 1);
|
|
||||||
|
|
||||||
Vector3f interpolatedPosition = start.interpolateLocal(end, progress);
|
|
||||||
figure.setLocalTranslation(interpolatedPosition);
|
|
||||||
|
|
||||||
if (progress >= 1.0f) {
|
|
||||||
elapsedTime = 0.0f;
|
|
||||||
currentSegment[0]++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void controlRender(RenderManager rm, ViewPort vp) {
|
|
||||||
// Nicht benötigt
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(PopUpEvent event) {
|
|
||||||
if (event.msg().equals("Buy")) {
|
|
||||||
Timer timer = new Timer();
|
|
||||||
timer.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
app.enqueue(() -> {
|
|
||||||
int field = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
|
|
||||||
Object fieldObject = app.getGameLogic().getBoardManager().getFieldAtIndex(field);
|
|
||||||
|
|
||||||
if (fieldObject instanceof BuildingProperty) {
|
|
||||||
new BuildingPropertyCard(app).open();
|
|
||||||
} else if (fieldObject instanceof GateField) {
|
|
||||||
new GateFieldCard(app).open();
|
|
||||||
} else if (fieldObject instanceof FoodField) {
|
|
||||||
new FoodFieldCard(app).open();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, 2500);
|
|
||||||
} else if (event.msg().equals("Winner")) {
|
|
||||||
new WinnerPopUp(app).open();
|
|
||||||
} else if (event.msg().equals("Looser")) {
|
|
||||||
new LooserPopUp(app).open();
|
|
||||||
} else if (event.msg().equals("timeout")) {
|
|
||||||
new TimeOut(app).open();
|
|
||||||
} else if (event.msg().equals("tradeRequest")) {
|
|
||||||
new ConfirmTrade(app).open();
|
|
||||||
} else if (event.msg().equals("goingToJail")) {
|
|
||||||
new Gulag(app).open();
|
|
||||||
} else if (event.msg().equals("NoMoneyWarning")) {
|
|
||||||
new NoMoneyWarning(app).open();
|
|
||||||
} else if(event.msg().equals("rent")) {
|
|
||||||
new Rent(app, ( (NotificationMessage) event.message()).getRentOwner(), ( (NotificationMessage) event.message()).getRentAmount() ).open();
|
|
||||||
} else if (event.msg().equals("jailtryagain")) {
|
|
||||||
new GulagInfo(app, 1).open();
|
|
||||||
} else if (event.msg().equals("jailpay")) {
|
|
||||||
new GulagInfo(app, 3).open();
|
|
||||||
} else if (event.msg().equals("tradepos")) {
|
|
||||||
new AcceptTrade(app, (TradeReply) event.message()).open();
|
|
||||||
} else if (event.msg().equals("tradeneg")) {
|
|
||||||
new RejectTrade(app, (TradeReply) event.message()).open();
|
|
||||||
} else if (event.msg().equals("ReceivedRent")) {
|
|
||||||
new ReceivedRent(app, ( (NotificationMessage) event.message()).getRentOwner(), ( (NotificationMessage) event.message()).getRentAmount() ).open();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector3f calculateBuildingPosition(int fieldID) {
|
|
||||||
float baseX = 0.0f;
|
|
||||||
float baseZ = 0.0f;
|
|
||||||
|
|
||||||
switch (fieldID) {
|
|
||||||
case 0: baseX = -8.4f; baseZ = -7.7f; break;
|
|
||||||
case 1: baseX = -6.3f; baseZ = -7.7f; break;
|
|
||||||
case 2: baseX = -4.7f; baseZ = -7.7f; break;
|
|
||||||
case 3: baseX = -3.1f; baseZ = -7.7f; break;
|
|
||||||
case 4: baseX = -1.4f; baseZ = -7.7f; break;
|
|
||||||
case 5: baseX = 0.2f; baseZ = -7.7f; break;
|
|
||||||
case 6: baseX = 1.8f; baseZ = -7.7f; break;
|
|
||||||
case 7: baseX = 3.5f; baseZ = -7.7f; break;
|
|
||||||
case 8: baseX = 5.1f; baseZ = -7.7f; break;
|
|
||||||
case 9: baseX = 6.7f; baseZ = -7.7f; break;
|
|
||||||
case 10: baseX = 8.2f; baseZ = -7.7f; break;
|
|
||||||
case 11: baseX = 8.2f; baseZ = -6.5f; break; //passt
|
|
||||||
case 12: baseX = 8.2f; baseZ = -4.9f; break; //passt
|
|
||||||
case 13: baseX = 8.2f; baseZ = -3.3f; break; //passt
|
|
||||||
case 14: baseX = 8.2f; baseZ = -1.6f; break; //passt
|
|
||||||
case 15: baseX = 8.2f; baseZ = 0.0f; break; //passt
|
|
||||||
case 16: baseX = 8.2f; baseZ = 1.6f; break; //passt
|
|
||||||
case 17: baseX = 8.2f; baseZ = 3.3f; break; //passt
|
|
||||||
case 18: baseX = 8.2f; baseZ = 4.9f; break; //passt
|
|
||||||
case 19: baseX = 8.2f; baseZ = 6.5f; break; //passt
|
|
||||||
case 20: baseX = 8.2f; baseZ = 7.7f; break;
|
|
||||||
case 21: baseX = 6.5f; baseZ = 7.7f; break;
|
|
||||||
case 22: baseX = 4.9f; baseZ = 7.7f; break;
|
|
||||||
case 23: baseX = 3.3f; baseZ = 7.7f; break;
|
|
||||||
case 24: baseX = 1.6f; baseZ = 7.7f; break;
|
|
||||||
case 25: baseX = 0.0f; baseZ = 7.7f; break;
|
|
||||||
case 26: baseX = -1.6f; baseZ = 7.7f; break;
|
|
||||||
case 27: baseX = -3.3f; baseZ = 7.7f; break;
|
|
||||||
case 28: baseX = -4.9f; baseZ = 7.7f; break;
|
|
||||||
case 29: baseX = -6.5f; baseZ = 7.7f; break;
|
|
||||||
case 30: baseX = -7.2f; baseZ = 7.7f; break;
|
|
||||||
case 31: baseX = -7.2f; baseZ = 6.5f; break;
|
|
||||||
case 32: baseX = -7.2f; baseZ = 4.9f; break;
|
|
||||||
case 33: baseX = -7.2f; baseZ = 3.3f; break;
|
|
||||||
case 34: baseX = -7.2f; baseZ = 1.6f; break;
|
|
||||||
case 35: baseX = -7.2f; baseZ = 0.0f; break;
|
|
||||||
case 36: baseX = -7.2f; baseZ = -1.6f; break;
|
|
||||||
case 37: baseX = -7.2f; baseZ = -3.3f; break;
|
|
||||||
case 38: baseX = -7.2f; baseZ = -4.9f; break;
|
|
||||||
case 39: baseX = -7.2f; baseZ = -6.5f; break;
|
|
||||||
default: throw new IllegalArgumentException("Ungültige Feld-ID: " + fieldID);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Vector3f(baseX, 0, baseZ);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateHousesOnBoard() {
|
|
||||||
app.enqueue(() -> {
|
|
||||||
List<BuildingProperty> propertiesWithBuildings = app.getGameLogic().getBoardManager().getPropertiesWithBuildings();
|
|
||||||
|
|
||||||
for (BuildingProperty property : propertiesWithBuildings) {
|
|
||||||
int houseCount = property.getHouses();
|
|
||||||
int hotelCount = property.getHotel();
|
|
||||||
|
|
||||||
String uniqueIdentifier = "Building_" + property.getId() + "_" + (hotelCount > 0 ? "Hotel" : houseCount);
|
|
||||||
|
|
||||||
if (existingHouses.contains(uniqueIdentifier)) continue;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String modelPath = hotelCount > 0
|
|
||||||
? "models/Hotel/Hotel.j3o"
|
|
||||||
: "models/Haus/" + houseCount + "Haus.j3o";
|
|
||||||
|
|
||||||
com.jme3.scene.Spatial buildingModel = app.getAssetManager().loadModel(modelPath);
|
|
||||||
|
|
||||||
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
|
|
||||||
|
|
||||||
buildingModel.setMaterial(mat);
|
|
||||||
|
|
||||||
buildingModel.setLocalScale(0.5f);
|
|
||||||
Vector3f position = calculateBuildingPosition(property.getId()).add(0, 0.5f, 0);
|
|
||||||
buildingModel.setLocalTranslation(position);
|
|
||||||
|
|
||||||
com.jme3.math.Quaternion rotation = new com.jme3.math.Quaternion();
|
|
||||||
if (property.getId() >= 1 && property.getId() <= 10) {
|
|
||||||
rotation.fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y);
|
|
||||||
} else if (property.getId() >= 21 && property.getId() <= 30) {
|
|
||||||
rotation.fromAngleAxis(3 * FastMath.HALF_PI, Vector3f.UNIT_Y);
|
|
||||||
} else if (property.getId() >= 31 && property.getId() <= 39) {
|
|
||||||
rotation.fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y);
|
|
||||||
}
|
|
||||||
|
|
||||||
buildingModel.setLocalRotation(rotation);
|
|
||||||
buildingModel.setName(uniqueIdentifier);
|
|
||||||
app.getRootNode().attachChild(buildingModel);
|
|
||||||
existingHouses.add(uniqueIdentifier);
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Fehler beim Hinzufügen eines Gebäudes: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(EventCardEvent event) {
|
|
||||||
Timer timer = new Timer();
|
|
||||||
timer.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
app.enqueue(() -> new EventCardPopup(app, event.description()).open());
|
|
||||||
}
|
|
||||||
}, 2500);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(UpdatePlayerView event) {
|
|
||||||
this.playerHandler = app.getGameLogic().getPlayerHandler();
|
|
||||||
for (Player player : playerHandler.getPlayers()) {
|
|
||||||
movePlayerFigure(player);
|
|
||||||
}
|
|
||||||
updateHousesOnBoard();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,465 +0,0 @@
|
|||||||
package pp.monopoly.client.gui;
|
|
||||||
|
|
||||||
import com.jme3.math.ColorRGBA;
|
|
||||||
import com.jme3.math.Vector2f;
|
|
||||||
import com.jme3.math.Vector3f;
|
|
||||||
import com.jme3.texture.Texture;
|
|
||||||
import com.simsilica.lemur.Axis;
|
|
||||||
import com.simsilica.lemur.Button;
|
|
||||||
import com.simsilica.lemur.Container;
|
|
||||||
import com.simsilica.lemur.HAlignment;
|
|
||||||
import com.simsilica.lemur.Label;
|
|
||||||
import com.simsilica.lemur.VAlignment;
|
|
||||||
import com.simsilica.lemur.component.IconComponent;
|
|
||||||
import com.simsilica.lemur.component.QuadBackgroundComponent;
|
|
||||||
import com.simsilica.lemur.component.SpringGridLayout;
|
|
||||||
import com.simsilica.lemur.style.ElementId;
|
|
||||||
|
|
||||||
import pp.dialog.Dialog;
|
|
||||||
import pp.monopoly.client.MonopolyApp;
|
|
||||||
import pp.monopoly.client.gui.popups.Bankrupt;
|
|
||||||
import pp.monopoly.game.server.Player;
|
|
||||||
import pp.monopoly.game.server.PlayerHandler;
|
|
||||||
import pp.monopoly.message.client.EndTurn;
|
|
||||||
import pp.monopoly.message.client.RollDice;
|
|
||||||
import pp.monopoly.notification.ButtonStatusEvent;
|
|
||||||
import pp.monopoly.notification.DiceRollEvent;
|
|
||||||
import pp.monopoly.notification.GameEventListener;
|
|
||||||
import pp.monopoly.notification.Sound;
|
|
||||||
import pp.monopoly.notification.UpdatePlayerView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents the toolbar interface in the Monopoly application.
|
|
||||||
* <p>
|
|
||||||
* This class provides game controls, player information, and event handling
|
|
||||||
* for actions such as dice rolling, trading, and ending turns.
|
|
||||||
* Implements {@link GameEventListener} to respond to game events.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class Toolbar extends Dialog implements GameEventListener {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reference to the Monopoly application instance.
|
|
||||||
*/
|
|
||||||
private final MonopolyApp app;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main container for the toolbar interface.
|
|
||||||
*/
|
|
||||||
private final Container toolbarContainer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container for displaying an overview of other players.
|
|
||||||
*/
|
|
||||||
private Container overviewContainer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Container for displaying account-related information.
|
|
||||||
*/
|
|
||||||
private Container accountContainer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles player-related data and actions.
|
|
||||||
*/
|
|
||||||
private PlayerHandler playerHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Label for the first dice display.
|
|
||||||
*/
|
|
||||||
private Label imageLabel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Label for the second dice display.
|
|
||||||
*/
|
|
||||||
private Label imageLabel2;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button for rolling the dice.
|
|
||||||
*/
|
|
||||||
private Button diceButton;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button for initiating trades.
|
|
||||||
*/
|
|
||||||
private Button tradeButton;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button for accessing the property menu.
|
|
||||||
*/
|
|
||||||
private Button propertyMenuButton;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button for ending the player's turn.
|
|
||||||
*/
|
|
||||||
private Button endTurnButton;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores the most recent dice roll event.
|
|
||||||
*/
|
|
||||||
private DiceRollEvent latestDiceRollEvent = null;
|
|
||||||
|
|
||||||
/**Indicates if the bankrupt PopUp has already been shown */
|
|
||||||
private boolean bankruptPopUp = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the toolbar for the Monopoly application.
|
|
||||||
* <p>
|
|
||||||
* Initializes the toolbar interface, adds event listeners, and sets up
|
|
||||||
* the GUI elements such as dice, buttons, and player information displays.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param app the Monopoly application instance
|
|
||||||
*/
|
|
||||||
public Toolbar(MonopolyApp app) {
|
|
||||||
super(app.getDialogManager());
|
|
||||||
this.app = app;
|
|
||||||
|
|
||||||
app.getGameLogic().addListener(this);
|
|
||||||
this.playerHandler = app.getGameLogic().getPlayerHandler();
|
|
||||||
|
|
||||||
toolbarContainer = createToolbarContainer();
|
|
||||||
app.getGuiNode().attachChild(toolbarContainer);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Container createToolbarContainer() {
|
|
||||||
Container container = new Container(new SpringGridLayout(Axis.X, Axis.Y), "toolbar");
|
|
||||||
container.setLocalTranslation(0, 200, 0);
|
|
||||||
container.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 200, 0));
|
|
||||||
Texture backgroundToolbar = app.getAssetManager().loadTexture("Pictures/toolbarbg.png");
|
|
||||||
QuadBackgroundComponent background = new QuadBackgroundComponent(backgroundToolbar);
|
|
||||||
container.setBackground(background);
|
|
||||||
|
|
||||||
// Spielerfarbe abrufen
|
|
||||||
Player currentPlayer = playerHandler.getPlayerById(app.getId());
|
|
||||||
ColorRGBA playerColor = Player.getColor(currentPlayer.getId()).getColor();
|
|
||||||
|
|
||||||
// Oberer Balken
|
|
||||||
Container playerColorBar = new Container();
|
|
||||||
playerColorBar.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 15, 0)); // Höhe des oberen Balkens
|
|
||||||
playerColorBar.setBackground(new QuadBackgroundComponent(playerColor));
|
|
||||||
playerColorBar.setLocalTranslation(0, 210, 3); // Position über der Toolbar
|
|
||||||
app.getGuiNode().attachChild(playerColorBar);
|
|
||||||
|
|
||||||
// unterer Balken
|
|
||||||
Container playerColorBarbot = new Container();
|
|
||||||
playerColorBarbot.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 10, 0)); // Höhe des oberen Balkens
|
|
||||||
playerColorBarbot.setBackground(new QuadBackgroundComponent(playerColor));
|
|
||||||
playerColorBarbot.setLocalTranslation(0, 10, 3); // Position über der Toolbar
|
|
||||||
app.getGuiNode().attachChild(playerColorBarbot);
|
|
||||||
|
|
||||||
|
|
||||||
// Linker Balken
|
|
||||||
Container leftBar = new Container();
|
|
||||||
leftBar.setPreferredSize(new Vector3f(10, 210, 0)); // Breite 10, Höhe 210
|
|
||||||
leftBar.setBackground(new QuadBackgroundComponent(playerColor));
|
|
||||||
leftBar.setLocalTranslation(0, 200, 3); // Position am linken Rand
|
|
||||||
app.getGuiNode().attachChild(leftBar);
|
|
||||||
|
|
||||||
// Rechter Balken
|
|
||||||
Container rightBar = new Container();
|
|
||||||
rightBar.setPreferredSize(new Vector3f(10, 210, 0)); // Breite 10, Höhe 210
|
|
||||||
rightBar.setBackground(new QuadBackgroundComponent(playerColor));
|
|
||||||
rightBar.setLocalTranslation(app.getCamera().getWidth() - 10, 200, 2); // Position am rechten Rand
|
|
||||||
app.getGuiNode().attachChild(rightBar);
|
|
||||||
|
|
||||||
// Übersicht und Konto
|
|
||||||
accountContainer = container.addChild(new Container());
|
|
||||||
overviewContainer = container.addChild(new Container());
|
|
||||||
receivedEvent(new UpdatePlayerView()); // Initiale Aktualisierung
|
|
||||||
|
|
||||||
// Würfel-Bereich
|
|
||||||
container.addChild(createDiceSection());
|
|
||||||
|
|
||||||
// Aktionsmenü
|
|
||||||
Container menuContainer = container.addChild(new Container());
|
|
||||||
menuContainer.addChild(createTradeButton());
|
|
||||||
menuContainer.addChild(createPropertyMenuButton());
|
|
||||||
menuContainer.addChild(createEndTurnButton());
|
|
||||||
menuContainer.setBackground(null);
|
|
||||||
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Container createDiceSection() {
|
|
||||||
Container diceContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
|
|
||||||
diceContainer.addChild(createDiceDisplay());
|
|
||||||
diceContainer.setBackground(null);
|
|
||||||
|
|
||||||
diceButton = new Button("Würfeln", new ElementId("button-toolbar"));
|
|
||||||
diceButton.setPreferredSize(new Vector3f(200, 50, 0));
|
|
||||||
diceButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
diceButton.setEnabled(false);
|
|
||||||
endTurnButton.setEnabled(true);
|
|
||||||
startDiceAnimation();
|
|
||||||
app.getGameLogic().send(new RollDice());
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
}));
|
|
||||||
diceContainer.addChild(diceButton);
|
|
||||||
|
|
||||||
return diceContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Container createDiceDisplay() {
|
|
||||||
Container horizontalContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
|
|
||||||
horizontalContainer.setPreferredSize(new Vector3f(200, 150, 0));
|
|
||||||
horizontalContainer.setBackground(null);
|
|
||||||
|
|
||||||
imageLabel = createDiceLabel("Pictures/dice/one.png");
|
|
||||||
imageLabel2 = createDiceLabel("Pictures/dice/two.png");
|
|
||||||
|
|
||||||
horizontalContainer.addChild(createDiceContainer(imageLabel));
|
|
||||||
horizontalContainer.addChild(createDiceContainer(imageLabel2));
|
|
||||||
|
|
||||||
return horizontalContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Label createDiceLabel(String iconPath) {
|
|
||||||
Label label = new Label("");
|
|
||||||
IconComponent icon = new IconComponent(iconPath);
|
|
||||||
icon.setIconSize(new Vector2f(100, 100));
|
|
||||||
label.setIcon(icon);
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Container createDiceContainer(Label label) {
|
|
||||||
Container container = new Container();
|
|
||||||
container.setBackground(null);
|
|
||||||
container.setPreferredSize(new Vector3f(100, 150, 0));
|
|
||||||
container.addChild(label);
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Button createTradeButton() {
|
|
||||||
|
|
||||||
tradeButton = new Button("", new ElementId("button-toolbar"));
|
|
||||||
tradeButton.setPreferredSize(new Vector3f(150, 50, 0));
|
|
||||||
|
|
||||||
String iconTradePath = "icons/icon-handeln.png";
|
|
||||||
IconComponent iconTrade = new IconComponent(iconTradePath);
|
|
||||||
iconTrade.setHAlignment(HAlignment.Center);
|
|
||||||
iconTrade.setVAlignment(VAlignment.Center);
|
|
||||||
iconTrade.setIconSize(new Vector2f(100, 100));
|
|
||||||
|
|
||||||
tradeButton.setIcon(iconTrade);
|
|
||||||
tradeButton.setFontSize(40);
|
|
||||||
|
|
||||||
// Add click behavior
|
|
||||||
tradeButton.addClickCommands(source -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
new ChoosePartner(app).open();
|
|
||||||
}));
|
|
||||||
|
|
||||||
return tradeButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Button createPropertyMenuButton() {
|
|
||||||
propertyMenuButton = new Button("", new ElementId("button-toolbar"));
|
|
||||||
propertyMenuButton.setPreferredSize(new Vector3f(150, 50, 0));
|
|
||||||
|
|
||||||
String iconBuildingPath = "icons/icon-gebaude.png";
|
|
||||||
IconComponent iconBuilding = new IconComponent(iconBuildingPath);
|
|
||||||
iconBuilding.setHAlignment(HAlignment.Center);
|
|
||||||
iconBuilding.setVAlignment(VAlignment.Center);
|
|
||||||
iconBuilding.setIconSize(new Vector2f(75, 75));
|
|
||||||
|
|
||||||
propertyMenuButton.setIcon(iconBuilding);
|
|
||||||
propertyMenuButton.setFontSize(30);
|
|
||||||
propertyMenuButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
new BuildingAdminMenu(app).open();
|
|
||||||
}));
|
|
||||||
return propertyMenuButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Button createEndTurnButton() {
|
|
||||||
endTurnButton = new Button("", new ElementId("button-toolbar"));
|
|
||||||
endTurnButton.setFontSize(28);
|
|
||||||
endTurnButton.setPreferredSize(new Vector3f(150, 50, 0));
|
|
||||||
|
|
||||||
String iconEndTurnPath = "icons/icon-zugbeenden.png";
|
|
||||||
IconComponent iconEndTurn = new IconComponent(iconEndTurnPath);
|
|
||||||
iconEndTurn.setHAlignment(HAlignment.Center);
|
|
||||||
iconEndTurn.setVAlignment(VAlignment.Center);
|
|
||||||
iconEndTurn.setIconSize(new Vector2f(75, 75));
|
|
||||||
|
|
||||||
endTurnButton.setIcon(iconEndTurn);
|
|
||||||
endTurnButton.addClickCommands(s -> ifTopDialog(() -> {
|
|
||||||
app.getGameLogic().playSound(Sound.BUTTON);
|
|
||||||
if (app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getAccountBalance() < 0 && !bankruptPopUp) {
|
|
||||||
new Bankrupt(app).open();
|
|
||||||
bankruptPopUp = true;
|
|
||||||
} else {
|
|
||||||
bankruptPopUp = false;
|
|
||||||
app.getGameLogic().send(new EndTurn());
|
|
||||||
receivedEvent(new ButtonStatusEvent(false));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
return endTurnButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void startDiceAnimation() {
|
|
||||||
long startTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
new Thread(() -> {
|
|
||||||
try {
|
|
||||||
animateDice(startTime);
|
|
||||||
if (latestDiceRollEvent != null) {
|
|
||||||
showFinalDiceResult(latestDiceRollEvent);
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
System.err.println("Dice animation interrupted: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Animates the dice roll by cycling through dice images.
|
|
||||||
*/
|
|
||||||
private void animateDice(long startTime) throws InterruptedException {
|
|
||||||
int[] currentFace = {1};
|
|
||||||
while (System.currentTimeMillis() - startTime < 2000) { // Animation duration
|
|
||||||
currentFace[0] = (currentFace[0] % 6) + 1;
|
|
||||||
|
|
||||||
String rotatingImage1 = diceToString(currentFace[0]);
|
|
||||||
String rotatingImage2 = diceToString((currentFace[0] % 6) + 1);
|
|
||||||
|
|
||||||
app.enqueue(() -> {
|
|
||||||
setDiceIcon(imageLabel, rotatingImage1);
|
|
||||||
setDiceIcon(imageLabel2, rotatingImage2);
|
|
||||||
});
|
|
||||||
|
|
||||||
Thread.sleep(100); // Time between frame updates
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays the final dice result after animation.
|
|
||||||
*
|
|
||||||
* @param event the dice roll event containing the result
|
|
||||||
*/
|
|
||||||
private void showFinalDiceResult(DiceRollEvent event) {
|
|
||||||
app.enqueue(() -> {
|
|
||||||
setDiceIcon(imageLabel, diceToString(event.a()));
|
|
||||||
setDiceIcon(imageLabel2, diceToString(event.b()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setDiceIcon(Label label, String imagePath) {
|
|
||||||
IconComponent icon = new IconComponent(imagePath);
|
|
||||||
icon.setIconSize(new Vector2f(80, 80)); // Set consistent dice size
|
|
||||||
label.setIcon(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String diceToString(int i) {
|
|
||||||
switch (i) {
|
|
||||||
case 1: return "Pictures/dice/one.png";
|
|
||||||
case 2: return "Pictures/dice/two.png";
|
|
||||||
case 3: return "Pictures/dice/three.png";
|
|
||||||
case 4: return "Pictures/dice/four.png";
|
|
||||||
case 5: return "Pictures/dice/five.png";
|
|
||||||
case 6: return "Pictures/dice/six.png";
|
|
||||||
default: throw new IllegalArgumentException("Invalid dice number: " + i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles dice roll events by updating the dice display.
|
|
||||||
*
|
|
||||||
* @param event the dice roll event containing dice values
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(DiceRollEvent event) {
|
|
||||||
latestDiceRollEvent = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the player view with the latest account and overview data.
|
|
||||||
*
|
|
||||||
* @param event the update event for the player view
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(UpdatePlayerView event) {
|
|
||||||
playerHandler = app.getGameLogic().getPlayerHandler();
|
|
||||||
System.out.println("Update Player View");
|
|
||||||
accountContainer.clearChildren();
|
|
||||||
overviewContainer.clearChildren();
|
|
||||||
|
|
||||||
accountContainer.addChild(new Label("Kontostand", new ElementId("label-toolbar")));
|
|
||||||
accountContainer.addChild(new Label(
|
|
||||||
playerHandler.getPlayerById(app.getId()).getAccountBalance() + " EUR",
|
|
||||||
new ElementId("label-account")
|
|
||||||
));
|
|
||||||
accountContainer.addChild(new Label("Gulag Karten", new ElementId("label-toolbar")));
|
|
||||||
accountContainer.addChild(new Label(
|
|
||||||
playerHandler.getPlayerById(app.getId()).getNumJailCard() + "",
|
|
||||||
new ElementId("label-account")
|
|
||||||
));
|
|
||||||
accountContainer.setBackground(null);
|
|
||||||
|
|
||||||
overviewContainer.addChild(new Label("Übersicht", new ElementId("label-toolbar")));
|
|
||||||
for (Player player : playerHandler.getPlayers()) {
|
|
||||||
if (player.getId() != app.getId()) {
|
|
||||||
// Spielerfarbe abrufen
|
|
||||||
ColorRGBA playerColor = (Player.getColor(player.getId()).getColor());
|
|
||||||
|
|
||||||
// Label für den Spieler erstellen
|
|
||||||
Label playerLabel = new Label(
|
|
||||||
player.getName() + ": " + player.getAccountBalance() + " EUR",
|
|
||||||
new ElementId("label-Text")
|
|
||||||
);
|
|
||||||
|
|
||||||
// Farbe setzen
|
|
||||||
playerLabel.setColor(playerColor);
|
|
||||||
|
|
||||||
// Label zum Container hinzufügen
|
|
||||||
overviewContainer.addChild(playerLabel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
overviewContainer.setBackground(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the enabled status of toolbar buttons based on the event.
|
|
||||||
*
|
|
||||||
* @param event the button status event
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void receivedEvent(ButtonStatusEvent event) {
|
|
||||||
boolean enabled = event.buttonsEnabled();
|
|
||||||
diceButton.setEnabled(enabled);
|
|
||||||
tradeButton.setEnabled(enabled);
|
|
||||||
propertyMenuButton.setEnabled(enabled);
|
|
||||||
endTurnButton.setEnabled(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the toolbar and detaches it from the GUI.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
app.getGuiNode().detachChild(toolbarContainer);
|
|
||||||
super.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the settings menu when the escape key is pressed.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void escape() {
|
|
||||||
new SettingsMenu(app).open();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the toolbar by refreshing player information.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void update() {
|
|
||||||
receivedEvent(new UpdatePlayerView());
|
|
||||||
super.update();
|
|
||||||
}
|
|
||||||
}
|
|