Compare commits
	
		
			110 Commits
		
	
	
		
			b_schmidt_
			...
			b6968df451
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					b6968df451 | ||
| 
						 | 
					b85bbdd0ad | ||
| 
						 | 
					aa986fb948 | ||
| 
						 | 
					6e63de6e99 | ||
| 
						 | 
					962ecd08fa | ||
| 
						 | 
					580a26b3ea | ||
| 
						 | 
					0cda6b5fa0 | ||
| 
						 | 
					cc8c3795b8 | ||
| 
						 | 
					3c21165efb | ||
| 
						 | 
					818fdf4670 | ||
| 
						 | 
					b7d679b492 | ||
| 
						 | 
					a42093df23 | ||
| 
						 | 
					6157db51da | ||
| 
						 | 
					843052989b | ||
| 
						 | 
					8e01fa3192 | ||
| 
						 | 
					e2126a7ea7 | ||
| 
						 | 
					0b46d14650 | ||
| 
						 | 
					9e28deedf7 | ||
| 
						 | 
					9c0172b413 | ||
| 
						 | 
					2933d022aa | ||
| 
						 | 
					bdefb7193e | ||
| 
						 | 
					7246133363 | ||
| 
						 | 
					69b68a6975 | ||
| 
						 | 
					e38a7e8cdd | ||
| 
						 | 
					dbd9dd0001 | ||
| 
						 | 
					ccb1743a01 | ||
| 
						 | 
					4ac897693c | ||
| 
						 | 
					dfb962e9fe | ||
| 
						 | 
					f97cbd778d | ||
| 
						 | 
					e889a61d43 | ||
| 
						 | 
					a33d422547 | ||
| 
						 | 
					c4b4001b96 | ||
| 
						 | 
					20949b641e | ||
| 
						 | 
					56e2174b6d | ||
| 
						 | 
					acfeebca14 | ||
| 
						 | 
					cc47acbd3f | ||
| 
						 | 
					869e16406d | ||
| 
						 | 
					1c99da4fc5 | ||
| 
						 | 
					b74ca4e702 | ||
| 
						 | 
					5fde7451c8 | ||
| 
						 | 
					6fd9209336 | ||
| 
						 | 
					0213dc3560 | ||
| 
						 | 
					e73c434c1b | ||
| 
						 | 
					b135f3fa50 | ||
| 
						 | 
					0e580a3180 | ||
| 
						 | 
					8e3cb43244 | ||
| 
						 | 
					6dcfb92dba | ||
| 
						 | 
					a7ea5773da | ||
| 
						 | 
					5b66131d30 | ||
| 
						 | 
					cac06dda7d | ||
| 
						 | 
					12c1c97a99 | ||
| 
						 | 
					af4b4243ea | ||
| 
						 | 
					5189c74058 | ||
| 
						 | 
					fffd32e13c | ||
| 
						 | 
					b37a5095f0 | ||
| 
						 | 
					951b4c198c | ||
| 
						 | 
					8698eed273 | ||
| 
						 | 
					1af8614b35 | ||
| 
						 | 
					11faeeaf45 | ||
| 
						 | 
					9d0fcc3370 | ||
| 
						 | 
					2f6d6037de | ||
| 
						 | 
					1d69bcc814 | ||
| 
						 | 
					62c363068b | ||
| 
						 | 
					d2e0b8187b | ||
| 
						 | 
					628b98af9b | ||
| 
						 | 
					627e3dbd7f | ||
| 
						 | 
					61b88f6bf8 | ||
| 
						 | 
					8ab37e73c1 | ||
| 
						 | 
					afdf43ebf1 | ||
| 
						 | 
					2e0d1c059d | ||
| 
						 | 
					2b772199b0 | ||
| 
						 | 
					c37b850798 | ||
| 
						 | 
					c31f924d77 | ||
| 
						 | 
					ed87a6167d | ||
| 
						 | 
					0d86ba0ca9 | ||
| 
						 | 
					f2fd283d06 | ||
| 
						 | 
					10b978debf | ||
| 
						 | 
					85756713df | ||
| 
						 | 
					3bdfd6a78a | ||
| 
						 | 
					e59ab4a320 | ||
| 
						 | 
					15b3902bd3 | ||
| 
						 | 
					b622d66942 | ||
| 
						 | 
					12ef219064 | ||
| 
						 | 
					853b52b52d | ||
| 
						 | 
					7a2ad1d31a | ||
| 
						 | 
					c5ad476eaf | ||
| 
						 | 
					12978ff410 | ||
| 
						 | 
					caa45097c3 | ||
| 
						 | 
					232e3a117c | ||
| 
						 | 
					3668382911 | ||
| 
						 | 
					fafa53ffb7 | ||
| 
						 | 
					44673fd57e | ||
| 
						 | 
					f7149f225c | ||
| 
						 | 
					6773e18d34 | ||
| 
						 | 
					19a9b06f3c | ||
| 
						 | 
					7ee2273761 | ||
| 
						 | 
					86b8297c9d | ||
| 
						 | 
					17533112a1 | ||
| 
						 | 
					2cc1a338ec | ||
| 
						 | 
					81731247c7 | ||
| 
						 | 
					65c85aacf0 | ||
| 
						 | 
					dca23151a8 | ||
| 
						 | 
					25305760c5 | ||
| 
						 | 
					3ef2459ad0 | ||
| 
						 | 
					29a56f42a8 | ||
| 
						 | 
					3784348f91 | ||
| 
						 | 
					188ec03abd | ||
| 
						 | 
					f47cda7dc4 | ||
| 
						 | 
					c7269f032f | ||
| 
						 | 
					4581e611ca | 
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						@@ -4,6 +4,7 @@ build
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
# VSC
 | 
					# VSC
 | 
				
			||||||
bin
 | 
					bin
 | 
				
			||||||
 | 
					.vscode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# IntelliJ
 | 
					# IntelliJ
 | 
				
			||||||
*.iml
 | 
					*.iml
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										18
									
								
								Projekte/.run/MonopolyApp.run.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					<component name="ProjectRunConfigurationManager">
 | 
				
			||||||
 | 
					  <configuration default="false" name="MonopolyApp" type="Application" factoryName="Application" singleton="false"
 | 
				
			||||||
 | 
					                 nameIsGenerated="true">
 | 
				
			||||||
 | 
					    <option name="MAIN_CLASS_NAME" value="pp.monopoly.client.MonopolyApp"/>
 | 
				
			||||||
 | 
					    <module name="Projekte.monopoly.client.main"/>
 | 
				
			||||||
 | 
					    <option name="VM_PARAMETERS" value="-Djava.util.logging.config.file=logging.properties"/>
 | 
				
			||||||
 | 
					    <option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$"/>
 | 
				
			||||||
 | 
					    <extension name="coverage">
 | 
				
			||||||
 | 
					      <pattern>
 | 
				
			||||||
 | 
					        <option name="PATTERN" value="pp.monopoly.client.*"/>
 | 
				
			||||||
 | 
					        <option name="ENABLED" value="true"/>
 | 
				
			||||||
 | 
					      </pattern>
 | 
				
			||||||
 | 
					    </extension>
 | 
				
			||||||
 | 
					    <method v="2">
 | 
				
			||||||
 | 
					      <option name="Make" enabled="true"/>
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					  </configuration>
 | 
				
			||||||
 | 
					</component>
 | 
				
			||||||
							
								
								
									
										17
									
								
								Projekte/.run/MonopolyServer.run.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					<component name="ProjectRunConfigurationManager">
 | 
				
			||||||
 | 
					  <configuration default="false" name="MonopolyServer" type="Application" factoryName="Application"
 | 
				
			||||||
 | 
					                 nameIsGenerated="true">
 | 
				
			||||||
 | 
					    <option name="MAIN_CLASS_NAME" value="pp.monopoly.server.MonopolyServer"/>
 | 
				
			||||||
 | 
					    <module name="Projekte.monopoly.server.main"/>
 | 
				
			||||||
 | 
					    <option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$"/>
 | 
				
			||||||
 | 
					    <extension name="coverage">
 | 
				
			||||||
 | 
					      <pattern>
 | 
				
			||||||
 | 
					        <option name="PATTERN" value="pp.monopoly.server.*"/>
 | 
				
			||||||
 | 
					        <option name="ENABLED" value="true"/>
 | 
				
			||||||
 | 
					      </pattern>
 | 
				
			||||||
 | 
					    </extension>
 | 
				
			||||||
 | 
					    <method v="2">
 | 
				
			||||||
 | 
					      <option name="Make" enabled="true"/>
 | 
				
			||||||
 | 
					    </method>
 | 
				
			||||||
 | 
					  </configuration>
 | 
				
			||||||
 | 
					</component>
 | 
				
			||||||
