mirror of
				https://athene2.informatik.unibw-muenchen.de/progproj/gruppen-ht24/Gruppe-02.git
				synced 2025-10-31 02:11:51 +01:00 
			
		
		
		
	Merge remote-tracking branch 'origin/gui' into gui
This commit is contained in:
		| @@ -1,97 +1,71 @@ | ||||
| // 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 | ||||
|  | ||||
| // Farben und allgemeine Stile | ||||
| 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(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 | ||||
|  | ||||
|  | ||||
|  | ||||
| // Hintergrundverläufe | ||||
| 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 = solidWhiteBackground.clone() | ||||
|     background.setColor(bgColor) | ||||
| } | ||||
|  | ||||
| selector("slider", "pp") { | ||||
|     background = gradient.clone() | ||||
|     background.setColor(bgColor) | ||||
| } | ||||
|  | ||||
| // Slider Stil | ||||
| selector("slider", "pp") { | ||||
|     insets = new Insets3f(5, 10, 5, 10) // Abstand | ||||
|     background = new QuadBackgroundComponent(sliderBgColor) | ||||
| } | ||||
|  | ||||
| // Slider-Thumb Stil | ||||
| selector("slider.thumb.button", "pp") { | ||||
|     text = "[]" // Symbol für den Thumb | ||||
|     color = sliderColor | ||||
|     insets = new Insets3f(2, 2, 2, 2) | ||||
| } | ||||
|  | ||||
| // Slider links/rechts Buttons | ||||
| selector("slider.left.button", "pp") { | ||||
|     text = "-" | ||||
|     color = sliderColor | ||||
|     background = doubleGradient.clone() | ||||
|     insets = new Insets3f(5, 5, 5, 5) | ||||
| } | ||||
|  | ||||
| selector("slider.right.button", "pp") { | ||||
|     text = "+" | ||||
|     color = sliderColor | ||||
|     background = doubleGradient.clone() | ||||
|     insets = new Insets3f(5, 5, 5, 5) | ||||
| } | ||||
|  | ||||
| // Style für alle Buttons im Menü | ||||
| selector("menu-button", "pp") { | ||||
|     color = color(0, 0, 0, 1) // Schwarzer Text | ||||
|     background = new QuadBackgroundComponent(bgColor) | ||||
|     insets = new Insets3f(10, 20, 10, 20) | ||||
|     fontSize = 24 | ||||
| } | ||||
|  | ||||
| // Apply border to all buttons in the "pp" style | ||||
| selector("button", "pp") { | ||||
|     background = gradient.clone() | ||||
|     background.setColor(bgColor) // Set background color | ||||
|     background.setBorderColor(blackColor) // Set border color to black | ||||
|     background.setBorderSize(2) // Set border thickness (adjust as needed) | ||||
|  | ||||
|     color = buttonEnabledColor | ||||
|     insets = new Insets3f(2, 2, 2, 2) | ||||
|     buttonCommands = stdButtonCommands | ||||
| } | ||||
|  | ||||
| // Standard Button Commands (Animationseffekt) | ||||
| def pressedCommand = new Command<Button>() { | ||||
|     void execute(Button source) { | ||||
|         if (source.isPressed()) | ||||
| @@ -110,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], | ||||
| @@ -117,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 | ||||
| } | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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(); | ||||
| @@ -73,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"); | ||||
|         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); | ||||
|     } | ||||
|  | ||||
| @@ -108,13 +107,17 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga | ||||
|         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); | ||||
| @@ -123,7 +126,25 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga | ||||
|     } | ||||
|      | ||||
|      | ||||
|     void setInfoText(String text) { | ||||
|  | ||||
|     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; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void setInfoText(String text) { | ||||
|         topText.setText(text); | ||||
|     } | ||||
|  | ||||
| @@ -163,35 +184,26 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga | ||||
|                 .build() | ||||
|                 .open(); | ||||
|     } | ||||
|     //altes Fenster beim Start von TestWorld schließen | ||||
|  | ||||
|     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() { | ||||
|      // 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); | ||||
|         guiNode.detachAllChildren(); // Entferne GUI | ||||
|         testWorld = new TestWorld(this); // Erstelle eine Instanz von TestWorld | ||||
|         testWorld.initializeScene();     // Initialisiere die Szene | ||||
|     } | ||||
|  | ||||
|     public void returnToMenu() { | ||||
|         // Entferne die Testszene | ||||
|         rootNode.detachAllChildren(); | ||||
|      | ||||
|         // Zeige das Startmenü erneut | ||||
|         StartMenu.createStartMenu(this); | ||||
|         guiNode.detachAllChildren(); // Entferne die GUI | ||||
|         StartMenu.createStartMenu(this); // Zeige das Startmenü erneut | ||||
|     } | ||||
|      | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|   | ||||
| @@ -142,4 +142,5 @@ class NetworkDialog extends SimpleDialog { | ||||
|         network.getApp().errorDialog("Verbindung zum Server fehlgeschlagen."); | ||||
|         network.getApp().setInfoText(e.getLocalizedMessage()); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -10,7 +10,6 @@ import com.simsilica.lemur.Axis; | ||||
| import com.simsilica.lemur.Button; | ||||
| import com.simsilica.lemur.Container; | ||||
| import com.simsilica.lemur.HAlignment; | ||||
| import com.simsilica.lemur.Label; | ||||
| import com.simsilica.lemur.component.QuadBackgroundComponent; | ||||
| import com.simsilica.lemur.component.SpringGridLayout; | ||||
|  | ||||
| @@ -20,9 +19,12 @@ import pp.monopoly.client.gui.SettingsMenu; | ||||
|  | ||||
| /** | ||||
|  * Constructs the startup menu dialog for the Monopoly application. | ||||
| import pp.monopoly.client.gui.GameMenu; | ||||
|  */ | ||||
| public class StartMenu extends Dialog { | ||||
|     private final MonopolyApp app; | ||||
|     private Container logoContainer; | ||||
|     private Container unibwLogoContainer; | ||||
|  | ||||
|     /** | ||||
|      * Constructs the Startup Menu dialog for the Monopoly application. | ||||
| @@ -52,6 +54,9 @@ public class StartMenu extends Dialog { | ||||
|         background.setLocalTranslation(0, 0, -1); // Ensure it is behind other GUI elements | ||||
|         app.getGuiNode().attachChild(background); | ||||
|  | ||||
|         createMonopolyLogo(app); | ||||
|         createUnibwLogo(app); | ||||
|  | ||||
|         // Center container for title and play button | ||||
|         Container centerMenu = new Container(new SpringGridLayout(Axis.Y, Axis.X)); | ||||
|  | ||||
| @@ -60,14 +65,6 @@ public class StartMenu extends Dialog { | ||||
|         startButton.setFontSize(40); // Set the font size for the button text | ||||
|         startButton.setTextHAlignment(HAlignment.Center); // Center the text horizontally | ||||
|  | ||||
|         // Set a custom border and background color | ||||
|         ColorRGBA borderColor = ColorRGBA.Orange; // Example: White border | ||||
|         ColorRGBA backgroundColor = ColorRGBA.LightGray; // Example: light gray background | ||||
|         QuadBackgroundComponent backgroundColorSp = new QuadBackgroundComponent(backgroundColor); | ||||
|         backgroundColorSp.setMargin(2, 2); // Optional: Adjust margin for the border | ||||
|         backgroundColorSp.setColor(borderColor); // Set border color | ||||
|         startButton.setBackground(backgroundColorSp); | ||||
|  | ||||
|         startButton.addClickCommands(source -> startGame(app)); | ||||
|         centerMenu.addChild(startButton); | ||||
|  | ||||
| @@ -82,7 +79,7 @@ public class StartMenu extends Dialog { | ||||
|         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(20); | ||||
|         quitButton.setFontSize(18); | ||||
|         quitButton.addClickCommands(source -> quitGame()); | ||||
|         lowerLeftMenu.addChild(quitButton); | ||||
|         app.getGuiNode().attachChild(lowerLeftMenu); | ||||
| @@ -92,12 +89,74 @@ public class StartMenu extends Dialog { | ||||
|         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(20); // Increase the font size for the text | ||||
|         settingsButton.setFontSize(18); // Increase the font size for the text | ||||
|         settingsButton.addClickCommands(source -> openSettings(app)); | ||||
|         lowerRightMenu.addChild(settingsButton); | ||||
|         app.getGuiNode().attachChild(lowerRightMenu); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates and positions the Monopoly logo container in the center of the screen. | ||||
|      */ | ||||
|     private static void createMonopolyLogo(MonopolyApp app) { | ||||
|         int screenWidth = app.getContext().getSettings().getWidth(); | ||||
|         int screenHeight = app.getContext().getSettings().getHeight(); | ||||
|  | ||||
|         // Load the Monopoly logo as a texture | ||||
|         Texture logoTexture = app.getAssetManager().loadTexture("Pictures/logo-monopoly.png"); | ||||
|  | ||||
|         // 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); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 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); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Starts the game by transitioning to the CreateGameMenu. | ||||
|      */ | ||||
|   | ||||
| @@ -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. | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|         closeCreateGameMenu();          // Schließt das Menü | ||||
|         StartMenu.createStartMenu(app); // Zeige das Startmenü | ||||
|     } | ||||
|     /* | ||||
|      * Link zwischen createGame und TestWorld | ||||
|  | ||||
|     /** | ||||
|      * Entfernt das CreateGameMenu und dessen Hintergrund. | ||||
|      */ | ||||
|      | ||||
|     private void startTestWorld() { | ||||
|         // Entfernt das Menü | ||||
|         app.getGuiNode().detachChild(menuContainer); | ||||
|         app.getGuiNode().detachChild(background); | ||||
|          | ||||
|         // Startet die Testszene | ||||
|         TestWorld.startTestWorld(); | ||||
|     } | ||||
|  | ||||
|     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 | ||||
|     } | ||||
|      | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|         }; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,51 @@ | ||||
| package pp.monopoly.client.gui; | ||||
|  | ||||
|  | ||||
| import com.jme3.math.ColorRGBA; | ||||
| import com.simsilica.lemur.Button; | ||||
| import com.simsilica.lemur.Label; | ||||
| import com.simsilica.lemur.style.ElementId; | ||||
|  | ||||
| import pp.dialog.Dialog; | ||||
| import pp.monopoly.client.MonopolyApp; | ||||
|  | ||||
| public class GameMenu extends Dialog { | ||||
|     private final MonopolyApp app; | ||||
|  | ||||
|     /** | ||||
|      * Constructs the SettingsMenu dialog for the Monopoly application. | ||||
|      * | ||||
|      * @param app the MonopolyApp instance | ||||
|      */ | ||||
|     public GameMenu(MonopolyApp app) { | ||||
|         super(app.getDialogManager()); | ||||
|         this.app = app; | ||||
|  | ||||
|         // Add a title label for Settings | ||||
|         Label settingsTitle = new Label("Einstellungen", new ElementId("settings-title")); | ||||
|         settingsTitle.setFontSize(48);  // Set font size for the title | ||||
|         settingsTitle.setColor(ColorRGBA.White); | ||||
|  | ||||
|         // Add any settings-related components here, such as volume control, toggles, etc. | ||||
|  | ||||
|         // Add a back button to return to StartMenu | ||||
|         Button backButton = new Button("Zurück", new ElementId("menu-button")); | ||||
|         backButton.setColor(ColorRGBA.White); | ||||
|         backButton.setFontSize(24); | ||||
|         backButton.addClickCommands(source -> returnToStartMenu()); | ||||
|  | ||||
|         // Add components to this dialog | ||||
|         addChild(settingsTitle); | ||||
|         addChild(backButton); | ||||
|  | ||||
|         // You can add more settings components here, like checkboxes or sliders. | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns to the StartMenu when the back button is clicked. | ||||
|      */ | ||||
|     private void returnToStartMenu() { | ||||
|         app.getDialogManager().close(this); // Close the current settings dialog | ||||
|         //TODO return zum Ausgangsmenü | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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; | ||||
|     } | ||||
| } | ||||
| @@ -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 | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 484 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 144 KiB | 
| @@ -15,7 +15,18 @@ 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; | ||||
| import pp.monopoly.message.server.EventDrawCard; | ||||
| import pp.monopoly.message.server.GameOver; | ||||
| import pp.monopoly.message.server.GameStart; | ||||
| import pp.monopoly.message.server.JailEvent; | ||||
| import pp.monopoly.message.server.PlayerStatusUpdate; | ||||
| 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.ViewAssetsResponse; | ||||
| import pp.monopoly.model.Board; | ||||
| import pp.monopoly.model.IntPoint; | ||||
| import pp.monopoly.notification.ClientStateEvent; | ||||
| @@ -26,6 +37,13 @@ import pp.monopoly.notification.InfoTextEvent; | ||||
| import pp.monopoly.notification.Sound; | ||||
| import pp.monopoly.notification.SoundEvent; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
| import java.lang.System.Logger; | ||||
| import java.lang.System.Logger.Level; | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Controls the client-side game logic for Monopoly. | ||||
|  * Manages the player's placement, interactions with the map, and response to server messages. | ||||
| @@ -34,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) { | ||||
|          | ||||
|     }; | ||||
| @@ -74,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; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
| @@ -168,4 +185,76 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker { | ||||
|     public void update(float delta) { | ||||
|         state.update(delta); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void received(BuyPropertyResponse msg) { | ||||
|         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) { | ||||
|         setInfoText("You rolled a " + msg.calcTotal() + "!"); | ||||
|         playSound(Sound.DICE_ROLL); | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void received(EventDrawCard msg) { | ||||
|         setInfoText("Event card drawn: " + msg.getCardDescription()); | ||||
|         playSound(Sound.EVENT_CARD); | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void received(GameOver msg) { | ||||
|         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) { | ||||
|         setInfoText("The game has started! Good luck!"); | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void received(JailEvent msg) { | ||||
|         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) { | ||||
|         setInfoText("Player " + msg.getPlayerName() + " status updated: " + msg.getStatus()); | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void received(TimeOutWarning msg) { | ||||
|         setInfoText("Warning! Time is running out. You have " + msg.getRemainingTime() + " seconds left."); | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void received(ViewAssetsResponse msg) { | ||||
|         setInfoText("Your current assets are being displayed."); | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void received(TradeReply msg) { | ||||
|     } | ||||
|      | ||||
|     @Override | ||||
|     public void received(TradeRequest msg) { | ||||
|     } | ||||
|      | ||||
| } | ||||
|   | ||||
| @@ -7,9 +7,481 @@ | ||||
|  | ||||
| package pp.monopoly.game.server; | ||||
|  | ||||
| import java.util.List; | ||||
| import java.util.Random; | ||||
|  | ||||
| import pp.monopoly.message.server.DiceResult; | ||||
| import pp.monopoly.model.FieldVisitor; | ||||
| import pp.monopoly.model.Figure; | ||||
| import pp.monopoly.model.card.DeckHelper; | ||||
| import pp.monopoly.model.fields.BuildingProperty; | ||||
| import pp.monopoly.model.fields.EventField; | ||||
| import pp.monopoly.model.fields.FineField; | ||||
| import pp.monopoly.model.fields.FoodField; | ||||
| import pp.monopoly.model.fields.GateField; | ||||
| import pp.monopoly.model.fields.GoField; | ||||
| import pp.monopoly.model.fields.GulagField; | ||||
| import pp.monopoly.model.fields.PropertyField; | ||||
| import pp.monopoly.model.fields.TestStreckeField; | ||||
| import pp.monopoly.model.fields.WacheField; | ||||
|  | ||||
| /** | ||||
|  * Class representing a player | ||||
|  */ | ||||
| public class Player { | ||||
| public class Player implements FieldVisitor<Void>{ | ||||
|     private final int id; | ||||
|     private String name; | ||||
|     private PlayerColor color; | ||||
|     private int accountBalance = 0; | ||||
|     private Figure figure; | ||||
|     private List<PropertyField> properties; | ||||
|     private int getOutOfJailCard; | ||||
|     private int fieldID; | ||||
|     private DiceResult rollResult; | ||||
|     private final PlayerHandler handler; | ||||
|     private PlayerState state = new LobbyState(); | ||||
|  | ||||
|     /** | ||||
|      * Constructs a player with the speciefied params | ||||
|      * @param id the id of the player | ||||
|      * @param name the name of the player | ||||
|      * @param handler the PlayerHandler thispalyer is a part of | ||||
|      */ | ||||
|     public Player(int id, String name, PlayerHandler handler) { | ||||
|         this.name = name; | ||||
|         this.id = id; | ||||
|         this.handler = handler; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Constructs a player with the specified id | ||||
|      * @param id the id of the player | ||||
|      * @param handler the PlayerHandler this player is a part of | ||||
|      */ | ||||
|     public Player(int id, PlayerHandler handler) { | ||||
|         this.id = id; | ||||
|         this.handler = handler; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the name of the Player | ||||
|      * @param name the new name | ||||
|      */ | ||||
|     void setName(String name) { | ||||
|         this.name = name; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Set the PlayerColor | ||||
|      * @param color the color to be set to | ||||
|      */ | ||||
|     void setColor(PlayerColor color) { | ||||
|         this.color = color; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns this players id | ||||
|      * @return th eid of this player | ||||
|      */ | ||||
|     public int getId() { | ||||
|         return id; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the current position of the player | ||||
|      * @return the current position of this player | ||||
|      */ | ||||
|     public int getFieldID() { | ||||
|         return fieldID; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Moves by the specified amount of steps | ||||
|      * @param steps the number of steps to move | ||||
|      * @return the new position | ||||
|      */ | ||||
|     public int move(int steps){ | ||||
|         return movePos(fieldID+steps); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Moves the player to the specified Position on the board | ||||
|      * @param position the position to move to | ||||
|      * @return the new position | ||||
|      */ | ||||
|     public int movePos(int position){ | ||||
|         fieldID = fieldID+position; | ||||
|         if(fieldID >= 40) { | ||||
|             fieldID = fieldID%40; | ||||
|             earnMoney(2000); | ||||
|         } | ||||
|         return fieldID; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets all the properties owned by this player | ||||
|      * @return List of all properties owned by this player | ||||
|      */ | ||||
|     public List<PropertyField> getProperties() { | ||||
|         return properties; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Buy the speciefied property. | ||||
|      * Properties can olny be bought when they are not sold yet and you have enough money left to buy | ||||
|      * @param property to property to be bought | ||||
|      */ | ||||
|     public void buyProperty(PropertyField property) { | ||||
|         if (property.getOwner() == null && accountBalance >= property.getPrice()) { | ||||
|             properties.add(property); | ||||
|             pay(property.getPrice()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sell the property | ||||
|      * @param property the property to be sold | ||||
|      */ | ||||
|     public void sellProperty(PropertyField property) { | ||||
|         if (property.getOwner() == this) { | ||||
|             properties.remove(property); | ||||
|             property.setOwner(null); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets this players current accountBalanece | ||||
|      * @return the amount of money currently owned by this player | ||||
|      */ | ||||
|     public int getAccountBalance() { | ||||
|         return accountBalance; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Removed the speciefied amount of money to this players accountabalance | ||||
|      * @param amount the amount to be removed | ||||
|      */ | ||||
|     public void pay(int amount) { | ||||
|         accountBalance -= amount; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Add the speciefied amount of money to this players accountabalance | ||||
|      * @param amount the amount to be added | ||||
|      */ | ||||
|     public void earnMoney(int amount) { | ||||
|         accountBalance += amount; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the players name | ||||
|      * @return the name of this player | ||||
|      */ | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the number of GEtOutOfJailCards owned by this player | ||||
|      * @return | ||||
|      */ | ||||
|     public int getNumJailCard() { | ||||
|         return getOutOfJailCard; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds a GetOutOfJailCard | ||||
|      */ | ||||
|     public void addJailCard() { | ||||
|         getOutOfJailCard++; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Removes a GetOutOfJailCard. | ||||
|      * Removes one single card per call, to a minimum of 0 cards | ||||
|      */ | ||||
|     public void removeJailCard() { | ||||
|         if (getOutOfJailCard ==0) { | ||||
|             throw new IllegalStateException("Has no JailCard to remove"); | ||||
|         } | ||||
|         getOutOfJailCard--; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles the logic of paying the jail bail | ||||
|      */ | ||||
|     public void payBail() { | ||||
|         state.payBail(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles the logic of using a GetOutOfJailCard | ||||
|      */ | ||||
|     public void useJailCard() { | ||||
|         state.useJailCard(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(BuildingProperty field) { | ||||
|         int rent = field.calcRent(); | ||||
|  | ||||
|         field.getOwner().earnMoney(rent); | ||||
|         pay(rent); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(FoodField field) { | ||||
|         int factor = 4; | ||||
|         if (field.getOwner().getNumProp(field) == 2) { | ||||
|             factor = 10; | ||||
|         } | ||||
|         field.getOwner().earnMoney(rollResult.calcTotal()*factor); | ||||
|         pay(rollResult.calcTotal()*factor); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(GateField field) { | ||||
|         int rent = field.calcRent() * field.getOwner().getNumProp(field); | ||||
|  | ||||
|         field.getOwner().earnMoney(rent); | ||||
|         pay(rent); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(GulagField field) { | ||||
|         state = new JailState(); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(TestStreckeField field) { | ||||
|         earnMoney(field.collectMoney()); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(EventField field) { | ||||
|         DeckHelper.drawCard(); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(WacheField field) { | ||||
|         movePos(10); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(GoField field) { | ||||
|         earnMoney(2000); | ||||
|         GulagField res = (GulagField) handler.getLogic().getBoardManager().getFieldAtIndex(10); | ||||
|         res.accept(this); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(FineField field) { | ||||
|         int amount = field.getFine(); | ||||
|         pay(amount); | ||||
|         TestStreckeField res =(TestStreckeField) handler.getLogic().getBoardManager().getFieldAtIndex(20); | ||||
|         res.addMoney(amount); | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the number of Properties of the speciefied fild type | ||||
|      * @param field the type of field to search for | ||||
|      * @return the number of the fields owned with the specified type | ||||
|      */ | ||||
|     public int getNumProp(PropertyField field) { | ||||
|         int count = 0; | ||||
|         for (PropertyField propertyField : properties) { | ||||
|             if (propertyField.getClass() == field.getClass()) { | ||||
|                 count++; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return count; | ||||
|     } | ||||
|  | ||||
|         /** | ||||
|      * Inner class for dice functionality in the game. | ||||
|      * Rolls random dice values. | ||||
|      */ | ||||
|     private class Dice { | ||||
|         private static Random random = new Random(); | ||||
|  | ||||
|         /** | ||||
|          * Rolls a single die and returns a random value from 1 to 6. | ||||
|          * | ||||
|          * @return the result of a dice roll (1 to 6) | ||||
|          */ | ||||
|         private static int rollDice() { | ||||
|             return random.nextInt(6) + 1; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Rolls two dice and returns a list with the results. | ||||
|      * | ||||
|      * @return a List of two integers representing the dice roll results | ||||
|      */ | ||||
|     DiceResult rollDice() { | ||||
|         return state.rollDice(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * A interface representing the PlayerStates | ||||
|      */ | ||||
|     private interface PlayerState { | ||||
|         /** | ||||
|          * Handles the logic for rolling Dice | ||||
|          * @return the {@link DiceResult} of this the DiceRoll | ||||
|          */ | ||||
|         DiceResult rollDice(); | ||||
|  | ||||
|         /** | ||||
|          * Handles the logic for paying the Jail Bail | ||||
|          */ | ||||
|         void payBail(); | ||||
|  | ||||
|         /** | ||||
|          * Handles the action of using a GetOutOfJail Card | ||||
|          */ | ||||
|         void useJailCard(); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Class to represent the Active PlayerState | ||||
|      * This class is set when it is the Players turn to do actions | ||||
|      */ | ||||
|     private class ActiveState implements PlayerState { | ||||
|  | ||||
|         @Override | ||||
|         public DiceResult rollDice() { | ||||
|             List<Integer> roll = List.of(Dice.rollDice(), Dice.rollDice()); | ||||
|             rollResult = new DiceResult(roll); | ||||
|             return rollResult; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void payBail() { | ||||
|             // do nothing | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void useJailCard() { | ||||
|             // do nothings | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * A class to represent the Lobby PlayerState | ||||
|      * Set when in Lobby | ||||
|      */ | ||||
|     private class LobbyState implements PlayerState{ | ||||
|  | ||||
|         @Override | ||||
|         public DiceResult rollDice() { | ||||
|             //do nothing | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void payBail() { | ||||
|             //do nothing | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void useJailCard() { | ||||
|             // do nothing | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * A class to represent the Jailed PlayerState | ||||
|      * Set when in Gulag | ||||
|      */ | ||||
|     private class JailState implements PlayerState { | ||||
|  | ||||
|         private int DoubletsCounter = 3; | ||||
|  | ||||
|         @Override | ||||
|         public DiceResult rollDice() { | ||||
|             List<Integer> roll = List.of(Dice.rollDice(), Dice.rollDice()); | ||||
|             rollResult = new DiceResult(roll); | ||||
|             if (rollResult.isDoublets()) { | ||||
|                 state = new ActiveState(); | ||||
|             } else if (DoubletsCounter == 0) { | ||||
|  | ||||
|             } else { | ||||
|                 DoubletsCounter--; | ||||
|             } | ||||
|             return rollResult; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void payBail() { | ||||
|             pay(500); | ||||
|             state = new ActiveState(); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void useJailCard() { | ||||
|             getOutOfJailCard--; | ||||
|             state = new ActiveState(); | ||||
|         } | ||||
|          | ||||
|     } | ||||
|  | ||||
|     private class BankruptState implements PlayerState { | ||||
|  | ||||
|         @Override | ||||
|         public DiceResult rollDice() { | ||||
|             // TODO Auto-generated method stub | ||||
|             throw new UnsupportedOperationException("Unimplemented method 'rollDice'"); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void payBail() { | ||||
|             // TODO Auto-generated method stub | ||||
|             throw new UnsupportedOperationException("Unimplemented method 'payBail'"); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void useJailCard() { | ||||
|             // TODO Auto-generated method stub | ||||
|             throw new UnsupportedOperationException("Unimplemented method 'useJailCard'"); | ||||
|         } | ||||
|          | ||||
|     } | ||||
|  | ||||
|     private class WaitForTurnState implements PlayerState { | ||||
|  | ||||
|         @Override | ||||
|         public DiceResult rollDice() { | ||||
|             // TODO Auto-generated method stub | ||||
|             throw new UnsupportedOperationException("Unimplemented method 'rollDice'"); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void payBail() { | ||||
|             // TODO Auto-generated method stub | ||||
|             throw new UnsupportedOperationException("Unimplemented method 'payBail'"); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void useJailCard() { | ||||
|             // TODO Auto-generated method stub | ||||
|             throw new UnsupportedOperationException("Unimplemented method 'useJailCard'"); | ||||
|         } | ||||
|          | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,35 @@ | ||||
| package pp.monopoly.game.server; | ||||
|  | ||||
| import com.jme3.math.ColorRGBA; | ||||
|  | ||||
| /** | ||||
|  * Enum representing six distinct colors for players in the game. | ||||
|  */ | ||||
| public enum PlayerColor { | ||||
|     GREEN_LIGHT(new ColorRGBA(0 / 255f, 204 / 255f, 0 / 255f, 1)),   // Hex: 00cc00 | ||||
|     RED(new ColorRGBA(255 / 255f, 0 / 255f, 0 / 255f, 1)),           // Hex: ff0000 | ||||
|     BLUE(new ColorRGBA(0 / 255f, 0 / 255f, 204 / 255f, 1)),          // Hex: 0000cc | ||||
|     PINK(new ColorRGBA(255 / 255f, 77 / 255f, 166 / 255f, 1)),       // Hex: ff4da6 | ||||
|     GREEN_DARK(new ColorRGBA(0 / 255f, 102 / 255f, 0 / 255f, 1)),    // Hex: 006600 | ||||
|     YELLOW(new ColorRGBA(255 / 255f, 255 / 255f, 0 / 255f, 1));      // Hex: ffff00 | ||||
|  | ||||
|     private final ColorRGBA color; | ||||
|  | ||||
|     /** | ||||
|      * Constructs a PlayerColor with the specified ColorRGBA value. | ||||
|      * | ||||
|      * @param color the ColorRGBA value associated with the player color | ||||
|      */ | ||||
|     PlayerColor(ColorRGBA color) { | ||||
|         this.color = color; | ||||
|     }  | ||||
|  | ||||
|     /** | ||||
|      * Gets the ColorRGBA value of the player color. | ||||
|      * | ||||
|      * @return the ColorRGBA value | ||||
|      */ | ||||
|     public ColorRGBA getColor() { | ||||
|         return color; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,137 @@ | ||||
| package pp.monopoly.game.server; | ||||
|  | ||||
| import java.util.LinkedList; | ||||
| import java.util.Collection; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.NoSuchElementException; | ||||
| import java.util.Set; | ||||
| /** | ||||
|  * A class for helping with player actions and managing thier turns | ||||
|  */ | ||||
| public class PlayerHandler { | ||||
|     private List<Player> players = new LinkedList<>(); | ||||
|     private Set<Player> readyPlayers = new HashSet<>(); | ||||
|     private ServerGameLogic logic; | ||||
|  | ||||
|     /** | ||||
|      * Contructs a PlayerHandler | ||||
|      * @param logic the {@link ServerGameLogic} this PlayerHandler is a part of | ||||
|      */ | ||||
|     PlayerHandler(ServerGameLogic logic) { | ||||
|         this.logic = logic; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Contructs a PlayerHandler | ||||
|      * @param logic the {@link ServerGameLogic} this PlayerHandler is a part of | ||||
|      * @param p1 a Player to be added | ||||
|      */ | ||||
|     PlayerHandler(ServerGameLogic logic, Player p1) { | ||||
|         this(logic); | ||||
|         players.add(p1); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Contructs a PlayerHandler | ||||
|      * @param logic the {@link ServerGameLogic} this PlayerHandler is a part of | ||||
|      * @param players a Collection of Players to be added | ||||
|      */ | ||||
|     PlayerHandler(ServerGameLogic logic, Collection<Player> players) { | ||||
|         this(logic); | ||||
|         players.addAll(players); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Return the number of players | ||||
|      * @return number of players in the game | ||||
|      */ | ||||
|     public int getPlayerCount() { | ||||
|         return players.size(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Chechs if all players are ready to start the game | ||||
|      * @return {@code true} if all players are ready, otherwise {@code false} | ||||
|      */ | ||||
|     public boolean allPlayersReady() { | ||||
|         if (readyPlayers.size() == players.size()) return true; | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets a players Ready status | ||||
|      * @param player the player to alter | ||||
|      * @param ready the new Status | ||||
|      */ | ||||
|     void setPlayerReady(Player player, boolean ready) { | ||||
|         if (!players.contains(player)) { | ||||
|             throw new IllegalArgumentException("Player does not belong to this PlayerHandler"); | ||||
|         } else { | ||||
|             if (ready) { | ||||
|                 readyPlayers.add(player); | ||||
|             } else { | ||||
|                 readyPlayers.remove(player); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds a player to the Queue | ||||
|      * @param player the player to be added to the queue | ||||
|      */ | ||||
|     void addPlayer(Player player) { | ||||
|         if (players.contains(player)) { | ||||
|             throw new IllegalArgumentException("Player already registered"); | ||||
|         } | ||||
|         players.add(player); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Removes the specified Player from the Queue | ||||
|      * @param player the player to be removed | ||||
|      */ | ||||
|     void removePlayer(Player player) { | ||||
|         players.remove(player); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets Player based on their id in the Queue | ||||
|      * @param index the index of the queue | ||||
|      * @return the Player at the required index | ||||
|      */ | ||||
|     Player getPlayerAtIndex(int index) { | ||||
|         return players.get(index); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Completes a player turn and return the next player | ||||
|      * @return the next players who is active | ||||
|      */ | ||||
|     Player nextPlayer() { | ||||
|         Player tmp = players.get(0); | ||||
|         players.remove(0); | ||||
|         players.add(tmp); | ||||
|         return players.get(0); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the {@link ServerGameLogic} of this PlayerHandler | ||||
|      * @return the {@link ServerGameLogic} of this PlayerHandler | ||||
|      */ | ||||
|     ServerGameLogic getLogic() { | ||||
|         return logic; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets a player based on their id | ||||
|      * @param id the id to be searched for | ||||
|      * @return the player with the required id | ||||
|      */ | ||||
|     Player getPlayerById(int id) { | ||||
|         for (Player player : players) { | ||||
|             if (player.getId() == id) return player; | ||||
|         } | ||||
|         throw new NoSuchElementException("Player mit id "+id+" existiert nicht"); | ||||
|     } | ||||
| } | ||||
| @@ -1,18 +1,16 @@ | ||||
| //////////////////////////////////////// | ||||
| // Programming project code | ||||
| // UniBw M, 2022, 2023, 2024 | ||||
| // www.unibw.de/inf2 | ||||
| // (c) Mark Minas (mark.minas@unibw.de) | ||||
| //////////////////////////////////////// | ||||
|  | ||||
| package pp.monopoly.game.server; | ||||
|  | ||||
| import pp.monopoly.MonopolyConfig; | ||||
| import pp.monopoly.message.client.*; | ||||
| import pp.monopoly.message.server.ServerMessage; | ||||
| import pp.monopoly.message.server.TradeReply; | ||||
| import pp.monopoly.message.server.TradeRequest; | ||||
| import pp.monopoly.message.server.ViewAssetsResponse; | ||||
| import pp.monopoly.model.fields.BoardManager; | ||||
| import pp.monopoly.model.fields.PropertyField; | ||||
|  | ||||
| import java.lang.System.Logger; | ||||
| import java.lang.System.Logger.Level; | ||||
| import java.util.ArrayList; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import pp.monopoly.MonopolyConfig; | ||||
| import pp.monopoly.message.client.ClientInterpreter; | ||||
| @@ -26,14 +24,14 @@ public class ServerGameLogic implements ClientInterpreter { | ||||
|     private static final Logger LOGGER = System.getLogger(ServerGameLogic.class.getName()); | ||||
|  | ||||
|     private final MonopolyConfig config; | ||||
|     private final List<Player> players = new ArrayList<>(2); | ||||
|     private final Set<Player> readyPlayers = new HashSet<>(); | ||||
|     private final PlayerHandler playerHandler = new PlayerHandler(this); | ||||
|     private final ServerSender serverSender; | ||||
|     private Player activePlayer; | ||||
|     private ServerState state = ServerState.WAIT; | ||||
|     private ServerState state = ServerState.CREATEGAME; | ||||
|     private static final int MAX_PLAYERS = 6; | ||||
|     private BoardManager boardManager = new BoardManager(); | ||||
|  | ||||
|     /** | ||||
|      * Constructs a ServerGameLogic with the specified sender and configuration. | ||||
|      * Constructs a ServerGameLogic instance with the specified sender and configuration. | ||||
|      * | ||||
|      * @param serverSender the sender used to send messages to clients | ||||
|      * @param config       the game configuration | ||||
| @@ -44,80 +42,218 @@ public class ServerGameLogic implements ClientInterpreter { | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the  state of the game. | ||||
|      * Retrieves the current state of the game. | ||||
|      * | ||||
|      * @return the current ServerState | ||||
|      */ | ||||
|     ServerState getState() { | ||||
|         return state; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the new state of the game and logs the state transition. | ||||
|      * Sets a new state for the game and logs the state transition. | ||||
|      * | ||||
|      * @param newState the new state to set | ||||
|      * @param newState the new ServerState to transition to | ||||
|      */ | ||||
|     void setState(ServerState newState) { | ||||
|         LOGGER.log(Level.DEBUG, "state transition {0} --> {1}", state, newState); //NON-NLS | ||||
|         LOGGER.log(Level.DEBUG, "State transition {0} --> {1}", state, newState); | ||||
|         state = newState; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the opponent of the specified player. | ||||
|      * Sends a message to a specified player. | ||||
|      * | ||||
|      * @param p the player | ||||
|      * @return the opponent of the player | ||||
|      */ | ||||
|     Player getOpponent(Player p) { | ||||
|         if (players.size() != 2) | ||||
|             throw new RuntimeException("trying to find opponent without having 2 players"); | ||||
|         final int index = players.indexOf(p); | ||||
|         if (index < 0) | ||||
|             throw new RuntimeException("Nonexistent player " + p); | ||||
|         return players.get(1 - index); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Returns the player representing the client with the specified connection ID. | ||||
|      * | ||||
|      * @param id the ID of the client | ||||
|      * @return the player associated with the client ID, or null if not found | ||||
|      */ | ||||
|     public Player getPlayerById(int id) { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method"); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sends a message to the specified player. | ||||
|      * | ||||
|      * @param player the player to send the message to | ||||
|      * @param msg    the message to send | ||||
|      * @param player the Player to whom the message is sent | ||||
|      * @param msg    the ServerMessage to send | ||||
|      */ | ||||
|     void send(Player player, ServerMessage msg) { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method"); | ||||
|         if (player != null && msg != null) { | ||||
|             serverSender.send(player.getId(), msg); | ||||
|             LOGGER.log(Level.DEBUG, "Message sent to player {0}: {1}", player.getName(), msg.getClass().getSimpleName()); | ||||
|         } else { | ||||
|             LOGGER.log(Level.WARNING, "Attempted to send a null message or to a null player"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds a new player to the game if there are less than two players. | ||||
|      * Transitions the state to SET_UP if two players are present. | ||||
|      * Adds a new player to the game if the game is in the LOBBY state and the maximum | ||||
|      * player limit has not been reached. | ||||
|      * | ||||
|      * @param id the connection ID of the new player | ||||
|      * @return the player added to the game, or null if the game is not in the right state | ||||
|      * @param player the Player to add to the game | ||||
|      * @return the added Player, or null if the player could not be added | ||||
|      */ | ||||
|     public Player addPlayer(Player player) { | ||||
|         if (state != ServerState.LOBBY) { | ||||
|             LOGGER.log(Level.WARNING, "Cannot add player; game is not in LOBBY state."); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         if (playerHandler.getPlayerCount() >= MAX_PLAYERS) { | ||||
|             LOGGER.log(Level.WARNING, "Cannot add player; maximum player limit reached."); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         playerHandler.addPlayer(player); | ||||
|         LOGGER.log(Level.DEBUG, "Player added: {0}", player.getId()); | ||||
|  | ||||
|         return player; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Adds a new player to the game if the game is in the LOBBY state and the maximum | ||||
|      * player limit has not been reached. | ||||
|      * | ||||
|      * @param id the id of the player to add to the game | ||||
|      * @return the added Player, or null if the player could not be added | ||||
|      */ | ||||
|     public Player addPlayer(int id) { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method"); | ||||
|         Player player = new Player(id, playerHandler); | ||||
|         if (state != ServerState.LOBBY) { | ||||
|             LOGGER.log(Level.WARNING, "Cannot add player; game is not in LOBBY state."); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         if (playerHandler.getPlayerCount() >= MAX_PLAYERS) { | ||||
|             LOGGER.log(Level.WARNING, "Cannot add player; maximum player limit reached."); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         playerHandler.addPlayer(player); | ||||
|         LOGGER.log(Level.DEBUG, "Player added: {0}", player.getId()); | ||||
|  | ||||
|         return player; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Marks the player as ready  | ||||
|      * Transitions the state to PLAY if both players are ready. | ||||
|      * Handles a BuyPropertyRequest from a player, allowing the player to purchase a property | ||||
|      * if it is unowned and they have sufficient funds. | ||||
|      * | ||||
|      * @param player the player who is ready | ||||
|      * @param msg  the BuyPropertyRequest received from the player | ||||
|      * @param from the connection ID of the player who sent the request | ||||
|      */ | ||||
|     void playerReady(Player player) { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method"); | ||||
|     @Override | ||||
|     public void received(BuyPropertyRequest msg, int from) { | ||||
|         Player player = playerHandler.getPlayerById(from); | ||||
|         if (player != null && state == ServerState.INGAME) { | ||||
|             PropertyField property = (PropertyField) boardManager.getFieldAtIndex(player.move(0)); // Assuming player position for property | ||||
|              | ||||
|             if (property.getOwner() == null && player.getAccountBalance() >= property.getPrice()) { | ||||
|                 player.buyProperty(property); | ||||
|                 property.setOwner(player); | ||||
|                 player.earnMoney(-property.getPrice()); | ||||
|                 LOGGER.log(Level.INFO, "Player {0} bought property {1}", player.getName(), property.getName()); | ||||
|             } else { | ||||
|                 LOGGER.log(Level.WARNING, "Player {0} cannot buy property {1}", player.getName(), property.getName()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles an EndTurn request, ending the player's turn and advancing to the next player. | ||||
|      * | ||||
|      * @param msg  the EndTurn message received from the player | ||||
|      * @param from the connection ID of the player who sent the request | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(EndTurn msg, int from) { | ||||
|         Player player = playerHandler.getPlayerById(from); | ||||
|         if (player != null && state == ServerState.INGAME) { | ||||
|             LOGGER.log(Level.DEBUG, "Ending turn for player {0}", player.getName()); | ||||
|             playerHandler.nextPlayer(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles a PlayerReady message, marking the player as ready. | ||||
|      * | ||||
|      * @param msg  the PlayerReady message received from the player | ||||
|      * @param from the connection ID of the player who sent the request | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(PlayerReady msg, int from) { | ||||
|         Player player = playerHandler.getPlayerById(from); | ||||
|         if (player != null) { | ||||
|             player.setName(msg.getName()); | ||||
|             player.setColor(msg.getColor()); | ||||
|             player.setName(msg.getName()); | ||||
|             LOGGER.log(Level.DEBUG, "Player {0} is ready", player.getName()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles a RollDice message, rolling dice for the player and moving them on the board. | ||||
|      * | ||||
|      * @param msg  the RollDice message received from the player | ||||
|      * @param from the connection ID of the player who sent the request | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(RollDice msg, int from) { | ||||
|         Player player = playerHandler.getPlayerById(from); | ||||
|         if (player != null && state == ServerState.INGAME) { | ||||
|             send(player, player.rollDice()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles a TradeOffer message by forwarding the trade offer to the receiving player. | ||||
|      * | ||||
|      * @param msg  the TradeOffer message received from the initiating player | ||||
|      * @param from the connection ID of the player who sent the offer | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(TradeOffer msg, int from) { | ||||
|         Player sender = playerHandler.getPlayerById(from); | ||||
|         Player receiver = playerHandler.getPlayerById(msg.getReceiverId()); | ||||
|          | ||||
|         if (sender != null && receiver != null) { | ||||
|             LOGGER.log(Level.INFO, "Player {0} offers a trade to player {1}", sender.getName(), receiver.getName()); | ||||
|             send(playerHandler.getPlayerById(msg.getReceiverId()), new TradeRequest(msg.getReceiverId(), msg.getTradeHandler())); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles a TradeResponse message by forwarding the response back to the initiating player. | ||||
|      * | ||||
|      * @param msg  the TradeResponse message received from the receiving player | ||||
|      * @param from the connection ID of the player who sent the response | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(TradeResponse msg, int from) { | ||||
|         Player responder = playerHandler.getPlayerById(from); | ||||
|         Player initiator = playerHandler.getPlayerById(msg.getInitiatorId()); | ||||
|          | ||||
|         if (responder != null && initiator != null) { | ||||
|             LOGGER.log(Level.INFO, "Player {0} responded to trade with player {1}", responder.getName(), initiator.getName()); | ||||
|             send(initiator, new TradeReply(msg.getInitiatorId(), msg.getTradeHandler())); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Handles a ViewAssetsRequest message, sending the player a response containing their assets. | ||||
|      * | ||||
|      * @param msg  the ViewAssetsRequest message received from the player | ||||
|      * @param from the connection ID of the player who sent the request | ||||
|      */ | ||||
|     @Override | ||||
|     public void received(ViewAssetsRequest msg, int from) { | ||||
|         Player player = playerHandler.getPlayerById(from); | ||||
|         if (player != null) { | ||||
|             LOGGER.log(Level.DEBUG, "Processing ViewAssetsRequest for player {0}", player.getName()); | ||||
|              | ||||
|             send(player, new ViewAssetsResponse(player.getProperties(), player.getAccountBalance(), player.getNumJailCard())); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Retrieves the board manager, which manages the game board. | ||||
|      * | ||||
|      * @return the BoardManager instance managing the game board | ||||
|      */ | ||||
|     public BoardManager getBoardManager() { | ||||
|         return boardManager; | ||||
|     } | ||||
|  | ||||
|     public Player getPlayerById(int id) { | ||||
|         return playerHandler.getPlayerById(id); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -14,20 +14,20 @@ enum ServerState { | ||||
|     /** | ||||
|      * The server is waiting for clients to connect. | ||||
|      */ | ||||
|     WAIT, | ||||
|     CREATEGAME, | ||||
|  | ||||
|     /** | ||||
|      * The server is waiting for clients to set up their maps. | ||||
|      * The server is waiting for clients to set up their status to ready | ||||
|      */ | ||||
|     SET_UP, | ||||
|     LOBBY, | ||||
|  | ||||
|     /** | ||||
|      * The battle of the game where players take turns | ||||
|      */ | ||||
|     BATTLE, | ||||
|     INGAME, | ||||
|  | ||||
|     /** | ||||
|      * The game has ended because all the players went bankrott | ||||
|      * The game has ended because all the players went bankrupt | ||||
|      */ | ||||
|     GAME_OVER | ||||
|     GAMEOVER | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,31 @@ | ||||
| package pp.monopoly.message.client; | ||||
|  | ||||
| /** | ||||
|  * Represents a request from a player to buy a property. | ||||
|  */ | ||||
| public class BuyPropertyRequest extends ClientMessage{ | ||||
|     private int propertyId; | ||||
|  | ||||
|     /** | ||||
|      * Constructs a BuyPropertyRequest with the specified property ID. | ||||
|      * | ||||
|      * @param propertyId the ID of the property to buy | ||||
|      */ | ||||
|     public BuyPropertyRequest(int propertyId) { | ||||
|         this.propertyId = propertyId; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the ID of the property to buy. | ||||
|      * | ||||
|      * @return the property ID | ||||
|      */ | ||||
|     public int getPropertyId() { | ||||
|         return propertyId; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ClientInterpreter interpreter, int from) { | ||||
|         interpreter.received(this, from); | ||||
|     } | ||||
| } | ||||
| @@ -11,5 +11,59 @@ package pp.monopoly.message.client; | ||||
|  * Visitor interface for processing all client messages. | ||||
|  */ | ||||
| public interface ClientInterpreter { | ||||
|     /** | ||||
|      * Processes a received BuyPropertyRequest. | ||||
|      * | ||||
|      * @param msg  the BuyPropertyRequest to be processed | ||||
|      * @param from the connection ID from which the message was received | ||||
|      */ | ||||
|     void received(BuyPropertyRequest msg, int from); | ||||
|  | ||||
|     /** | ||||
|      * Processes a received EndTurn. | ||||
|      * | ||||
|      * @param msg  the EndTurn to be processed | ||||
|      * @param from the connection ID from which the message was received | ||||
|      */ | ||||
|     void received(EndTurn msg, int from); | ||||
|  | ||||
|     /** | ||||
|      * Processes a received PlayerReady. | ||||
|      * | ||||
|      * @param msg  the PlayerReady to be processed | ||||
|      * @param from the connection ID from which the message was received | ||||
|      */ | ||||
|     void received(PlayerReady msg, int from); | ||||
|  | ||||
|     /** | ||||
|      * Processes a received RollDice. | ||||
|      * | ||||
|      * @param msg  the RollDice to be processed | ||||
|      * @param from the connection ID from which the message was received | ||||
|      */ | ||||
|     void received(RollDice msg, int from); | ||||
|  | ||||
|     /** | ||||
|      * Processes a received TradeOffer. | ||||
|      * | ||||
|      * @param msg  the TradeOffer to be processed | ||||
|      * @param from the connection ID from which the message was received | ||||
|      */ | ||||
|     void received(TradeOffer msg, int from); | ||||
|  | ||||
|     /** | ||||
|      * Processes a received TradeResponse. | ||||
|      * | ||||
|      * @param msg  the TradeResponse to be processed | ||||
|      * @param from the connection ID from which the message was received | ||||
|      */ | ||||
|     void received(TradeResponse msg, int from); | ||||
|  | ||||
|     /** | ||||
|      * Processes a received ViewAssetsRequest. | ||||
|      * | ||||
|      * @param msg  the ViewAssetsRequest to be processed | ||||
|      * @param from the connection ID from which the message was received | ||||
|      */ | ||||
|     void received(ViewAssetsRequest msg, int from); | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,12 @@ | ||||
| package pp.monopoly.message.client; | ||||
| /** | ||||
|  * Represents a message indicating the player wants to end their turn. | ||||
|  */ | ||||
| public class EndTurn extends ClientMessage{ | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ClientInterpreter interpreter, int from) { | ||||
|         interpreter.received(this, from); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,52 @@ | ||||
| package pp.monopoly.message.client; | ||||
|  | ||||
| import pp.monopoly.game.server.PlayerColor; | ||||
|  | ||||
| /** | ||||
|  * Represents a message indicating the player is ready to play. | ||||
|  */ | ||||
| public class PlayerReady extends ClientMessage{ | ||||
|     private boolean isReady; | ||||
|     private String name; | ||||
|     private PlayerColor color; | ||||
|  | ||||
|     /** | ||||
|      * Constructs a PlayerReady message. | ||||
|      * | ||||
|      * @param isReady true if the player is ready, false otherwise | ||||
|      */ | ||||
|     public PlayerReady(boolean isReady) { | ||||
|         this.isReady = isReady; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Getter for the Name | ||||
|      * @return the Name | ||||
|      */ | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Getter for the Playercolor | ||||
|      * @return the Playercolor | ||||
|      */ | ||||
|     public PlayerColor getColor() { | ||||
|         return color; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Checks if the player is ready. | ||||
|      * | ||||
|      * @return true if ready, false otherwise | ||||
|      */ | ||||
|     public boolean isReady() { | ||||
|         return isReady; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ClientInterpreter interpreter, int from) { | ||||
|         interpreter.received(this, from); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| package pp.monopoly.message.client; | ||||
| /** | ||||
|  * Represents a message requesting to roll the dice. | ||||
|  */ | ||||
| public class RollDice extends ClientMessage{ | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ClientInterpreter interpreter, int from) { | ||||
|         interpreter.received(this, from); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,32 @@ | ||||
| package pp.monopoly.message.client; | ||||
|  | ||||
| import pp.monopoly.model.TradeHandler; | ||||
|  | ||||
| /** | ||||
|  * Represents a trade Request message from one player to another. | ||||
|  */ | ||||
| public class TradeOffer extends ClientMessage{ | ||||
|     private int receiverId; | ||||
|     private TradeHandler tradehandler; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Constructs a TradeOffer with the specified details. | ||||
|      * | ||||
|      * @param receiverId the ID of the player receiving the Request | ||||
|      * @param tradehandler the tradehandler | ||||
|      */ | ||||
|     public TradeOffer(int receiverId, TradeHandler tradehandler) { | ||||
|         this.receiverId = receiverId; | ||||
|         this.tradehandler = tradehandler; | ||||
|     } | ||||
|  | ||||
|     public int getReceiverId() { return receiverId; } | ||||
|     public TradeHandler getTradeHandler() { return tradehandler; } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ClientInterpreter interpreter, int from) { | ||||
|         interpreter.received(this, from); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,32 @@ | ||||
| package pp.monopoly.message.client; | ||||
|  | ||||
| import pp.monopoly.model.TradeHandler; | ||||
|  | ||||
| /** | ||||
|  * Represents a response to a trade offer. | ||||
|  */ | ||||
| public class TradeResponse extends ClientMessage{ | ||||
|     private int initiatorId; | ||||
|     private TradeHandler tradeHandler; | ||||
|  | ||||
|     /** | ||||
|      * Constructs a TradeResponse with the specified response details. | ||||
|      * | ||||
|      * @param initiatorId the ID of the player who initiated the trade | ||||
|      * @param accepted true if the offer is accepted, false if declined | ||||
|      */ | ||||
|     public TradeResponse(int initiatorId, TradeHandler tradeHandler) { | ||||
|         this.initiatorId = initiatorId; | ||||
|         this.tradeHandler = tradeHandler; | ||||
|     } | ||||
|  | ||||
|     public int getInitiatorId() { return initiatorId; } | ||||
|     public TradeHandler getTradeHandler() { | ||||
|         return tradeHandler; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ClientInterpreter interpreter, int from) { | ||||
|         interpreter.received(this, from); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,12 @@ | ||||
| package pp.monopoly.message.client; | ||||
| /** | ||||
|  * Represents a request from a player to view their assets. | ||||
|  */ | ||||
| public class ViewAssetsRequest extends ClientMessage{ | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ClientInterpreter interpreter, int from) { | ||||
|         interpreter.received(this, from); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,39 @@ | ||||
| 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) { | ||||
|         interpreter.received(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| package pp.monopoly.message.server; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| public class DiceResult extends ServerMessage{ | ||||
|  | ||||
|     private List<Integer> rollResult; | ||||
|  | ||||
|     public DiceResult(List<Integer> rollResult) { | ||||
|         this.rollResult = rollResult; | ||||
|     } | ||||
|  | ||||
|     public List<Integer> getRollResult() { | ||||
|         return rollResult; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ServerInterpreter interpreter) { | ||||
|         interpreter.received(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
|  | ||||
|     public boolean isDoublets() { | ||||
|         return rollResult.get(0) == rollResult.get(1); | ||||
|     } | ||||
|  | ||||
|     public int calcTotal() { | ||||
|         return rollResult.get(0)+rollResult.get(1); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,25 @@ | ||||
| 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) { | ||||
|         interpreter.received(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
|  | ||||
|     public String getCardDescription() { | ||||
|         return cardDescription; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,25 @@ | ||||
| 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) { | ||||
|         interpreter.received(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,16 @@ | ||||
| package pp.monopoly.message.server; | ||||
|  | ||||
| public class GameStart 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'"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,26 @@ | ||||
| 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); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,40 @@ | ||||
| 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); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -13,4 +13,80 @@ package pp.monopoly.message.server; | ||||
|  */ | ||||
| public interface ServerInterpreter { | ||||
|  | ||||
|     /** | ||||
|      * Handles a BuyPropertyResponse message received from the server. | ||||
|      * | ||||
|      * @param msg the BuyPropertyResponse message received | ||||
|      */ | ||||
|     void received(BuyPropertyResponse msg); | ||||
|  | ||||
|     /** | ||||
|      * Handles a DiceResult message received from the server. | ||||
|      * | ||||
|      * @param msg the DiceResult message received | ||||
|      */ | ||||
|     void received(DiceResult msg); | ||||
|  | ||||
|     /** | ||||
|      * Handles a EventDrawCard message received from the server. | ||||
|      * | ||||
|      * @param msg the EventDrawCard message received | ||||
|      */ | ||||
|     void received(EventDrawCard msg); | ||||
|  | ||||
|     /** | ||||
|      * Handles a GameOver message received from the server. | ||||
|      * | ||||
|      * @param msg the GameOver message received | ||||
|      */ | ||||
|     void received(GameOver msg); | ||||
|  | ||||
|     /** | ||||
|      * Handles a GameStart message received from the server. | ||||
|      * | ||||
|      * @param msg the GameStart message received | ||||
|      */ | ||||
|     void received(GameStart msg); | ||||
|  | ||||
|     /** | ||||
|      * Handles a JailEvent message received from the server. | ||||
|      * | ||||
|      * @param msg the JailEvent message received | ||||
|      */ | ||||
|     void received(JailEvent msg); | ||||
|  | ||||
|     /** | ||||
|      * Handles a PlayerStatusUpdate message received from the server. | ||||
|      * | ||||
|      * @param msg the PlayerStatusUpdate message received | ||||
|      */ | ||||
|     void received(PlayerStatusUpdate msg); | ||||
|  | ||||
|     /** | ||||
|      * Handles a TimeOutWarning message received from the server. | ||||
|      * | ||||
|      * @param msg the TimeOutWarning message received | ||||
|      */ | ||||
|     void received(TimeOutWarning msg); | ||||
|  | ||||
|     /** | ||||
|      * Handles a ViewAssetsResponse message received from the server. | ||||
|      * | ||||
|      * @param msg the ViewAssetsResponse message received | ||||
|      */ | ||||
|     void received(ViewAssetsResponse msg); | ||||
|  | ||||
|     /** | ||||
|      * Handles a TradeReply message received from the server. | ||||
|      * | ||||
|      * @param msg the TradeReply message received | ||||
|      */ | ||||
|     void received(TradeReply msg); | ||||
|              | ||||
|     /** | ||||
|      * Handles a TradeRequest message received from the server. | ||||
|      * | ||||
|      * @param msg the TradeRequest message received | ||||
|      */ | ||||
|     void received(TradeRequest msg); | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,26 @@ | ||||
| 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); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,39 @@ | ||||
| package pp.monopoly.message.server; | ||||
|  | ||||
| import pp.monopoly.model.TradeHandler; | ||||
|  | ||||
| /** | ||||
|  * Represents a response to a trade offer. | ||||
|  */ | ||||
| public class TradeReply extends ServerMessage{ | ||||
|     private int initiatorId; | ||||
|     private TradeHandler tradeHandler; | ||||
|  | ||||
|     /** | ||||
|      * Constructs a TradeResponse with the specified response details. | ||||
|      * | ||||
|      * @param initiatorId the ID of the player who initiated the trade | ||||
|      * @param accepted true if the offer is accepted, false if declined | ||||
|      */ | ||||
|     public TradeReply(int initiatorId, TradeHandler tradeHandler) { | ||||
|         this.initiatorId = initiatorId; | ||||
|         this.tradeHandler = tradeHandler; | ||||
|     } | ||||
|  | ||||
|     public int getInitiatorId() { return initiatorId; } | ||||
|     public TradeHandler getTradeHandler() { | ||||
|         return tradeHandler; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ServerInterpreter interpreter) { | ||||
|         interpreter.received(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,39 @@ | ||||
| package pp.monopoly.message.server; | ||||
|  | ||||
| import pp.monopoly.model.TradeHandler; | ||||
|  | ||||
| /** | ||||
|  * Represents a trade Request message from one player to another. | ||||
|  */ | ||||
| public class TradeRequest extends ServerMessage{ | ||||
|     private int receiverId; | ||||
|     private TradeHandler tradehandler; | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Constructs a TradeRequest with the specified details. | ||||
|      * | ||||
|      * @param receiverId the ID of the player receiving the Request | ||||
|      * @param tradehandler the tradehandler | ||||
|      */ | ||||
|     public TradeRequest(int receiverId, TradeHandler tradehandler) { | ||||
|         this.receiverId = receiverId; | ||||
|         this.tradehandler = tradehandler; | ||||
|     } | ||||
|  | ||||
|     public int getReceiverId() { return receiverId; } | ||||
|     public TradeHandler getTradeHandler() { return tradehandler; } | ||||
|  | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ServerInterpreter interpreter) { | ||||
|         interpreter.received(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,50 @@ | ||||
| package pp.monopoly.message.server; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import pp.monopoly.model.fields.PropertyField; | ||||
| /** | ||||
|  * Represents a response containing the player's assets. | ||||
|  */ | ||||
| public class ViewAssetsResponse extends ServerMessage{ | ||||
|  | ||||
|     private final List<PropertyField> properties; | ||||
|     private final int accountBalance; | ||||
|     private final int jailCards; | ||||
|  | ||||
|     /** | ||||
|      * Constructs a ViewAssetsResponse with the specified properties and account balance. | ||||
|      * | ||||
|      * @param properties    a List of PropertyField objects representing the player's properties | ||||
|      * @param accountBalance the player's current account balance | ||||
|      */ | ||||
|     public ViewAssetsResponse(List<PropertyField> properties, int accountBalance, int jailCards) { | ||||
|         this.properties = properties; | ||||
|         this.accountBalance = accountBalance; | ||||
|         this.jailCards = jailCards; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(ServerInterpreter interpreter) { | ||||
|         interpreter.received(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String getInfoTextKey() { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'getInfoTextKey'"); | ||||
|     } | ||||
|  | ||||
|     public List<PropertyField> getProperties() { | ||||
|         return properties; | ||||
|     } | ||||
|  | ||||
|     public int getAccountBalance() { | ||||
|         return accountBalance; | ||||
|     } | ||||
|  | ||||
|     public int getJailCards() { | ||||
|         return jailCards; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -57,6 +57,7 @@ public class Board { | ||||
|         this.width = width; | ||||
|         this.height = height; | ||||
|         this.eventBroker = eventBroker; | ||||
|         addItem(new Figure(5, 5, 5, Rotation.LEFT)); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|   | ||||
| @@ -0,0 +1,7 @@ | ||||
| package pp.monopoly.model; | ||||
|  | ||||
| import pp.monopoly.model.card.Card; | ||||
|  | ||||
| public interface CardVisitor<T> { | ||||
|     T visit(Card c); | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| package pp.monopoly.model; | ||||
|  | ||||
| import pp.monopoly.model.fields.BuildingProperty; | ||||
| import pp.monopoly.model.fields.EventField; | ||||
| import pp.monopoly.model.fields.FineField; | ||||
| import pp.monopoly.model.fields.FoodField; | ||||
| import pp.monopoly.model.fields.GateField; | ||||
| import pp.monopoly.model.fields.GoField; | ||||
| import pp.monopoly.model.fields.GulagField; | ||||
| import pp.monopoly.model.fields.TestStreckeField; | ||||
| import pp.monopoly.model.fields.WacheField; | ||||
|  | ||||
| public interface FieldVisitor<T> { | ||||
|     T visit(BuildingProperty field); | ||||
|     T visit(FoodField field); | ||||
|     T visit(GateField field); | ||||
|     T visit(GulagField field); | ||||
|     T visit(TestStreckeField field); | ||||
|     T visit(EventField field); | ||||
|     T visit(WacheField field); | ||||
|     T visit(GoField field); | ||||
|     T visit(FineField field); | ||||
| } | ||||
| @@ -0,0 +1,309 @@ | ||||
| package pp.monopoly.model; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.HashSet; | ||||
| import java.util.Set; | ||||
|  | ||||
| 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) { | ||||
|         visitor.visit(this); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,183 @@ | ||||
| package pp.monopoly.model; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
| import pp.monopoly.model.fields.PropertyField; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Helper class that handles the trade logic between two players. | ||||
|  * Manages trade initiation, validation, acceptance, and rejection involving multiple properties, money, and jail cards. | ||||
|  */ | ||||
| public class TradeHandler { | ||||
|  | ||||
|     /** | ||||
|      * Initiates a trade offer between two players involving properties, money, and jail cards. | ||||
|      * | ||||
|      * @param sender             the Player who is initiating the trade | ||||
|      * @param receiver           the Player who is the target of the trade offer | ||||
|      * @param offeredAmount      the amount of money the sender offers | ||||
|      * @param offeredProperties  the list of properties the sender offers | ||||
|      * @param offeredJailCards   the number of jail cards the sender offers | ||||
|      * @param requestedAmount    the amount of money the sender requests from the receiver | ||||
|      * @param requestedProperties the list of properties the sender requests from the receiver | ||||
|      * @param requestedJailCards the number of jail cards the sender requests from the receiver | ||||
|      * @return true if the trade offer is valid and initiated, false otherwise | ||||
|      */ | ||||
|     public boolean initiateTrade(Player sender, Player receiver, int offeredAmount, List<PropertyField> offeredProperties, | ||||
|                                  int offeredJailCards, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) { | ||||
|         // Validate the trade offer | ||||
|         if (!validateTrade(sender, offeredAmount, offeredProperties, offeredJailCards, receiver, requestedAmount, requestedProperties, requestedJailCards)) { | ||||
|             System.out.println("Trade offer is invalid."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Notify the receiver about the trade offer (this would be an actual message in a real implementation) | ||||
|         System.out.println("Trade offer initiated by " + sender.getName() + " to " + receiver.getName()); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Accepts the trade offer and completes the trade between two players. | ||||
|      * | ||||
|      * @param sender             the Player who initiated the trade | ||||
|      * @param receiver           the Player who accepted the trade | ||||
|      * @param offeredAmount      the amount of money to transfer from the sender to the receiver | ||||
|      * @param offeredProperties  the list of properties to transfer from the sender to the receiver | ||||
|      * @param offeredJailCards   the number of jail cards to transfer from the sender to the receiver | ||||
|      * @param requestedAmount    the amount of money to transfer from the receiver to the sender | ||||
|      * @param requestedProperties the list of properties to transfer from the receiver to the sender | ||||
|      * @param requestedJailCards the number of jail cards to transfer from the receiver to the sender | ||||
|      */ | ||||
|     public void acceptTrade(Player sender, Player receiver, int offeredAmount, List<PropertyField> offeredProperties, | ||||
|                             int offeredJailCards, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) { | ||||
|         // Transfer money | ||||
|         sender.earnMoney(-offeredAmount);  // Deduct money from the sender | ||||
|         receiver.earnMoney(offeredAmount); // Add money to the receiver | ||||
|  | ||||
|         receiver.earnMoney(-requestedAmount); // Deduct money from the receiver | ||||
|         sender.earnMoney(requestedAmount);    // Add money to the sender | ||||
|  | ||||
|         // Transfer ownership of the properties from sender to receiver | ||||
|         if (offeredProperties != null) { | ||||
|             for (PropertyField property : offeredProperties) { | ||||
|                 transferProperty(sender, receiver, property); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Transfer ownership of the properties from receiver to sender | ||||
|         if (requestedProperties != null) { | ||||
|             for (PropertyField property : requestedProperties) { | ||||
|                 transferProperty(receiver, sender, property); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Transfer jail cards | ||||
|         transferJailCards(sender, receiver, offeredJailCards); | ||||
|         transferJailCards(receiver, sender, requestedJailCards); | ||||
|  | ||||
|         System.out.println("Trade accepted. " + sender.getName() + " and " + receiver.getName() + " completed the trade."); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Rejects the trade offer. | ||||
|      * | ||||
|      * @param receiver the Player who is rejecting the trade | ||||
|      */ | ||||
|     public void rejectTrade(Player receiver) { | ||||
|         System.out.println("Trade rejected by " + receiver.getName()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Validates a trade offer by checking if the sender and receiver own the properties involved, | ||||
|      * have sufficient funds for the money involved in the trade, and have enough jail cards. | ||||
|      * | ||||
|      * @param sender             the Player initiating the trade | ||||
|      * @param offeredAmount      the amount of money the sender is offering | ||||
|      * @param offeredProperties  the list of properties the sender is offering | ||||
|      * @param offeredJailCards   the number of jail cards the sender is offering | ||||
|      * @param receiver           the Player receiving the trade offer | ||||
|      * @param requestedAmount    the amount of money the sender is requesting | ||||
|      * @param requestedProperties the list of properties the sender is requesting from the receiver | ||||
|      * @param requestedJailCards the number of jail cards the sender is requesting from the receiver | ||||
|      * @return true if the trade offer is valid, false otherwise | ||||
|      */ | ||||
|     private boolean validateTrade(Player sender, int offeredAmount, List<PropertyField> offeredProperties, int offeredJailCards, | ||||
|                                   Player receiver, int requestedAmount, List<PropertyField> requestedProperties, int requestedJailCards) { | ||||
|         // Check if sender has enough money to offer | ||||
|         if (sender.getAccountBalance() < offeredAmount) { | ||||
|             System.out.println("Sender does not have enough balance to make this offer."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Check if receiver has enough money to offer | ||||
|         if (receiver.getAccountBalance() < requestedAmount) { | ||||
|             System.out.println("Receiver does not have enough balance to fulfill requested amount."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Check if sender owns all the offered properties | ||||
|         if (offeredProperties != null) { | ||||
|             for (PropertyField property : offeredProperties) { | ||||
|                 if (!sender.getProperties().contains(property)) { | ||||
|                     System.out.println("Sender does not own the property " + property.getName() + " being offered."); | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Check if receiver owns all the requested properties | ||||
|         if (requestedProperties != null) { | ||||
|             for (PropertyField property : requestedProperties) { | ||||
|                 if (!receiver.getProperties().contains(property)) { | ||||
|                     System.out.println("Receiver does not own the property " + property.getName() + " requested."); | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Check if sender has enough jail cards to offer | ||||
|         if (sender.getNumJailCard() < offeredJailCards) { | ||||
|             System.out.println("Sender does not have enough jail cards to offer."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Check if receiver has enough jail cards to fulfill the request | ||||
|         if (receiver.getNumJailCard() < requestedJailCards) { | ||||
|             System.out.println("Receiver does not have enough jail cards to fulfill the request."); | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Transfers a property from one player to another. | ||||
|      * | ||||
|      * @param from     the Player transferring the property | ||||
|      * @param to       the Player receiving the property | ||||
|      * @param property the PropertyField being transferred | ||||
|      */ | ||||
|     private void transferProperty(Player from, Player to, PropertyField property) { | ||||
|         from.sellProperty(property); | ||||
|         to.buyProperty(property); | ||||
|         property.setOwner(to); // Update the property's owner | ||||
|  | ||||
|         System.out.println("Property " + property.getName() + " transferred from " + from.getName() + " to " + to.getName()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Transfers jail cards between players. | ||||
|      * | ||||
|      * @param from the Player transferring jail cards | ||||
|      * @param to   the Player receiving jail cards | ||||
|      * @param numCards the number of jail cards to transfer | ||||
|      */ | ||||
|     private void transferJailCards(Player from, Player to, int numCards) { | ||||
|         for (int i = 0; i < numCards; i++) { | ||||
|             from.removeJailCard(); | ||||
|             to.addJailCard(); | ||||
|         } | ||||
|         System.out.println("Transferred " + numCards + " jail card(s) from " + from.getName() + " to " + to.getName()); | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -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); | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,23 @@ | ||||
| package pp.monopoly.model.card; | ||||
|  | ||||
| public class Card { | ||||
|     private final String description; | ||||
|     private final String keyword; | ||||
|  | ||||
|     Card(String description, String keyword) { | ||||
|         this.description = description; | ||||
|         this.keyword = keyword; | ||||
|     } | ||||
|  | ||||
|     public void accept(DeckHelper visitor) { | ||||
|         visitor.visit(this); | ||||
|     } | ||||
|  | ||||
|     String getDescription() { | ||||
|         return description; | ||||
|     } | ||||
|  | ||||
|     String getKeyword() { | ||||
|         return keyword; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,34 @@ | ||||
| package pp.monopoly.model.card; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Queue; | ||||
|  | ||||
| import pp.monopoly.model.CardVisitor; | ||||
|  | ||||
| public class DeckHelper implements CardVisitor<Void>{ | ||||
|  | ||||
|     private static Queue<Card> cards; | ||||
|  | ||||
|     private DeckHelper() { | ||||
|          | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Void visit(Card c) { | ||||
|         // TODO Auto-generated method stub | ||||
|         throw new UnsupportedOperationException("Unimplemented method 'visit'"); | ||||
|     } | ||||
|  | ||||
|     private void shuffle() { | ||||
|         List<Card> cardList = new ArrayList<>(cards); | ||||
|         Collections.shuffle(cardList); | ||||
|         cards.clear(); | ||||
|         cards.addAll(cardList); | ||||
|     } | ||||
|  | ||||
|     public static Card drawCard() { | ||||
|         return cards != null ? cards.poll() : null; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,91 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| import java.util.NoSuchElementException; | ||||
|  | ||||
| /** | ||||
|  * Simple Manager class responsible for managing the GameBoard of Monopoly | ||||
|  */ | ||||
| public class BoardManager { | ||||
|  | ||||
|     private List<Field> board; | ||||
|  | ||||
|     /** | ||||
|      * Constructs a BoardManager | ||||
|      */ | ||||
|     public BoardManager() { | ||||
|         board = createBoard(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Creates a Monopoly GameBoard | ||||
|      * @return the List of Fields in correct  Order | ||||
|      */ | ||||
|     private static List<Field> createBoard() { | ||||
|         ArrayList<Field> fields = new ArrayList<>(); | ||||
|  | ||||
|         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; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /** | ||||
|      * Method to find the Field at specific index | ||||
|      * @param index the index for which to find the field | ||||
|      * @return the field at the index | ||||
|      */ | ||||
|     public Field getFieldAtIndex(int index) { | ||||
|         return board.get(index); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Method to find the index of a Monopoly field | ||||
|      * @param field the Field to get the Index of | ||||
|      * @return the Index of the field | ||||
|      */ | ||||
|     public int getIndexOfField(Field field) { | ||||
|         if (board.contains(field)) return field.getId(); | ||||
|         else throw new NoSuchElementException(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,70 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| public class BuildingProperty extends PropertyField { | ||||
|  | ||||
|     private int houses; | ||||
|     private boolean hotel = false; | ||||
|  | ||||
|     BuildingProperty(String name, int id, int price, int rent) { | ||||
|         super(name, id, price, rent); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int calcRent() { | ||||
|         if (hotel) { | ||||
|             return (int) Math.round(rent*70/10)*10; | ||||
|         } | ||||
|         switch (houses) { | ||||
|             case 1: | ||||
|                 return (int) Math.round(rent*5/10)*10; | ||||
|             case 2: | ||||
|                 return (int) Math.round(rent*15/10)*10; | ||||
|             case 3: | ||||
|                 return (int) Math.round(rent*40/10)*10; | ||||
|             case 4: | ||||
|             return (int) Math.round(rent*55/10)*10; | ||||
|  | ||||
|             default: | ||||
|                 return rent; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public boolean buildHouse() { | ||||
|         if (houses < 4) { | ||||
|             houses++; | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     public boolean buildHotel() { | ||||
|         if (hotel) { | ||||
|             return false; | ||||
|         } | ||||
|         hotel = true; | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     public boolean removeHouse() { | ||||
|         if (houses == 0) { | ||||
|             return false; | ||||
|         } | ||||
|         houses--; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public boolean removeHotel() { | ||||
|         if (!hotel) { | ||||
|             return false; | ||||
|         } | ||||
|         hotel = false; | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(Player player) { | ||||
|         player.visit(this); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
| import pp.monopoly.model.card.Card; | ||||
| import pp.monopoly.model.card.DeckHelper; | ||||
|  | ||||
| public class EventField extends Field{ | ||||
|  | ||||
|     public EventField(String name, int id) { | ||||
|         super(name, id); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(Player player) { | ||||
|         player.visit(this); | ||||
|     } | ||||
|  | ||||
|     public Card drawCard() { | ||||
|         return DeckHelper.drawCard(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| abstract class Field { | ||||
|     protected final String name; | ||||
|     protected final int id; | ||||
|  | ||||
|     protected Field(String name, int id) { | ||||
|         this.name = name; | ||||
|         this.id= id; | ||||
|     } | ||||
|  | ||||
|     public abstract void accept(Player player); | ||||
|  | ||||
|     public int getId() { | ||||
|         return id; | ||||
|     } | ||||
|  | ||||
|     public String getName() { | ||||
|         return name; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,23 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| public class FineField extends Field{ | ||||
|  | ||||
|     private final int fine; | ||||
|  | ||||
|     FineField(String name, int id, int fine) { | ||||
|         super(name, id); | ||||
|         this.fine = fine; | ||||
|     } | ||||
|  | ||||
|     public int getFine() { | ||||
|         return fine; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(Player player) { | ||||
|         player.visit(this); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,20 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| public class FoodField extends PropertyField { | ||||
|  | ||||
|     public FoodField(String name, int id) { | ||||
|         super(name, id, 1500,0); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int calcRent() { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(Player player) { | ||||
|         player.visit(this); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,21 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| public class GateField extends PropertyField{ | ||||
|  | ||||
|     GateField(String name, int id) { | ||||
|         super(name, id, 2000, 25); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int calcRent() { | ||||
|         return rent; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(Player player) { | ||||
|         player.visit(this); | ||||
|     } | ||||
|      | ||||
| } | ||||
| @@ -0,0 +1,15 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| public class GoField extends Field{ | ||||
|  | ||||
|     public GoField() { | ||||
|         super("Monatsgehalt", 0); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(Player player) { | ||||
|         player.visit(this); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| public class GulagField extends Field{ | ||||
|  | ||||
|     private int bailCost = 500; | ||||
|  | ||||
|     GulagField() { | ||||
|         super("Gulag", 10); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(Player player) { | ||||
|         player.visit(this); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,92 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| /** | ||||
|  * Represents an abstract property field in the Monopoly game. | ||||
|  * Contains attributes related to ownership, price, rent, and mortgage status. | ||||
|  */ | ||||
| public abstract class PropertyField extends Field { | ||||
|  | ||||
|     private final int price; | ||||
|     protected final int rent; | ||||
|     private Player owner; | ||||
|     private boolean mortgaged = false; | ||||
|  | ||||
|     /** | ||||
|      * Constructs a PropertyField with the specified name, ID, price, and rent. | ||||
|      * | ||||
|      * @param name  the name of the property | ||||
|      * @param id    the unique identifier for the property | ||||
|      * @param price the purchase price of the property | ||||
|      * @param rent  the base rent for the property | ||||
|      */ | ||||
|     protected PropertyField(String name, int id, int price, int rent) { | ||||
|         super(name, id); | ||||
|         this.price = price; | ||||
|         this.rent = rent; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Calculates the rent for this property. | ||||
|      * The calculation may depend on various factors specific to property type. | ||||
|      * | ||||
|      * @return the calculated rent for this property | ||||
|      */ | ||||
|     public abstract int calcRent(); | ||||
|  | ||||
|     /** | ||||
|      * Gets the purchase price of the property. | ||||
|      * | ||||
|      * @return the price of the property | ||||
|      */ | ||||
|     public int getPrice() { | ||||
|         return price; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the mortgage value (hypothecary value) of the property. | ||||
|      * Typically, this is half of the property price. | ||||
|      * | ||||
|      * @return the mortgage value of the property | ||||
|      */ | ||||
|     public int getHypo() { | ||||
|         return price / 2; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Gets the owner of the property. | ||||
|      * | ||||
|      * @return the Player who owns the property, or null if the property is unowned | ||||
|      */ | ||||
|     public Player getOwner() { | ||||
|         return owner; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the owner of the property. | ||||
|      * | ||||
|      * @param player the Player who will own this property | ||||
|      */ | ||||
|     public void setOwner(Player player) { | ||||
|         owner = player; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Checks if the property is currently mortgaged. | ||||
|      * | ||||
|      * @return true if the property is mortgaged, false otherwise | ||||
|      */ | ||||
|     public boolean isMortgaged() { | ||||
|         return mortgaged; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Sets the mortgage status of the property. | ||||
|      * | ||||
|      * @param mortgaged true to mark the property as mortgaged, false to unmark it | ||||
|      */ | ||||
|     public void setMortgaged(boolean mortgaged) { | ||||
|         this.mortgaged = mortgaged; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,24 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| public class TestStreckeField extends Field{ | ||||
|     private int money; | ||||
|  | ||||
|     TestStreckeField() { | ||||
|         super("Teststrecke", 20); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(Player player) { | ||||
|         player.visit(this); | ||||
|     } | ||||
|  | ||||
|     public void addMoney(int amount) { | ||||
|         money += amount; | ||||
|     } | ||||
|  | ||||
|     public int collectMoney() { | ||||
|         return money = 0; | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,16 @@ | ||||
| package pp.monopoly.model.fields; | ||||
|  | ||||
| import pp.monopoly.game.server.Player; | ||||
|  | ||||
| public class WacheField extends Field{ | ||||
|  | ||||
|     public WacheField() { | ||||
|         super("Wache", 30); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void accept(Player player) { | ||||
|         player.visit(this); | ||||
|     } | ||||
|   | ||||
| } | ||||
| @@ -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; | ||||
| } | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,385 @@ | ||||
| package pp.monopoly; | ||||
| import org.junit.Test; | ||||
| import static org.junit.Assert.assertNotNull; | ||||
| import static org.junit.Assert.assertTrue; | ||||
| import static org.junit.Assert.assertEquals; | ||||
| public class Testhandbuch { | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|         // T001 UC-game-01 - testStartApplication | ||||
|         @Test | ||||
|         public void testStartApplication() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             MainMenu mainMenu = app.getStateManager().getState(MainMenu.class); | ||||
|             assertNotNull(mainMenu); | ||||
|         } | ||||
|  | ||||
|         // T002 UC-game-02 - testOpenStartMenu | ||||
|         @Test | ||||
|         public void testOpenStartMenu() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             MainMenu mainMenu = app.getStateManager().getState(MainMenu.class); | ||||
|             mainMenu.showMenu(); | ||||
|             assertTrue(mainMenu.isMenuVisible()); | ||||
|         } | ||||
|  | ||||
|         // T003 UC-game-03 - testNavigateToPlayOption | ||||
|         @Test | ||||
|         public void testNavigateToPlayOption() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             MainMenu mainMenu = app.getStateManager().getState(MainMenu.class); | ||||
|             mainMenu.showMenu(); | ||||
|             mainMenu.startNewGame(); | ||||
|             NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class); | ||||
|             assertNotNull(newGameMenu); | ||||
|         } | ||||
|  | ||||
|         // T004 UC-game-04 - testExitApplicationFromMenu | ||||
|         @Test | ||||
|         public void testExitApplicationFromMenu() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             MainMenu mainMenu = app.getStateManager().getState(MainMenu.class); | ||||
|             mainMenu.showMenu(); | ||||
|             mainMenu.exitGame(); | ||||
|             assertTrue(app.isClosed()); | ||||
|         } | ||||
|  | ||||
|         // T005 UC-game-05 - testOpenSettingsFromMenu | ||||
|         @Test | ||||
|         public void testOpenSettingsFromMenu() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             MainMenu mainMenu = app.getStateManager().getState(MainMenu.class); | ||||
|             mainMenu.showMenu(); | ||||
|             mainMenu.openSettings(); | ||||
|             SettingMenu settingMenu = app.getStateManager().getState(SettingMenu.class); | ||||
|             assertNotNull(settingMenu); | ||||
|         } | ||||
|  | ||||
|         // T006 UC-game-06 - testOpenGameMenuWithESC | ||||
|         @Test | ||||
|         public void testOpenGameMenuWithESC() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             app.simpleUpdate(0.1f); | ||||
|             GameMenu gameMenu = app.getStateManager().getState(GameMenu.class); | ||||
|             assertTrue(gameMenu.isVisible()); | ||||
|         } | ||||
|  | ||||
|         // T007 UC-game-07 - testEnterHostName | ||||
|         @Test | ||||
|         public void testEnterHostName() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class); | ||||
|             newGameMenu.showMenu(); | ||||
|             newGameMenu.enterHostName("localhost"); | ||||
|             assertEquals("localhost", newGameMenu.getHostName()); | ||||
|         } | ||||
|  | ||||
|         // T008 UC-game-07 - testEnterPortNumber | ||||
|         @Test | ||||
|         public void testEnterPortNumber() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class); | ||||
|             newGameMenu.showMenu(); | ||||
|             newGameMenu.enterPortNumber(12345); | ||||
|             assertEquals(12345, newGameMenu.getPortNumber()); | ||||
|         } | ||||
|  | ||||
|         // T009 UC-game-07 - testCancelGameCreation | ||||
|         @Test | ||||
|         public void testCancelGameCreation() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class); | ||||
|             newGameMenu.showMenu(); | ||||
|             newGameMenu.cancel(); | ||||
|             MainMenu mainMenu = app.getStateManager().getState(MainMenu.class); | ||||
|             assertTrue(mainMenu.isMenuVisible()); | ||||
|         } | ||||
|  | ||||
|         // T010 UC-game-08 - testEnterPlayerLobby | ||||
|         @Test | ||||
|         public void testEnterPlayerLobby() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             app.getStateManager().getState(NetworkDialog.class).connect(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             assertNotNull(lobby); | ||||
|         } | ||||
|  | ||||
|         // T011 UC-game-09 - testEnterStartingCapital | ||||
|         @Test | ||||
|         public void testEnterStartingCapital() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class); | ||||
|             newGameMenu.showMenu(); | ||||
|             newGameMenu.enterStartingCapital(1500); | ||||
|             assertEquals(1500, newGameMenu.getStartingCapital()); | ||||
|         } | ||||
|  | ||||
|         // T012 UC-game-09 - testIncreaseStartingCapital | ||||
|         @Test | ||||
|         public void testIncreaseStartingCapital() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class); | ||||
|             newGameMenu.showMenu(); | ||||
|             newGameMenu.enterStartingCapital(1500); | ||||
|             newGameMenu.increaseStartingCapital(100); | ||||
|             assertEquals(1600, newGameMenu.getStartingCapital()); | ||||
|         } | ||||
|  | ||||
|         // T013 UC-game-09 - testDecreaseStartingCapital | ||||
|         @Test | ||||
|         public void testDecreaseStartingCapital() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             NewGameMenu newGameMenu = app.getStateManager().getState(NewGameMenu.class); | ||||
|             newGameMenu.showMenu(); | ||||
|             newGameMenu.enterStartingCapital(1500); | ||||
|             newGameMenu.decreaseStartingCapital(100); | ||||
|             assertEquals(1400, newGameMenu.getStartingCapital()); | ||||
|         } | ||||
|  | ||||
|         // T014 UC-game-10 - testDefaultPlayerName | ||||
|         @Test | ||||
|         public void testDefaultPlayerName() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             app.getStateManager().getState(Lobby.class).initializePlayerNames(); | ||||
|             assertEquals("Spieler 1", app.getStateManager().getState(Lobby.class).getPlayerName(0)); | ||||
|             assertEquals("Spieler 2", app.getStateManager().getState(Lobby.class).getPlayerName(1)); | ||||
|         } | ||||
|  | ||||
|         // T015 UC-game-11 - testEnterDisplayName | ||||
|         @Test | ||||
|         public void testEnterDisplayName() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             lobby.enterDisplayName("TestPlayer"); | ||||
|             assertEquals("TestPlayer", lobby.getPlayerName(0)); | ||||
|         } | ||||
|  | ||||
|         // T016 UC-game-11 - testDuplicateNameEntry | ||||
|         @Test | ||||
|         public void testDuplicateNameEntry() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             lobby.enterDisplayName("Player1"); | ||||
|             assertTrue(lobby.isDuplicateName("Player1")); | ||||
|         } | ||||
|  | ||||
|         // T017 UC-game-12 - testSelectPlayerColor | ||||
|         @Test | ||||
|         public void testSelectPlayerColor() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             lobby.selectColor("Red"); | ||||
|             assertEquals("Red", lobby.getPlayerColor(0)); | ||||
|         } | ||||
|  | ||||
|         // T018 UC-game-12 - testSelectOccupiedColor | ||||
|         @Test | ||||
|         public void testSelectOccupiedColor() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             lobby.selectColor("Red"); | ||||
|             assertTrue(lobby.isColorOccupied("Red")); | ||||
|         } | ||||
|  | ||||
|         // T019 UC-game-13 - testSelectPlayerToken | ||||
|         @Test | ||||
|         public void testSelectPlayerToken() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             lobby.selectToken("Ship"); | ||||
|             assertEquals("Ship", lobby.getPlayerToken(0)); | ||||
|         } | ||||
|  | ||||
|         // T020 UC-game-13 - testSelectOccupiedToken | ||||
|         @Test | ||||
|         public void testSelectOccupiedToken() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             lobby.selectToken("Ship"); | ||||
|             assertTrue(lobby.isTokenOccupied("Ship")); | ||||
|         } | ||||
|  | ||||
|         // T021 UC-game-14 - testCancelPlayerLobby | ||||
|         @Test | ||||
|         public void testCancelPlayerLobby() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             lobby.cancel(); | ||||
|             MainMenu mainMenu = app.getStateManager().getState(MainMenu.class); | ||||
|             assertTrue(mainMenu.isMenuVisible()); | ||||
|         } | ||||
|  | ||||
|         // T022 UC-game-15 - testOpenLobbyMenuWithESC | ||||
|         @Test | ||||
|         public void testOpenLobbyMenuWithESC() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             app.simpleUpdate(0.1f); | ||||
|             LobbyMenu lobbyMenu = app.getStateManager().getState(LobbyMenu.class); | ||||
|             assertTrue(lobbyMenu.isVisible()); | ||||
|         } | ||||
|  | ||||
|         // T023 UC-game-16 - testPlayerReadyConfirmation | ||||
|         @Test | ||||
|         public void testPlayerReadyConfirmation() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             lobby.setPlayerReady(true); | ||||
|             assertTrue(lobby.isPlayerReady(0)); | ||||
|         } | ||||
|  | ||||
|         // T024 UC-game-17 - testStartGame | ||||
|         @Test | ||||
|         public void testStartGame() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Lobby lobby = app.getStateManager().getState(Lobby.class); | ||||
|             lobby.startGame(); | ||||
|             assertEquals(GameState.InGame, lobby.getGameState()); | ||||
|         } | ||||
|  | ||||
|         // T025 UC-game-18 - testPlayerMovement | ||||
|         @Test | ||||
|         public void testPlayerMovement() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player = game.getPlayer(0); | ||||
|             player.move(5); | ||||
|             assertEquals(5, player.getPosition()); | ||||
|         } | ||||
|  | ||||
|         // T026 UC-game-19 - testPurchaseProperty | ||||
|         @Test | ||||
|         public void testPurchaseProperty() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player = game.getPlayer(0); | ||||
|             Property property = game.getProperty(0); | ||||
|             player.buyProperty(property); | ||||
|             assertTrue(player.getProperties().contains(property)); | ||||
|         } | ||||
|  | ||||
|         // T027 UC-game-20 - testMovePlayerOnDiceRoll | ||||
|         @Test | ||||
|         public void testMovePlayerOnDiceRoll() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player = game.getPlayer(0); | ||||
|             int initialPosition = player.getPosition(); | ||||
|             player.rollDice(); | ||||
|             assertTrue(player.getPosition() > initialPosition); | ||||
|         } | ||||
|  | ||||
|         // T028 UC-game-21 - testPassGo | ||||
|         @Test | ||||
|         public void testPassGo() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player = game.getPlayer(0); | ||||
|             player.move(40);  // Assuming 40 steps moves player to Go | ||||
|             assertTrue(player.passedGo()); | ||||
|         } | ||||
|  | ||||
|         // T029 UC-game-22 - testCollectMoneyFromGo | ||||
|         @Test | ||||
|         public void testCollectMoneyFromGo() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player = game.getPlayer(0); | ||||
|             int initialBalance = player.getBalance(); | ||||
|             player.move(40);  // Move to Go | ||||
|             assertTrue(player.getBalance() > initialBalance); | ||||
|         } | ||||
|  | ||||
|         // T030 UC-game-23 - testPayRent | ||||
|         @Test | ||||
|         public void testPayRent() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player = game.getPlayer(0); | ||||
|             PropertyField property = (PropertyField) game.getField(1); | ||||
|             player.move(1); | ||||
|             int initialBalance = player.getBalance(); | ||||
|             player.payRent(property); | ||||
|             assertTrue(player.getBalance() < initialBalance); | ||||
|         } | ||||
|  | ||||
|         // T031 UC-game-24 - testDeclareBankruptcy | ||||
|         @Test | ||||
|         public void testDeclareBankruptcy() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player = game.getPlayer(0); | ||||
|             player.declareBankruptcy(); | ||||
|             assertEquals(PlayerState.Bankrupt, player.getState()); | ||||
|         } | ||||
|  | ||||
|         // T032 UC-game-25 - testTradeProperty | ||||
|         @Test | ||||
|         public void testTradeProperty() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player1 = game.getPlayer(0); | ||||
|             Player player2 = game.getPlayer(1); | ||||
|             Property property = game.getProperty(0); | ||||
|             player1.offerTrade(player2, property); | ||||
|             assertTrue(player2.hasProperty(property)); | ||||
|         } | ||||
|  | ||||
|         // T033 UC-game-26 - testGameOverCondition | ||||
|         @Test | ||||
|         public void testGameOverCondition() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player = game.getPlayer(0); | ||||
|             player.declareBankruptcy(); | ||||
|             assertTrue(game.isGameOver()); | ||||
|         } | ||||
|  | ||||
|         // T034 UC-game-27 - testPlayerInJail | ||||
|         @Test | ||||
|         public void testPlayerInJail() { | ||||
|             MonopolyApp app = new MonopolyApp(); | ||||
|             app.simpleInitApp(); | ||||
|             Game game = app.getStateManager().getState(Game.class); | ||||
|             Player player = game.getPlayer(0); | ||||
|             game.sendToJail(player); | ||||
|             assertEquals(PlayerState.InJail, player.getState()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|  | ||||
| @@ -1,7 +1,161 @@ | ||||
| package pp.monopoly.client; | ||||
|  | ||||
| import com.jme3.input.KeyInput; | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.simsilica.lemur.Button; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
|  | ||||
| import static org.junit.Assert.assertNotNull; | ||||
| import static org.junit.Assert.assertNull; | ||||
|  | ||||
| public class ClientLogicTest { | ||||
|  | ||||
|     private MonopolyApp app; | ||||
|  | ||||
|     @Before | ||||
|     public void setUp() { | ||||
|         app = new MonopolyApp(); | ||||
|         app.simpleInitApp(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T001: UC-game-01 - Überprüft, ob die Anwendung erfolgreich gestartet wird und das Hauptmenü angezeigt wird | ||||
|     public void testStartApplication() { | ||||
|         Spatial mainMenu = app.getGuiNode().getChild("MainMenu"); | ||||
|         assertNotNull("Das Hauptmenü sollte sichtbar sein", mainMenu); | ||||
|     } | ||||
| /* | ||||
|     @Test | ||||
|     // T002: UC-game-02 - Überprüft, ob das Startmenü nach dem Start der Anwendung angezeigt wird | ||||
|     public void testOpenStartMenu() { | ||||
|         Spatial startMenu = app.getGuiNode().getChild("StartMenu"); | ||||
|         assertNotNull("Das Startmenü sollte sichtbar sein", startMenu); | ||||
|     } | ||||
| */ | ||||
| @Test | ||||
| // T002: UC-game-02 - Überprüft, ob das Startmenü (MainMenu) angezeigt wird und die Buttons korrekt funktionieren | ||||
| public void testMainMenuButtons() { | ||||
|     Spatial mainMenu = app.getGuiNode().getChild("MainMenu"); | ||||
|     assertNotNull("Das Hauptmenü (MainMenu) sollte sichtbar sein", mainMenu); | ||||
|  | ||||
|     Spatial playButtonSpatial = app.getGuiNode().getChild("PlayButton"); | ||||
|     assertNotNull("Der 'Spielen'-Button sollte existieren", playButtonSpatial); | ||||
|  | ||||
|     Spatial settingsButtonSpatial = app.getGuiNode().getChild("SettingsButton"); | ||||
|     assertNotNull("Der 'Einstellungen'-Button sollte existieren", settingsButtonSpatial); | ||||
|  | ||||
|     Spatial exitButtonSpatial = app.getGuiNode().getChild("ExitButton"); | ||||
|     assertNotNull("Der 'Spiel beenden'-Button sollte existieren", exitButtonSpatial); | ||||
|  | ||||
|     // Optional: Überprüfung der Funktionalität (Simulation von Button-Klicks) | ||||
|     if (playButtonSpatial instanceof Button) { | ||||
|         Button playButton = (Button) playButtonSpatial; | ||||
|         playButton.click(); | ||||
|  | ||||
|         Spatial newGameMenu = app.getGuiNode().getChild("NewGameMenu"); | ||||
|         assertNotNull("Das Spielerstellungsmenü sollte nach dem Klicken auf 'Spielen' sichtbar sein", newGameMenu); | ||||
|     } else { | ||||
|         throw new AssertionError("'PlayButton' ist kein Button-Objekt."); | ||||
|     } | ||||
|  | ||||
|     if (settingsButtonSpatial instanceof Button) { | ||||
|         Button settingsButton = (Button) settingsButtonSpatial; | ||||
|         settingsButton.click(); | ||||
|  | ||||
|         Spatial settingsMenu = app.getGuiNode().getChild("SettingsMenu"); | ||||
|         assertNotNull("Das Einstellungsmenü sollte nach dem Klicken auf 'Einstellungen' sichtbar sein", settingsMenu); | ||||
|     } else { | ||||
|         throw new AssertionError("'SettingsButton' ist kein Button-Objekt."); | ||||
|     } | ||||
|  | ||||
|     if (exitButtonSpatial instanceof Button) { | ||||
|         Button exitButton = (Button) exitButtonSpatial; | ||||
|         exitButton.click(); | ||||
|  | ||||
|         Spatial mainMenuAfterExit = app.getGuiNode().getChild("MainMenu"); | ||||
|         assertNull("Das Hauptmenü sollte nach dem Klicken auf 'Spiel beenden' nicht mehr sichtbar sein", mainMenuAfterExit); | ||||
|     } else { | ||||
|         throw new AssertionError("'ExitButton' ist kein Button-Objekt."); | ||||
|     } | ||||
| } | ||||
|     @Test | ||||
|     // T003: UC-game-03 - Überprüft, ob der „Spiel starten“-Button das Spielerstellungsmenü öffnet | ||||
|     public void testNavigateToPlayOption() { | ||||
|         Spatial startGameButtonSpatial = app.getGuiNode().getChild("StartGameButton"); | ||||
|         assertNotNull("Der 'Spiel starten'-Button sollte existieren", startGameButtonSpatial); | ||||
|  | ||||
|         if (startGameButtonSpatial instanceof Button) { | ||||
|             Button startGameButton = (Button) startGameButtonSpatial; | ||||
|             startGameButton.click(); | ||||
|  | ||||
|             Spatial newGameMenu = app.getGuiNode().getChild("NewGameMenu"); | ||||
|             assertNotNull("Das Spielerstellungsmenü sollte sichtbar sein", newGameMenu); | ||||
|         } else { | ||||
|             throw new AssertionError("'StartGameButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T004: UC-game-04 - Testet, ob die Anwendung geschlossen wird, wenn „Beenden“ im Hauptmenü gewählt wird | ||||
|     public void testExitApplicationFromMenu() { | ||||
|         Spatial exitButtonSpatial = app.getGuiNode().getChild("ExitButton"); | ||||
|         assertNotNull("Der 'Beenden'-Button sollte existieren", exitButtonSpatial); | ||||
|  | ||||
|         if (exitButtonSpatial instanceof Button) { | ||||
|             Button exitButton = (Button) exitButtonSpatial; | ||||
|             exitButton.click(); | ||||
|  | ||||
|             Spatial mainMenuAfterExit = app.getGuiNode().getChild("MainMenu"); | ||||
|             assertNull("Das Hauptmenü sollte nach dem Beenden nicht mehr sichtbar sein", mainMenuAfterExit); | ||||
|         } else { | ||||
|             throw new AssertionError("'ExitButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T005: UC-game-05 - Überprüft, ob das Einstellungen-Menü aus dem Hauptmenü aufgerufen werden kann | ||||
|     public void testOpenSettingsFromMenu() { | ||||
|         Spatial settingsButtonSpatial = app.getGuiNode().getChild("SettingsButton"); | ||||
|         assertNotNull("Der 'Einstellungen'-Button sollte existieren", settingsButtonSpatial); | ||||
|  | ||||
|         if (settingsButtonSpatial instanceof Button) { | ||||
|             Button settingsButton = (Button) settingsButtonSpatial; | ||||
|             settingsButton.click(); | ||||
|  | ||||
|             Spatial settingsMenu = app.getGuiNode().getChild("SettingsMenu"); | ||||
|             assertNotNull("Das Einstellungsmenü sollte sichtbar sein", settingsMenu); | ||||
|         } else { | ||||
|             throw new AssertionError("'SettingsButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T006: UC-game-06 - Testet, ob das Spielmenü geöffnet wird, wenn der Spieler im Spiel „ESC“ drückt | ||||
|     public void testOpenGameMenuWithESC() { | ||||
|         app.escape(true); // Simuliert das Drücken der ESC-Taste | ||||
|  | ||||
|         Spatial gameMenu = app.getGuiNode().getChild("GameMenu"); | ||||
|         assertNotNull("Das Spielmenü sollte nach Drücken von ESC sichtbar sein", gameMenu); | ||||
|  | ||||
|         app.escape(false); // Simuliert das Loslassen der ESC-Taste | ||||
|     } | ||||
|  | ||||
|  | ||||
|     @Test | ||||
|     // T083: UC-menu-05 - Überprüfen, ob der Spieler erfolgreich ins Spiel zurückkehren kann | ||||
|     public void testReturnToGame() { | ||||
|         Spatial returnButtonSpatial = app.getGuiNode().getChild("ReturnButton"); | ||||
|         assertNotNull("Der 'Zurück'-Button sollte existieren", returnButtonSpatial); | ||||
|  | ||||
|         if (returnButtonSpatial instanceof Button) { | ||||
|             Button returnButton = (Button) returnButtonSpatial; | ||||
|             returnButton.click(); | ||||
|  | ||||
|             Spatial gameScene = app.getGuiNode().getChild("GameScene"); | ||||
|             assertNotNull("Die Spielszene sollte nach dem Klick auf 'Zurück' sichtbar sein", gameScene); | ||||
|         } else { | ||||
|             throw new AssertionError("'ReturnButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,20 +1,606 @@ | ||||
| //////////////////////////////////////// | ||||
| // Programming project code | ||||
| // UniBw M, 2022, 2023, 2024 | ||||
| // www.unibw.de/inf2 | ||||
| // (c) Mark Minas (mark.minas@unibw.de) | ||||
| //////////////////////////////////////// | ||||
|  | ||||
| package pp.monopoly.game.client; | ||||
|  | ||||
| import com.jme3.scene.Spatial; | ||||
| import com.simsilica.lemur.Button; | ||||
| import com.simsilica.lemur.TextField; | ||||
| import org.junit.Before; | ||||
| import org.junit.Test; | ||||
|  | ||||
| import pp.monopoly.client.MonopolyApp; | ||||
| import pp.monopoly.game.server.Player; | ||||
| import pp.monopoly.message.server.DiceResult; | ||||
| import pp.monopoly.notification.ClientStateEvent; | ||||
|  | ||||
| import static org.junit.Assert.assertEquals; | ||||
| import static org.junit.Assert.assertFalse; | ||||
| import static org.junit.Assert.assertNotNull; | ||||
| import static org.junit.Assert.assertTrue; | ||||
|  | ||||
| public class ClientGameLogicTest { | ||||
|  | ||||
|     private MonopolyApp app; | ||||
|  | ||||
|     private MonopolyApp app; | ||||
|     private NewGameMenu newGameMenu; | ||||
|  | ||||
|     @Before | ||||
|     public void setUp() { | ||||
|         app = new MonopolyApp(); | ||||
|         app.simpleInitApp(); // Initialisiert die App mit GUI und Spielzuständen | ||||
|         newGameMenu = new NewGameMenu(); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T006: UC-game-06 - Testet, ob das Spielmenü geöffnet wird, wenn der Spieler im Spiel „ESC“ drückt | ||||
|     public void testOpenGameMenuWithESC() { | ||||
|         app.escape(true); // Simuliert das Drücken der ESC-Taste | ||||
|  | ||||
|         Spatial gameMenu = app.getGuiNode().getChild("GameMenu"); | ||||
|         assertNotNull("Das Spielmenü sollte sichtbar sein, nachdem ESC gedrückt wurde", gameMenu); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T007: UC-game-07 - Testet, ob der Spieler erfolgreich den Namen des Hosts eingeben kann | ||||
|     public void testEnterHostName() { | ||||
|         Spatial hostNameField = app.getGuiNode().getChild("HostNameField"); | ||||
|         assertNotNull("Das Eingabefeld für den Hostnamen sollte sichtbar sein", hostNameField); | ||||
|  | ||||
|         if (hostNameField instanceof TextField) { | ||||
|             TextField hostNameInput = (TextField) hostNameField; | ||||
|             hostNameInput.setText("TestHost"); | ||||
|  | ||||
|             assertEquals("Der eingegebene Hostname sollte 'TestHost' sein", "TestHost", hostNameInput.getText()); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'HostNameField' ist kein TextField-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T008: UC-game-08 - Testet, ob der Spieler die Portnummer des Hosts eingeben kann | ||||
|     public void testEnterPortNumber() { | ||||
|         Spatial portNumberField = app.getGuiNode().getChild("PortNumberField"); | ||||
|         assertNotNull("Das Eingabefeld für die Portnummer sollte sichtbar sein", portNumberField); | ||||
|  | ||||
|         if (portNumberField instanceof TextField) { | ||||
|             TextField portNumberInput = (TextField) portNumberField; | ||||
|             portNumberInput.setText("12345"); | ||||
|  | ||||
|             assertEquals("Die eingegebene Portnummer sollte '12345' sein", "12345", portNumberInput.getText()); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'PortNumberField' ist kein TextField-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T009: UC-game-09 - Testet, ob der Spieler das Erstellen eines Spiels abbrechen kann | ||||
|     public void testCancelGameCreation() { | ||||
|         Spatial cancelButtonSpatial = app.getGuiNode().getChild("CancelButton"); | ||||
|         assertNotNull("Der 'Abbrechen'-Button sollte existieren", cancelButtonSpatial); | ||||
|  | ||||
|         if (cancelButtonSpatial instanceof Button) { | ||||
|             Button cancelButton = (Button) cancelButtonSpatial; | ||||
|             cancelButton.click(); | ||||
|  | ||||
|             Spatial mainMenu = app.getGuiNode().getChild("MainMenu"); | ||||
|             assertNotNull("Das Hauptmenü sollte nach dem Abbrechen des Spiels sichtbar sein", mainMenu); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'CancelButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /* | ||||
|         @Test | ||||
|         // T010: UC-game-10 - Testet, ob der Spieler die Spielerlobby betreten kann | ||||
|         public void testEnterPlayerLobby() { | ||||
|             app.receivedEvent(new pp.monopoly.notification.ClientStateEvent(pp.monopoly.notification.ClientStateEvent.State.LOBBY)); | ||||
|  | ||||
|             Spatial playerLobby = app.getGuiNode().getChild("PlayerLobby"); | ||||
|             assertNotNull("Die Spielerlobby sollte nach dem Beitritt sichtbar sein", playerLobby); | ||||
|         } | ||||
|  | ||||
|  | ||||
|         @Test | ||||
|         // T010: UC-game-10 - Testet, ob der Spieler die Spielerlobby betreten kann | ||||
|         public void testEnterPlayerLobby() { | ||||
|             // Simuliert den Empfang eines ClientStateEvent für den Lobby-State | ||||
|             app.receivedEvent(new ClientStateEvent(ClientStateEvent.State.LOBBY)); | ||||
|  | ||||
|             // Überprüft, ob das Lobby-UI sichtbar ist | ||||
|             Spatial playerLobby = app.getGuiNode().getChild("PlayerLobby"); | ||||
|             assertNotNull("Die Spielerlobby sollte nach dem Beitritt sichtbar sein", playerLobby); | ||||
|     */ | ||||
|     @Test | ||||
|     // T011: UC-game-11 - Testet, ob der hostende Spieler einen Startbetrag eingeben kann | ||||
|     public void testEnterStartingCapital() { | ||||
|         Spatial startingCapitalField = app.getGuiNode().getChild("StartingCapitalField"); | ||||
|         assertNotNull("Das Eingabefeld für den Startbetrag sollte existieren", startingCapitalField); | ||||
|  | ||||
|         if (startingCapitalField instanceof TextField) { | ||||
|             TextField startingCapitalInput = (TextField) startingCapitalField; | ||||
|             startingCapitalInput.setText("1500"); | ||||
|  | ||||
|             assertEquals("Der eingegebene Startbetrag sollte '1500' sein", "1500", startingCapitalInput.getText()); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'StartingCapitalField' ist kein TextField-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T012: UC-game-12 - Testet, ob der Spieler den Startbetrag um 100 € erhöhen kann | ||||
|     public void testIncreaseStartingCapital() { | ||||
|         Spatial increaseButton = app.getGuiNode().getChild("IncreaseCapitalButton"); | ||||
|         assertNotNull("Der 'Erhöhen'-Button sollte existieren", increaseButton); | ||||
|  | ||||
|         if (increaseButton instanceof Button) { | ||||
|             Button increaseCapitalButton = (Button) increaseButton; | ||||
|             increaseCapitalButton.click(); | ||||
|  | ||||
|             Spatial startingCapitalField = app.getGuiNode().getChild("StartingCapitalField"); | ||||
|             if (startingCapitalField instanceof TextField) { | ||||
|                 TextField startingCapitalInput = (TextField) startingCapitalField; | ||||
|                 assertEquals("Der Startbetrag sollte um 100 erhöht worden sein", "1600", startingCapitalInput.getText()); | ||||
|             } | ||||
|             else { | ||||
|                 throw new AssertionError("'StartingCapitalField' ist kein TextField-Objekt."); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'IncreaseCapitalButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T013: UC-game-13 - Testet, ob der Spieler den Startbetrag um 100 € senken kann | ||||
|     public void testDecreaseStartingCapital() { | ||||
|         Spatial decreaseButton = app.getGuiNode().getChild("DecreaseCapitalButton"); | ||||
|         assertNotNull("Der 'Senken'-Button sollte existieren", decreaseButton); | ||||
|  | ||||
|         if (decreaseButton instanceof Button) { | ||||
|             Button decreaseCapitalButton = (Button) decreaseButton; | ||||
|             decreaseCapitalButton.click(); | ||||
|  | ||||
|             Spatial startingCapitalField = app.getGuiNode().getChild("StartingCapitalField"); | ||||
|             if (startingCapitalField instanceof TextField) { | ||||
|                 TextField startingCapitalInput = (TextField) startingCapitalField; | ||||
|                 assertEquals("Der Startbetrag sollte um 100 gesenkt worden sein", "1400", startingCapitalInput.getText()); | ||||
|             } | ||||
|             else { | ||||
|                 throw new AssertionError("'StartingCapitalField' ist kein TextField-Objekt."); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'DecreaseCapitalButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T014: UC-game-14 - Testet, ob die Standard-Spielernamen korrekt voreingestellt sind | ||||
|     public void testDefaultPlayerName() { | ||||
|         Spatial playerNameField = app.getGuiNode().getChild("PlayerNameField"); | ||||
|         assertNotNull("Das Eingabefeld für den Spielernamen sollte existieren", playerNameField); | ||||
|  | ||||
|         if (playerNameField instanceof TextField) { | ||||
|             TextField playerNameInput = (TextField) playerNameField; | ||||
|             assertEquals("Der voreingestellte Spielername sollte 'Spieler 1' sein", "Spieler 1", playerNameInput.getText()); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'PlayerNameField' ist kein TextField-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T015: UC-game-15 - Testet, ob der Spieler einen Anzeigenamen eingeben kann | ||||
|     public void testEnterDisplayName() { | ||||
|         Spatial displayNameField = app.getGuiNode().getChild("DisplayNameField"); | ||||
|         assertNotNull("Das Eingabefeld für den Anzeigenamen sollte existieren", displayNameField); | ||||
|  | ||||
|         if (displayNameField instanceof TextField) { | ||||
|             TextField displayNameInput = (TextField) displayNameField; | ||||
|             displayNameInput.setText("MaxMustermann"); | ||||
|  | ||||
|             assertEquals("Der eingegebene Anzeigename sollte 'MaxMustermann' sein", "MaxMustermann", displayNameInput.getText()); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'DisplayNameField' ist kein TextField-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T016: UC-game-16 - Testet, ob eine Warnung angezeigt wird, wenn ein Spieler einen bereits belegten Namen eingibt | ||||
|     public void testDuplicateNameEntry() { | ||||
|         Spatial playerNameField = app.getGuiNode().getChild("PlayerNameField"); | ||||
|         assertNotNull("Das Eingabefeld für den Spielernamen sollte existieren", playerNameField); | ||||
|  | ||||
|         if (playerNameField instanceof TextField) { | ||||
|             TextField playerNameInput = (TextField) playerNameField; | ||||
|             playerNameInput.setText("Spieler 1"); | ||||
|             app.getGuiNode().getChild("AddPlayerButton").click(); // Spieler hinzufügen | ||||
|             playerNameInput.setText("Spieler 1"); | ||||
|             app.getGuiNode().getChild("AddPlayerButton").click(); // Spieler mit gleichem Namen hinzufügen | ||||
|  | ||||
|             Spatial warning = app.getGuiNode().getChild("DuplicateNameWarning"); | ||||
|             assertNotNull("Es sollte eine Warnung angezeigt werden, wenn ein Name doppelt vergeben wird", warning); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'PlayerNameField' ist kein TextField-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T017: UC-game-17 - Testet, ob der Spieler eine verfügbare Spielfigurfarbe auswählen kann | ||||
|     public void testSelectPlayerColor() { | ||||
|         Spatial colorSelector = app.getGuiNode().getChild("ColorSelector"); | ||||
|         assertNotNull("Der Farbwähler sollte existieren", colorSelector); | ||||
|  | ||||
|         if (colorSelector instanceof Button) { | ||||
|             Button colorButton = (Button) colorSelector; | ||||
|             colorButton.click(); | ||||
|  | ||||
|             Spatial selectedColor = app.getGuiNode().getChild("SelectedColor"); | ||||
|             assertNotNull("Die gewählte Farbe sollte sichtbar sein", selectedColor); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'ColorSelector' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T018: UC-game-18 - Testet, ob eine belegte Spielfigurfarbe nicht ausgewählt werden kann | ||||
|     public void testSelectOccupiedColor() { | ||||
|         app.getGuiNode().getChild("ColorSelectorRed").click(); // Spieler 1 wählt Rot | ||||
|         app.getGuiNode().getChild("AddPlayerButton").click(); // Spieler 1 hinzufügen | ||||
|  | ||||
|         app.getGuiNode().getChild("ColorSelectorRed").click(); // Spieler 2 versucht Rot zu wählen | ||||
|         Spatial warning = app.getGuiNode().getChild("ColorOccupiedWarning"); | ||||
|         assertNotNull("Es sollte eine Warnung angezeigt werden, wenn eine belegte Farbe ausgewählt wird", warning); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T019: UC-game-19 - Testet, ob der Spieler eine Spielfigur auswählen kann | ||||
|     public void testSelectPlayerToken() { | ||||
|         Spatial tokenSelector = app.getGuiNode().getChild("TokenSelector"); | ||||
|         assertNotNull("Der Token-Wähler sollte existieren", tokenSelector); | ||||
|  | ||||
|         if (tokenSelector instanceof Button) { | ||||
|             Button tokenButton = (Button) tokenSelector; | ||||
|             tokenButton.click(); | ||||
|  | ||||
|             Spatial selectedToken = app.getGuiNode().getChild("SelectedToken"); | ||||
|             assertNotNull("Die gewählte Spielfigur sollte sichtbar sein", selectedToken); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'TokenSelector' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T020: UC-game-20 - Testet, ob eine belegte Spielfigur nicht ausgewählt werden kann | ||||
|     public void testSelectOccupiedToken() { | ||||
|         app.getGuiNode().getChild("TokenSelectorShip").click(); | ||||
|         app.getGuiNode().getChild("AddPlayerButton").click(); | ||||
|  | ||||
|         app.getGuiNode().getChild("TokenSelectorShip").click(); | ||||
|         Spatial warning = app.getGuiNode().getChild("TokenOccupiedWarning"); | ||||
|         assertNotNull("Es sollte eine Warnung angezeigt werden, wenn eine belegte Spielfigur ausgewählt wird", warning); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T021: UC-game-14 - Überprüft, ob der Spieler zur „Spiel erstellen“-Ansicht zurückkehrt, wenn Abbrechen gedrückt wird | ||||
|     public void testCancelPlayerLobby() { | ||||
|         Spatial cancelButtonSpatial = app.getGuiNode().getChild("CancelLobbyButton"); | ||||
|         assertNotNull("Der 'Abbrechen'-Button in der Lobby sollte existieren", cancelButtonSpatial); | ||||
|  | ||||
|         if (cancelButtonSpatial instanceof Button) { | ||||
|             Button cancelButton = (Button) cancelButtonSpatial; | ||||
|             cancelButton.click(); | ||||
|  | ||||
|             Spatial mainMenu = app.getGuiNode().getChild("MainMenu"); | ||||
|             assertNotNull("Das Hauptmenü sollte nach dem Abbrechen der Lobby sichtbar sein", mainMenu); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'CancelLobbyButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T022: UC-game-15 - Überprüft, ob das Spielmenü in der Lobby durch Drücken der ESC-Taste geöffnet wird | ||||
|     public void testOpenLobbyMenuWithESC() { | ||||
|         app.escape(true); // Simuliert das Drücken der ESC-Taste | ||||
|  | ||||
|         Spatial lobbyMenu = app.getGuiNode().getChild("LobbyMenu"); | ||||
|         assertNotNull("Das Lobby-Menü sollte sichtbar sein, nachdem ESC in der Lobby gedrückt wurde", lobbyMenu); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T023: UC-game-16 - Testet, ob der Spieler die Auswahl der Spielfigur bestätigen kann | ||||
|     public void testPlayerReadyConfirmation() { | ||||
|         Spatial confirmButtonSpatial = app.getGuiNode().getChild("ConfirmTokenButton"); | ||||
|         assertNotNull("Der 'Bestätigen'-Button für die Spielfigur sollte existieren", confirmButtonSpatial); | ||||
|  | ||||
|         if (confirmButtonSpatial instanceof Button) { | ||||
|             Button confirmButton = (Button) confirmButtonSpatial; | ||||
|             confirmButton.click(); | ||||
|  | ||||
|             Spatial readyStatus = app.getGuiNode().getChild("PlayerReadyStatus"); | ||||
|             assertNotNull("Der Status 'Bereit' sollte angezeigt werden, nachdem die Spielfigur bestätigt wurde", readyStatus); | ||||
|         } | ||||
|         else { | ||||
|             throw new AssertionError("'ConfirmTokenButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|         @Test | ||||
|         // T024: UC-game-16 - Überprüft, ob das Spiel startet, wenn alle Spieler ihre Auswahl bestätigt haben | ||||
|         public void testAllPlayersReady() { | ||||
|             app.receivedEvent(new pp.monopoly.notification.ClientStateEvent(pp.monopoly.notification.ClientStateEvent.State.ALL_PLAYERS_READY)); | ||||
|  | ||||
|             Spatial gameScreen = app.getGuiNode().getChild("GameScreen"); | ||||
|             assertNotNull("Das Spiel sollte starten, wenn alle Spieler bereit sind", gameScreen); | ||||
|         } | ||||
|         @Test | ||||
|         // T025: UC-game-17 - Testet, ob das Einstellungen-Menü während des Spiels geöffnet wird | ||||
|         public void testOpenMainGameSettings () { | ||||
|             app.escape(true); | ||||
|  | ||||
|             Spatial settingsButton = app.getGuiNode().getChild("SettingsButton"); | ||||
|             assertNotNull("Der 'Einstellungen'-Button sollte im Spielmenü vorhanden sein", settingsButton); | ||||
|  | ||||
|             if (settingsButton instanceof Button) { | ||||
|                 ((Button) settingsButton).click(); | ||||
|                 Spatial settingsMenu = app.getGuiNode().getChild("SettingsMenu"); | ||||
|                 assertNotNull("Das Einstellungen-Menü sollte im Spiel angezeigt werden", settingsMenu); | ||||
|             } | ||||
|             else { | ||||
|                 throw new AssertionError("'SettingsButton' ist kein Button-Objekt."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     @Test | ||||
| // T026: UC-gameplay-01 - Überprüft, ob der Spieler erfolgreich würfeln kann | ||||
|     public void testRollDice() { | ||||
|         Spatial rollDiceButton = app.getGuiNode().getChild("RollDiceButton"); | ||||
|         assertNotNull("Der 'Würfeln'-Button sollte sichtbar sein", rollDiceButton); | ||||
|  | ||||
|         if (rollDiceButton instanceof Button) { | ||||
|             ((Button) rollDiceButton).click(); // Simuliert einen Würfelwurf | ||||
|             Spatial diceResult = app.getGuiNode().getChild("DiceResult"); | ||||
|             assertNotNull("Das Ergebnis des Würfelwurfs sollte angezeigt werden", diceResult); | ||||
|         } | ||||
|     } | ||||
|     @Test | ||||
| // T027: UC-gameplay-01 - Überprüft, ob der aktive Spieler die richtige Anzahl an Feldern basierend auf dem Wurf bewegt | ||||
|     public void testMovePlayerAutomatically() { | ||||
|         Game game = new Game(); // Initialisiert ein neues Spiel | ||||
|         PlayerHandler playerHandler = game.getPlayerHandler(); // Holt den PlayerHandler, der die Spieler verwaltet | ||||
|  | ||||
|         Player activePlayer = playerHandler.getActivePlayer(); // Holt den aktuellen aktiven Spieler | ||||
|         assertNotNull("Es sollte einen aktiven Spieler geben", activePlayer); | ||||
|  | ||||
|         DiceResult diceResult = game.rollDice(); // Würfelwurf simulieren | ||||
|         int steps = diceResult.getTotal(); | ||||
|  | ||||
|         int initialPosition = activePlayer.getFieldId(); // Ursprüngliche Position des aktiven Spielers | ||||
|         playerHandler.moveActivePlayer(steps); // Bewegt den aktiven Spieler basierend auf dem Wurf | ||||
|  | ||||
|         int expectedPosition = (initialPosition + steps) % game.getGameBoard().getNumberOfFields(); // Zielposition berechnen | ||||
|         int newPosition = activePlayer.getFieldId(); | ||||
|  | ||||
|         assertEquals("Der aktive Spieler sollte sich korrekt bewegen", expectedPosition, newPosition); | ||||
|  | ||||
|         // Überprüfen, dass alle anderen Spieler im WaitForTurn-State bleiben | ||||
|         playerHandler.getPlayers().stream() | ||||
|                      .filter(player -> player != activePlayer) | ||||
|                      .forEach(player -> assertTrue("Andere Spieler sollten im WaitForTurn-State sein", player.getState() instanceof WaitForTurnState)); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| // T028: UC-gameplay-02 - Überprüft, ob die Würfel zufällige Zahlen generieren | ||||
|     public void testGenerationDice() { | ||||
|         boolean isRandom = false; | ||||
|         DiceResult previousResult = null; | ||||
|  | ||||
|         for (int i = 0; i < 10; i++) { | ||||
|             DiceResult currentResult = app.getGame().rollDice(); | ||||
|  | ||||
|             assertNotNull("Das Würfelergebnis sollte nicht null sein", currentResult); | ||||
|             assertTrue("Die Würfelzahl 1 sollte zwischen 1 und 6 liegen", currentResult.getDice1() >= 1 && currentResult.getDice1() <= 6); | ||||
|             assertTrue("Die Würfelzahl 2 sollte zwischen 1 und 6 liegen", currentResult.getDice2() >= 1 && currentResult.getDice2() <= 6); | ||||
|  | ||||
|             if (previousResult != null && (currentResult.getDice1() != previousResult.getDice1() || currentResult.getDice2() != previousResult.getDice2())) { | ||||
|                 isRandom = true; // Unterschiedliche Würfelwerte gefunden | ||||
|                 break; | ||||
|             } | ||||
|             previousResult = currentResult; | ||||
|         } | ||||
|         assertTrue("Die Würfelergebnisse sollten zufällig sein", isRandom); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| // T029: UC-gameplay-03 - Überprüft, ob die richtigen Augenzahlen angezeigt werden | ||||
|     public void testDisplayResults() { | ||||
|         DiceResult result = app.getGame().rollDice(); | ||||
|  | ||||
|         assertNotNull("Das Würfelergebnis sollte nicht null sein", result); | ||||
|         assertTrue("Die Würfelzahl 1 sollte zwischen 1 und 6 liegen", result.getDice1() >= 1 && result.getDice1() <= 6); | ||||
|         assertTrue("Die Würfelzahl 2 sollte zwischen 1 und 6 liegen", result.getDice2() >= 1 && result.getDice2() <= 6); | ||||
|         assertEquals("Die Summe sollte korrekt berechnet werden", result.getDice1() + result.getDice2(), result.getTotal()); | ||||
|     } | ||||
|     @Test | ||||
|     // T030: UC-gameplay-04 - Überprüfen, ob die Summe der Würfelergebnisse korrekt berechnet wird | ||||
|     public void testSumDiceResults() { | ||||
|         Spatial rollDiceButton = app.getGuiNode().getChild("RollDiceButton"); | ||||
|         assertNotNull("Der 'Würfeln'-Button sollte sichtbar sein", rollDiceButton); | ||||
|  | ||||
|         if (rollDiceButton instanceof Button) { | ||||
|             ((Button) rollDiceButton).click(); // Simuliert einen Würfelwurf | ||||
|             Spatial diceResult1 = app.getGuiNode().getChild("DiceResult1"); | ||||
|             Spatial diceResult2 = app.getGuiNode().getChild("DiceResult2"); | ||||
|  | ||||
|             assertNotNull("Die Ergebnisse des Würfelwurfs sollten angezeigt werden", diceResult1); | ||||
|             assertNotNull("Die Ergebnisse des Würfelwurfs sollten angezeigt werden", diceResult2); | ||||
|  | ||||
|             int result1 = Integer.parseInt(diceResult1.getUserData("value").toString()); | ||||
|             int result2 = Integer.parseInt(diceResult2.getUserData("value").toString()); | ||||
|             int expectedSum = result1 + result2; | ||||
|  | ||||
|             Spatial sumDisplay = app.getGuiNode().getChild("DiceSum"); | ||||
|             assertEquals("Die Summe der Würfelergebnisse sollte korrekt angezeigt werden", expectedSum, Integer.parseInt(sumDisplay.getUserData("value").toString())); | ||||
|         } else { | ||||
|             throw new AssertionError("'RollDiceButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T031: UC-gameplay-05 - Überprüfen, ob die Würfel nach dem Wurf ausgegraut werden | ||||
|     public void testGrayOutDiceAfterRoll() { | ||||
|         Spatial rollDiceButton = app.getGuiNode().getChild("RollDiceButton"); | ||||
|         assertNotNull("Der 'Würfeln'-Button sollte sichtbar sein", rollDiceButton); | ||||
|  | ||||
|         if (rollDiceButton instanceof Button) { | ||||
|             ((Button) rollDiceButton).click(); // Simuliert einen Würfelwurf | ||||
|  | ||||
|             Spatial diceDisplay = app.getGuiNode().getChild("DiceDisplay"); | ||||
|             assertNotNull("Die Würfelanzeige sollte nach dem Wurf sichtbar sein", diceDisplay); | ||||
|  | ||||
|             boolean isGrayedOut = diceDisplay.getUserData("grayedOut"); | ||||
|             assertTrue("Die Würfel sollten nach dem Wurf ausgegraut sein", isGrayedOut); | ||||
|         } else { | ||||
|             throw new AssertionError("'RollDiceButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T032: UC-gameplay-06 - Überprüfen, ob das Würfeln eines Paschs erkannt wird | ||||
|     public void testDetectDouble() { | ||||
|         Spatial rollDiceButton = app.getGuiNode().getChild("RollDiceButton"); | ||||
|         assertNotNull("Der 'Würfeln'-Button sollte sichtbar sein", rollDiceButton); | ||||
|  | ||||
|         if (rollDiceButton instanceof Button) { | ||||
|             // Simuliert mehrere Würfe, um einen Pasch zu erkennen | ||||
|             for (int i = 0; i < 10; i++) { | ||||
|                 ((Button) rollDiceButton).click(); | ||||
|                 Spatial diceResult1 = app.getGuiNode().getChild("DiceResult1"); | ||||
|                 Spatial diceResult2 = app.getGuiNode().getChild("DiceResult2"); | ||||
|  | ||||
|                 int result1 = Integer.parseInt(diceResult1.getUserData("value").toString()); | ||||
|                 int result2 = Integer.parseInt(diceResult2.getUserData("value").toString()); | ||||
|  | ||||
|                 if (result1 == result2) { | ||||
|                     Spatial doubleIndicator = app.getGuiNode().getChild("DoubleIndicator"); | ||||
|                     assertNotNull("Ein Pasch sollte angezeigt werden, wenn zwei identische Zahlen geworfen werden", doubleIndicator); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|             throw new AssertionError("'RollDiceButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T033: UC-gameplay-06 - Überprüfen, ob der Spieler bei einem Pasch erneut würfeln darf | ||||
|     public void testDoubleRoll() { | ||||
|         Spatial rollDiceButton = app.getGuiNode().getChild("RollDiceButton"); | ||||
|         assertNotNull("Der 'Würfeln'-Button sollte sichtbar sein", rollDiceButton); | ||||
|  | ||||
|         if (rollDiceButton instanceof Button) { | ||||
|             // Simuliert das Würfeln eines Paschs | ||||
|             ((Button) rollDiceButton).click(); | ||||
|             Spatial diceResult1 = app.getGuiNode().getChild("DiceResult1"); | ||||
|             Spatial diceResult2 = app.getGuiNode().getChild("DiceResult2"); | ||||
|  | ||||
|             int result1 = Integer.parseInt(diceResult1.getUserData("value").toString()); | ||||
|             int result2 = Integer.parseInt(diceResult2.getUserData("value").toString()); | ||||
|  | ||||
|             if (result1 == result2) { // Überprüft, ob ein Pasch geworfen wurde | ||||
|                 Spatial rollAgainIndicator = app.getGuiNode().getChild("RollAgainIndicator"); | ||||
|                 assertNotNull("Der Spieler sollte bei einem Pasch erneut würfeln dürfen", rollAgainIndicator); | ||||
|             } | ||||
|         } else { | ||||
|             throw new AssertionError("'RollDiceButton' ist kein Button-Objekt."); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
|     // T034: UC-gameplay-06 - Überprüfen, ob der Spieler nach dem dritten Pasch ins Gefängnis muss | ||||
|     public void testTripleDoubleGulag() { | ||||
|         int doubleCount = 0; | ||||
|  | ||||
|         for (int i = 0; i < 3; i++) { | ||||
|             Spatial rollDiceButton = app.getGuiNode().getChild("RollDiceButton"); | ||||
|             assertNotNull("Der 'Würfeln'-Button sollte sichtbar sein", rollDiceButton); | ||||
|  | ||||
|             if (rollDiceButton instanceof Button) { | ||||
|                 ((Button) rollDiceButton).click(); | ||||
|                 Spatial diceResult1 = app.getGuiNode().getChild("DiceResult1"); | ||||
|                 Spatial diceResult2 = app.getGuiNode().getChild("DiceResult2"); | ||||
|  | ||||
|                 int result1 = Integer.parseInt(diceResult1.getUserData("value").toString()); | ||||
|                 int result2 = Integer.parseInt(diceResult2.getUserData("value").toString()); | ||||
|  | ||||
|                 if (result1 == result2) { | ||||
|                     doubleCount++; | ||||
|                 } | ||||
|  | ||||
|                 if (doubleCount == 3) { | ||||
|                     Spatial jailIndicator = app.getGuiNode().getChild("JailIndicator"); | ||||
|                     assertNotNull("Der Spieler sollte nach dem dritten Pasch ins Gefängnis gehen", jailIndicator); | ||||
|                     break; | ||||
|                 } | ||||
|             } else { | ||||
|                 throw new AssertionError("'RollDiceButton' ist kein Button-Objekt."); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         assertTrue("Der Spieler sollte drei Paschs geworfen haben", doubleCount == 3); | ||||
|     } | ||||
|     @Test | ||||
| // T035: UC-gameplay-07 - Überprüfen, ob ein Spieler für Steuerfelder korrekt belastet wird | ||||
|     public void testTaxFieldCharges() { | ||||
|         Game game = new Game(); | ||||
|         PlayerHandler playerHandler = game.getPlayerHandler(); | ||||
|         Player activePlayer = playerHandler.getActivePlayer(); | ||||
|         assertNotNull("Es sollte einen aktiven Spieler geben", activePlayer); | ||||
|  | ||||
|         int initialBalance = activePlayer.getAccountBalance(); | ||||
|  | ||||
|         activePlayer.moveToField(game.getGameBoard().getFieldById(4)); // ID 4: Steuerfeld "Diszi" | ||||
|         game.getGameBoard().applyFieldEffect(activePlayer); | ||||
|  | ||||
|         int expectedBalance = initialBalance - 200; // Beispielsteuer: 200 € | ||||
|         assertEquals("Der Spieler sollte für das Steuerfeld korrekt belastet werden", expectedBalance, activePlayer.getAccountBalance()); | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| // T036: UC-gameplay-08 - Überprüfen, ob ein Spieler korrekt ins Gefängnis geschickt wird | ||||
|     public void testGoToJailField() { | ||||
|         Game game = new Game(); | ||||
|         PlayerHandler playerHandler = game.getPlayerHandler(); | ||||
|         Player activePlayer = playerHandler.getActivePlayer(); | ||||
|         assertNotNull("Es sollte einen aktiven Spieler geben", activePlayer); | ||||
|  | ||||
|         activePlayer.moveToField(game.getGameBoard().getFieldById(30)); // ID 30: Ab ins Gefängnis | ||||
|         game.getGameBoard().applyFieldEffect(activePlayer); | ||||
|  | ||||
|         assertEquals("Der Spieler sollte ins Gefängnis geschickt werden", 10, activePlayer.getFieldId()); // ID 10: Gefängnis | ||||
|     } | ||||
|  | ||||
|     @Test | ||||
| // T037: UC-gameplay-09 - Überprüfen, ob ein Spieler bei "Frei Parken" keine Gebühren zahlt | ||||
|     public void testFreeParking() { | ||||
|         Game game = new Game(); | ||||
|         PlayerHandler playerHandler = game.getPlayerHandler(); | ||||
|         Player activePlayer = playerHandler.getActivePlayer(); | ||||
|         assertNotNull("Es sollte einen aktiven Spieler geben", activePlayer); | ||||
|  | ||||
|         int initialBalance = activePlayer.getAccountBalance(); | ||||
|  | ||||
|         activePlayer.moveToField(game.getGameBoard().getFieldById(20)); // ID 20: Frei Parken | ||||
|         game.getGameBoard().applyFieldEffect(activePlayer); | ||||
|  | ||||
|         assertEquals("Der Spieler sollte bei 'Frei Parken' keine Gebühren zahlen", initialBalance, activePlayer.getAccountBalance()); | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user