diff --git a/Battle.j3o b/Battle.j3o new file mode 100644 index 0000000..9e1ef4d Binary files /dev/null and b/Battle.j3o differ diff --git a/BoatSmall.j3o b/BoatSmall.j3o new file mode 100644 index 0000000..c13785d Binary files /dev/null and b/BoatSmall.j3o differ diff --git a/CV.j3o b/CV.j3o new file mode 100644 index 0000000..c49ddb2 Binary files /dev/null and b/CV.j3o differ diff --git a/KingGeorgeV.j3o b/KingGeorgeV.j3o new file mode 100644 index 0000000..b030d91 Binary files /dev/null and b/KingGeorgeV.j3o differ diff --git a/Projekte/battleship/converter/src/main/java/pp/battleship/exporter/ModelExporter.java b/Projekte/battleship/converter/src/main/java/pp/battleship/exporter/ModelExporter.java index ae9350b..5a95a98 100644 --- a/Projekte/battleship/converter/src/main/java/pp/battleship/exporter/ModelExporter.java +++ b/Projekte/battleship/converter/src/main/java/pp/battleship/exporter/ModelExporter.java @@ -45,7 +45,12 @@ public class ModelExporter extends SimpleApplication { export("Models/BoatSmall/12219_boat_v2_L2.obj", "BoatSmall.j3o"); //NON-NLS export("Models/Battle/14084_WWII_Ship_German_Type_II_U-boat_v2_L1.obj", "Battle.j3o"); //NON-NLS export("Models/CV/essex_scb-125_generic.obj", "CV.j3o"); //NON-NLS - + export("Models/Figures/Würfel_blau.obj", "Würfel_blau.j30"); + export("Models/Figures/Würfel_gelb.obj", "Würfel_gelb.j30"); + export("Models/Figures/Würfel_grün.obj", "Würfel_grün.j30"); + export("Models/Figures/Würfel_rosa.obj", "Würfel_rosa.j30"); + export("Models/Figures/Würfel_rot.obj", "Würfel_rot.j30"); + export("Models/Figures/Würfel_schwarz.obj", "Würfel_schwarz.j30"); stop(); } 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 957bc68..36ee17e 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,76 +1,69 @@ // Styling of Lemur components // For documentation, see: // https://github.com/jMonkeyEngine-Contributions/Lemur/wiki/Styling - import com.simsilica.lemur.* import com.simsilica.lemur.component.QuadBackgroundComponent +import com.simsilica.lemur.Button +import com.simsilica.lemur.Button.ButtonAction +import com.simsilica.lemur.Command +import com.simsilica.lemur.HAlignment +import com.simsilica.lemur.Insets3f +import com.simsilica.lemur.component.QuadBackgroundComponent +import com.simsilica.lemur.component.TbtQuadBackgroundComponent def bgColor = color(1, 1, 1, 1) -def buttonEnabledColor = color(0.8, 0.9, 1, 1) +def buttonEnabledColor = color(0, 0, 0, 1) def buttonDisabledColor = color(0.8, 0.9, 1, 0.2) -//def buttonBgColor = color(0, 0.75, 0.75, 1) +def buttonBgColor = color(1, 1, 1, 1) def sliderColor = color(0.6, 0.8, 0.8, 1) def sliderBgColor = color(0.5, 0.75, 0.75, 1) -def gradientColor = color(1, 1, 1, 1) +def gradientColor = color(0.5, 0.75, 0.85, 0.5) def tabbuttonEnabledColor = color(0.4, 0.45, 0.5, 1) -def playButtonBorderColor = color(1, 0.6, 0, 1) // For "Spielen" button -def blackColor = color(0, 0, 0, 1) // Define black color for border +def solidWhiteBackground = new QuadBackgroundComponent(color(1, 1, 1, 1)) // Solid white -def playButtonBorderColor = color(1, 0.6, 0, 1) // Orange border for "Spielen" button -def playButtonTextColor = color(0, 0, 0, 1) // Black text color for "Spielen" button -def buttonBgColor = color(1, 1, 1, 1) // White background for "Spiel beenden" and "Einstellungen" buttons -def buttonTextColor = color(0, 0, 0, 1) // Black text color for "Spiel beenden" and "Einstellungen" buttons -def borderColor = color(0, 0, 0, 1) // Black border for "Spiel beenden" and "Einstellungen" def gradient = TbtQuadBackgroundComponent.create( - texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png", generateMips: false), - 1, 1, 1, 126, 126, 1f, false) + texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png", + generateMips: false), + 1, 1, 1, 126, 126, + 1f, false) def doubleGradient = new QuadBackgroundComponent(gradientColor) -doubleGradient.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png", generateMips: false) +doubleGradient.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png", + generateMips: false) + +def orangeBorder = TbtQuadBackgroundComponent.create( + texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png", // Replace with an appropriate texture if needed + generateMips: false), + 1, 1, 1, 126, 126, + 1f, false) +orangeBorder.color = color(1, 0.5, 0, 1) // Orange color -// Hauptstil für die Schriftart selector("pp") { font = font("Interface/Fonts/Metropolis/Metropolis-Regular-32.fnt") } -// Titel für "Einstellungen" -selector("settings-title", "pp") { - color = color(1, 1, 1, 1) - fontSize = 48 - textHAlignment = HAlignment.Center - insets = new Insets3f(5, 5, 5, 5) +selector("label", "pp") { + insets = new Insets3f(2, 2, 2, 2) + 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 } -// Container Stil selector("container", "pp") { - background = gradient.clone() + background = solidWhiteBackground.clone() background.setColor(bgColor) } -// Slider Stil selector("slider", "pp") { - insets = new Insets3f(5, 10, 5, 10) // Abstand - background = new QuadBackgroundComponent(sliderBgColor) -} - -selector("play-button", "pp") { - color = playButtonTextColor // Black text color - background = new QuadBackgroundComponent(playButtonBorderColor) // Orange border background - insets = new Insets3f(15, 25, 15, 25) // Padding for larger button size - background.setMargin(5, 5) // Thin border effect around the background color - fontSize = 36 // Larger font size for prominence -} - -selector("menu-button", "pp") { - color = buttonTextColor // Black text color - background = new QuadBackgroundComponent(buttonBgColor) // White background - insets = new Insets3f(10, 20, 10, 20) // Padding - background.setMargin(1, 1) // Thin black border - background.setColor(borderColor) // Set black border color - - fontSize = 24 // Standard font size + background = gradient.clone() + background.setColor(bgColor) } def pressedCommand = new Command<Button>() { @@ -91,6 +84,30 @@ def enabledCommand = new Command<Button>() { } } +def repeatCommand = new Command<Button>() { + private long startTime + private long lastClick + + void execute(Button source) { + // Only do the repeating click while the mouse is + // over the button (and pressed of course) + if (source.isPressed() && source.isHighlightOn()) { + long elapsedTime = System.currentTimeMillis() - startTime + // After half a second pause, click 8 times a second + if (elapsedTime > 500 && elapsedTime > lastClick + 125) { + source.click() + + // Try to quantize the last click time to prevent drift + lastClick = ((elapsedTime - 500) / 125) * 125 + 500 + } + } + else { + startTime = System.currentTimeMillis() + lastClick = 0 + } + } +} + def stdButtonCommands = [ (ButtonAction.Down) : [pressedCommand], (ButtonAction.Up) : [pressedCommand], @@ -98,9 +115,101 @@ def stdButtonCommands = [ (ButtonAction.Disabled): [enabledCommand] ] -selector("button", "pp") { - background = gradient.clone() - color = buttonEnabledColor +def sliderButtonCommands = [ + (ButtonAction.Hover): [repeatCommand] +] + +selector("title", "pp") { + color = color(0.8, 0.9, 1, 0.85f) + highlightColor = color(1, 0.8, 1, 0.85f) + shadowColor = color(0, 0, 0, 0.75f) + shadowOffset = vec3(2, -2, -1) + background = new QuadBackgroundComponent(color(0.5, 0.75, 0.85, 1)) + background.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png", + generateMips: false) insets = new Insets3f(2, 2, 2, 2) + + buttonCommands = stdButtonCommands +} + + +selector("button", "pp") { + def outerBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)) // Orange border + def innerBackground = new QuadBackgroundComponent(buttonBgColor) // Inner button background + + // Apply the outer border as the main background + background = outerBackground + + // Use insets to create a margin/padding effect for the inner background + insets = new Insets3f(3, 3, 3, 3) // Adjust the border thickness + buttonCommands = stdButtonCommands +} + +selector("slider", "pp") { + insets = new Insets3f(1, 3, 1, 2) +} + +selector("slider", "button", "pp") { + background = doubleGradient.clone() + //background.setColor(sliderBgColor) + insets = new Insets3f(0, 0, 0, 0) +} + +selector("slider.thumb.button", "pp") { + text = "[]" + color = sliderColor +} + +selector("slider.left.button", "pp") { + text = "-" + background = doubleGradient.clone() + //background.setColor(sliderBgColor) + background.setMargin(5, 0) + color = sliderColor + + buttonCommands = sliderButtonCommands +} + +selector("slider.right.button", "pp") { + text = "+" + background = doubleGradient.clone() + //background.setColor(sliderBgColor) + background.setMargin(4, 0) + color = sliderColor + + buttonCommands = sliderButtonCommands +} + +selector("slider.up.button", "pp") { + buttonCommands = sliderButtonCommands +} + +selector("slider.down.button", "pp") { + buttonCommands = sliderButtonCommands +} + +selector("checkbox", "pp") { + color = buttonEnabledColor +} + +selector("rollup", "pp") { + background = gradient.clone() + background.setColor(bgColor) +} + +selector("tabbedPanel", "pp") { + activationColor = buttonEnabledColor +} + +selector("tabbedPanel.container", "pp") { + background = null +} + +selector("tab.button", "pp") { + background = solidWhiteBackground.clone() + background.setColor(bgColor) + color = tabbuttonEnabledColor + insets = new Insets3f(4, 2, 0, 2) + buttonCommands = stdButtonCommands } 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 df498f5..bf5d649 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 @@ -1,3 +1,10 @@ +//////////////////////////////////////// +// Programming project code +// UniBw M, 2022, 2023, 2024 +// www.unibw.de/inf2 +// (c) Mark Minas (mark.minas@unibw.de) +//////////////////////////////////////// + package pp.monopoly.client; import java.lang.System.Logger; @@ -7,6 +14,8 @@ 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; @@ -15,14 +24,16 @@ import pp.monopoly.notification.SoundEvent; import static pp.util.PreferencesUtils.getPreferences; /** - * An application state that plays sounds based on game events. + * An application state that plays sounds. */ 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"; + private static final String ENABLED_PREF = "enabled"; //NON-NLS - private Application app; // Feld zum Speichern der Application-Instanz + private AudioNode splashSound; + private AudioNode shipDestroyedSound; + private AudioNode explosionSound; /** * Checks if sound is enabled in the preferences. @@ -42,6 +53,7 @@ public class GameSound extends AbstractAppState implements GameEventListener { /** * 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. */ @@ -49,52 +61,69 @@ public class GameSound extends AbstractAppState implements GameEventListener { public void setEnabled(boolean enabled) { if (isEnabled() == enabled) return; super.setEnabled(enabled); - LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); + LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); //NON-NLS PREFERENCES.putBoolean(ENABLED_PREF, enabled); } /** - * Initializes the sound effects for the game and stores the application reference. + * Initializes the sound effects for the game. + * Overrides {@link AbstractAppState#initialize(AppStateManager, Application)} * * @param stateManager The state manager - * @param app The application instance + * @param app The application */ @Override public void initialize(AppStateManager stateManager, Application app) { super.initialize(stateManager, app); - this.app = app; // Speichert die Application-Instanz } /** * 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(String name) { + private AudioNode loadSound(Application app, String name) { try { - AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer); + final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer); sound.setLooping(false); sound.setPositional(false); return sound; - } catch (Exception ex) { + } + catch (AssetLoadException | AssetNotFoundException ex) { LOGGER.log(Level.ERROR, ex.getMessage(), ex); } return null; } /** - * Handles sound-related game events to play specific sounds. - * - * @param event The sound event received. + * Plays the splash sound effect. */ + public void splash() { + if (isEnabled() && splashSound != null) + splashSound.playInstance(); + } + + /** + * Plays the explosion sound effect. + */ + public void explosion() { + if (isEnabled() && explosionSound != null) + explosionSound.playInstance(); + } + + /** + * Plays sound effect when a ship has been destroyed. + */ + public void shipDestroyed() { + if (isEnabled() && shipDestroyedSound != null) + shipDestroyedSound.playInstance(); + } + @Override public void receivedEvent(SoundEvent event) { - if (isEnabled()) { - AudioNode sound = loadSound(event.getSoundFileName()); - if (sound != null) { - sound.play(); - } + switch (event.sound()) { } } -}//heloo +} 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 68c73d9..baac5f7 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 @@ -9,12 +9,7 @@ import com.jme3.font.BitmapText; import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.KeyTrigger; -import com.jme3.material.Material; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.shape.Box; import com.jme3.system.AppSettings; -import com.jme3.texture.Texture; import com.simsilica.lemur.GuiGlobals; import com.simsilica.lemur.style.BaseStyles; @@ -22,6 +17,7 @@ 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; @@ -36,10 +32,20 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga private final ActionListener escapeListener = (name, isPressed, tpf) -> handleEscape(isPressed); private final DialogManager dialogManager = new DialogManager(this); private final ExecutorService executor = Executors.newCachedThreadPool(); - private SettingsMenu settingsMenu; private final Draw draw; - + private SettingsMenu settingsMenu; + private TestWorld testWorld; private boolean isSettingsMenuOpen = false; + private boolean inputBlocked = false; + /** + * 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 + public static void main(String[] args) { new MonopolyApp().start(); @@ -55,55 +61,6 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga setSettings(makeSettings()); } - /** - * Creates and configures application settings from the client configuration. - * - * @return A configured {@link AppSettings} object. - */ - private AppSettings makeSettings() { - final AppSettings settings = new AppSettings(true); - settings.setTitle(lookup("monopoly.name")); - settings.setResolution(config.getResolutionWidth(), config.getResolutionHeight()); - settings.setFullscreen(config.fullScreen()); - settings.setUseRetinaFrameBuffer(config.useRetinaFrameBuffer()); - settings.setGammaCorrection(config.useGammaCorrection()); - return settings; - } - - /** - * Factory method for creating a server connection based on the current - * client configuration. - * - * @return A {@link ServerConnection} instance, which could be a real or mock server. - */ - private ServerConnection makeServerConnection() { - return new NetworkSupport(this); - } - - /** - * Returns the dialog manager responsible for managing in-game dialogs. - * - * @return The {@link DialogManager} instance. - */ - public DialogManager getDialogManager() { - return dialogManager; - } - - /** - * Returns the game logic handler for the client. - * - * @return The {@link ClientGameLogic} instance. - */ - @Override - public ClientGameLogic getGameLogic() { - return logic; - } - - /** - * Returns the current configuration settings for the Monopoly client. - * - * @return The {@link MonopolyClientConfig} instance. - */ @Override public MonopolyAppConfig getConfig() { return config; @@ -122,24 +79,17 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga return settings; } - public boolean isSettingsMenuOpen() { - return isSettingsMenuOpen; - } - - public void setSettingsMenuOpen(boolean isOpen) { - this.isSettingsMenuOpen = isOpen; - } - @Override public void simpleInitApp() { - GuiGlobals.initialize(this); - BaseStyles.loadGlassStyle(); - GuiGlobals.getInstance().getStyles().setDefaultStyle("glass"); + GuiGlobals.initialize(this); + BaseStyles.loadStyleResources(STYLES_SCRIPT); + GuiGlobals.getInstance().getStyles().setDefaultStyle("pp"); //NON-NLS + final BitmapFont normalFont = assetManager.loadFont(FONT); //NON-NLS setupInput(); setupGui(); - // Initialisiere das Startmenü + // Zeige das Startmenü StartMenu.createStartMenu(this); } @@ -156,23 +106,45 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga inputManager.addMapping("ESC", new KeyTrigger(KeyInput.KEY_ESCAPE)); inputManager.addListener(escapeListener, "ESC"); } - private void handleEscape(boolean isPressed) { if (isPressed) { - if (isSettingsMenuOpen && settingsMenu != null) { + if (settingsMenu != null && isSettingsMenuOpen) { + // Schließe das SettingsMenu + System.out.println("Schließe SettingsMenu..."); settingsMenu.close(); + settingsMenu = null; setSettingsMenuOpen(false); } else { + // Öffne das SettingsMenu + System.out.println("Öffne SettingsMenu..."); settingsMenu = new SettingsMenu(this); settingsMenu.open(); setSettingsMenuOpen(true); } } } + + + 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; + } + } - void setInfoText(String text) { + public void setInfoText(String text) { topText.setText(text); } @@ -212,35 +184,26 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga .build() .open(); } - //altes Fenster beim Start von TestWorld schließen - public void startTestWorld() { - // Entferne die aktuelle GUI - guiNode.detachAllChildren(); - - // Erstelle ein Quadrat mit Textur - Box box = new Box(1, 0.01f, 1); // Dünnes Quadrat - Geometry geom = new Geometry("Box", box); - - // Setze das Material mit Textur - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture texture = assetManager.loadTexture("Pictures/board.png"); - mat.setTexture("ColorMap", texture); - geom.setMaterial(mat); - - // Füge das Quadrat zur Szene hinzu - rootNode.attachChild(geom); - - // Setze die Kameraposition - cam.setLocation(new Vector3f(0, 0, 3)); - cam.lookAt(geom.getLocalTranslation(), Vector3f.UNIT_Y); - } - public void returnToMenu() { - // Entferne die Testszene - rootNode.detachAllChildren(); - - // Zeige das Startmenü erneut - StartMenu.createStartMenu(this); - } - + 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 + } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyAppConfig.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyAppConfig.java index e126f75..15a69f1 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyAppConfig.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyAppConfig.java @@ -1,10 +1,3 @@ -//////////////////////////////////////// -// Programming project code -// UniBw M, 2022, 2023, 2024 -// www.unibw.de/inf2 -// (c) Mark Minas (mark.minas@unibw.de) -//////////////////////////////////////// - package pp.monopoly.client; import com.jme3.math.ColorRGBA; @@ -194,4 +187,4 @@ public class MonopolyAppConfig extends MonopolyClientConfig { public ColorRGBA getTopColor() { return topColor; } -} +} \ No newline at end of file diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyAppState.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyAppState.java index 76b002e..ec11aa4 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyAppState.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyAppState.java @@ -81,4 +81,4 @@ public abstract class MonopolyAppState extends AbstractAppState { * Called when the state is disabled. Override to define specific behavior. */ protected abstract void disableState(); -} +} \ No newline at end of file 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 index 6671b7b..d399636 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/NetworkDialog.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/NetworkDialog.java @@ -142,4 +142,5 @@ class NetworkDialog extends SimpleDialog { 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 065bcf6..3ea11b0 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 @@ -141,4 +141,4 @@ class NetworkSupport implements MessageListener<Client>, ClientStateListener, Se client.send(message); } } -} +} \ No newline at end of file diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java index 75c37a2..e1796cc 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java @@ -1,117 +1,182 @@ package pp.monopoly.client; -import com.jme3.asset.TextureKey; 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.Button; -import com.simsilica.lemur.Insets3f; -import com.simsilica.lemur.Label; -import com.simsilica.lemur.Panel; -import com.simsilica.lemur.style.ElementId; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.HAlignment; import com.simsilica.lemur.component.QuadBackgroundComponent; -import com.jme3.math.ColorRGBA; +import com.simsilica.lemur.component.SpringGridLayout; + import pp.dialog.Dialog; +import pp.monopoly.client.gui.CreateGameMenu; +import pp.monopoly.client.gui.SettingsMenu; + +/** + * Constructs the startup menu dialog for the Monopoly application. import pp.monopoly.client.gui.GameMenu; -import pp.dialog.DialogManager; - -import java.util.prefs.Preferences; - -import static pp.monopoly.Resources.lookup; -import static pp.util.PreferencesUtils.getPreferences; - + */ public class StartMenu extends Dialog { - private static final Preferences PREFERENCES = getPreferences(StartMenu.class); private final MonopolyApp app; - - // Buttons for the menu - private final Button playButton = new Button(lookup("button.play")); - private final Button quitButton = new Button(lookup("menu.quit")); - private final Button settingsButton = new Button("Einstellungen", new ElementId("menu-button")); + private Container logoContainer; + private Container unibwLogoContainer; /** - * Constructs the StartMenu dialog for the Monopoly application. + * Constructs the Startup Menu dialog for the Monopoly application. * * @param app the MonopolyApp instance */ public StartMenu(MonopolyApp app) { super(app.getDialogManager()); this.app = app; + } - // Load and display the background image - TextureKey backgroundKey = new TextureKey("unibw-bib", false); - Texture backgroundTexture = app.getAssetManager().loadTexture(backgroundKey); + /** + * 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(); + + // Set up the background image + Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png"); + Quad quad = new Quad(screenWidth, screenHeight); + Geometry background = new Geometry("Background", quad); Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - backgroundMaterial.setTexture("ColorMap", backgroundTexture); - - // Create a large Quad for the background - Quad backgroundQuad = new Quad(16, 9); // Adjust size as necessary to fill the screen - Geometry background = new Geometry("Background", backgroundQuad); + backgroundMaterial.setTexture("ColorMap", backgroundImage); background.setMaterial(backgroundMaterial); - background.setLocalTranslation(new Vector3f(-8, -4.5f, -1)); // Position it behind the UI components - - // Attach the background as the first element + background.setLocalTranslation(0, 0, -1); // Ensure it is behind other GUI elements app.getGuiNode().attachChild(background); - // Load and display the Monopoly logo - TextureKey monopolyLogoKey = new TextureKey("log-Monopoly", false); - Texture monopolyLogoTexture = app.getAssetManager().loadTexture(monopolyLogoKey); - Material monopolyLogoMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - monopolyLogoMaterial.setTexture("ColorMap", monopolyLogoTexture); + createMonopolyLogo(app); + createUnibwLogo(app); - Quad monopolyQuad = new Quad(5, 1.5f); // Adjust dimensions as necessary - Geometry monopolyLogo = new Geometry("MonopolyLogo", monopolyQuad); - monopolyLogo.setMaterial(monopolyLogoMaterial); - monopolyLogo.setLocalTranslation(new Vector3f(0, 5, 0)); // Position Monopoly logo at the top + // Center container for title and play button + Container centerMenu = new Container(new SpringGridLayout(Axis.Y, Axis.X)); - Panel monopolyLogoPanel = new Panel(); - addChild(monopolyLogoPanel); + Button startButton = new Button("Spielen"); + startButton.setPreferredSize(new Vector3f(190, 60, 0)); // Increase button size (width, height) + startButton.setFontSize(40); // Set the font size for the button text + startButton.setTextHAlignment(HAlignment.Center); // Center the text horizontally - // Load and display the university logo - TextureKey universityLogoKey = new TextureKey("unibw-logo.png", false); - Texture universityLogoTexture = app.getAssetManager().loadTexture(universityLogoKey); - Material universityLogoMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); - universityLogoMaterial.setTexture("ColorMap", universityLogoTexture); + startButton.addClickCommands(source -> startGame(app)); + centerMenu.addChild(startButton); - Quad universityQuad = new Quad(4, 1); // Adjust dimensions to fit below Monopoly logo - Geometry universityLogo = new Geometry("UniversityLogo", universityQuad); - universityLogo.setMaterial(universityLogoMaterial); - universityLogo.setLocalTranslation(new Vector3f(0, 3, 0)); // Position below the Monopoly logo + // Position the center container in the middle of the screen + centerMenu.setLocalTranslation(new Vector3f(screenWidth / 2f - centerMenu.getPreferredSize().x / 2f, + screenHeight / 2f - 280 + centerMenu.getPreferredSize().y / 2f, + 0)); + app.getGuiNode().attachChild(centerMenu); - Panel universityLogoPanel = new Panel(); - addChild(universityLogoPanel); + // 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); - - - // Button actions - playButton.addClickCommands(source -> startGame()); - quitButton.addClickCommands(source -> app.closeApp()); - settingsButton.addClickCommands(source -> openSettings()); - - addChild(monopolyLogoPanel); - addChild(universityLogoPanel); - addChild(playButton); - addChild(quitButton); - addChild(settingsButton); + // 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); } - private void startGame() { - System.out.println("Starting game..."); + /** + * 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"); + + // Create a container for the logo + Container logoContainer = new Container(); + QuadBackgroundComponent logoBackground = new QuadBackgroundComponent(logoTexture); + logoContainer.setBackground(logoBackground); + + // Set the size of the container to fit the logo + float logoWidth = 512; // Adjust these values based on the logo dimensions + float logoHeight = 128; // Adjust these values based on the logo dimensions + logoContainer.setPreferredSize(new Vector3f(logoWidth, logoHeight, 0)); + + // Position the container at the center of the screen + logoContainer.setLocalTranslation(new Vector3f( + screenWidth / 2f - logoWidth / 2f, + screenHeight / 2f + 200, // Adjust this value for vertical position + 0 + )); + + // Attach the container to the GUI node + app.getGuiNode().attachChild(logoContainer); } - private void openSettings() { - app.getDialogManager().close(this); - app.getDialogManager().open(new GameMenu(app)); + /** + * 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"); + + // Create a container for the Unibw logo + Container unibwContainer = new Container(); + QuadBackgroundComponent unibwBackground = new QuadBackgroundComponent(unibwTexture); + unibwContainer.setBackground(unibwBackground); + + // Set the size of the container to fit the Unibw logo + float unibwWidth = 512; // Adjust these values based on the logo dimensions + float unibwHeight = 128; // Adjust these values based on the logo dimensions + unibwContainer.setPreferredSize(new Vector3f(unibwWidth, unibwHeight, 0)); + + // Position the container slightly below the Monopoly logo + unibwContainer.setLocalTranslation(new Vector3f( + screenWidth / 2f - unibwWidth / 2f, + screenHeight / 2f + 100, // Adjust this value for vertical position + 0 + )); + + // Attach the container to the GUI node + app.getGuiNode().attachChild(unibwContainer); } - @Override - public void update() { + /** + * Starts the game by transitioning to the CreateGameMenu. + */ + private static void startGame(MonopolyApp app) { + app.getGuiNode().detachAllChildren(); + new CreateGameMenu(app); } - @Override - public void escape() { - close(); + /** + * Opens the settings menu. + */ + private static void openSettings(MonopolyApp app) { + app.getGuiNode().detachAllChildren(); + new SettingsMenu(app); } -} + + /** + * Quits the game application. + */ + private static void quitGame() { + System.exit(0); + } +} \ No newline at end of file diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BoardSynchronizer.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BoardSynchronizer.java index f1819e4..007389d 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BoardSynchronizer.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/BoardSynchronizer.java @@ -17,7 +17,7 @@ import pp.view.ModelViewSynchronizer; * are accurately reflected in the view. */ abstract class BoardSynchronizer extends ModelViewSynchronizer<Item> implements Visitor<Spatial>, GameEventListener { - private final Board board; + protected final Board board; /** * Constructs a new BoardSynchronizer. 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 new file mode 100644 index 0000000..314ebb6 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CameraController.java @@ -0,0 +1,59 @@ +package pp.monopoly.client.gui; + +import com.jme3.math.FastMath; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; + +/** + * Steuert die Kamerabewegung in der Szene. + */ +public class CameraController { + private final Camera camera; + private final Vector3f center; // Fokuspunkt der Kamera + private final float radius; // Radius der Kreisbewegung + private final float height; // Höhe der Kamera über dem Spielfeld + private final float speed; // Geschwindigkeit der Kamerabewegung + private float angle; // Aktueller Winkel in der Kreisbewegung + + /** + * Konstruktor für den CameraController. + * + * @param camera Die Kamera, die gesteuert werden soll + * @param center Der Mittelpunkt der Kreisbewegung (Fokuspunkt) + * @param radius Der Radius der Kreisbewegung + * @param height Die Höhe der Kamera über dem Fokuspunkt + * @param speed Die Geschwindigkeit der Kamerabewegung + */ + public CameraController(Camera camera, Vector3f center, float radius, float height, float speed) { + this.camera = camera; + this.center = center; + this.radius = radius; + this.height = height; + this.speed = speed; + this.angle = 0; // Starte bei Winkel 0 + } + + /** + * Aktualisiert die Kameraposition und -ausrichtung. + * + * @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); + } +} 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 6b88b62..5472da4 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 @@ -23,6 +23,11 @@ public class CreateGameMenu { private final Container menuContainer; private Geometry background; + /** + * Konstruktor für das CreateGameMenu. + * + * @param app Die Hauptanwendung (MonopolyApp) + */ public CreateGameMenu(MonopolyApp app) { this.app = app; @@ -43,12 +48,12 @@ public class CreateGameMenu { inputContainer.setLocalTranslation(20, 0, 0); // Abstand vom Rand inputContainer.addChild(new Label("Server-Adresse:")); - TextField playerNameField = inputContainer.addChild(new TextField("localhost")); - playerNameField.setPreferredWidth(400); // Breite des Textfelds + TextField serverAddressField = inputContainer.addChild(new TextField("localhost")); + serverAddressField.setPreferredWidth(400); // Breite des Textfelds inputContainer.addChild(new Label("Port:")); - TextField serverAddressField = inputContainer.addChild(new TextField("42069")); - serverAddressField.setPreferredWidth(400); // Breite des Textfelds + 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))); @@ -64,15 +69,14 @@ public class CreateGameMenu { 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(); // Startet die Testwelt in der Hauptanwendung -}); - + 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)); - // joinButton.addClickCommands(source -> joinGame()); // Placeholder for joining logic + // Placeholder für die Beitrittslogik // Zentrierung des Containers menuContainer.setLocalTranslation( @@ -103,27 +107,15 @@ public class CreateGameMenu { * Geht zum Startmenü zurück, wenn "Abbrechen" angeklickt wird. */ private void goBackToStartMenu() { - app.getGuiNode().detachChild(menuContainer); - app.getGuiNode().detachChild(background); // Entfernt das Hintergrundbild - StartMenu.createStartMenu(app); - } - /* - * Link zwischen createGame und TestWorld - */ - - private void startTestWorld() { - // Entfernt das Menü - app.getGuiNode().detachChild(menuContainer); - app.getGuiNode().detachChild(background); - - // Startet die Testszene - TestWorld.startTestWorld(); + closeCreateGameMenu(); // Schließt das Menü + StartMenu.createStartMenu(app); // Zeige das Startmenü } + /** + * Entfernt das CreateGameMenu und dessen Hintergrund. + */ private void closeCreateGameMenu() { app.getGuiNode().detachChild(menuContainer); // Entfernt den Menü-Container - app.getGuiNode().detachChild(background); // Entfernt das Hintergrundbild + app.getGuiNode().detachChild(background); // Entfernt das Hintergrundbild } - - } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java new file mode 100644 index 0000000..637f7ab --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java @@ -0,0 +1,124 @@ +//////////////////////////////////////// +// 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.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Box; + +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.game.server.PlayerColor; +import pp.monopoly.model.Board; +import pp.monopoly.model.Figure; +import pp.monopoly.model.Rotation; +import static pp.util.FloatMath.HALF_PI; +import static pp.util.FloatMath.PI; + +/** + * The {@code GameBoardSynchronizer} class is responsible for synchronizing the graphical + * representation of the ships and shots on the sea map with the underlying data model. + * It extends the {@link BoardSynchronizer} to provide specific synchronization + * logic for the sea map. + */ +class GameBoardSynchronizer extends BoardSynchronizer { + private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS + private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md"; + private static final String COLOR = "Color"; //NON-NLS + private static final String FIGURE = "figure"; //NON-NLS + + private final MonopolyApp app; + private final ParticleEffectFactory particleFactory; + + /** + * Constructs a {@code GameBoardSynchronizer} object with the specified application, root node, and ship map. + * + * @param app the Battleship application + * @param root the root node to which graphical elements will be attached + * @param map the ship map containing the ships and shots + */ + public GameBoardSynchronizer(MonopolyApp app, Node root, Board board) { + super(board, root); + this.app = app; + this.particleFactory = new ParticleEffectFactory(app); + addExisting(); + } + + /** + * Visits a {@link Battleship} and creates a graphical representation of it. + * The representation is either a 3D model or a simple box depending on the + * type of battleship. + * + * @param ship the battleship to be represented + * @return the node containing the graphical representation of the battleship + */ + public Spatial visit(Figure figure) { + final Node node = new Node(FIGURE); + node.attachChild(createBox(figure)); + // compute the center of the ship in world coordinates + final float x = 1; + final float z = 1; + node.setLocalTranslation(x, 0f, z); + return node; + } + + /** + * Creates a simple box to represent a battleship that is not of the "King George V" type. + * + * @param ship the battleship to be represented + * @return the geometry representing the battleship as a box + */ + private Spatial createBox(Figure figure) { + final Box box = new Box(0.5f * (figure.getMaxY() - figure.getMinY()) + 0.3f, + 0.3f, + 0.5f * (figure.getMaxX() - figure.getMinX()) + 0.3f); + final Geometry geometry = new Geometry(FIGURE, box); + geometry.setMaterial(createColoredMaterial(PlayerColor.PINK.getColor())); + geometry.setShadowMode(ShadowMode.CastAndReceive); + + return geometry; + } + + /** + * Creates a new {@link Material} with the specified color. + * If the color includes transparency (i.e., alpha value less than 1), + * the material's render state is set to use alpha blending, allowing for + * semi-transparent rendering. + * + * @param color the {@link ColorRGBA} to be applied to the material. If the alpha value + * of the color is less than 1, the material will support transparency. + * @return a {@link Material} instance configured with the specified color and, + * if necessary, alpha blending enabled. + */ + private Material createColoredMaterial(ColorRGBA color) { + final Material material = new Material(app.getAssetManager(), UNSHADED); + if (color.getAlpha() < 1f) + material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + material.setColor(COLOR, color); + return material; + } + + /** + * Calculates the rotation angle for the specified rotation. + * + * @param rot the rotation of the battleship + * @return the rotation angle in radians + */ + private static float calculateRotationAngle(Rotation rot) { + return switch (rot) { + case RIGHT -> HALF_PI; + case DOWN -> 0f; + case LEFT -> -HALF_PI; + case UP -> PI; + }; + } +} 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 index ef5dd78..da390c8 100644 --- 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 @@ -5,6 +5,7 @@ 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; 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 index 638e402..f93c1d5 100644 --- 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 @@ -55,7 +55,7 @@ class MapView { 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, 0f, BACKGROUND_DEPTH); + background.setLocalTranslation(0f, 1f, BACKGROUND_DEPTH); background.setCullHint(CullHint.Never); mapNode.attachChild(background); } 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 index c239394..4853356 100644 --- 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 @@ -1,5 +1,9 @@ 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. @@ -33,4 +37,9 @@ class MapViewSynchronizer extends BoardSynchronizer { 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/ParticleEffectFactory.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/ParticleEffectFactory.java new file mode 100644 index 0000000..790400c --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/ParticleEffectFactory.java @@ -0,0 +1,22 @@ +package pp.monopoly.client.gui; + +import com.jme3.effect.ParticleMesh.Type; + +import pp.monopoly.client.MonopolyApp; + +/** + * Factory class responsible for creating particle effects used in the game. + * This centralizes the creation of various types of particle emitters. + */ +public class ParticleEffectFactory { + private static final int COUNT_FACTOR = 1; + private static final float COUNT_FACTOR_F = 1f; + private static final boolean POINT_SPRITE = true; + private static final Type EMITTER_TYPE = POINT_SPRITE ? Type.Point : Type.Triangle; + + private final MonopolyApp app; + + ParticleEffectFactory(MonopolyApp app) { + this.app = app; + } +} \ No newline at end of file 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 eb882da..6997c45 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,10 +1,10 @@ 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.shape.Quad; -import com.jme3.texture.Texture; import com.simsilica.lemur.Button; import com.simsilica.lemur.Checkbox; import com.simsilica.lemur.Container; @@ -16,122 +16,85 @@ import com.simsilica.lemur.style.ElementId; import pp.dialog.Dialog; import pp.monopoly.client.MonopolyApp; +/** + * SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann. + */ public class SettingsMenu extends Dialog { private final MonopolyApp app; + private final Geometry overlayBackground; private final Container settingsContainer; - private Geometry blockLayer; - private final Node savedGuiNodeContent = new Node("SavedGuiNodeContent"); public SettingsMenu(MonopolyApp app) { super(app.getDialogManager()); this.app = app; - // Blockierungsebene hinzufügen - addBlockLayer(); - - // Hintergrundbild - addBackgroundImage(); + // Halbtransparentes Overlay hinzufügen + overlayBackground = createOverlayBackground(); + app.getGuiNode().attachChild(overlayBackground); + // Hauptcontainer für das Menü settingsContainer = new Container(); + settingsContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f))); - // Hintergrundfarbe für das Container-Element setzen, um es undurchsichtig zu machen - settingsContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.8f))); // Teiltransparent, falls gewünscht - - // Titel "Einstellungen" + // Titel Label settingsTitle = settingsContainer.addChild(new Label("Einstellungen", new ElementId("settings-title"))); settingsTitle.setFontSize(48); - settingsTitle.setColor(ColorRGBA.White); - // Effekt Sound mit Slider und Checkbox + // Effekt-Sound: Slider und Checkbox Container effectSoundContainer = settingsContainer.addChild(new Container()); - Label effectSoundLabel = effectSoundContainer.addChild(new Label("Effekt Sound", new ElementId("label"))); - effectSoundLabel.setFontSize(24); - effectSoundLabel.setColor(ColorRGBA.White); + effectSoundContainer.addChild(new Label("Effekt Sound", new ElementId("label"))); + effectSoundContainer.addChild(new Slider()); + effectSoundContainer.addChild(new Checkbox("Aktivieren")).setChecked(true); - Slider effectSoundSlider = effectSoundContainer.addChild(new Slider()); - effectSoundSlider.setPreferredSize(new com.jme3.math.Vector3f(300, 30, 0)); - - Checkbox effectSoundCheckbox = effectSoundContainer.addChild(new Checkbox("")); - effectSoundCheckbox.setChecked(true); - - // Hintergrund Musik mit Slider und Checkbox + // Hintergrundmusik: Slider und Checkbox Container backgroundMusicContainer = settingsContainer.addChild(new Container()); - Label backgroundMusicLabel = backgroundMusicContainer.addChild(new Label("Hintergrund Musik", new ElementId("label"))); - backgroundMusicLabel.setFontSize(24); - backgroundMusicLabel.setColor(ColorRGBA.White); + backgroundMusicContainer.addChild(new Label("Hintergrund Musik", new ElementId("label"))); + backgroundMusicContainer.addChild(new Slider()); + backgroundMusicContainer.addChild(new Checkbox("Aktivieren")).setChecked(true); - Slider backgroundMusicSlider = backgroundMusicContainer.addChild(new Slider()); - backgroundMusicSlider.setPreferredSize(new com.jme3.math.Vector3f(300, 30, 0)); - - Checkbox backgroundMusicCheckbox = backgroundMusicContainer.addChild(new Checkbox("")); - backgroundMusicCheckbox.setChecked(true); - - // Beenden Button + // Beenden-Button Button quitButton = settingsContainer.addChild(new Button("Beenden", new ElementId("menu-button"))); quitButton.setFontSize(32); - quitButton.setColor(ColorRGBA.White); quitButton.addClickCommands(source -> app.stop()); - // Zentrieren des Containers + // Zentriere das Menü settingsContainer.setLocalTranslation( (app.getCamera().getWidth() - settingsContainer.getPreferredSize().x) / 2, (app.getCamera().getHeight() + settingsContainer.getPreferredSize().y) / 2, - 1 // Höhere Z-Ebene für den Vordergrund + 1 ); app.getGuiNode().attachChild(settingsContainer); } - private void addBlockLayer() { - // Sichern des aktuellen GUI-Inhalts - for (var child : app.getGuiNode().getChildren()) { - savedGuiNodeContent.attachChild(child); - } - app.getGuiNode().detachAllChildren(); - - // Blockierungsebene erstellen und hinzufügen - blockLayer = new Geometry("BlockLayer", new Quad(app.getCamera().getWidth(), app.getCamera().getHeight())); - blockLayer.setMaterial(createTransparentMaterial()); - blockLayer.setLocalTranslation(0, 0, 0); // Platzierung unterhalb des SettingsMenu - app.getGuiNode().attachChild(blockLayer); - } - - private com.jme3.material.Material createTransparentMaterial() { - com.jme3.material.Material material = new com.jme3.material.Material( - app.getAssetManager(), - "Common/MatDefs/Misc/Unshaded.j3md" - ); - material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent - material.getAdditionalRenderState().setBlendMode(com.jme3.material.RenderState.BlendMode.Alpha); - return material; - } - - private void addBackgroundImage() { - Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png"); + /** + * 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 background = new Geometry("Background", quad); - com.jme3.material.Material backgroundMaterial = new com.jme3.material.Material( - app.getAssetManager(), - "Common/MatDefs/Misc/Unshaded.j3md" - ); - backgroundMaterial.setTexture("ColorMap", backgroundImage); - background.setMaterial(backgroundMaterial); - background.setLocalTranslation(0, 0, -1); // Platzierung hinter dem SettingsMenu - app.getGuiNode().attachChild(background); + 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() { - // Entfernt das SettingsMenu und die Blockierungsebene - app.getGuiNode().detachChild(settingsContainer); - app.getGuiNode().detachChild(blockLayer); - - // Stellt die ursprüngliche GUI wieder her - for (var child : savedGuiNodeContent.getChildren()) { - app.getGuiNode().attachChild(child); - } - savedGuiNodeContent.detachAllChildren(); - - app.setSettingsMenuOpen(false); + 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 } + + } 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 b0a82fc..50a2a23 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,39 +1,107 @@ package pp.monopoly.client.gui; -import com.jme3.app.SimpleApplication; 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.Box; import com.jme3.texture.Texture; +import pp.monopoly.client.MonopolyApp; + /** * TestWorld zeigt eine einfache Szene mit einem texturierten Quadrat. + * Die Kamera wird durch den CameraController gesteuert. */ -public class TestWorld extends SimpleApplication { +public class TestWorld { - @Override - public void simpleInitApp() { + private final MonopolyApp app; + private CameraController cameraController; // Steuert die Kamera + private Geometry cube; // Spielfigur + + /** + * Konstruktor für TestWorld. + * + * @param app Die Hauptanwendung (MonopolyApp) + */ + public TestWorld(MonopolyApp app) { + this.app = app; + } + + /** + * Initialisiert die Szene und startet die Kamerabewegung. + */ + public void initializeScene() { + app.getGuiNode().detachAllChildren(); // Entferne GUI + app.getRootNode().detachAllChildren(); // Entferne andere Szenenobjekte + + 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 + ); + + // Füge die Toolbar hinzu + new Toolbar(app, cube); + } + + /** + * Aktualisiert die Kameraposition. + * + * @param tpf Zeit pro Frame + */ + public void update(float tpf) { + if (cameraController != null) { + cameraController.update(tpf); + } + } + + /** + * Setzt die Hintergrundfarbe der Szene auf hellblau. + */ + private void setSkyColor() { + app.getViewPort().setBackgroundColor(new ColorRGBA(0.5f, 0.7f, 1.0f, 1.0f)); // Hellblauer Himmel + } + + /** + * Erstelle das Spielfeld. + */ + private void createBoard() { // Erstelle ein Quadrat Box box = new Box(1, 0.01f, 1); // Dünnes Quadrat für die Textur - Geometry geom = new Geometry("Box", box); + Geometry geom = new Geometry("Board", box); // Setze das Material mit Textur - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture texture = assetManager.loadTexture("Pictures/board.png"); // Ersetze durch den Pfad zum gewünschten Bild + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + Texture texture = app.getAssetManager().loadTexture("Pictures/board.png"); mat.setTexture("ColorMap", texture); geom.setMaterial(mat); - // Füge das Quadrat zur Szene hinzu - rootNode.attachChild(geom); - - // Setze die Kameraposition, um das Quadrat zu fokussieren - cam.setLocation(new Vector3f(0, 0, 3)); // Kamera auf der Z-Achse, nah am Quadrat - cam.lookAt(geom.getLocalTranslation(), Vector3f.UNIT_Y); + app.getRootNode().attachChild(geom); } - public static void startTestWorld() { - TestWorld testWorldApp = new TestWorld(); - testWorldApp.start(); + /** + * 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/TestWorldWithMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/TestWorldWithMenu.java deleted file mode 100644 index 8240049..0000000 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/TestWorldWithMenu.java +++ /dev/null @@ -1,80 +0,0 @@ -package pp.monopoly.client.gui; - -import com.jme3.app.SimpleApplication; -import com.jme3.material.Material; -import com.jme3.math.Vector3f; -import com.jme3.scene.Geometry; -import com.jme3.scene.shape.Box; -import com.jme3.texture.Texture; -import com.jme3.system.JmeCanvasContext; -import com.jme3.system.AppSettings; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; - -public class TestWorldWithMenu extends SimpleApplication { - - public static void createAndShowGUI() { - // Create JFrame - JFrame frame = new JFrame("Test World with Menu"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.setLayout(new BorderLayout()); - frame.setSize(800, 600); - - // Create Menu Bar - JMenuBar menuBar = new JMenuBar(); - JMenu fileMenu = new JMenu("File"); - JMenuItem exitItem = new JMenuItem(new AbstractAction("Exit") { - @Override - public void actionPerformed(ActionEvent e) { - System.exit(0); - } - }); - fileMenu.add(exitItem); - menuBar.add(fileMenu); - frame.setJMenuBar(menuBar); - - // Create Canvas for jMonkey - AppSettings settings = new AppSettings(true); - settings.setWidth(800); - settings.setHeight(600); - - TestWorldWithMenu app = new TestWorldWithMenu(); - app.setSettings(settings); - app.createCanvas(); // Create a canvas for embedding - JmeCanvasContext ctx = (JmeCanvasContext) app.getContext(); - ctx.setSystemListener(app); - Canvas canvas = ctx.getCanvas(); - canvas.setSize(800, 600); - - // Add the canvas to JFrame - frame.add(canvas, BorderLayout.CENTER); - - // Show the frame - frame.setVisible(true); - - // Start the jMonkeyEngine application - app.startCanvas(); - } - - @Override - public void simpleInitApp() { - // Erstelle ein Quadrat - Box box = new Box(1, 0.01f, 1); // Dünnes Quadrat für die Textur - Geometry geom = new Geometry("Box", box); - - // Setze das Material mit Textur - Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md"); - Texture texture = assetManager.loadTexture("Pictures/board.png"); // Replace with the path to your image - mat.setTexture("ColorMap", texture); - geom.setMaterial(mat); - - // Füge das Quadrat zur Szene hinzu - rootNode.attachChild(geom); - - // Setze die Kameraposition, um das Quadrat zu fokussieren - cam.setLocation(new Vector3f(0, 0, 3)); // Kamera auf der Z-Achse, nah am Quadrat - cam.lookAt(geom.getLocalTranslation(), Vector3f.UNIT_Y); - } -} 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 new file mode 100644 index 0000000..5a9a3f0 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar.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 Toolbar { + + 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 Toolbar(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/resources/Models/Würfel_blau.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_blau.j30 new file mode 100644 index 0000000..eb3b559 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_blau.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_gelb.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_gelb.j30 new file mode 100644 index 0000000..e0156e1 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_gelb.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_grün.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_grün.j30 new file mode 100644 index 0000000..e442c42 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_grün.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_rosa.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_rosa.j30 new file mode 100644 index 0000000..cb0c503 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_rosa.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_rot.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_rot.j30 new file mode 100644 index 0000000..ec70137 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_rot.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_schwarz.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_schwarz.j30 new file mode 100644 index 0000000..d61fbdf Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_schwarz.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/logo-unibw.png b/Projekte/monopoly/client/src/main/resources/Pictures/logo-unibw.png index 5d1b76f..bf1c878 100644 Binary files a/Projekte/monopoly/client/src/main/resources/Pictures/logo-unibw.png and b/Projekte/monopoly/client/src/main/resources/Pictures/logo-unibw.png differ 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 e05af47..f972be6 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 @@ -7,6 +7,13 @@ 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.message.client.ClientMessage; import pp.monopoly.message.server.BuyPropertyResponse; import pp.monopoly.message.server.DiceResult; @@ -19,10 +26,9 @@ import pp.monopoly.message.server.ServerInterpreter; import pp.monopoly.message.server.TimeOutWarning; import pp.monopoly.message.server.TradeReply; import pp.monopoly.message.server.TradeRequest; -import pp.monopoly.message.server.UpdatePlayerAssets; import pp.monopoly.message.server.ViewAssetsResponse; -import pp.monopoly.model.IntPoint; import pp.monopoly.model.Board; +import pp.monopoly.model.IntPoint; import pp.monopoly.notification.ClientStateEvent; import pp.monopoly.notification.GameEvent; import pp.monopoly.notification.GameEventBroker; @@ -38,18 +44,6 @@ import java.lang.System.Logger.Level; import java.util.ArrayList; import java.util.List; -import pp.monopoly.message.client.ClientMessage; -import pp.monopoly.message.server.ServerInterpreter; -import pp.monopoly.model.Board; -import pp.monopoly.model.IntPoint; -import pp.monopoly.notification.ClientStateEvent; -import pp.monopoly.notification.GameEvent; -import pp.monopoly.notification.GameEventBroker; -import pp.monopoly.notification.GameEventListener; -import pp.monopoly.notification.InfoTextEvent; -import pp.monopoly.notification.Sound; -import pp.monopoly.notification.SoundEvent; - /** * Controls the client-side game logic for Monopoly. * Manages the player's placement, interactions with the map, and response to server messages. @@ -58,9 +52,8 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { static final Logger LOGGER = System.getLogger(ClientGameLogic.class.getName()); private final ClientSender clientSender; private final List<GameEventListener> listeners = new ArrayList<>(); - private Board ownMap; - private Board harbor; - private Board opponentMap; + private Board board; + private ClientState state = new ClientState(this) { }; @@ -98,8 +91,8 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { * * @return the player's own map */ - public Board getMap() { - return ownMap; + public Board getBoard() { + return board; } /** @@ -195,73 +188,73 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { @Override public void received(BuyPropertyResponse msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); + if (msg.isSuccessful()) { + setInfoText("You successfully bought " + msg.getPropertyName() + "!"); + playSound(Sound.MONEY_LOST); + } else { + setInfoText("Unable to buy " + msg.getPropertyName() + ". Reason: " + msg.getReason()); + } } - + @Override public void received(DiceResult msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); + setInfoText("You rolled a " + msg.calcTotal() + "!"); + playSound(Sound.DICE_ROLL); } - + @Override public void received(EventDrawCard msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); + setInfoText("Event card drawn: " + msg.getCardDescription()); + playSound(Sound.EVENT_CARD); } - + @Override public void received(GameOver msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); + if (msg.isWinner()) { + setInfoText("Congratulations! You have won the game!"); + playSound(Sound.WINNER); + } else { + setInfoText("Game over. Better luck next time!"); + playSound(Sound.LOSER); + } } - + @Override public void received(GameStart msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); + setInfoText("The game has started! Good luck!"); } - + @Override public void received(JailEvent msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); + if (msg.isGoingToJail()) { + setInfoText("You are sent to jail!"); + playSound(Sound.GULAG); + } else { + setInfoText("You are out of jail!"); + } } - + @Override public void received(PlayerStatusUpdate msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); + setInfoText("Player " + msg.getPlayerName() + " status updated: " + msg.getStatus()); } - + @Override public void received(TimeOutWarning msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); + setInfoText("Warning! Time is running out. You have " + msg.getRemainingTime() + " seconds left."); } - - @Override - public void received(UpdatePlayerAssets msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); - } - + @Override public void received(ViewAssetsResponse msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); + setInfoText("Your current assets are being displayed."); } - + @Override public void received(TradeReply msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); } - + @Override public void received(TradeRequest msg) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'received'"); } + } 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 a5cc850..d7bbe10 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 @@ -109,8 +109,10 @@ public class PlayerHandler { * @return the next players who is active */ Player nextPlayer() { - players.addLast(players.removeFirst()); - return players.getFirst(); + Player tmp = players.get(0); + players.remove(0); + players.add(tmp); + return players.get(0); } /** 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 cd2b906..e926317 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,11 +1,34 @@ package pp.monopoly.message.server; +/** + * Represents the server's response to a player's request to buy a property. + */ public class BuyPropertyResponse extends ServerMessage{ + private final boolean successful; + private final String propertyName; + private final String reason; // Reason for failure, if any + + public BuyPropertyResponse(boolean successful, String propertyName, String reason) { + this.successful = successful; + this.propertyName = propertyName; + this.reason = reason; + } + + public boolean isSuccessful() { + return successful; + } + + public String getPropertyName() { + return propertyName; + } + + public String getReason() { + return reason; + } @Override public void accept(ServerInterpreter interpreter) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'accept'"); + interpreter.received(this); } @Override @@ -13,5 +36,4 @@ public class BuyPropertyResponse extends ServerMessage{ // TODO Auto-generated method stub throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); } - } 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 7ed4938..968f2bb 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,6 +1,11 @@ package pp.monopoly.message.server; public class EventDrawCard extends ServerMessage{ + private final String cardDescription; + + public EventDrawCard(String cardDescription) { + this.cardDescription = cardDescription; + } @Override public void accept(ServerInterpreter interpreter) { @@ -13,4 +18,8 @@ public class EventDrawCard extends ServerMessage{ throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); } + public String getCardDescription() { + return 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 9b2cff3..e91041e 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,6 +1,15 @@ package pp.monopoly.message.server; public class GameOver extends ServerMessage{ + private final boolean isWinner; + + public GameOver(boolean isWinner) { + this.isWinner = isWinner; + } + + public boolean isWinner() { + return isWinner; + } @Override public void accept(ServerInterpreter interpreter) { 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 a802398..485a7fd 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 @@ -2,6 +2,16 @@ package pp.monopoly.message.server; public class JailEvent extends ServerMessage{ + private final boolean goingToJail; + + public JailEvent(boolean goingToJail) { + this.goingToJail = goingToJail; + } + + public boolean isGoingToJail() { + return goingToJail; + } + @Override public void accept(ServerInterpreter interpreter) { interpreter.received(this); 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 8b51066..c876108 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,7 +1,31 @@ package pp.monopoly.message.server; +import pp.monopoly.game.server.PlayerColor; + public class PlayerStatusUpdate extends ServerMessage{ + private final String playerName; + private final String status; + private final PlayerColor color; + + public PlayerStatusUpdate(String playerName, String status, PlayerColor color) { + this.playerName = playerName; + this.status = status; + this.color = color; + } + + public String getPlayerName() { + return playerName; + } + + public String getStatus() { + return status; + } + + public PlayerColor getColor() { + return color; + } + @Override public void accept(ServerInterpreter interpreter) { interpreter.received(this); 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 3480d20..1f78ba7 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 @@ -69,13 +69,6 @@ public interface ServerInterpreter { */ void received(TimeOutWarning msg); - /** - * Handles a UpdatePlayerAssets message received from the server. - * - * @param msg the UpdatePlayerAssets message received - */ - void received(UpdatePlayerAssets msg); - /** * Handles a ViewAssetsResponse message received from the server. * 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 f9510a7..b862170 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 @@ -2,6 +2,16 @@ package pp.monopoly.message.server; public class TimeOutWarning extends ServerMessage{ + private final int remainingTime; + + public TimeOutWarning(int remainingTime) { + this.remainingTime = remainingTime; + } + + public int getRemainingTime() { + return remainingTime; + } + @Override public void accept(ServerInterpreter interpreter) { interpreter.received(this); diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/UpdatePlayerAssets.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/UpdatePlayerAssets.java deleted file mode 100644 index e37c78c..0000000 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/message/server/UpdatePlayerAssets.java +++ /dev/null @@ -1,16 +0,0 @@ -package pp.monopoly.message.server; - -public class UpdatePlayerAssets extends ServerMessage{ - - @Override - public void accept(ServerInterpreter interpreter) { - interpreter.received(this); - } - - @Override - public String getInfoTextKey() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); - } - -} 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 116366e..9a00833 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 @@ -8,9 +8,9 @@ import pp.monopoly.model.fields.PropertyField; */ public class ViewAssetsResponse extends ServerMessage{ - private List<PropertyField> properties; - private int accountBalance; - private int jailCards; + private final List<PropertyField> properties; + private final int accountBalance; + private final int jailCards; /** * Constructs a ViewAssetsResponse with the specified properties and account balance. 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 e46308f..31ee634 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,6 +57,7 @@ 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/Figure.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Figure.java index b27f06f..d076acd 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 @@ -1,17 +1,309 @@ package pp.monopoly.model; -public class Figure implements Item{ +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; - @Override - public <T> T accept(Visitor<T> visitor) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'accept'"); +import static java.lang.Math.max; +import static java.lang.Math.min; + +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 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<IntPoint> damaged = new HashSet<>(); // The set of positions that have been hit on this ship + + /** + * Default constructor for serialization. Initializes a Figure with length 0, + * at position (0, 0), with a default rotation of RIGHT. + */ + private Figure() { + this(0, 0, 0, Rotation.RIGHT); + } + + /** + * Constructs a new Figure with the specified length, position, and rotation. + * + * @param length the length of the Figure + * @param x the x-coordinate of the Figure's initial position + * @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) { + this.x = x; + this.y = y; + this.rot = rot; + this.length = length; + this.status = Status.NORMAL; + } + + /** + * Returns the current x-coordinate of the Figure's position. + * + * @return the x-coordinate of the Figure + */ + public int getX() { + return x; + } + + /** + * Returns the current y-coordinate of the Figure's position. + * + * @return the y-coordinate of the Figure + */ + public int getY() { + return y; + } + + /** + * Moves the Figure to the specified coordinates. + * + * @param x the new x-coordinate of the Figure's position + * @param y the new y-coordinate of the Figure's position + */ + public void moveTo(int x, int y) { + this.x = x; + this.y = y; + } + + /** + * Moves the Figure to the specified position. + * + * @param pos the new position of the Figure + */ + public void moveTo(IntPosition pos) { + moveTo(pos.getX(), pos.getY()); + } + + /** + * Returns the current status of the Figure. + * + * @return the status of the Figure + */ + public Status getStatus() { + return status; + } + + /** + * 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; + } + + /** + * Returns the length of the Figure. + * + * @return the length of the Figure + */ + public int getLength() { + return length; + } + + /** + * Returns the minimum x-coordinate that the Figure occupies based on its current position and rotation. + * + * @return the minimum x-coordinate of the Figure + */ + public int getMinX() { + return x + min(0, (length - 1) * rot.dx()); + } + + /** + * Returns the maximum x-coordinate that the Figure occupies based on its current position and rotation. + * + * @return the maximum x-coordinate of the Figure + */ + public int getMaxX() { + return x + max(0, (length - 1) * rot.dx()); + } + + /** + * Returns the minimum y-coordinate that the Figure occupies based on its current position and rotation. + * + * @return the minimum y-coordinate of the Figure + */ + public int getMinY() { + return y + min(0, (length - 1) * rot.dy()); + } + + /** + * Returns the maximum y-coordinate that the Figure occupies based on its current position and rotation. + * + * @return the maximum y-coordinate of the Figure + */ + public int getMaxY() { + return y + max(0, (length - 1) * rot.dy()); + } + + /** + * Returns the current rotation of the Figure. + * + * @return the rotation of the Figure + */ + public Rotation getRot() { + return rot; + } + + /** + * Sets the rotation of the Figure. + * + * @param rot the new rotation to be set for the Figure + */ + public void setRotation(Rotation rot) { + this.rot = rot; + } + + /** + * Rotates the Figure by 90 degrees clockwise. + */ + public void rotated() { + setRotation(rot.rotate()); + } + + /** + * Attempts to hit the Figure at the specified position. + * If the position is part of the Figure, the hit is recorded. + * + * @param x the x-coordinate of the position to hit + * @param y the y-coordinate of the position to hit + * @return true if the position is part of the Figure, false otherwise + * @see #contains(int, int) + */ + public boolean hit(int x, int y) { + if (!contains(x, y)) + return false; + damaged.add(new IntPoint(x, y)); + return true; + } + + /** + * Attempts to hit the Figure at the specified position. + * If the position is part of the Figure, the hit is recorded. + * This is a convenience method for {@linkplain #hit(int, int)}. + * + * @param position the position to hit + * @return true if the position is part of the Figure, false otherwise + */ + public boolean hit(IntPosition position) { + return hit(position.getX(), position.getY()); + } + + /** + * Returns the positions of this Figure that have been hit. + * + * @return a set of positions that have been hit + * @see #hit(int, int) + */ + public Set<IntPoint> getDamaged() { + return Collections.unmodifiableSet(damaged); + } + + /** + * Checks whether the specified position is covered by the Figure. This method does + * not record a hit, only checks coverage. + * This is a convenience method for {@linkplain #contains(int, int)}. + * + * @param pos the position to check + * @return true if the position is covered by the Figure, false otherwise + */ + public boolean contains(IntPosition pos) { + return contains(pos.getX(), pos.getY()); + } + + /** + * Checks whether the specified position is covered by the Figure. This method does + * not record a hit, only checks coverage. + * + * @param x the x-coordinate of the position to check + * @param y the y-coordinate of the position to check + * @return true if the position is covered by the Figure, false otherwise + */ + public boolean contains(int x, int y) { + return getMinX() <= x && x <= getMaxX() && + getMinY() <= y && y <= getMaxY(); + } + + /** + * Determines if the Figure has been completely destroyed. A Figure is considered + * destroyed if all of its positions have been hit. + * + * @return true if the Figure is destroyed, false otherwise + * @see #hit(int, int) + */ + public boolean isDestroyed() { + return damaged.size() == length; + } + + /** + * Checks whether this Figure collides with another Figure. Two Figures collide + * if any of their occupied positions overlap. + * + * @param other the other Figure to check collision with + * @return true if the Figures collide, false otherwise + */ + public boolean collidesWith(Figure other) { + return other.getMaxX() >= getMinX() && getMaxX() >= other.getMinX() && + other.getMaxY() >= getMinY() && getMaxY() >= other.getMinY(); + } + + /** + * Returns a string representation of the Figure, including its length, position, + * and rotation. + * + * @return a string representation of the Figure + */ + @Override + public String toString() { + return "Ship{length=" + length + ", x=" + x + ", y=" + y + ", rot=" + rot + '}'; //NON-NLS + } + + /** + * Accepts a visitor that returns a value of type {@code T}. This method is part of the + * Visitor design pattern. + * + * @param visitor the visitor to accept + * @param <T> the type of the value returned by the visitor + * @return the value returned by the visitor + */ + @Override + public <T> T accept(Visitor<T> visitor) { + return visitor.visit(this); + } + + /** + * Accepts a visitor that does not return a value. This method is part of the + * Visitor design pattern. + * + * @param visitor the visitor to accept + */ @Override public void accept(VoidVisitor visitor) { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Unimplemented method 'accept'"); + visitor.visit(this); } } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Visitor.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Visitor.java index ed5deec..bba2ace 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Visitor.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/Visitor.java @@ -14,4 +14,12 @@ package pp.monopoly.model; */ public interface Visitor<T> { + /** + * Visits a Figure element. + * + * @param figure the figure element to visit + * @return the result of visiting the figure element + */ + T visit(Figure figure); + } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/VoidVisitor.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/VoidVisitor.java index 471421d..0654feb 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/model/VoidVisitor.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/model/VoidVisitor.java @@ -12,5 +12,11 @@ package pp.monopoly.model; * without returning any result. */ public interface VoidVisitor { + /** + * Visits a Figure element. + * + * @param figure the Figure element to visit + */ + void visit(Figure figure); } 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 cf0b74d..0cb7c0c 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 @@ -25,46 +25,46 @@ public class BoardManager { private static List<Field> createBoard() { ArrayList<Field> fields = new ArrayList<>(); - fields.addLast(new GoField()); - fields.addLast(new BuildingProperty("Gym", 1, 600, 20)); - fields.addLast(new EventField("Hausfeier", 2)); - fields.addLast(new BuildingProperty("Sportplatz", 3, 600, 40)); - fields.addLast(new FineField("Diszi", 4, 2000)); - fields.addLast(new GateField("Südtor", 5)); - fields.addLast(new BuildingProperty("Studium+", 6, 1000, 60)); - fields.addLast(new EventField("Üvas", 7)); - fields.addLast(new BuildingProperty("PhysikHörsaal", 8, 1000, 60)); - fields.addLast(new BuildingProperty("Audimax", 9, 1200, 80)); - fields.addLast(new GulagField()); - fields.addLast(new BuildingProperty("99er", 11, 1400, 100)); - fields.addLast(new FoodField("Brandl", 12)); - fields.addLast(new BuildingProperty("12er", 13, 1400, 100)); - fields.addLast(new BuildingProperty("23er", 14, 1600, 120)); - fields.addLast(new GateField("HauptWache", 15)); - fields.addLast(new BuildingProperty("Schwimmhalle", 16, 1800, 140)); - fields.addLast(new BuildingProperty("CISM-Bahn", 17, 1800, 140)); - fields.addLast(new EventField("Marine-Welcome-Party", 18)); - fields.addLast(new BuildingProperty("Kletterturm", 19, 2000, 160)); - fields.addLast(new TestStreckeField()); - fields.addLast(new BuildingProperty("StudFBer C", 21, 2200, 180)); - fields.addLast(new EventField("Üvas", 22)); - fields.addLast(new BuildingProperty("StudFBer B", 23, 2200, 180)); - fields.addLast(new BuildingProperty("StudFBer A", 24, 2400, 200)); - fields.addLast(new GateField("Nordtor", 25)); - fields.addLast(new BuildingProperty("Cascada", 26, 2600, 220)); - fields.addLast(new BuildingProperty("Fakultätsgebäude", 27, 2600, 220)); - fields.addLast(new FoodField("Truppenküche", 28)); - fields.addLast(new BuildingProperty("Prüfungsamt", 29, 2800, 240)); - fields.addLast(new WacheField()); - fields.addLast(new BuildingProperty("Feuerwehr", 31, 3000, 260)); - fields.addLast(new BuildingProperty("SanZ", 32, 300, 260)); - fields.addLast(new EventField("Maibock", 33)); - fields.addLast(new BuildingProperty("Rechenzentrum", 34, 3200, 280)); - fields.addLast(new GateField("Osttor", 35)); - fields.addLast(new EventField("Üvas", 36)); - fields.addLast(new BuildingProperty("2er", 37, 3500, 350)); - fields.addLast(new FineField("EZM", 38, 1000)); - fields.addLast(new BuildingProperty("20er", 39, 4000, 500)); + fields.add(new GoField()); + fields.add(new BuildingProperty("Gym", 1, 600, 20)); + fields.add(new EventField("Hausfeier", 2)); + fields.add(new BuildingProperty("Sportplatz", 3, 600, 40)); + 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 EventField("Üvas", 7)); + fields.add(new BuildingProperty("PhysikHörsaal", 8, 1000, 60)); + fields.add(new BuildingProperty("Audimax", 9, 1200, 80)); + fields.add(new GulagField()); + fields.add(new BuildingProperty("99er", 11, 1400, 100)); + 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 GateField("HauptWache", 15)); + fields.add(new BuildingProperty("Schwimmhalle", 16, 1800, 140)); + fields.add(new BuildingProperty("CISM-Bahn", 17, 1800, 140)); + fields.add(new EventField("Marine-Welcome-Party", 18)); + fields.add(new BuildingProperty("Kletterturm", 19, 2000, 160)); + fields.add(new TestStreckeField()); + fields.add(new BuildingProperty("StudFBer C", 21, 2200, 180)); + 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 GateField("Nordtor", 25)); + fields.add(new BuildingProperty("Cascada", 26, 2600, 220)); + fields.add(new BuildingProperty("Fakultätsgebäude", 27, 2600, 220)); + fields.add(new FoodField("Truppenküche", 28)); + fields.add(new BuildingProperty("Prüfungsamt", 29, 2800, 240)); + fields.add(new WacheField()); + fields.add(new BuildingProperty("Feuerwehr", 31, 3000, 260)); + fields.add(new BuildingProperty("SanZ", 32, 300, 260)); + fields.add(new EventField("Maibock", 33)); + fields.add(new BuildingProperty("Rechenzentrum", 34, 3200, 280)); + fields.add(new GateField("Osttor", 35)); + fields.add(new EventField("Üvas", 36)); + fields.add(new BuildingProperty("2er", 37, 3500, 350)); + fields.add(new FineField("EZM", 38, 1000)); + fields.add(new BuildingProperty("20er", 39, 4000, 500)); return fields; } diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/Sound.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/Sound.java index 6bbe2cb..9e0e3df 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/Sound.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/Sound.java @@ -1,20 +1,61 @@ package pp.monopoly.notification; /** - * Enumeration representing different types of sounds used in the game. + * Enum representing various sound effects in the game. */ public enum Sound { - CLICK("click_sound.wav"), - WIN("win_sound.wav"), - LOSE("lose_sound.wav"); + /** + * UC-sound-01: Sound effect for passing the start/Los field. + */ + PASS_START, - private final String fileName; + /** + * UC-sound-02: Sound effect for drawing an event card. + */ + EVENT_CARD, - Sound(String fileName) { - this.fileName = fileName; - } + /** + * UC-sound-03: Sound effect for entering the Gulag. + */ + GULAG, - public String getFileName() { - return fileName; - } + /** + * UC-sound-04: Sound effect for rolling the dice. + */ + DICE_ROLL, + + /** + * UC-sound-05: Sound effect for collecting money. + */ + MONEY_COLLECTED, + + /** + * UC-sound-06: Sound effect for losing money. + */ + MONEY_LOST, + + /** + * UC-sound-07: Sound effect for accepting a trade offer. + */ + TRADE_ACCEPTED, + + /** + * UC-sound-08: Sound effect for rejecting a trade offer. + */ + TRADE_REJECTED, + + /** + * UC-sound-09: Sound effect for winning the game. + */ + WINNER, + + /** + * UC-sound-10: Sound effect for losing the game. + */ + LOSER, + + /** + * UC-sound-11: Sound effect for button click. + */ + BUTTON; } \ No newline at end of file diff --git a/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/SoundEvent.java b/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/SoundEvent.java index b42ed7d..2eaaefe 100644 --- a/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/SoundEvent.java +++ b/Projekte/monopoly/model/src/main/java/pp/monopoly/notification/SoundEvent.java @@ -1,25 +1,26 @@ +//////////////////////////////////////// +// Programming project code +// UniBw M, 2022, 2023, 2024 +// www.unibw.de/inf2 +// (c) Mark Minas (mark.minas@unibw.de) +//////////////////////////////////////// + package pp.monopoly.notification; /** - * Event when a sound needs to be played. + * Event when an item is added to a map. * - * @param soundFileName the sound file to be played + * @param sound the sound to be played */ -public class SoundEvent implements GameEvent { - private final String soundFileName; - - public SoundEvent(Sound sound) { - this.soundFileName = sound.getFileName(); // Angenommen, Sound hat eine Methode getFileName() - } - - public String getSoundFileName() { - return soundFileName; - } +public record SoundEvent(Sound sound) implements GameEvent { + /** + * Notifies the game event listener of this event. + * + * @param listener the game event listener + */ @Override public void notifyListener(GameEventListener listener) { listener.receivedEvent(this); } } - -