@@ -7,18 +7,14 @@ description = 'Battleship Client'
 | 
				
			|||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
    implementation project(":jme-common")
 | 
					    implementation project(":jme-common")
 | 
				
			||||||
    implementation project(":battleship:model")
 | 
					    implementation project(":battleship:model")
 | 
				
			||||||
    implementation 'org.jmonkeyengine:jme3-core:3.6.0-stable'
 | 
					 | 
				
			||||||
    implementation 'org.jmonkeyengine:jme3-effects:3.6.0-stable'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    implementation libs.jme3.desktop
 | 
					    implementation libs.jme3.desktop
 | 
				
			||||||
 | 
					    implementation libs.jme3.effects
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    runtimeOnly libs.jme3.awt.dialogs
 | 
					    runtimeOnly libs.jme3.awt.dialogs
 | 
				
			||||||
    runtimeOnly libs.jme3.plugins
 | 
					    runtimeOnly libs.jme3.plugins
 | 
				
			||||||
    runtimeOnly libs.jme3.jogg
 | 
					    runtimeOnly libs.jme3.jogg
 | 
				
			||||||
    runtimeOnly libs.jme3.testdata
 | 
					    runtimeOnly libs.jme3.testdata
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
application {
 | 
					application {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,8 +22,8 @@ import com.simsilica.lemur.GuiGlobals;
 | 
				
			|||||||
import com.simsilica.lemur.style.BaseStyles;
 | 
					import com.simsilica.lemur.style.BaseStyles;
 | 
				
			||||||
import pp.battleship.client.gui.BattleAppState;
 | 
					import pp.battleship.client.gui.BattleAppState;
 | 
				
			||||||
import pp.battleship.client.gui.EditorAppState;
 | 
					import pp.battleship.client.gui.EditorAppState;
 | 
				
			||||||
 | 
					import pp.battleship.client.gui.GameMusic;
 | 
				
			||||||
import pp.battleship.client.gui.SeaAppState;
 | 
					import pp.battleship.client.gui.SeaAppState;
 | 
				
			||||||
import pp.battleship.client.GameMusic;
 | 
					 | 
				
			||||||
import pp.battleship.game.client.BattleshipClient;
 | 
					import pp.battleship.game.client.BattleshipClient;
 | 
				
			||||||
import pp.battleship.game.client.ClientGameLogic;
 | 
					import pp.battleship.game.client.ClientGameLogic;
 | 
				
			||||||
import pp.battleship.game.client.ServerConnection;
 | 
					import pp.battleship.game.client.ServerConnection;
 | 
				
			||||||
@@ -269,7 +269,6 @@ public class BattleshipApp extends SimpleApplication implements BattleshipClient
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        attachGameSound();
 | 
					        attachGameSound();
 | 
				
			||||||
        attachGameMusic();
 | 
					        attachGameMusic();
 | 
				
			||||||
 | 
					 | 
				
			||||||
        stateManager.attachAll(new EditorAppState(), new BattleAppState(), new SeaAppState());
 | 
					        stateManager.attachAll(new EditorAppState(), new BattleAppState(), new SeaAppState());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -284,12 +283,12 @@ public class BattleshipApp extends SimpleApplication implements BattleshipClient
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Attaches the music state and sets its initial enabled state.
 | 
					     * Attaches the background music state and sets its initial enabled state.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private void attachGameMusic() {
 | 
					    private void attachGameMusic() {
 | 
				
			||||||
        final GameMusic gameMusic = new GameMusic();
 | 
					        final GameMusic gameSound = new GameMusic();
 | 
				
			||||||
        gameMusic.setEnabled(GameMusic.enabledInPreferences());
 | 
					        gameSound.setEnabled(GameMusic.enabledInPreferences());
 | 
				
			||||||
        stateManager.attach(gameMusic);
 | 
					        stateManager.attach(gameSound);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,6 +34,7 @@ public class GameSound extends AbstractAppState implements GameEventListener {
 | 
				
			|||||||
    private AudioNode splashSound;
 | 
					    private AudioNode splashSound;
 | 
				
			||||||
    private AudioNode shipDestroyedSound;
 | 
					    private AudioNode shipDestroyedSound;
 | 
				
			||||||
    private AudioNode explosionSound;
 | 
					    private AudioNode explosionSound;
 | 
				
			||||||
 | 
					    private AudioNode shellFlyingSound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Checks if sound is enabled in the preferences.
 | 
					     * Checks if sound is enabled in the preferences.
 | 
				
			||||||
@@ -78,6 +79,7 @@ public class GameSound extends AbstractAppState implements GameEventListener {
 | 
				
			|||||||
        shipDestroyedSound = loadSound(app, "Sound/Effects/sunken.wav"); //NON-NLS
 | 
					        shipDestroyedSound = loadSound(app, "Sound/Effects/sunken.wav"); //NON-NLS
 | 
				
			||||||
        splashSound = loadSound(app, "Sound/Effects/splash.wav"); //NON-NLS
 | 
					        splashSound = loadSound(app, "Sound/Effects/splash.wav"); //NON-NLS
 | 
				
			||||||
        explosionSound = loadSound(app, "Sound/Effects/explosion.wav"); //NON-NLS
 | 
					        explosionSound = loadSound(app, "Sound/Effects/explosion.wav"); //NON-NLS
 | 
				
			||||||
 | 
					        shellFlyingSound = loadSound(app, "Sound/Effects/shell_flying.wav");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -124,12 +126,27 @@ public class GameSound extends AbstractAppState implements GameEventListener {
 | 
				
			|||||||
            shipDestroyedSound.playInstance();
 | 
					            shipDestroyedSound.playInstance();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the shell flying sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void shellFly() {
 | 
				
			||||||
 | 
					        if (isEnabled() && shellFlyingSound != null) {
 | 
				
			||||||
 | 
					            shellFlyingSound.playInstance();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handles a recieved {@code SoundEvent} and plays the according sound.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param event the Sound event to be processed
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void receivedEvent(SoundEvent event) {
 | 
					    public void receivedEvent(SoundEvent event) {
 | 
				
			||||||
        switch (event.sound()) {
 | 
					        switch (event.sound()) {
 | 
				
			||||||
            case EXPLOSION -> explosion();
 | 
					            case EXPLOSION -> explosion();
 | 
				
			||||||
            case SPLASH -> splash();
 | 
					            case SPLASH -> splash();
 | 
				
			||||||
            case DESTROYED_SHIP -> shipDestroyed();
 | 
					            case DESTROYED_SHIP -> shipDestroyed();
 | 
				
			||||||
 | 
					            case SHELL_FLYING -> shellFly();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,20 +7,21 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package pp.battleship.client;
 | 
					package pp.battleship.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.simsilica.lemur.Button;
 | 
					 | 
				
			||||||
import com.simsilica.lemur.Checkbox;
 | 
					 | 
				
			||||||
import com.simsilica.lemur.Label;
 | 
					 | 
				
			||||||
import com.simsilica.lemur.style.ElementId;
 | 
					 | 
				
			||||||
import pp.battleship.client.gui.VolumeControl;
 | 
					 | 
				
			||||||
import pp.dialog.Dialog;
 | 
					 | 
				
			||||||
import pp.dialog.StateCheckboxModel;
 | 
					 | 
				
			||||||
import pp.dialog.TextInputDialog;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.io.File;
 | 
					import java.io.File;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.util.prefs.Preferences;
 | 
					import java.util.prefs.Preferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Button;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Checkbox;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Label;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.style.ElementId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static pp.battleship.Resources.lookup;
 | 
					import static pp.battleship.Resources.lookup;
 | 
				
			||||||
 | 
					import pp.battleship.client.gui.GameMusic;
 | 
				
			||||||
 | 
					import pp.battleship.client.gui.VolumeSlider;
 | 
				
			||||||
 | 
					import pp.dialog.Dialog;
 | 
				
			||||||
 | 
					import pp.dialog.StateCheckboxModel;
 | 
				
			||||||
 | 
					import pp.dialog.TextInputDialog;
 | 
				
			||||||
import static pp.util.PreferencesUtils.getPreferences;
 | 
					import static pp.util.PreferencesUtils.getPreferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -34,35 +35,29 @@ class Menu extends Dialog {
 | 
				
			|||||||
    private final BattleshipApp app;
 | 
					    private final BattleshipApp app;
 | 
				
			||||||
    private final Button loadButton = new Button(lookup("menu.map.load"));
 | 
					    private final Button loadButton = new Button(lookup("menu.map.load"));
 | 
				
			||||||
    private final Button saveButton = new Button(lookup("menu.map.save"));
 | 
					    private final Button saveButton = new Button(lookup("menu.map.save"));
 | 
				
			||||||
    private final VolumeControl volumeSlider;
 | 
					    private final VolumeSlider slider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Constructs the Menu dialog for the Battleship application.
 | 
					     * Constructs the Menu dialog for the Battleship application.+
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param app the BattleshipApp instance
 | 
					     * @param app the BattleshipApp instance
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public Menu(BattleshipApp app) {
 | 
					    public Menu(BattleshipApp app) {
 | 
				
			||||||
        super(app.getDialogManager());
 | 
					        super(app.getDialogManager());
 | 
				
			||||||
        this.app = app;
 | 
					        this.app = app;
 | 
				
			||||||
 | 
					        slider = new VolumeSlider(app.getStateManager().getState(GameMusic.class));
 | 
				
			||||||
        addChild(new Label(lookup("battleship.name"), new ElementId("header")));//NON-NLS
 | 
					        addChild(new Label(lookup("battleship.name"), new ElementId("header"))); //NON-NLS
 | 
				
			||||||
 | 
					 | 
				
			||||||
        addChild(new Checkbox(lookup("menu.sound-enabled"), new StateCheckboxModel(app, GameSound.class)));
 | 
					        addChild(new Checkbox(lookup("menu.sound-enabled"), new StateCheckboxModel(app, GameSound.class)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        addChild(new Checkbox(lookup("menu.background-sound-enabled"), new StateCheckboxModel(app, GameMusic.class)));
 | 
					        addChild(new Checkbox(lookup("menu.background-sound-enabled"), new StateCheckboxModel(app, GameMusic.class)));
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        volumeSlider = new VolumeControl(app.getStateManager().getState(GameMusic.class));
 | 
					        addChild(slider);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        addChild(volumeSlider);
 | 
					        addChild(loadButton).addClickCommands(s -> ifTopDialog(this::loadDialog));
 | 
				
			||||||
 | 
					        addChild(saveButton).addClickCommands(s -> ifTopDialog(this::saveDialog));
 | 
				
			||||||
 | 
					        addChild(new Button(lookup("menu.return-to-game"))).addClickCommands(s -> ifTopDialog(this::close));
 | 
				
			||||||
 | 
					        addChild(new Button(lookup("menu.quit"))).addClickCommands(s -> ifTopDialog(app::closeApp));
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        addChild(loadButton)
 | 
					 | 
				
			||||||
                .addClickCommands(s -> ifTopDialog(this::loadDialog));
 | 
					 | 
				
			||||||
        addChild(saveButton)
 | 
					 | 
				
			||||||
                .addClickCommands(s -> ifTopDialog(this::saveDialog));
 | 
					 | 
				
			||||||
        addChild(new Button(lookup("menu.return-to-game")))
 | 
					 | 
				
			||||||
                .addClickCommands(s -> ifTopDialog(this::close));
 | 
					 | 
				
			||||||
        addChild(new Button(lookup("menu.quit")))
 | 
					 | 
				
			||||||
                .addClickCommands(s -> ifTopDialog(app::closeApp));
 | 
					 | 
				
			||||||
        update();
 | 
					        update();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,15 +70,11 @@ class Menu extends Dialog {
 | 
				
			|||||||
        saveButton.setEnabled(app.getGameLogic().maySaveMap());
 | 
					        saveButton.setEnabled(app.getGameLogic().maySaveMap());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Updates the position of the volume control slider.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void update(float delta) {
 | 
					    public void update(float delta) {
 | 
				
			||||||
        volumeSlider.update();
 | 
					        slider.update();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * As an escape action, this method closes the menu if it is the top dialog.
 | 
					     * As an escape action, this method closes the menu if it is the top dialog.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,12 +7,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package pp.battleship.client;
 | 
					package pp.battleship.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.simsilica.lemur.Checkbox;
 | 
					import com.simsilica.lemur.Button;
 | 
				
			||||||
import com.simsilica.lemur.Container;
 | 
					import com.simsilica.lemur.Container;
 | 
				
			||||||
import com.simsilica.lemur.Label;
 | 
					import com.simsilica.lemur.Label;
 | 
				
			||||||
import com.simsilica.lemur.TextField;
 | 
					import com.simsilica.lemur.TextField;
 | 
				
			||||||
import com.simsilica.lemur.component.SpringGridLayout;
 | 
					import com.simsilica.lemur.component.SpringGridLayout;
 | 
				
			||||||
import pp.battleship.client.server.BattleshipSelfhostServer;
 | 
					
 | 
				
			||||||
 | 
					import pp.battleship.server.BattleshipServer;
 | 
				
			||||||
import pp.dialog.Dialog;
 | 
					import pp.dialog.Dialog;
 | 
				
			||||||
import pp.dialog.DialogBuilder;
 | 
					import pp.dialog.DialogBuilder;
 | 
				
			||||||
import pp.dialog.SimpleDialog;
 | 
					import pp.dialog.SimpleDialog;
 | 
				
			||||||
@@ -35,7 +36,8 @@ class NetworkDialog extends SimpleDialog {
 | 
				
			|||||||
    private final NetworkSupport network;
 | 
					    private final NetworkSupport network;
 | 
				
			||||||
    private final TextField host = new TextField(LOCALHOST);
 | 
					    private final TextField host = new TextField(LOCALHOST);
 | 
				
			||||||
    private final TextField port = new TextField(DEFAULT_PORT);
 | 
					    private final TextField port = new TextField(DEFAULT_PORT);
 | 
				
			||||||
    private static final Checkbox HOST  = new Checkbox(lookup("server.host"));
 | 
					    // private final Button serverButton = new Button(lookup("client.server-star"));
 | 
				
			||||||
 | 
					    private final Button serverButton = new Button(lookup("client.server-start"));
 | 
				
			||||||
    private String hostname;
 | 
					    private String hostname;
 | 
				
			||||||
    private int portNumber;
 | 
					    private int portNumber;
 | 
				
			||||||
    private Future<Object> connectionFuture;
 | 
					    private Future<Object> connectionFuture;
 | 
				
			||||||
@@ -59,7 +61,6 @@ class NetworkDialog extends SimpleDialog {
 | 
				
			|||||||
        input.addChild(host, 1);
 | 
					        input.addChild(host, 1);
 | 
				
			||||||
        input.addChild(new Label(lookup("port.number") + ":  "));
 | 
					        input.addChild(new Label(lookup("port.number") + ":  "));
 | 
				
			||||||
        input.addChild(port, 1);
 | 
					        input.addChild(port, 1);
 | 
				
			||||||
        input.addChild(HOST).addClickCommands(s -> ifTopDialog(this::startClientServer));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        DialogBuilder.simple(app.getDialogManager())
 | 
					        DialogBuilder.simple(app.getDialogManager())
 | 
				
			||||||
                     .setTitle(lookup("server.dialog"))
 | 
					                     .setTitle(lookup("server.dialog"))
 | 
				
			||||||
@@ -69,6 +70,9 @@ class NetworkDialog extends SimpleDialog {
 | 
				
			|||||||
                     .setOkClose(false)
 | 
					                     .setOkClose(false)
 | 
				
			||||||
                     .setNoClose(false)
 | 
					                     .setNoClose(false)
 | 
				
			||||||
                     .build(this);
 | 
					                     .build(this);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        //Add the button to start the sever
 | 
				
			||||||
 | 
					        addChild(serverButton).addClickCommands(s -> ifTopDialog(this::startServerInThread));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
@@ -156,15 +160,15 @@ class NetworkDialog extends SimpleDialog {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Starts the server of the client in a new thread and catches connectivity issues.
 | 
					     * Starts the server in a separate thread.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private void startClientServer() {
 | 
					    private void startServerInThread() {
 | 
				
			||||||
        HOST.setEnabled(false);
 | 
					        serverButton.setEnabled(false);
 | 
				
			||||||
        Thread serverThread = new Thread(() -> {
 | 
					        Thread serverThread = new Thread(() -> {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                BattleshipSelfhostServer.main(null);
 | 
					                BattleshipServer.main(null);
 | 
				
			||||||
            } catch (Exception e) {
 | 
					            } catch (Exception e) {
 | 
				
			||||||
                HOST.setEnabled(true);
 | 
					                serverButton.setEnabled(true);
 | 
				
			||||||
                LOGGER.log(Level.ERROR, "Server could not be started", e);
 | 
					                LOGGER.log(Level.ERROR, "Server could not be started", e);
 | 
				
			||||||
                network.getApp().errorDialog("Could not start server: " + e.getMessage());
 | 
					                network.getApp().errorDialog("Could not start server: " + e.getMessage());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -172,4 +176,3 @@ class NetworkDialog extends SimpleDialog {
 | 
				
			|||||||
        serverThread.start();
 | 
					        serverThread.start();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,10 @@
 | 
				
			|||||||
package pp.battleship.client;
 | 
					package pp.battleship.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static pp.util.PreferencesUtils.getPreferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.lang.System.Logger;
 | 
				
			||||||
 | 
					import java.lang.System.Logger.Level;
 | 
				
			||||||
 | 
					import java.util.prefs.Preferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.jme3.app.Application;
 | 
					import com.jme3.app.Application;
 | 
				
			||||||
import com.jme3.app.state.AbstractAppState;
 | 
					import com.jme3.app.state.AbstractAppState;
 | 
				
			||||||
@@ -9,25 +14,16 @@ import com.jme3.asset.AssetNotFoundException;
 | 
				
			|||||||
import com.jme3.audio.AudioData;
 | 
					import com.jme3.audio.AudioData;
 | 
				
			||||||
import com.jme3.audio.AudioNode;
 | 
					import com.jme3.audio.AudioNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.lang.System.Logger;
 | 
					 | 
				
			||||||
import java.lang.System.Logger.Level;
 | 
					 | 
				
			||||||
import java.util.prefs.Preferences;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import static pp.util.PreferencesUtils.getPreferences;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * An application state that plays music.
 | 
					 * Handles the background music beeing played. Is able to start and stop the music. Set the Volume of the Audio.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class GameMusic extends AbstractAppState {
 | 
					public class GameMusic extends AbstractAppState{
 | 
				
			||||||
    private static final Logger LOGGER = System.getLogger(GameMusic.class.getName());
 | 
					    private static final Logger LOGGER = System.getLogger(GameMusic.class.getName());
 | 
				
			||||||
    private static final Preferences PREFERENCES = getPreferences(GameMusic.class);
 | 
					    private static final Preferences PREFERENCES = getPreferences(GameMusic.class);
 | 
				
			||||||
    private static final String ENABLED_PREF = "enabled"; //NON-NLS
 | 
					    private static final String ENABLED_PREF = "enabled"; //NON-NLS
 | 
				
			||||||
    private static final String VOLUME_PREF = "volume"; //NON-NLS
 | 
					    private static final String VOLUME_PREF = "volume"; //NON-NLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private AudioNode battleMusic;
 | 
					    private AudioNode music;
 | 
				
			||||||
    private AudioNode menuMusicModern;
 | 
					 | 
				
			||||||
    private AudioNode menuMusicTraditional;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Checks if sound is enabled in the preferences.
 | 
					     * Checks if sound is enabled in the preferences.
 | 
				
			||||||
@@ -37,47 +33,18 @@ public class GameMusic extends AbstractAppState {
 | 
				
			|||||||
    public static boolean enabledInPreferences() {
 | 
					    public static boolean enabledInPreferences() {
 | 
				
			||||||
        return PREFERENCES.getBoolean(ENABLED_PREF, true);
 | 
					        return PREFERENCES.getBoolean(ENABLED_PREF, true);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Checks enabled volume in preferences.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return {@code float} if a volumePreference is set, the volume is set to the Value in  PREFERENCES,
 | 
					 | 
				
			||||||
     *         {@code 0.25f} if no volumePreference is set in  PREFERENCES, the Volume is set to a default of 0.25f
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static float volumePreference() {
 | 
					        /**
 | 
				
			||||||
        return PREFERENCES.getFloat(VOLUME_PREF, 0.25f);
 | 
					     * Checks if sound is enabled in the preferences.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return float to which the volume is set
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static float volumeInPreferences() {
 | 
				
			||||||
 | 
					        return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Toggles the game sound on or off.
 | 
					     * Initializes the sound effects for the game.
 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public void toggleSound() {
 | 
					 | 
				
			||||||
        setEnabled(!isEnabled());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Sets the enabled state of this AppState.
 | 
					 | 
				
			||||||
     * Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)}
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param enabled {@code true} to enable the AppState, {@code false} to disable it.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void setEnabled(boolean enabled) {
 | 
					 | 
				
			||||||
        if (isEnabled() == enabled) return;
 | 
					 | 
				
			||||||
         else if (!isEnabled() && enabled) {
 | 
					 | 
				
			||||||
             if (menuMusicModern != null) menuMusicModern.play();
 | 
					 | 
				
			||||||
        } else if (isEnabled() && !enabled) {
 | 
					 | 
				
			||||||
             if (menuMusicModern != null) menuMusicModern.stop();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        super.setEnabled(enabled);
 | 
					 | 
				
			||||||
        LOGGER.log(Level.INFO, "Music enabled: {0}", enabled); //NON-NLS
 | 
					 | 
				
			||||||
        PREFERENCES.putBoolean(ENABLED_PREF, enabled);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Initializes the music.
 | 
					 | 
				
			||||||
     * Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
 | 
					     * Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param stateManager The state manager
 | 
					     * @param stateManager The state manager
 | 
				
			||||||
@@ -86,17 +53,16 @@ public class GameMusic extends AbstractAppState {
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void initialize(AppStateManager stateManager, Application app) {
 | 
					    public void initialize(AppStateManager stateManager, Application app) {
 | 
				
			||||||
        super.initialize(stateManager, app);
 | 
					        super.initialize(stateManager, app);
 | 
				
			||||||
        menuMusicModern =loadSound(app, "Sound/BackgroundMusic/menu-music-modern.ogg");
 | 
					        music = loadSound(app, "Sound/background.ogg");
 | 
				
			||||||
        setVolume(volumePreference());
 | 
					        setVolume(volumeInPreferences());
 | 
				
			||||||
        menuMusicModern.setLooping(true);
 | 
					        music.setLooping(true);
 | 
				
			||||||
        if (isEnabled() && menuMusicModern != null) {
 | 
					        if (isEnabled() && music != null) {
 | 
				
			||||||
            menuMusicModern.play();
 | 
					            music.play();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Loads the music from the specified file.
 | 
					     * Loads a sound from the specified file.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param app  The application
 | 
					     * @param app  The application
 | 
				
			||||||
     * @param name The name of the sound file.
 | 
					     * @param name The name of the sound file.
 | 
				
			||||||
@@ -116,14 +82,41 @@ public class GameMusic extends AbstractAppState {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Sets the vol param to the level set in PREFERENCES
 | 
					     * Sets the enabled state of this AppState.
 | 
				
			||||||
     *
 | 
					     * Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)}
 | 
				
			||||||
     * @param vol  Volume level of the music as indicated by the Volume control Slider
 | 
					 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 | 
					     * @param enabled {@code true} to enable the AppState, {@code false} to disable it.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public void setVolume(float vol){
 | 
					    @Override
 | 
				
			||||||
        menuMusicModern.setVolume(vol);
 | 
					    public void setEnabled(boolean enabled) {
 | 
				
			||||||
        PREFERENCES.putFloat(VOLUME_PREF, vol);
 | 
					        if (isEnabled() == enabled) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (music != null) {
 | 
				
			||||||
 | 
					            if (enabled) {
 | 
				
			||||||
 | 
					                music.play();
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                music.stop();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        super.setEnabled(enabled);
 | 
				
			||||||
 | 
					        LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); //NON-NLS
 | 
				
			||||||
 | 
					        PREFERENCES.putBoolean(ENABLED_PREF, enabled);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Toggles the game sound on or off.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void toggleSound() {
 | 
				
			||||||
 | 
					        setEnabled(!isEnabled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the volume of music
 | 
				
			||||||
 | 
					     * @param vol the volume to which the music should be set
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setVolume(float vol){
 | 
				
			||||||
 | 
					        music.setVolume(vol);
 | 
				
			||||||
 | 
					        PREFERENCES.putFloat(VOLUME_PREF, vol);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -15,6 +15,7 @@ import com.jme3.scene.Geometry;
 | 
				
			|||||||
import com.jme3.scene.Node;
 | 
					import com.jme3.scene.Node;
 | 
				
			||||||
import com.jme3.scene.Spatial;
 | 
					import com.jme3.scene.Spatial;
 | 
				
			||||||
import com.jme3.scene.shape.Sphere;
 | 
					import com.jme3.scene.shape.Sphere;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.battleship.model.Battleship;
 | 
					import pp.battleship.model.Battleship;
 | 
				
			||||||
import pp.battleship.model.Shell;
 | 
					import pp.battleship.model.Shell;
 | 
				
			||||||
import pp.battleship.model.Shot;
 | 
					import pp.battleship.model.Shot;
 | 
				
			||||||
@@ -22,6 +23,7 @@ import pp.util.Position;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import static com.jme3.material.Materials.UNSHADED;
 | 
					import static com.jme3.material.Materials.UNSHADED;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Synchronizes the visual representation of the ship map with the game model.
 | 
					 * Synchronizes the visual representation of the ship map with the game model.
 | 
				
			||||||
 * It handles the rendering of ships and shots on the map view, updating the view
 | 
					 * It handles the rendering of ships and shots on the map view, updating the view
 | 
				
			||||||
@@ -130,7 +132,7 @@ class MapViewSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
        return view.getApp().getDraw().makeFatLine(x1, y1, x2, y2, SHIP_DEPTH, color, SHIP_LINE_WIDTH);
 | 
					        return view.getApp().getDraw().makeFatLine(x1, y1, x2, y2, SHIP_DEPTH, color, SHIP_LINE_WIDTH);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					        /**
 | 
				
			||||||
     * Creates and returns a Spatial representation of the given {@code Shell} object
 | 
					     * Creates and returns a Spatial representation of the given {@code Shell} object
 | 
				
			||||||
     * for 2D visualization in the game. The shell is represented as a circle.
 | 
					     * for 2D visualization in the game. The shell is represented as a circle.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -149,5 +151,4 @@ class MapViewSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
        ellipse.addControl(new Shell2DControl(view, shell));
 | 
					        ellipse.addControl(new Shell2DControl(view, shell));
 | 
				
			||||||
        return ellipse;
 | 
					        return ellipse;
 | 
				
			||||||
    }      
 | 
					    }      
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,285 @@
 | 
				
			|||||||
 | 
					package pp.battleship.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.effect.ParticleEmitter;
 | 
				
			||||||
 | 
					import com.jme3.effect.ParticleMesh.Type;
 | 
				
			||||||
 | 
					import com.jme3.effect.shapes.EmitterSphereShape;
 | 
				
			||||||
 | 
					import com.jme3.material.Material;
 | 
				
			||||||
 | 
					import com.jme3.math.ColorRGBA;
 | 
				
			||||||
 | 
					import com.jme3.math.FastMath;
 | 
				
			||||||
 | 
					import com.jme3.math.Vector3f;
 | 
				
			||||||
 | 
					import pp.battleship.client.BattleshipApp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Factory class responsible for creating particle effects used in the game.
 | 
				
			||||||
 | 
					 * This centralizes the creation of various types of particle emitters.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ParticleEffectFactory {
 | 
				
			||||||
 | 
					    private static final int COUNT_FACTOR = 1;
 | 
				
			||||||
 | 
					    private static final float COUNT_FACTOR_F = 1f;
 | 
				
			||||||
 | 
					    private static final boolean POINT_SPRITE = true;
 | 
				
			||||||
 | 
					    private static final Type EMITTER_TYPE = POINT_SPRITE ? Type.Point : Type.Triangle;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private final BattleshipApp app;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ParticleEffectFactory(BattleshipApp app) {
 | 
				
			||||||
 | 
					        this.app = app;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a flame particle emitter.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return a configured flame particle emitter
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ParticleEmitter createFlame() {
 | 
				
			||||||
 | 
					        ParticleEmitter flame = new ParticleEmitter("Flame", EMITTER_TYPE, 32 * COUNT_FACTOR);
 | 
				
			||||||
 | 
					        flame.setSelectRandomImage(true);
 | 
				
			||||||
 | 
					        flame.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (1f / COUNT_FACTOR_F)));
 | 
				
			||||||
 | 
					        flame.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
 | 
				
			||||||
 | 
					        flame.setStartSize(0.1f);
 | 
				
			||||||
 | 
					        flame.setEndSize(0.5f);
 | 
				
			||||||
 | 
					        flame.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
 | 
				
			||||||
 | 
					        flame.setParticlesPerSec(0);
 | 
				
			||||||
 | 
					        flame.setGravity(0, -5, 0);
 | 
				
			||||||
 | 
					        flame.setLowLife(.4f);
 | 
				
			||||||
 | 
					        flame.setHighLife(.5f);
 | 
				
			||||||
 | 
					        flame.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 7, 0));
 | 
				
			||||||
 | 
					        flame.getParticleInfluencer().setVelocityVariation(1f);
 | 
				
			||||||
 | 
					        flame.setImagesX(2);
 | 
				
			||||||
 | 
					        flame.setImagesY(2);
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
				
			||||||
 | 
					        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flame.png"));
 | 
				
			||||||
 | 
					        mat.setBoolean("PointSprite", POINT_SPRITE);
 | 
				
			||||||
 | 
					        flame.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return flame;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a flash particle emitter.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return a configured flash particle emitter
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ParticleEmitter createFlash() {
 | 
				
			||||||
 | 
					        ParticleEmitter flash = new ParticleEmitter("Flash", EMITTER_TYPE, 24 * COUNT_FACTOR);
 | 
				
			||||||
 | 
					        flash.setSelectRandomImage(true);
 | 
				
			||||||
 | 
					        flash.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1f / COUNT_FACTOR_F));
 | 
				
			||||||
 | 
					        flash.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
 | 
				
			||||||
 | 
					        flash.setStartSize(.1f);
 | 
				
			||||||
 | 
					        flash.setEndSize(0.5f);
 | 
				
			||||||
 | 
					        flash.setShape(new EmitterSphereShape(Vector3f.ZERO, .05f));
 | 
				
			||||||
 | 
					        flash.setParticlesPerSec(0);
 | 
				
			||||||
 | 
					        flash.setGravity(0, 0, 0);
 | 
				
			||||||
 | 
					        flash.setLowLife(.2f);
 | 
				
			||||||
 | 
					        flash.setHighLife(.2f);
 | 
				
			||||||
 | 
					        flash.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 5f, 0));
 | 
				
			||||||
 | 
					        flash.getParticleInfluencer().setVelocityVariation(1);
 | 
				
			||||||
 | 
					        flash.setImagesX(2);
 | 
				
			||||||
 | 
					        flash.setImagesY(2);
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
				
			||||||
 | 
					        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flash.png"));
 | 
				
			||||||
 | 
					        mat.setBoolean("PointSprite", POINT_SPRITE);
 | 
				
			||||||
 | 
					        flash.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return flash;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a round spark particle emitter.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return a configured round spark particle emitter
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ParticleEmitter createRoundSpark() {
 | 
				
			||||||
 | 
					        ParticleEmitter roundSpark = new ParticleEmitter("RoundSpark", EMITTER_TYPE, 20 * COUNT_FACTOR);
 | 
				
			||||||
 | 
					        roundSpark.setStartColor(new ColorRGBA(1f, 0.29f, 0.34f, (float) (1.0 / COUNT_FACTOR_F)));
 | 
				
			||||||
 | 
					        roundSpark.setEndColor(new ColorRGBA(0, 0, 0, 0.5f / COUNT_FACTOR_F));
 | 
				
			||||||
 | 
					        roundSpark.setStartSize(0.2f);
 | 
				
			||||||
 | 
					        roundSpark.setEndSize(0.8f);
 | 
				
			||||||
 | 
					        roundSpark.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
 | 
				
			||||||
 | 
					        roundSpark.setParticlesPerSec(0);
 | 
				
			||||||
 | 
					        roundSpark.setGravity(0, -.5f, 0);
 | 
				
			||||||
 | 
					        roundSpark.setLowLife(1.8f);
 | 
				
			||||||
 | 
					        roundSpark.setHighLife(2f);
 | 
				
			||||||
 | 
					        roundSpark.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 3, 0));
 | 
				
			||||||
 | 
					        roundSpark.getParticleInfluencer().setVelocityVariation(.5f);
 | 
				
			||||||
 | 
					        roundSpark.setImagesX(1);
 | 
				
			||||||
 | 
					        roundSpark.setImagesY(1);
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
				
			||||||
 | 
					        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/roundspark.png"));
 | 
				
			||||||
 | 
					        mat.setBoolean("PointSprite", POINT_SPRITE);
 | 
				
			||||||
 | 
					        roundSpark.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return roundSpark;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a spark particle emitter.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return a configured spark particle emitter
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ParticleEmitter createSpark() {
 | 
				
			||||||
 | 
					        ParticleEmitter spark = new ParticleEmitter("Spark", Type.Triangle, 30 * COUNT_FACTOR);
 | 
				
			||||||
 | 
					        spark.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F));
 | 
				
			||||||
 | 
					        spark.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
 | 
				
			||||||
 | 
					        spark.setStartSize(.5f);
 | 
				
			||||||
 | 
					        spark.setEndSize(.5f);
 | 
				
			||||||
 | 
					        spark.setFacingVelocity(true);
 | 
				
			||||||
 | 
					        spark.setParticlesPerSec(0);
 | 
				
			||||||
 | 
					        spark.setGravity(0, 5, 0);
 | 
				
			||||||
 | 
					        spark.setLowLife(1.1f);
 | 
				
			||||||
 | 
					        spark.setHighLife(1.5f);
 | 
				
			||||||
 | 
					        spark.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 20, 0));
 | 
				
			||||||
 | 
					        spark.getParticleInfluencer().setVelocityVariation(1);
 | 
				
			||||||
 | 
					        spark.setImagesX(1);
 | 
				
			||||||
 | 
					        spark.setImagesY(1);
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
				
			||||||
 | 
					        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/spark.png"));
 | 
				
			||||||
 | 
					        spark.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return spark;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a smoke trail particle emitter.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return a configured smoke trail particle emitter
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ParticleEmitter createSmokeTrail() {
 | 
				
			||||||
 | 
					        ParticleEmitter smokeTrail = new ParticleEmitter("SmokeTrail", Type.Triangle, 22 * COUNT_FACTOR);
 | 
				
			||||||
 | 
					        smokeTrail.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F));
 | 
				
			||||||
 | 
					        smokeTrail.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
 | 
				
			||||||
 | 
					        smokeTrail.setStartSize(.2f);
 | 
				
			||||||
 | 
					        smokeTrail.setEndSize(1f);
 | 
				
			||||||
 | 
					        smokeTrail.setFacingVelocity(true);
 | 
				
			||||||
 | 
					        smokeTrail.setParticlesPerSec(0);
 | 
				
			||||||
 | 
					        smokeTrail.setGravity(0, 1, 0);
 | 
				
			||||||
 | 
					        smokeTrail.setLowLife(.4f);
 | 
				
			||||||
 | 
					        smokeTrail.setHighLife(.5f);
 | 
				
			||||||
 | 
					        smokeTrail.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 12, 0));
 | 
				
			||||||
 | 
					        smokeTrail.getParticleInfluencer().setVelocityVariation(1);
 | 
				
			||||||
 | 
					        smokeTrail.setImagesX(1);
 | 
				
			||||||
 | 
					        smokeTrail.setImagesY(3);
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
				
			||||||
 | 
					        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/smoketrail.png"));
 | 
				
			||||||
 | 
					        smokeTrail.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return smokeTrail;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a debris particle emitter.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return a configured debris particle emitter
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ParticleEmitter createDebris() {
 | 
				
			||||||
 | 
					        ParticleEmitter debris = new ParticleEmitter("Debris", Type.Triangle, 15 * COUNT_FACTOR);
 | 
				
			||||||
 | 
					        debris.setSelectRandomImage(true);
 | 
				
			||||||
 | 
					        debris.setRandomAngle(true);
 | 
				
			||||||
 | 
					        debris.setRotateSpeed(FastMath.TWO_PI * 4);
 | 
				
			||||||
 | 
					        debris.setStartColor(new ColorRGBA(1f, 0.59f, 0.28f, 1.0f / COUNT_FACTOR_F));
 | 
				
			||||||
 | 
					        debris.setEndColor(new ColorRGBA(.5f, 0.5f, 0.5f, 0f));
 | 
				
			||||||
 | 
					        debris.setStartSize(.10f);
 | 
				
			||||||
 | 
					        debris.setEndSize(.15f);
 | 
				
			||||||
 | 
					        debris.setParticlesPerSec(0);
 | 
				
			||||||
 | 
					        debris.setGravity(0, 12f, 0);
 | 
				
			||||||
 | 
					        debris.setLowLife(1.4f);
 | 
				
			||||||
 | 
					        debris.setHighLife(1.5f);
 | 
				
			||||||
 | 
					        debris.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 15, 0));
 | 
				
			||||||
 | 
					        debris.getParticleInfluencer().setVelocityVariation(.60f);
 | 
				
			||||||
 | 
					        debris.setImagesX(3);
 | 
				
			||||||
 | 
					        debris.setImagesY(3);
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
				
			||||||
 | 
					        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/Debris.png"));
 | 
				
			||||||
 | 
					        debris.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return debris;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a shockwave particle emitter.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return a configured shockwave particle emitter
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ParticleEmitter createShockwave() {
 | 
				
			||||||
 | 
					        ParticleEmitter shockwave = new ParticleEmitter("Shockwave", Type.Triangle, 1 * COUNT_FACTOR);
 | 
				
			||||||
 | 
					        shockwave.setFaceNormal(Vector3f.UNIT_Y);
 | 
				
			||||||
 | 
					        shockwave.setStartColor(new ColorRGBA(.48f, 0.17f, 0.01f, .8f / COUNT_FACTOR_F));
 | 
				
			||||||
 | 
					        shockwave.setEndColor(new ColorRGBA(.48f, 0.17f, 0.01f, 0f));
 | 
				
			||||||
 | 
					        shockwave.setStartSize(0f);
 | 
				
			||||||
 | 
					        shockwave.setEndSize(3f);
 | 
				
			||||||
 | 
					        shockwave.setParticlesPerSec(0);
 | 
				
			||||||
 | 
					        shockwave.setGravity(0, 0, 0);
 | 
				
			||||||
 | 
					        shockwave.setLowLife(0.5f);
 | 
				
			||||||
 | 
					        shockwave.setHighLife(0.5f);
 | 
				
			||||||
 | 
					        shockwave.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 0, 0));
 | 
				
			||||||
 | 
					        shockwave.getParticleInfluencer().setVelocityVariation(0f);
 | 
				
			||||||
 | 
					        shockwave.setImagesX(1);
 | 
				
			||||||
 | 
					        shockwave.setImagesY(1);
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
				
			||||||
 | 
					        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/shockwave.png"));
 | 
				
			||||||
 | 
					        shockwave.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return shockwave;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a moving smoke emitter.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return a configured smoke emitter
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    ParticleEmitter createMovingSmokeEmitter() {
 | 
				
			||||||
 | 
					        ParticleEmitter smokeEmitter = new ParticleEmitter("SmokeEmitter", Type.Triangle, 300);
 | 
				
			||||||
 | 
					        smokeEmitter.setGravity(0, 0, 0);
 | 
				
			||||||
 | 
					        smokeEmitter.getParticleInfluencer().setVelocityVariation(1);
 | 
				
			||||||
 | 
					        smokeEmitter.setLowLife(1);
 | 
				
			||||||
 | 
					        smokeEmitter.setHighLife(1);
 | 
				
			||||||
 | 
					        smokeEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, .5f, 0));
 | 
				
			||||||
 | 
					        smokeEmitter.setImagesX(15); // Assuming the smoke texture is a sprite sheet with 15 frames
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
				
			||||||
 | 
					        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Smoke/Smoke.png"));
 | 
				
			||||||
 | 
					        smokeEmitter.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return smokeEmitter;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a one-time water splash particle emitter.
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @return a configured one-time water splash particle emitter
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ParticleEmitter createWaterSplash() {
 | 
				
			||||||
 | 
					        // Create a new particle emitter for the splash effect
 | 
				
			||||||
 | 
					        ParticleEmitter waterSplash = new ParticleEmitter("WaterSplash", Type.Triangle, 30);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Set the shape of the emitter, making particles emit from a point or small area
 | 
				
			||||||
 | 
					        waterSplash.setShape(new EmitterSphereShape(Vector3f.ZERO, 0.2f));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Start and end colors for water (blue, fading out)
 | 
				
			||||||
 | 
					        waterSplash.setStartColor(new ColorRGBA(0.4f, 0.4f, 1f, 1f));  // Light blue at start
 | 
				
			||||||
 | 
					        waterSplash.setEndColor(new ColorRGBA(0.4f, 0.4f, 1f, 0f));    // Transparent at the end
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Particle size: small at start, larger before fading out
 | 
				
			||||||
 | 
					        waterSplash.setStartSize(0.1f);
 | 
				
			||||||
 | 
					        waterSplash.setEndSize(0.3f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Particle lifespan (how long particles live)
 | 
				
			||||||
 | 
					        waterSplash.setLowLife(0.5f);
 | 
				
			||||||
 | 
					        waterSplash.setHighLife(1f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Gravity: Pull the water particles downwards
 | 
				
			||||||
 | 
					        waterSplash.setGravity(0, -9.81f, 0);  // Earth's gravity simulation
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Velocity: Give particles an initial burst upward (simulates splash)
 | 
				
			||||||
 | 
					        waterSplash.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 3, 0));
 | 
				
			||||||
 | 
					        waterSplash.getParticleInfluencer().setVelocityVariation(0.6f);  // Add randomness to splash
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Set how many particles are emitted per second (0 to emit all particles at once)
 | 
				
			||||||
 | 
					        waterSplash.setParticlesPerSec(0);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Load a texture for the water splash (assuming a texture exists at this path)
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
				
			||||||
 | 
					        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Splash/splash.png"));
 | 
				
			||||||
 | 
					        waterSplash.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return waterSplash;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -8,13 +8,9 @@
 | 
				
			|||||||
package pp.battleship.client.gui;
 | 
					package pp.battleship.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.jme3.effect.ParticleEmitter;
 | 
					import com.jme3.effect.ParticleEmitter;
 | 
				
			||||||
import com.jme3.effect.ParticleMesh.Type;
 | 
					 | 
				
			||||||
import com.jme3.effect.shapes.EmitterSphereShape;
 | 
					 | 
				
			||||||
import com.jme3.material.Material;
 | 
					import com.jme3.material.Material;
 | 
				
			||||||
import com.jme3.material.RenderState.BlendMode;
 | 
					import com.jme3.material.RenderState.BlendMode;
 | 
				
			||||||
import com.jme3.math.ColorRGBA;
 | 
					import com.jme3.math.ColorRGBA;
 | 
				
			||||||
import com.jme3.math.FastMath;
 | 
					 | 
				
			||||||
import com.jme3.math.Vector3f;
 | 
					 | 
				
			||||||
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
 | 
					import com.jme3.renderer.queue.RenderQueue.ShadowMode;
 | 
				
			||||||
import com.jme3.scene.Geometry;
 | 
					import com.jme3.scene.Geometry;
 | 
				
			||||||
import com.jme3.scene.Node;
 | 
					import com.jme3.scene.Node;
 | 
				
			||||||
@@ -34,16 +30,16 @@ import static pp.util.FloatMath.PI;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * The {@code SeaSynchronizer} class is responsible for synchronizing the graphical
 | 
					 * The {@code SeaSynchronizer} class is responsible for synchronizing the graphical
 | 
				
			||||||
 * representation of the ships and shots on the sea map with the underlying  model.
 | 
					 * representation of the ships and shots on the sea map with the underlying data model.
 | 
				
			||||||
 * It extends the {@link ShipMapSynchronizer} to provide specific synchronization
 | 
					 * It extends the {@link ShipMapSynchronizer} to provide specific synchronization
 | 
				
			||||||
 * logic for the sea map.
 | 
					 * logic for the sea map.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
class SeaSynchronizer extends ShipMapSynchronizer {
 | 
					class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			||||||
    private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
 | 
					    private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
 | 
				
			||||||
    private static final String KING_GEORGE_V_MODEL = "Models/KingGeorgeV/KingGeorgeV.j3o";//NON-NLS
 | 
					    private static final String KING_GEORGE_V_MODEL = "Models/KingGeorgeV/KingGeorgeV.j3o"; //NON-NLS
 | 
				
			||||||
    private static final String SMALL_BOAT_MODEL = "Models/BoatSmall/12219_boat_v2_L2.j3o";
 | 
					    private static final String BOAT_SMALL_MODEL = "Models/BoatSmall/12219_boat_v2_L2.j3o"; //NON-NLS
 | 
				
			||||||
    private static final String BATTLE_MODEL = "Models/Battle/Battle.j3o"; //NON-NLS
 | 
					 | 
				
			||||||
    private static final String CV_MODEL = "Models/CV/CV.j3o"; //NON-NLS
 | 
					    private static final String CV_MODEL = "Models/CV/CV.j3o"; //NON-NLS
 | 
				
			||||||
 | 
					    private static final String BATTLE_MODEL = "Models/Battle/Battle.j3o"; //NON-NLS
 | 
				
			||||||
    private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md";
 | 
					    private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md";
 | 
				
			||||||
    private static final String COLOR = "Color"; //NON-NLS
 | 
					    private static final String COLOR = "Color"; //NON-NLS
 | 
				
			||||||
    private static final String SHIP = "ship"; //NON-NLS
 | 
					    private static final String SHIP = "ship"; //NON-NLS
 | 
				
			||||||
@@ -54,273 +50,7 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private final ShipMap map;
 | 
					    private final ShipMap map;
 | 
				
			||||||
    private final BattleshipApp app;
 | 
					    private final BattleshipApp app;
 | 
				
			||||||
    private Shell shell;
 | 
					    private final ParticleEffectFactory particleFactory;
 | 
				
			||||||
    private Spatial shellModel;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private ParticleEmitter flame, flash, spark, roundspark, smoketrail, debris,
 | 
					 | 
				
			||||||
            shockwave;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final int COUNT_FACTOR = 1;
 | 
					 | 
				
			||||||
    private static final float COUNT_FACTOR_F = 1f;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private static final boolean POINT_SPRITE = true;
 | 
					 | 
				
			||||||
    private static final Type EMITTER_TYPE = POINT_SPRITE ? Type.Point : Type.Triangle;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates a Flame texture with indefinite Lifetime
 | 
					 | 
				
			||||||
     * @return flame texture
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private ParticleEmitter createFlame(){
 | 
					 | 
				
			||||||
        flame = new ParticleEmitter("Flame", EMITTER_TYPE, 32 * COUNT_FACTOR);
 | 
					 | 
				
			||||||
        flame.setSelectRandomImage(true);
 | 
					 | 
				
			||||||
        flame.setStartColor(new ColorRGBA(1f, 0.4f, 0.05f, (1f / COUNT_FACTOR_F)));
 | 
					 | 
				
			||||||
        flame.setEndColor(new ColorRGBA(.4f, .22f, .12f, 0f));
 | 
					 | 
				
			||||||
        flame.setStartSize(0.1f);
 | 
					 | 
				
			||||||
        flame.setEndSize(0.5f);
 | 
					 | 
				
			||||||
        flame.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
 | 
					 | 
				
			||||||
        flame.setParticlesPerSec(0);
 | 
					 | 
				
			||||||
        flame.setGravity(0, -5, 0);
 | 
					 | 
				
			||||||
        flame.setLowLife(.4f);
 | 
					 | 
				
			||||||
        flame.setHighLife(.5f);
 | 
					 | 
				
			||||||
        flame.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 7, 0));
 | 
					 | 
				
			||||||
        flame.getParticleInfluencer().setVelocityVariation(1f);
 | 
					 | 
				
			||||||
        flame.setImagesX(2);
 | 
					 | 
				
			||||||
        flame.setImagesY(2);
 | 
					 | 
				
			||||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
					 | 
				
			||||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flame.png"));
 | 
					 | 
				
			||||||
        mat.setBoolean("PointSprite", POINT_SPRITE);
 | 
					 | 
				
			||||||
        flame.setMaterial(mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return flame;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates a Flash texture
 | 
					 | 
				
			||||||
     * @return flash texture
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private ParticleEmitter createFlash(){
 | 
					 | 
				
			||||||
        flash = new ParticleEmitter("Flash", EMITTER_TYPE, 24 * COUNT_FACTOR);
 | 
					 | 
				
			||||||
        flash.setSelectRandomImage(true);
 | 
					 | 
				
			||||||
        flash.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1f / COUNT_FACTOR_F));
 | 
					 | 
				
			||||||
        flash.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
 | 
					 | 
				
			||||||
        flash.setStartSize(.1f);
 | 
					 | 
				
			||||||
        flash.setEndSize(0.5f);
 | 
					 | 
				
			||||||
        flash.setShape(new EmitterSphereShape(Vector3f.ZERO, .05f));
 | 
					 | 
				
			||||||
        flash.setParticlesPerSec(0);
 | 
					 | 
				
			||||||
        flash.setGravity(0, 0, 0);
 | 
					 | 
				
			||||||
        flash.setLowLife(.2f);
 | 
					 | 
				
			||||||
        flash.setHighLife(.2f);
 | 
					 | 
				
			||||||
        flash.getParticleInfluencer()
 | 
					 | 
				
			||||||
             .setInitialVelocity(new Vector3f(0, 5f, 0));
 | 
					 | 
				
			||||||
        flash.getParticleInfluencer().setVelocityVariation(1);
 | 
					 | 
				
			||||||
        flash.setImagesX(2);
 | 
					 | 
				
			||||||
        flash.setImagesY(2);
 | 
					 | 
				
			||||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
					 | 
				
			||||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/flash.png"));
 | 
					 | 
				
			||||||
        mat.setBoolean("PointSprite", POINT_SPRITE);
 | 
					 | 
				
			||||||
        flash.setMaterial(mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return flash;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates small spark particles, that dissipate into the air
 | 
					 | 
				
			||||||
     * @return spark texture
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private ParticleEmitter createRoundSpark(){
 | 
					 | 
				
			||||||
        roundspark = new ParticleEmitter("RoundSpark", EMITTER_TYPE, 20 * COUNT_FACTOR);
 | 
					 | 
				
			||||||
        roundspark.setStartColor(new ColorRGBA(1f, 0.29f, 0.34f, (float) (1.0 / COUNT_FACTOR_F)));
 | 
					 | 
				
			||||||
        roundspark.setEndColor(new ColorRGBA(0, 0, 0, 0.5f / COUNT_FACTOR_F));
 | 
					 | 
				
			||||||
        roundspark.setStartSize(0.2f);
 | 
					 | 
				
			||||||
        roundspark.setEndSize(0.8f);
 | 
					 | 
				
			||||||
        roundspark.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
 | 
					 | 
				
			||||||
        roundspark.setParticlesPerSec(0);
 | 
					 | 
				
			||||||
        roundspark.setGravity(0, -.5f, 0);
 | 
					 | 
				
			||||||
        roundspark.setLowLife(1.8f);
 | 
					 | 
				
			||||||
        roundspark.setHighLife(2f);
 | 
					 | 
				
			||||||
        roundspark.getParticleInfluencer()
 | 
					 | 
				
			||||||
                  .setInitialVelocity(new Vector3f(0, 3, 0));
 | 
					 | 
				
			||||||
        roundspark.getParticleInfluencer().setVelocityVariation(.5f);
 | 
					 | 
				
			||||||
        roundspark.setImagesX(1);
 | 
					 | 
				
			||||||
        roundspark.setImagesY(1);
 | 
					 | 
				
			||||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
					 | 
				
			||||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/roundspark.png"));
 | 
					 | 
				
			||||||
        mat.setBoolean("PointSprite", POINT_SPRITE);
 | 
					 | 
				
			||||||
        roundspark.setMaterial(mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return roundspark;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates small, thin smoke trails that enhance the flying debris
 | 
					 | 
				
			||||||
     * @return crates a thin smoke trail texture
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private ParticleEmitter createSpark(){
 | 
					 | 
				
			||||||
        spark = new ParticleEmitter("Spark", Type.Triangle, 30 * COUNT_FACTOR);
 | 
					 | 
				
			||||||
        spark.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F));
 | 
					 | 
				
			||||||
        spark.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
 | 
					 | 
				
			||||||
        spark.setStartSize(.5f);
 | 
					 | 
				
			||||||
        spark.setEndSize(.5f);
 | 
					 | 
				
			||||||
        spark.setFacingVelocity(true);
 | 
					 | 
				
			||||||
        spark.setParticlesPerSec(0);
 | 
					 | 
				
			||||||
        spark.setGravity(0, 5, 0);
 | 
					 | 
				
			||||||
        spark.setLowLife(1.1f);
 | 
					 | 
				
			||||||
        spark.setHighLife(1.5f);
 | 
					 | 
				
			||||||
        spark.getParticleInfluencer().setInitialVelocity(new Vector3f(0, 20, 0));
 | 
					 | 
				
			||||||
        spark.getParticleInfluencer().setVelocityVariation(1);
 | 
					 | 
				
			||||||
        spark.setImagesX(1);
 | 
					 | 
				
			||||||
        spark.setImagesY(1);
 | 
					 | 
				
			||||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
					 | 
				
			||||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/spark.png"));
 | 
					 | 
				
			||||||
        spark.setMaterial(mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return spark;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates a dynamic smoke trail
 | 
					 | 
				
			||||||
     * @return smoke texture
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private ParticleEmitter createSmokeTrail(){
 | 
					 | 
				
			||||||
        smoketrail = new ParticleEmitter("SmokeTrail", Type.Triangle, 22 * COUNT_FACTOR);
 | 
					 | 
				
			||||||
        smoketrail.setStartColor(new ColorRGBA(1f, 0.8f, 0.36f, 1.0f / COUNT_FACTOR_F));
 | 
					 | 
				
			||||||
        smoketrail.setEndColor(new ColorRGBA(1f, 0.8f, 0.36f, 0f));
 | 
					 | 
				
			||||||
        smoketrail.setStartSize(.2f);
 | 
					 | 
				
			||||||
        smoketrail.setEndSize(1f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//        smoketrail.setShape(new EmitterSphereShape(Vector3f.ZERO, 1f));
 | 
					 | 
				
			||||||
        smoketrail.setFacingVelocity(true);
 | 
					 | 
				
			||||||
        smoketrail.setParticlesPerSec(0);
 | 
					 | 
				
			||||||
        smoketrail.setGravity(0, 1, 0);
 | 
					 | 
				
			||||||
        smoketrail.setLowLife(.4f);
 | 
					 | 
				
			||||||
        smoketrail.setHighLife(.5f);
 | 
					 | 
				
			||||||
        smoketrail.getParticleInfluencer()
 | 
					 | 
				
			||||||
                  .setInitialVelocity(new Vector3f(0, 12, 0));
 | 
					 | 
				
			||||||
        smoketrail.getParticleInfluencer().setVelocityVariation(1);
 | 
					 | 
				
			||||||
        smoketrail.setImagesX(1);
 | 
					 | 
				
			||||||
        smoketrail.setImagesY(3);
 | 
					 | 
				
			||||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
					 | 
				
			||||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/smoketrail.png"));
 | 
					 | 
				
			||||||
        smoketrail.setMaterial(mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return smoketrail;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * creates flying debris particles
 | 
					 | 
				
			||||||
     * @return debris texture
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private ParticleEmitter createDebris(){
 | 
					 | 
				
			||||||
        debris = new ParticleEmitter("Debris", Type.Triangle, 15 * COUNT_FACTOR);
 | 
					 | 
				
			||||||
        debris.setSelectRandomImage(true);
 | 
					 | 
				
			||||||
        debris.setRandomAngle(true);
 | 
					 | 
				
			||||||
        debris.setRotateSpeed(FastMath.TWO_PI * 4);
 | 
					 | 
				
			||||||
        debris.setStartColor(new ColorRGBA(1f, 0.59f, 0.28f, 1.0f / COUNT_FACTOR_F));
 | 
					 | 
				
			||||||
        debris.setEndColor(new ColorRGBA(.5f, 0.5f, 0.5f, 0f));
 | 
					 | 
				
			||||||
        debris.setStartSize(.10f);
 | 
					 | 
				
			||||||
        debris.setEndSize(.15f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//        debris.setShape(new EmitterSphereShape(Vector3f.ZERO, .05f));
 | 
					 | 
				
			||||||
        debris.setParticlesPerSec(0);
 | 
					 | 
				
			||||||
        debris.setGravity(0, 12f, 0);
 | 
					 | 
				
			||||||
        debris.setLowLife(1.4f);
 | 
					 | 
				
			||||||
        debris.setHighLife(1.5f);
 | 
					 | 
				
			||||||
        debris.getParticleInfluencer()
 | 
					 | 
				
			||||||
              .setInitialVelocity(new Vector3f(0, 15, 0));
 | 
					 | 
				
			||||||
        debris.getParticleInfluencer().setVelocityVariation(.60f);
 | 
					 | 
				
			||||||
        debris.setImagesX(3);
 | 
					 | 
				
			||||||
        debris.setImagesY(3);
 | 
					 | 
				
			||||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
					 | 
				
			||||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/Debris.png"));
 | 
					 | 
				
			||||||
        debris.setMaterial(mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return debris;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates an expanding circular shockwave
 | 
					 | 
				
			||||||
     * @return shockwave texture
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private ParticleEmitter createShockwave(){
 | 
					 | 
				
			||||||
        shockwave = new ParticleEmitter("Shockwave", Type.Triangle, 1 * COUNT_FACTOR);
 | 
					 | 
				
			||||||
//        shockwave.setRandomAngle(true);
 | 
					 | 
				
			||||||
        shockwave.setFaceNormal(Vector3f.UNIT_Y);
 | 
					 | 
				
			||||||
        shockwave.setStartColor(new ColorRGBA(.48f, 0.17f, 0.01f, .8f / COUNT_FACTOR_F));
 | 
					 | 
				
			||||||
        shockwave.setEndColor(new ColorRGBA(.48f, 0.17f, 0.01f, 0f));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        shockwave.setStartSize(0f);
 | 
					 | 
				
			||||||
        shockwave.setEndSize(3f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        shockwave.setParticlesPerSec(0);
 | 
					 | 
				
			||||||
        shockwave.setGravity(0, 0, 0);
 | 
					 | 
				
			||||||
        shockwave.setLowLife(0.5f);
 | 
					 | 
				
			||||||
        shockwave.setHighLife(0.5f);
 | 
					 | 
				
			||||||
        shockwave.getParticleInfluencer()
 | 
					 | 
				
			||||||
                 .setInitialVelocity(new Vector3f(0, 0, 0));
 | 
					 | 
				
			||||||
        shockwave.getParticleInfluencer().setVelocityVariation(0f);
 | 
					 | 
				
			||||||
        shockwave.setImagesX(1);
 | 
					 | 
				
			||||||
        shockwave.setImagesY(1);
 | 
					 | 
				
			||||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
					 | 
				
			||||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Explosion/shockwave.png"));
 | 
					 | 
				
			||||||
        shockwave.setMaterial(mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return shockwave;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates an animated smoke column.
 | 
					 | 
				
			||||||
     * @return moving smoke texture
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private ParticleEmitter createMovingSmokeEmitter() {
 | 
					 | 
				
			||||||
        ParticleEmitter smokeEmitter = new ParticleEmitter("SmokeEmitter", Type.Triangle, 300);
 | 
					 | 
				
			||||||
        smokeEmitter.setGravity(0, 0, 0);
 | 
					 | 
				
			||||||
        smokeEmitter.getParticleInfluencer().setVelocityVariation(1);
 | 
					 | 
				
			||||||
        smokeEmitter.setLowLife(1);
 | 
					 | 
				
			||||||
        smokeEmitter.setHighLife(1);
 | 
					 | 
				
			||||||
        smokeEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, .5f, 0));
 | 
					 | 
				
			||||||
        smokeEmitter.setImagesX(15); // Assuming the smoke texture is a sprite sheet with 15 frames
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Set the material for the emitter
 | 
					 | 
				
			||||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
					 | 
				
			||||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Smoke/Smoke.png"));
 | 
					 | 
				
			||||||
        smokeEmitter.setMaterial(mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return smokeEmitter;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates a small burst of bubbles.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @return  bubbling water texture.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public ParticleEmitter createWaterSplash() {
 | 
					 | 
				
			||||||
        ParticleEmitter waterSplash = new ParticleEmitter("WaterSplash", Type.Triangle, 30);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        waterSplash.setShape(new EmitterSphereShape(Vector3f.ZERO, 0.2f));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        waterSplash.setStartColor(new ColorRGBA(0.4f, 0.4f, 1f, 1f));  // Light blue at start
 | 
					 | 
				
			||||||
        waterSplash.setEndColor(new ColorRGBA(0.4f, 0.4f, 1f, 0f));    // Transparent at the end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        waterSplash.setStartSize(0.05f);
 | 
					 | 
				
			||||||
        waterSplash.setEndSize(0.1f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        waterSplash.setLowLife(0.5f);
 | 
					 | 
				
			||||||
        waterSplash.setHighLife(1f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        waterSplash.setGravity(0, 0, 0);  // No gravity to simulate a horizontal water splash
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        waterSplash.getParticleInfluencer().setInitialVelocity(new Vector3f(1f, 0f, 1f));  // Horizontal spread
 | 
					 | 
				
			||||||
        waterSplash.getParticleInfluencer().setVelocityVariation(1f);  // Add randomness to splash
 | 
					 | 
				
			||||||
        waterSplash.setParticlesPerSec(0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
 | 
					 | 
				
			||||||
        mat.setTexture("Texture", app.getAssetManager().loadTexture("Effects/Splash/splash.png"));
 | 
					 | 
				
			||||||
        waterSplash.setMaterial(mat);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return waterSplash;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Constructs a {@code SeaSynchronizer} object with the specified application, root node, and ship map.
 | 
					     * Constructs a {@code SeaSynchronizer} object with the specified application, root node, and ship map.
 | 
				
			||||||
@@ -333,6 +63,7 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
        super(app.getGameLogic().getOwnMap(), root);
 | 
					        super(app.getGameLogic().getOwnMap(), root);
 | 
				
			||||||
        this.app = app;
 | 
					        this.app = app;
 | 
				
			||||||
        this.map = map;
 | 
					        this.map = map;
 | 
				
			||||||
 | 
					        this.particleFactory = new ParticleEffectFactory(app);
 | 
				
			||||||
        addExisting();
 | 
					        addExisting();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -349,13 +80,49 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
        return shot.isHit() ? handleHit(shot) : handleMiss(shot);
 | 
					        return shot.isHit() ? handleHit(shot) : handleMiss(shot);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handles a miss by representing it with a blue cylinder
 | 
				
			||||||
 | 
					     * and attaching a water splash effect to it.
 | 
				
			||||||
 | 
					     * @param shot the shot to be processed
 | 
				
			||||||
 | 
					     * @return a Spatial simulating a miss with water splash effect
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Spatial handleMiss(Shot shot) {
 | 
				
			||||||
 | 
					        Node shotNode = new Node("ShotNode");
 | 
				
			||||||
 | 
					        Geometry shotCylinder = createCylinder(shot);
 | 
				
			||||||
 | 
					        shotNode.attachChild(shotCylinder);
 | 
				
			||||||
 | 
					        ParticleEmitter waterSplash = particleFactory.createWaterSplash();
 | 
				
			||||||
 | 
					        waterSplash.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f);
 | 
				
			||||||
 | 
					        shotNode.attachChild(waterSplash);
 | 
				
			||||||
 | 
					        waterSplash.emitAllParticles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return shotNode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handles the sinking animation and removal of ship if destroyed
 | 
				
			||||||
 | 
					     * @param ship the ship to be sunk
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void sinkAndRemoveShip(Battleship ship) {
 | 
				
			||||||
 | 
					        Battleship wilkeningklaunichtmeinencode = ship;
 | 
				
			||||||
 | 
					        final Node shipNode = (Node) getSpatial(wilkeningklaunichtmeinencode);
 | 
				
			||||||
 | 
					        if (shipNode == null) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add sinking control to animate the sinking
 | 
				
			||||||
 | 
					        shipNode.addControl(new SinkingControl(shipNode));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Add particle effects
 | 
				
			||||||
 | 
					        ParticleEmitter bubbles = particleFactory.createWaterSplash();
 | 
				
			||||||
 | 
					        bubbles.setLocalTranslation(shipNode.getLocalTranslation());
 | 
				
			||||||
 | 
					        shipNode.attachChild(bubbles);
 | 
				
			||||||
 | 
					        bubbles.emitAllParticles();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Handles a hit by attaching its representation to the node that
 | 
					     * Handles a hit by attaching its representation to the node that
 | 
				
			||||||
     * contains the ship model as a child so that it moves with the ship.
 | 
					     * contains the ship model as a child so that it moves with the ship.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param shot a hit
 | 
					     * @param shot a hit
 | 
				
			||||||
     * @return always null to prevent the representation from being attached
 | 
					     * @return always null to prevent the representation from being attached to the items node as well
 | 
				
			||||||
     * to the items node as well
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private Spatial handleHit(Shot shot) {
 | 
					    private Spatial handleHit(Shot shot) {
 | 
				
			||||||
        final Battleship ship = requireNonNull(map.findShipAt(shot), "Missing ship");
 | 
					        final Battleship ship = requireNonNull(map.findShipAt(shot), "Missing ship");
 | 
				
			||||||
@@ -367,14 +134,14 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
        Node hitEffectNode = new Node("HitEffectNode");
 | 
					        Node hitEffectNode = new Node("HitEffectNode");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Create particle effects
 | 
					        // Create particle effects
 | 
				
			||||||
        ParticleEmitter flame = createFlame();
 | 
					        ParticleEmitter flame = particleFactory.createFlame();
 | 
				
			||||||
        ParticleEmitter flash = createFlash();
 | 
					        ParticleEmitter flash = particleFactory.createFlash();
 | 
				
			||||||
        ParticleEmitter spark = createSpark();
 | 
					        ParticleEmitter spark = particleFactory.createSpark();
 | 
				
			||||||
        ParticleEmitter roundSpark = createRoundSpark();
 | 
					        ParticleEmitter roundSpark = particleFactory.createRoundSpark();
 | 
				
			||||||
        ParticleEmitter smokeTrail = createSmokeTrail();
 | 
					        ParticleEmitter smokeTrail = particleFactory.createSmokeTrail();
 | 
				
			||||||
        ParticleEmitter debris = createDebris();
 | 
					        ParticleEmitter debris = particleFactory.createDebris();
 | 
				
			||||||
        ParticleEmitter shockwave = createShockwave();
 | 
					        ParticleEmitter shockwave = particleFactory.createShockwave();
 | 
				
			||||||
        ParticleEmitter movingSmoke = createMovingSmokeEmitter(); // New moving smoke emitter
 | 
					        ParticleEmitter movingSmoke = particleFactory.createMovingSmokeEmitter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Attach all effects to the hitEffectNode
 | 
					        // Attach all effects to the hitEffectNode
 | 
				
			||||||
        hitEffectNode.attachChild(flame);
 | 
					        hitEffectNode.attachChild(flame);
 | 
				
			||||||
@@ -403,36 +170,14 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
        flame.emitAllParticles();
 | 
					        flame.emitAllParticles();
 | 
				
			||||||
        roundSpark.emitAllParticles();
 | 
					        roundSpark.emitAllParticles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        //Checks if ship is destroyed and triggers animation accordingly
 | 
				
			||||||
        if (ship.isDestroyed()) {
 | 
					        if (ship.isDestroyed()) {
 | 
				
			||||||
            // Add ShipSinkingControl to the shipNode
 | 
					            sinkAndRemoveShip(ship);
 | 
				
			||||||
            ShipSinkingControl sinkingControl = new ShipSinkingControl(2f, 5f, -5f, 60f);
 | 
					 | 
				
			||||||
            shipNode.addControl(sinkingControl);
 | 
					 | 
				
			||||||
            sinkingControl.startSinking(); // Start the sinking process
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Handles a miss by representing it with a blue cylinder
 | 
					 | 
				
			||||||
     * and attaching a water splash effect to it.
 | 
					 | 
				
			||||||
     * @param shot the shot to be processed
 | 
					 | 
				
			||||||
     * @return a Spatial simulating a miss with water splash effect
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private Spatial handleMiss(Shot shot) {
 | 
					 | 
				
			||||||
        Node shotNode = new Node("ShotNode");
 | 
					 | 
				
			||||||
        Geometry shotCylinder = createCylinder(shot);
 | 
					 | 
				
			||||||
        shotNode.attachChild(shotCylinder);
 | 
					 | 
				
			||||||
        ParticleEmitter waterSplash = createWaterSplash();
 | 
					 | 
				
			||||||
        waterSplash.setLocalTranslation(shot.getY() + 0.5f, 0f, shot.getX() + 0.5f);
 | 
					 | 
				
			||||||
        shotNode.attachChild(waterSplash);
 | 
					 | 
				
			||||||
        waterSplash.emitAllParticles();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return shotNode;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Creates a cylinder geometry representing the specified shot.
 | 
					     * Creates a cylinder geometry representing the specified shot.
 | 
				
			||||||
     * The appearance of the cylinder depends on whether the shot is a hit or a miss.
 | 
					     * The appearance of the cylinder depends on whether the shot is a hit or a miss.
 | 
				
			||||||
@@ -484,17 +229,11 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    private Spatial createShip(Battleship ship) {
 | 
					    private Spatial createShip(Battleship ship) {
 | 
				
			||||||
        switch (ship.getLength()) {
 | 
					        switch (ship.getLength()) {
 | 
				
			||||||
            case 4:
 | 
					            case 4: return createBattleship(ship);
 | 
				
			||||||
                return createBattleship(ship);
 | 
					            case 3: return createCV(ship);
 | 
				
			||||||
            case 3:
 | 
					            case 2: return createBattle(ship);
 | 
				
			||||||
                return createCV(ship);
 | 
					            case 1: return createSmallship(ship);
 | 
				
			||||||
            case 2:
 | 
					            default: return createBox(ship);
 | 
				
			||||||
                return createBattle(ship);
 | 
					 | 
				
			||||||
            case 1:
 | 
					 | 
				
			||||||
                return createSmallShip(ship);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            default:
 | 
					 | 
				
			||||||
                return createBox(ship);
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -538,26 +277,27 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
     * Creates a detailed 3D model to represent a "King George V" battleship.
 | 
					     * Creates a detailed 3D model to represent a "King George V" battleship.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param ship the battleship to be represented
 | 
					     * @param ship the battleship to be represented
 | 
				
			||||||
     * @return the spatial representing the "King George V" battleship.
 | 
					     * @return the spatial representing the "King George V" battleship
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private Spatial createBattleship(Battleship ship) {
 | 
					    private Spatial createBattleship(Battleship ship) {
 | 
				
			||||||
        final Spatial model = app.getAssetManager().loadModel(KING_GEORGE_V_MODEL);
 | 
					        final Spatial model = app.getAssetManager().loadModel(KING_GEORGE_V_MODEL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
 | 
					        model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
 | 
				
			||||||
        model.scale(1.48f);
 | 
					        model.scale(1.48f);
 | 
				
			||||||
 | 
					        // model.scale(0.0007f);
 | 
				
			||||||
        model.setShadowMode(ShadowMode.CastAndReceive);
 | 
					        model.setShadowMode(ShadowMode.CastAndReceive);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return model;
 | 
					        return model;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Creates a detailed 3D model to represent a small tugboat.
 | 
					     * Creates a detailed 3D model to represent a small tug boat.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param ship the battleship to be represented
 | 
					     * @param ship the battleship to be represented
 | 
				
			||||||
     * @return the spatial representing the small tugboat.
 | 
					     * @return the spatial representing a small tug boat
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private Spatial createSmallShip(Battleship ship) {
 | 
					    private Spatial createSmallship(Battleship ship) {
 | 
				
			||||||
        final Spatial model = app.getAssetManager().loadModel(SMALL_BOAT_MODEL);
 | 
					        final Spatial model = app.getAssetManager().loadModel(BOAT_SMALL_MODEL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
 | 
					        model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
 | 
				
			||||||
        model.scale(0.0005f);
 | 
					        model.scale(0.0005f);
 | 
				
			||||||
@@ -567,27 +307,10 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Creates a detailed 3D model to represent a U-Boat .
 | 
					     * Creates a detailed 3D model to represent a "German WWII UBoat".
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param ship the battleship to be represented
 | 
					     * @param ship the battleship to be represented
 | 
				
			||||||
     * @return the spatial representing the U-Boat.
 | 
					     * @return the spatial representing the "German WWII UBoat"
 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    private Spatial createBattle(Battleship ship) {
 | 
					 | 
				
			||||||
        final Spatial model = app.getAssetManager().loadModel(BATTLE_MODEL);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
 | 
					 | 
				
			||||||
        // model.move(0f, -0.05f, 0f);
 | 
					 | 
				
			||||||
        model.scale(0.27f);
 | 
					 | 
				
			||||||
        model.setShadowMode(ShadowMode.CastAndReceive);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return model;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Creates a detailed 3D model to represent an aircraft carrier
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param ship the battleship to be represented
 | 
					 | 
				
			||||||
     * @return the spatial representing the aircraft carrier.
 | 
					 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private Spatial createCV(Battleship ship) {
 | 
					    private Spatial createCV(Battleship ship) {
 | 
				
			||||||
        final Spatial model = app.getAssetManager().loadModel(CV_MODEL);
 | 
					        final Spatial model = app.getAssetManager().loadModel(CV_MODEL);
 | 
				
			||||||
@@ -600,6 +323,23 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
        return model;
 | 
					        return model;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a detailed 3D model to represent a battleship.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param ship the battleship to be represented
 | 
				
			||||||
 | 
					     * @return the spatial representing a battleship
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Spatial createBattle(Battleship ship) {
 | 
				
			||||||
 | 
					        final Spatial model = app.getAssetManager().loadModel(BATTLE_MODEL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        model.rotate(-HALF_PI, calculateRotationAngle(ship.getRot()), 0f);
 | 
				
			||||||
 | 
					        model.move(0f, -0.06f, 0f);
 | 
				
			||||||
 | 
					        model.scale(0.27f);
 | 
				
			||||||
 | 
					        model.setShadowMode(ShadowMode.CastAndReceive);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return model;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Calculates the rotation angle for the specified rotation.
 | 
					     * Calculates the rotation angle for the specified rotation.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -635,5 +375,4 @@ class SeaSynchronizer extends ShipMapSynchronizer {
 | 
				
			|||||||
        model.addControl(new ShellControl(shell));
 | 
					        model.addControl(new ShellControl(shell));
 | 
				
			||||||
        return model;
 | 
					        return model;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -42,9 +42,16 @@ public class Shell2DControl extends AbstractControl {
 | 
				
			|||||||
        spatial.setLocalTranslation(viewPos.getX() + MapView.FIELD_SIZE / 2, viewPos.getY() + MapView.FIELD_SIZE / 2, 0);
 | 
					        spatial.setLocalTranslation(viewPos.getX() + MapView.FIELD_SIZE / 2, viewPos.getY() + MapView.FIELD_SIZE / 2, 0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * This method is called during the rendering phase, but it does not perform any
 | 
				
			||||||
 | 
					     * operations in this implementation as the control only influences the spatial's
 | 
				
			||||||
 | 
					     * transformation, not its rendering process.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param rm the RenderManager rendering the controlled Spatial (not null)
 | 
				
			||||||
 | 
					     * @param vp the ViewPort being rendered (not null)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void controlRender(RenderManager rm, ViewPort vp) {
 | 
					    protected void controlRender(RenderManager rm, ViewPort vp) {
 | 
				
			||||||
        // No rendering-specific behavior required for this control
 | 
					        // No rendering-specific behavior required for this control
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,9 +43,16 @@ public class ShellControl extends AbstractControl {
 | 
				
			|||||||
        spatial.rotate(PI/2,0,0);
 | 
					        spatial.rotate(PI/2,0,0);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * This method is called during the rendering phase, but it does not perform any
 | 
				
			||||||
 | 
					     * operations in this implementation as the control only influences the spatial's
 | 
				
			||||||
 | 
					     * transformation, not its rendering process.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param rm the RenderManager rendering the controlled Spatial (not null)
 | 
				
			||||||
 | 
					     * @param vp the ViewPort being rendered (not null)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void controlRender(RenderManager rm, ViewPort vp) {
 | 
					    protected void controlRender(RenderManager rm, ViewPort vp) {
 | 
				
			||||||
        // No rendering-specific behavior required for this control
 | 
					        // No rendering-specific behavior required for this control
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,116 +0,0 @@
 | 
				
			|||||||
package pp.battleship.client.gui;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.jme3.math.FastMath;
 | 
					 | 
				
			||||||
import com.jme3.math.Quaternion;
 | 
					 | 
				
			||||||
import com.jme3.math.Vector3f;
 | 
					 | 
				
			||||||
import com.jme3.renderer.RenderManager;
 | 
					 | 
				
			||||||
import com.jme3.renderer.ViewPort;
 | 
					 | 
				
			||||||
import com.jme3.scene.Node;
 | 
					 | 
				
			||||||
import com.jme3.scene.Spatial;
 | 
					 | 
				
			||||||
import com.jme3.scene.control.AbstractControl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Controls the burning, tilting, and sinking behavior of a battleship.
 | 
					 | 
				
			||||||
 * The ship will burn and tilt for a specified duration, then sink below the water surface.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class ShipSinkingControl extends AbstractControl {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private float elapsedTime = 0f;
 | 
					 | 
				
			||||||
    private final float burnTiltDuration;
 | 
					 | 
				
			||||||
    private final float sinkDuration;
 | 
					 | 
				
			||||||
    private final float sinkDepth;
 | 
					 | 
				
			||||||
    private final float tiltAngle;
 | 
					 | 
				
			||||||
    private final Vector3f initialPosition;
 | 
					 | 
				
			||||||
    private boolean sinkingStarted = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Constructs a new ShipSinkingControl instance.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param burnTiltDuration Time in seconds for the ship to burn and tilt on the surface
 | 
					 | 
				
			||||||
     * @param sinkDuration     Time in seconds for the ship to fully sink
 | 
					 | 
				
			||||||
     * @param sinkDepth        Depth below the water to sink the ship
 | 
					 | 
				
			||||||
     * @param tiltAngle        Final tilt angle in degrees
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public ShipSinkingControl(float burnTiltDuration, float sinkDuration, float sinkDepth, float tiltAngle) {
 | 
					 | 
				
			||||||
        this.burnTiltDuration = burnTiltDuration;
 | 
					 | 
				
			||||||
        this.sinkDuration = sinkDuration;
 | 
					 | 
				
			||||||
        this.sinkDepth = sinkDepth;
 | 
					 | 
				
			||||||
        this.tiltAngle = tiltAngle;
 | 
					 | 
				
			||||||
        this.initialPosition = new Vector3f(); // Placeholder; will be set in controlUpdate
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Overrides controlUpdate in AbstractControl
 | 
					 | 
				
			||||||
     * regulates the burn and tilt timeframe
 | 
					 | 
				
			||||||
     * @param tpf time per frame (in seconds)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void controlUpdate(float tpf) {
 | 
					 | 
				
			||||||
        if (spatial == null) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        elapsedTime += tpf;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (elapsedTime < burnTiltDuration) {
 | 
					 | 
				
			||||||
            float progress = elapsedTime / burnTiltDuration;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            float angleInRadians = FastMath.DEG_TO_RAD * FastMath.interpolateLinear(progress, 0f, -tiltAngle);
 | 
					 | 
				
			||||||
            Quaternion tiltRotation = new Quaternion().fromAngles(angleInRadians, 0, 0);
 | 
					 | 
				
			||||||
            spatial.setLocalRotation(tiltRotation);
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Start sinking if it hasn't started yet
 | 
					 | 
				
			||||||
        if (!sinkingStarted) {
 | 
					 | 
				
			||||||
            sinkingStarted = true;
 | 
					 | 
				
			||||||
            // Save the initial position when sinking starts
 | 
					 | 
				
			||||||
            initialPosition.set(spatial.getLocalTranslation());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Remove the hitEffectNode
 | 
					 | 
				
			||||||
            Node parentNode = (Node) spatial;
 | 
					 | 
				
			||||||
            Spatial hitEffects = parentNode.getChild("HitEffectNode");
 | 
					 | 
				
			||||||
            if (hitEffects != null) {
 | 
					 | 
				
			||||||
                parentNode.detachChild(hitEffects);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Calculate the progress of the sinking (0 to 1)
 | 
					 | 
				
			||||||
        float progress = Math.min((elapsedTime - burnTiltDuration) / sinkDuration, 1f);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Apply the tilt angle (remains constant during sinking)
 | 
					 | 
				
			||||||
        Quaternion tiltRotation = new Quaternion().fromAngles(-FastMath.DEG_TO_RAD * tiltAngle, 0, 0);
 | 
					 | 
				
			||||||
        spatial.setLocalRotation(tiltRotation);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Sink the ship by interpolating the Y position
 | 
					 | 
				
			||||||
        float currentY = FastMath.interpolateLinear(progress, initialPosition.y, sinkDepth);
 | 
					 | 
				
			||||||
        spatial.setLocalTranslation(initialPosition.x, currentY, initialPosition.z);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (currentY <= sinkDepth) {
 | 
					 | 
				
			||||||
            Node parentNode = (Node) spatial.getParent();
 | 
					 | 
				
			||||||
            if (parentNode != null) {
 | 
					 | 
				
			||||||
                parentNode.detachChild(spatial);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            spatial.removeControl(this);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * This method is called during the rendering phase, but it does not perform any
 | 
					 | 
				
			||||||
     * operations in this implementation as the control only influences the spatial's
 | 
					 | 
				
			||||||
     * transformation, not its rendering process.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param rm the RenderManager rendering the controlled Spatial (not null)
 | 
					 | 
				
			||||||
     * @param vp the ViewPort being rendered (not null)
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void controlRender(RenderManager rm, ViewPort vp) {
 | 
					 | 
				
			||||||
        // No rendering logic is needed for this control
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Starts the sinking process for the ship.
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public void startSinking() {
 | 
					 | 
				
			||||||
        // Nothing to do here as the control update handles the timing and sequence
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -0,0 +1,61 @@
 | 
				
			|||||||
 | 
					package pp.battleship.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.scene.control.AbstractControl;
 | 
				
			||||||
 | 
					import com.jme3.math.Vector3f;
 | 
				
			||||||
 | 
					import com.jme3.renderer.RenderManager;
 | 
				
			||||||
 | 
					import com.jme3.renderer.ViewPort;
 | 
				
			||||||
 | 
					import com.jme3.scene.Node;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Control that handles the sinking effect for destroyed ships.
 | 
				
			||||||
 | 
					 * It will gradually move the ship downwards and then remove it from the scene.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class SinkingControl extends AbstractControl {
 | 
				
			||||||
 | 
					    private static final float SINK_DURATION = 5f;  // Duration of the sinking animation
 | 
				
			||||||
 | 
					    private static final float SINK_SPEED = 0.1f;   // Speed at which the ship sinks
 | 
				
			||||||
 | 
					    private float elapsedTime = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final Node shipNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a {@code SinkingControl} object with the shipNode to be to be sunk
 | 
				
			||||||
 | 
					     * @param shipNode the node to handeld
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public SinkingControl(Node shipNode) {
 | 
				
			||||||
 | 
					        this.shipNode = shipNode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updated the Map to sink the ship
 | 
				
			||||||
 | 
					     * 
 | 
				
			||||||
 | 
					     * @param tpf time per frame
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void controlUpdate(float tpf) {
 | 
				
			||||||
 | 
					        // Update the sinking effect
 | 
				
			||||||
 | 
					        elapsedTime += tpf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Move the ship down over time
 | 
				
			||||||
 | 
					        Vector3f currentPos = shipNode.getLocalTranslation();
 | 
				
			||||||
 | 
					        shipNode.setLocalTranslation(currentPos.x, currentPos.y - SINK_SPEED * tpf, currentPos.z);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check if sinking duration has passed
 | 
				
			||||||
 | 
					        if (elapsedTime >= SINK_DURATION) {
 | 
				
			||||||
 | 
					            // Remove the ship from the scene
 | 
				
			||||||
 | 
					            shipNode.removeFromParent();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * This method is called during the rendering phase, but it does not perform any
 | 
				
			||||||
 | 
					     * operations in this implementation as the control only influences the spatial's
 | 
				
			||||||
 | 
					     * transformation, not its rendering process.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param rm the RenderManager rendering the controlled Spatial (not null)
 | 
				
			||||||
 | 
					     * @param vp the ViewPort being rendered (not null)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void controlRender(RenderManager rm, ViewPort vp) {
 | 
				
			||||||
 | 
					        // No rendering-related code needed
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,34 +0,0 @@
 | 
				
			|||||||
package pp.battleship.client.gui;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.simsilica.lemur.Slider;
 | 
					 | 
				
			||||||
import pp.battleship.client.GameMusic;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class VolumeControl extends Slider {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private final GameMusic menuMusicModern;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private double vol;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public VolumeControl(GameMusic music) {
 | 
					 | 
				
			||||||
        super();
 | 
					 | 
				
			||||||
        this.menuMusicModern = music;
 | 
					 | 
				
			||||||
        vol = GameMusic.volumePreference();
 | 
					 | 
				
			||||||
        getModel().setPercent(vol);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Updates the volume of the music to the appropriate level set by the User through the Volume control Slider
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public void update() {
 | 
					 | 
				
			||||||
        if (vol != getModel().getPercent()) {
 | 
					 | 
				
			||||||
            vol = getModel().getPercent();
 | 
					 | 
				
			||||||
            menuMusicModern.setVolume( (float) vol);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					package pp.battleship.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Slider;
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The VolumeSlider class represents the Volume Slider in the Menu.
 | 
				
			||||||
 | 
					 * It extends the Slider class and provides functionalities for setting the music volume,
 | 
				
			||||||
 | 
					 * with the help of the Slider in the GUI 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class VolumeSlider extends Slider {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final GameMusic music;
 | 
				
			||||||
 | 
					    private double vol;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs the Volume Slider for the Menu dialog
 | 
				
			||||||
 | 
					     * @param music the music instance
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public VolumeSlider(GameMusic music) {
 | 
				
			||||||
 | 
					        super();
 | 
				
			||||||
 | 
					        this.music = music;
 | 
				
			||||||
 | 
					        vol = GameMusic.volumeInPreferences();
 | 
				
			||||||
 | 
					        getModel().setPercent(vol);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * when triggered it updates the volume to the value set with the slider
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void update() {
 | 
				
			||||||
 | 
					        if (vol != getModel().getPercent()) {
 | 
				
			||||||
 | 
					            vol = getModel().getPercent();
 | 
				
			||||||
 | 
					            music.setVolume( (float) vol);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -5,7 +5,7 @@
 | 
				
			|||||||
// (c) Mark Minas (mark.minas@unibw.de)
 | 
					// (c) Mark Minas (mark.minas@unibw.de)
 | 
				
			||||||
////////////////////////////////////////
 | 
					////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package pp.battleship.client.server;
 | 
					package pp.battleship.server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.jme3.network.ConnectionListener;
 | 
					import com.jme3.network.ConnectionListener;
 | 
				
			||||||
import com.jme3.network.HostedConnection;
 | 
					import com.jme3.network.HostedConnection;
 | 
				
			||||||
@@ -42,14 +42,14 @@ import java.util.logging.LogManager;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * Server implementing the visitor pattern as MessageReceiver for ClientMessages
 | 
					 * Server implementing the visitor pattern as MessageReceiver for ClientMessages
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class BattleshipSelfhostServer implements MessageListener<HostedConnection>, ConnectionListener, ServerSender {
 | 
					public class BattleshipServer implements MessageListener<HostedConnection>, ConnectionListener, ServerSender {
 | 
				
			||||||
    private static final Logger LOGGER = System.getLogger(BattleshipSelfhostServer.class.getName());
 | 
					    private static final Logger LOGGER = System.getLogger(BattleshipServer.class.getName());
 | 
				
			||||||
    private static final File CONFIG_FILE = new File("server.properties");
 | 
					    private static final File CONFIG_FILE = new File("server.properties");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final BattleshipConfig config = new BattleshipConfig();
 | 
					    private final BattleshipConfig config = new BattleshipConfig();
 | 
				
			||||||
    private Server myServer;
 | 
					    private Server myServer;
 | 
				
			||||||
    private final ServerGameLogic logic;
 | 
					    private final ServerGameLogic logic;
 | 
				
			||||||
    private final BlockingQueue<ReceivedMessageSelfhost> pendingMessages = new LinkedBlockingQueue<>();
 | 
					    private final BlockingQueue<ReceivedMessage> pendingMessages = new LinkedBlockingQueue<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static {
 | 
					    static {
 | 
				
			||||||
        // Configure logging
 | 
					        // Configure logging
 | 
				
			||||||
@@ -64,16 +64,16 @@ public class BattleshipSelfhostServer implements MessageListener<HostedConnectio
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Starts the server.
 | 
					     * Starts the Battleships server.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public static void main(String[] args) {
 | 
					    public static void main(String[] args) {
 | 
				
			||||||
        new BattleshipSelfhostServer().run();
 | 
					        new BattleshipServer().run();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Creates the server.
 | 
					     * Creates the server.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    BattleshipSelfhostServer() {
 | 
					    BattleshipServer() {
 | 
				
			||||||
        config.readFromIfExists(CONFIG_FILE);
 | 
					        config.readFromIfExists(CONFIG_FILE);
 | 
				
			||||||
        LOGGER.log(Level.INFO, "Configuration: {0}", config); //NON-NLS
 | 
					        LOGGER.log(Level.INFO, "Configuration: {0}", config); //NON-NLS
 | 
				
			||||||
        logic = new ServerGameLogic(this, config);
 | 
					        logic = new ServerGameLogic(this, config);
 | 
				
			||||||
@@ -133,7 +133,7 @@ public class BattleshipSelfhostServer implements MessageListener<HostedConnectio
 | 
				
			|||||||
    public void messageReceived(HostedConnection source, Message message) {
 | 
					    public void messageReceived(HostedConnection source, Message message) {
 | 
				
			||||||
        LOGGER.log(Level.INFO, "message received from {0}: {1}", source.getId(), message); //NON-NLS
 | 
					        LOGGER.log(Level.INFO, "message received from {0}: {1}", source.getId(), message); //NON-NLS
 | 
				
			||||||
        if (message instanceof ClientMessage clientMessage)
 | 
					        if (message instanceof ClientMessage clientMessage)
 | 
				
			||||||
            pendingMessages.add(new ReceivedMessageSelfhost(clientMessage, source.getId()));
 | 
					            pendingMessages.add(new ReceivedMessage(clientMessage, source.getId()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@@ -5,12 +5,12 @@
 | 
				
			|||||||
// (c) Mark Minas (mark.minas@unibw.de)
 | 
					// (c) Mark Minas (mark.minas@unibw.de)
 | 
				
			||||||
////////////////////////////////////////
 | 
					////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package pp.battleship.client.server;
 | 
					package pp.battleship.server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.battleship.message.client.ClientInterpreter;
 | 
					import pp.battleship.message.client.ClientInterpreter;
 | 
				
			||||||
import pp.battleship.message.client.ClientMessage;
 | 
					import pp.battleship.message.client.ClientMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
record ReceivedMessageSelfhost(ClientMessage message, int from) {
 | 
					record ReceivedMessage(ClientMessage message, int from) {
 | 
				
			||||||
    void process(ClientInterpreter interpreter) {
 | 
					    void process(ClientInterpreter interpreter) {
 | 
				
			||||||
        message.accept(interpreter, from);
 | 
					        message.accept(interpreter, from);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -1,16 +0,0 @@
 | 
				
			|||||||
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
 | 
					 | 
				
			||||||
# File Created: 29.03.2012 14:25:39
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl default
 | 
					 | 
				
			||||||
	Ns 35.0000
 | 
					 | 
				
			||||||
	Ni 1.5000
 | 
					 | 
				
			||||||
	d 1.0000
 | 
					 | 
				
			||||||
	Tr 0.0000
 | 
					 | 
				
			||||||
	Tf 1.0000 1.0000 1.0000 
 | 
					 | 
				
			||||||
	illum 2
 | 
					 | 
				
			||||||
	Ka 1.0000 1.0000 1.0000
 | 
					 | 
				
			||||||
	Kd 1.0000 1.0000 1.0000
 | 
					 | 
				
			||||||
	Ks 0.5400 0.5400 0.5400
 | 
					 | 
				
			||||||
	Ke 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
	map_Ka 14084_WWII_ship_German_Type_II_U-boat_diff.jpg
 | 
					 | 
				
			||||||
	map_Kd 14084_WWII_ship_German_Type_II_U-boat_diff.jpg
 | 
					 | 
				
			||||||
@@ -1,104 +0,0 @@
 | 
				
			|||||||
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
 | 
					 | 
				
			||||||
# File Created: 16.12.2011 14:18:52
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl white
 | 
					 | 
				
			||||||
	Ns 53.0000
 | 
					 | 
				
			||||||
	Ni 1.5000
 | 
					 | 
				
			||||||
	d 1.0000
 | 
					 | 
				
			||||||
	Tr 0.0000
 | 
					 | 
				
			||||||
	Tf 1.0000 1.0000 1.0000 
 | 
					 | 
				
			||||||
	illum 2
 | 
					 | 
				
			||||||
	Ka 0.6667 0.6667 0.6667
 | 
					 | 
				
			||||||
	Kd 0.6667 0.6667 0.6667
 | 
					 | 
				
			||||||
	Ks 0.1800 0.1800 0.1800
 | 
					 | 
				
			||||||
	Ke 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl boat_elements_black
 | 
					 | 
				
			||||||
	Ns 55.0000
 | 
					 | 
				
			||||||
	Ni 1.5000
 | 
					 | 
				
			||||||
	d 1.0000
 | 
					 | 
				
			||||||
	Tr 0.0000
 | 
					 | 
				
			||||||
	Tf 1.0000 1.0000 1.0000 
 | 
					 | 
				
			||||||
	illum 2
 | 
					 | 
				
			||||||
	Ka 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
	Kd 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
	Ks 0.3600 0.3600 0.3600
 | 
					 | 
				
			||||||
	Ke 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl boat_glass
 | 
					 | 
				
			||||||
	Ns 60.0000
 | 
					 | 
				
			||||||
	Ni 7.0000
 | 
					 | 
				
			||||||
	d 0.4000
 | 
					 | 
				
			||||||
	Tr 0.6000
 | 
					 | 
				
			||||||
	Tf 0.4000 0.4000 0.4000 
 | 
					 | 
				
			||||||
	illum 2
 | 
					 | 
				
			||||||
	Ka 0.1059 0.1569 0.1451
 | 
					 | 
				
			||||||
	Kd 0.1059 0.1569 0.1451
 | 
					 | 
				
			||||||
	Ks 0.6750 0.6750 0.6750
 | 
					 | 
				
			||||||
	Ke 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl boat_screw_hooks_bronze
 | 
					 | 
				
			||||||
	Ns 80.0000
 | 
					 | 
				
			||||||
	Ni 1.5000
 | 
					 | 
				
			||||||
	d 1.0000
 | 
					 | 
				
			||||||
	Tr 0.0000
 | 
					 | 
				
			||||||
	Tf 1.0000 1.0000 1.0000 
 | 
					 | 
				
			||||||
	illum 2
 | 
					 | 
				
			||||||
	Ka 0.2941 0.2157 0.0510
 | 
					 | 
				
			||||||
	Kd 0.2941 0.2157 0.0510
 | 
					 | 
				
			||||||
	Ks 0.7200 0.7200 0.7200
 | 
					 | 
				
			||||||
	Ke 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl boat_silver
 | 
					 | 
				
			||||||
	Ns 80.0000
 | 
					 | 
				
			||||||
	Ni 1.5000
 | 
					 | 
				
			||||||
	d 1.0000
 | 
					 | 
				
			||||||
	Tr 0.0000
 | 
					 | 
				
			||||||
	Tf 1.0000 1.0000 1.0000 
 | 
					 | 
				
			||||||
	illum 2
 | 
					 | 
				
			||||||
	Ka 0.3333 0.3333 0.3333
 | 
					 | 
				
			||||||
	Kd 0.3333 0.3333 0.3333
 | 
					 | 
				
			||||||
	Ks 0.7200 0.7200 0.7200
 | 
					 | 
				
			||||||
	Ke 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl boat_buffer
 | 
					 | 
				
			||||||
	Ns 10.0000
 | 
					 | 
				
			||||||
	Ni 1.5000
 | 
					 | 
				
			||||||
	d 1.0000
 | 
					 | 
				
			||||||
	Tr 0.0000
 | 
					 | 
				
			||||||
	Tf 1.0000 1.0000 1.0000 
 | 
					 | 
				
			||||||
	illum 2
 | 
					 | 
				
			||||||
	Ka 1.0000 1.0000 1.0000
 | 
					 | 
				
			||||||
	Kd 1.0000 1.0000 1.0000
 | 
					 | 
				
			||||||
	Ks 0.2700 0.2700 0.2700
 | 
					 | 
				
			||||||
	Ke 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
	map_Ka boat_buffer_diffuse.jpg
 | 
					 | 
				
			||||||
	map_Kd boat_buffer_diffuse.jpg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl boat_roof_accessory
 | 
					 | 
				
			||||||
	Ns 15.0000
 | 
					 | 
				
			||||||
	Ni 1.5000
 | 
					 | 
				
			||||||
	d 1.0000
 | 
					 | 
				
			||||||
	Tr 0.0000
 | 
					 | 
				
			||||||
	Tf 1.0000 1.0000 1.0000 
 | 
					 | 
				
			||||||
	illum 2
 | 
					 | 
				
			||||||
	Ka 1.0000 1.0000 1.0000
 | 
					 | 
				
			||||||
	Kd 1.0000 1.0000 1.0000
 | 
					 | 
				
			||||||
	Ks 0.3600 0.3600 0.3600
 | 
					 | 
				
			||||||
	Ke 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
	map_Ka boat_roof_accessory_diffuse.jpg
 | 
					 | 
				
			||||||
	map_Kd boat_roof_accessory_diffuse.jpg
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl boat_body
 | 
					 | 
				
			||||||
	Ns 55.0000
 | 
					 | 
				
			||||||
	Ni 1.5000
 | 
					 | 
				
			||||||
	d 1.0000
 | 
					 | 
				
			||||||
	Tr 0.0000
 | 
					 | 
				
			||||||
	Tf 1.0000 1.0000 1.0000 
 | 
					 | 
				
			||||||
	illum 2
 | 
					 | 
				
			||||||
	Ka 1.0000 1.0000 1.0000
 | 
					 | 
				
			||||||
	Kd 1.0000 1.0000 1.0000
 | 
					 | 
				
			||||||
	Ks 0.3600 0.3600 0.3600
 | 
					 | 
				
			||||||
	Ke 0.0000 0.0000 0.0000
 | 
					 | 
				
			||||||
	map_Ka boat_body_diffuse.jpg
 | 
					 | 
				
			||||||
	map_Kd boat_body_diffuse.jpg
 | 
					 | 
				
			||||||
| 
		 Before Width: | Height: | Size: 78 B  | 
@@ -1,180 +0,0 @@
 | 
				
			|||||||
newmtl Model001_Material001
 | 
					 | 
				
			||||||
map_Kd Yorktown_paint6.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material002
 | 
					 | 
				
			||||||
map_Kd diff_null_7.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material003
 | 
					 | 
				
			||||||
map_Kd diff_null_14.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material004
 | 
					 | 
				
			||||||
map_Kd diff_null_Color005.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material005
 | 
					 | 
				
			||||||
map_Kd diff_null_8.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material006
 | 
					 | 
				
			||||||
map_Kd diff_null_Color007.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material007
 | 
					 | 
				
			||||||
map_Kd diff_null_17.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material008
 | 
					 | 
				
			||||||
map_Kd diff_null_FrontColor.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material009
 | 
					 | 
				
			||||||
map_Kd diff_null_BackColor.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material010
 | 
					 | 
				
			||||||
map_Kd diff_null_4.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material011
 | 
					 | 
				
			||||||
map_Kd Color_004.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material012
 | 
					 | 
				
			||||||
map_Kd diff_null_Gray1.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material013
 | 
					 | 
				
			||||||
map_Kd diff_null_3.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material014
 | 
					 | 
				
			||||||
map_Kd Metal_Rough.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material015
 | 
					 | 
				
			||||||
map_Kd diff_null_12.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material016
 | 
					 | 
				
			||||||
map_Kd diff_null_19.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material017
 | 
					 | 
				
			||||||
map_Kd diff_null_Color003.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material018
 | 
					 | 
				
			||||||
map_Kd diff_null_mat1.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material019
 | 
					 | 
				
			||||||
map_Kd diff_null_1.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material020
 | 
					 | 
				
			||||||
map_Kd diff_null_2.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material021
 | 
					 | 
				
			||||||
map_Kd diff_null_Black1.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material022
 | 
					 | 
				
			||||||
map_Kd diff_null_Model001Materia.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material023
 | 
					 | 
				
			||||||
map_Kd diff_null_Model001Mate1.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material024
 | 
					 | 
				
			||||||
map_Kd diff_null_13.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material025
 | 
					 | 
				
			||||||
map_Kd _6.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material026
 | 
					 | 
				
			||||||
map_Kd Metal_Aluminum_Anodized.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material027
 | 
					 | 
				
			||||||
map_Kd diff_null_Color006.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material028
 | 
					 | 
				
			||||||
map_Kd Blinds_Vertical_Stripe_Gray.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material029
 | 
					 | 
				
			||||||
map_Kd diff_null_15.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material030
 | 
					 | 
				
			||||||
map_Kd diff_null_Color002.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material031
 | 
					 | 
				
			||||||
map_Kd Cladding_Siding_Tan.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material032
 | 
					 | 
				
			||||||
map_Kd diff_null_Color008.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material033
 | 
					 | 
				
			||||||
map_Kd diff_null_16.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material034
 | 
					 | 
				
			||||||
map_Kd _2.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material035
 | 
					 | 
				
			||||||
map_Kd Blinds_Roman_Hobbled_Blue.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material036
 | 
					 | 
				
			||||||
map_Kd Blinds_Wood_White.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material037
 | 
					 | 
				
			||||||
map_Kd Metal_Seamed.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material038
 | 
					 | 
				
			||||||
map_Kd image_16.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material039
 | 
					 | 
				
			||||||
map_Kd diff_null_9.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material040
 | 
					 | 
				
			||||||
map_Kd image_15.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material041
 | 
					 | 
				
			||||||
map_Kd _Blinds_Roman_Hobbled_Blue_1.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material042
 | 
					 | 
				
			||||||
map_Kd diff_null_10.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material043
 | 
					 | 
				
			||||||
map_Kd diff_null_Material5.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material044
 | 
					 | 
				
			||||||
map_Kd diff_null_Material1.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
newmtl Model001_Material045
 | 
					 | 
				
			||||||
map_Kd diff_null_11.png
 | 
					 | 
				
			||||||
map_bump bumpmap_flat.png
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@@ -41,14 +41,16 @@ public class ModelExporter extends SimpleApplication {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void simpleInitApp() {
 | 
					    public void simpleInitApp() {
 | 
				
			||||||
        export("Models/KingGeorgeV/King_George_V.obj", "KingGeorgeV.j3o");//NON-NLS
 | 
					        export("Models/KingGeorgeV/King_George_V.obj", "KingGeorgeV.j3o"); //NON-NLS
 | 
				
			||||||
        export("Models/BoatSmall/12219_boat_v2_L2.obj", "12219_boat_v2_L2.j3o");
 | 
					        export("Models/BoatSmall/12219_boat_v2_L2.obj", "BoatSmall.j3o"); //NON-NLS
 | 
				
			||||||
        export("Models/Battle/14084_WWII_Ship_German_Type_II_U-boat_v2_L1.obj", "Battle.j3o"); //NON-NLS
 | 
					        export("Models/Battle/14084_WWII_Ship_German_Type_II_U-boat_v2_L1.obj", "Battle.j3o"); //NON-NLS
 | 
				
			||||||
        export("Models/CV/essex_scb-125_generic.obj", "CV.j3o"); //NON-NLS
 | 
					        export("Models/CV/essex_scb-125_generic.obj", "CV.j3o"); //NON-NLS
 | 
				
			||||||
        export("Models/Shell/Shell/45.obj", "Shell.j3o");
 | 
					        export("Models/Figures/Würfel_blau.obj", "Würfel_blau.j30");
 | 
				
			||||||
 | 
					        export("Models/Figures/Würfel_gelb.obj", "Würfel_gelb.j30");
 | 
				
			||||||
 | 
					        export("Models/Figures/Würfel_grün.obj", "Würfel_grün.j30");
 | 
				
			||||||
 | 
					        export("Models/Figures/Würfel_rosa.obj", "Würfel_rosa.j30");
 | 
				
			||||||
 | 
					        export("Models/Figures/Würfel_rot.obj", "Würfel_rot.j30");
 | 
				
			||||||
 | 
					        export("Models/Figures/Würfel_schwarz.obj", "Würfel_schwarz.j30");
 | 
				
			||||||
        stop();
 | 
					        stop();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
| 
		 Before Width: | Height: | Size: 78 B  | 
| 
		 Before Width: | Height: | Size: 127 KiB  | 
| 
		 Before Width: | Height: | Size: 94 KiB  | 
| 
		 Before Width: | Height: | Size: 90 KiB  | 
| 
		 Before Width: | Height: | Size: 147 KiB  | 
| 
		 Before Width: | Height: | Size: 109 KiB  | 
| 
		 Before Width: | Height: | Size: 101 KiB  | 
| 
		 Before Width: | Height: | Size: 102 KiB  | 
| 
		 Before Width: | Height: | Size: 35 KiB  | 
| 
		 Before Width: | Height: | Size: 3.0 MiB  | 
| 
		 Before Width: | Height: | Size: 246 KiB  | 
| 
		 Before Width: | Height: | Size: 330 KiB  | 
@@ -10,8 +10,8 @@ package pp.battleship.game.client;
 | 
				
			|||||||
import pp.battleship.message.client.ShootMessage;
 | 
					import pp.battleship.message.client.ShootMessage;
 | 
				
			||||||
import pp.battleship.message.server.EffectMessage;
 | 
					import pp.battleship.message.server.EffectMessage;
 | 
				
			||||||
import pp.battleship.model.IntPoint;
 | 
					import pp.battleship.model.IntPoint;
 | 
				
			||||||
import pp.battleship.model.ShipMap;
 | 
					 | 
				
			||||||
import pp.battleship.model.Shell;
 | 
					import pp.battleship.model.Shell;
 | 
				
			||||||
 | 
					import pp.battleship.model.ShipMap;
 | 
				
			||||||
import pp.battleship.notification.Sound;
 | 
					import pp.battleship.notification.Sound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.lang.System.Logger.Level;
 | 
					import java.lang.System.Logger.Level;
 | 
				
			||||||
@@ -54,13 +54,13 @@ class BattleState extends ClientState {
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void receivedEffect(EffectMessage msg) {
 | 
					    public void receivedEffect(EffectMessage msg) {
 | 
				
			||||||
        ClientGameLogic.LOGGER.log(Level.INFO, "report effect: {0}", msg); //NON-NLS
 | 
					        ClientGameLogic.LOGGER.log(Level.INFO, "report effect: {0}", msg); //NON-NLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        myTurn = msg.isMyTurn();
 | 
					        myTurn = msg.isMyTurn();
 | 
				
			||||||
        logic.setInfoText(msg.getInfoTextKey());
 | 
					        logic.setInfoText(msg.getInfoTextKey());
 | 
				
			||||||
        Shell shell = new Shell(msg.getShot());
 | 
					        Shell shell = new Shell(msg.getShot());
 | 
				
			||||||
        affectedMap(msg).add(shell);
 | 
					        affectedMap(msg).add(shell);
 | 
				
			||||||
        logic.playSound(Sound.SHELL_FLYING);
 | 
					        logic.playSound(Sound.SHELL_FLYING);
 | 
				
			||||||
        logic.setState(new ShootingState(logic, shell, myTurn, msg));
 | 
					        logic.setState(new ShootingState(logic, shell, myTurn, msg));
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,10 +8,10 @@
 | 
				
			|||||||
package pp.battleship.game.server;
 | 
					package pp.battleship.game.server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.battleship.BattleshipConfig;
 | 
					import pp.battleship.BattleshipConfig;
 | 
				
			||||||
 | 
					import pp.battleship.message.client.AnimationFinishedMessage;
 | 
				
			||||||
import pp.battleship.message.client.ClientInterpreter;
 | 
					import pp.battleship.message.client.ClientInterpreter;
 | 
				
			||||||
import pp.battleship.message.client.MapMessage;
 | 
					import pp.battleship.message.client.MapMessage;
 | 
				
			||||||
import pp.battleship.message.client.ShootMessage;
 | 
					import pp.battleship.message.client.ShootMessage;
 | 
				
			||||||
import pp.battleship.message.client.AnimationFinishedMessage;
 | 
					 | 
				
			||||||
import pp.battleship.message.server.EffectMessage;
 | 
					import pp.battleship.message.server.EffectMessage;
 | 
				
			||||||
import pp.battleship.message.server.GameDetails;
 | 
					import pp.battleship.message.server.GameDetails;
 | 
				
			||||||
import pp.battleship.message.server.ServerMessage;
 | 
					import pp.battleship.message.server.ServerMessage;
 | 
				
			||||||
@@ -36,10 +36,10 @@ public class ServerGameLogic implements ClientInterpreter {
 | 
				
			|||||||
    private final BattleshipConfig config;
 | 
					    private final BattleshipConfig config;
 | 
				
			||||||
    private final List<Player> players = new ArrayList<>(2);
 | 
					    private final List<Player> players = new ArrayList<>(2);
 | 
				
			||||||
    private final Set<Player> readyPlayers = new HashSet<>();
 | 
					    private final Set<Player> readyPlayers = new HashSet<>();
 | 
				
			||||||
    private Set<Player> waitPlayers = new HashSet<>();
 | 
					 | 
				
			||||||
    private final ServerSender serverSender;
 | 
					    private final ServerSender serverSender;
 | 
				
			||||||
    private Player activePlayer;
 | 
					    private Player activePlayer;
 | 
				
			||||||
    private ServerState state = ServerState.WAIT;
 | 
					    private ServerState state = ServerState.WAIT;
 | 
				
			||||||
 | 
					    private Set<Player> waitPlayers = new HashSet<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Constructs a ServerGameLogic with the specified sender and configuration.
 | 
					     * Constructs a ServerGameLogic with the specified sender and configuration.
 | 
				
			||||||
@@ -136,7 +136,6 @@ public class ServerGameLogic implements ClientInterpreter {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Handles the reception of a MapMessage.
 | 
					     * Handles the reception of a MapMessage.
 | 
				
			||||||
     * Also tests valid ship placement on the Map.
 | 
					 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param msg  the received MapMessage
 | 
					     * @param msg  the received MapMessage
 | 
				
			||||||
     * @param from the ID of the sender client
 | 
					     * @param from the ID of the sender client
 | 
				
			||||||
@@ -154,85 +153,8 @@ public class ServerGameLogic implements ClientInterpreter {
 | 
				
			|||||||
                send(players.get(from), new GameDetails(config));
 | 
					                send(players.get(from), new GameDetails(config));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Handles the reception of a ShootMessage.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param msg  the received ShootMessage
 | 
					 | 
				
			||||||
     * @param from the ID of the sender client
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void received(ShootMessage msg, int from) {
 | 
					 | 
				
			||||||
        if (state != ServerState.BATTLE)
 | 
					 | 
				
			||||||
            LOGGER.log(Level.ERROR, "shoot not allowed in {0}", state); //NON-NLS
 | 
					 | 
				
			||||||
        else{
 | 
					 | 
				
			||||||
            setState(ServerState.ANIMATION);
 | 
					 | 
				
			||||||
            shoot(getPlayerById(from), msg.getPosition());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Marks the player as ready and sets their ships.
 | 
					 | 
				
			||||||
     * Transitions the state to PLAY if both players are ready.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param player the player who is ready
 | 
					 | 
				
			||||||
     * @param ships  the list of ships placed by the player
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    void playerReady(Player player, List<Battleship> ships) {
 | 
					 | 
				
			||||||
        if (!readyPlayers.add(player)) {
 | 
					 | 
				
			||||||
            LOGGER.log(Level.ERROR, "{0} was already ready", player); //NON-NLS
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        ships.forEach(player.getMap()::add);
 | 
					 | 
				
			||||||
        if (readyPlayers.size() == 2) {
 | 
					 | 
				
			||||||
            for (Player p : players)
 | 
					 | 
				
			||||||
                send(p, new StartBattleMessage(p == activePlayer));
 | 
					 | 
				
			||||||
            setState(ServerState.BATTLE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Handles the shooting action by the player.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param p   the player who shot
 | 
					 | 
				
			||||||
     * @param pos the position of the shot
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    void shoot(Player p, IntPoint pos) {
 | 
					 | 
				
			||||||
        if (p != activePlayer) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        final Player otherPlayer = getOpponent(activePlayer);
 | 
					 | 
				
			||||||
        final Battleship selectedShip = otherPlayer.getMap().findShipAt(pos);
 | 
					 | 
				
			||||||
        if (selectedShip == null) {
 | 
					 | 
				
			||||||
            // shot missed
 | 
					 | 
				
			||||||
            send(activePlayer, EffectMessage.miss(true, pos));
 | 
					 | 
				
			||||||
            send(otherPlayer, EffectMessage.miss(false, pos));
 | 
					 | 
				
			||||||
            activePlayer = otherPlayer;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        else {
 | 
					 | 
				
			||||||
            // shot hit a ship
 | 
					 | 
				
			||||||
            selectedShip.hit(pos);
 | 
					 | 
				
			||||||
            if (otherPlayer.getMap().getRemainingShips().isEmpty()) {
 | 
					 | 
				
			||||||
                // game is over
 | 
					 | 
				
			||||||
                send(activePlayer, EffectMessage.won(pos, selectedShip));
 | 
					 | 
				
			||||||
                send(otherPlayer, EffectMessage.lost(pos, selectedShip, activePlayer.getMap().getRemainingShips()));
 | 
					 | 
				
			||||||
                setState(ServerState.GAME_OVER);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else if (selectedShip.isDestroyed()) {
 | 
					 | 
				
			||||||
                // ship has been destroyed, but game is not yet over
 | 
					 | 
				
			||||||
                send(activePlayer, EffectMessage.shipDestroyed(true, pos, selectedShip));
 | 
					 | 
				
			||||||
                send(otherPlayer, EffectMessage.shipDestroyed(false, pos, selectedShip));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else {
 | 
					 | 
				
			||||||
                // ship has been hit, but it hasn't been destroyed
 | 
					 | 
				
			||||||
                send(activePlayer, EffectMessage.hit(true, pos));
 | 
					 | 
				
			||||||
                send(otherPlayer, EffectMessage.hit(false, pos));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Handles the reception of a AnimationFinishedMessage.
 | 
					     * Handles the reception of a AnimationFinishedMessage.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -290,4 +212,79 @@ public class ServerGameLogic implements ClientInterpreter {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return true;
 | 
					        return true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handles the reception of a ShootMessage.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param msg  the received ShootMessage
 | 
				
			||||||
 | 
					     * @param from the ID of the sender client
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void received(ShootMessage msg, int from) {
 | 
				
			||||||
 | 
					        if (state != ServerState.BATTLE)
 | 
				
			||||||
 | 
					            LOGGER.log(Level.ERROR, "shoot not allowed in {0}", state); //NON-NLS
 | 
				
			||||||
 | 
					        else{
 | 
				
			||||||
 | 
					            setState(ServerState.ANIMATION);
 | 
				
			||||||
 | 
					            shoot(getPlayerById(from), msg.getPosition());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Marks the player as ready and sets their ships.
 | 
				
			||||||
 | 
					     * Transitions the state to PLAY if both players are ready.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param player the player who is ready
 | 
				
			||||||
 | 
					     * @param ships  the list of ships placed by the player
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void playerReady(Player player, List<Battleship> ships) {
 | 
				
			||||||
 | 
					        if (!readyPlayers.add(player)) {
 | 
				
			||||||
 | 
					            LOGGER.log(Level.ERROR, "{0} was already ready", player); //NON-NLS
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        ships.forEach(player.getMap()::add);
 | 
				
			||||||
 | 
					        if (readyPlayers.size() == 2) {
 | 
				
			||||||
 | 
					            for (Player p : players)
 | 
				
			||||||
 | 
					                send(p, new StartBattleMessage(p == activePlayer));
 | 
				
			||||||
 | 
					            setState(ServerState.BATTLE);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handles the shooting action by the player.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param p   the player who shot
 | 
				
			||||||
 | 
					     * @param pos the position of the shot
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void shoot(Player p, IntPoint pos) {
 | 
				
			||||||
 | 
					        if (p != activePlayer) return;
 | 
				
			||||||
 | 
					        final Player otherPlayer = getOpponent(activePlayer);
 | 
				
			||||||
 | 
					        final Battleship selectedShip = otherPlayer.getMap().findShipAt(pos);
 | 
				
			||||||
 | 
					        if (selectedShip == null) {
 | 
				
			||||||
 | 
					            // shot missed
 | 
				
			||||||
 | 
					            send(activePlayer, EffectMessage.miss(true, pos));
 | 
				
			||||||
 | 
					            send(otherPlayer, EffectMessage.miss(false, pos));
 | 
				
			||||||
 | 
					            activePlayer = otherPlayer;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            // shot hit a ship
 | 
				
			||||||
 | 
					            selectedShip.hit(pos);
 | 
				
			||||||
 | 
					            if (otherPlayer.getMap().getRemainingShips().isEmpty()) {
 | 
				
			||||||
 | 
					                // game is over
 | 
				
			||||||
 | 
					                send(activePlayer, EffectMessage.won(pos, selectedShip));
 | 
				
			||||||
 | 
					                send(otherPlayer, EffectMessage.lost(pos, selectedShip, activePlayer.getMap().getRemainingShips()));
 | 
				
			||||||
 | 
					                setState(ServerState.GAME_OVER);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else if (selectedShip.isDestroyed()) {
 | 
				
			||||||
 | 
					                // ship has been destroyed, but game is not yet over
 | 
				
			||||||
 | 
					                send(activePlayer, EffectMessage.shipDestroyed(true, pos, selectedShip));
 | 
				
			||||||
 | 
					                send(otherPlayer, EffectMessage.shipDestroyed(false, pos, selectedShip));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                // ship has been hit, but it hasn't been destroyed
 | 
				
			||||||
 | 
					                send(activePlayer, EffectMessage.hit(true, pos));
 | 
				
			||||||
 | 
					                send(otherPlayer, EffectMessage.hit(false, pos));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,9 +31,8 @@ enum ServerState {
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    ANIMATION,
 | 
					    ANIMATION,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * The game has ended because all the ships of one player have been destroyed.
 | 
					     * The game has ended because all the ships of one player have been destroyed.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    GAME_OVER,
 | 
					    GAME_OVER
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,11 +7,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package pp.battleship.game.singlemode;
 | 
					package pp.battleship.game.singlemode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.battleship.message.client.AnimationFinishedMessage;
 | 
				
			||||||
import pp.battleship.message.client.ClientInterpreter;
 | 
					import pp.battleship.message.client.ClientInterpreter;
 | 
				
			||||||
import pp.battleship.message.client.ClientMessage;
 | 
					import pp.battleship.message.client.ClientMessage;
 | 
				
			||||||
import pp.battleship.message.client.MapMessage;
 | 
					import pp.battleship.message.client.MapMessage;
 | 
				
			||||||
import pp.battleship.message.client.ShootMessage;
 | 
					import pp.battleship.message.client.ShootMessage;
 | 
				
			||||||
import pp.battleship.message.client.AnimationFinishedMessage;
 | 
					 | 
				
			||||||
import pp.battleship.model.Battleship;
 | 
					import pp.battleship.model.Battleship;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -69,7 +69,6 @@ class Copycat implements ClientInterpreter {
 | 
				
			|||||||
        copiedMessage = msg;
 | 
					        copiedMessage = msg;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Creates a copy of the provided {@link Battleship}.
 | 
					     * Creates a copy of the provided {@link Battleship}.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -79,5 +78,4 @@ class Copycat implements ClientInterpreter {
 | 
				
			|||||||
    private static Battleship copy(Battleship ship) {
 | 
					    private static Battleship copy(Battleship ship) {
 | 
				
			||||||
        return new Battleship(ship.getLength(), ship.getX(), ship.getY(), ship.getRot());
 | 
					        return new Battleship(ship.getLength(), ship.getX(), ship.getY(), ship.getRot());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,9 @@
 | 
				
			|||||||
package pp.battleship.game.singlemode;
 | 
					package pp.battleship.game.singlemode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pp.battleship.game.client.BattleshipClient;
 | 
					import pp.battleship.game.client.BattleshipClient;
 | 
				
			||||||
 | 
					import pp.battleship.message.client.AnimationFinishedMessage;
 | 
				
			||||||
import pp.battleship.message.client.MapMessage;
 | 
					import pp.battleship.message.client.MapMessage;
 | 
				
			||||||
import pp.battleship.message.client.ShootMessage;
 | 
					import pp.battleship.message.client.ShootMessage;
 | 
				
			||||||
import pp.battleship.message.client.AnimationFinishedMessage;
 | 
					 | 
				
			||||||
import pp.battleship.message.server.EffectMessage;
 | 
					import pp.battleship.message.server.EffectMessage;
 | 
				
			||||||
import pp.battleship.message.server.GameDetails;
 | 
					import pp.battleship.message.server.GameDetails;
 | 
				
			||||||
import pp.battleship.message.server.ServerInterpreter;
 | 
					import pp.battleship.message.server.ServerInterpreter;
 | 
				
			||||||
@@ -72,6 +72,7 @@ class RobotClient implements ServerInterpreter {
 | 
				
			|||||||
     * Makes the RobotClient take a shot by sending a ShootMessage with the target position.
 | 
					     * Makes the RobotClient take a shot by sending a ShootMessage with the target position.
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private void robotShot() {
 | 
					    private void robotShot() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        connection.sendRobotMessage(new ShootMessage(getShotPosition()));
 | 
					        connection.sendRobotMessage(new ShootMessage(getShotPosition()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,8 +30,8 @@ public interface ClientInterpreter {
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Processes a received AnimationFinishedMessage.
 | 
					     * Processes a received AnimationFinishedMessage.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param animationFinishedMessage  the MapMessage to be processed
 | 
					     * @param msg  the MapMessage to be processed
 | 
				
			||||||
     * @param from the connection ID from which the message was received
 | 
					     * @param from the connection ID from which the message was received
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void received(AnimationFinishedMessage animationFinishedMessage, int from);
 | 
					    void received(AnimationFinishedMessage msg, int from);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -123,4 +123,3 @@ public class Shell implements Item {
 | 
				
			|||||||
        visitor.visit(this);
 | 
					        visitor.visit(this);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -78,6 +78,15 @@ public class ShipMap {
 | 
				
			|||||||
        addItem(ship);
 | 
					        addItem(ship);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Registers a shot on the map and triggers an item addition event.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param shell the shell to be registered on the map
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void add(Shell shell) {
 | 
				
			||||||
 | 
					        addItem(shell);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Registers a shot on the map, updates the state of the affected ship (if any),
 | 
					     * Registers a shot on the map, updates the state of the affected ship (if any),
 | 
				
			||||||
     * and triggers an item addition event.
 | 
					     * and triggers an item addition event.
 | 
				
			||||||
@@ -91,16 +100,6 @@ public class ShipMap {
 | 
				
			|||||||
        addItem(shot);
 | 
					        addItem(shot);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					 | 
				
			||||||
     * Registers a shot on the map and triggers an item addition event.
 | 
					 | 
				
			||||||
     *
 | 
					 | 
				
			||||||
     * @param shell the shell to be registered on the map
 | 
					 | 
				
			||||||
     */
 | 
					 | 
				
			||||||
    public void add(Shell shell) {
 | 
					 | 
				
			||||||
        addItem(shell);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Removes an item from the map and triggers an item removal event.
 | 
					     * Removes an item from the map and triggers an item removal event.
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,14 +28,12 @@ public interface Visitor<T> {
 | 
				
			|||||||
     * @return the result of visiting the Battleship element
 | 
					     * @return the result of visiting the Battleship element
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    T visit(Battleship ship);
 | 
					    T visit(Battleship ship);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Visits a Shell element
 | 
					     * Visits a Shell element
 | 
				
			||||||
     * 
 | 
					     * 
 | 
				
			||||||
     * @param shell the Shell element to visit
 | 
					     * @param shell the Shell element to visit
 | 
				
			||||||
     * @return the result of visitung the Battleship element
 | 
					     * @return the result of visitung the Battleship element
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 | 
					 | 
				
			||||||
    T visit(Shell shell);
 | 
					    T visit(Shell shell);
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,9 +27,8 @@ public interface VoidVisitor {
 | 
				
			|||||||
    void visit(Battleship ship);
 | 
					    void visit(Battleship ship);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * * Visits a Shell element
 | 
					     * Visits a Shell element
 | 
				
			||||||
     * @param shell the Shell element to visit
 | 
					     * @param shell the Shell element to visit
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void visit(Shell shell);
 | 
					    void visit(Shell shell);
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,5 +27,4 @@ public enum Sound {
 | 
				
			|||||||
     * Sound of a shell flying
 | 
					     * Sound of a shell flying
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    SHELL_FLYING
 | 
					    SHELL_FLYING
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,12 +25,11 @@ button.cancel=Cancel
 | 
				
			|||||||
server.dialog=Server
 | 
					server.dialog=Server
 | 
				
			||||||
host.name=Host
 | 
					host.name=Host
 | 
				
			||||||
port.number=Port
 | 
					port.number=Port
 | 
				
			||||||
server.host= Self-Host Game
 | 
					 | 
				
			||||||
wait.its.not.your.turn=Wait, it's not your turn!!
 | 
					wait.its.not.your.turn=Wait, it's not your turn!!
 | 
				
			||||||
menu.quit=Quit game
 | 
					menu.quit=Quit game
 | 
				
			||||||
menu.return-to-game=Return to game
 | 
					menu.return-to-game=Return to game
 | 
				
			||||||
menu.sound-enabled=Sound switched on
 | 
					menu.sound-enabled=Sound switched on
 | 
				
			||||||
menu.background-sound-enabled=Music switched on
 | 
					menu.background-sound-enabled=Background music switched on
 | 
				
			||||||
menu.map.load=Load map from file...
 | 
					menu.map.load=Load map from file...
 | 
				
			||||||
menu.map.save=Save map in file...
 | 
					menu.map.save=Save map in file...
 | 
				
			||||||
label.file=File:
 | 
					label.file=File:
 | 
				
			||||||
@@ -39,3 +38,4 @@ dialog.error=Error
 | 
				
			|||||||
dialog.question=Question
 | 
					dialog.question=Question
 | 
				
			||||||
port.must.be.integer=Port must be an integer number
 | 
					port.must.be.integer=Port must be an integer number
 | 
				
			||||||
map.doesnt.fit=The map doesn't fit to this game
 | 
					map.doesnt.fit=The map doesn't fit to this game
 | 
				
			||||||
 | 
					client.server-start=Start server
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,12 +25,11 @@ button.cancel=Abbruch
 | 
				
			|||||||
server.dialog=Server
 | 
					server.dialog=Server
 | 
				
			||||||
host.name=Host
 | 
					host.name=Host
 | 
				
			||||||
port.number=Port
 | 
					port.number=Port
 | 
				
			||||||
server.host= Spiel selbst hosten
 | 
					 | 
				
			||||||
wait.its.not.your.turn=Warte, Du bist nicht dran!!
 | 
					wait.its.not.your.turn=Warte, Du bist nicht dran!!
 | 
				
			||||||
menu.quit=Spiel beenden
 | 
					menu.quit=Spiel beenden
 | 
				
			||||||
menu.return-to-game=Zurück zum Spiel
 | 
					menu.return-to-game=Zurück zum Spiel
 | 
				
			||||||
menu.sound-enabled=Sound eingeschaltet
 | 
					menu.sound-enabled=Sound eingeschaltet
 | 
				
			||||||
menu.background-sound-enabled=Musik eingeschaltet
 | 
					menu.background-sound-enabled=Hintergrundmusik eingeschaltet
 | 
				
			||||||
menu.map.load=Karte von Datei laden...
 | 
					menu.map.load=Karte von Datei laden...
 | 
				
			||||||
menu.map.save=Karte in Datei speichern...
 | 
					menu.map.save=Karte in Datei speichern...
 | 
				
			||||||
label.file=Datei:
 | 
					label.file=Datei:
 | 
				
			||||||
@@ -39,3 +38,4 @@ dialog.error=Fehler
 | 
				
			|||||||
dialog.question=Frage
 | 
					dialog.question=Frage
 | 
				
			||||||
port.must.be.integer=Der Port muss eine ganze Zahl sein
 | 
					port.must.be.integer=Der Port muss eine ganze Zahl sein
 | 
				
			||||||
map.doesnt.fit=Diese Karte passt nicht zu diesem Spiel
 | 
					map.doesnt.fit=Diese Karte passt nicht zu diesem Spiel
 | 
				
			||||||
 | 
					client.server-start=Server starten
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,10 +18,10 @@ import pp.battleship.BattleshipConfig;
 | 
				
			|||||||
import pp.battleship.game.server.Player;
 | 
					import pp.battleship.game.server.Player;
 | 
				
			||||||
import pp.battleship.game.server.ServerGameLogic;
 | 
					import pp.battleship.game.server.ServerGameLogic;
 | 
				
			||||||
import pp.battleship.game.server.ServerSender;
 | 
					import pp.battleship.game.server.ServerSender;
 | 
				
			||||||
 | 
					import pp.battleship.message.client.AnimationFinishedMessage;
 | 
				
			||||||
import pp.battleship.message.client.ClientMessage;
 | 
					import pp.battleship.message.client.ClientMessage;
 | 
				
			||||||
import pp.battleship.message.client.MapMessage;
 | 
					import pp.battleship.message.client.MapMessage;
 | 
				
			||||||
import pp.battleship.message.client.ShootMessage;
 | 
					import pp.battleship.message.client.ShootMessage;
 | 
				
			||||||
import pp.battleship.message.client.AnimationFinishedMessage;
 | 
					 | 
				
			||||||
import pp.battleship.message.server.EffectMessage;
 | 
					import pp.battleship.message.server.EffectMessage;
 | 
				
			||||||
import pp.battleship.message.server.GameDetails;
 | 
					import pp.battleship.message.server.GameDetails;
 | 
				
			||||||
import pp.battleship.message.server.ServerMessage;
 | 
					import pp.battleship.message.server.ServerMessage;
 | 
				
			||||||
@@ -81,9 +81,8 @@ public class BattleshipServer implements MessageListener<HostedConnection>, Conn
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public void run() {
 | 
					    public void run() {
 | 
				
			||||||
        startServer();
 | 
					        startServer();
 | 
				
			||||||
        while (true) {
 | 
					        while (true)
 | 
				
			||||||
            processNextMessage();
 | 
					            processNextMessage();
 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void startServer() {
 | 
					    private void startServer() {
 | 
				
			||||||
@@ -121,7 +120,6 @@ public class BattleshipServer implements MessageListener<HostedConnection>, Conn
 | 
				
			|||||||
        Serializer.registerClass(Battleship.class);
 | 
					        Serializer.registerClass(Battleship.class);
 | 
				
			||||||
        Serializer.registerClass(IntPoint.class);
 | 
					        Serializer.registerClass(IntPoint.class);
 | 
				
			||||||
        Serializer.registerClass(Shot.class);
 | 
					        Serializer.registerClass(Shot.class);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void registerListeners() {
 | 
					    private void registerListeners() {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -111,7 +111,7 @@ public class DialogManager {
 | 
				
			|||||||
     *
 | 
					     *
 | 
				
			||||||
     * @param dialog the dialog to open
 | 
					     * @param dialog the dialog to open
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void open(Dialog dialog) {
 | 
					    public void open(Dialog dialog) {
 | 
				
			||||||
        dialogStack.push(dialog);
 | 
					        dialogStack.push(dialog);
 | 
				
			||||||
        dialog.update();
 | 
					        dialog.update();
 | 
				
			||||||
        app.getGuiNode().attachChild(dialog);
 | 
					        app.getGuiNode().attachChild(dialog);
 | 
				
			||||||
@@ -133,7 +133,7 @@ public class DialogManager {
 | 
				
			|||||||
     * @param dialog the dialog to close
 | 
					     * @param dialog the dialog to close
 | 
				
			||||||
     * @throws IllegalArgumentException if the specified dialog is not the top dialog
 | 
					     * @throws IllegalArgumentException if the specified dialog is not the top dialog
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    void close(Dialog dialog) {
 | 
					    public void close(Dialog dialog) {
 | 
				
			||||||
        if (!isTop(dialog))
 | 
					        if (!isTop(dialog))
 | 
				
			||||||
            throw new IllegalArgumentException(dialog + " is not the top dialog");
 | 
					            throw new IllegalArgumentException(dialog + " is not the top dialog");
 | 
				
			||||||
        dialogStack.pop();
 | 
					        dialogStack.pop();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,8 @@
 | 
				
			|||||||
// Styling of Lemur components
 | 
					// Styling of Lemur components
 | 
				
			||||||
// For documentation, see
 | 
					// For documentation, see:
 | 
				
			||||||
// https://github.com/jMonkeyEngine-Contributions/Lemur/wiki/Styling
 | 
					// https://github.com/jMonkeyEngine-Contributions/Lemur/wiki/Styling
 | 
				
			||||||
 | 
					import com.simsilica.lemur.*
 | 
				
			||||||
 | 
					import com.simsilica.lemur.component.QuadBackgroundComponent
 | 
				
			||||||
import com.simsilica.lemur.Button
 | 
					import com.simsilica.lemur.Button
 | 
				
			||||||
import com.simsilica.lemur.Button.ButtonAction
 | 
					import com.simsilica.lemur.Button.ButtonAction
 | 
				
			||||||
import com.simsilica.lemur.Command
 | 
					import com.simsilica.lemur.Command
 | 
				
			||||||
@@ -10,14 +11,19 @@ import com.simsilica.lemur.Insets3f
 | 
				
			|||||||
import com.simsilica.lemur.component.QuadBackgroundComponent
 | 
					import com.simsilica.lemur.component.QuadBackgroundComponent
 | 
				
			||||||
import com.simsilica.lemur.component.TbtQuadBackgroundComponent
 | 
					import com.simsilica.lemur.component.TbtQuadBackgroundComponent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def bgColor = color(0.25, 0.5, 0.5, 1)
 | 
					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 buttonDisabledColor = color(0.8, 0.9, 1, 0.2)
 | 
				
			||||||
def buttonBgColor = color(0, 0.75, 0.75, 1)
 | 
					def buttonBgColor = color(1, 1, 1, 1)
 | 
				
			||||||
def sliderColor = color(0.6, 0.8, 0.8, 1)
 | 
					def sliderColor = color(0.6, 0.8, 0.8, 1)
 | 
				
			||||||
def sliderBgColor = color(0.5, 0.75, 0.75, 1)
 | 
					def sliderBgColor = color(0.5, 0.75, 0.75, 1)
 | 
				
			||||||
def gradientColor = color(0.5, 0.75, 0.85, 0.5)
 | 
					def gradientColor = color(0.5, 0.75, 0.85, 0.5)
 | 
				
			||||||
def tabbuttonEnabledColor = color(0.4, 0.45, 0.5, 1)
 | 
					def tabbuttonEnabledColor = color(0.4, 0.45, 0.5, 1)
 | 
				
			||||||
 | 
					def solidWhiteBackground = new QuadBackgroundComponent(color(1, 1, 1, 1)) // Solid white
 | 
				
			||||||
 | 
					def greyBackground = color(0.8, 0.8, 0.8, 1)  // Grey background color
 | 
				
			||||||
 | 
					def redBorderColor = color(1, 0, 0, 1)        // Red border color
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def gradient = TbtQuadBackgroundComponent.create(
 | 
					def gradient = TbtQuadBackgroundComponent.create(
 | 
				
			||||||
        texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png",
 | 
					        texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png",
 | 
				
			||||||
@@ -27,7 +33,14 @@ def gradient = TbtQuadBackgroundComponent.create(
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
def doubleGradient = new QuadBackgroundComponent(gradientColor)
 | 
					def doubleGradient = new QuadBackgroundComponent(gradientColor)
 | 
				
			||||||
doubleGradient.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
 | 
					doubleGradient.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
 | 
				
			||||||
                                 generateMips: false)
 | 
					        generateMips: false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
selector("pp") {
 | 
					selector("pp") {
 | 
				
			||||||
    font = font("Interface/Fonts/Metropolis/Metropolis-Regular-32.fnt")
 | 
					    font = font("Interface/Fonts/Metropolis/Metropolis-Regular-32.fnt")
 | 
				
			||||||
@@ -46,10 +59,26 @@ selector("header", "pp") {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
selector("container", "pp") {
 | 
					selector("container", "pp") {
 | 
				
			||||||
    background = gradient.clone()
 | 
					    background = solidWhiteBackground.clone()
 | 
				
			||||||
    background.setColor(bgColor)
 | 
					    background.setColor(bgColor)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					selector("toolbar") {
 | 
				
			||||||
 | 
					    // Set the grey background
 | 
				
			||||||
 | 
					    background = new QuadBackgroundComponent(greyBackground)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Add a red border using a TbtQuadBackgroundComponent
 | 
				
			||||||
 | 
					    def redBorder = TbtQuadBackgroundComponent.create(
 | 
				
			||||||
 | 
					            texture(name: "/com/simsilica/lemur/icons/bordered-gradient.png",
 | 
				
			||||||
 | 
					                    generateMips: false),
 | 
				
			||||||
 | 
					            1, 1, 1, 1, 1,
 | 
				
			||||||
 | 
					            1f, false)
 | 
				
			||||||
 | 
					    redBorder.color = redBorderColor
 | 
				
			||||||
 | 
					    background = greyBackground
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Optional: Set padding inside the toolbar
 | 
				
			||||||
 | 
					    insets = new Insets3f(10, 10, 10, 10)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
selector("slider", "pp") {
 | 
					selector("slider", "pp") {
 | 
				
			||||||
    background = gradient.clone()
 | 
					    background = gradient.clone()
 | 
				
			||||||
    background.setColor(bgColor)
 | 
					    background.setColor(bgColor)
 | 
				
			||||||
@@ -115,7 +144,7 @@ selector("title", "pp") {
 | 
				
			|||||||
    shadowOffset = vec3(2, -2, -1)
 | 
					    shadowOffset = vec3(2, -2, -1)
 | 
				
			||||||
    background = new QuadBackgroundComponent(color(0.5, 0.75, 0.85, 1))
 | 
					    background = new QuadBackgroundComponent(color(0.5, 0.75, 0.85, 1))
 | 
				
			||||||
    background.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
 | 
					    background.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
 | 
				
			||||||
                                 generateMips: false)
 | 
					            generateMips: false)
 | 
				
			||||||
    insets = new Insets3f(2, 2, 2, 2)
 | 
					    insets = new Insets3f(2, 2, 2, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buttonCommands = stdButtonCommands
 | 
					    buttonCommands = stdButtonCommands
 | 
				
			||||||
@@ -123,11 +152,14 @@ selector("title", "pp") {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
selector("button", "pp") {
 | 
					selector("button", "pp") {
 | 
				
			||||||
    background = gradient.clone()
 | 
					    def outerBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)) // Orange border
 | 
				
			||||||
    color = buttonEnabledColor
 | 
					    def innerBackground = new QuadBackgroundComponent(buttonBgColor) // Inner button background
 | 
				
			||||||
    background.setColor(buttonBgColor)
 | 
					 | 
				
			||||||
    insets = new Insets3f(2, 2, 2, 2)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Apply the outer border as the main background
 | 
				
			||||||
 | 
					    background = outerBackground
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Use insets to create a margin/padding effect for the inner background
 | 
				
			||||||
 | 
					    insets = new Insets3f(3, 3, 3, 3) // Adjust the border thickness
 | 
				
			||||||
    buttonCommands = stdButtonCommands
 | 
					    buttonCommands = stdButtonCommands
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -176,6 +208,7 @@ selector("slider.down.button", "pp") {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
selector("checkbox", "pp") {
 | 
					selector("checkbox", "pp") {
 | 
				
			||||||
    color = buttonEnabledColor
 | 
					    color = buttonEnabledColor
 | 
				
			||||||
 | 
					    fontSize = 20
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
selector("rollup", "pp") {
 | 
					selector("rollup", "pp") {
 | 
				
			||||||
@@ -192,10 +225,20 @@ selector("tabbedPanel.container", "pp") {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
selector("tab.button", "pp") {
 | 
					selector("tab.button", "pp") {
 | 
				
			||||||
    background = gradient.clone()
 | 
					    background = solidWhiteBackground.clone()
 | 
				
			||||||
    background.setColor(bgColor)
 | 
					    background.setColor(bgColor)
 | 
				
			||||||
    color = tabbuttonEnabledColor
 | 
					    color = tabbuttonEnabledColor
 | 
				
			||||||
    insets = new Insets3f(4, 2, 0, 2)
 | 
					    insets = new Insets3f(4, 2, 0, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    buttonCommands = stdButtonCommands
 | 
					    buttonCommands = stdButtonCommands
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					selector("settings-title", "pp") {
 | 
				
			||||||
 | 
					    fontSize = 48 // Set font size
 | 
				
			||||||
 | 
					    background = new QuadBackgroundComponent(color(0.4157f, 0.4235f, 0.4392f, 1.0f)) // Grey background
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					selector("menu-button", "pp") {
 | 
				
			||||||
 | 
					    fontSize = 40 // Set font size
 | 
				
			||||||
 | 
					    background = new QuadBackgroundComponent(color(0.4157f, 0.4235f, 0.4392f, 1.0f)) // Grey background
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										23
									
								
								Projekte/monopoly/client/build.gradle
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,23 @@
 | 
				
			|||||||
 | 
					plugins {
 | 
				
			||||||
 | 
					    id 'buildlogic.jme-application-conventions'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					description = 'Monopoly Client'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					dependencies {
 | 
				
			||||||
 | 
					    implementation project(":jme-common")
 | 
				
			||||||
 | 
					    implementation project(":monopoly:model")
 | 
				
			||||||
 | 
					    implementation project(":monopoly:server")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    implementation libs.jme3.desktop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    runtimeOnly libs.jme3.awt.dialogs
 | 
				
			||||||
 | 
					    runtimeOnly libs.jme3.plugins
 | 
				
			||||||
 | 
					    runtimeOnly libs.jme3.jogg
 | 
				
			||||||
 | 
					    runtimeOnly libs.jme3.testdata
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					application {
 | 
				
			||||||
 | 
					    mainClass = 'pp.monopoly.client.MonopolyApp'
 | 
				
			||||||
 | 
					    applicationName = 'monopoly'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										73
									
								
								Projekte/monopoly/client/client.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					########################################
 | 
				
			||||||
 | 
					## Programming project code
 | 
				
			||||||
 | 
					## UniBw M, 2022, 2023, 2024
 | 
				
			||||||
 | 
					## www.unibw.de/inf2
 | 
				
			||||||
 | 
					## (c) Mark Minas (mark.minas@unibw.de)
 | 
				
			||||||
 | 
					########################################
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Battleship client configuration
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Specifies the map used by the opponent in single mode.
 | 
				
			||||||
 | 
					# Single mode is activated if this property is set.
 | 
				
			||||||
 | 
					#map.opponent=maps/map2.json
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Specifies the map used by the player in single mode.
 | 
				
			||||||
 | 
					# The player must define their own map if this property is not set.
 | 
				
			||||||
 | 
					map.own=maps/map1.json
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Coordinates of the shots fired by the RobotClient in the order listed.
 | 
				
			||||||
 | 
					# Example:
 | 
				
			||||||
 | 
					#   2, 0,\
 | 
				
			||||||
 | 
					#   2, 1,\
 | 
				
			||||||
 | 
					#   2, 2,\
 | 
				
			||||||
 | 
					#   2, 3
 | 
				
			||||||
 | 
					#  defines four shots, namely at the coordinates
 | 
				
			||||||
 | 
					#  (x=2, y=0), (x=2, y=1), (x=2, y=2), and (x=2, y=3)
 | 
				
			||||||
 | 
					robot.targets=2, 0,\
 | 
				
			||||||
 | 
					              2, 1,\
 | 
				
			||||||
 | 
					              2, 2,\
 | 
				
			||||||
 | 
					              2, 3
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Delay in milliseconds between each shot fired by the RobotClient.
 | 
				
			||||||
 | 
					robot.delay=500
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# The dimensions of the game map used in single mode.
 | 
				
			||||||
 | 
					# 'map.width' defines the number of columns, and 'map.height' defines the number of rows.
 | 
				
			||||||
 | 
					map.width=10
 | 
				
			||||||
 | 
					map.height=10
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# The number of ships of each length available in single mode.
 | 
				
			||||||
 | 
					# The value is a comma-separated list where each element corresponds to the number of ships
 | 
				
			||||||
 | 
					# with a specific length. For example:
 | 
				
			||||||
 | 
					# ship.nums=4, 3, 2, 1
 | 
				
			||||||
 | 
					# This configuration means:
 | 
				
			||||||
 | 
					#   - 4 ships of length 1
 | 
				
			||||||
 | 
					#   - 3 ships of length 2
 | 
				
			||||||
 | 
					#   - 2 ships of length 3
 | 
				
			||||||
 | 
					#   - 1 ship of length 4
 | 
				
			||||||
 | 
					ship.nums=4, 3, 2, 1
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Screen settings
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Color of the text displayed at the top of the overlay.
 | 
				
			||||||
 | 
					# The format is (red, green, blue, alpha) where each value ranges from 0 to 1.
 | 
				
			||||||
 | 
					overlay.top.color=1, 1, 1, 1
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Application settings configuration
 | 
				
			||||||
 | 
					# Determines whether the settings window is shown at startup.
 | 
				
			||||||
 | 
					settings.show=false
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Specifies the width of the application window in pixels.
 | 
				
			||||||
 | 
					settings.resolution.width=1200
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Specifies the height of the application window in pixels.
 | 
				
			||||||
 | 
					settings.resolution.height=800
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Determines whether the application runs in full-screen mode.
 | 
				
			||||||
 | 
					settings.full-screen=false
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Enables or disables gamma correction to improve color accuracy.
 | 
				
			||||||
 | 
					settings.use-gamma-correction=true
 | 
				
			||||||
 | 
					#
 | 
				
			||||||
 | 
					# Indicates whether the statistics window is displayed during gameplay.
 | 
				
			||||||
 | 
					statistics.show=false
 | 
				
			||||||
							
								
								
									
										8
									
								
								Projekte/monopoly/client/logging.properties
									
									
									
									
									
										Normal file
									
								
							
							
						
						@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					handlers=java.util.logging.ConsoleHandler
 | 
				
			||||||
 | 
					.level=INFO
 | 
				
			||||||
 | 
					pp.level=FINE
 | 
				
			||||||
 | 
					com.jme3.network.level=INFO
 | 
				
			||||||
 | 
					;com.jme3.util.TangentBinormalGenerator.level=SEVERE
 | 
				
			||||||
 | 
					java.util.logging.ConsoleHandler.level=FINER
 | 
				
			||||||
 | 
					java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
 | 
				
			||||||
 | 
					;java.util.logging.SimpleFormatter.format=[%4$s %2$s] %5$s%n
 | 
				
			||||||
@@ -0,0 +1,122 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static pp.util.PreferencesUtils.getPreferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.lang.System.Logger;
 | 
				
			||||||
 | 
					import java.lang.System.Logger.Level;
 | 
				
			||||||
 | 
					import java.util.prefs.Preferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.app.Application;
 | 
				
			||||||
 | 
					import com.jme3.app.state.AbstractAppState;
 | 
				
			||||||
 | 
					import com.jme3.app.state.AppStateManager;
 | 
				
			||||||
 | 
					import com.jme3.asset.AssetLoadException;
 | 
				
			||||||
 | 
					import com.jme3.asset.AssetNotFoundException;
 | 
				
			||||||
 | 
					import com.jme3.audio.AudioData;
 | 
				
			||||||
 | 
					import com.jme3.audio.AudioNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Handles the background music beeing played. Is able to start and stop the music. Set the Volume of the Audio.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class GameMusic extends AbstractAppState{
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = System.getLogger(GameMusic.class.getName());
 | 
				
			||||||
 | 
					    private static final Preferences PREFERENCES = getPreferences(GameMusic.class);
 | 
				
			||||||
 | 
					    private static final String ENABLED_PREF = "enabled"; //NON-NLS
 | 
				
			||||||
 | 
					    private static final String VOLUME_PREF = "volume"; //NON-NLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private AudioNode music;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Checks if sound is enabled in the preferences.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {@code true} if sound is enabled, {@code false} otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean enabledInPreferences() {
 | 
				
			||||||
 | 
					        return PREFERENCES.getBoolean(ENABLED_PREF, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					     * Checks if sound is enabled in the preferences.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return float to which the volume is set
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static float volumeInPreferences() {
 | 
				
			||||||
 | 
					        return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initializes the sound effects for the game.
 | 
				
			||||||
 | 
					     * Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param stateManager The state manager
 | 
				
			||||||
 | 
					     * @param app          The application
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void initialize(AppStateManager stateManager, Application app) {
 | 
				
			||||||
 | 
					        super.initialize(stateManager, app);
 | 
				
			||||||
 | 
					        music = loadSound(app, "Sound/background.ogg");
 | 
				
			||||||
 | 
					        setVolume(volumeInPreferences());
 | 
				
			||||||
 | 
					        music.setLooping(true);
 | 
				
			||||||
 | 
					        if (isEnabled() && music != null) {
 | 
				
			||||||
 | 
					            music.play();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Loads a sound from the specified file.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param app  The application
 | 
				
			||||||
 | 
					     * @param name The name of the sound file.
 | 
				
			||||||
 | 
					     * @return The loaded AudioNode.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private AudioNode loadSound(Application app, String name) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
 | 
				
			||||||
 | 
					            sound.setLooping(false);
 | 
				
			||||||
 | 
					            sound.setPositional(false);
 | 
				
			||||||
 | 
					            return sound;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (AssetLoadException | AssetNotFoundException ex) {
 | 
				
			||||||
 | 
					            LOGGER.log(Level.ERROR, ex.getMessage(), ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the enabled state of this AppState.
 | 
				
			||||||
 | 
					     * Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)}
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param enabled {@code true} to enable the AppState, {@code false} to disable it.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setEnabled(boolean enabled) {
 | 
				
			||||||
 | 
					        if (isEnabled() == enabled) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (music != null) {
 | 
				
			||||||
 | 
					            if (enabled) {
 | 
				
			||||||
 | 
					                music.play();
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                music.stop();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					        super.setEnabled(enabled);
 | 
				
			||||||
 | 
					        LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); //NON-NLS
 | 
				
			||||||
 | 
					        PREFERENCES.putBoolean(ENABLED_PREF, enabled);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Toggles the game sound on or off.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void toggleSound() {
 | 
				
			||||||
 | 
					        setEnabled(!isEnabled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the volume of music
 | 
				
			||||||
 | 
					     * @param vol the volume to which the music should be set
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void setVolume(float vol){
 | 
				
			||||||
 | 
					        music.setVolume(vol);
 | 
				
			||||||
 | 
					        PREFERENCES.putFloat(VOLUME_PREF, vol);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,213 @@
 | 
				
			|||||||
 | 
					////////////////////////////////////////
 | 
				
			||||||
 | 
					// Programming project code
 | 
				
			||||||
 | 
					// UniBw M, 2022, 2023, 2024
 | 
				
			||||||
 | 
					// www.unibw.de/inf2
 | 
				
			||||||
 | 
					// (c) Mark Minas (mark.minas@unibw.de)
 | 
				
			||||||
 | 
					////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pp.monopoly.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.lang.System.Logger;
 | 
				
			||||||
 | 
					import java.lang.System.Logger.Level;
 | 
				
			||||||
 | 
					import java.util.prefs.Preferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.app.Application;
 | 
				
			||||||
 | 
					import com.jme3.app.state.AbstractAppState;
 | 
				
			||||||
 | 
					import com.jme3.app.state.AppStateManager;
 | 
				
			||||||
 | 
					import com.jme3.asset.AssetLoadException;
 | 
				
			||||||
 | 
					import com.jme3.asset.AssetNotFoundException;
 | 
				
			||||||
 | 
					import com.jme3.audio.AudioData;
 | 
				
			||||||
 | 
					import com.jme3.audio.AudioNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.monopoly.notification.GameEventListener;
 | 
				
			||||||
 | 
					import pp.monopoly.notification.SoundEvent;
 | 
				
			||||||
 | 
					import static pp.util.PreferencesUtils.getPreferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * An application state that plays sounds.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class GameSound extends AbstractAppState implements GameEventListener {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = System.getLogger(GameSound.class.getName());
 | 
				
			||||||
 | 
					    private static final Preferences PREFERENCES = getPreferences(GameSound.class);
 | 
				
			||||||
 | 
					    private static final String ENABLED_PREF = "enabled"; //NON-NLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private AudioNode passStartSound;
 | 
				
			||||||
 | 
					    private AudioNode eventCardSound;
 | 
				
			||||||
 | 
					    private AudioNode gulagSound;
 | 
				
			||||||
 | 
					    private AudioNode diceRollSound;
 | 
				
			||||||
 | 
					    private AudioNode moneyCollectSound;
 | 
				
			||||||
 | 
					    private AudioNode moneyLostSound;
 | 
				
			||||||
 | 
					    private AudioNode tradeAcceptedSound;
 | 
				
			||||||
 | 
					    private AudioNode tradeRejectedSound;
 | 
				
			||||||
 | 
					    private AudioNode winnerSound;
 | 
				
			||||||
 | 
					    private AudioNode looserSound;
 | 
				
			||||||
 | 
					    private AudioNode buttonSound;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Checks if sound is enabled in the preferences.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {@code true} if sound is enabled, {@code false} otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static boolean enabledInPreferences() {
 | 
				
			||||||
 | 
					        return PREFERENCES.getBoolean(ENABLED_PREF, true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Toggles the game sound on or off.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void toggleSound() {
 | 
				
			||||||
 | 
					        setEnabled(!isEnabled());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the enabled state of this AppState.
 | 
				
			||||||
 | 
					     * Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)}
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param enabled {@code true} to enable the AppState, {@code false} to disable it.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setEnabled(boolean enabled) {
 | 
				
			||||||
 | 
					        if (isEnabled() == enabled) return;
 | 
				
			||||||
 | 
					        super.setEnabled(enabled);
 | 
				
			||||||
 | 
					        LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); //NON-NLS
 | 
				
			||||||
 | 
					        PREFERENCES.putBoolean(ENABLED_PREF, enabled);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initializes the sound effects for the game.
 | 
				
			||||||
 | 
					     * Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param stateManager The state manager
 | 
				
			||||||
 | 
					     * @param app          The application
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void initialize(AppStateManager stateManager, Application app) {
 | 
				
			||||||
 | 
					        super.initialize(stateManager, app);
 | 
				
			||||||
 | 
					        passStartSound = loadSound(app, "Sound/Effects/passStart.ogg");
 | 
				
			||||||
 | 
					        eventCardSound = loadSound(app, "Sound/Effects/eventCard.ogg");
 | 
				
			||||||
 | 
					        gulagSound = loadSound(app, "Sound/Effects/gulag.ogg");
 | 
				
			||||||
 | 
					        diceRollSound = loadSound(app, "Sound/Effects/diceRoll.ogg");
 | 
				
			||||||
 | 
					        moneyCollectSound = loadSound(app, "Sound/Effects/moneyCollect.ogg");
 | 
				
			||||||
 | 
					        moneyLostSound = loadSound(app, "Sound/Effects/moneyLost.ogg");
 | 
				
			||||||
 | 
					        tradeAcceptedSound = loadSound(app, "Sound/Effects/tradeAccepted.ogg");
 | 
				
			||||||
 | 
					        tradeRejectedSound = loadSound(app, "Sound/Effects/tradeRejected.ogg");
 | 
				
			||||||
 | 
					        winnerSound = loadSound(app, "Sound/Effects/winner.ogg");
 | 
				
			||||||
 | 
					        looserSound = loadSound(app, "Sound/Effects/looser.ogg");
 | 
				
			||||||
 | 
					        buttonSound = loadSound(app, "Sound/Effects/button.ogg");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Loads a sound from the specified file.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param app  The application
 | 
				
			||||||
 | 
					     * @param name The name of the sound file.
 | 
				
			||||||
 | 
					     * @return The loaded AudioNode.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private AudioNode loadSound(Application app, String name) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
 | 
				
			||||||
 | 
					            sound.setLooping(false);
 | 
				
			||||||
 | 
					            sound.setPositional(false);
 | 
				
			||||||
 | 
					            return sound;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (AssetLoadException | AssetNotFoundException ex) {
 | 
				
			||||||
 | 
					            LOGGER.log(Level.ERROR, ex.getMessage(), ex);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the passStart sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void passStart() {
 | 
				
			||||||
 | 
					        if (isEnabled() && passStartSound != null)
 | 
				
			||||||
 | 
					            passStartSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the eventCard sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void eventCard() {
 | 
				
			||||||
 | 
					        if (isEnabled() && eventCardSound != null)
 | 
				
			||||||
 | 
					            eventCardSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the gulag sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void gulag() {
 | 
				
			||||||
 | 
					        if (isEnabled() && gulagSound != null)
 | 
				
			||||||
 | 
					            gulagSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the diceRoll sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void diceRoll() {
 | 
				
			||||||
 | 
					        if (isEnabled() && diceRollSound != null)
 | 
				
			||||||
 | 
					            diceRollSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the moneyCollect sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void moneyCollect() {
 | 
				
			||||||
 | 
					        if (isEnabled() && moneyCollectSound != null)
 | 
				
			||||||
 | 
					            moneyCollectSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the moneyLost sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void moneyLost() {
 | 
				
			||||||
 | 
					        if (isEnabled() && moneyLostSound != null)
 | 
				
			||||||
 | 
					            moneyLostSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the tradeAccepted sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void tradeAccepted() {
 | 
				
			||||||
 | 
					        if (isEnabled() && tradeAcceptedSound != null)
 | 
				
			||||||
 | 
					            tradeAcceptedSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the tradeRejected sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void tradeRejected() {
 | 
				
			||||||
 | 
					        if (isEnabled() && tradeRejectedSound != null)
 | 
				
			||||||
 | 
					            tradeRejectedSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the winner sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void winner() {
 | 
				
			||||||
 | 
					        if (isEnabled() && winnerSound != null)
 | 
				
			||||||
 | 
					            winnerSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the looser sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void looser() {
 | 
				
			||||||
 | 
					        if (isEnabled() && looserSound != null)
 | 
				
			||||||
 | 
					            looserSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Plays the button sound effect.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void button() {
 | 
				
			||||||
 | 
					        if (isEnabled() && buttonSound != null)
 | 
				
			||||||
 | 
					            buttonSound.playInstance();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void receivedEvent(SoundEvent event) {
 | 
				
			||||||
 | 
					        switch (event.sound()) {
 | 
				
			||||||
 | 
					            case PASS_START -> passStart();
 | 
				
			||||||
 | 
					            case EVENT_CARD -> eventCard();
 | 
				
			||||||
 | 
					            case GULAG -> eventCard();
 | 
				
			||||||
 | 
					            case DICE_ROLL -> eventCard();
 | 
				
			||||||
 | 
					            case MONEY_COLLECTED -> eventCard();
 | 
				
			||||||
 | 
					            case MONEY_LOST -> eventCard();
 | 
				
			||||||
 | 
					            case TRADE_ACCEPTED -> eventCard();
 | 
				
			||||||
 | 
					            case TRADE_REJECTED -> eventCard();
 | 
				
			||||||
 | 
					            case WINNER -> eventCard();
 | 
				
			||||||
 | 
					            case LOSER -> eventCard();
 | 
				
			||||||
 | 
					            case BUTTON -> eventCard();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,51 @@
 | 
				
			|||||||
 | 
					////////////////////////////////////////
 | 
				
			||||||
 | 
					// Programming project code
 | 
				
			||||||
 | 
					// UniBw M, 2022, 2023, 2024
 | 
				
			||||||
 | 
					// www.unibw.de/inf2
 | 
				
			||||||
 | 
					// (c) Mark Minas (mark.minas@unibw.de)
 | 
				
			||||||
 | 
					////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package pp.monopoly.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.prefs.Preferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.dialog.Dialog;
 | 
				
			||||||
 | 
					import static pp.util.PreferencesUtils.getPreferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * The Menu class represents the main menu in the Battleship game application.
 | 
				
			||||||
 | 
					 * It extends the Dialog class and provides functionalities for loading, saving,
 | 
				
			||||||
 | 
					 * returning to the game, and quitting the application.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class Menu extends Dialog {
 | 
				
			||||||
 | 
					    private static final Preferences PREFERENCES = getPreferences(Menu.class);
 | 
				
			||||||
 | 
					    private static final String LAST_PATH = "last.file.path";
 | 
				
			||||||
 | 
					    private final MonopolyApp app;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs the Menu dialog for the Battleship application.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param app the BattleshipApp instance
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Menu(MonopolyApp app) {
 | 
				
			||||||
 | 
					        super(app.getDialogManager());
 | 
				
			||||||
 | 
					        this.app = app;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the state of the load and save buttons based on the game logic.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void update() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * As an escape action, this method closes the menu if it is the top dialog.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void escape() {
 | 
				
			||||||
 | 
					        close();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,235 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.concurrent.ExecutorService;
 | 
				
			||||||
 | 
					import java.util.concurrent.Executors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.app.SimpleApplication;
 | 
				
			||||||
 | 
					import com.jme3.font.BitmapFont;
 | 
				
			||||||
 | 
					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.system.AppSettings;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.GuiGlobals;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Label;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.style.BaseStyles;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.dialog.DialogBuilder;
 | 
				
			||||||
 | 
					import pp.dialog.DialogManager;
 | 
				
			||||||
 | 
					import pp.graphics.Draw;
 | 
				
			||||||
 | 
					import pp.monopoly.client.gui.SettingsMenu;
 | 
				
			||||||
 | 
					import pp.monopoly.client.gui.TestWorld;
 | 
				
			||||||
 | 
					import pp.monopoly.game.client.ClientGameLogic;
 | 
				
			||||||
 | 
					import pp.monopoly.game.client.MonopolyClient;
 | 
				
			||||||
 | 
					import pp.monopoly.game.client.ServerConnection;
 | 
				
			||||||
 | 
					import pp.monopoly.notification.GameEventListener;
 | 
				
			||||||
 | 
					import pp.monopoly.notification.InfoTextEvent;
 | 
				
			||||||
 | 
					import pp.monopoly.server.MonopolyServer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MonopolyApp extends SimpleApplication implements MonopolyClient, GameEventListener {
 | 
				
			||||||
 | 
					    private BitmapText topText;
 | 
				
			||||||
 | 
					    private final ServerConnection serverConnection;
 | 
				
			||||||
 | 
					    private final ClientGameLogic logic;
 | 
				
			||||||
 | 
					    private final MonopolyAppConfig config;
 | 
				
			||||||
 | 
					    private final ActionListener escapeListener = (name, isPressed, tpf) -> handleEscape(isPressed);
 | 
				
			||||||
 | 
					    private final DialogManager dialogManager = new DialogManager(this);
 | 
				
			||||||
 | 
					    private final ExecutorService executor = Executors.newCachedThreadPool();
 | 
				
			||||||
 | 
					    private final Draw draw;
 | 
				
			||||||
 | 
					    private SettingsMenu settingsMenu;
 | 
				
			||||||
 | 
					    private TestWorld testWorld;
 | 
				
			||||||
 | 
					    private boolean isSettingsMenuOpen = false;
 | 
				
			||||||
 | 
					    private boolean inputBlocked = false;
 | 
				
			||||||
 | 
					    private MonopolyServer monopolyServer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MonopolyApp() {
 | 
				
			||||||
 | 
					        this.draw = new Draw(assetManager);
 | 
				
			||||||
 | 
					        config = new MonopolyAppConfig();
 | 
				
			||||||
 | 
					        serverConnection = new NetworkSupport(this);
 | 
				
			||||||
 | 
					        logic = new ClientGameLogic(serverConnection);
 | 
				
			||||||
 | 
					        logic.addListener(this);
 | 
				
			||||||
 | 
					        setShowSettings(config.getShowSettings());
 | 
				
			||||||
 | 
					        setSettings(makeSettings());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public MonopolyAppConfig getConfig() {
 | 
				
			||||||
 | 
					        return config;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public ClientGameLogic getGameLogic() {
 | 
				
			||||||
 | 
					        return logic;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private AppSettings makeSettings() {
 | 
				
			||||||
 | 
					        final AppSettings settings = new AppSettings(true);
 | 
				
			||||||
 | 
					        settings.setTitle("Monopoly Game");
 | 
				
			||||||
 | 
					        settings.setResolution(config.getResolutionWidth(), config.getResolutionHeight());
 | 
				
			||||||
 | 
					        settings.setFullscreen(config.fullScreen());
 | 
				
			||||||
 | 
					        return settings;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void simpleInitApp() {
 | 
				
			||||||
 | 
					        GuiGlobals.initialize(this);
 | 
				
			||||||
 | 
					        BaseStyles.loadStyleResources(STYLES_SCRIPT);
 | 
				
			||||||
 | 
					        GuiGlobals.getInstance().getStyles().setDefaultStyle("pp"); //NON-NLS
 | 
				
			||||||
 | 
					        final BitmapFont normalFont = assetManager.loadFont(FONT); //NON-NLS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        setupInput();
 | 
				
			||||||
 | 
					        setupGui();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Zeige das Startmenü
 | 
				
			||||||
 | 
					        StartMenu.createStartMenu(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void setupGui() {
 | 
				
			||||||
 | 
					        BitmapFont normalFont = assetManager.loadFont("Interface/Fonts/Default.fnt");
 | 
				
			||||||
 | 
					        topText = new BitmapText(normalFont);
 | 
				
			||||||
 | 
					        topText.setLocalTranslation(10, settings.getHeight() - 10, 0);
 | 
				
			||||||
 | 
					        guiNode.attachChild(topText);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void setupInput() {
 | 
				
			||||||
 | 
					        inputManager.deleteMapping(INPUT_MAPPING_EXIT);
 | 
				
			||||||
 | 
					        inputManager.setCursorVisible(true);
 | 
				
			||||||
 | 
					        inputManager.addMapping("ESC", new KeyTrigger(KeyInput.KEY_ESCAPE));
 | 
				
			||||||
 | 
					        inputManager.addListener(escapeListener, "ESC");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void handleEscape(boolean isPressed) {
 | 
				
			||||||
 | 
					        if (isPressed) {
 | 
				
			||||||
 | 
					            if (settingsMenu != null && isSettingsMenuOpen) {
 | 
				
			||||||
 | 
					                // Schließe das SettingsMenu
 | 
				
			||||||
 | 
					                System.out.println("Schließe SettingsMenu...");
 | 
				
			||||||
 | 
					                settingsMenu.close();
 | 
				
			||||||
 | 
					                settingsMenu = null;
 | 
				
			||||||
 | 
					                setSettingsMenuOpen(false);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Öffne das SettingsMenu
 | 
				
			||||||
 | 
					                System.out.println("Öffne SettingsMenu...");
 | 
				
			||||||
 | 
					                settingsMenu = new SettingsMenu(this);
 | 
				
			||||||
 | 
					                settingsMenu.open();
 | 
				
			||||||
 | 
					                setSettingsMenuOpen(true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void blockInputs() {
 | 
				
			||||||
 | 
					        if (!inputBlocked) {
 | 
				
			||||||
 | 
					            System.out.println("Blockiere Eingaben...");
 | 
				
			||||||
 | 
					            inputManager.setCursorVisible(true); // Cursor sichtbar machen
 | 
				
			||||||
 | 
					            inputManager.clearMappings();       // Alle Mappings entfernen
 | 
				
			||||||
 | 
					            inputBlocked = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void unblockInputs() {
 | 
				
			||||||
 | 
					        if (inputBlocked) {
 | 
				
			||||||
 | 
					            System.out.println("Aktiviere Eingaben...");
 | 
				
			||||||
 | 
					            setupInput(); // Standard-Eingaben neu registrieren
 | 
				
			||||||
 | 
					            inputBlocked = false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setInfoText(String text) {
 | 
				
			||||||
 | 
					        topText.setText(text);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void receivedEvent(InfoTextEvent event) {
 | 
				
			||||||
 | 
					        setInfoText(event.key());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void stop(boolean waitFor) {
 | 
				
			||||||
 | 
					        if (executor != null) executor.shutdownNow();
 | 
				
			||||||
 | 
					        serverConnection.disconnect();
 | 
				
			||||||
 | 
					        super.stop(waitFor);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public DialogManager getDialogManager() {
 | 
				
			||||||
 | 
					        return dialogManager;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Draw getDraw() {
 | 
				
			||||||
 | 
					        return draw;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ExecutorService getExecutor() {
 | 
				
			||||||
 | 
					        return executor;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void closeApp() {
 | 
				
			||||||
 | 
					        stop();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void errorDialog(String errorMessage) {
 | 
				
			||||||
 | 
					        DialogBuilder.simple(dialogManager)
 | 
				
			||||||
 | 
					                .setTitle("Fehler")
 | 
				
			||||||
 | 
					                .setText(errorMessage)
 | 
				
			||||||
 | 
					                .setOkButton("OK")
 | 
				
			||||||
 | 
					                .build()
 | 
				
			||||||
 | 
					                .open();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setSettingsMenuOpen(boolean isOpen) {
 | 
				
			||||||
 | 
					        this.isSettingsMenuOpen = isOpen;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void simpleUpdate(float tpf) {
 | 
				
			||||||
 | 
					        if (testWorld != null) {
 | 
				
			||||||
 | 
					            testWorld.update(tpf); // Aktualisiere die Kamera in der TestWorld
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void startTestWorld() {
 | 
				
			||||||
 | 
					        guiNode.detachAllChildren(); // Entferne GUI
 | 
				
			||||||
 | 
					        testWorld = new TestWorld(this); // Erstelle eine Instanz von TestWorld
 | 
				
			||||||
 | 
					        testWorld.initializeScene();     // Initialisiere die Szene
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void returnToMenu() {
 | 
				
			||||||
 | 
					        guiNode.detachAllChildren(); // Entferne die GUI
 | 
				
			||||||
 | 
					        StartMenu.createStartMenu(this); // Zeige das Startmenü erneut
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Startet den Server in einem neuen Thread.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void startServer() {
 | 
				
			||||||
 | 
					        new Thread(() -> {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                monopolyServer = new MonopolyServer(); // Erstelle Serverinstanz
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                e.printStackTrace();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }).start();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public MonopolyServer getMonopolyServer() {
 | 
				
			||||||
 | 
					        return monopolyServer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ServerConnection getServerConnection() {
 | 
				
			||||||
 | 
					        return serverConnection;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,190 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.math.ColorRGBA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.monopoly.game.client.MonopolyClientConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Provides access to the Monopoly application configuration.
 | 
				
			||||||
 | 
					 * Extends {@link MonopolyClientConfig} to include additional properties specific to the client,
 | 
				
			||||||
 | 
					 * particularly those related to screen settings and visual customization.
 | 
				
			||||||
 | 
					 * <p>
 | 
				
			||||||
 | 
					 * <b>Note:</b> Attributes of this class should not be marked as {@code final}
 | 
				
			||||||
 | 
					 * to ensure proper functionality when reading from a properties file.
 | 
				
			||||||
 | 
					 * </p>
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MonopolyAppConfig extends MonopolyClientConfig {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Converts a string value found in the properties file into an object of the specified type.
 | 
				
			||||||
 | 
					     * Extends the superclass method to support conversion to {@link ColorRGBA}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param value      the string value to be converted
 | 
				
			||||||
 | 
					     * @param targetType the target type into which the value string is converted
 | 
				
			||||||
 | 
					     * @return the converted object of the specified type
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected Object convertToType(String value, Class<?> targetType) {
 | 
				
			||||||
 | 
					        if (targetType == ColorRGBA.class)
 | 
				
			||||||
 | 
					            return makeColorRGBA(value);
 | 
				
			||||||
 | 
					        return super.convertToType(value, targetType);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Converts the specified string value to a corresponding {@link ColorRGBA} object.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param value the color in the format "red, green, blue, alpha" with all values in the range [0..1]
 | 
				
			||||||
 | 
					     * @return a {@link ColorRGBA} object representing the color
 | 
				
			||||||
 | 
					     * @throws IllegalArgumentException if the input string is not in the expected format
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static ColorRGBA makeColorRGBA(String value) {
 | 
				
			||||||
 | 
					        String[] split = value.split(",", -1);
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (split.length == 4)
 | 
				
			||||||
 | 
					                return new ColorRGBA(Float.parseFloat(split[0]),
 | 
				
			||||||
 | 
					                                     Float.parseFloat(split[1]),
 | 
				
			||||||
 | 
					                                     Float.parseFloat(split[2]),
 | 
				
			||||||
 | 
					                                     Float.parseFloat(split[3]));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (NumberFormatException e) {
 | 
				
			||||||
 | 
					            // deliberately left empty
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        throw new IllegalArgumentException(value + " should consist of exactly 4 numbers");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The width of the game view resolution in pixels.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Property("settings.resolution.width") //NON-NLS
 | 
				
			||||||
 | 
					    private int resolutionWidth = 1200;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The height of the game view resolution in pixels.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Property("settings.resolution.height") //NON-NLS
 | 
				
			||||||
 | 
					    private int resolutionHeight = 800;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Specifies whether the game should start in full-screen mode.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Property("settings.full-screen") //NON-NLS
 | 
				
			||||||
 | 
					    private boolean fullScreen = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Specifies whether gamma correction should be enabled.
 | 
				
			||||||
 | 
					     * If enabled, the main framebuffer is configured for sRGB colors,
 | 
				
			||||||
 | 
					     * and sRGB images are linearized.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * Requires a GPU that supports GL_ARB_framebuffer_sRGB; otherwise, this setting will be ignored.
 | 
				
			||||||
 | 
					     * </p>
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Property("settings.use-gamma-correction") //NON-NLS
 | 
				
			||||||
 | 
					    private boolean useGammaCorrection = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Specifies whether full resolution framebuffers should be used on Retina displays.
 | 
				
			||||||
 | 
					     * This setting is ignored on non-Retina platforms.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Property("settings.use-retina-framebuffer") //NON-NLS
 | 
				
			||||||
 | 
					    private boolean useRetinaFrameBuffer = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Specifies whether the settings window should be shown for configuring the game.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Property("settings.show") //NON-NLS
 | 
				
			||||||
 | 
					    private boolean showSettings = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Specifies whether the JME statistics window should be shown in the lower left corner of the screen.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Property("statistics.show") //NON-NLS
 | 
				
			||||||
 | 
					    private boolean showStatistics = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * The color of the top text during gameplay, represented as a {@link ColorRGBA} object.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Property("overlay.top.color") //NON-NLS
 | 
				
			||||||
 | 
					    private ColorRGBA topColor = ColorRGBA.White;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a default {@code MonopolyAppConfig} with predefined values.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public MonopolyAppConfig() {
 | 
				
			||||||
 | 
					        // Default constructor
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the width of the game view resolution in pixels.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the width of the game view resolution in pixels
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getResolutionWidth() {
 | 
				
			||||||
 | 
					        return resolutionWidth;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the height of the game view resolution in pixels.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the height of the game view resolution in pixels
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public int getResolutionHeight() {
 | 
				
			||||||
 | 
					        return resolutionHeight;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns whether the game should start in full-screen mode.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {@code true} if the game should start in full-screen mode; {@code false} otherwise
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean fullScreen() {
 | 
				
			||||||
 | 
					        return fullScreen;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns whether gamma correction is enabled.
 | 
				
			||||||
 | 
					     * If enabled, the main framebuffer is configured for sRGB colors,
 | 
				
			||||||
 | 
					     * and sRGB images are linearized.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {@code true} if gamma correction is enabled; {@code false} otherwise
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean useGammaCorrection() {
 | 
				
			||||||
 | 
					        return useGammaCorrection;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns whether full resolution framebuffers should be used on Retina displays.
 | 
				
			||||||
 | 
					     * This setting is ignored on non-Retina platforms.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {@code true} if full resolution framebuffers should be used on Retina displays; {@code false} otherwise
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean useRetinaFrameBuffer() {
 | 
				
			||||||
 | 
					        return useRetinaFrameBuffer;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns whether the settings window should be shown for configuring the game.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {@code true} if the settings window should be shown; {@code false} otherwise
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean getShowSettings() {
 | 
				
			||||||
 | 
					        return showSettings;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns whether the JME statistics window should be shown in the lower left corner of the screen.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return {@code true} if the statistics window should be shown; {@code false} otherwise
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public boolean getShowStatistics() {
 | 
				
			||||||
 | 
					        return showStatistics;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the color of the top text during gameplay as a {@link ColorRGBA} object.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the color of the top text during gameplay
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ColorRGBA getTopColor() {
 | 
				
			||||||
 | 
					        return topColor;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,84 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.app.Application;
 | 
				
			||||||
 | 
					import com.jme3.app.state.AbstractAppState;
 | 
				
			||||||
 | 
					import com.jme3.app.state.AppStateManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.monopoly.game.client.ClientGameLogic;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Abstract class representing a state in the Monopoly game.
 | 
				
			||||||
 | 
					 * Extends the AbstractAppState from jMonkeyEngine to manage state behavior.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public abstract class MonopolyAppState extends AbstractAppState {
 | 
				
			||||||
 | 
					    private MonopolyApp app;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates a new MonopolyAppState that is initially disabled.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected MonopolyAppState() {
 | 
				
			||||||
 | 
					        setEnabled(false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initializes the state manager and application.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param stateManager The state manager
 | 
				
			||||||
 | 
					     * @param application  The application instance
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void initialize(AppStateManager stateManager, Application application) {
 | 
				
			||||||
 | 
					        super.initialize(stateManager, application);
 | 
				
			||||||
 | 
					        this.app = (MonopolyApp) application;
 | 
				
			||||||
 | 
					        if (isEnabled()) {
 | 
				
			||||||
 | 
					            enableState();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the MonopolyApp instance associated with this MonopolyAppState.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return The MonopolyApp instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public MonopolyApp getApp() {
 | 
				
			||||||
 | 
					        return app;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the client game logic handler.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the client game logic handler
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public ClientGameLogic getGameLogic() {
 | 
				
			||||||
 | 
					        return app.getGameLogic();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets the enabled state of the MonopolyAppState.
 | 
				
			||||||
 | 
					     * If the new state is the same as the current state, the method returns.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param enabled The new enabled state.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void setEnabled(boolean enabled) {
 | 
				
			||||||
 | 
					        if (isEnabled() == enabled) return;
 | 
				
			||||||
 | 
					        super.setEnabled(enabled);
 | 
				
			||||||
 | 
					        if (app != null) {
 | 
				
			||||||
 | 
					            if (enabled) {
 | 
				
			||||||
 | 
					                enableState();
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                disableState();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called when the state is enabled. Override to define specific behavior.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected abstract void enableState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called when the state is disabled. Override to define specific behavior.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected abstract void disableState();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,146 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.lang.System.Logger;
 | 
				
			||||||
 | 
					import java.lang.System.Logger.Level;
 | 
				
			||||||
 | 
					import java.util.concurrent.ExecutionException;
 | 
				
			||||||
 | 
					import java.util.concurrent.Future;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Button;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Container;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Label;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.TextField;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.dialog.Dialog;
 | 
				
			||||||
 | 
					import pp.dialog.DialogBuilder;
 | 
				
			||||||
 | 
					import pp.dialog.SimpleDialog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Represents a dialog for setting up a network connection in the Monopoly game.
 | 
				
			||||||
 | 
					 * Allows users to specify the host and port for connecting to a game server.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class NetworkDialog extends SimpleDialog {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = System.getLogger(NetworkDialog.class.getName());
 | 
				
			||||||
 | 
					    private static final String LOCALHOST = "localhost";
 | 
				
			||||||
 | 
					    private static final String DEFAULT_PORT = "1234";
 | 
				
			||||||
 | 
					    private final NetworkSupport network;
 | 
				
			||||||
 | 
					    private final TextField host = new TextField(LOCALHOST);
 | 
				
			||||||
 | 
					    private final TextField port = new TextField(DEFAULT_PORT);
 | 
				
			||||||
 | 
					    private String hostname;
 | 
				
			||||||
 | 
					    private int portNumber;
 | 
				
			||||||
 | 
					    private Future<Object> connectionFuture;
 | 
				
			||||||
 | 
					    private Dialog progressDialog;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new NetworkDialog.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param network The NetworkSupport instance to be used for network operations.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    NetworkDialog(NetworkSupport network) {
 | 
				
			||||||
 | 
					        super(network.getApp().getDialogManager());
 | 
				
			||||||
 | 
					        this.network = network;
 | 
				
			||||||
 | 
					        initializeDialog();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initializes the dialog with input fields and connection buttons.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void initializeDialog() {
 | 
				
			||||||
 | 
					        final MonopolyApp app = network.getApp();
 | 
				
			||||||
 | 
					        Container inputContainer = new Container();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Titel und Eingabefelder für Host und Port
 | 
				
			||||||
 | 
					        inputContainer.addChild(new Label("Server-Adresse"));
 | 
				
			||||||
 | 
					        inputContainer.addChild(host);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        inputContainer.addChild(new Label("Port"));
 | 
				
			||||||
 | 
					        inputContainer.addChild(port);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Button connectButton = inputContainer.addChild(new Button("Verbinden"));
 | 
				
			||||||
 | 
					        connectButton.addClickCommands(source -> connect());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Button cancelButton = inputContainer.addChild(new Button("Abbrechen"));
 | 
				
			||||||
 | 
					        cancelButton.addClickCommands(source -> app.closeApp());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(inputContainer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initiates the connection attempt based on the entered host and port.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void connect() {
 | 
				
			||||||
 | 
					        LOGGER.log(Level.INFO, "Connecting to host={0}, port={1}", host, port);
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            hostname = host.getText().trim().isEmpty() ? LOCALHOST : host.getText();
 | 
				
			||||||
 | 
					            portNumber = Integer.parseInt(port.getText());
 | 
				
			||||||
 | 
					            openProgressDialog();
 | 
				
			||||||
 | 
					            connectionFuture = network.getApp().getExecutor().submit(this::initNetwork);
 | 
				
			||||||
 | 
					        } catch (NumberFormatException e) {
 | 
				
			||||||
 | 
					            network.getApp().errorDialog("Port muss eine Zahl sein.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Opens a progress dialog while connecting.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void openProgressDialog() {
 | 
				
			||||||
 | 
					        progressDialog = DialogBuilder.simple(network.getApp().getDialogManager())
 | 
				
			||||||
 | 
					                                      .setText("Verbinde zum Server...")
 | 
				
			||||||
 | 
					                                      .build();
 | 
				
			||||||
 | 
					        progressDialog.open();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Attempts to initialize the network connection.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @throws RuntimeException If an error occurs when creating the client.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Object initNetwork() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            network.initNetwork(hostname, portNumber);
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            throw new RuntimeException(e);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Updates the connection status and handles completion or failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void update(float delta) {
 | 
				
			||||||
 | 
					        if (connectionFuture != null && connectionFuture.isDone()) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                connectionFuture.get();
 | 
				
			||||||
 | 
					                onSuccess();
 | 
				
			||||||
 | 
					            } catch (ExecutionException e) {
 | 
				
			||||||
 | 
					                onFailure(e.getCause());
 | 
				
			||||||
 | 
					            } catch (InterruptedException e) {
 | 
				
			||||||
 | 
					                LOGGER.log(Level.WARNING, "Connection interrupted.", e);
 | 
				
			||||||
 | 
					                Thread.currentThread().interrupt();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handles a successful connection to the game server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void onSuccess() {
 | 
				
			||||||
 | 
					        connectionFuture = null;
 | 
				
			||||||
 | 
					        progressDialog.close();
 | 
				
			||||||
 | 
					        this.close();
 | 
				
			||||||
 | 
					        network.getApp().setInfoText("Warte auf einen Gegner...");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handles a failed connection attempt.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param e The cause of the failure.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void onFailure(Throwable e) {
 | 
				
			||||||
 | 
					        connectionFuture = null;
 | 
				
			||||||
 | 
					        progressDialog.close();
 | 
				
			||||||
 | 
					        network.getApp().errorDialog("Verbindung zum Server fehlgeschlagen.");
 | 
				
			||||||
 | 
					        network.getApp().setInfoText(e.getLocalizedMessage());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,144 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.lang.System.Logger;
 | 
				
			||||||
 | 
					import java.lang.System.Logger.Level;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.network.Client;
 | 
				
			||||||
 | 
					import com.jme3.network.ClientStateListener;
 | 
				
			||||||
 | 
					import com.jme3.network.Message;
 | 
				
			||||||
 | 
					import com.jme3.network.MessageListener;
 | 
				
			||||||
 | 
					import com.jme3.network.Network;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.monopoly.game.client.ServerConnection;
 | 
				
			||||||
 | 
					import pp.monopoly.message.client.ClientMessage;
 | 
				
			||||||
 | 
					import pp.monopoly.message.server.ServerMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Manages the network connection for the Monopoly application.
 | 
				
			||||||
 | 
					 * Handles connecting to and disconnecting from the server, and sending messages.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class NetworkSupport implements MessageListener<Client>, ClientStateListener, ServerConnection {
 | 
				
			||||||
 | 
					    private static final Logger LOGGER = System.getLogger(NetworkSupport.class.getName());
 | 
				
			||||||
 | 
					    private final MonopolyApp app;
 | 
				
			||||||
 | 
					    private Client client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a NetworkSupport instance for the Monopoly application.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param app The Monopoly application instance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public NetworkSupport(MonopolyApp app) {
 | 
				
			||||||
 | 
					        this.app = app;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the Monopoly application instance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Monopoly application instance
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    MonopolyApp getApp() {
 | 
				
			||||||
 | 
					        return app;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Checks if there is a connection to the game server.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return true if there is a connection to the game server, false otherwise.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean isConnected() {
 | 
				
			||||||
 | 
					        return client != null && client.isConnected();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Attempts to join the game if there is no connection yet.
 | 
				
			||||||
 | 
					     * Opens a dialog for the user to enter the host and port information.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void connect() {
 | 
				
			||||||
 | 
					        if (client == null) {
 | 
				
			||||||
 | 
					            new NetworkDialog(this).open();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Closes the client connection.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void disconnect() {
 | 
				
			||||||
 | 
					        if (client == null) return;
 | 
				
			||||||
 | 
					        client.close();
 | 
				
			||||||
 | 
					        client = null;
 | 
				
			||||||
 | 
					        LOGGER.log(Level.INFO, "Client connection closed.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initializes the network connection.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param host The server's address.
 | 
				
			||||||
 | 
					     * @param port The server's port.
 | 
				
			||||||
 | 
					     * @throws IOException If an I/O error occurs when creating the client.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void initNetwork(String host, int port) throws IOException {
 | 
				
			||||||
 | 
					        if (client != null) {
 | 
				
			||||||
 | 
					            throw new IllegalStateException("Already connected to the game server.");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        client = Network.connectToServer(host, port);
 | 
				
			||||||
 | 
					        client.start();
 | 
				
			||||||
 | 
					        client.addMessageListener(this);
 | 
				
			||||||
 | 
					        client.addClientStateListener(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called when a message is received from the server.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client  The client instance that received the message.
 | 
				
			||||||
 | 
					     * @param message The message received from the server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void messageReceived(Client client, Message message) {
 | 
				
			||||||
 | 
					        LOGGER.log(Level.INFO, "Message received from server: {0}", message);
 | 
				
			||||||
 | 
					        if (message instanceof ServerMessage serverMessage) {
 | 
				
			||||||
 | 
					            app.enqueue(() -> serverMessage.accept(app.getGameLogic()));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called when the client has successfully connected to the server.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client The client that connected to the server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void clientConnected(Client client) {
 | 
				
			||||||
 | 
					        LOGGER.log(Level.INFO, "Successfully connected to server: {0}", client);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called when the client is disconnected from the server.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param client         The client that was disconnected.
 | 
				
			||||||
 | 
					     * @param disconnectInfo Information about the disconnection.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void clientDisconnected(Client client, DisconnectInfo disconnectInfo) {
 | 
				
			||||||
 | 
					        LOGGER.log(Level.INFO, "Disconnected from server: {0}", disconnectInfo);
 | 
				
			||||||
 | 
					        this.client = null;
 | 
				
			||||||
 | 
					        app.enqueue(() -> app.setInfoText("Verbindung zum Server verloren."));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sends the specified message to the server.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param message The message to be sent to the server.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void send(ClientMessage message) {
 | 
				
			||||||
 | 
					        LOGGER.log(Level.INFO, "Sending message to server: {0}", message);
 | 
				
			||||||
 | 
					        if (client == null) {
 | 
				
			||||||
 | 
					            app.errorDialog("Verbindung zum Server verloren.");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            client.send(message);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,181 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.material.Material;
 | 
				
			||||||
 | 
					import com.jme3.math.Vector3f;
 | 
				
			||||||
 | 
					import com.jme3.scene.Geometry;
 | 
				
			||||||
 | 
					import com.jme3.scene.shape.Quad;
 | 
				
			||||||
 | 
					import com.jme3.texture.Texture;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Axis;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Button;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Container;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.HAlignment;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.component.QuadBackgroundComponent;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.component.SpringGridLayout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.dialog.Dialog;
 | 
				
			||||||
 | 
					import pp.monopoly.client.gui.CreateGameMenu;
 | 
				
			||||||
 | 
					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.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param app the MonopolyApp instance
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public StartMenu(MonopolyApp app) {
 | 
				
			||||||
 | 
					        super(app.getDialogManager());
 | 
				
			||||||
 | 
					        this.app = app;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates and displays the Start Menu with buttons for starting the game,
 | 
				
			||||||
 | 
					     * opening settings, and quitting the application.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static void createStartMenu(MonopolyApp app) {
 | 
				
			||||||
 | 
					        int screenWidth = app.getContext().getSettings().getWidth();
 | 
				
			||||||
 | 
					        int screenHeight = app.getContext().getSettings().getHeight();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Set up the background image
 | 
				
			||||||
 | 
					        Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
 | 
				
			||||||
 | 
					        Quad quad = new Quad(screenWidth, screenHeight);
 | 
				
			||||||
 | 
					        Geometry background = new Geometry("Background", quad);
 | 
				
			||||||
 | 
					        Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
 | 
				
			||||||
 | 
					        backgroundMaterial.setTexture("ColorMap", backgroundImage);
 | 
				
			||||||
 | 
					        background.setMaterial(backgroundMaterial);
 | 
				
			||||||
 | 
					        background.setLocalTranslation(0, 0, -1); // Ensure it is behind other GUI elements
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(background);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        createMonopolyLogo(app);
 | 
				
			||||||
 | 
					        createUnibwLogo(app);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Center container for title and play button
 | 
				
			||||||
 | 
					        Container centerMenu = new Container(new SpringGridLayout(Axis.Y, Axis.X));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Button startButton = new Button("Spielen");
 | 
				
			||||||
 | 
					        startButton.setPreferredSize(new Vector3f(190, 60, 0)); // Increase button size (width, height)
 | 
				
			||||||
 | 
					        startButton.setFontSize(40); // Set the font size for the button text
 | 
				
			||||||
 | 
					        startButton.setTextHAlignment(HAlignment.Center); // Center the text horizontally
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        startButton.addClickCommands(source -> startGame(app));
 | 
				
			||||||
 | 
					        centerMenu.addChild(startButton);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Position the center container in the middle of the screen
 | 
				
			||||||
 | 
					        centerMenu.setLocalTranslation(new Vector3f(screenWidth / 2f - centerMenu.getPreferredSize().x / 2f,
 | 
				
			||||||
 | 
					                                                    screenHeight / 2f - 280 + centerMenu.getPreferredSize().y / 2f,
 | 
				
			||||||
 | 
					                                                    0));
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(centerMenu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Lower-left container for "Spiel beenden" button
 | 
				
			||||||
 | 
					        Container lowerLeftMenu = new Container();
 | 
				
			||||||
 | 
					        lowerLeftMenu.setLocalTranslation(new Vector3f(100, 90, 0));
 | 
				
			||||||
 | 
					        Button quitButton = new Button("Spiel beenden");
 | 
				
			||||||
 | 
					        quitButton.setPreferredSize(new Vector3f(130, 40, 0)); // Increase button size slightly (width, height)
 | 
				
			||||||
 | 
					        quitButton.setFontSize(18);
 | 
				
			||||||
 | 
					        quitButton.addClickCommands(source -> quitGame());
 | 
				
			||||||
 | 
					        lowerLeftMenu.addChild(quitButton);
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(lowerLeftMenu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Lower-right container for "Einstellungen" button
 | 
				
			||||||
 | 
					        Container lowerRightMenu = new Container();
 | 
				
			||||||
 | 
					        lowerRightMenu.setLocalTranslation(new Vector3f(screenWidth - 200, 90, 0));
 | 
				
			||||||
 | 
					        Button settingsButton = new Button("Einstellungen");
 | 
				
			||||||
 | 
					        settingsButton.setPreferredSize(new Vector3f(130, 40, 0)); // Increase button size slightly (width, height)
 | 
				
			||||||
 | 
					        settingsButton.setFontSize(18); // Increase the font size for the text
 | 
				
			||||||
 | 
					        settingsButton.addClickCommands(source -> openSettings(app));
 | 
				
			||||||
 | 
					        lowerRightMenu.addChild(settingsButton);
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(lowerRightMenu);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates and positions the Monopoly logo container in the center of the screen.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static void createMonopolyLogo(MonopolyApp app) {
 | 
				
			||||||
 | 
					        int screenWidth = app.getContext().getSettings().getWidth();
 | 
				
			||||||
 | 
					        int screenHeight = app.getContext().getSettings().getHeight();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Load the Monopoly logo as a texture
 | 
				
			||||||
 | 
					        Texture logoTexture = app.getAssetManager().loadTexture("Pictures/logo-monopoly.png");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 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.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static void startGame(MonopolyApp app) {
 | 
				
			||||||
 | 
					        app.getGuiNode().detachAllChildren();
 | 
				
			||||||
 | 
					        new CreateGameMenu(app);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Opens the settings menu.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static void openSettings(MonopolyApp app) {
 | 
				
			||||||
 | 
					        app.getGuiNode().detachAllChildren();
 | 
				
			||||||
 | 
					        new SettingsMenu(app);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Quits the game application.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static void quitGame() {
 | 
				
			||||||
 | 
					        System.exit(0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.scene.Node;
 | 
				
			||||||
 | 
					import com.jme3.scene.Spatial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.monopoly.model.Board;
 | 
				
			||||||
 | 
					import pp.monopoly.model.Item;
 | 
				
			||||||
 | 
					import pp.monopoly.model.Visitor;
 | 
				
			||||||
 | 
					import pp.monopoly.notification.GameEventListener;
 | 
				
			||||||
 | 
					import pp.monopoly.notification.ItemAddedEvent;
 | 
				
			||||||
 | 
					import pp.monopoly.notification.ItemRemovedEvent;
 | 
				
			||||||
 | 
					import pp.view.ModelViewSynchronizer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Abstract base class for synchronizing the visual representation of a {@link Board} with its model state.
 | 
				
			||||||
 | 
					 * This class handles the addition and removal of items from the map, ensuring that changes in the model
 | 
				
			||||||
 | 
					 * are accurately reflected in the view.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					abstract class BoardSynchronizer extends ModelViewSynchronizer<Item> implements Visitor<Spatial>, GameEventListener {
 | 
				
			||||||
 | 
					    protected final Board board;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new BoardSynchronizer.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param board the game board to synchronize
 | 
				
			||||||
 | 
					     * @param root  the root node to which the view representations of the board items are attached
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected BoardSynchronizer(Board board, Node root) {
 | 
				
			||||||
 | 
					        super(root);
 | 
				
			||||||
 | 
					        this.board = board;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Translates a model item into its corresponding visual representation.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param item the item from the model to be translated
 | 
				
			||||||
 | 
					     * @return the visual representation of the item as a {@link Spatial}
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected Spatial translate(Item item) {
 | 
				
			||||||
 | 
					        return item.accept(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Adds the existing items from the board to the view during initialization.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected void addExisting() {
 | 
				
			||||||
 | 
					        board.getItems().forEach(this::add);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handles the event when an item is removed from the board.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param event the event indicating that an item has been removed from the board
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void receivedEvent(ItemRemovedEvent event) {
 | 
				
			||||||
 | 
					        if (board == event.getBoard()) {
 | 
				
			||||||
 | 
					            delete(event.getItem());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Handles the event when an item is added to the board.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param event the event indicating that an item has been added to the board
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void receivedEvent(ItemAddedEvent event) {
 | 
				
			||||||
 | 
					        if (board == event.getBoard()) {
 | 
				
			||||||
 | 
					            add(event.getItem());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,132 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.swing.JOptionPane;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.material.Material;
 | 
				
			||||||
 | 
					import com.jme3.math.Vector3f;
 | 
				
			||||||
 | 
					import com.jme3.network.Client;
 | 
				
			||||||
 | 
					import com.jme3.network.Network;
 | 
				
			||||||
 | 
					import com.jme3.scene.Geometry;
 | 
				
			||||||
 | 
					import com.jme3.scene.shape.Quad;
 | 
				
			||||||
 | 
					import com.jme3.texture.Texture;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Axis;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Button;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Container;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Label;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.TextField;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.component.SpringGridLayout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.monopoly.client.MonopolyApp;
 | 
				
			||||||
 | 
					import pp.monopoly.client.StartMenu;
 | 
				
			||||||
 | 
					import pp.monopoly.server.MonopolyServer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * CreateGameMenu class represents the menu for creating a new game.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class CreateGameMenu {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final MonopolyApp app;
 | 
				
			||||||
 | 
					    private final Container menuContainer;
 | 
				
			||||||
 | 
					    private Geometry background;
 | 
				
			||||||
 | 
					    private Label serverStatusLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public CreateGameMenu(MonopolyApp app) {
 | 
				
			||||||
 | 
					        this.app = app;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Hintergrundbild laden und hinzufügen
 | 
				
			||||||
 | 
					        addBackgroundImage();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Hauptcontainer für das Menü
 | 
				
			||||||
 | 
					        menuContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
 | 
				
			||||||
 | 
					        menuContainer.setPreferredSize(new Vector3f(600, 400, 0)); // Feste Größe des Containers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Titel
 | 
				
			||||||
 | 
					        Label title = menuContainer.addChild(new Label("Neues Spiel"));
 | 
				
			||||||
 | 
					        title.setFontSize(48);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Eingabefelder-Container
 | 
				
			||||||
 | 
					        Container inputContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X)));
 | 
				
			||||||
 | 
					        inputContainer.setPreferredSize(new Vector3f(200, 150, 0)); // Eingabefelder nicht ganz so breit
 | 
				
			||||||
 | 
					        inputContainer.setLocalTranslation(20, 0, 0); // Abstand vom Rand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        inputContainer.addChild(new Label("Server-Adresse:"));
 | 
				
			||||||
 | 
					        TextField playerNameField = inputContainer.addChild(new TextField("localhost"));
 | 
				
			||||||
 | 
					        playerNameField.setPreferredWidth(400); // Breite des Textfelds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        inputContainer.addChild(new Label("Port:"));
 | 
				
			||||||
 | 
					        TextField serverAddressField = inputContainer.addChild(new TextField("42069"));
 | 
				
			||||||
 | 
					        serverAddressField.setPreferredWidth(400); // Breite des Textfelds
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Button-Container
 | 
				
			||||||
 | 
					        Container buttonContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
 | 
				
			||||||
 | 
					        buttonContainer.setPreferredSize(new Vector3f(400, 50, 0));
 | 
				
			||||||
 | 
					        buttonContainer.setLocalTranslation(20, 0, 0); // Abstand vom Rand
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // "Abbrechen"-Button
 | 
				
			||||||
 | 
					        Button cancelButton = buttonContainer.addChild(new Button("Abbrechen"));
 | 
				
			||||||
 | 
					        cancelButton.setPreferredSize(new Vector3f(120, 40, 0));
 | 
				
			||||||
 | 
					        cancelButton.addClickCommands(source -> goBackToStartMenu());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // "Selber hosten"-Button
 | 
				
			||||||
 | 
					        Button hostButton = buttonContainer.addChild(new Button("Selber hosten"));
 | 
				
			||||||
 | 
					        hostButton.setPreferredSize(new Vector3f(120, 40, 0));
 | 
				
			||||||
 | 
					        hostButton.addClickCommands(source -> startServer());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // "Beitreten"-Button (vorerst funktionslos)
 | 
				
			||||||
 | 
					        Button joinButton = buttonContainer.addChild(new Button("Beitreten"));
 | 
				
			||||||
 | 
					        joinButton.setPreferredSize(new Vector3f(120, 40, 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Serverstatus-Label
 | 
				
			||||||
 | 
					        serverStatusLabel = menuContainer.addChild(new Label("Serverstatus: Noch nicht gestartet"));
 | 
				
			||||||
 | 
					        serverStatusLabel.setFontSize(24);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Zentrierung des Containers
 | 
				
			||||||
 | 
					        menuContainer.setLocalTranslation(
 | 
				
			||||||
 | 
					                (app.getCamera().getWidth() - menuContainer.getPreferredSize().x) / 2,
 | 
				
			||||||
 | 
					                (app.getCamera().getHeight() + menuContainer.getPreferredSize().y) / 2,
 | 
				
			||||||
 | 
					                1  // Höhere Z-Ebene für den Vordergrund
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.getInputManager().addMapping("OpenTestWorld", new com.jme3.input.controls.KeyTrigger(com.jme3.input.KeyInput.KEY_T));
 | 
				
			||||||
 | 
					        app.getInputManager().addListener(new com.jme3.input.controls.ActionListener() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onAction(String name, boolean isPressed, float tpf) {
 | 
				
			||||||
 | 
					                if (name.equals("OpenTestWorld") && isPressed) {
 | 
				
			||||||
 | 
					                    app.startTestWorld(); // Öffnet die TestWorld
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }, "OpenTestWorld");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(menuContainer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Lädt das Hintergrundbild und fügt es als geometrische Ebene hinzu.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void addBackgroundImage() {
 | 
				
			||||||
 | 
					        Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
 | 
				
			||||||
 | 
					        Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
 | 
				
			||||||
 | 
					        background = new Geometry("Background", quad);
 | 
				
			||||||
 | 
					        Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
 | 
				
			||||||
 | 
					        backgroundMaterial.setTexture("ColorMap", backgroundImage);
 | 
				
			||||||
 | 
					        background.setMaterial(backgroundMaterial);
 | 
				
			||||||
 | 
					        background.setLocalTranslation(0, 0, -1); // Hintergrundebene
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(background);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void startServer() {
 | 
				
			||||||
 | 
					        app.start();
 | 
				
			||||||
 | 
					        app.getServerConnection().connect();
 | 
				
			||||||
 | 
					        JOptionPane.showMessageDialog(null, "Server Started");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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ü
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.material.Material;
 | 
				
			||||||
 | 
					import com.jme3.material.RenderState.BlendMode;
 | 
				
			||||||
 | 
					import com.jme3.math.ColorRGBA;
 | 
				
			||||||
 | 
					import com.jme3.scene.Geometry;
 | 
				
			||||||
 | 
					import com.jme3.scene.Node;
 | 
				
			||||||
 | 
					import com.jme3.scene.Spatial.CullHint;
 | 
				
			||||||
 | 
					import com.jme3.scene.shape.Quad;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.monopoly.client.MonopolyApp;
 | 
				
			||||||
 | 
					import pp.monopoly.model.Board;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Represents the visual view of a {@link Board}, used to display the map structure and elements.
 | 
				
			||||||
 | 
					 * This class handles the graphical representation of the board, including background setup and grid lines.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class MapView {
 | 
				
			||||||
 | 
					    private static final float FIELD_SIZE = 40f;
 | 
				
			||||||
 | 
					    private static final float BACKGROUND_DEPTH = -4f;
 | 
				
			||||||
 | 
					    private static final ColorRGBA BACKGROUND_COLOR = new ColorRGBA(0, 0.05f, 0.05f, 0.5f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final MonopolyApp app;
 | 
				
			||||||
 | 
					    private final Node mapNode = new Node("map");
 | 
				
			||||||
 | 
					    private final Board board;
 | 
				
			||||||
 | 
					    private final MapViewSynchronizer synchronizer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new MapView for a given {@link Board} and {@link MonopolyApp}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param board the board to visualize
 | 
				
			||||||
 | 
					     * @param app   the main application instance
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    MapView(Board board, MonopolyApp app) {
 | 
				
			||||||
 | 
					        this.board = board;
 | 
				
			||||||
 | 
					        this.app = app;
 | 
				
			||||||
 | 
					        this.synchronizer = new MapViewSynchronizer(this);
 | 
				
			||||||
 | 
					        setupBackground();
 | 
				
			||||||
 | 
					        app.getGameLogic().addListener(synchronizer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Unregisters the {@link MapViewSynchronizer} from listening to board changes.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void unregister() {
 | 
				
			||||||
 | 
					        app.getGameLogic().removeListener(synchronizer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Sets up the background of the map view using a quad geometry.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void setupBackground() {
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
 | 
				
			||||||
 | 
					        mat.setColor("Color", BACKGROUND_COLOR);
 | 
				
			||||||
 | 
					        mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
 | 
				
			||||||
 | 
					        Geometry background = new Geometry("MapBackground", new Quad(board.getWidth() * FIELD_SIZE, board.getHeight() * FIELD_SIZE));
 | 
				
			||||||
 | 
					        background.setMaterial(mat);
 | 
				
			||||||
 | 
					        background.setLocalTranslation(0f, 1f, BACKGROUND_DEPTH);
 | 
				
			||||||
 | 
					        background.setCullHint(CullHint.Never);
 | 
				
			||||||
 | 
					        mapNode.attachChild(background);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Gets the root node containing all visual elements in this map view.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return the root node for the map view
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Node getNode() {
 | 
				
			||||||
 | 
					        return mapNode;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public Board getBoard() {
 | 
				
			||||||
 | 
					        return board;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.scene.Spatial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.monopoly.model.Figure;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Synchronizes the visual representation of the board with the game model.
 | 
				
			||||||
 | 
					 * Handles updates for items on the board.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					class MapViewSynchronizer extends BoardSynchronizer {
 | 
				
			||||||
 | 
					    private final MapView view;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Constructs a new MapViewSynchronizer for the given MapView.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param view the MapView to synchronize with the game model
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public MapViewSynchronizer(MapView view) {
 | 
				
			||||||
 | 
					        super(view.getBoard(), view.getNode());
 | 
				
			||||||
 | 
					        this.view = view;
 | 
				
			||||||
 | 
					        addExisting();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Enables the state by performing initial setup, such as adding any items to the view.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    protected void enableState() {
 | 
				
			||||||
 | 
					        // Platz für zusätzliche Initialisierungen
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Disables the state by clearing the view.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    protected void disableState() {
 | 
				
			||||||
 | 
					        view.getNode().detachAllChildren(); // Entfernt alle visuellen Elemente vom Knoten
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public Spatial visit(Figure figure) {
 | 
				
			||||||
 | 
					        return figure.accept(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.awt.BorderLayout;
 | 
				
			||||||
 | 
					import java.awt.FlowLayout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import javax.swing.JButton;
 | 
				
			||||||
 | 
					import javax.swing.JFrame;
 | 
				
			||||||
 | 
					import javax.swing.JLabel;
 | 
				
			||||||
 | 
					import javax.swing.JOptionPane;
 | 
				
			||||||
 | 
					import javax.swing.JPanel;
 | 
				
			||||||
 | 
					import javax.swing.JTextField;
 | 
				
			||||||
 | 
					import javax.swing.SwingUtilities;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ServerScreen {
 | 
				
			||||||
 | 
					    private JFrame frame;
 | 
				
			||||||
 | 
					    private JTextField inputField;
 | 
				
			||||||
 | 
					    private JLabel label;
 | 
				
			||||||
 | 
					    private JButton startButton;
 | 
				
			||||||
 | 
					    private JButton stopButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ServerScreen() {
 | 
				
			||||||
 | 
					        initialize();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void initialize() {
 | 
				
			||||||
 | 
					        // Erstelle das Hauptfenster
 | 
				
			||||||
 | 
					        frame = new JFrame("Server Placeholder"); // Setze den Titel
 | 
				
			||||||
 | 
					        frame.setSize(400, 200);
 | 
				
			||||||
 | 
					        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 | 
				
			||||||
 | 
					        frame.setLayout(new BorderLayout());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Eingabefeld und Label im oberen Bereich
 | 
				
			||||||
 | 
					        JPanel topPanel = new JPanel();
 | 
				
			||||||
 | 
					        topPanel.setLayout(new FlowLayout());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        label = new JLabel("Server-Port:");
 | 
				
			||||||
 | 
					        inputField = new JTextField("42069", 10);
 | 
				
			||||||
 | 
					        topPanel.add(label);
 | 
				
			||||||
 | 
					        topPanel.add(inputField);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Buttons im unteren Bereich
 | 
				
			||||||
 | 
					        JPanel bottomPanel = new JPanel();
 | 
				
			||||||
 | 
					        bottomPanel.setLayout(new FlowLayout());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        startButton = new JButton("Start Server");
 | 
				
			||||||
 | 
					        stopButton = new JButton("Stop Server");
 | 
				
			||||||
 | 
					        stopButton.setEnabled(false); // Stop-Button ist anfangs deaktiviert
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        bottomPanel.add(startButton);
 | 
				
			||||||
 | 
					        bottomPanel.add(stopButton);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Füge die Panels zum Hauptfenster hinzu
 | 
				
			||||||
 | 
					        frame.add(topPanel, BorderLayout.NORTH);
 | 
				
			||||||
 | 
					        frame.add(bottomPanel, BorderLayout.SOUTH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Aktion für Start-Button
 | 
				
			||||||
 | 
					        startButton.addActionListener(e -> startServer());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Aktion für Stop-Button
 | 
				
			||||||
 | 
					        stopButton.addActionListener(e -> stopServer());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Zeige das Fenster
 | 
				
			||||||
 | 
					        frame.setVisible(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void startServer() {
 | 
				
			||||||
 | 
					        String port = inputField.getText();
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            int portNumber = Integer.parseInt(port);
 | 
				
			||||||
 | 
					            // Server-Startlogik hier einfügen
 | 
				
			||||||
 | 
					            JOptionPane.showMessageDialog(frame, "Server gestartet auf Port " + portNumber);
 | 
				
			||||||
 | 
					            startButton.setEnabled(false); // Deaktiviere den Start-Button
 | 
				
			||||||
 | 
					            stopButton.setEnabled(true);  // Aktiviere den Stop-Button
 | 
				
			||||||
 | 
					        } catch (NumberFormatException e) {
 | 
				
			||||||
 | 
					            JOptionPane.showMessageDialog(frame, "Ungültiger Port: " + port, "Fehler", JOptionPane.ERROR_MESSAGE);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void stopServer() {
 | 
				
			||||||
 | 
					        // Server-Stoplogik hier einfügen
 | 
				
			||||||
 | 
					        JOptionPane.showMessageDialog(frame, "Server gestoppt.");
 | 
				
			||||||
 | 
					        startButton.setEnabled(true); // Aktiviere den Start-Button
 | 
				
			||||||
 | 
					        stopButton.setEnabled(false); // Deaktiviere den Stop-Button
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void main(String[] args) {
 | 
				
			||||||
 | 
					        SwingUtilities.invokeLater(ServerScreen::new);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,120 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.material.Material;
 | 
				
			||||||
 | 
					import com.jme3.material.RenderState.BlendMode;
 | 
				
			||||||
 | 
					import com.jme3.math.ColorRGBA;
 | 
				
			||||||
 | 
					import com.jme3.scene.Geometry;
 | 
				
			||||||
 | 
					import com.jme3.scene.shape.Quad;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Button;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Checkbox;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Container;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Label;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Slider;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.component.QuadBackgroundComponent;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.style.ElementId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.dialog.Dialog;
 | 
				
			||||||
 | 
					import 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 final Container backgroundContainer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public SettingsMenu(MonopolyApp app) {
 | 
				
			||||||
 | 
					        super(app.getDialogManager());
 | 
				
			||||||
 | 
					        this.app = app;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Halbtransparentes Overlay hinzufügen
 | 
				
			||||||
 | 
					        overlayBackground = createOverlayBackground();
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(overlayBackground);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Create the background container
 | 
				
			||||||
 | 
					        backgroundContainer = new Container();
 | 
				
			||||||
 | 
					        backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(backgroundContainer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Hauptcontainer für das Menü
 | 
				
			||||||
 | 
					        settingsContainer = new Container();
 | 
				
			||||||
 | 
					        settingsContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Titel
 | 
				
			||||||
 | 
					        Label settingsTitle = settingsContainer.addChild(new Label("Einstellungen", new ElementId("settings-title")));
 | 
				
			||||||
 | 
					        settingsTitle.setFontSize(48);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Effekt-Sound: Slider und Checkbox
 | 
				
			||||||
 | 
					        Container effectSoundContainer = settingsContainer.addChild(new Container());
 | 
				
			||||||
 | 
					        effectSoundContainer.addChild(new Label("Effekt Sound", new ElementId("label")));
 | 
				
			||||||
 | 
					        effectSoundContainer.addChild(new Slider());
 | 
				
			||||||
 | 
					        effectSoundContainer.addChild(new Checkbox("Soundeffekte an")).setChecked(true);
 | 
				
			||||||
 | 
					        effectSoundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
 | 
				
			||||||
 | 
					        // Hintergrundmusik: Slider und Checkbox
 | 
				
			||||||
 | 
					        Container backgroundMusicContainer = settingsContainer.addChild(new Container());
 | 
				
			||||||
 | 
					        backgroundMusicContainer.addChild(new Label("Hintergrund Musik", new ElementId("label")));
 | 
				
			||||||
 | 
					        backgroundMusicContainer.addChild(new Slider());
 | 
				
			||||||
 | 
					        backgroundMusicContainer.addChild(new Checkbox("Musik an")).setChecked(true);
 | 
				
			||||||
 | 
					        backgroundMusicContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Beenden-Button
 | 
				
			||||||
 | 
					        Button quitButton = settingsContainer.addChild(new Button("Beenden", new ElementId("menu-button")));
 | 
				
			||||||
 | 
					        quitButton.setFontSize(32);
 | 
				
			||||||
 | 
					        quitButton.addClickCommands(source -> app.stop());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        float padding = 10; // Padding around the settingsContainer for the background
 | 
				
			||||||
 | 
					        backgroundContainer.setPreferredSize(settingsContainer.getPreferredSize().addLocal(padding, padding, 0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Zentriere das Menü
 | 
				
			||||||
 | 
					        settingsContainer.setLocalTranslation(
 | 
				
			||||||
 | 
					            (app.getCamera().getWidth() - settingsContainer.getPreferredSize().x) / 2,
 | 
				
			||||||
 | 
					            (app.getCamera().getHeight() + settingsContainer.getPreferredSize().y) / 2,
 | 
				
			||||||
 | 
					            4
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        backgroundContainer.setLocalTranslation(
 | 
				
			||||||
 | 
					                (app.getCamera().getWidth() - settingsContainer.getPreferredSize().x - padding) / 2,
 | 
				
			||||||
 | 
					                (app.getCamera().getHeight() + settingsContainer.getPreferredSize().y+ padding) / 2,
 | 
				
			||||||
 | 
					                3
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(settingsContainer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Erstellt einen halbtransparenten Hintergrund für das Menü.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Geometrie des Overlays
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Geometry createOverlayBackground() {
 | 
				
			||||||
 | 
					        Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
 | 
				
			||||||
 | 
					        Geometry overlay = new Geometry("Overlay", quad);
 | 
				
			||||||
 | 
					        Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
 | 
				
			||||||
 | 
					        material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent
 | 
				
			||||||
 | 
					        material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
 | 
				
			||||||
 | 
					        overlay.setMaterial(material);
 | 
				
			||||||
 | 
					        overlay.setLocalTranslation(0, 0, 0);
 | 
				
			||||||
 | 
					        return overlay;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Schließt das Menü und entfernt die GUI-Elemente.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void close() {
 | 
				
			||||||
 | 
					        System.out.println("Schließe SettingsMenu..."); // Debugging-Ausgabe
 | 
				
			||||||
 | 
					        app.getGuiNode().detachChild(settingsContainer);  // Entferne das Menü
 | 
				
			||||||
 | 
					        app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
 | 
				
			||||||
 | 
					        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
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.material.Material;
 | 
				
			||||||
 | 
					import com.jme3.math.ColorRGBA;
 | 
				
			||||||
 | 
					import com.jme3.math.Vector3f;
 | 
				
			||||||
 | 
					import com.jme3.scene.Geometry;
 | 
				
			||||||
 | 
					import com.jme3.scene.shape.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 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    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("Board", box);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Setze das Material mit Textur
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
 | 
				
			||||||
 | 
					        Texture texture = app.getAssetManager().loadTexture("Pictures/board2.png");
 | 
				
			||||||
 | 
					        mat.setTexture("ColorMap", texture);
 | 
				
			||||||
 | 
					        geom.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.getRootNode().attachChild(geom);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Erstellt den Würfel (Spielfigur) in der Szene.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void createCube() {
 | 
				
			||||||
 | 
					        Box box = new Box(0.05f, 0.05f, 0.05f); // Kleinere Größe für Spielfigur
 | 
				
			||||||
 | 
					        cube = new Geometry("Cube", box);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Setze das Material für den Würfel
 | 
				
			||||||
 | 
					        Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
 | 
				
			||||||
 | 
					        mat.setColor("Color", ColorRGBA.Blue); // Blau gefärbter Würfel
 | 
				
			||||||
 | 
					        cube.setMaterial(mat);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Setze den Startpunkt des Würfels
 | 
				
			||||||
 | 
					        cube.setLocalTranslation(0.8999999f, 0.1f, -0.9f);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        app.getRootNode().attachChild(cube);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,168 @@
 | 
				
			|||||||
 | 
					package pp.monopoly.client.gui;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Random;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.jme3.font.BitmapText;
 | 
				
			||||||
 | 
					import com.jme3.math.Vector3f;
 | 
				
			||||||
 | 
					import com.jme3.scene.Geometry;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Axis;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Button;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.Container;
 | 
				
			||||||
 | 
					import com.simsilica.lemur.component.SpringGridLayout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import pp.monopoly.client.MonopolyApp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Toolbar Klasse, die am unteren Rand der Szene angezeigt wird.
 | 
				
			||||||
 | 
					 * Die Buttons bewegen den Würfel auf dem Spielfeld.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class Toolbar2 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final MonopolyApp app;
 | 
				
			||||||
 | 
					    private final Container toolbarContainer;
 | 
				
			||||||
 | 
					    private final Geometry cube; // Referenz auf den Würfel
 | 
				
			||||||
 | 
					    private final BitmapText positionText; // Anzeige für die aktuelle Position
 | 
				
			||||||
 | 
					    private final float boardLimit = 0.95f; // Grenzen des Bretts
 | 
				
			||||||
 | 
					    private final float stepSize = 0.18f; // Schrittgröße pro Bewegung
 | 
				
			||||||
 | 
					    private int currentPosition = 0; // Aktuelle Position auf dem Spielfeld
 | 
				
			||||||
 | 
					    private final int positionsPerSide = 10; // Anzahl der Positionen pro Seite
 | 
				
			||||||
 | 
					    private final Random random = new Random(); // Zufallsgenerator für den Würfelwurf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Konstruktor für die Toolbar.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param app  Die Hauptanwendung (MonopolyApp)
 | 
				
			||||||
 | 
					     * @param cube Der Würfel, der bewegt werden soll
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Toolbar2(MonopolyApp app, Geometry cube) {
 | 
				
			||||||
 | 
					        this.app = app;
 | 
				
			||||||
 | 
					        this.cube = cube;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Erstelle die Toolbar
 | 
				
			||||||
 | 
					        toolbarContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Setze die Position am unteren Rand und die Breite
 | 
				
			||||||
 | 
					        toolbarContainer.setLocalTranslation(
 | 
				
			||||||
 | 
					                0,                                 // Links bündig
 | 
				
			||||||
 | 
					                100,                               // Höhe über dem unteren Rand
 | 
				
			||||||
 | 
					                0                                  // Z-Ebene
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        toolbarContainer.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 100, 0)); // Volle Breite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Füge Buttons zur Toolbar hinzu
 | 
				
			||||||
 | 
					        initializeButtons();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Füge die Toolbar zur GUI hinzu
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(toolbarContainer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Erstelle die Position-Anzeige
 | 
				
			||||||
 | 
					        positionText = createPositionDisplay();
 | 
				
			||||||
 | 
					        updatePositionDisplay(); // Initialisiere die Anzeige mit der Startposition
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Initialisiert die Buttons in der Toolbar.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void initializeButtons() {
 | 
				
			||||||
 | 
					        addButton("Vorwärts", 1);  // Bewegung nach vorne
 | 
				
			||||||
 | 
					        addButton("Rückwärts", -1); // Bewegung nach hinten
 | 
				
			||||||
 | 
					        addDiceRollButton();       // Würfel-Button
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fügt einen Button mit einer Bewegung hinzu.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param label   Der Text des Buttons
 | 
				
			||||||
 | 
					     * @param step    Schrittweite (+1 für vorwärts, -1 für rückwärts)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void addButton(String label, int step) {
 | 
				
			||||||
 | 
					        Button button = new Button(label);
 | 
				
			||||||
 | 
					        button.setPreferredSize(new Vector3f(150, 50, 0)); // Größe der Buttons
 | 
				
			||||||
 | 
					        button.addClickCommands(source -> moveCube(step));
 | 
				
			||||||
 | 
					        toolbarContainer.addChild(button);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fügt den Würfel-Button hinzu, der die Figur entsprechend der gewürfelten Zahl bewegt.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void addDiceRollButton() {
 | 
				
			||||||
 | 
					        Button diceButton = new Button("Würfeln");
 | 
				
			||||||
 | 
					        diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons
 | 
				
			||||||
 | 
					        diceButton.addClickCommands(source -> rollDice());
 | 
				
			||||||
 | 
					        toolbarContainer.addChild(diceButton);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Simuliert einen Würfelwurf und bewegt die Figur entsprechend.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void rollDice() {
 | 
				
			||||||
 | 
					        int diceRoll = random.nextInt(6) + 1; // Zahl zwischen 1 und 6
 | 
				
			||||||
 | 
					        System.out.println("Gewürfelt: " + diceRoll);
 | 
				
			||||||
 | 
					        moveCube(diceRoll); // Bewege die Figur um die gewürfelte Zahl
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Bewegt den Würfel basierend auf der aktuellen Position auf dem Brett.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param step Schrittweite (+1 für vorwärts, -1 für rückwärts oder andere Werte)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void moveCube(int step) {
 | 
				
			||||||
 | 
					        currentPosition = (currentPosition + step + 4 * positionsPerSide) % (4 * positionsPerSide);
 | 
				
			||||||
 | 
					        Vector3f newPosition = calculatePosition(currentPosition);
 | 
				
			||||||
 | 
					        cube.setLocalTranslation(newPosition);
 | 
				
			||||||
 | 
					        updatePositionDisplay(); // Aktualisiere die Positionsanzeige
 | 
				
			||||||
 | 
					        System.out.println("Würfelposition: " + newPosition + " (Feld-ID: " + currentPosition + ")");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Berechnet die neue Position des Würfels basierend auf der aktuellen Brettseite und Position.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param position Aktuelle Position auf dem Spielfeld
 | 
				
			||||||
 | 
					     * @return Die berechnete Position als Vector3f
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private Vector3f calculatePosition(int position) {
 | 
				
			||||||
 | 
					        int side = position / positionsPerSide; // Seite des Bretts (0 = unten, 1 = rechts, 2 = oben, 3 = links)
 | 
				
			||||||
 | 
					        int offset = position % positionsPerSide; // Position auf der aktuellen Seite
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (side) {
 | 
				
			||||||
 | 
					            case 0: // Unten (positive x-Achse)
 | 
				
			||||||
 | 
					                return new Vector3f(-boardLimit + offset * stepSize, 0.1f, -boardLimit + 0.05f);
 | 
				
			||||||
 | 
					            case 1: // Rechts (positive z-Achse)
 | 
				
			||||||
 | 
					                return new Vector3f(boardLimit - 0.05f, 0.1f, -boardLimit + offset * stepSize);
 | 
				
			||||||
 | 
					            case 2: // Oben (negative x-Achse)
 | 
				
			||||||
 | 
					                return new Vector3f(boardLimit - offset * stepSize, 0.1f, boardLimit - 0.05f);
 | 
				
			||||||
 | 
					            case 3: // Links (negative z-Achse)
 | 
				
			||||||
 | 
					                return new Vector3f(-boardLimit + 0.05f, 0.1f, boardLimit - offset * stepSize);
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                throw new IllegalArgumentException("Ungültige Position: " + position);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Erstellt die Anzeige für die aktuelle Position.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @return Das BitmapText-Objekt für die Anzeige
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private BitmapText createPositionDisplay() {
 | 
				
			||||||
 | 
					        BitmapText text = new BitmapText(app.getAssetManager().loadFont("Interface/Fonts/Default.fnt"), false);
 | 
				
			||||||
 | 
					        text.setSize(20); // Schriftgröße
 | 
				
			||||||
 | 
					        text.setLocalTranslation(10, app.getCamera().getHeight() - 10, 0); // Oben links
 | 
				
			||||||
 | 
					        app.getGuiNode().attachChild(text);
 | 
				
			||||||
 | 
					        return text;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Aktualisiert die Anzeige für die aktuelle Position.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void updatePositionDisplay() {
 | 
				
			||||||
 | 
					        positionText.setText("Feld-ID: " + currentPosition);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Entfernt die Toolbar.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void remove() {
 | 
				
			||||||
 | 
					        app.getGuiNode().detachChild(toolbarContainer);
 | 
				
			||||||
 | 
					        app.getGuiNode().detachChild(positionText);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||