diff --git a/Projekte/.run/MonopolyApp (Mac).run.xml b/Projekte/.run/MonopolyApp (Mac).run.xml new file mode 100644 index 0000000..95dd7c1 --- /dev/null +++ b/Projekte/.run/MonopolyApp (Mac).run.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/Projekte/.run/MonopolyApp.run.xml b/Projekte/.run/MonopolyApp.run.xml new file mode 100644 index 0000000..ccd1e2f --- /dev/null +++ b/Projekte/.run/MonopolyApp.run.xml @@ -0,0 +1,18 @@ + + + + \ No newline at end of file diff --git a/Projekte/.run/MonopolyServer.run.xml b/Projekte/.run/MonopolyServer.run.xml new file mode 100644 index 0000000..d892cf3 --- /dev/null +++ b/Projekte/.run/MonopolyServer.run.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/Menu.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/Menu.java index 44a9a74..12afb6d 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/Menu.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/Menu.java @@ -18,10 +18,11 @@ import com.simsilica.lemur.style.ElementId; import static pp.battleship.Resources.lookup; import pp.battleship.client.gui.GameMusic; -import pp.battleship.client.gui.VolumeSlider; import pp.dialog.Dialog; import pp.dialog.StateCheckboxModel; import pp.dialog.TextInputDialog; +import pp.battleship.client.gui.VolumeSlider; + import static pp.util.PreferencesUtils.getPreferences; /** diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/NetworkDialog.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/NetworkDialog.java index 2e043c1..d9dde65 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/NetworkDialog.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/NetworkDialog.java @@ -7,24 +7,23 @@ package pp.battleship.client; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.util.concurrent.ExecutionException; +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 pp.battleship.server.BattleshipServer; import pp.dialog.Dialog; import pp.dialog.DialogBuilder; import pp.dialog.SimpleDialog; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -import static pp.battleship.Resources.lookup; - /** * 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. @@ -32,7 +31,7 @@ import static pp.battleship.Resources.lookup; class NetworkDialog extends SimpleDialog { private static final Logger LOGGER = System.getLogger(NetworkDialog.class.getName()); private static final String LOCALHOST = "localhost"; //NON-NLS - private static final String DEFAULT_PORT = "1234"; //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); diff --git a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/VolumeSlider.java b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/VolumeSlider.java index aa27292..70f9097 100644 --- a/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/VolumeSlider.java +++ b/Projekte/battleship/client/src/main/java/pp/battleship/client/gui/VolumeSlider.java @@ -1,6 +1,7 @@ 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, diff --git a/Projekte/battleship/model/src/main/java/pp/battleship/BattleshipConfig.java b/Projekte/battleship/model/src/main/java/pp/battleship/BattleshipConfig.java index f6b4cf2..af4c63d 100644 --- a/Projekte/battleship/model/src/main/java/pp/battleship/BattleshipConfig.java +++ b/Projekte/battleship/model/src/main/java/pp/battleship/BattleshipConfig.java @@ -7,12 +7,11 @@ package pp.battleship; -import pp.util.config.Config; - +import static java.lang.Math.max; import java.util.Map; import java.util.TreeMap; -import static java.lang.Math.max; +import pp.util.config.Config; /** * Provides access to the configuration settings for the Battleship game. @@ -31,7 +30,7 @@ public class BattleshipConfig extends Config { * The default port number for the Battleship server. */ @Property("port") - private int port = 1234; + private int port = 12234; /** * The width of the game map in terms of grid units. diff --git a/Projekte/battleship/server/server.properties b/Projekte/battleship/server/server.properties index 9f168a6..1a03cec 100644 --- a/Projekte/battleship/server/server.properties +++ b/Projekte/battleship/server/server.properties @@ -10,7 +10,7 @@ # This file defines the configuration settings for the Battleship server. # # The port number on which the server will listen for incoming connections. -port=1234 +port=42069 # # The dimensions of the game map. # 'map.width' defines the number of columns, and 'map.height' defines the number of rows. diff --git a/Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy b/Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy index 3ad66c1..398fdf1 100644 --- a/Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy +++ b/Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy @@ -1,6 +1,8 @@ // Styling of Lemur components // For documentation, see: // 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 @@ -19,7 +21,11 @@ def sliderColor = color(0.6, 0.8, 0.8, 1) def sliderBgColor = color(0.5, 0.75, 0.75, 1) def gradientColor = color(0.5, 0.75, 0.85, 0.5) def tabbuttonEnabledColor = color(0.4, 0.45, 0.5, 1) -def solidWhiteBackground = new QuadBackgroundComponent(color(1, 1, 1, 1)) // Solid white +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) + @@ -32,9 +38,10 @@ def gradient = TbtQuadBackgroundComponent.create( def doubleGradient = new QuadBackgroundComponent(gradientColor) doubleGradient.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png", generateMips: false) +//doubleGradient.color = color(0, 0, 0, 1) def orangeBorder = TbtQuadBackgroundComponent.create( - texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png", // Replace with an appropriate texture if needed + texture(name: "/com/simsilica/lemur/icons/border.png", // Replace with an appropriate texture if needed generateMips: false), 1, 1, 1, 126, 126, 1f, false) @@ -49,11 +56,27 @@ selector("label", "pp") { 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-Text", "pp") { + insets = new Insets3f(2, 2, 2, 2) + fontSize = 25 + color = buttonEnabledColor +} + selector("header", "pp") { font = font("Interface/Fonts/Metropolis/Metropolis-Bold-42.fnt") insets = new Insets3f(2, 2, 2, 2) color = color(1, 0.5, 0, 1) textHAlignment = HAlignment.Center + textVAlignment = VAlignment.Center } selector("container", "pp") { @@ -62,20 +85,10 @@ selector("container", "pp") { } selector("toolbar") { - // Set the grey background - background = new QuadBackgroundComponent(greyBackground) + background = gradient.clone() + background.setColor(bgColor) + //color = (new ColorRGBA(0.4157f, 0.4235f, 0.4392f, 1.0f)) - // Add a red border using a TbtQuadBackgroundComponent - def redBorder = TbtQuadBackgroundComponent.create( - texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png", - generateMips: false), - 1, 1, 1, 1, 1, - 1f, false) - redBorder.color = redBorderColor - background = greyBackground - - // Optional: Set padding inside the toolbar - insets = new Insets3f(10, 10, 10, 10) } selector("slider", "pp") { background = gradient.clone() @@ -144,6 +157,8 @@ selector("title", "pp") { background.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png", generateMips: false) insets = new Insets3f(2, 2, 2, 2) + textHAlignment = HAlignment.Center + textVAlignment = VAlignment.Center buttonCommands = stdButtonCommands } @@ -158,6 +173,8 @@ selector("button", "pp") { // 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 } @@ -231,6 +248,47 @@ selector("tab.button", "pp") { buttonCommands = stdButtonCommands } selector("settings-title", "pp") { - fontSize = 48 // Set font size - background = new QuadBackgroundComponent(color(0.4157f, 0.4235f, 0.4392f, 1.0f)) // Grey background - } \ No newline at end of file + 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("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 +} + diff --git a/Projekte/monopoly/client/build.gradle b/Projekte/monopoly/client/build.gradle index 66985e3..85ddd4b 100644 --- a/Projekte/monopoly/client/build.gradle +++ b/Projekte/monopoly/client/build.gradle @@ -9,7 +9,12 @@ dependencies { 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 diff --git a/Projekte/monopoly/client/client.properties b/Projekte/monopoly/client/client.properties index 4403fa3..3e2d911 100644 --- a/Projekte/monopoly/client/client.properties +++ b/Projekte/monopoly/client/client.properties @@ -5,47 +5,13 @@ ## (c) Mark Minas (mark.minas@unibw.de) ######################################## # -# Battleship client configuration -# -# Specifies the map used by the opponent in single mode. -# Single mode is activated if this property is set. -#map.opponent=maps/map2.json -# -# Specifies the map used by the player in single mode. -# The player must define their own map if this property is not set. -map.own=maps/map1.json -# -# Coordinates of the shots fired by the RobotClient in the order listed. -# Example: -# 2, 0,\ -# 2, 1,\ -# 2, 2,\ -# 2, 3 -# defines four shots, namely at the coordinates -# (x=2, y=0), (x=2, y=1), (x=2, y=2), and (x=2, y=3) -robot.targets=2, 0,\ - 2, 1,\ - 2, 2,\ - 2, 3 -# -# Delay in milliseconds between each shot fired by the RobotClient. -robot.delay=500 +# 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 # -# The number of ships of each length available in single mode. -# The value is a comma-separated list where each element corresponds to the number of ships -# with a specific length. For example: -# ship.nums=4, 3, 2, 1 -# This configuration means: -# - 4 ships of length 1 -# - 3 ships of length 2 -# - 2 ships of length 3 -# - 1 ship of length 4 -ship.nums=4, 3, 2, 1 # # Screen settings # diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/GameMusic.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/GameMusic.java new file mode 100644 index 0000000..1e6f94e --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/GameMusic.java @@ -0,0 +1,122 @@ +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 music beeing played. Is able to start and stop the music. Set the Volume of the Audio. + */ +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 music; + + /** + * 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); + } + + /** + * 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); + } + + /** + * 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); + music = loadSound(app, "Sound/background.ogg"); + setVolume(volumeInPreferences()); + music.setLooping(true); + if (isEnabled() && music != null) { + music.play(); + } + } + + /** + * 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; + } + + /** + * 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; + + 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){ + music.setVolume(vol); + PREFERENCES.putFloat(VOLUME_PREF, vol); + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/GameSound.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/GameSound.java index bf5d649..2722afa 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/GameSound.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/GameSound.java @@ -30,10 +30,19 @@ public class GameSound extends AbstractAppState implements GameEventListener { private static final Logger LOGGER = System.getLogger(GameSound.class.getName()); private static final Preferences PREFERENCES = getPreferences(GameSound.class); private static final String ENABLED_PREF = "enabled"; //NON-NLS + private static final String VOLUME_PREF = "volume"; //NON-NLS - private AudioNode splashSound; - private AudioNode shipDestroyedSound; - private AudioNode explosionSound; + 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. @@ -51,6 +60,15 @@ public class GameSound extends AbstractAppState implements GameEventListener { 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)} @@ -75,6 +93,17 @@ public class GameSound extends AbstractAppState implements GameEventListener { @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"); } /** @@ -98,32 +127,116 @@ public class GameSound extends AbstractAppState implements GameEventListener { } /** - * Plays the splash sound effect. + * Plays the passStart sound effect. */ - public void splash() { - if (isEnabled() && splashSound != null) - splashSound.playInstance(); + public void passStart() { + if (isEnabled() && passStartSound != null) + passStartSound.playInstance(); } - /** - * Plays the explosion sound effect. + * Plays the eventCard sound effect. */ - public void explosion() { - if (isEnabled() && explosionSound != null) - explosionSound.playInstance(); + public void eventCard() { + if (isEnabled() && eventCardSound != null) + eventCardSound.playInstance(); } - /** - * Plays sound effect when a ship has been destroyed. + * Plays the gulag sound effect. */ - public void shipDestroyed() { - if (isEnabled() && shipDestroyedSound != null) - shipDestroyedSound.playInstance(); + 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); } @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(); } } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/Menu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/Menu.java deleted file mode 100644 index a9a22e5..0000000 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/Menu.java +++ /dev/null @@ -1,51 +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.util.prefs.Preferences; - -import pp.dialog.Dialog; -import static pp.util.PreferencesUtils.getPreferences; - -/** - * The Menu class represents the main menu in the Battleship game application. - * It extends the Dialog class and provides functionalities for loading, saving, - * returning to the game, and quitting the application. - */ -class Menu extends Dialog { - private static final Preferences PREFERENCES = getPreferences(Menu.class); - private static final String LAST_PATH = "last.file.path"; - private final MonopolyApp app; - - - /** - * Constructs the Menu dialog for the Battleship application. - * - * @param app the BattleshipApp instance - */ - public Menu(MonopolyApp app) { - super(app.getDialogManager()); - this.app = app; - } - - /** - * Updates the state of the load and save buttons based on the game logic. - */ - @Override - public void update() { - - } - - /** - * As an escape action, this method closes the menu if it is the top dialog. - */ - @Override - public void escape() { - close(); - } -} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java index 02f926d..186425a 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java @@ -1,210 +1,506 @@ +//////////////////////////////////////// +// 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.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - +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.Label; import com.simsilica.lemur.style.BaseStyles; - +import pp.monopoly.game.client.MonopolyClient; +import pp.monopoly.client.gui.SettingsMenu; +import pp.monopoly.client.gui.StartMenu; +import pp.monopoly.client.gui.TestWorld; +import pp.monopoly.client.gui.popups.BuildingPropertyCard; +import pp.monopoly.client.gui.popups.BuyCard; +import pp.monopoly.client.gui.popups.EventCard; +import pp.monopoly.client.gui.popups.FoodFieldCard; +import pp.monopoly.client.gui.popups.GateFieldCard; +import pp.monopoly.game.client.ClientGameLogic; +import pp.monopoly.game.client.ServerConnection; +import pp.monopoly.notification.ClientStateEvent; +import pp.monopoly.notification.GameEventListener; +import pp.monopoly.notification.InfoTextEvent; +import pp.monopoly.notification.Sound; +import pp.dialog.Dialog; import pp.dialog.DialogBuilder; import pp.dialog.DialogManager; import pp.graphics.Draw; -import pp.monopoly.client.gui.SettingsMenu; -import pp.monopoly.client.gui.TestWorld; -import pp.monopoly.game.client.ClientGameLogic; -import pp.monopoly.game.client.MonopolyClient; -import pp.monopoly.game.client.ServerConnection; -import pp.monopoly.notification.GameEventListener; -import pp.monopoly.notification.InfoTextEvent; +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 static pp.monopoly.Resources.lookup; + +/** + * The main class for the Battleship client application. + * It manages the initialization, input setup, GUI setup, and game states for the client. + */ public class MonopolyApp extends SimpleApplication implements MonopolyClient, GameEventListener { - private BitmapText topText; - private final ServerConnection serverConnection; - private final ClientGameLogic logic; - private final MonopolyAppConfig config; - private final ActionListener escapeListener = (name, isPressed, tpf) -> handleEscape(isPressed); - private final DialogManager dialogManager = new DialogManager(this); - private final ExecutorService executor = Executors.newCachedThreadPool(); - private final Draw draw; - private SettingsMenu settingsMenu; - private TestWorld testWorld; - private boolean isSettingsMenuOpen = false; - private boolean inputBlocked = false; + + /** + * 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); + + //TODO temp for testing + private EventCard eventCard; + private BuildingPropertyCard buildingProperty; + private FoodFieldCard foodField; + private GateFieldCard gateField; + private BuyCard buyCard; + private boolean isBuyCardPopupOpen = false; + private final ActionListener BListener = (name, isPressed, tpf) -> handleB(isPressed); + private final ActionListener TListener = (name, isPressed, tpf) -> handleT(isPressed); + private TestWorld testWorld; + + 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 Battleship application. + * + * @param args Command-line arguments for launching the application. + */ public static void main(String[] args) { new MonopolyApp().start(); } - public MonopolyApp() { - this.draw = new Draw(assetManager); + /** + * Constructs a new {@code MonopolyApp} instance. + * Initializes the configuration, server connection, and game logic listeners. + */ + private MonopolyApp() { config = new MonopolyAppConfig(); - serverConnection = new NetworkSupport(this); + config.readFromIfExists(CONFIG_FILE); + serverConnection = makeServerConnection(); logic = new ClientGameLogic(serverConnection); logic.addListener(this); setShowSettings(config.getShowSettings()); setSettings(makeSettings()); } - @Override - public MonopolyAppConfig getConfig() { - return config; + /** + * 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; } - private AppSettings makeSettings() { - final AppSettings settings = new AppSettings(true); - settings.setTitle("Monopoly Game"); - settings.setResolution(config.getResolutionWidth(), config.getResolutionHeight()); - settings.setFullscreen(config.fullScreen()); - return settings; + /** + * Returns the current configuration settings for the Battleship client. + * + * @return The {@link BattleshipClientConfig} 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() { - GuiGlobals.initialize(this); - BaseStyles.loadStyleResources(STYLES_SCRIPT); - GuiGlobals.getInstance().getStyles().setDefaultStyle("pp"); //NON-NLS - final BitmapFont normalFont = assetManager.loadFont(FONT); //NON-NLS - + setPauseOnLostFocus(false); + draw = new Draw(assetManager); setupInput(); + setupStates(); setupGui(); - - // Zeige das Startmenü - StartMenu.createStartMenu(this); + new StartMenu(this).open(); } + /** + * Sets up the graphical user interface (GUI) for the application. + */ private void setupGui() { - BitmapFont normalFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); + 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); - topText.setLocalTranslation(10, settings.getHeight() - 10, 0); + 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(true); - inputManager.addMapping("ESC", new KeyTrigger(KeyInput.KEY_ESCAPE)); - inputManager.addListener(escapeListener, "ESC"); + inputManager.setCursorVisible(false); + inputManager.addMapping(ESC, new KeyTrigger(KeyInput.KEY_ESCAPE)); + inputManager.addMapping(CLICK, new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); + inputManager.addListener(escapeListener, ESC); + + //TODO tmp for testing + inputManager.addMapping("B", new KeyTrigger(KeyInput.KEY_B)); + inputManager.addListener(BListener, "B"); + inputManager.addMapping("T", new KeyTrigger(KeyInput.KEY_T)); + inputManager.addListener(TListener, "T"); } - private void handleEscape(boolean isPressed) { + //logik zum wechselnden erscheinen und verschwinden beim drücken von B //TODO süäter entfernen + private void handleB(boolean isPressed) { if (isPressed) { - if (settingsMenu != null && isSettingsMenuOpen) { + Dialog tmp = new BuyCard(this); + if (eventCard != null && isBuyCardPopupOpen) { // Schließe das SettingsMenu - System.out.println("Schließe SettingsMenu..."); - settingsMenu.close(); - settingsMenu = null; - setSettingsMenuOpen(false); + System.out.println("Schließe BuyCardPopup..."); + eventCard.close(); + eventCard = null; + tmp.open(); } else { // Öffne das SettingsMenu - System.out.println("Öffne SettingsMenu..."); - settingsMenu = new SettingsMenu(this); - settingsMenu.open(); - setSettingsMenuOpen(true); + System.out.println("Öffne BuyCardPopup..."); + eventCard = new EventCard(this); + eventCard.open(); + dialogManager.close(tmp); } } } - - - private void blockInputs() { - if (!inputBlocked) { - System.out.println("Blockiere Eingaben..."); - inputManager.setCursorVisible(true); // Cursor sichtbar machen - inputManager.clearMappings(); // Alle Mappings entfernen - inputBlocked = true; - } - } - - public void unblockInputs() { - if (inputBlocked) { - System.out.println("Aktiviere Eingaben..."); - setupInput(); // Standard-Eingaben neu registrieren - inputBlocked = false; + //logik zum wechselnden erscheinen und verschwinden beim drücken von B //TODO süäter entfernen + private void handleT(boolean isPressed) { + if (isPressed) { + testWorld = new TestWorld(this); + testWorld.initializeScene(); } } - public void setInfoText(String text) { - topText.setText(text); + + + /** + * 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(); } + /** + * 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 receivedEvent(InfoTextEvent event) { - setInfoText(event.key()); + public void simpleUpdate(float tpf) { + super.simpleUpdate(tpf); + dialogManager.update(tpf); + logic.update(tpf); + + //TODO testing replace later + if (testWorld != null) { + testWorld.update(tpf); + } } - @Override - public void stop(boolean waitFor) { - if (executor != null) executor.shutdownNow(); - serverConnection.disconnect(); - super.stop(waitFor); - } - - public DialogManager getDialogManager() { - return dialogManager; + /** + * 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; } - public ExecutorService getExecutor() { - return executor; + /** + * 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) { + } + + /** + * 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("Fehler") - .setText(errorMessage) - .setOkButton("OK") - .build() - .open(); + .setTitle(lookup("dialog.error")) + .setText(errorMessage) + .setOkButton(lookup("button.ok"), d -> getGameLogic().playSound(Sound.BUTTON)) + .build() + .open(); } - public void setSettingsMenuOpen(boolean isOpen) { - this.isSettingsMenuOpen = isOpen; - } - - @Override - public void simpleUpdate(float tpf) { - if (testWorld != null) { - testWorld.update(tpf); // Aktualisiere die Kamera in der TestWorld - } - } - - public void startTestWorld() { - guiNode.detachAllChildren(); // Entferne GUI - testWorld = new TestWorld(this); // Erstelle eine Instanz von TestWorld - testWorld.initializeScene(); // Initialisiere die Szene - } - - public void returnToMenu() { - guiNode.detachAllChildren(); // Entferne die GUI - StartMenu.createStartMenu(this); // Zeige das Startmenü erneut + public void disconnect() { + serverConnection.disconnect(); } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/NetworkDialog.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/NetworkDialog.java deleted file mode 100644 index d399636..0000000 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/NetworkDialog.java +++ /dev/null @@ -1,146 +0,0 @@ -package pp.monopoly.client; - -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.concurrent.ExecutionException; -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 pp.dialog.Dialog; -import pp.dialog.DialogBuilder; -import pp.dialog.SimpleDialog; - -/** - * Represents a dialog for setting up a network connection in the Monopoly game. - * Allows users to specify the host and port for connecting to a game server. - */ -class NetworkDialog extends SimpleDialog { - private static final Logger LOGGER = System.getLogger(NetworkDialog.class.getName()); - private static final String LOCALHOST = "localhost"; - private static final String DEFAULT_PORT = "1234"; - private final NetworkSupport network; - private final TextField host = new TextField(LOCALHOST); - private final TextField port = new TextField(DEFAULT_PORT); - private String hostname; - private int portNumber; - private Future connectionFuture; - private Dialog progressDialog; - - /** - * Constructs a new NetworkDialog. - * - * @param network The NetworkSupport instance to be used for network operations. - */ - NetworkDialog(NetworkSupport network) { - super(network.getApp().getDialogManager()); - this.network = network; - initializeDialog(); - } - - /** - * Initializes the dialog with input fields and connection buttons. - */ - private void initializeDialog() { - final MonopolyApp app = network.getApp(); - Container inputContainer = new Container(); - - // Titel und Eingabefelder für Host und Port - inputContainer.addChild(new Label("Server-Adresse")); - inputContainer.addChild(host); - - inputContainer.addChild(new Label("Port")); - inputContainer.addChild(port); - - Button connectButton = inputContainer.addChild(new Button("Verbinden")); - connectButton.addClickCommands(source -> connect()); - - Button cancelButton = inputContainer.addChild(new Button("Abbrechen")); - cancelButton.addClickCommands(source -> app.closeApp()); - - app.getGuiNode().attachChild(inputContainer); - } - - /** - * Initiates the connection attempt based on the entered host and port. - */ - private void connect() { - LOGGER.log(Level.INFO, "Connecting to host={0}, port={1}", host, port); - 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("Port muss eine Zahl sein."); - } - } - - /** - * Opens a progress dialog while connecting. - */ - private void openProgressDialog() { - progressDialog = DialogBuilder.simple(network.getApp().getDialogManager()) - .setText("Verbinde zum Server...") - .build(); - progressDialog.open(); - } - - /** - * Attempts 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); - } - } - - /** - * Updates the connection status and handles completion or failure. - */ - @Override - public void update(float delta) { - if (connectionFuture != null && connectionFuture.isDone()) { - try { - connectionFuture.get(); - onSuccess(); - } catch (ExecutionException e) { - onFailure(e.getCause()); - } catch (InterruptedException e) { - LOGGER.log(Level.WARNING, "Connection interrupted.", e); - Thread.currentThread().interrupt(); - } - } - } - - /** - * Handles a successful connection to the game server. - */ - private void onSuccess() { - connectionFuture = null; - progressDialog.close(); - this.close(); - network.getApp().setInfoText("Warte auf einen Gegner..."); - } - - /** - * Handles a failed connection attempt. - * - * @param e The cause of the failure. - */ - private void onFailure(Throwable e) { - connectionFuture = null; - progressDialog.close(); - network.getApp().errorDialog("Verbindung zum Server fehlgeschlagen."); - network.getApp().setInfoText(e.getLocalizedMessage()); - } - -} \ No newline at end of file diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/NetworkSupport.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/NetworkSupport.java index 3ea11b0..da9198f 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/NetworkSupport.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/NetworkSupport.java @@ -1,8 +1,11 @@ -package pp.monopoly.client; +//////////////////////////////////////// +// Programming project code +// UniBw M, 2022, 2023, 2024 +// www.unibw.de/inf2 +// (c) Mark Minas (mark.minas@unibw.de) +//////////////////////////////////////// -import java.io.IOException; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; +package pp.monopoly.client; import com.jme3.network.Client; import com.jme3.network.ClientStateListener; @@ -10,34 +13,50 @@ 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. + * Manages the network connection for the Battleship application. * Handles connecting to and disconnecting from the server, and sending messages. */ -class NetworkSupport implements MessageListener, ClientStateListener, ServerConnection { +public class NetworkSupport implements MessageListener, 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 Monopoly application. + * Constructs a NetworkSupport instance for the given Battleship application. * - * @param app The Monopoly application instance. + * @param app The Battleship application instance. */ public NetworkSupport(MonopolyApp app) { this.app = app; } /** - * Returns the Monopoly application instance. - * - * @return Monopoly application instance + * Return the client connections Id + * @return the client id */ - MonopolyApp getApp() { + public int getId() { + if (client == null) return 0; + return client.getId(); + } + + /** + * Returns the Battleship application instance. + * + * @return Battleship application instance + */ + public MonopolyApp getApp() { return app; } @@ -57,9 +76,8 @@ class NetworkSupport implements MessageListener, ClientStateListener, Se */ @Override public void connect() { - if (client == null) { - new NetworkDialog(this).open(); - } + if (client == null) + new CreateGameMenu(this).open(); } /** @@ -70,7 +88,7 @@ class NetworkSupport implements MessageListener, ClientStateListener, Se if (client == null) return; client.close(); client = null; - LOGGER.log(Level.INFO, "Client connection closed."); + LOGGER.log(Level.INFO, "client closed"); //NON-NLS } /** @@ -80,10 +98,9 @@ class NetworkSupport implements MessageListener, ClientStateListener, Se * @param port The server's port. * @throws IOException If an I/O error occurs when creating the client. */ - void initNetwork(String host, int port) throws IOException { - if (client != null) { - throw new IllegalStateException("Already connected to the game server."); - } + 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); @@ -98,10 +115,9 @@ class NetworkSupport implements MessageListener, ClientStateListener, Se */ @Override public void messageReceived(Client client, Message message) { - LOGGER.log(Level.INFO, "Message received from server: {0}", message); - if (message instanceof ServerMessage serverMessage) { + LOGGER.log(Level.INFO, "message received from server: {0}", message); //NON-NLS + if (message instanceof ServerMessage serverMessage) app.enqueue(() -> serverMessage.accept(app.getGameLogic())); - } } /** @@ -111,7 +127,7 @@ class NetworkSupport implements MessageListener, ClientStateListener, Se */ @Override public void clientConnected(Client client) { - LOGGER.log(Level.INFO, "Successfully connected to server: {0}", client); + LOGGER.log(Level.INFO, "Client connected: {0}", client); //NON-NLS } /** @@ -122,9 +138,13 @@ class NetworkSupport implements MessageListener, ClientStateListener, Se */ @Override public void clientDisconnected(Client client, DisconnectInfo disconnectInfo) { - LOGGER.log(Level.INFO, "Disconnected from server: {0}", 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; - app.enqueue(() -> app.setInfoText("Verbindung zum Server verloren.")); + disconnect(); + app.enqueue(() -> app.setInfoText(lookup("lost.connection.to.server"))); } /** @@ -134,11 +154,10 @@ class NetworkSupport implements MessageListener, ClientStateListener, Se */ @Override public void send(ClientMessage message) { - LOGGER.log(Level.INFO, "Sending message to server: {0}", message); - if (client == null) { - app.errorDialog("Verbindung zum Server verloren."); - } else { + LOGGER.log(Level.INFO, "sending {0}", message); //NON-NLS + if (client == null) + app.errorDialog(lookup("lost.connection.to.server")); + else client.send(message); - } } -} \ No newline at end of file +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CameraController.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CameraController.java index 314ebb6..a2d04cf 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CameraController.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CameraController.java @@ -39,21 +39,22 @@ public class CameraController { * @param tpf Zeit pro Frame */ public void update(float tpf) { - // Aktualisiere den Winkel basierend auf der Geschwindigkeit - angle += speed * tpf; - if (angle >= FastMath.TWO_PI) { - angle -= FastMath.TWO_PI; // Winkel zurücksetzen, um Überläufe zu vermeiden - } - - // Berechne die neue Position der Kamera - float x = center.x + radius * FastMath.cos(angle); - float z = center.z + radius * FastMath.sin(angle); - float y = center.y + height; - - // Setze die Kameraposition - camera.setLocation(new Vector3f(x, y, z)); - - // Lasse die Kamera auf den Fokuspunkt blicken camera.lookAt(center, Vector3f.UNIT_Y); } + + public void setPosition(int fieldID) { + camera.setLocation(fieldIdToVector(fieldID)); + } + + public void setPosition(float x, float y) { + camera.setLocation(new Vector3f(x,height,y)); + } + + 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(); + } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/ChoosePartner.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/ChoosePartner.java new file mode 100644 index 0000000..709bb32 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/ChoosePartner.java @@ -0,0 +1,64 @@ +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.Axis; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.TextField; +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; + +public class ChoosePartner extends Dialog { + + private final MonopolyApp app; + private final Container menuContainer; + private Geometry background; + + + public ChoosePartner(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + + // 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("Wähle deinen Handelspartner:", new ElementId("label-Bold"))); + title.setFontSize(40); + + } + + /** + * Lädt das Hintergrundbild und fügt es als geometrische Ebene hinzu. + */ + 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, -1); // Hintergrundebene + + app.getGuiNode().attachChild(background); + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java index 5472da4..82cf38a 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java @@ -1,121 +1,226 @@ +//////////////////////////////////////// +// 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.Axis; 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.StartMenu; +import pp.monopoly.client.NetworkSupport; +import pp.monopoly.notification.Sound; +import pp.monopoly.server.MonopolyServer; +import pp.dialog.Dialog; +import pp.dialog.DialogBuilder; /** - * CreateGameMenu class represents the menu for creating a new game. + * 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 { - private final MonopolyApp app; - private final Container menuContainer; - private Geometry background; +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 connectionFuture; + private Dialog progressDialog; /** - * Konstruktor für das CreateGameMenu. + * Constructs a new CreateGameMenu. * - * @param app Die Hauptanwendung (MonopolyApp) + * @param network The NetworkSupport instance to be used for network operations. */ - public CreateGameMenu(MonopolyApp app) { - this.app = app; + public CreateGameMenu(NetworkSupport network) { + super(network.getApp().getDialogManager()); + this.network = network; + host.setSingleLine(true); + host.setPreferredWidth(400f); + port.setSingleLine(true); - // Hintergrundbild laden und hinzufügen - addBackgroundImage(); + final MonopolyApp app = network.getApp(); - // Hauptcontainer für das Menü - menuContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X)); - menuContainer.setPreferredSize(new Vector3f(600, 400, 0)); // Feste Größe des Containers + int screenWidth = app.getContext().getSettings().getWidth(); + int screenHeight = app.getContext().getSettings().getHeight(); - // Titel - Label title = menuContainer.addChild(new Label("Neues Spiel")); - title.setFontSize(48); - - // Eingabefelder-Container - Container inputContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X))); - inputContainer.setPreferredSize(new Vector3f(200, 150, 0)); // Eingabefelder nicht ganz so breit - inputContainer.setLocalTranslation(20, 0, 0); // Abstand vom Rand - - inputContainer.addChild(new Label("Server-Adresse:")); - TextField serverAddressField = inputContainer.addChild(new TextField("localhost")); - serverAddressField.setPreferredWidth(400); // Breite des Textfelds - - inputContainer.addChild(new Label("Port:")); - TextField portField = inputContainer.addChild(new TextField("42069")); - portField.setPreferredWidth(400); // Breite des Textfelds - - // Button-Container - Container buttonContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y))); - buttonContainer.setPreferredSize(new Vector3f(400, 50, 0)); - buttonContainer.setLocalTranslation(20, 0, 0); // Abstand vom Rand - - // "Abbrechen"-Button - Button cancelButton = buttonContainer.addChild(new Button("Abbrechen")); - cancelButton.setPreferredSize(new Vector3f(120, 40, 0)); - cancelButton.addClickCommands(source -> goBackToStartMenu()); - - // "Spiel hosten"-Button - Button hostButton = buttonContainer.addChild(new Button("Spiel hosten")); - hostButton.setPreferredSize(new Vector3f(120, 40, 0)); - hostButton.addClickCommands(source -> { - closeCreateGameMenu(); // Schließt das Menü - app.startTestWorld(); // Starte die TestWorld im selben Fenster - }); - - // "Beitreten"-Button - Button joinButton = buttonContainer.addChild(new Button("Beitreten")); - joinButton.setPreferredSize(new Vector3f(120, 40, 0)); - // Placeholder für die Beitrittslogik - - // 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); - } - - /** - * Lädt das Hintergrundbild und fügt es als geometrische Ebene hinzu. - */ - private void addBackgroundImage() { + // Set up the background image Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png"); - Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); - background = new Geometry("Background", quad); + 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); // Hintergrundebene - + 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); } /** - * Geht zum Startmenü zurück, wenn "Abbrechen" angeklickt wird. + * 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 goBackToStartMenu() { - closeCreateGameMenu(); // Schließt das Menü - StartMenu.createStartMenu(app); // Zeige das Startmenü + 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")); + } } /** - * Entfernt das CreateGameMenu und dessen Hintergrund. + * Creates a dialog indicating that the connection is in progress. */ - private void closeCreateGameMenu() { - app.getGuiNode().detachChild(menuContainer); // Entfernt den Menü-Container - app.getGuiNode().detachChild(background); // Entfernt das Hintergrundbild + 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(); } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameMenu.java deleted file mode 100644 index da390c8..0000000 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameMenu.java +++ /dev/null @@ -1,51 +0,0 @@ -package pp.monopoly.client.gui; - - -import com.jme3.math.ColorRGBA; -import com.simsilica.lemur.Button; -import com.simsilica.lemur.Label; -import com.simsilica.lemur.style.ElementId; - -import pp.dialog.Dialog; -import pp.monopoly.client.MonopolyApp; - -public class GameMenu extends Dialog { - private final MonopolyApp app; - - /** - * Constructs the SettingsMenu dialog for the Monopoly application. - * - * @param app the MonopolyApp instance - */ - public GameMenu(MonopolyApp app) { - super(app.getDialogManager()); - this.app = app; - - // Add a title label for Settings - Label settingsTitle = new Label("Einstellungen", new ElementId("settings-title")); - settingsTitle.setFontSize(48); // Set font size for the title - settingsTitle.setColor(ColorRGBA.White); - - // Add any settings-related components here, such as volume control, toggles, etc. - - // Add a back button to return to StartMenu - Button backButton = new Button("Zurück", new ElementId("menu-button")); - backButton.setColor(ColorRGBA.White); - backButton.setFontSize(24); - backButton.addClickCommands(source -> returnToStartMenu()); - - // Add components to this dialog - addChild(settingsTitle); - addChild(backButton); - - // You can add more settings components here, like checkboxes or sliders. - } - - /** - * Returns to the StartMenu when the back button is clicked. - */ - private void returnToStartMenu() { - app.getDialogManager().close(this); // Close the current settings dialog - //TODO return zum Ausgangsmenü - } -} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/LobbyMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/LobbyMenu.java new file mode 100644 index 0000000..9e60d9f --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/LobbyMenu.java @@ -0,0 +1,296 @@ +package pp.monopoly.client.gui; + +import com.jme3.app.Application; +import com.jme3.app.state.BaseAppState; +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.MonopolyApp; +import pp.monopoly.message.client.PlayerReady; +import pp.monopoly.notification.Sound; + +import java.util.Set; + +public class LobbyMenu extends Dialog { + + private final MonopolyApp app; + private final Container menuContainer; + private Geometry background; + private Geometry circle; + private Container lowerLeftMenu; + private Container lowerRightMenu; + + private TextField playerInputField = new TextField("Spieler 1"); + private TextField startingCapital = new TextField("15000"); + private String figure; + + public LobbyMenu(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + + // 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.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 figures = new VersionedList<>(); + figures.add("Laptop"); + figures.add("Flugzeug"); + figures.add("Jägermeister"); + figures.add("Katze"); + figures.add("OOP"); + figures.add("Handyholster"); + + Selector figureDropdown = new Selector<>(figures, "glass"); + figureDropdown.setBackground(new QuadBackgroundComponent(ColorRGBA.DarkGray)); + figureDropdown.setPreferredSize(new Vector3f(100, 20, 0)); + figureDropdownContainer.addChild(figureDropdown); + + addSelectionActionListener(figureDropdown, this::onDropdownSelectionChanged); + + 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(() -> { + toggleReady(); + app.getGameLogic().playSound(Sound.BUTTON); + })); + 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( ColorRGBA.Red); // 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); + } + + + /** + * Lädt das Hintergrundbild und fügt es als geometrische Ebene hinzu. + */ + 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); + } + + private Geometry createCircle(ColorRGBA color) { + + 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", color); // Set the desired color + circleGeometry.setMaterial(material); + + return circleGeometry; + } + + /** + * Schaltet den "Bereit"-Status um. + */ + private void toggleReady() { + app.getGameLogic().send(new PlayerReady(true, playerInputField.getText(), figure, Integer.parseInt(startingCapital.getText()))); + } + + @Override + public void escape() { + new SettingsMenu(app).open(); + } + + /** + * Adds a custom action listener to the Selector. + */ + private void addSelectionActionListener(Selector selector, SelectionActionListener listener) { + VersionedReference> selectionRef = selector.getSelectionModel().createReference(); + + app.getStateManager().attach(new BaseAppState() { + @Override + public void update(float tpf) { + if (selectionRef.update()) { + String selected = selectionRef.get().toString(); + System.out.println(selected); + listener.onSelectionChanged(selected); + } + } + + @Override + protected void initialize(Application app) { + } + + @Override + protected void cleanup(Application app) { + } + + @Override + protected void onEnable() { + + } + + @Override + protected void onDisable() { + + } + }); + } + + /** + * Callback for when the dropdown selection changes. + */ + private void onDropdownSelectionChanged(String selected) { + System.out.println("Selected: " + selected); + app.getGameLogic().playSound(Sound.BUTTON); + switch (selected) { + case "[0]": + figure = "Laptop"; + break; + case "[1]": + figure = "Flugzeug"; + break; + case "[2]": + figure = "Jägermeister"; + break; + case "[3]": + figure = "Katze"; + break; + case "[4]": + figure = "OOP"; + break; + case "[5]": + figure = "Handyholster"; + break; + default: + break; + } + } + + /** + * Functional interface for a selection action listener. + */ + @FunctionalInterface + private interface SelectionActionListener { + void onSelectionChanged(T selection); + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/MapView.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/MapView.java deleted file mode 100644 index f93c1d5..0000000 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/MapView.java +++ /dev/null @@ -1,75 +0,0 @@ -package pp.monopoly.client.gui; - -import com.jme3.material.Material; -import com.jme3.material.RenderState.BlendMode; -import com.jme3.math.ColorRGBA; -import com.jme3.scene.Geometry; -import com.jme3.scene.Node; -import com.jme3.scene.Spatial.CullHint; -import com.jme3.scene.shape.Quad; - -import pp.monopoly.client.MonopolyApp; -import pp.monopoly.model.Board; - -/** - * Represents the visual view of a {@link Board}, used to display the map structure and elements. - * This class handles the graphical representation of the board, including background setup and grid lines. - */ -class MapView { - private static final float FIELD_SIZE = 40f; - private static final float BACKGROUND_DEPTH = -4f; - private static final ColorRGBA BACKGROUND_COLOR = new ColorRGBA(0, 0.05f, 0.05f, 0.5f); - - private final MonopolyApp app; - private final Node mapNode = new Node("map"); - private final Board board; - private final MapViewSynchronizer synchronizer; - - /** - * Constructs a new MapView for a given {@link Board} and {@link MonopolyApp}. - * - * @param board the board to visualize - * @param app the main application instance - */ - MapView(Board board, MonopolyApp app) { - this.board = board; - this.app = app; - this.synchronizer = new MapViewSynchronizer(this); - setupBackground(); - app.getGameLogic().addListener(synchronizer); - } - - /** - * Unregisters the {@link MapViewSynchronizer} from listening to board changes. - */ - void unregister() { - app.getGameLogic().removeListener(synchronizer); - } - - /** - * Sets up the background of the map view using a quad geometry. - */ - private void setupBackground() { - Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setColor("Color", BACKGROUND_COLOR); - mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); - Geometry background = new Geometry("MapBackground", new Quad(board.getWidth() * FIELD_SIZE, board.getHeight() * FIELD_SIZE)); - background.setMaterial(mat); - background.setLocalTranslation(0f, 1f, BACKGROUND_DEPTH); - background.setCullHint(CullHint.Never); - mapNode.attachChild(background); - } - - /** - * Gets the root node containing all visual elements in this map view. - * - * @return the root node for the map view - */ - public Node getNode() { - return mapNode; - } - - public Board getBoard() { - return board; - } -} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/MapViewSynchronizer.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/MapViewSynchronizer.java deleted file mode 100644 index 4853356..0000000 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/MapViewSynchronizer.java +++ /dev/null @@ -1,45 +0,0 @@ -package pp.monopoly.client.gui; - -import com.jme3.scene.Spatial; - -import pp.monopoly.model.Figure; - -/** - * Synchronizes the visual representation of the board with the game model. - * Handles updates for items on the board. - */ -class MapViewSynchronizer extends BoardSynchronizer { - private final MapView view; - - /** - * Constructs a new MapViewSynchronizer for the given MapView. - * - * @param view the MapView to synchronize with the game model - */ - public MapViewSynchronizer(MapView view) { - super(view.getBoard(), view.getNode()); - this.view = view; - addExisting(); - } - - /** - * Enables the state by performing initial setup, such as adding any items to the view. - */ - - protected void enableState() { - // Platz für zusätzliche Initialisierungen - } - - /** - * Disables the state by clearing the view. - */ - - protected void disableState() { - view.getNode().detachAllChildren(); // Entfernt alle visuellen Elemente vom Knoten - } - - - public Spatial visit(Figure figure) { - return figure.accept(this); - } -} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SettingsMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SettingsMenu.java index 6997c45..f0faeb5 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SettingsMenu.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SettingsMenu.java @@ -1,100 +1,92 @@ +//////////////////////////////////////// +// 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 com.jme3.material.Material; -import com.jme3.material.RenderState.BlendMode; -import com.jme3.math.ColorRGBA; -import com.jme3.scene.Geometry; -import com.jme3.scene.shape.Quad; +import java.util.prefs.Preferences; + import com.simsilica.lemur.Button; import com.simsilica.lemur.Checkbox; -import com.simsilica.lemur.Container; import com.simsilica.lemur.Label; -import com.simsilica.lemur.Slider; -import com.simsilica.lemur.component.QuadBackgroundComponent; import com.simsilica.lemur.style.ElementId; -import pp.dialog.Dialog; +import static pp.monopoly.Resources.lookup; +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; /** - * SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann. + * The Menu class represents the main menu in the Battleship game application. + * It extends the Dialog class and provides functionalities for loading, saving, + * returning to the game, and quitting the application. */ public class SettingsMenu extends Dialog { + private static final Preferences PREFERENCES = getPreferences(SettingsMenu.class); + private static final String LAST_PATH = "last.file.path"; private final MonopolyApp app; - private final Geometry overlayBackground; - private final Container settingsContainer; + private final VolumeSlider musicSlider; + 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 - // Halbtransparentes Overlay hinzufügen - overlayBackground = createOverlayBackground(); - app.getGuiNode().attachChild(overlayBackground); + addChild(soundSlider); - // Hauptcontainer für das Menü - settingsContainer = new Container(); - settingsContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f))); + 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); - // Titel - Label settingsTitle = settingsContainer.addChild(new Label("Einstellungen", new ElementId("settings-title"))); - settingsTitle.setFontSize(48); - - // Effekt-Sound: Slider und Checkbox - Container effectSoundContainer = settingsContainer.addChild(new Container()); - effectSoundContainer.addChild(new Label("Effekt Sound", new ElementId("label"))); - effectSoundContainer.addChild(new Slider()); - effectSoundContainer.addChild(new Checkbox("Aktivieren")).setChecked(true); - - // Hintergrundmusik: Slider und Checkbox - Container backgroundMusicContainer = settingsContainer.addChild(new Container()); - backgroundMusicContainer.addChild(new Label("Hintergrund Musik", new ElementId("label"))); - backgroundMusicContainer.addChild(new Slider()); - backgroundMusicContainer.addChild(new Checkbox("Aktivieren")).setChecked(true); - - // Beenden-Button - Button quitButton = settingsContainer.addChild(new Button("Beenden", new ElementId("menu-button"))); - quitButton.setFontSize(32); - quitButton.addClickCommands(source -> app.stop()); - - // Zentriere das Menü - settingsContainer.setLocalTranslation( - (app.getCamera().getWidth() - settingsContainer.getPreferredSize().x) / 2, - (app.getCamera().getHeight() + settingsContainer.getPreferredSize().y) / 2, - 1 - ); - - app.getGuiNode().attachChild(settingsContainer); + 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(); } /** - * Erstellt einen halbtransparenten Hintergrund für das Menü. - * - * @return Geometrie des Overlays - */ - private Geometry createOverlayBackground() { - Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); - Geometry overlay = new Geometry("Overlay", quad); - Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent - material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); - overlay.setMaterial(material); - overlay.setLocalTranslation(0, 0, 0); - return overlay; - } - - /** - * Schließt das Menü und entfernt die GUI-Elemente. + * Updates the state of the load and save buttons based on the game logic. */ @Override - public void close() { - System.out.println("Schließe SettingsMenu..."); // Debugging-Ausgabe - app.getGuiNode().detachChild(settingsContainer); // Entferne das Menü - app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay - app.setSettingsMenuOpen(false); // Menü als geschlossen markieren - app.unblockInputs(); // Eingaben wieder aktivieren - System.out.println("SettingsMenu geschlossen."); // Debugging-Ausgabe + 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(); + } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SoundSlider.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SoundSlider.java new file mode 100644 index 0000000..31c547e --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SoundSlider.java @@ -0,0 +1,31 @@ +package pp.monopoly.client.gui; + +import com.simsilica.lemur.Slider; +import pp.monopoly.client.GameSound; + +public class SoundSlider extends Slider { + + private final pp.monopoly.client.GameSound sound; + 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); + } + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/StartMenu.java similarity index 63% rename from Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java rename to Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/StartMenu.java index e1796cc..8a9c532 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/StartMenu.java @@ -1,7 +1,6 @@ -package pp.monopoly.client; +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; @@ -14,8 +13,8 @@ import com.simsilica.lemur.component.QuadBackgroundComponent; import com.simsilica.lemur.component.SpringGridLayout; import pp.dialog.Dialog; -import pp.monopoly.client.gui.CreateGameMenu; -import pp.monopoly.client.gui.SettingsMenu; +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.notification.Sound; /** * Constructs the startup menu dialog for the Monopoly application. @@ -23,8 +22,6 @@ import pp.monopoly.client.gui.GameMenu; */ public class StartMenu extends Dialog { private final MonopolyApp app; - private Container logoContainer; - private Container unibwLogoContainer; /** * Constructs the Startup Menu dialog for the Monopoly application. @@ -34,13 +31,7 @@ public class StartMenu extends Dialog { public StartMenu(MonopolyApp app) { super(app.getDialogManager()); this.app = app; - } - - /** - * Creates and displays the Start Menu with buttons for starting the game, - * opening settings, and quitting the application. - */ - public static void createStartMenu(MonopolyApp app) { + int screenWidth = app.getContext().getSettings().getWidth(); int screenHeight = app.getContext().getSettings().getHeight(); @@ -54,9 +45,6 @@ public class StartMenu extends Dialog { background.setLocalTranslation(0, 0, -1); // Ensure it is behind other GUI elements app.getGuiNode().attachChild(background); - createMonopolyLogo(app); - createUnibwLogo(app); - // Center container for title and play button Container centerMenu = new Container(new SpringGridLayout(Axis.Y, Axis.X)); @@ -65,7 +53,11 @@ public class StartMenu extends Dialog { startButton.setFontSize(40); // Set the font size for the button text startButton.setTextHAlignment(HAlignment.Center); // Center the text horizontally - startButton.addClickCommands(source -> startGame(app)); + startButton.addClickCommands(s -> ifTopDialog(() -> { + this.close(); // Close the StartMenu dialog + app.connect(); // Perform the connection logic + app.getGameLogic().playSound(Sound.BUTTON); + })); centerMenu.addChild(startButton); // Position the center container in the middle of the screen @@ -74,34 +66,6 @@ public class StartMenu extends Dialog { 0)); app.getGuiNode().attachChild(centerMenu); - // Lower-left container for "Spiel beenden" button - Container lowerLeftMenu = new Container(); - lowerLeftMenu.setLocalTranslation(new Vector3f(100, 90, 0)); - Button quitButton = new Button("Spiel beenden"); - quitButton.setPreferredSize(new Vector3f(130, 40, 0)); // Increase button size slightly (width, height) - quitButton.setFontSize(18); - quitButton.addClickCommands(source -> quitGame()); - lowerLeftMenu.addChild(quitButton); - app.getGuiNode().attachChild(lowerLeftMenu); - - // Lower-right container for "Einstellungen" button - Container lowerRightMenu = new Container(); - lowerRightMenu.setLocalTranslation(new Vector3f(screenWidth - 200, 90, 0)); - Button settingsButton = new Button("Einstellungen"); - settingsButton.setPreferredSize(new Vector3f(130, 40, 0)); // Increase button size slightly (width, height) - settingsButton.setFontSize(18); // Increase the font size for the text - settingsButton.addClickCommands(source -> openSettings(app)); - lowerRightMenu.addChild(settingsButton); - app.getGuiNode().attachChild(lowerRightMenu); - } - - /** - * Creates and positions the Monopoly logo container in the center of the screen. - */ - private static void createMonopolyLogo(MonopolyApp app) { - int screenWidth = app.getContext().getSettings().getWidth(); - int screenHeight = app.getContext().getSettings().getHeight(); - // Load the Monopoly logo as a texture Texture logoTexture = app.getAssetManager().loadTexture("Pictures/logo-monopoly.png"); @@ -124,14 +88,6 @@ public class StartMenu extends Dialog { // Attach the container to the GUI node app.getGuiNode().attachChild(logoContainer); - } - - /** - * Creates and positions the Unibw logo container in the center of the screen. - */ - private static void createUnibwLogo(MonopolyApp app) { - int screenWidth = app.getContext().getSettings().getWidth(); - int screenHeight = app.getContext().getSettings().getHeight(); // Load the Unibw logo as a texture Texture unibwTexture = app.getAssetManager().loadTexture("Pictures/logo-unibw.png"); @@ -157,26 +113,14 @@ public class StartMenu extends Dialog { app.getGuiNode().attachChild(unibwContainer); } - /** - * Starts the game by transitioning to the CreateGameMenu. - */ - private static void startGame(MonopolyApp app) { - app.getGuiNode().detachAllChildren(); - new CreateGameMenu(app); + @Override + public void escape() { + new SettingsMenu(app).open(); } - /** - * Opens the settings menu. - */ - private static void openSettings(MonopolyApp app) { + @Override + public void close() { app.getGuiNode().detachAllChildren(); - new SettingsMenu(app); - } - - /** - * Quits the game application. - */ - private static void quitGame() { - System.exit(0); + super.close(); } } \ No newline at end of file diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/TestWorld.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/TestWorld.java index 50a2a23..41d35bb 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/TestWorld.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/TestWorld.java @@ -1,5 +1,9 @@ package pp.monopoly.client.gui; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + import com.jme3.material.Material; import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; @@ -17,7 +21,6 @@ public class TestWorld { private final MonopolyApp app; private CameraController cameraController; // Steuert die Kamera - private Geometry cube; // Spielfigur /** * Konstruktor für TestWorld. @@ -37,21 +40,23 @@ public class TestWorld { setSkyColor(); // Setze den Himmel auf hellblau createBoard(); // Erstelle das Spielfeld - createCube(); // Füge den Würfel hinzu // Erstelle den CameraController cameraController = new CameraController( app.getCamera(), // Die Kamera der App Vector3f.ZERO, // Fokus auf die Mitte des Spielfelds - 5, // Radius des Kreises - 3, // Höhe der Kamera - 0.5f // Geschwindigkeit der Bewegung + 4, // Radius des Kreises + 15, // Höhe der Kamera + 0 // Geschwindigkeit der Bewegung ); // Füge die Toolbar hinzu - new Toolbar(app, cube); + new Toolbar(app).open(); + + cameraController.setPosition(0); } + /** * Aktualisiert die Kameraposition. * @@ -75,33 +80,15 @@ public class TestWorld { */ private void createBoard() { // Erstelle ein Quadrat - Box box = new Box(1, 0.01f, 1); // Dünnes Quadrat für die Textur + Box box = new Box(10, 0.1f, 10); // Dünnes Quadrat für die Textur Geometry geom = new Geometry("Board", box); // Setze das Material mit Textur Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - Texture texture = app.getAssetManager().loadTexture("Pictures/board.png"); + Texture texture = app.getAssetManager().loadTexture("Pictures/board2.png"); mat.setTexture("ColorMap", texture); geom.setMaterial(mat); app.getRootNode().attachChild(geom); } - - /** - * Erstellt den Würfel (Spielfigur) in der Szene. - */ - private void createCube() { - Box box = new Box(0.05f, 0.05f, 0.05f); // Kleinere Größe für Spielfigur - cube = new Geometry("Cube", box); - - // Setze das Material für den Würfel - Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - mat.setColor("Color", ColorRGBA.Blue); // Blau gefärbter Würfel - cube.setMaterial(mat); - - // Setze den Startpunkt des Würfels - cube.setLocalTranslation(0.8999999f, 0.1f, -0.9f); - - app.getRootNode().attachChild(cube); - } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar.java index 5a9a3f0..bd06f5e 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar.java @@ -1,26 +1,31 @@ + package pp.monopoly.client.gui; import java.util.Random; import com.jme3.font.BitmapText; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector2f; import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.simsilica.lemur.Axis; -import com.simsilica.lemur.Button; -import com.simsilica.lemur.Container; +import com.simsilica.lemur.*; +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.notification.Sound; /** * Toolbar Klasse, die am unteren Rand der Szene angezeigt wird. * Die Buttons bewegen den Würfel auf dem Spielfeld. */ -public class Toolbar { +public class Toolbar extends Dialog { private final MonopolyApp app; private final Container toolbarContainer; - private final Geometry cube; // Referenz auf den Würfel private final BitmapText positionText; // Anzeige für die aktuelle Position private final float boardLimit = 0.95f; // Grenzen des Bretts private final float stepSize = 0.18f; // Schrittgröße pro Bewegung @@ -32,25 +37,115 @@ public class Toolbar { * Konstruktor für die Toolbar. * * @param app Die Hauptanwendung (MonopolyApp) - * @param cube Der Würfel, der bewegt werden soll */ - public Toolbar(MonopolyApp app, Geometry cube) { + public Toolbar(MonopolyApp app) { + super(app.getDialogManager()); this.app = app; - this.cube = cube; // Erstelle die Toolbar - toolbarContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y)); + toolbarContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y), "toolbar"); // Setze die Position am unteren Rand und die Breite toolbarContainer.setLocalTranslation( 0, // Links bündig - 100, // Höhe über dem unteren Rand + 200, // Höhe über dem unteren Rand 0 // Z-Ebene ); - toolbarContainer.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 100, 0)); // Volle Breite + toolbarContainer.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 200, 0)); // Volle Breite + // Füge Buttons zur Toolbar hinzu - initializeButtons(); + //initializeButtons(); + + + // Menü-Container: Ein Nested-Container für Kontostand und "Meine Gulag Frei Karten" + Container accountContainer = toolbarContainer.addChild(new Container()); + accountContainer.addChild(new Label("Kontostand", new ElementId("label-Bold"))); + accountContainer.addChild(new Label("6666€", new ElementId("label-Text"))); //TODO Variable hier einsetzen + accountContainer.addChild(new Label("Gulag Frei Karten", new ElementId("label-Bold"))); + accountContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); + + // Add a spacer between accountContainer and overviewContainer + Panel spacer = new Panel(); // Create an empty panel as a spacer + spacer.setPreferredSize(new Vector3f(5, 0, 0)); // Adjust the width as needed + spacer.setBackground(null); + toolbarContainer.addChild(spacer); + + // Menü-Container: Ein Container für Übersicht + Container overviewContainer = toolbarContainer.addChild(new Container()); + overviewContainer.addChild(new Label("Übersicht", new ElementId("label-Bold"))); + overviewContainer.addChild(new Label("„Spieler 1“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen + overviewContainer.addChild(new Label("„Spieler 2“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen + overviewContainer.addChild(new Label("„Spieler 3“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen + overviewContainer.addChild(new Label("„Spieler 4“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen + overviewContainer.addChild(new Label("„Spieler 5“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen + overviewContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); + + // Menü-Container: Ein Container für Würfel + Container diceContainer = toolbarContainer.addChild(new Container()); + diceContainer.setLayout(new SpringGridLayout(Axis.X, Axis.Y)); + + // Create a horizontal container to align leftContainer and rightContainer side by side + Container horizontalContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y)); + horizontalContainer.setPreferredSize(new Vector3f(200, 150, 0)); // Adjust size as needed + + // Create the first container (leftContainer) + Container leftContainer = new Container(); + leftContainer.setPreferredSize(new Vector3f(100, 150, 0)); // Adjust size as needed + + Label imageLabel = new Label(""); + IconComponent icon = new IconComponent("Pictures/dice/one.png"); // Icon mit Textur erstellen + icon.setIconSize(new Vector2f(100,100)); // Skalierung des Bildes + imageLabel.setIcon(icon); + + Label imageLabel2 = new Label(""); + IconComponent icon2 = new IconComponent("Pictures/dice/two.png"); // Icon mit Textur erstellen + icon2.setIconSize(new Vector2f(100,100)); // Skalierung des Bildes + imageLabel2.setIcon(icon2); + + // Create the second container (rightContainer) + Container rightContainer = new Container(); + rightContainer.setPreferredSize(new Vector3f(100, 150, 0)); // Adjust size as needed + leftContainer.setBackground(null); + rightContainer.setBackground(null); + diceContainer.setBackground(null); + horizontalContainer.setBackground(null); + + imageLabel.setTextVAlignment(VAlignment.Center); + imageLabel.setTextHAlignment(HAlignment.Center); + imageLabel2.setTextVAlignment(VAlignment.Center); + imageLabel2.setTextHAlignment(HAlignment.Center); + + leftContainer.addChild(imageLabel); + rightContainer.addChild(imageLabel2); + + + + // Add leftContainer and rightContainer to the horizontal container + horizontalContainer.addChild(leftContainer); + horizontalContainer.addChild(rightContainer); + + // Add the horizontalContainer to the diceContainer (top section) + diceContainer.addChild(horizontalContainer); + + // Add the Würfeln button directly below the horizontalContainer + Button diceButton = new Button("Würfeln"); + diceButton.setPreferredSize(new Vector3f(200, 50, 0)); // Full width for Würfeln button + diceButton.addClickCommands(s -> ifTopDialog(() -> { + rollDice(); + app.getGameLogic().playSound(Sound.BUTTON); + })); + diceContainer.addChild(diceButton); + + + + + // Menü-Container: Ein Nested-Container für Handeln, Grundstücke und Zug beenden + Container menuContainer = toolbarContainer.addChild(new Container()); + menuContainer.addChild(new Button("Handeln")); + menuContainer.addChild(new Button("Grundstücke")); + menuContainer.addChild(new Button("Zug beenden")); + menuContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); // Füge die Toolbar zur GUI hinzu app.getGuiNode().attachChild(toolbarContainer); @@ -64,9 +159,10 @@ public class Toolbar { * Initialisiert die Buttons in der Toolbar. */ private void initializeButtons() { - addButton("Vorwärts", 1); // Bewegung nach vorne - addButton("Rückwärts", -1); // Bewegung nach hinten + addTradeMenuButton(); // Bewegung nach vorne + addEndTurnButton(); // Bewegung nach hinten addDiceRollButton(); // Würfel-Button + } /** @@ -75,20 +171,58 @@ public class Toolbar { * @param label Der Text des Buttons * @param step Schrittweite (+1 für vorwärts, -1 für rückwärts) */ - private void addButton(String label, int step) { + + /*private void addButton(String label, int step) { Button button = new Button(label); button.setPreferredSize(new Vector3f(150, 50, 0)); // Größe der Buttons button.addClickCommands(source -> moveCube(step)); toolbarContainer.addChild(button); - } + }*/ /** * Fügt den Würfel-Button hinzu, der die Figur entsprechend der gewürfelten Zahl bewegt. */ - private void addDiceRollButton() { + private Button addDiceRollButton() { Button diceButton = new Button("Würfeln"); + diceButton.setPreferredSize(new Vector3f(50, 20, 0)); + diceButton.addClickCommands(s -> ifTopDialog(() -> { + rollDice(); + app.getGameLogic().playSound(Sound.BUTTON); + })); + toolbarContainer.addChild(diceButton); + return diceButton; + } + + private void addTradeMenuButton() { + Button diceButton = new Button("Handeln"); diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons - diceButton.addClickCommands(source -> rollDice()); + diceButton.addClickCommands(s -> { + rollDice(); + app.getGameLogic().playSound(Sound.BUTTON); + this.close(); + System.out.println("test"); + new ChoosePartner(app).open(); + }); + toolbarContainer.addChild(diceButton); + }// TODO Funktion der Buttons Überarbeiten und prüfen + + private void addEndTurnButton() { + Button diceButton = new Button("Grundstücke"); + diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons + diceButton.addClickCommands(s -> ifTopDialog(() -> { + rollDice(); + app.getGameLogic().playSound(Sound.BUTTON); + })); + toolbarContainer.addChild(diceButton); + } + + private void addPropertyMenuButton() { + Button diceButton = new Button("Zug beenden"); + diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons + diceButton.addClickCommands(s -> ifTopDialog(() -> { + rollDice(); + app.getGameLogic().playSound(Sound.BUTTON); + })); toolbarContainer.addChild(diceButton); } @@ -98,20 +232,6 @@ public class Toolbar { private void rollDice() { int diceRoll = random.nextInt(6) + 1; // Zahl zwischen 1 und 6 System.out.println("Gewürfelt: " + diceRoll); - moveCube(diceRoll); // Bewege die Figur um die gewürfelte Zahl - } - - /** - * Bewegt den Würfel basierend auf der aktuellen Position auf dem Brett. - * - * @param step Schrittweite (+1 für vorwärts, -1 für rückwärts oder andere Werte) - */ - private void moveCube(int step) { - currentPosition = (currentPosition + step + 4 * positionsPerSide) % (4 * positionsPerSide); - Vector3f newPosition = calculatePosition(currentPosition); - cube.setLocalTranslation(newPosition); - updatePositionDisplay(); // Aktualisiere die Positionsanzeige - System.out.println("Würfelposition: " + newPosition + " (Feld-ID: " + currentPosition + ")"); } /** @@ -158,11 +278,15 @@ public class Toolbar { positionText.setText("Feld-ID: " + currentPosition); } - /** - * Entfernt die Toolbar. - */ - public void remove() { + @Override + public void close() { app.getGuiNode().detachChild(toolbarContainer); app.getGuiNode().detachChild(positionText); + super.close(); + } + + @Override + public void escape() { + new SettingsMenu(app).open(); } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar2.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar2.java new file mode 100644 index 0000000..bc86e17 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar2.java @@ -0,0 +1,168 @@ +package pp.monopoly.client.gui; + +import java.util.Random; + +import com.jme3.font.BitmapText; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.simsilica.lemur.Axis; +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.component.SpringGridLayout; + +import pp.monopoly.client.MonopolyApp; + +/** + * Toolbar Klasse, die am unteren Rand der Szene angezeigt wird. + * Die Buttons bewegen den Würfel auf dem Spielfeld. + */ +public class Toolbar2 { + + private final MonopolyApp app; + private final Container toolbarContainer; + private final Geometry cube; // Referenz auf den Würfel + private final BitmapText positionText; // Anzeige für die aktuelle Position + private final float boardLimit = 0.95f; // Grenzen des Bretts + private final float stepSize = 0.18f; // Schrittgröße pro Bewegung + private int currentPosition = 0; // Aktuelle Position auf dem Spielfeld + private final int positionsPerSide = 10; // Anzahl der Positionen pro Seite + private final Random random = new Random(); // Zufallsgenerator für den Würfelwurf + + /** + * Konstruktor für die Toolbar. + * + * @param app Die Hauptanwendung (MonopolyApp) + * @param cube Der Würfel, der bewegt werden soll + */ + public Toolbar2(MonopolyApp app, Geometry cube) { + this.app = app; + this.cube = cube; + + // Erstelle die Toolbar + toolbarContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y)); + + // Setze die Position am unteren Rand und die Breite + toolbarContainer.setLocalTranslation( + 0, // Links bündig + 100, // Höhe über dem unteren Rand + 0 // Z-Ebene + ); + toolbarContainer.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 100, 0)); // Volle Breite + + // Füge Buttons zur Toolbar hinzu + initializeButtons(); + + // Füge die Toolbar zur GUI hinzu + app.getGuiNode().attachChild(toolbarContainer); + + // Erstelle die Position-Anzeige + positionText = createPositionDisplay(); + updatePositionDisplay(); // Initialisiere die Anzeige mit der Startposition + } + + /** + * Initialisiert die Buttons in der Toolbar. + */ + private void initializeButtons() { + addButton("Vorwärts", 1); // Bewegung nach vorne + addButton("Rückwärts", -1); // Bewegung nach hinten + addDiceRollButton(); // Würfel-Button + } + + /** + * Fügt einen Button mit einer Bewegung hinzu. + * + * @param label Der Text des Buttons + * @param step Schrittweite (+1 für vorwärts, -1 für rückwärts) + */ + private void addButton(String label, int step) { + Button button = new Button(label); + button.setPreferredSize(new Vector3f(150, 50, 0)); // Größe der Buttons + button.addClickCommands(source -> moveCube(step)); + toolbarContainer.addChild(button); + } + + /** + * Fügt den Würfel-Button hinzu, der die Figur entsprechend der gewürfelten Zahl bewegt. + */ + private void addDiceRollButton() { + Button diceButton = new Button("Würfeln"); + diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons + diceButton.addClickCommands(source -> rollDice()); + toolbarContainer.addChild(diceButton); + } + + /** + * Simuliert einen Würfelwurf und bewegt die Figur entsprechend. + */ + private void rollDice() { + int diceRoll = random.nextInt(6) + 1; // Zahl zwischen 1 und 6 + System.out.println("Gewürfelt: " + diceRoll); + moveCube(diceRoll); // Bewege die Figur um die gewürfelte Zahl + } + + /** + * Bewegt den Würfel basierend auf der aktuellen Position auf dem Brett. + * + * @param step Schrittweite (+1 für vorwärts, -1 für rückwärts oder andere Werte) + */ + private void moveCube(int step) { + currentPosition = (currentPosition + step + 4 * positionsPerSide) % (4 * positionsPerSide); + Vector3f newPosition = calculatePosition(currentPosition); + cube.setLocalTranslation(newPosition); + updatePositionDisplay(); // Aktualisiere die Positionsanzeige + System.out.println("Würfelposition: " + newPosition + " (Feld-ID: " + currentPosition + ")"); + } + + /** + * Berechnet die neue Position des Würfels basierend auf der aktuellen Brettseite und Position. + * + * @param position Aktuelle Position auf dem Spielfeld + * @return Die berechnete Position als Vector3f + */ + private Vector3f calculatePosition(int position) { + int side = position / positionsPerSide; // Seite des Bretts (0 = unten, 1 = rechts, 2 = oben, 3 = links) + int offset = position % positionsPerSide; // Position auf der aktuellen Seite + + switch (side) { + case 0: // Unten (positive x-Achse) + return new Vector3f(-boardLimit + offset * stepSize, 0.1f, -boardLimit + 0.05f); + case 1: // Rechts (positive z-Achse) + return new Vector3f(boardLimit - 0.05f, 0.1f, -boardLimit + offset * stepSize); + case 2: // Oben (negative x-Achse) + return new Vector3f(boardLimit - offset * stepSize, 0.1f, boardLimit - 0.05f); + case 3: // Links (negative z-Achse) + return new Vector3f(-boardLimit + 0.05f, 0.1f, boardLimit - offset * stepSize); + default: + throw new IllegalArgumentException("Ungültige Position: " + position); + } + } + + /** + * Erstellt die Anzeige für die aktuelle Position. + * + * @return Das BitmapText-Objekt für die Anzeige + */ + private BitmapText createPositionDisplay() { + BitmapText text = new BitmapText(app.getAssetManager().loadFont("Interface/Fonts/Default.fnt"), false); + text.setSize(20); // Schriftgröße + text.setLocalTranslation(10, app.getCamera().getHeight() - 10, 0); // Oben links + app.getGuiNode().attachChild(text); + return text; + } + + /** + * Aktualisiert die Anzeige für die aktuelle Position. + */ + private void updatePositionDisplay() { + positionText.setText("Feld-ID: " + currentPosition); + } + + /** + * Entfernt die Toolbar. + */ + public void remove() { + app.getGuiNode().detachChild(toolbarContainer); + app.getGuiNode().detachChild(positionText); + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/VolumeSlider.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/VolumeSlider.java new file mode 100644 index 0000000..c0c8cc2 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/VolumeSlider.java @@ -0,0 +1,37 @@ +package pp.monopoly.client.gui; + +import com.simsilica.lemur.Slider; + +import pp.monopoly.client.GameMusic; +/** + * 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 pp.monopoly.client.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); + } + } + +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/BuildingPropertyCard.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/BuildingPropertyCard.java new file mode 100644 index 0000000..300f4aa --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/BuildingPropertyCard.java @@ -0,0 +1,135 @@ +package pp.monopoly.client.gui.popups; + +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Quad; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import com.simsilica.lemur.style.ElementId; + +import pp.dialog.Dialog; +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.client.gui.SettingsMenu; +import pp.monopoly.model.fields.BuildingProperty; + +/** + * TODO Kommentare fixen + * SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann. + */ +public class BuildingPropertyCard extends Dialog { + private final MonopolyApp app; + private final Geometry overlayBackground; + private final Container buildingPropertyContainer; + private final Container backgroundContainer; + private int index = 37; + + public BuildingPropertyCard(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + + //Generate the corresponfing field + BuildingProperty field = (BuildingProperty) app.getGameLogic().getBoardManager().getFieldAtIndex(index); + + // Halbtransparentes Overlay hinzufügen + overlayBackground = createOverlayBackground(); + app.getGuiNode().attachChild(overlayBackground); + + // Create the background container + backgroundContainer = new Container(); + backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background + app.getGuiNode().attachChild(backgroundContainer); + + // Hauptcontainer für die Gebäudekarte + buildingPropertyContainer = new Container(); + buildingPropertyContainer.setBackground(new QuadBackgroundComponent(field.getColor().getColor())); + + + Label settingsTitle = buildingPropertyContainer.addChild(new Label( field.getName(), new ElementId("settings-title"))); + settingsTitle.setFontSize(48); + + // Text, der auf der Karte steht + // Die Preise werden dynamisch dem BoardManager entnommen + Container propertyValuesContainer = buildingPropertyContainer.addChild(new Container()); + propertyValuesContainer.addChild(new Label("„Grundstückswert: " + field.getPrice() + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile + propertyValuesContainer.addChild(new Label("„Miete allein: " + field.getAllRent().get(0)+ " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 1 Haus: " + field.getAllRent().get(1) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 2 Häuser: " + field.getAllRent().get(2) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 3 Häuser: " + field.getAllRent().get(3) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 4 Häuser: " + field.getAllRent().get(4) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 1 Hotel: " + field.getAllRent().get(5) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-1 Haus kostet: " + field.getHousePrice()+ " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile + propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); + + //TODO eventuell diese Stelle löschen, da nur die BuyCard Kaufen und beenden hat + + /* + // Beenden-Button + Button quitButton = foodFieldContainer.addChild(new Button("Beenden", new ElementId("button"))); + quitButton.setFontSize(32); + // Kaufen-Button + Button buyButton = foodFieldContainer.addChild(new Button("Kaufen", new ElementId("button"))); + buyButton.setFontSize(32); + */ + + float padding = 10; // Padding around the settingsContainer for the background + backgroundContainer.setPreferredSize(buildingPropertyContainer.getPreferredSize().addLocal(padding, padding, 0)); + + + // Zentriere das Menü + buildingPropertyContainer.setLocalTranslation( + (app.getCamera().getWidth() - buildingPropertyContainer.getPreferredSize().x) / 2, + (app.getCamera().getHeight() + buildingPropertyContainer.getPreferredSize().y) / 2, + 8 + ); + + backgroundContainer.setLocalTranslation( + (app.getCamera().getWidth() - buildingPropertyContainer.getPreferredSize().x - padding) / 2, + (app.getCamera().getHeight() + buildingPropertyContainer.getPreferredSize().y+ padding) / 2, + 7 + ); + + app.getGuiNode().attachChild(buildingPropertyContainer); + } + + /** + * Erstellt einen halbtransparenten Hintergrund für das Menü. + * + * @return Geometrie des Overlays + */ + private Geometry createOverlayBackground() { + Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); + Geometry overlay = new Geometry("Overlay", quad); + Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent + material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + overlay.setMaterial(material); + overlay.setLocalTranslation(0, 0, 0); + return overlay; + } + + /** + * Schließt das Menü und entfernt die GUI-Elemente. + */ + @Override + public void close() { + app.getGuiNode().detachChild(buildingPropertyContainer); // Entferne das Menü + app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand + app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay + super.close(); + } + + public void setIndex(int index) { + this.index = index; + } + + @Override + public void escape() { + new SettingsMenu(app).open(); + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/BuyCard.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/BuyCard.java new file mode 100644 index 0000000..8a65d6a --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/BuyCard.java @@ -0,0 +1,128 @@ +package pp.monopoly.client.gui.popups; + +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Quad; +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import com.simsilica.lemur.style.ElementId; +import pp.dialog.Dialog; +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.client.gui.SettingsMenu; +import pp.monopoly.model.fields.BoardManager; +import pp.monopoly.model.fields.BuildingProperty; + +/** + * SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann. + */ +public class BuyCard extends Dialog { + private final MonopolyApp app; + private final Geometry overlayBackground; + private final Container buyCardContainer; + private final Container backgroundContainer; + + private int index = 37; + + public BuyCard(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + + //Generate the corresponfing field + BuildingProperty field = (BuildingProperty) new BoardManager().getFieldAtIndex(index); + + // Halbtransparentes Overlay hinzufügen + overlayBackground = createOverlayBackground(); + app.getGuiNode().attachChild(overlayBackground); + + // Create the background container + backgroundContainer = new Container(); + backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background + app.getGuiNode().attachChild(backgroundContainer); + + // Hauptcontainer für die Gebäudekarte + buyCardContainer = new Container(); + buyCardContainer.setBackground(new QuadBackgroundComponent(field.getColor().getColor())); + + + Label settingsTitle = buyCardContainer.addChild(new Label( field.getName(), new ElementId("settings-title"))); + settingsTitle.setFontSize(48); + + // Text, der auf der Karte steht + // Die Preise werden dynamisch dem BoardManager entnommen + Container propertyValuesContainer = buyCardContainer.addChild(new Container()); + propertyValuesContainer.addChild(new Label("„Grundstückswert: " + field.getPrice() + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile + propertyValuesContainer.addChild(new Label("„Miete allein: " + field.getAllRent().get(0)+ " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 1 Haus: " + field.getAllRent().get(1) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 2 Häuser: " + field.getAllRent().get(2) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 3 Häuser: " + field.getAllRent().get(3) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 4 Häuser: " + field.getAllRent().get(4) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-mit 1 Hotel: " + field.getAllRent().get(5) + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„-1 Haus kostet: " + field.getHousePrice()+ " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile + propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); + + // Beenden-Button + Button quitButton = buyCardContainer.addChild(new Button("Beenden", new ElementId("button"))); + quitButton.setFontSize(32); + // Kaufen-Button + Button buyButton = buyCardContainer.addChild(new Button("Kaufen", new ElementId("button"))); + buyButton.setFontSize(32); + + float padding = 10; // Padding around the settingsContainer for the background + backgroundContainer.setPreferredSize(buyCardContainer.getPreferredSize().addLocal(padding, padding, 0)); + + + // Zentriere das Menü + buyCardContainer.setLocalTranslation( + (app.getCamera().getWidth() - buyCardContainer.getPreferredSize().x) / 2, + (app.getCamera().getHeight() + buyCardContainer.getPreferredSize().y) / 2, + 8 + ); + + backgroundContainer.setLocalTranslation( + (app.getCamera().getWidth() - buyCardContainer.getPreferredSize().x - padding) / 2, + (app.getCamera().getHeight() + buyCardContainer.getPreferredSize().y+ padding) / 2, + 7 + ); + + app.getGuiNode().attachChild(buyCardContainer); + } + + /** + * Erstellt einen halbtransparenten Hintergrund für das Menü. + * + * @return Geometrie des Overlays + */ + private Geometry createOverlayBackground() { + Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); + Geometry overlay = new Geometry("Overlay", quad); + Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent + material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + overlay.setMaterial(material); + overlay.setLocalTranslation(0, 0, 0); + return overlay; + } + + /** + * Schließt das Menü und entfernt die GUI-Elemente. + */ + @Override + public void close() { + app.getGuiNode().detachChild(buyCardContainer); // Entferne das Menü + app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand + app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay + super.close(); + } + + @Override + public void escape() { + new SettingsMenu(app).open(); + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/EventCard.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/EventCard.java new file mode 100644 index 0000000..64a8afa --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/EventCard.java @@ -0,0 +1,124 @@ +package pp.monopoly.client.gui.popups; + +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Quad; +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import com.simsilica.lemur.style.ElementId; +import pp.dialog.Dialog; +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.client.gui.SettingsMenu; +import pp.monopoly.model.card.Card; // TODO für den Import der Queue notwendig +import pp.monopoly.model.card.DeckHelper; +/** + * SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann. + */ +public class EventCard extends Dialog { + private final MonopolyApp app; + private final Geometry overlayBackground; + private final Container eventCardContainer; + private final Container backgroundContainer; + + + public EventCard(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + + //Generate the corresponfing field + Card card = new DeckHelper().drawCard(); // TODO nimmt die Karten gerade unabhängig aus dem DeckHelper + + // Halbtransparentes Overlay hinzufügen + overlayBackground = createOverlayBackground(); + app.getGuiNode().attachChild(overlayBackground); + + // Create the background container + backgroundContainer = new Container(); + backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background + app.getGuiNode().attachChild(backgroundContainer); + + // Hauptcontainer für die Gebäudekarte + eventCardContainer = new Container(); + eventCardContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); + eventCardContainer.setPreferredSize(new Vector3f(550,400,10)); + + // Titel + // Die Namen werden dynamisch dem BoardManager entnommen + Label gateFieldTitle = eventCardContainer.addChild(new Label("Ereigniskarte", new ElementId("settings-title"))); + gateFieldTitle.setFontSize(48); + gateFieldTitle.setColor(ColorRGBA.Black); + + // Text, der auf der Karte steht + // Die Preise werden dynamisch dem BoardManager entnommen + Container propertyValuesContainer = eventCardContainer.addChild(new Container()); + propertyValuesContainer.addChild(new Label(card.getDescription(), new ElementId("label-Text"))); + propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); + propertyValuesContainer.setPreferredSize(new Vector3f(300,200,10)); + + // Beenden-Button + Button quitButton = eventCardContainer.addChild(new Button("Jawohl", new ElementId("button"))); + quitButton.setFontSize(32); + quitButton.addClickCommands(source -> close()); + + + // TODO Kaufen-Button wird nicht mehr benötigt, prüfen ob weg kann + //Button buyButton = buyCardContainer.addChild(new Button("Kaufen", new ElementId("button"))); + //buyButton.setFontSize(32); + + float padding = 10; // Padding around the settingsContainer for the background + backgroundContainer.setPreferredSize(eventCardContainer.getPreferredSize().addLocal(padding, padding, 0)); + + + // Zentriere das Menü + eventCardContainer.setLocalTranslation( + (app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x) / 2, + (app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y) / 2, + 8 + ); + + backgroundContainer.setLocalTranslation( + (app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x - padding) / 2, + (app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y+ padding) / 2, + 7 + ); + + app.getGuiNode().attachChild(eventCardContainer); + } + + /** + * Erstellt einen halbtransparenten Hintergrund für das Menü. + * + * @return Geometrie des Overlays + */ + private Geometry createOverlayBackground() { + Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); + Geometry overlay = new Geometry("Overlay", quad); + Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent + material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + overlay.setMaterial(material); + overlay.setLocalTranslation(0, 0, 0); + return overlay; + } + + /** + * Schließt das Menü und entfernt die GUI-Elemente. + */ + @Override + public void close() { + app.getGuiNode().detachChild(eventCardContainer); // Entferne das Menü + app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand + app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay + super.close(); + } + + @Override + public void escape() { + new SettingsMenu(app).open(); + } +} \ No newline at end of file diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/FoodFieldCard.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/FoodFieldCard.java new file mode 100644 index 0000000..2880416 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/FoodFieldCard.java @@ -0,0 +1,139 @@ +package pp.monopoly.client.gui.popups; + +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Quad; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import com.simsilica.lemur.style.ElementId; + +import pp.dialog.Dialog; +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.client.gui.SettingsMenu; +import pp.monopoly.model.fields.FoodField; + +/** + * FoodFieldCard erstellt die Geböudekarte vom Brandl und der Truppenküche + */ +public class FoodFieldCard extends Dialog { + private final MonopolyApp app; + private final Geometry overlayBackground; + private final Container foodFieldContainer; + private final Container backgroundContainer; + private int index = 12; + + public FoodFieldCard(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + + //Generate the corresponfing field + FoodField field = (FoodField) app.getGameLogic().getBoardManager().getFieldAtIndex(index); + + // Halbtransparentes Overlay hinzufügen + overlayBackground = createOverlayBackground(); + app.getGuiNode().attachChild(overlayBackground); + + // Create the background container + backgroundContainer = new Container(); + backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background + app.getGuiNode().attachChild(backgroundContainer); + + // Hauptcontainer für die Gebäudekarte + foodFieldContainer = new Container(); + foodFieldContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f))); + + + + // Titel, bestehend aus dynamischen Namen anhand der ID und der Schriftfarbe/größe + Label settingsTitle = foodFieldContainer.addChild(new Label(field.getName(), new ElementId("settings-title"))); + settingsTitle.setFontSize(48); + + // Text, der auf der Karte steht + Container propertyValuesContainer = foodFieldContainer.addChild(new Container()); + propertyValuesContainer.addChild(new Label("„Preis: " + field.getPrice() + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))); // Leerzeile + propertyValuesContainer.addChild(new Label("„Wenn man Besitzer des", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label(field.getName()+" ist, so ist die", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("Miete 40-mal so hoch, wie", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("Augen auf den zwei Würfeln sind.", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))); // Leerzeile + propertyValuesContainer.addChild(new Label("„Wenn man Besitzer beider", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("Restaurants ist, so ist die", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("Miete 100-mal so hoch, wie", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("Augen auf den zwei Würfeln sind.", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))); // Leerzeile + propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); + + + //TODO eventuell diese Stelle löschen, da nur die BuyCard Kaufen und beenden hat + + /* + // Beenden-Button + Button quitButton = foodFieldContainer.addChild(new Button("Beenden", new ElementId("button"))); + quitButton.setFontSize(32); + // Kaufen-Button + Button buyButton = foodFieldContainer.addChild(new Button("Kaufen", new ElementId("button"))); + buyButton.setFontSize(32); + */ + + float padding = 10; // Padding around the settingsContainer for the background + backgroundContainer.setPreferredSize(foodFieldContainer.getPreferredSize().addLocal(padding, padding, 0)); + + + // Zentriere das Menü + foodFieldContainer.setLocalTranslation( + (app.getCamera().getWidth() - foodFieldContainer.getPreferredSize().x) / 2, + (app.getCamera().getHeight() + foodFieldContainer.getPreferredSize().y) / 2, + 8 + ); + + backgroundContainer.setLocalTranslation( + (app.getCamera().getWidth() - foodFieldContainer.getPreferredSize().x - padding) / 2, + (app.getCamera().getHeight() + foodFieldContainer.getPreferredSize().y+ padding) / 2, + 7 + ); + + app.getGuiNode().attachChild(foodFieldContainer); + } + + /** + * Erstellt einen halbtransparenten Hintergrund für das Menü. + * + * @return Geometrie des Overlays + */ + private Geometry createOverlayBackground() { + Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); + Geometry overlay = new Geometry("Overlay", quad); + Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent + material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + overlay.setMaterial(material); + overlay.setLocalTranslation(0, 0, 0); + return overlay; + } + + /** + * Schließt das Menü und entfernt die GUI-Elemente. + */ + @Override + public void close() { + app.getGuiNode().detachChild(foodFieldContainer); // Entferne das Menü + app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand + app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay + super.close(); + } + + public void setIndex(int index) { + this.index = index; + } + + @Override + public void escape() { + new SettingsMenu(app).open(); + } + +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/GateFieldCard.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/GateFieldCard.java new file mode 100644 index 0000000..c1d9561 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/GateFieldCard.java @@ -0,0 +1,132 @@ +package pp.monopoly.client.gui.popups; + +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Quad; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import com.simsilica.lemur.style.ElementId; +import pp.dialog.Dialog; +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.client.gui.SettingsMenu; +import pp.monopoly.model.fields.GateField; + +/** + * SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann. + */ +public class GateFieldCard extends Dialog { + private final MonopolyApp app; + private final Geometry overlayBackground; + private final Container gateFieldContainer; + private final Container backgroundContainer; + private int index = 5; + + public GateFieldCard(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + + //Generate the corresponfing field + GateField field = (GateField) app.getGameLogic().getBoardManager().getFieldAtIndex(index); + + // Halbtransparentes Overlay hinzufügen + overlayBackground = createOverlayBackground(); + app.getGuiNode().attachChild(overlayBackground); + + // Create the background container + backgroundContainer = new Container(); + backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background + app.getGuiNode().attachChild(backgroundContainer); + + // Hauptcontainer für die Gebäudekarte + gateFieldContainer = new Container(); + gateFieldContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); + + // Titel + // Die Namen werden dynamisch dem BoardManager entnommen + Label gateFieldTitle = gateFieldContainer.addChild(new Label(field.getName(), new ElementId("settings-title"))); + gateFieldTitle.setFontSize(48); + gateFieldTitle.setColor(ColorRGBA.Black); + + // Text, der auf der Karte steht + // Die Preise werden dynamisch dem BoardManager entnommen + Container propertyValuesContainer = gateFieldContainer.addChild(new Container()); + propertyValuesContainer.addChild(new Label("„Preis: " + field.getPrice() + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("Wenn man 1 Bahnhof besitzt: 250 EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("Wenn man 2 Bahnhöfe besitzt: 500 EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("Wenn man 3 Bahnhöfe besitzt: 1000 EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("Wenn man 4 Bahnhöfe besitzt: 2000 EUR", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))); + propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text"))); + propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); + + //TODO eventuell diese Stelle löschen, da nur die BuyCard Kaufen und beenden hat + + /* + // Beenden-Button + Button quitButton = foodFieldContainer.addChild(new Button("Beenden", new ElementId("button"))); + quitButton.setFontSize(32); + // Kaufen-Button + Button buyButton = foodFieldContainer.addChild(new Button("Kaufen", new ElementId("button"))); + buyButton.setFontSize(32); + */ + + float padding = 10; // Padding around the settingsContainer for the background + backgroundContainer.setPreferredSize(gateFieldContainer.getPreferredSize().addLocal(padding, padding, 0)); + + + // Zentriere das Menü + gateFieldContainer.setLocalTranslation( + (app.getCamera().getWidth() - gateFieldContainer.getPreferredSize().x) / 2, + (app.getCamera().getHeight() + gateFieldContainer.getPreferredSize().y) / 2, + 8 + ); + + backgroundContainer.setLocalTranslation( + (app.getCamera().getWidth() - gateFieldContainer.getPreferredSize().x - padding) / 2, + (app.getCamera().getHeight() + gateFieldContainer.getPreferredSize().y+ padding) / 2, + 7 + ); + + app.getGuiNode().attachChild(gateFieldContainer); + } + + /** + * Erstellt einen halbtransparenten Hintergrund für das Menü. + * + * @return Geometrie des Overlays + */ + private Geometry createOverlayBackground() { + Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); + Geometry overlay = new Geometry("Overlay", quad); + Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent + material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + overlay.setMaterial(material); + overlay.setLocalTranslation(0, 0, 0); + return overlay; + } + + /** + * Schließt das Menü und entfernt die GUI-Elemente. + */ + @Override + public void close() { + app.getGuiNode().detachChild(gateFieldContainer); // Entferne das Menü + app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand + app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay + super.close(); + } + + public void setIndex(int index) { + this.index = index; + } + + @Override + public void escape() { + new SettingsMenu(app).open(); + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/LooserPopUp.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/LooserPopUp.java new file mode 100644 index 0000000..a24fb2a --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/LooserPopUp.java @@ -0,0 +1,52 @@ +package pp.monopoly.client.gui.popups; + +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.component.IconComponent; + +import pp.dialog.Dialog; +import pp.monopoly.client.MonopolyApp; + +public class LooserPopUp extends Dialog { + + private final MonopolyApp app; + + /** + * Constructs a new NetworkDialog. + * + * @param network The NetworkSupport instance to be used for network operations. + */ + public LooserPopUp(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + initializeDialog(); + } + + /** + * Initializes the dialog with input fields and connection buttons. + */ + private void initializeDialog() { + Container inputContainer = new Container(); + + // Titel und Eingabefelder für Host und Port + inputContainer.addChild(new Label("Schade, du hast leider verloren!")); + inputContainer.addChild(new Label("Die nächste Runde wird besser!")); + + Label imageLabel = new Label(""); + IconComponent icon = new IconComponent("Pictures/MonopolyLooser.png"); // Icon mit Textur erstellen + icon.setIconScale(1); // Skalierung des Bildes + imageLabel.setIcon(icon); + + // Setze das Icon im Label + inputContainer.addChild(imageLabel); + + Button cancelButton = inputContainer.addChild(new Button("Spiel beenden")); + cancelButton.addClickCommands(source -> ifTopDialog(app::closeApp)); + + inputContainer.setLocalTranslation(300, 800, 0); + app.getGuiNode().attachChild(inputContainer); + + } +} + diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/SelectionListener.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/SelectionListener.java new file mode 100644 index 0000000..e668df5 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/SelectionListener.java @@ -0,0 +1,49 @@ +package pp.monopoly.client.gui.popups; + +/* + * $Id$ + * + * Copyright (c) 2013-2013 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +import com.jme3.scene.Spatial; + + +/** + * Notified when the current selection changes. + * + * @author Paul Speed + */ +public interface SelectionListener { + + public void selectionChanged( Spatial selection, Spatial previous ); +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/WinnerPopUp.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/WinnerPopUp.java new file mode 100644 index 0000000..ffb4c09 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/popups/WinnerPopUp.java @@ -0,0 +1,51 @@ +package pp.monopoly.client.gui.popups; + +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.component.IconComponent; + +import pp.dialog.Dialog; +import pp.monopoly.client.MonopolyApp; + +public class WinnerPopUp extends Dialog { + + private final MonopolyApp app; + + /** + * Constructs a new NetworkDialog. + * + * @param app The NetworkSupport instance to be used for network operations. + */ + public WinnerPopUp(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + initializeDialog(); + } + + /** + * Initializes the dialog with input fields and connection buttons. + */ + private void initializeDialog() { + Container inputContainer = new Container(); + + // Titel und Eingabefelder für Host und Port + inputContainer.addChild(new Label("Herlichen Glückwunsch!")); + inputContainer.addChild(new Label("Du,bist der Monopoly Champion!!!")); + + Label imageLabel = new Label(""); + IconComponent icon = new IconComponent("Pictures/MonopolyWinner.png"); // Icon mit Textur erstellen + icon.setIconScale(1); // Skalierung des Bildes + imageLabel.setIcon(icon); + + // Setze das Icon im Label + inputContainer.addChild(imageLabel); + + Button cancelButton = inputContainer.addChild(new Button("Spiel beenden")); + cancelButton.addClickCommands(source -> ifTopDialog(app::closeApp)); + + inputContainer.setLocalTranslation(300, 800, 0); + app.getGuiNode().attachChild(inputContainer); + } +} + diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/MonopolyLooser.png b/Projekte/monopoly/client/src/main/resources/Pictures/MonopolyLooser.png new file mode 100644 index 0000000..2de2fa1 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/MonopolyLooser.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/MonopolyLoser.png b/Projekte/monopoly/client/src/main/resources/Pictures/MonopolyLoser.png new file mode 100644 index 0000000..2de2fa1 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/MonopolyLoser.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/MonopolyWinner.png b/Projekte/monopoly/client/src/main/resources/Pictures/MonopolyWinner.png new file mode 100644 index 0000000..1566809 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/MonopolyWinner.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/board2.png b/Projekte/monopoly/client/src/main/resources/Pictures/board2.png new file mode 100644 index 0000000..4cd3bdc Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/board2.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/dice/five.png b/Projekte/monopoly/client/src/main/resources/Pictures/dice/five.png new file mode 100644 index 0000000..92a4500 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/dice/five.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/dice/four.png b/Projekte/monopoly/client/src/main/resources/Pictures/dice/four.png new file mode 100644 index 0000000..c5510c0 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/dice/four.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/dice/one.png b/Projekte/monopoly/client/src/main/resources/Pictures/dice/one.png new file mode 100644 index 0000000..1fe1cd7 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/dice/one.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/dice/six.png b/Projekte/monopoly/client/src/main/resources/Pictures/dice/six.png new file mode 100644 index 0000000..a71e581 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/dice/six.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/dice/three.png b/Projekte/monopoly/client/src/main/resources/Pictures/dice/three.png new file mode 100644 index 0000000..40ea246 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/dice/three.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/dice/two.png b/Projekte/monopoly/client/src/main/resources/Pictures/dice/two.png new file mode 100644 index 0000000..7b6b9b5 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/dice/two.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/lobby.png b/Projekte/monopoly/client/src/main/resources/Pictures/lobby.png new file mode 100644 index 0000000..da2cfc5 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/lobby.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/button.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/button.ogg new file mode 100644 index 0000000..c95c40b Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/button.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/diceRoll.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/diceRoll.ogg new file mode 100644 index 0000000..0b0eb1d Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/diceRoll.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/eventCard.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/eventCard.ogg new file mode 100644 index 0000000..1ba649f Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/eventCard.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/gulag.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/gulag.ogg new file mode 100644 index 0000000..b0a118a Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/gulag.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/loser.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/loser.ogg new file mode 100644 index 0000000..d695688 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/loser.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/moneyCollect.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/moneyCollect.ogg new file mode 100644 index 0000000..4a2c662 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/moneyCollect.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/moneyLost.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/moneyLost.ogg new file mode 100644 index 0000000..fc1a68e Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/moneyLost.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/passStart.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/passStart.ogg new file mode 100644 index 0000000..45d6fec Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/passStart.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/tradeAccepted.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/tradeAccepted.ogg new file mode 100644 index 0000000..06bca39 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/tradeAccepted.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/tradeRejected.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/tradeRejected.ogg new file mode 100644 index 0000000..b3d8d09 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/tradeRejected.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/Effects/winner.ogg b/Projekte/monopoly/client/src/main/resources/Sound/Effects/winner.ogg new file mode 100644 index 0000000..7648cce Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/Effects/winner.ogg differ diff --git a/Projekte/monopoly/client/src/main/resources/Sound/background.ogg b/Projekte/monopoly/client/src/main/resources/Sound/background.ogg new file mode 100644 index 0000000..b47ec29 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Sound/background.ogg differ diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/MonopolyConfig.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/MonopolyConfig.java index 3a5da01..cbef5e0 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/MonopolyConfig.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/MonopolyConfig.java @@ -34,13 +34,13 @@ public class MonopolyConfig extends Config { * The width of the game map in terms of grid units. */ @Property("map.width") - private int mapWidth = 10; + private int mapWidth = 12; /** * The height of the game map in terms of grid units. */ @Property("map.height") - private int mapHeight = 10; + private int mapHeight = 12; /** * Creates an instance of {@code MonopolyConfig} with default settings. diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ActiveState.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ActiveState.java new file mode 100644 index 0000000..78dfed1 --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ActiveState.java @@ -0,0 +1,8 @@ +package pp.monopoly.game.client; + +public class ActiveState extends ClientState{ + + ActiveState(ClientGameLogic logic) { + super(logic); + } +} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java index f972be6..4467043 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/ClientGameLogic.java @@ -1,19 +1,11 @@ -//////////////////////////////////////// -// Programming project code -// UniBw M, 2022, 2023, 2024 -// www.unibw.de/inf2 -// (c) Mark Minas (mark.minas@unibw.de) -//////////////////////////////////////// - package pp.monopoly.game.client; -import java.io.File; -import java.io.IOException; import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.util.ArrayList; import java.util.List; +import pp.monopoly.game.server.Player; import pp.monopoly.message.client.ClientMessage; import pp.monopoly.message.server.BuyPropertyResponse; import pp.monopoly.message.server.DiceResult; @@ -21,6 +13,7 @@ import pp.monopoly.message.server.EventDrawCard; import pp.monopoly.message.server.GameOver; import pp.monopoly.message.server.GameStart; import pp.monopoly.message.server.JailEvent; +import pp.monopoly.message.server.NextPlayerTurn; import pp.monopoly.message.server.PlayerStatusUpdate; import pp.monopoly.message.server.ServerInterpreter; import pp.monopoly.message.server.TimeOutWarning; @@ -29,6 +22,7 @@ import pp.monopoly.message.server.TradeRequest; import pp.monopoly.message.server.ViewAssetsResponse; import pp.monopoly.model.Board; import pp.monopoly.model.IntPoint; +import pp.monopoly.model.fields.BoardManager; import pp.monopoly.notification.ClientStateEvent; import pp.monopoly.notification.GameEvent; import pp.monopoly.notification.GameEventBroker; @@ -37,26 +31,30 @@ import pp.monopoly.notification.InfoTextEvent; import pp.monopoly.notification.Sound; import pp.monopoly.notification.SoundEvent; -import java.io.File; -import java.io.IOException; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.util.ArrayList; -import java.util.List; - /** * Controls the client-side game logic for Monopoly. - * Manages the player's placement, interactions with the map, and response to server messages. + * Handles interactions with the server and game state management on the client side. */ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { + + /** Logger for the client-side game logic. */ static final Logger LOGGER = System.getLogger(ClientGameLogic.class.getName()); + + /** The object responsible for sending messages to the server. */ private final ClientSender clientSender; + + /** A list of listeners to receive game events. */ private final List listeners = new ArrayList<>(); + + /** The game board representing the player's current state. */ private Board board; - private ClientState state = new ClientState(this) { - - }; + /** The current state of the client game logic. */ + private ClientState state = new LobbyState(this); + + private List players; + + private BoardManager boardManager = new BoardManager(); /** * Constructs a ClientGameLogic with the specified sender object. @@ -67,8 +65,18 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { this.clientSender = clientSender; } + /** + * Reutns the BoardManager + * @return the boardManager + */ + public BoardManager getBoardManager() { + return boardManager; + } + /** * Returns the current state of the game logic. + * + * @return the current state */ ClientState getState() { return state; @@ -86,10 +94,14 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { state.entry(); } + public List getPlayers() { + return players; + } + /** - * Returns the player's own map. + * Returns the player's game board. * - * @return the player's own map + * @return the player's game board */ public Board getBoard() { return board; @@ -116,32 +128,24 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { /** * Emits an event to play the specified sound. * - * @param sound the sound to be played. + * @param sound the sound to be played */ public void playSound(Sound sound) { notifyListeners(new SoundEvent(sound)); } - /** - * Loads a map from the specified file. - * - * @param file the file to load the map from - * @throws IOException if an I/O error occurs - */ - public void loadMap(File file) throws IOException { - state.loadMap(file); - } - /** * Sends a message to the server. * * @param msg the message to be sent */ - void send(ClientMessage msg) { - if (clientSender == null) + public void send(ClientMessage msg) { + if (clientSender == null) { LOGGER.log(Level.ERROR, "trying to send {0} with sender==null", msg); //NON-NLS - else + } else { clientSender.send(msg); + System.out.println("Message gesendet"); + } } /** @@ -173,12 +177,13 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { synchronized (this) { copy = new ArrayList<>(listeners); } - for (GameEventListener listener : copy) + for (GameEventListener listener : copy) { event.notifyListener(listener); + } } /** - * Called once per frame by the update loop. + * Updates the game logic once per frame in the update loop. * * @param delta time in seconds since the last update call */ @@ -186,6 +191,11 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { state.update(delta); } + /** + * Handles the response for buying a property. + * + * @param msg the message containing the buy property response + */ @Override public void received(BuyPropertyResponse msg) { if (msg.isSuccessful()) { @@ -195,35 +205,66 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { setInfoText("Unable to buy " + msg.getPropertyName() + ". Reason: " + msg.getReason()); } } - + + /** + * Handles the result of a dice roll. + * + * @param msg the message containing the dice roll result + */ @Override public void received(DiceResult msg) { setInfoText("You rolled a " + msg.calcTotal() + "!"); + //Set the dice images playSound(Sound.DICE_ROLL); } - + + /** + * Handles drawing an event card. + * + * @param msg the message containing the drawn card details + */ @Override public void received(EventDrawCard msg) { setInfoText("Event card drawn: " + msg.getCardDescription()); + // Kartenlogik playSound(Sound.EVENT_CARD); } - + + /** + * Handles the game over message. + * + * @param msg the message containing game over details + */ @Override public void received(GameOver msg) { if (msg.isWinner()) { setInfoText("Congratulations! You have won the game!"); + //Winner popup playSound(Sound.WINNER); } else { setInfoText("Game over. Better luck next time!"); + // Looser popup playSound(Sound.LOSER); } } - + + /** + * Handles the start of the game. + * + * @param msg the game start message + */ @Override public void received(GameStart msg) { + players = msg.getPlayers(); setInfoText("The game has started! Good luck!"); + setState(new WaitForTurnState(this)); } - + + /** + * Handles jail-related events. + * + * @param msg the message containing jail event details + */ @Override public void received(JailEvent msg) { if (msg.isGoingToJail()) { @@ -233,28 +274,74 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { setInfoText("You are out of jail!"); } } - + + /** + * Updates the status of a player. + * + * @param msg the message containing player status update details + */ @Override public void received(PlayerStatusUpdate msg) { + setInfoText("Player " + msg.getPlayerName() + " status updated: " + msg.getStatus()); } - + + /** + * Handles timeout warnings. + * + * @param msg the message containing timeout warning details + */ @Override public void received(TimeOutWarning msg) { setInfoText("Warning! Time is running out. You have " + msg.getRemainingTime() + " seconds left."); } - + + /** + * Displays the player's assets in response to a server query. + * + * @param msg the message containing the player's assets + */ @Override public void received(ViewAssetsResponse msg) { setInfoText("Your current assets are being displayed."); } - + + /** + * Handles trade replies from other players. + * + * @param msg the message containing the trade reply + */ @Override public void received(TradeReply msg) { + if (msg.getTradeHandler().getStatus()) { + setInfoText("Trade accepted by " + msg.getTradeHandler().getReceiver().getName() + "."); + playSound(Sound.TRADE_ACCEPTED); + } else { + setInfoText("Trade rejected by " + msg.getTradeHandler().getReceiver().getName() + "."); + playSound(Sound.TRADE_REJECTED); + } } - + + /** + * Handles trade requests from other players. + * + * @param msg the message containing the trade request details + */ @Override public void received(TradeRequest msg) { + setInfoText("Trade offer received from " + msg.getTradeHandler().getSender().getName()); + // playSound(Sound.TRADE_REQUEST); no sound effect + // notifyListeners(); + } + + /** + * Handles the transition to the next player's turn. + * + * @param msg the message indicating it's the next player's turn + */ + @Override + public void received(NextPlayerTurn msg) { + setInfoText("It's your turn!"); + setState(new ActiveState(this)); } - } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/LobbyState.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/LobbyState.java new file mode 100644 index 0000000..5fc6bd0 --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/LobbyState.java @@ -0,0 +1,8 @@ +package pp.monopoly.game.client; + +public class LobbyState extends ClientState{ + + LobbyState(ClientGameLogic logic) { + super(logic); + } +} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/WaitForTurnState.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/WaitForTurnState.java new file mode 100644 index 0000000..d6c6407 --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/client/WaitForTurnState.java @@ -0,0 +1,8 @@ +package pp.monopoly.game.client; + +public class WaitForTurnState extends ClientState{ + + WaitForTurnState(ClientGameLogic logic) { + super(logic); + } +} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/Player.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/Player.java index b64d499..649a38c 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/Player.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/Player.java @@ -10,10 +10,12 @@ package pp.monopoly.game.server; import java.util.List; import java.util.Random; +import com.jme3.network.serializing.Serializable; + import pp.monopoly.message.server.DiceResult; import pp.monopoly.model.FieldVisitor; import pp.monopoly.model.Figure; -import pp.monopoly.model.card.DeckHelper; +import pp.monopoly.model.card.Card; import pp.monopoly.model.fields.BuildingProperty; import pp.monopoly.model.fields.EventField; import pp.monopoly.model.fields.FineField; @@ -28,11 +30,11 @@ import pp.monopoly.model.fields.WacheField; /** * Class representing a player */ +@Serializable public class Player implements FieldVisitor{ private final int id; private String name; - private PlayerColor color; - private int accountBalance = 0; + private int accountBalance = 15000; private Figure figure; private List properties; private int getOutOfJailCard; @@ -41,6 +43,14 @@ public class Player implements FieldVisitor{ private final PlayerHandler handler; private PlayerState state = new LobbyState(); + /** + * Default constructor for serialization purposes. + */ + private Player(){ + id = 0; + handler = null; + } + /** * Constructs a player with the speciefied params * @param id the id of the player @@ -63,20 +73,38 @@ public class Player implements FieldVisitor{ this.handler = handler; } + public void setFigure(Figure figure) { + this.figure = figure; + } + + public PlayerColor getColor() { + switch ((id%6)+1) { + case 1: return PlayerColor.BLUE; + case 2: return PlayerColor.GREEN_DARK; + case 3: return PlayerColor.GREEN_LIGHT; + case 4: return PlayerColor.PINK; + case 5: return PlayerColor.RED; + case 6: return PlayerColor.YELLOW; + + default: + return null; + } + } + /** * Set the name of the Player * @param name the new name */ - void setName(String name) { + public void setName(String name) { this.name = name; } /** - * Set the PlayerColor - * @param color the color to be set to + * Retuns the Playerhandler + * @return the Playerhandler */ - void setColor(PlayerColor color) { - this.color = color; + public PlayerHandler getHandler() { + return handler; } /** @@ -94,6 +122,21 @@ public class Player implements FieldVisitor{ public int getFieldID() { return fieldID; } + void setActive() { + state = new ActiveState(); + } + + boolean finishTurn() { + if(canFinishTurn()) { + state = new WaitForTurnState(); + return true; + } + else return false; + } + + boolean canFinishTurn() { + return accountBalance >= 0; + } /** * Moves by the specified amount of steps @@ -115,9 +158,11 @@ public class Player implements FieldVisitor{ fieldID = fieldID%40; earnMoney(2000); } + figure.moveTo(fieldID); return fieldID; } + /** * Gets all the properties owned by this player * @return List of all properties owned by this player @@ -134,6 +179,7 @@ public class Player implements FieldVisitor{ public void buyProperty(PropertyField property) { if (property.getOwner() == null && accountBalance >= property.getPrice()) { properties.add(property); + property.setOwner(this); pay(property.getPrice()); } } @@ -149,6 +195,14 @@ public class Player implements FieldVisitor{ } } + /** + * Set the account Balance + * @param accountBalance the amount to be set to + */ + public void setAccountBalance(int accountBalance) { + this.accountBalance = accountBalance; + } + /** * Gets this players current accountBalanece * @return the amount of money currently owned by this player @@ -264,7 +318,8 @@ public class Player implements FieldVisitor{ @Override public Void visit(EventField field) { - DeckHelper.drawCard(); + Card c = getHandler().getLogic().getDeckHelper().drawCard(); + getHandler().getLogic().getDeckHelper().visit(c, this); return null; } @@ -307,7 +362,27 @@ public class Player implements FieldVisitor{ return count; } - /** + public int getNumHouses() { + int total = 0; + for (PropertyField field : properties) { + if (field.getClass() == BuildingProperty.class) { + total += ((BuildingProperty) field).getHouses(); + } + } + return total; + } + + public int getNumHotels() { + int total = 0; + for (PropertyField field : properties) { + if (field.getClass() == BuildingProperty.class) { + total += ((BuildingProperty) field).getHotel(); + } + } + return total; + } + + /** * Inner class for dice functionality in the game. * Rolls random dice values. */ @@ -440,47 +515,19 @@ public class Player implements FieldVisitor{ } } - - private class BankruptState implements PlayerState { - - @Override - public DiceResult rollDice() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'rollDice'"); - } - - @Override - public void payBail() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'payBail'"); - } - - @Override - public void useJailCard() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'useJailCard'"); - } - - } - private class WaitForTurnState implements PlayerState { @Override public DiceResult rollDice() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'rollDice'"); + throw new UnsupportedOperationException("not allowed"); } @Override public void payBail() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'payBail'"); } @Override public void useJailCard() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'useJailCard'"); } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/PlayerHandler.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/PlayerHandler.java index d7bbe10..aebe09c 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/PlayerHandler.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/PlayerHandler.java @@ -2,17 +2,30 @@ package pp.monopoly.game.server; import java.util.LinkedList; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.NoSuchElementException; import java.util.Set; + +import com.jme3.network.serializing.Serializable; + +import pp.monopoly.model.LimitedLinkedList; /** * A class for helping with player actions and managing thier turns */ +@Serializable public class PlayerHandler { - private List players = new LinkedList<>(); + private List players = new LimitedLinkedList<>(6); private Set readyPlayers = new HashSet<>(); private ServerGameLogic logic; + private Player hostPlayer; + private Player extra = null; + + /** + * Default constructor for serialization purposes. + */ + private PlayerHandler() {} /** * Contructs a PlayerHandler @@ -42,6 +55,14 @@ public class PlayerHandler { players.addAll(players); } + /** + * Return the host player + * @return the host player + */ + public Player getHostPlayer() { + return hostPlayer; + } + /** * Return the number of players * @return number of players in the game @@ -50,6 +71,14 @@ public class PlayerHandler { return players.size(); } + /** + * Retuns all players + * @return List of all players + */ + public List getPlayers() { + return players; + } + /** * Chechs if all players are ready to start the game * @return {@code true} if all players are ready, otherwise {@code false} @@ -85,6 +114,9 @@ public class PlayerHandler { throw new IllegalArgumentException("Player already registered"); } players.add(player); + if(hostPlayer == null) { + hostPlayer = player; + } } /** @@ -108,8 +140,13 @@ public class PlayerHandler { * Completes a player turn and return the next player * @return the next players who is active */ - Player nextPlayer() { + public Player nextPlayer() { Player tmp = players.get(0); + if (extra != null) { + tmp = extra; + extra = null; + return tmp; + } players.remove(0); players.add(tmp); return players.get(0); @@ -119,7 +156,7 @@ public class PlayerHandler { * Returns the {@link ServerGameLogic} of this PlayerHandler * @return the {@link ServerGameLogic} of this PlayerHandler */ - ServerGameLogic getLogic() { + public ServerGameLogic getLogic() { return logic; } @@ -128,10 +165,32 @@ public class PlayerHandler { * @param id the id to be searched for * @return the player with the required id */ - Player getPlayerById(int id) { + public Player getPlayerById(int id) { for (Player player : players) { if (player.getId() == id) return player; } throw new NoSuchElementException("Player mit id "+id+" existiert nicht"); } + + /** + * Arranges the players turns in a random order. + * Shuffles the players and sets their state to WaitForNextTurn, the first one will be active + */ + void randomOrder() { + Collections.shuffle(players); + for (Player player : players) { + player.finishTurn(); + } + players.get(0).setActive(); + } + + public void setStartBalance(int amount) { + for (Player player : players) { + player.setAccountBalance(amount); + } + } + + public void extraTurn(Player player) { + if (players.contains(player)) extra = player; + } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/ServerGameLogic.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/ServerGameLogic.java index 0685b39..a901fac 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/ServerGameLogic.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/game/server/ServerGameLogic.java @@ -1,20 +1,30 @@ package pp.monopoly.game.server; -import pp.monopoly.MonopolyConfig; -import pp.monopoly.message.client.*; -import pp.monopoly.message.server.ServerMessage; -import pp.monopoly.message.server.TradeReply; -import pp.monopoly.message.server.TradeRequest; -import pp.monopoly.message.server.ViewAssetsResponse; -import pp.monopoly.model.fields.BoardManager; -import pp.monopoly.model.fields.PropertyField; - import java.lang.System.Logger; import java.lang.System.Logger.Level; import pp.monopoly.MonopolyConfig; +import pp.monopoly.message.client.BuyPropertyRequest; import pp.monopoly.message.client.ClientInterpreter; +import pp.monopoly.message.client.EndTurn; +import pp.monopoly.message.client.PlayerReady; +import pp.monopoly.message.client.RollDice; +import pp.monopoly.message.client.TradeOffer; +import pp.monopoly.message.client.TradeResponse; +import pp.monopoly.message.client.ViewAssetsRequest; +import pp.monopoly.message.server.GameStart; +import pp.monopoly.message.server.NextPlayerTurn; +import pp.monopoly.message.server.PlayerStatusUpdate; import pp.monopoly.message.server.ServerMessage; +import pp.monopoly.message.server.TradeReply; +import pp.monopoly.message.server.TradeRequest; +import pp.monopoly.message.server.ViewAssetsResponse; +import pp.monopoly.model.Board; +import pp.monopoly.model.Figure; +import pp.monopoly.model.Rotation; +import pp.monopoly.model.card.DeckHelper; +import pp.monopoly.model.fields.BoardManager; +import pp.monopoly.model.fields.PropertyField; /** * Controls the server-side game logic for Monopoly. @@ -26,9 +36,11 @@ public class ServerGameLogic implements ClientInterpreter { private final MonopolyConfig config; private final PlayerHandler playerHandler = new PlayerHandler(this); private final ServerSender serverSender; - private ServerState state = ServerState.CREATEGAME; + private ServerState state = ServerState.LOBBY; private static final int MAX_PLAYERS = 6; private BoardManager boardManager = new BoardManager(); + private final DeckHelper deckHelper = new DeckHelper(); + private int startMoney; /** * Constructs a ServerGameLogic instance with the specified sender and configuration. @@ -120,6 +132,7 @@ public class ServerGameLogic implements ClientInterpreter { playerHandler.addPlayer(player); LOGGER.log(Level.DEBUG, "Player added: {0}", player.getId()); + System.out.println("Anzahl Spieler verbunden:"+ playerHandler.getPlayerCount()); return player; } @@ -158,8 +171,12 @@ public class ServerGameLogic implements ClientInterpreter { public void received(EndTurn msg, int from) { Player player = playerHandler.getPlayerById(from); if (player != null && state == ServerState.INGAME) { - LOGGER.log(Level.DEBUG, "Ending turn for player {0}", player.getName()); - playerHandler.nextPlayer(); + if (player.finishTurn()) { + LOGGER.log(Level.DEBUG, "Ending turn for player {0}", player.getName()); + Player next = playerHandler.nextPlayer(); + next.setActive(); + send(next, new NextPlayerTurn(next)); + } } } @@ -172,12 +189,26 @@ public class ServerGameLogic implements ClientInterpreter { @Override public void received(PlayerReady msg, int from) { Player player = playerHandler.getPlayerById(from); + if(player == playerHandler.getHostPlayer()) { + startMoney = msg.getStartMoney(); + } + if (player != null) { player.setName(msg.getName()); - player.setColor(msg.getColor()); - player.setName(msg.getName()); + player.setFigure(new Figure(1, -10, -10, Rotation.LEFT, msg.getFigure())); + //TODO add figure to the map + playerHandler.setPlayerReady(player, true); LOGGER.log(Level.DEBUG, "Player {0} is ready", player.getName()); } + + if(playerHandler.allPlayersReady()) { + playerHandler.setStartBalance(startMoney); + for (Player p : playerHandler.getPlayers()) { + send(p, new GameStart(playerHandler.getPlayers())); + } + playerHandler.randomOrder(); + send(playerHandler.getPlayerAtIndex(0), new NextPlayerTurn(playerHandler.getPlayerAtIndex(0))); + } } /** @@ -236,11 +267,12 @@ public class ServerGameLogic implements ClientInterpreter { */ @Override public void received(ViewAssetsRequest msg, int from) { - Player player = playerHandler.getPlayerById(from); - if (player != null) { - LOGGER.log(Level.DEBUG, "Processing ViewAssetsRequest for player {0}", player.getName()); + Player sender = playerHandler.getPlayerById(from); + Player player = msg.getPlayer(); + if (sender != null && player != null) { + LOGGER.log(Level.DEBUG, "Processing ViewAssetsRequest for player {0}", sender.getName()); - send(player, new ViewAssetsResponse(player.getProperties(), player.getAccountBalance(), player.getNumJailCard())); + send(sender, new ViewAssetsResponse(boardManager, player.getProperties(), player.getAccountBalance(), player.getNumJailCard())); } } @@ -256,4 +288,8 @@ public class ServerGameLogic implements ClientInterpreter { public Player getPlayerById(int id) { return playerHandler.getPlayerById(id); } + + public DeckHelper getDeckHelper() { + return deckHelper; + } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/BuyPropertyRequest.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/BuyPropertyRequest.java index 12a6bc9..6ecd9b3 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/BuyPropertyRequest.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/BuyPropertyRequest.java @@ -1,11 +1,19 @@ package pp.monopoly.message.client; +import com.jme3.network.serializing.Serializable; + /** * Represents a request from a player to buy a property. */ +@Serializable public class BuyPropertyRequest extends ClientMessage{ private int propertyId; + /** + * Default constructor for serialization purposes. + */ + private BuyPropertyRequest() { /* empty */ } + /** * Constructs a BuyPropertyRequest with the specified property ID. * diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/EndTurn.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/EndTurn.java index dae8120..f08e8cd 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/EndTurn.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/EndTurn.java @@ -1,9 +1,18 @@ package pp.monopoly.message.client; + +import com.jme3.network.serializing.Serializable; + /** * Represents a message indicating the player wants to end their turn. */ +@Serializable public class EndTurn extends ClientMessage{ + /** + * Default constructor for serialization purposes. + */ + public EndTurn() { /* empty */ } + @Override public void accept(ClientInterpreter interpreter, int from) { interpreter.received(this, from); diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/PlayerReady.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/PlayerReady.java index 6324b9e..ee61d11 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/PlayerReady.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/PlayerReady.java @@ -1,49 +1,51 @@ package pp.monopoly.message.client; -import pp.monopoly.game.server.PlayerColor; +import com.jme3.network.serializing.Serializable; /** * Represents a message indicating the player is ready to play. */ -public class PlayerReady extends ClientMessage{ +@Serializable +public class PlayerReady extends ClientMessage { private boolean isReady; private String name; - private PlayerColor color; + private String figure; + private int startMoney; + + /** + * Default constructor for serialization purposes. + */ + private PlayerReady() { /* empty */ } /** * Constructs a PlayerReady message. * * @param isReady true if the player is ready, false otherwise + * @param name the name of the player + * @param color the color of the player (can be null) */ - public PlayerReady(boolean isReady) { + public PlayerReady(boolean isReady, String name, String figure, int startMoney) { this.isReady = isReady; + this.name = name; + this.figure = figure; + this.startMoney = startMoney; } - /** - * Getter for the Name - * @return the Name - */ public String getName() { return name; } - /** - * Getter for the Playercolor - * @return the Playercolor - */ - public PlayerColor getColor() { - return color; + public String getFigure() { + return figure; } - /** - * Checks if the player is ready. - * - * @return true if ready, false otherwise - */ public boolean isReady() { return isReady; } + public int getStartMoney() { + return startMoney; + } @Override public void accept(ClientInterpreter interpreter, int from) { diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/RollDice.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/RollDice.java index 9fb0b93..a4d5dfc 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/RollDice.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/RollDice.java @@ -1,9 +1,18 @@ package pp.monopoly.message.client; + +import com.jme3.network.serializing.Serializable; + /** * Represents a message requesting to roll the dice. */ +@Serializable public class RollDice extends ClientMessage{ + /** + * Default constructor for serialization purposes. + */ + private RollDice() { /* empty */ } + @Override public void accept(ClientInterpreter interpreter, int from) { interpreter.received(this, from); diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/TradeOffer.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/TradeOffer.java index b614365..edb885f 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/TradeOffer.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/TradeOffer.java @@ -1,14 +1,21 @@ package pp.monopoly.message.client; +import com.jme3.network.serializing.Serializable; + import pp.monopoly.model.TradeHandler; /** * Represents a trade Request message from one player to another. */ +@Serializable public class TradeOffer extends ClientMessage{ private int receiverId; private TradeHandler tradehandler; + /** + * Default constructor for serialization purposes. + */ + private TradeOffer() { /* empty */ } /** * Constructs a TradeOffer with the specified details. diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/TradeResponse.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/TradeResponse.java index d317c6f..78d38f0 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/TradeResponse.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/TradeResponse.java @@ -1,14 +1,22 @@ package pp.monopoly.message.client; +import com.jme3.network.serializing.Serializable; + import pp.monopoly.model.TradeHandler; /** * Represents a response to a trade offer. */ +@Serializable public class TradeResponse extends ClientMessage{ private int initiatorId; private TradeHandler tradeHandler; + /** + * Default constructor for serialization purposes. + */ + private TradeResponse() { /* empty */ } + /** * Constructs a TradeResponse with the specified response details. * diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/ViewAssetsRequest.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/ViewAssetsRequest.java index f78d778..11c26af 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/ViewAssetsRequest.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/client/ViewAssetsRequest.java @@ -1,12 +1,33 @@ package pp.monopoly.message.client; + +import com.jme3.network.serializing.Serializable; + +import pp.monopoly.game.server.Player; + /** * Represents a request from a player to view their assets. */ +@Serializable public class ViewAssetsRequest extends ClientMessage{ + private Player player; + + /** + * Default constructor for serialization purposes. + */ + private ViewAssetsRequest() { /* empty */ } + + public ViewAssetsRequest(Player player) { + this.player = player; + } + @Override public void accept(ClientInterpreter interpreter, int from) { interpreter.received(this, from); } + public Player getPlayer() { + return player; + } + } \ No newline at end of file diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/BuyPropertyResponse.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/BuyPropertyResponse.java index e926317..39e1e24 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/BuyPropertyResponse.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/BuyPropertyResponse.java @@ -1,12 +1,20 @@ package pp.monopoly.message.server; +import com.jme3.network.serializing.Serializable; + /** * Represents the server's response to a player's request to buy a property. */ +@Serializable public class BuyPropertyResponse extends ServerMessage{ - private final boolean successful; - private final String propertyName; - private final String reason; // Reason for failure, if any + private boolean successful; + private String propertyName; + private String reason; // Reason for failure, if any + + /** + * Default constructor for serialization purposes. + */ + private BuyPropertyResponse() { /* empty */ } public BuyPropertyResponse(boolean successful, String propertyName, String reason) { this.successful = successful; diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/DiceResult.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/DiceResult.java index 65a8223..4d63f91 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/DiceResult.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/DiceResult.java @@ -2,10 +2,18 @@ package pp.monopoly.message.server; import java.util.List; +import com.jme3.network.serializing.Serializable; + +@Serializable public class DiceResult extends ServerMessage{ private List rollResult; + /** + * Default constructor for serialization purposes. + */ + private DiceResult() { /* empty */ } + public DiceResult(List rollResult) { this.rollResult = rollResult; } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/EventDrawCard.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/EventDrawCard.java index 968f2bb..929ac6e 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/EventDrawCard.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/EventDrawCard.java @@ -1,7 +1,15 @@ package pp.monopoly.message.server; +import com.jme3.network.serializing.Serializable; + +@Serializable public class EventDrawCard extends ServerMessage{ - private final String cardDescription; + private String cardDescription; + + /** + * Default constructor for serialization purposes. + */ + private EventDrawCard() { /* empty */ } public EventDrawCard(String cardDescription) { this.cardDescription = cardDescription; diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/GameOver.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/GameOver.java index e91041e..91de2fc 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/GameOver.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/GameOver.java @@ -1,7 +1,15 @@ package pp.monopoly.message.server; +import com.jme3.network.serializing.Serializable; + +@Serializable public class GameOver extends ServerMessage{ - private final boolean isWinner; + private boolean isWinner; + + /** + * Default constructor for serialization purposes. + */ + private GameOver() { /* empty */ } public GameOver(boolean isWinner) { this.isWinner = isWinner; diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/GameStart.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/GameStart.java index bf39031..9dfab8d 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/GameStart.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/GameStart.java @@ -1,7 +1,29 @@ package pp.monopoly.message.server; +import java.util.List; + +import com.jme3.network.serializing.Serializable; + +import pp.monopoly.game.server.Player; + +@Serializable public class GameStart extends ServerMessage{ + private List players; + + /** + * Default constructor for serialization purposes. + */ + private GameStart() { /* empty */ } + + public GameStart(List players) { + this.players = players; + } + + public List getPlayers() { + return players; + } + @Override public void accept(ServerInterpreter interpreter) { interpreter.received(this); diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/JailEvent.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/JailEvent.java index 485a7fd..9996545 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/JailEvent.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/JailEvent.java @@ -1,8 +1,16 @@ package pp.monopoly.message.server; +import com.jme3.network.serializing.Serializable; + +@Serializable public class JailEvent extends ServerMessage{ - private final boolean goingToJail; + private boolean goingToJail; + + /** + * Default constructor for serialization purposes. + */ + private JailEvent() { /* empty */ } public JailEvent(boolean goingToJail) { this.goingToJail = goingToJail; diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/NextPlayerTurn.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/NextPlayerTurn.java new file mode 100644 index 0000000..d0f5a18 --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/NextPlayerTurn.java @@ -0,0 +1,36 @@ +package pp.monopoly.message.server; + +import com.jme3.network.serializing.Serializable; + +import pp.monopoly.game.server.Player; + +@Serializable +public class NextPlayerTurn extends ServerMessage{ + + private Player player; + + /** + * Default constructor for serialization purposes. + */ + private NextPlayerTurn() { /* empty */ } + + public NextPlayerTurn(Player player) { + this.player = player; + } + + @Override + public void accept(ServerInterpreter interpreter) { + interpreter.received(this); + } + + @Override + public String getInfoTextKey() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); + } + + public Player getPlayer() { + return player; + } + +} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/PlayerStatusUpdate.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/PlayerStatusUpdate.java index c876108..0a84661 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/PlayerStatusUpdate.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/PlayerStatusUpdate.java @@ -1,12 +1,20 @@ package pp.monopoly.message.server; +import com.jme3.network.serializing.Serializable; + import pp.monopoly.game.server.PlayerColor; +@Serializable public class PlayerStatusUpdate extends ServerMessage{ - private final String playerName; - private final String status; - private final PlayerColor color; + private String playerName; + private String status; + private PlayerColor color; + + /** + * Default constructor for serialization purposes. + */ + private PlayerStatusUpdate() { /* empty */ } public PlayerStatusUpdate(String playerName, String status, PlayerColor color) { this.playerName = playerName; diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ServerInterpreter.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ServerInterpreter.java index 1f78ba7..4817e2a 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ServerInterpreter.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ServerInterpreter.java @@ -89,4 +89,11 @@ public interface ServerInterpreter { * @param msg the TradeRequest message received */ void received(TradeRequest msg); + + /** + * Handles a NextPlayerTurn message received from the server. + * + * @param msg the NextPlayerTurn message received + */ + void received(NextPlayerTurn msg); } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TimeOutWarning.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TimeOutWarning.java index b862170..684ee2a 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TimeOutWarning.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TimeOutWarning.java @@ -1,8 +1,16 @@ package pp.monopoly.message.server; +import com.jme3.network.serializing.Serializable; + +@Serializable public class TimeOutWarning extends ServerMessage{ - private final int remainingTime; + private int remainingTime; + + /** + * Default constructor for serialization purposes. + */ + private TimeOutWarning() { /* empty */ } public TimeOutWarning(int remainingTime) { this.remainingTime = remainingTime; diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TradeReply.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TradeReply.java index 6535906..38ef4ec 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TradeReply.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TradeReply.java @@ -1,14 +1,22 @@ package pp.monopoly.message.server; +import com.jme3.network.serializing.Serializable; + import pp.monopoly.model.TradeHandler; /** * Represents a response to a trade offer. */ +@Serializable public class TradeReply extends ServerMessage{ private int initiatorId; private TradeHandler tradeHandler; + /** + * Default constructor for serialization purposes. + */ + private TradeReply() { /* empty */ } + /** * Constructs a TradeResponse with the specified response details. * diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TradeRequest.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TradeRequest.java index 5320953..7afeb34 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TradeRequest.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/TradeRequest.java @@ -1,15 +1,23 @@ package pp.monopoly.message.server; +import com.jme3.network.serializing.Serializable; + import pp.monopoly.model.TradeHandler; /** * Represents a trade Request message from one player to another. */ +@Serializable public class TradeRequest extends ServerMessage{ private int receiverId; private TradeHandler tradehandler; + /** + * Default constructor for serialization purposes. + */ + private TradeRequest() { /* empty */ } + /** * Constructs a TradeRequest with the specified details. * diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ViewAssetsResponse.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ViewAssetsResponse.java index 9a00833..5c944f4 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ViewAssetsResponse.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/ViewAssetsResponse.java @@ -2,15 +2,26 @@ package pp.monopoly.message.server; import java.util.List; +import com.jme3.network.serializing.Serializable; + +import pp.monopoly.model.fields.BoardManager; import pp.monopoly.model.fields.PropertyField; + /** * Represents a response containing the player's assets. */ +@Serializable public class ViewAssetsResponse extends ServerMessage{ - private final List properties; - private final int accountBalance; - private final int jailCards; + private List properties; + private BoardManager board; + private int accountBalance; + private int jailCards; + + /** + * Default constructor for serialization purposes. + */ + private ViewAssetsResponse() { /* empty */ } /** * Constructs a ViewAssetsResponse with the specified properties and account balance. @@ -18,7 +29,8 @@ public class ViewAssetsResponse extends ServerMessage{ * @param properties a List of PropertyField objects representing the player's properties * @param accountBalance the player's current account balance */ - public ViewAssetsResponse(List properties, int accountBalance, int jailCards) { + public ViewAssetsResponse(BoardManager board, List properties, int accountBalance, int jailCards) { + this.board = board; this.properties = properties; this.accountBalance = accountBalance; this.jailCards = jailCards; @@ -47,4 +59,7 @@ public class ViewAssetsResponse extends ServerMessage{ return jailCards; } + public BoardManager getboard() { + return board; + } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Board.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Board.java index 31ee634..e46308f 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Board.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Board.java @@ -57,7 +57,6 @@ public class Board { this.width = width; this.height = height; this.eventBroker = eventBroker; - addItem(new Figure(5, 5, 5, Rotation.LEFT)); } /** diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/CardVisitor.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/CardVisitor.java deleted file mode 100644 index 257f81f..0000000 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/CardVisitor.java +++ /dev/null @@ -1,7 +0,0 @@ -package pp.monopoly.model; - -import pp.monopoly.model.card.Card; - -public interface CardVisitor { - T visit(Card c); -} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Figure.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Figure.java index d076acd..9ac874b 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Figure.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Figure.java @@ -4,35 +4,18 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import com.jme3.network.serializing.Serializable; + import static java.lang.Math.max; import static java.lang.Math.min; +@Serializable public class Figure implements Item{ - /** - * Enumeration representing the different statuses a Figure can have during the game. - */ - public enum Status { - /** - * The ship is in its normal state, not being previewed for placement. - */ - NORMAL, - - /** - * The ship is being previewed in a valid position for placement. - */ - VALID_PREVIEW, - - /** - * The ship is being previewed in an invalid position for placement. - */ - INVALID_PREVIEW - } - + private final String type; private final int length; // The length of the Figure private int x; // The x-coordinate of the Figure's position private int y; // The y-coordinate of the Figure's position private Rotation rot; // The rotation of the Figure - private Status status; // The current status of the Figure private final Set damaged = new HashSet<>(); // The set of positions that have been hit on this ship /** @@ -40,7 +23,7 @@ public class Figure implements Item{ * at position (0, 0), with a default rotation of RIGHT. */ private Figure() { - this(0, 0, 0, Rotation.RIGHT); + this(0, 0, 0, Rotation.RIGHT, "cube"); } /** @@ -51,12 +34,12 @@ public class Figure implements Item{ * @param y the y-coordinate of the Figure's initial position * @param rot the rotation of the Figure */ - public Figure(int length, int x, int y, Rotation rot) { + public Figure(int length, int x, int y, Rotation rot, String type) { this.x = x; this.y = y; this.rot = rot; this.length = length; - this.status = Status.NORMAL; + this.type = type; } /** @@ -87,7 +70,7 @@ public class Figure implements Item{ this.x = x; this.y = y; } - + /** * Moves the Figure to the specified position. * @@ -98,21 +81,46 @@ public class Figure implements Item{ } /** - * Returns the current status of the Figure. + * Moves the Figure to the specified coordinates. * - * @return the status of the Figure + * @param x the new x-coordinate of the Figure's position + * @param y the new y-coordinate of the Figure's position */ - public Status getStatus() { - return status; + public void moveTo(int fieldId) { + moveTo(fieldIdToPosition(fieldId)); } - /** - * Sets the status of the Figure. - * - * @param status the new status to be set for the Figure - */ - public void setStatus(Status status) { - this.status = status; + private IntPoint fieldIdToPosition(int fieldId) { + if (fieldId < 0 || fieldId > 39) { + throw new IllegalArgumentException("Invalid fieldId: " + fieldId); + } + + // Determine which edge and position along the edge + if (fieldId <= 9) { + // Bottom edge: From (-10, -10) to (10, -10) + int x = -10 + fieldId * 2; + return new IntPoint(x, -10); + } else if (fieldId <= 19) { + // Right edge: From (10, -10) to (10, 10) + int y = -10 + (fieldId - 10) * 2; + return new IntPoint(10, y); + } else if (fieldId <= 29) { + // Top edge: From (10, 10) to (-10, 10) + int x = 10 - (fieldId - 20) * 2; + return new IntPoint(x, 10); + } else { + // Left edge: From (-10, 10) to (-10, -10) + int y = 10 - (fieldId - 30) * 2; + return new IntPoint(-10, y); + } + } + + private Rotation fieldIdToRotation(int fieldId) { + if (fieldId >= 0 && fieldId <= 10) return Rotation.DOWN; + else if (fieldId <= 20) return Rotation.LEFT; + else if (fieldId <= 30) return Rotation.UP; + else if (fieldId <= 39) return Rotation.RIGHT; + else throw new IllegalArgumentException(); } /** diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/LimitedLinkedList.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/LimitedLinkedList.java new file mode 100644 index 0000000..34990f6 --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/LimitedLinkedList.java @@ -0,0 +1,57 @@ +package pp.monopoly.model; + +import java.util.LinkedList; + +import com.jme3.network.serializing.Serializable; + +/** + * A LinkedList with a maximum size limit. + * + * @param the type of elements held in this collection + */ +@Serializable +public class LimitedLinkedList extends LinkedList { + + private int maxSize; + + /** + * Default constructor for serialization purposes. + */ + private LimitedLinkedList() {} + + /** + * Constructs a LimitedLinkedList with the specified maximum size. + * + * @param maxSize the maximum number of elements this list can hold + */ + public LimitedLinkedList(int maxSize) { + if (maxSize <= 0) { + throw new IllegalArgumentException("Max size must be greater than 0"); + } + this.maxSize = maxSize; + } + + /** + * Adds an element to the list. If the list exceeds its maximum size, + * the oldest element (first) is removed. + * + * @param element the element to be added + * @return true if the element was added successfully + */ + @Override + public boolean add(E element) { + if (size() >= maxSize) { + return false; + } + return super.add(element); + } + + /** + * Gets the maximum size of this list. + * + * @return the maximum size + */ + public int getMaxSize() { + return maxSize; + } +} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/TradeHandler.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/TradeHandler.java index fc1f65f..6f63d8e 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/TradeHandler.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/TradeHandler.java @@ -6,66 +6,112 @@ import pp.monopoly.model.fields.PropertyField; import java.util.List; /** - * Helper class that handles the trade logic between two players. - * Manages trade initiation, validation, acceptance, and rejection involving multiple properties, money, and jail cards. + * Handles a single trade between two players. + * Encapsulates trade details, validation, acceptance, and rejection. */ public class TradeHandler { + private final Player sender; + private final Player receiver; + private final int offeredAmount; + private final List offeredProperties; + private final int offeredJailCards; + private final int requestedAmount; + private final List requestedProperties; + private final int requestedJailCards; + private Boolean status = null; + /** - * Initiates a trade offer between two players involving properties, money, and jail cards. + * Constructs a TradeHandler for a single trade instance. * - * @param sender the Player who is initiating the trade - * @param receiver the Player who is the target of the trade offer - * @param offeredAmount the amount of money the sender offers - * @param offeredProperties the list of properties the sender offers - * @param offeredJailCards the number of jail cards the sender offers - * @param requestedAmount the amount of money the sender requests from the receiver - * @param requestedProperties the list of properties the sender requests from the receiver - * @param requestedJailCards the number of jail cards the sender requests from the receiver - * @return true if the trade offer is valid and initiated, false otherwise + * @param sender the Player initiating the trade + * @param receiver the Player receiving the trade offer + * @param offeredAmount the amount of money offered by the sender + * @param offeredProperties the properties offered by the sender + * @param offeredJailCards the jail cards offered by the sender + * @param requestedAmount the amount of money requested from the receiver + * @param requestedProperties the properties requested from the receiver + * @param requestedJailCards the jail cards requested from the receiver */ - public boolean initiateTrade(Player sender, Player receiver, int offeredAmount, List offeredProperties, - int offeredJailCards, int requestedAmount, List requestedProperties, int requestedJailCards) { - // Validate the trade offer - if (!validateTrade(sender, offeredAmount, offeredProperties, offeredJailCards, receiver, requestedAmount, requestedProperties, requestedJailCards)) { + public TradeHandler(Player sender, Player receiver, int offeredAmount, List offeredProperties, + int offeredJailCards, int requestedAmount, List requestedProperties, int requestedJailCards) { + this.sender = sender; + this.receiver = receiver; + this.offeredAmount = offeredAmount; + this.offeredProperties = offeredProperties; + this.offeredJailCards = offeredJailCards; + this.requestedAmount = requestedAmount; + this.requestedProperties = requestedProperties; + this.requestedJailCards = requestedJailCards; + } + + public int getOfferedAmount() { + return offeredAmount; + } + + public int getOfferedJailCards() { + return offeredJailCards; + } + + public List getOfferedProperties() { + return offeredProperties; + } + + public Player getReceiver() { + return receiver; + } + + public int getRequestedAmount() { + return requestedAmount; + } + + public int getRequestedJailCards() { + return requestedJailCards; + } + + public List getRequestedProperties() { + return requestedProperties; + } + + public Player getSender() { + return sender; + } + + public Boolean getStatus() { + return status; + } + + /** + * Initiates the trade and validates its terms. + * + * @return true if the trade is valid and can proceed, false otherwise + */ + public boolean initiateTrade() { + if (!validateTrade()) { System.out.println("Trade offer is invalid."); return false; } - - // Notify the receiver about the trade offer (this would be an actual message in a real implementation) - System.out.println("Trade offer initiated by " + sender.getName() + " to " + receiver.getName()); + System.out.println("Trade initiated by " + sender.getName() + " to " + receiver.getName()); return true; } /** - * Accepts the trade offer and completes the trade between two players. - * - * @param sender the Player who initiated the trade - * @param receiver the Player who accepted the trade - * @param offeredAmount the amount of money to transfer from the sender to the receiver - * @param offeredProperties the list of properties to transfer from the sender to the receiver - * @param offeredJailCards the number of jail cards to transfer from the sender to the receiver - * @param requestedAmount the amount of money to transfer from the receiver to the sender - * @param requestedProperties the list of properties to transfer from the receiver to the sender - * @param requestedJailCards the number of jail cards to transfer from the receiver to the sender + * Completes the trade by transferring money, properties, and jail cards. */ - public void acceptTrade(Player sender, Player receiver, int offeredAmount, List offeredProperties, - int offeredJailCards, int requestedAmount, List requestedProperties, int requestedJailCards) { + public void acceptTrade() { // Transfer money - sender.earnMoney(-offeredAmount); // Deduct money from the sender - receiver.earnMoney(offeredAmount); // Add money to the receiver + sender.earnMoney(-offeredAmount); + receiver.earnMoney(offeredAmount); - receiver.earnMoney(-requestedAmount); // Deduct money from the receiver - sender.earnMoney(requestedAmount); // Add money to the sender + receiver.earnMoney(-requestedAmount); + sender.earnMoney(requestedAmount); - // Transfer ownership of the properties from sender to receiver + // Transfer properties if (offeredProperties != null) { for (PropertyField property : offeredProperties) { transferProperty(sender, receiver, property); } } - - // Transfer ownership of the properties from receiver to sender if (requestedProperties != null) { for (PropertyField property : requestedProperties) { transferProperty(receiver, sender, property); @@ -76,73 +122,57 @@ public class TradeHandler { transferJailCards(sender, receiver, offeredJailCards); transferJailCards(receiver, sender, requestedJailCards); - System.out.println("Trade accepted. " + sender.getName() + " and " + receiver.getName() + " completed the trade."); + System.out.println("Trade completed between " + sender.getName() + " and " + receiver.getName()); } /** - * Rejects the trade offer. - * - * @param receiver the Player who is rejecting the trade + * Rejects the trade. */ - public void rejectTrade(Player receiver) { - System.out.println("Trade rejected by " + receiver.getName()); + public void rejectTrade() { + System.out.println(receiver.getName() + " rejected the trade."); } /** - * Validates a trade offer by checking if the sender and receiver own the properties involved, - * have sufficient funds for the money involved in the trade, and have enough jail cards. + * Validates the trade offer by checking ownership, balances, and jail cards. * - * @param sender the Player initiating the trade - * @param offeredAmount the amount of money the sender is offering - * @param offeredProperties the list of properties the sender is offering - * @param offeredJailCards the number of jail cards the sender is offering - * @param receiver the Player receiving the trade offer - * @param requestedAmount the amount of money the sender is requesting - * @param requestedProperties the list of properties the sender is requesting from the receiver - * @param requestedJailCards the number of jail cards the sender is requesting from the receiver - * @return true if the trade offer is valid, false otherwise + * @return true if the trade is valid, false otherwise */ - private boolean validateTrade(Player sender, int offeredAmount, List offeredProperties, int offeredJailCards, - Player receiver, int requestedAmount, List requestedProperties, int requestedJailCards) { - // Check if sender has enough money to offer + private boolean validateTrade() { + // Validate sender's ability to offer money if (sender.getAccountBalance() < offeredAmount) { - System.out.println("Sender does not have enough balance to make this offer."); + System.out.println("Sender does not have enough money to offer."); return false; } - // Check if receiver has enough money to offer + // Validate receiver's ability to fulfill the requested amount if (receiver.getAccountBalance() < requestedAmount) { - System.out.println("Receiver does not have enough balance to fulfill requested amount."); + System.out.println("Receiver does not have enough money to fulfill the request."); return false; } - // Check if sender owns all the offered properties + // Validate property ownership if (offeredProperties != null) { for (PropertyField property : offeredProperties) { if (!sender.getProperties().contains(property)) { - System.out.println("Sender does not own the property " + property.getName() + " being offered."); + System.out.println("Sender does not own property: " + property.getName()); return false; } } } - - // Check if receiver owns all the requested properties if (requestedProperties != null) { for (PropertyField property : requestedProperties) { if (!receiver.getProperties().contains(property)) { - System.out.println("Receiver does not own the property " + property.getName() + " requested."); + System.out.println("Receiver does not own property: " + property.getName()); return false; } } } - // Check if sender has enough jail cards to offer + // Validate jail cards if (sender.getNumJailCard() < offeredJailCards) { System.out.println("Sender does not have enough jail cards to offer."); return false; } - - // Check if receiver has enough jail cards to fulfill the request if (receiver.getNumJailCard() < requestedJailCards) { System.out.println("Receiver does not have enough jail cards to fulfill the request."); return false; @@ -152,17 +182,16 @@ public class TradeHandler { } /** - * Transfers a property from one player to another. + * Transfers a property between players. * - * @param from the Player transferring the property - * @param to the Player receiving the property + * @param from the Player transferring the property + * @param to the Player receiving the property * @param property the PropertyField being transferred */ private void transferProperty(Player from, Player to, PropertyField property) { from.sellProperty(property); to.buyProperty(property); - property.setOwner(to); // Update the property's owner - + property.setOwner(to); System.out.println("Property " + property.getName() + " transferred from " + from.getName() + " to " + to.getName()); } @@ -178,6 +207,6 @@ public class TradeHandler { from.removeJailCard(); to.addJailCard(); } - System.out.println("Transferred " + numCards + " jail card(s) from " + from.getName() + " to " + to.getName()); + System.out.println(numCards + " jail card(s) transferred from " + from.getName() + " to " + to.getName()); } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/card/Card.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/card/Card.java index 0c8bb98..fb1e615 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/card/Card.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/card/Card.java @@ -1,5 +1,7 @@ package pp.monopoly.model.card; +import pp.monopoly.game.server.Player; + public class Card { private final String description; private final String keyword; @@ -9,13 +11,13 @@ public class Card { this.keyword = keyword; } - public void accept(DeckHelper visitor) { - visitor.visit(this); + public void accept(DeckHelper visitor, Player player) { + visitor.visit(this, player); } public String getDescription() { return description; - } + } // TODO wird gerade in der EventCard zur erstellung des Popup genutzt String getKeyword() { return keyword; diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/card/DeckHelper.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/card/DeckHelper.java index a270fd6..c967efd 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/card/DeckHelper.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/card/DeckHelper.java @@ -2,24 +2,317 @@ package pp.monopoly.model.card; import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Queue; -import pp.monopoly.model.CardVisitor; +import pp.monopoly.game.server.Player; +import pp.monopoly.message.client.EndTurn; -public class DeckHelper implements CardVisitor{ +public class DeckHelper{ - private static Queue cards; + private Queue cards; + private List drawn = new ArrayList<>(); - private DeckHelper() { + public DeckHelper() { + cards = new LinkedList(); + cards.add(new Card("Du wurdest mit einem Dienst KFZ geblitzt. Zahle: 800€", "dienst-kfz-blitzer")); + cards.add(new Card("Die erste Spoparty steht bevor. Ziehe vor zum 23er.", "spoparty")); + cards.add(new Card("Du kommst aus dem Gulak frei.", "gulak-frei-1")); + cards.add(new Card("Du kommst aus dem Gulak frei.", "gulak-frei-2")); + cards.add(new Card("Du hast den Dienstführerschein bestanden. Ziehe vor bis Teststrecke.", "dienstfuehrerschein")); + cards.add(new Card("Malkmus läd zum Pubquiz ein. Rücke vor bis zum 20er.", "pubquiz")); + cards.add(new Card("Du warst ohne Namensschild in der Truppenküche. Rücke vor zum 10er. Gehe nicht über Monatsgehalt. Ziehe keine 2000x€ ein.", "namensschild-truppenkueche")); + cards.add(new Card("Du hast heute die Spendierhosen an und gibst eine Runde in der Unibar. Zahle jedem Spieler: 400€", "spendierhosen-unibar")); + cards.add(new Card("Du warst in der Prüfungsphase krank. Gehe 3 Felder zurück.", "pruefungsphase-krank")); + cards.add(new Card("Ziehe vor bis zum nächsten Monatsgehalt.", "naechstes-monatsgehalt")); + cards.add(new Card("Du hast ein Antreten verschlafen. Zahle: 500€", "antreten-verschlafen-1")); + cards.add(new Card("Du hast den Maibock organisiert. Du erhältst: 3000€", "maibock-organisiert")); + cards.add(new Card("Der Spieß macht eine unangekündigte Inventur. Zahle für jedes Haus: 400€ und jedes Hotel: 2800€", "inventur-haeuser-hotels")); + cards.add(new Card("Es gab keine Mozzarella Bällchen mehr für Thoma. Alle Spieler ziehen vor auf Gym.", "dienstsport-gym")); + cards.add(new Card("Auf deiner Stube wurde Schimmel gefunden. Gehe ins Gulak. Begib Dich direkt dorthin. Gehe nicht über Monatsgehalt. Ziehe nicht ein.", "schimmel-gulak")); + cards.add(new Card("Deine Stube ist nach einer Partynacht nicht mehr bewohnbar. Du ziehst ins Gulak. Begib Dich direkt dorthin. Gehe nicht über Monatsgehalt. Ziehe nicht ein.", "partynacht-gulak")); + cards.add(new Card("Das Jahresabschlussantreten steht an. Ziehe vor bis Schwimmhalle.", "jahresabschlussantreten")); + cards.add(new Card("Du wurdest beim Verkaufen von Versicherungen erwischt. Zahle: 4000€", "verkaufen-versicherungen")); + cards.add(new Card("Du musstest einen Rückstuferantrag stellen. Setze eine Runde aus.", "rueckstuferantrag")); + cards.add(new Card("Auf einer Hausfeier bist du betrunken auf der Treppe gestürzt und dabei auf einen Kameraden gefallen. Zahle: 800€ und gehe zurück zu SanZ.", "hausfeier-sturz")); + cards.add(new Card("Beförderung. Beim nächsten Monatsgehalt ziehst du ein: 3000€", "befoerderung")); + cards.add(new Card("Du entscheidest dich für eine Dienstreise nach Lourd. Zahle: 1000€ und setze eine Runde aus.", "dienstreise-lourd")); + cards.add(new Card("Du warst fleißig Blutspenden und erhältst einen Tag Sonderurlaub. Du bist nochmal an der Reihe.", "blutspenden-sonderurlaub")); + cards.add(new Card("Dir wurde auf dem Oktoberfest dein Geldbeutel geklaut. Gebe 10% deines Vermögens ab.", "geldbeutel-oktoberfest")); + cards.add(new Card("Du wirst von deinem Chef für vorbildliches Verhalten gelobt. Du erhältst: 4000€", "lob-chef")); + cards.add(new Card("Deine Bekanntschaft von letzter Nacht war eine Spo. Lasse dich testen und zahle: 200€", "spo-testen")); + cards.add(new Card("Du wurdest von Kranz geexmattet. Gehe zurück zu Prüfungsamt.", "kranz-exmatrikulation")); + cards.add(new Card("Die letzte Party ist ein wenig eskaliert. Setze eine Runde aus.", "party-eskaliert")); + cards.add(new Card("Du wurdest zur VP gewählt und schmeißt eine Einstandsparty. Zahle: 800€", "vp-einstandsparty")); + cards.add(new Card("Du hast eine Party veranstaltet und dick Gewinn gemacht. Ziehe ein: 1500€", "party-gewinn")); + cards.add(new Card("Zur falschen Zeit am falschen Ort. Du musst einen Bergmarsch planen und setzt eine Runde aus.", "bergmarsch")); + cards.add(new Card("Dein Jodel eines Eispenis mit Unterhodenbeleuchtung geht viral. Ziehe ein: 1000€", "jodel-eispenis")); + } + + public void visit(Card card, Player player) { + switch (card.getKeyword()) { + case "dienst-kfz-blitzer": + dienstKfzBlitzer(player); + break; + case "spoparty": + spoparty(player); + break; + + case "gulak-frei-1": + case "gulak-frei-2": + gulakFrei(player); + break; + + case "dienstfuehrerschein": + dienstfuehrerschein(player); + break; + + case "pubquiz": + pubquiz(player); + break; + + case "namensschild-truppenkueche": + namensschildTruppenkueche(player); + break; + + case "spendierhosen-unibar": + spendierhosenUnibar(player); + break; + + case "pruefungsphase-krank": + pruefungsphaseKrank(player); + break; + + case "naechstes-monatsgehalt": + naechstesMonatsgehalt(player); + break; + + case "antreten-verschlafen-1": + antretenVerschlafen(player); + break; + + case "maibock-organisiert": + maibockOrganisiert(player); + break; + + case "inventur-haeuser-hotels": + inventurHaeuserHotels(player); + break; + + case "dienstsport-gym": + dienstsportGym(player); + break; + + case "schimmel-gulak": + schimmelGulak(player); + break; + + case "partynacht-gulak": + partynachtGulak(player); + break; + + case "jahresabschlussantreten": + jahresabschlussantreten(player); + break; + + case "verkaufen-versicherungen": + verkaufenVersicherungen(player); + break; + + case "rueckstuferantrag": + rueckstuferantrag(player); + break; + + case "hausfeier-sturz": + hausfeierSturz(player); + break; + + case "befoerderung": + befoerderung(player); + break; + + case "dienstreise-lourd": + dienstreiseLourd(player); + break; + + case "blutspenden-sonderurlaub": + blutspendenSonderurlaub(player); + break; + + case "geldbeutel-oktoberfest": + geldbeutelOktoberfest(player); + break; + + case "lob-chef": + lobChef(player); + break; + + case "spo-testen": + spoTesten(player); + break; + + case "kranz-exmatrikulation": + kranzExmatrikulation(player); + break; + + case "party-eskaliert": + partyEskaliert(player); + break; + + case "vp-einstandsparty": + vpEinstandsparty(player); + break; + + case "party-gewinn": + partyGewinn(player); + break; + + case "bergmarsch": + bergmarsch(player); + break; + + case "jodel-eispenis": + jodelEispenis(player); + break; + + default: + break; + } } - @Override - public Void visit(Card c) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'visit'"); - } + private void dienstKfzBlitzer(Player player) { + player.pay(800); + } + + private void spoparty(Player player) { + player.movePos(14); + } + + private void gulakFrei(Player player) { + player.addJailCard(); + } + + private void dienstfuehrerschein(Player player) { + player.movePos(20); + } + + private void pubquiz(Player player) { + player.movePos(39); + } + + private void namensschildTruppenkueche(Player player) { + //TODO + } + + private void spendierhosenUnibar(Player player) { + for (Player p : player.getHandler().getPlayers()) { + p.earnMoney(400); + } + player.pay(player.getHandler().getPlayerCount()*400 - 400); + } + + private void pruefungsphaseKrank(Player player) { + player.movePos(player.getFieldID() - 3); + } + + private void naechstesMonatsgehalt(Player player) { + player.movePos(0); + } + + private void antretenVerschlafen(Player player) { + player.pay(500); + } + + private void maibockOrganisiert(Player player) { + player.earnMoney(3000); + } + + private void inventurHaeuserHotels(Player player) { + player.pay(player.getNumHouses() * 400 + player.getNumHotels() * 2800); + } + + private void dienstsportGym(Player player) { + for (Player p : player.getHandler().getPlayers()) { + p.movePos(1); + } + } + + private void schimmelGulak(Player player) { + player.movePos(10); + } + + private void partynachtGulak(Player player) { + player.movePos(10); + } + + private void jahresabschlussantreten(Player player) { + player.movePos(17); + } + + private void verkaufenVersicherungen(Player player) { + player.pay(4000); + } + + private void rueckstuferantrag(Player player) { + player.getHandler().getLogic().received(new EndTurn(), player.getId()); + } + + private void hausfeierSturz(Player player) { + player.pay(800); + player.movePos(32); + } + + private void befoerderung(Player player) { + player.earnMoney(3000); + } + + private void dienstreiseLourd(Player player) { + player.pay(1000); + player.getHandler().getLogic().received(new EndTurn(), player.getId()); + } + + private void blutspendenSonderurlaub(Player player) { + player.getHandler().extraTurn(player); + } + + private void geldbeutelOktoberfest(Player player) { + player.pay(player.getAccountBalance() / 10); + } + + private void lobChef(Player player) { + player.earnMoney( 4000); + } + + private void spoTesten(Player player) { + player.pay( 200); + } + + private void kranzExmatrikulation(Player player) { + player.movePos(5); + } + + private void partyEskaliert(Player player) { + player.getHandler().getLogic().received(new EndTurn(), player.getId()); + } + + private void vpEinstandsparty(Player player) { + player.pay( 800); + } + + private void partyGewinn(Player player) { + player.earnMoney( 1500); + } + + private void bergmarsch(Player player) { + player.getHandler().getLogic().received(new EndTurn(), player.getId()); + } + + private void jodelEispenis(Player player) { + player.earnMoney(1000); + } + private void shuffle() { List cardList = new ArrayList<>(cards); @@ -28,7 +321,13 @@ public class DeckHelper implements CardVisitor{ cards.addAll(cardList); } - public static Card drawCard() { - return cards != null ? cards.poll() : null; + public Card drawCard() { + if (cards.isEmpty()) { + drawn.forEach(cards::add); + shuffle(); + } + Card card = cards.poll(); + drawn.add(card); + return card; } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/BoardManager.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/BoardManager.java index 0cb7c0c..d3dafe8 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/BoardManager.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/BoardManager.java @@ -22,49 +22,49 @@ public class BoardManager { * Creates a Monopoly GameBoard * @return the List of Fields in correct Order */ - private static List createBoard() { + public static List createBoard() { ArrayList fields = new ArrayList<>(); fields.add(new GoField()); - fields.add(new BuildingProperty("Gym", 1, 600, 20)); + fields.add(new BuildingProperty("Gym", 1, 600, 20, 500, FieldColor.BROWN)); fields.add(new EventField("Hausfeier", 2)); - fields.add(new BuildingProperty("Sportplatz", 3, 600, 40)); + fields.add(new BuildingProperty("Sportplatz", 3, 600, 40, 500, FieldColor.BROWN)); fields.add(new FineField("Diszi", 4, 2000)); fields.add(new GateField("Südtor", 5)); - fields.add(new BuildingProperty("Studium+", 6, 1000, 60)); + fields.add(new BuildingProperty("Studium+", 6, 1000, 60, 500, FieldColor.BLUE_LIGHT)); fields.add(new EventField("Üvas", 7)); - fields.add(new BuildingProperty("PhysikHörsaal", 8, 1000, 60)); - fields.add(new BuildingProperty("Audimax", 9, 1200, 80)); + fields.add(new BuildingProperty("PhysikHörsaal", 8, 1000, 60, 500, FieldColor.BLUE_LIGHT)); + fields.add(new BuildingProperty("Audimax", 9, 1200, 80, 500, FieldColor.BLUE_LIGHT)); fields.add(new GulagField()); - fields.add(new BuildingProperty("99er", 11, 1400, 100)); + fields.add(new BuildingProperty("99er", 11, 1400, 100, 1000, FieldColor.PINK)); fields.add(new FoodField("Brandl", 12)); - fields.add(new BuildingProperty("12er", 13, 1400, 100)); - fields.add(new BuildingProperty("23er", 14, 1600, 120)); + fields.add(new BuildingProperty("12er", 13, 1400, 100, 1000, FieldColor.PINK)); + fields.add(new BuildingProperty("23er", 14, 1600, 120, 1000, FieldColor.PINK)); fields.add(new GateField("HauptWache", 15)); - fields.add(new BuildingProperty("Schwimmhalle", 16, 1800, 140)); - fields.add(new BuildingProperty("CISM-Bahn", 17, 1800, 140)); + fields.add(new BuildingProperty("Schwimmhalle", 16, 1800, 140, 1000, FieldColor.ORANGE)); + fields.add(new BuildingProperty("CISM-Bahn", 17, 1800, 140, 1000, FieldColor.ORANGE)); fields.add(new EventField("Marine-Welcome-Party", 18)); - fields.add(new BuildingProperty("Kletterturm", 19, 2000, 160)); + fields.add(new BuildingProperty("Kletterturm", 19, 2000, 160, 1000, FieldColor.ORANGE)); fields.add(new TestStreckeField()); - fields.add(new BuildingProperty("StudFBer C", 21, 2200, 180)); + fields.add(new BuildingProperty("StudFBer C", 21, 2200, 180, 1500, FieldColor.RED)); fields.add(new EventField("Üvas", 22)); - fields.add(new BuildingProperty("StudFBer B", 23, 2200, 180)); - fields.add(new BuildingProperty("StudFBer A", 24, 2400, 200)); + fields.add(new BuildingProperty("StudFBer B", 23, 2200, 180, 1500, FieldColor.RED)); + fields.add(new BuildingProperty("StudFBer A", 24, 2400, 200, 1500, FieldColor.RED)); fields.add(new GateField("Nordtor", 25)); - fields.add(new BuildingProperty("Cascada", 26, 2600, 220)); - fields.add(new BuildingProperty("Fakultätsgebäude", 27, 2600, 220)); + fields.add(new BuildingProperty("Cascada", 26, 2600, 220, 1500, FieldColor.YELLOW)); + fields.add(new BuildingProperty("Fakultätsgebäude", 27, 2600, 220, 1500, FieldColor.YELLOW)); fields.add(new FoodField("Truppenküche", 28)); - fields.add(new BuildingProperty("Prüfungsamt", 29, 2800, 240)); + fields.add(new BuildingProperty("Prüfungsamt", 29, 2800, 240, 1500, FieldColor.YELLOW)); fields.add(new WacheField()); - fields.add(new BuildingProperty("Feuerwehr", 31, 3000, 260)); - fields.add(new BuildingProperty("SanZ", 32, 300, 260)); + fields.add(new BuildingProperty("Feuerwehr", 31, 3000, 260, 2000, FieldColor.GREEN)); + fields.add(new BuildingProperty("SanZ", 32, 300, 260, 2000, FieldColor.GREEN)); fields.add(new EventField("Maibock", 33)); - fields.add(new BuildingProperty("Rechenzentrum", 34, 3200, 280)); + fields.add(new BuildingProperty("Rechenzentrum", 34, 3200, 280, 2000, FieldColor.GREEN)); fields.add(new GateField("Osttor", 35)); fields.add(new EventField("Üvas", 36)); - fields.add(new BuildingProperty("2er", 37, 3500, 350)); + fields.add(new BuildingProperty("2er", 37, 3500, 350, 2000, FieldColor.BLUE_DARK)); fields.add(new FineField("EZM", 38, 1000)); - fields.add(new BuildingProperty("20er", 39, 4000, 500)); + fields.add(new BuildingProperty("20er", 39, 4000, 500, 2000, FieldColor.BLUE_DARK)); return fields; } @@ -88,4 +88,8 @@ public class BoardManager { if (board.contains(field)) return field.getId(); else throw new NoSuchElementException(); } + + public List getBoard() { + return board; + } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/BuildingProperty.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/BuildingProperty.java index ae73cb8..118d0ed 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/BuildingProperty.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/BuildingProperty.java @@ -1,30 +1,42 @@ package pp.monopoly.model.fields; +import java.util.ArrayList; +import java.util.List; + import pp.monopoly.game.server.Player; public class BuildingProperty extends PropertyField { private int houses; private boolean hotel = false; + private final int housePrice; + private final FieldColor color; + private final int rentFactor1 = 5; + private final int rentFactor2 = 15; + private final int rentFactor3 = 40; + private final int rentFactor4 = 55; + private final int rentFactorHotel = 70; - BuildingProperty(String name, int id, int price, int rent) { + BuildingProperty(String name, int id, int price, int rent, int housePrice, FieldColor color) { super(name, id, price, rent); + this.housePrice = housePrice; + this.color = color; } @Override public int calcRent() { if (hotel) { - return (int) Math.round(rent*70/10)*10; + return (int) Math.round(rent*rentFactorHotel/10)*10; } switch (houses) { case 1: - return (int) Math.round(rent*5/10)*10; + return (int) Math.round(rent*rentFactor1/10)*10; case 2: - return (int) Math.round(rent*15/10)*10; + return (int) Math.round(rent*rentFactor2/10)*10; case 3: - return (int) Math.round(rent*40/10)*10; + return (int) Math.round(rent*rentFactor3/10)*10; case 4: - return (int) Math.round(rent*55/10)*10; + return (int) Math.round(rent*rentFactor4/10)*10; default: return rent; @@ -67,4 +79,31 @@ public class BuildingProperty extends PropertyField { public void accept(Player player) { player.visit(this); } + + public List getAllRent() { + List list = new ArrayList<>(); + list.add(rent); + list.add((int) Math.round(rent*rentFactor1/10)*10); + list.add((int) Math.round(rent*rentFactor2/10)*10); + list.add((int) Math.round(rent*rentFactor3/10)*10); + list.add((int) Math.round(rent*rentFactor4/10)*10); + list.add((int) Math.round(rent*rentFactorHotel/10)*10); + return list; + } + + public FieldColor getColor() { + return color; + } + + public int getHousePrice() { + return housePrice; + } + + public int getHouses() { + return houses; + } + + public int getHotel() { + return hotel ? 1:0; + } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/EventField.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/EventField.java index fc2ca57..cad2dde 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/EventField.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/EventField.java @@ -2,8 +2,6 @@ package pp.monopoly.model.fields; import pp.monopoly.game.server.Player; -import pp.monopoly.model.card.Card; -import pp.monopoly.model.card.DeckHelper; public class EventField extends Field{ @@ -15,9 +13,4 @@ public class EventField extends Field{ public void accept(Player player) { player.visit(this); } - - public Card drawCard() { - return DeckHelper.drawCard(); - } - } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/FieldColor.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/FieldColor.java new file mode 100644 index 0000000..f0a9bd0 --- /dev/null +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/FieldColor.java @@ -0,0 +1,37 @@ +package pp.monopoly.model.fields; + +import com.jme3.math.ColorRGBA; + +/** + * Enum representing eight distinct colors for properties in the game. + */ +public enum FieldColor { + BROWN(new ColorRGBA(148 / 255f, 86 / 255f, 57 / 255f, 1)), + GREEN(new ColorRGBA(30 / 255f, 179 / 255f, 90 / 255f, 1)), + YELLOW(new ColorRGBA(252 / 255f, 241 / 255f, 1 / 255f, 1)), + BLUE_LIGHT(new ColorRGBA(170 / 255f, 223 / 255f, 246 / 255f, 1)), + PINK(new ColorRGBA(214 / 255f, 60 / 255f, 153 / 255f, 1)), + ORANGE(new ColorRGBA(244 / 255f, 147 / 255f, 32 / 255f, 1)), + RED(new ColorRGBA(232 / 255f, 27 / 255f, 30 / 255f, 1)), + BLUE_DARK(new ColorRGBA(2 / 255f, 112 / 255f, 191 / 255f, 1)); + + private final ColorRGBA color; + + /** + * Constructs a FieldColor with the specified ColorRGBA value. + * + * @param color the ColorRGBA value associated with the field color + */ + FieldColor(ColorRGBA color) { + this.color = color; + } + + /** + * Gets the ColorRGBA value of the field color. + * + * @return the ColorRGBA value + */ + public ColorRGBA getColor() { + return color; + } +} diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/GulagField.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/GulagField.java index 172a1f4..08c3a2c 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/GulagField.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/GulagField.java @@ -15,4 +15,8 @@ public class GulagField extends Field{ player.visit(this); } + public int getBailCost() { + return bailCost; + } + } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/TestStreckeField.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/TestStreckeField.java index 89da7dc..1ea6ae7 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/TestStreckeField.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/fields/TestStreckeField.java @@ -19,6 +19,8 @@ public class TestStreckeField extends Field{ } public int collectMoney() { - return money = 0; + int tmp = money; + money = 0; + return tmp; } } diff --git a/Projekte/monopoly/server/server.properties b/Projekte/monopoly/server/server.properties index 2439842..53a2737 100644 --- a/Projekte/monopoly/server/server.properties +++ b/Projekte/monopoly/server/server.properties @@ -10,5 +10,5 @@ # This file defines the configuration settings for the Battleship server. # # The port number on which the server will listen for incoming connections. -port=1234 +port=42069 diff --git a/Projekte/monopoly/server/src/main/java/pp/monopoly/server/MonopolyServer.java b/Projekte/monopoly/server/src/main/java/pp/monopoly/server/MonopolyServer.java index f02f48d..4c1b56d 100644 --- a/Projekte/monopoly/server/src/main/java/pp/monopoly/server/MonopolyServer.java +++ b/Projekte/monopoly/server/src/main/java/pp/monopoly/server/MonopolyServer.java @@ -7,21 +7,6 @@ package pp.monopoly.server; -import com.jme3.network.ConnectionListener; -import com.jme3.network.HostedConnection; -import com.jme3.network.Message; -import com.jme3.network.MessageListener; -import com.jme3.network.Network; -import com.jme3.network.Server; -import com.jme3.network.serializing.Serializer; -import pp.monopoly.MonopolyConfig; -import pp.monopoly.game.server.Player; -import pp.monopoly.game.server.ServerGameLogic; -import pp.monopoly.game.server.ServerSender; -import pp.monopoly.message.client.ClientMessage; -import pp.monopoly.message.server.ServerMessage; -import pp.monopoly.model.IntPoint; - import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -31,6 +16,34 @@ import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; import java.util.logging.LogManager; +import com.jme3.network.ConnectionListener; +import com.jme3.network.HostedConnection; +import com.jme3.network.Message; +import com.jme3.network.MessageListener; +import com.jme3.network.Network; +import com.jme3.network.Server; +import com.jme3.network.serializing.Serializer; + +import pp.monopoly.MonopolyConfig; +import pp.monopoly.game.server.Player; +import pp.monopoly.game.server.PlayerHandler; +import pp.monopoly.game.server.ServerGameLogic; +import pp.monopoly.game.server.ServerSender; +import pp.monopoly.message.client.BuyPropertyRequest; +import pp.monopoly.message.client.ClientMessage; +import pp.monopoly.message.client.EndTurn; +import pp.monopoly.message.client.PlayerReady; +import pp.monopoly.message.client.RollDice; +import pp.monopoly.message.client.TradeOffer; +import pp.monopoly.message.client.TradeResponse; +import pp.monopoly.message.client.ViewAssetsRequest; +import pp.monopoly.message.server.GameStart; +import pp.monopoly.message.server.NextPlayerTurn; +import pp.monopoly.message.server.ServerMessage; +import pp.monopoly.model.Figure; +import pp.monopoly.model.IntPoint; +import pp.monopoly.model.LimitedLinkedList; + /** * Server implementing the visitor pattern as MessageReceiver for ClientMessages */ @@ -104,14 +117,35 @@ public class MonopolyServer implements MessageListener, Connec private void initializeSerializables() { Serializer.registerClass(IntPoint.class); + Serializer.registerClass(BuyPropertyRequest.class); + Serializer.registerClass(EndTurn.class); + Serializer.registerClass(PlayerReady.class); + Serializer.registerClass(RollDice.class); + Serializer.registerClass(TradeOffer.class); + Serializer.registerClass(TradeResponse.class); + Serializer.registerClass(ViewAssetsRequest.class); + Serializer.registerClass(GameStart.class); + Serializer.registerClass(LimitedLinkedList.class); + Serializer.registerClass(NextPlayerTurn.class); + Serializer.registerClass(Player.class); + Serializer.registerClass(Figure.class); + Serializer.registerClass(PlayerHandler.class); } private void registerListeners() { + myServer.addMessageListener(this, BuyPropertyRequest.class); + myServer.addMessageListener(this, EndTurn.class); + myServer.addMessageListener(this, PlayerReady.class); + myServer.addMessageListener(this, RollDice.class); + myServer.addMessageListener(this, TradeOffer.class); + myServer.addMessageListener(this, TradeResponse.class); + myServer.addMessageListener(this, ViewAssetsRequest.class); myServer.addConnectionListener(this); } @Override public void messageReceived(HostedConnection source, Message message) { + System.out.println("Message recieved"); LOGGER.log(Level.INFO, "message received from {0}: {1}", source.getId(), message); //NON-NLS if (message instanceof ClientMessage clientMessage) pendingMessages.add(new ReceivedMessage(clientMessage, source.getId())); @@ -119,8 +153,9 @@ public class MonopolyServer implements MessageListener, Connec @Override public void connectionAdded(Server server, HostedConnection hostedConnection) { - LOGGER.log(Level.INFO, "new connection {0}", hostedConnection); //NON-NLS + LOGGER.log(Level.INFO, "New connection established: {0}", hostedConnection); //NON-NLS logic.addPlayer(hostedConnection.getId()); + System.out.println("Spieler verbunden: ID = " + hostedConnection.getId()); } @Override diff --git a/Projekte/settings.gradle b/Projekte/settings.gradle index 92a7ec5..928fed6 100644 --- a/Projekte/settings.gradle +++ b/Projekte/settings.gradle @@ -25,7 +25,9 @@ dependencyResolutionManagement { library('jme3-effects', 'org.jmonkeyengine', 'jme3-effects').versionRef('jme') library('lemur', 'com.simsilica:lemur:1.16.0') - library('lemur-proto', 'com.simsilica:lemur-proto:1.13.0') + library('lemurproto', 'com.simsilica:lemur-proto:1.13.0') + + library('selenium', 'org.seleniumhq.selenium:selenium-java:4.11.0') library('junit4', 'junit:junit:4.13.2') library('gson', 'com.google.code.gson:gson:2.11.0')