11 Commits

Author SHA1 Message Date
Filip Szepielewicz
a9e4afec08 Update 2 files
- /Projekte/monopoly/model/src/test/java/pp/monopoly/Testhandbuch.java
- /Projekte/monopoly/model/src/test/java/pp/monopoly/game/server/ServerGameLogicTest.java
2024-11-28 20:08:07 +00:00
Johannes Schmelz
6e5d9452b3 Merge branch 'main' into 'Testhandbuch'
# Conflicts:
#   Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy
#   Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java
#   Projekte/monopoly/model/src/test/java/pp/monopoly/client/ClientLogicTest.java
#   Projekte/monopoly/model/src/test/java/pp/monopoly/game/client/ClientGameLogicTest.java
2024-11-25 04:32:59 +00:00
Filip Szepielewicz
f1d52c445b Tests bis T075 überarbeitet 2024-11-22 21:45:22 +01:00
Filip Szepielewicz
30e4298f88 Merge remote-tracking branch 'origin/Testhandbuch' into Testhandbuch 2024-11-17 20:38:30 +01:00
Filip Szepielewicz
7a9e84f49c Tests bis T071 erweitert 2024-11-17 20:06:47 +01:00
Filip Szepielewicz
7fce07ac19 Tests bis T070 erweitert 2024-11-17 20:00:05 +01:00
Johannes Schmelz
2496ad812a Merge branch 'main' into 'Testhandbuch'
# Conflicts:
#   Projekte/monopoly/model/src/test/java/pp/monopoly/Testhandbuch.java
2024-11-17 18:27:54 +00:00
Filip Szepielewicz
f4fb04d17e T001 - T037 überarbeitet 2024-11-15 19:46:26 +01:00
Filip Szepielewicz
1b4fee2853 T001 - T034 erster versuch 2024-11-15 08:42:56 +01:00
Filip Szepielewicz
b65f458302 T001 - T034 erster versuch 2024-11-15 08:42:11 +01:00
Filip Szepielewicz
c792a8b3fb T001 - T034 erster versuch 2024-11-14 21:58:47 +01:00
262 changed files with 2778 additions and 13797 deletions

View File

@@ -1,18 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="MonopolyApp (Mac)" type="Application" factoryName="Application"
singleton="false">
<option name="MAIN_CLASS_NAME" value="pp.monopoly.client.MonopolyApp"/>
<module name="Projekte.monopoly.client.main"/>
<option name="VM_PARAMETERS" value="-XstartOnFirstThread"/>
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$"/>
<extension name="coverage">
<pattern>
<option name="PATTERN" value="pp.monopoly.client.*"/>
<option name="ENABLED" value="true"/>
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true"/>
</method>
</configuration>
</component>

View File

@@ -1,18 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="MonopolyApp" type="Application" factoryName="Application" singleton="false"
nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="pp.monopoly.client.MonopolyApp"/>
<module name="Projekte.monopoly.client.main"/>
<option name="VM_PARAMETERS" value="-Djava.util.logging.config.file=logging.properties"/>
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$"/>
<extension name="coverage">
<pattern>
<option name="PATTERN" value="pp.monopoly.client.*"/>
<option name="ENABLED" value="true"/>
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true"/>
</method>
</configuration>
</component>

View File

@@ -1,17 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="MonopolyServer" type="Application" factoryName="Application"
nameIsGenerated="true">
<option name="MAIN_CLASS_NAME" value="pp.monopoly.server.MonopolyServer"/>
<module name="Projekte.monopoly.server.main"/>
<option name="WORKING_DIRECTORY" value="$MODULE_WORKING_DIR$"/>
<extension name="coverage">
<pattern>
<option name="PATTERN" value="pp.monopoly.server.*"/>
<option name="ENABLED" value="true"/>
</pattern>
</extension>
<method v="2">
<option name="Make" enabled="true"/>
</method>
</configuration>
</component>

View File

@@ -18,11 +18,10 @@ import com.simsilica.lemur.style.ElementId;
import static pp.battleship.Resources.lookup;
import pp.battleship.client.gui.GameMusic;
import pp.battleship.client.gui.VolumeSlider;
import pp.dialog.Dialog;
import pp.dialog.StateCheckboxModel;
import pp.dialog.TextInputDialog;
import pp.battleship.client.gui.VolumeSlider;
import static pp.util.PreferencesUtils.getPreferences;
/**

View File

@@ -7,23 +7,24 @@
package pp.battleship.client;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.SpringGridLayout;
import static pp.battleship.Resources.lookup;
import pp.battleship.server.BattleshipServer;
import pp.dialog.Dialog;
import pp.dialog.DialogBuilder;
import pp.dialog.SimpleDialog;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import static pp.battleship.Resources.lookup;
/**
* Represents a dialog for setting up a network connection in the Battleship game.
* Allows users to specify the host and port for connecting to a game server.
@@ -31,7 +32,7 @@ import pp.dialog.SimpleDialog;
class NetworkDialog extends SimpleDialog {
private static final Logger LOGGER = System.getLogger(NetworkDialog.class.getName());
private static final String LOCALHOST = "localhost"; //NON-NLS
private static final String DEFAULT_PORT = "42069"; //NON-NLS
private static final String DEFAULT_PORT = "1234"; //NON-NLS
private final NetworkSupport network;
private final TextField host = new TextField(LOCALHOST);
private final TextField port = new TextField(DEFAULT_PORT);

View File

@@ -1,7 +1,6 @@
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,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 KiB

View File

@@ -1,152 +0,0 @@
# Blender 4.2.2 LTS MTL File: 'Hotel.V.1.2.blend'
# www.blender.org
newmtl Material
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0.6 0 -s 3.8 4 0 Hotel_texture2.png
newmtl Material.002
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Kd 0.799099 0.004025 0.021219
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
newmtl Material.008
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Kd 0.215859 0.215861 0.215861
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
newmtl Material.010
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0 -15 -s 4.28 4 0 Hotel_texture3.jpeg
newmtl Material.011
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0.6 0 -s 3.8 4 0 Hotel_texture2.png
newmtl Material.012
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0 -15 -s 4.28 4 0 Hotel_texture3.jpeg
newmtl Material.013
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Kd 0.799099 0.004025 0.021219
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
newmtl Material.014
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0.6 0 -s 3.8 4 0 Hotel_texture2.png
newmtl Material.015
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0 -15 -s 4.28 4 0 Hotel_texture3.jpeg
newmtl Material.016
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Kd 0.799099 0.004025 0.021219
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
newmtl Material.019
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0.6 0 -s 4.15 3.9 0 Hotel_texture1.jpeg
newmtl Material.022
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0.6 0 -s 4.15 3.8 0 Hotel_texture1.jpeg
newmtl Material.023
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0.6 0 -s 4.15 3.8 0 Hotel_texture1.jpeg
newmtl Material.024
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2
map_Kd -o 0 0.6 0 -s 4.15 3.9 0 Hotel_texture1.jpeg
newmtl Material.025
Ns 250.000000
Ka 1.000000 1.000000 1.000000
Kd 0.215859 0.215861 0.215861
Ks 0.500000 0.500000 0.500000
Ke 0.000000 0.000000 0.000000
Ni 1.500000
d 1.000000
illum 2

View File

@@ -1,558 +0,0 @@
# Blender 4.2.2 LTS
# www.blender.org
mtllib Hotel.V.1.2.mtl
o Korridor
v 1.832325 0.743096 0.449194
v 1.832325 0.295686 0.449194
v 0.271347 0.743096 0.429009
v 0.271347 0.295686 0.429009
v 1.837850 0.743096 0.022004
v 1.837850 0.295686 0.022004
v 0.276871 0.743096 0.001818
v 0.276871 0.295686 0.001818
vn -0.0129 -0.0000 0.9999
vn -0.9999 -0.0000 -0.0129
vn 0.0129 -0.0000 -0.9999
vn 0.9999 -0.0000 0.0129
vn -0.0000 1.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.008
f 1/1/1 3/4/1 4/3/1 2/2/1
f 3/4/2 7/6/2 8/5/2 4/3/2
f 7/6/3 5/8/3 6/7/3 8/5/3
f 5/8/4 1/10/4 2/9/4 6/7/4
f 3/11/5 1/12/5 5/8/5 7/6/5
f 8/5/6 6/7/6 2/14/6 4/13/6
o Dach300.002
v 2.503119 1.817345 1.102498
v 2.142795 1.919807 1.102498
v 2.503119 1.817345 -0.809905
v 2.142795 1.919807 -0.809905
v 1.831221 1.817345 1.102498
v 2.138520 1.919807 1.102498
v 1.831221 1.817345 -0.809905
v 2.138520 1.919807 -0.809905
vn 0.2735 0.9619 -0.0000
vn -0.0000 -0.0000 -1.0000
vn -0.3163 0.9487 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.002
f 9/15/7 11/18/7 12/17/7 10/16/7
f 11/18/8 15/20/8 16/19/8 12/17/8
f 15/20/9 13/22/9 14/21/9 16/19/9
f 13/22/10 9/24/10 10/23/10 14/21/10
f 11/25/11 9/26/11 13/22/11 15/20/11
f 16/19/12 14/21/12 10/28/12 12/27/12
o neu1
v 1.833977 0.294749 1.094962
v 1.833977 1.817107 1.094962
v 1.833977 0.294749 -0.825603
v 1.833977 1.817107 -0.825603
v 2.517500 0.294749 1.094962
v 2.517500 1.817107 1.094962
v 2.517500 0.294749 -0.825603
v 2.517500 1.817107 -0.825603
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material
f 17/29/13 18/30/13 20/31/13 19/32/13
f 19/32/14 20/31/14 24/33/14 23/34/14
f 23/34/15 24/33/15 22/35/15 21/36/15
f 21/36/16 22/35/16 18/37/16 17/38/16
f 19/39/17 23/34/17 21/36/17 17/40/17
f 24/33/18 20/41/18 18/42/18 22/35/18
o Frontfassade
v 1.837487 0.305766 1.101834
v 1.837487 1.807752 1.101834
v 1.837487 0.305766 1.092930
v 1.837487 1.807752 1.092930
v 2.509542 0.305766 1.101834
v 2.509542 1.807752 1.101834
v 2.509542 0.305766 1.092930
v 2.509542 1.807752 1.092930
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.010
f 25/43/19 26/44/19 28/45/19 27/46/19
f 27/46/20 28/45/20 32/47/20 31/48/20
f 31/48/21 32/47/21 30/49/21 29/50/21
f 29/50/22 30/49/22 26/51/22 25/52/22
f 27/53/23 31/48/23 29/50/23 25/54/23
f 32/47/24 28/55/24 26/56/24 30/49/24
o neu1.001
v -2.645782 0.310702 1.102233
v -2.645782 1.833060 1.102233
v -2.645782 0.310702 -0.818333
v -2.645782 1.833060 -0.818333
v -1.962258 0.310702 1.102233
v -1.962258 1.833060 1.102233
v -1.962258 0.310702 -0.818333
v -1.962258 1.833060 -0.818333
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.011
f 33/57/25 34/58/25 36/59/25 35/60/25
f 35/60/26 36/59/26 40/61/26 39/62/26
f 39/62/27 40/61/27 38/63/27 37/64/27
f 37/64/28 38/63/28 34/65/28 33/66/28
f 35/67/29 39/62/29 37/64/29 33/68/29
f 40/61/30 36/69/30 34/70/30 38/63/30
o Frontfassade.001
v -2.642272 0.321718 1.109104
v -2.642272 1.823704 1.109104
v -2.642272 0.321718 1.100200
v -2.642272 1.823704 1.100200
v -1.970217 0.321718 1.109104
v -1.970217 1.823704 1.109104
v -1.970217 0.321718 1.100200
v -1.970217 1.823704 1.100200
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.012
f 41/71/31 42/72/31 44/73/31 43/74/31
f 43/74/32 44/73/32 48/75/32 47/76/32
f 47/76/33 48/75/33 46/77/33 45/78/33
f 45/78/34 46/77/34 42/79/34 41/80/34
f 43/81/35 47/76/35 45/78/35 41/82/35
f 48/75/36 44/83/36 42/84/36 46/77/36
o Dach300.001
v -1.976640 1.833298 1.092314
v -2.336963 1.935760 1.092314
v -1.976640 1.833298 -0.785180
v -2.336963 1.935760 -0.785180
v -2.648537 1.833298 1.092314
v -2.341239 1.935760 1.092314
v -2.648537 1.833298 -0.785180
v -2.341239 1.935760 -0.785180
vn 0.2735 0.9619 -0.0000
vn -0.0000 -0.0000 -1.0000
vn -0.3163 0.9487 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.013
f 49/85/37 51/88/37 52/87/37 50/86/37
f 51/88/38 55/90/38 56/89/38 52/87/38
f 55/90/39 53/92/39 54/91/39 56/89/39
f 53/92/40 49/94/40 50/93/40 54/91/40
f 51/95/41 49/96/41 53/92/41 55/90/41
f 56/89/42 54/91/42 50/98/42 52/97/42
o neu1.002
v -0.141720 0.277559 1.245076
v -0.141720 1.799917 1.245076
v -0.526580 0.277559 -0.636534
v -0.526580 1.799917 -0.636534
v 0.527939 0.277559 1.108105
v 0.527939 1.799917 1.108105
v 0.143079 0.277559 -0.773505
v 0.143079 1.799917 -0.773505
vn -0.9797 -0.0000 0.2004
vn -0.2004 -0.0000 -0.9797
vn 0.9797 -0.0000 -0.2004
vn 0.2004 -0.0000 0.9797
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.014
f 57/99/43 58/100/43 60/101/43 59/102/43
f 59/102/44 60/101/44 64/103/44 63/104/44
f 63/104/45 64/103/45 62/105/45 61/106/45
f 61/106/46 62/105/46 58/107/46 57/108/46
f 59/109/47 63/104/47 61/106/47 57/110/47
f 64/103/48 60/111/48 58/112/48 62/105/48
o Frontfassade.002
v -0.136904 0.288576 1.251104
v -0.136904 1.790561 1.251104
v -0.138688 0.288576 1.242381
v -0.138688 1.790561 1.242381
v 0.521519 0.288576 1.116432
v 0.521519 1.790561 1.116432
v 0.519735 0.288576 1.107709
v 0.519735 1.790561 1.107709
vn -0.9797 -0.0000 0.2004
vn -0.2004 -0.0000 -0.9797
vn 0.9797 -0.0000 -0.2004
vn 0.2004 -0.0000 0.9797
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.015
f 65/113/49 66/114/49 68/115/49 67/116/49
f 67/116/50 68/115/50 72/117/50 71/118/50
f 71/118/51 72/117/51 70/119/51 69/120/51
f 69/120/52 70/119/52 66/121/52 65/122/52
f 67/123/53 71/118/53 69/120/53 65/124/53
f 72/117/54 68/125/54 66/126/54 70/119/54
o Dach300.003
v 0.511862 1.800155 1.101269
v 0.158847 1.902617 1.173474
v 0.135633 1.800155 -0.738143
v -0.217382 1.902617 -0.665938
v -0.146407 1.800155 1.235910
v 0.154658 1.902617 1.174331
v -0.522636 1.800155 -0.603502
v -0.221571 1.902617 -0.665081
vn 0.2680 0.9619 -0.0548
vn -0.2004 -0.0000 -0.9797
vn -0.3099 0.9487 0.0634
vn 0.2004 -0.0000 0.9797
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.016
f 73/127/55 75/130/55 76/129/55 74/128/55
f 75/130/56 79/132/56 80/131/56 76/129/56
f 79/132/57 77/134/57 78/133/57 80/131/57
f 77/134/58 73/136/58 74/135/58 78/133/58
f 75/137/59 73/138/59 77/134/59 79/132/59
f 80/131/60 78/133/60 74/140/60 76/139/60
o Korridor.002
v 0.294809 0.748953 0.441551
v 0.294809 0.301543 0.441551
v 1.816918 0.748953 0.453486
v 1.816918 0.301543 0.453486
v 0.294640 0.748953 0.463026
v 0.294640 0.301543 0.463026
v 1.816749 0.748953 0.474962
v 1.816749 0.301543 0.474962
vn 0.0078 -0.0000 -1.0000
vn 1.0000 -0.0000 0.0078
vn -0.0078 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0078
vn -0.0000 1.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.019
f 81/141/61 83/144/61 84/143/61 82/142/61
f 83/144/62 87/146/62 88/145/62 84/143/62
f 87/146/63 85/148/63 86/147/63 88/145/63
f 85/148/64 81/150/64 82/149/64 86/147/64
f 83/151/65 81/152/65 85/148/65 87/146/65
f 88/145/66 86/147/66 82/154/66 84/153/66
o Korridor.003
v 0.295923 0.753172 -0.007372
v 0.295923 0.305763 -0.007372
v 1.818032 0.753172 0.004563
v 1.818032 0.305763 0.004563
v 0.295755 0.753172 0.014104
v 0.295755 0.305763 0.014104
v 1.817863 0.753172 0.026039
v 1.817863 0.305763 0.026039
vn 0.0078 -0.0000 -1.0000
vn 1.0000 -0.0000 0.0078
vn -0.0078 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0078
vn -0.0000 1.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.022
f 89/155/67 91/158/67 92/157/67 90/156/67
f 91/158/68 95/160/68 96/159/68 92/157/68
f 95/160/69 93/162/69 94/161/69 96/159/69
f 93/162/70 89/164/70 90/163/70 94/161/70
f 91/165/71 89/166/71 93/162/71 95/160/71
f 96/159/72 94/161/72 90/168/72 92/167/72
o Korridor.004
v -2.035452 0.753172 0.001578
v -2.035452 0.305763 0.001578
v -0.305429 0.753172 0.015144
v -0.305429 0.305763 0.015144
v -2.035621 0.753172 0.023054
v -2.035621 0.305763 0.023054
v -0.305598 0.753172 0.036620
v -0.305598 0.305763 0.036620
vn 0.0078 -0.0000 -1.0000
vn 1.0000 -0.0000 0.0078
vn -0.0078 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0078
vn -0.0000 1.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.023
f 97/169/73 99/172/73 100/171/73 98/170/73
f 99/172/74 103/174/74 104/173/74 100/171/74
f 103/174/75 101/176/75 102/175/75 104/173/75
f 101/176/76 97/178/76 98/177/76 102/175/76
f 99/179/77 97/180/77 101/176/77 103/174/77
f 104/173/78 102/175/78 98/182/78 100/181/78
o Korridor.005
v -2.036718 0.748953 0.450501
v -2.036718 0.301543 0.450501
v -0.306695 0.748953 0.464067
v -0.306695 0.301543 0.464067
v -2.036887 0.748953 0.471977
v -2.036887 0.301543 0.471977
v -0.306864 0.748953 0.485542
v -0.306864 0.301543 0.485542
vn 0.0078 -0.0000 -1.0000
vn 1.0000 -0.0000 0.0078
vn -0.0078 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0078
vn -0.0000 1.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.024
f 105/183/79 107/186/79 108/185/79 106/184/79
f 107/186/80 111/188/80 112/187/80 108/185/80
f 111/188/81 109/190/81 110/189/81 112/187/81
f 109/190/82 105/192/82 106/191/82 110/189/82
f 107/193/83 105/194/83 109/190/83 111/188/83
f 112/187/84 110/189/84 106/196/84 108/195/84
o Korridor.006
v -0.288805 0.743096 0.460344
v -0.288805 0.295686 0.460344
v -2.062986 0.743096 0.437401
v -2.062986 0.295686 0.437401
v -0.283281 0.743096 0.033143
v -0.283281 0.295686 0.033143
v -2.057462 0.743096 0.010200
v -2.057462 0.295686 0.010200
vn -0.0129 -0.0000 0.9999
vn -0.9999 -0.0000 -0.0129
vn 0.0129 -0.0000 -0.9999
vn 0.9999 -0.0000 0.0129
vn -0.0000 1.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.625000 0.250000
vt 0.375000 0.250000
vt 0.625000 0.500000
vt 0.375000 0.500000
vt 0.625000 0.750000
vt 0.375000 0.750000
vt 0.625000 1.000000
vt 0.375000 1.000000
vt 0.125000 0.500000
vt 0.125000 0.750000
vt 0.875000 0.500000
vt 0.875000 0.750000
s 0
usemtl Material.025
f 113/197/85 115/200/85 116/199/85 114/198/85
f 115/200/86 119/202/86 120/201/86 116/199/86
f 119/202/87 117/204/87 118/203/87 120/201/87
f 117/204/88 113/206/88 114/205/88 118/203/88
f 115/207/89 113/208/89 117/204/89 119/202/89
f 120/201/90 118/203/90 114/210/90 116/209/90

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

View File

@@ -7,11 +7,12 @@
package pp.battleship;
import static java.lang.Math.max;
import pp.util.config.Config;
import java.util.Map;
import java.util.TreeMap;
import pp.util.config.Config;
import static java.lang.Math.max;
/**
* Provides access to the configuration settings for the Battleship game.
@@ -30,7 +31,7 @@ public class BattleshipConfig extends Config {
* The default port number for the Battleship server.
*/
@Property("port")
private int port = 12234;
private int port = 1234;
/**
* The width of the game map in terms of grid units.

View File

@@ -10,7 +10,7 @@
# This file defines the configuration settings for the Battleship server.
#
# The port number on which the server will listen for incoming connections.
port=42069
port=1234
#
# The dimensions of the game map.
# 'map.width' defines the number of columns, and 'map.height' defines the number of rows.

View File

@@ -1,9 +1,6 @@
// Styling of Lemur components
// For documentation, see:
// https://github.com/jMonkeyEngine-Contributions/Lemur/wiki/Styling
import com.jme3.math.ColorRGBA
import com.jme3.texture.Texture
import com.simsilica.lemur.*
import com.simsilica.lemur.component.QuadBackgroundComponent
import com.simsilica.lemur.Button
@@ -13,8 +10,6 @@ import com.simsilica.lemur.HAlignment
import com.simsilica.lemur.Insets3f
import com.simsilica.lemur.component.QuadBackgroundComponent
import com.simsilica.lemur.component.TbtQuadBackgroundComponent
import pp.monopoly.client.MonopolyApp
import pp.monopoly.game.server.Player
def bgColor = color(1, 1, 1, 1)
def buttonEnabledColor = color(0, 0, 0, 1)
@@ -24,11 +19,7 @@ def sliderColor = color(0.6, 0.8, 0.8, 1)
def sliderBgColor = color(0.5, 0.75, 0.75, 1)
def gradientColor = color(0.5, 0.75, 0.85, 0.5)
def tabbuttonEnabledColor = color(0.4, 0.45, 0.5, 1)
def solidWhiteBackground = new QuadBackgroundComponent(new ColorRGBA(1, 1, 1, 1))
def greyBackground = new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 1.0f));
def lightGreyBackground = new QuadBackgroundComponent(new ColorRGBA(0.4f, 0.4f, 0.4f, 1.0f));
def lightGrey = color(0.6, 0.6, 0.6, 1.0)
def solidWhiteBackground = new QuadBackgroundComponent(color(1, 1, 1, 1)) // Solid white
@@ -41,32 +32,14 @@ def gradient = TbtQuadBackgroundComponent.create(
def doubleGradient = new QuadBackgroundComponent(gradientColor)
doubleGradient.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
generateMips: false)
//doubleGradient.color = color(0, 0, 0, 1)
def orangeBorder = TbtQuadBackgroundComponent.create(
texture(name: "/com/simsilica/lemur/icons/border.png", // Replace with an appropriate texture if needed
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
def createCustomBackground(app) {
// Load the texture from the assets
Texture texture = app.getAssetManager().loadTexture("Pictures/kontobg.png")
// Create the TbtQuadBackgroundComponent
def backgroundCustom = TbtQuadBackgroundComponent.create(
texture, // The texture to use
1, 1, 1, // Insets for the 9-patch behavior
126, 126, // The size of the texture
1f, // The scale factor
false // No tiling
)
return backgroundCustom
}
selector("pp") {
font = font("Interface/Fonts/Metropolis/Metropolis-Regular-32.fnt")
}
@@ -76,57 +49,11 @@ selector("label", "pp") {
color = buttonEnabledColor
}
selector("label-Bold", "pp") {
insets = new Insets3f(2, 2, 2, 2)
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-32.fnt")
fontSize = 30
color = buttonEnabledColor
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
}
selector("label-toolbar", "pp") {
insets = new Insets3f(2, 2, 2, 2)
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-32.fnt")
fontSize = 25
color = new ColorRGBA(ColorRGBA.White)
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
}
selector("label-Text", "pp") {
insets = new Insets3f(2, 2, 2, 2)
fontSize = 25
color = buttonEnabledColor
}
selector("label-player", "pp") {
insets = new Insets3f(2, 2, 2, 2)
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-32.fnt")
fontSize = 20
color = buttonEnabledColor
}
selector("label-account", "pp") {
insets = new Insets3f(2, 2, 2, 2)
fontSize = 25
color = new ColorRGBA(ColorRGBA.White)
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
}
selector("card-label", "pp") {
insets = new Insets3f(2, 2, 2, 2)
color = ColorRGBA.Black
}
selector("header", "pp") {
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-42.fnt")
insets = new Insets3f(2, 2, 2, 2)
color = color(1, 0.5, 0, 1)
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
}
selector("container", "pp") {
@@ -135,10 +62,20 @@ selector("container", "pp") {
}
selector("toolbar") {
background = gradient.clone()
background.setColor(bgColor)
//color = (new ColorRGBA(0.4157f, 0.4235f, 0.4392f, 1.0f))
// 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") {
background = gradient.clone()
@@ -207,8 +144,6 @@ selector("title", "pp") {
background.texture = texture(name: "/com/simsilica/lemur/icons/double-gradient-128.png",
generateMips: false)
insets = new Insets3f(2, 2, 2, 2)
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
buttonCommands = stdButtonCommands
}
@@ -223,8 +158,7 @@ selector("button", "pp") {
// Use insets to create a margin/padding effect for the inner background
insets = new Insets3f(3, 3, 3, 3) // Adjust the border thickness
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
buttonCommands = stdButtonCommands
}
selector("slider", "pp") {
@@ -297,111 +231,6 @@ selector("tab.button", "pp") {
buttonCommands = stdButtonCommands
}
selector("settings-title", "pp") {
def outerBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)) // Grey inner border
def innerBackground = new QuadBackgroundComponent(buttonBgColor) // White outer border background
background = outerBackground
fontSize = 40
insets = new Insets3f(3, 3, 3, 3)
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
fontSize = 48 // Set font size
background = new QuadBackgroundComponent(color(0.4157f, 0.4235f, 0.4392f, 1.0f)) // Grey background
}
selector("warning-title", "pp") {
def outerBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)) // Grey inner border
def innerBackground = new QuadBackgroundComponent(buttonBgColor) // White outer border background
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-42.fnt")
background = outerBackground
fontSize = 40
insets = new Insets3f(3, 3, 3, 3)
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
}
selector("menu-button", "pp") {
fontSize = 40 // Set font size
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
buttonCommands = stdButtonCommands
}
// Style for Selector text
selector("selector.item.label") {
color = color(0, 0, 0, 1) // Black text
fontSize = 16 // Optional: Adjust the text size if needed
textHAlignment = HAlignment.Left // Optional: Align text to the left
insets = new Insets3f(2, 2, 2, 2) // Optional: Add padding around text
}
// Style the popup container background
selector("selector.popup") {
background = new QuadBackgroundComponent(new ColorRGBA(1, 1, 1, 0.8f)) // Translucent white background
insets = new Insets3f(5, 5, 5, 5) // Padding inside the popup container
}
// Style the text of dropdown options
selector("selector.item.label") {
color = color(0, 0, 0, 1) // Black text
fontSize = 16 // Optional: Adjust font size
textHAlignment = HAlignment.Left // Align text to the left
insets = new Insets3f(2, 5, 2, 5) // Add padding for each option
}
// Style the hover state of dropdown options
selector("selector.item.label", "hover") {
color = color(1, 1, 1, 1) // White text when hovered
background = new QuadBackgroundComponent(new ColorRGBA(0.2f, 0.6f, 1.0f, 0.9f)) // Highlighted background
}
def enabledCommandToolbar = new Command<Button>() {
MonopolyApp app // Pass the app instance to access player details
void execute(Button source) {
// Get the current player's color
Player currentPlayer = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId());
ColorRGBA playerColor = Player.getColor(currentPlayer.getId()).getColor();
if (source.isEnabled()) {
source.setColor(ColorRGBA.White);
def playerBackground = new QuadBackgroundComponent(playerColor); // Use player's color
source.setBackground(playerBackground);
} else {
source.setColor(ColorRGBA.White);
def grayBackground = new QuadBackgroundComponent(ColorRGBA.Gray); // Use gray when disabled
source.setBackground(grayBackground);
}
}
}
def stdButtonCommandsToolbar = [
(ButtonAction.Down) : [pressedCommand],
(ButtonAction.Up) : [pressedCommand],
(ButtonAction.Enabled) : [enabledCommandToolbar],
(ButtonAction.Disabled): [enabledCommandToolbar]
]
selector("button-toolbar", "pp") {
def outerBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)) // Orange border
def innerBackground = new QuadBackgroundComponent(buttonBgColor) // Inner button background
// Apply the outer border as the main background
background = outerBackground
// Use insets to create a margin/padding effect for the inner background
insets = new Insets3f(3, 3, 3, 3) // Adjust the border thickness
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
buttonCommands = stdButtonCommandsToolbar
}
selector("button-clear", "pp") { playerColor ->
def validColor = playerColor ?: new ColorRGBA(0, 0, 0, 0) // Vollständig transparent
def playerGradientBackground = new QuadBackgroundComponent(validColor)
// Kein Hintergrundbild, komplett transparent
playerGradientBackground.setColor(new ColorRGBA(0, 0, 0, 0)) // RGBA (Rot, Grün, Blau, Alpha)
background = playerGradientBackground.clone() // Setze den Hintergrund
insets = new Insets3f(-3, -3, -3, -3) // Optional: Ränder
textHAlignment = HAlignment.Center // Text-Zentrierung
textVAlignment = VAlignment.Center // Text-Zentrierung
}

View File

@@ -1,6 +1,5 @@
plugins {
id 'buildlogic.jme-application-conventions'
id 'com.github.johnrengelman.shadow' version '8.1.1'
}
description = 'Monopoly Client'
@@ -10,12 +9,7 @@ dependencies {
implementation project(":monopoly:model")
implementation project(":monopoly:server")
implementation 'com.simsilica:lemur-proto:1.13.0'
implementation libs.jme3.desktop
implementation libs.lemur
implementation libs.lemurproto
implementation libs.selenium
runtimeOnly libs.jme3.awt.dialogs
runtimeOnly libs.jme3.plugins
@@ -27,12 +21,3 @@ application {
mainClass = 'pp.monopoly.client.MonopolyApp'
applicationName = 'monopoly'
}
shadowJar {
manifest {
attributes(
'Main-Class': 'pp.monopoly.client.MonopolyApp'
)
}
}

View File

@@ -5,13 +5,47 @@
## (c) Mark Minas (mark.minas@unibw.de)
########################################
#
# Monopoly client configuration
# 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
#

View File

@@ -1,277 +0,0 @@
package pp.monopoly.client;
import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
import com.jme3.effect.ParticleEmitter;
import com.jme3.effect.ParticleMesh;
import com.jme3.effect.shapes.EmitterSphereShape;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.shadow.DirectionalLightShadowRenderer;
import com.jme3.shadow.EdgeFilteringMode;
import com.jme3.texture.Texture;
import com.jme3.util.SkyFactory;
import com.jme3.util.TangentBinormalGenerator;
import pp.monopoly.client.gui.BobTheBuilder;
import pp.monopoly.client.gui.Toolbar;
import pp.monopoly.model.Board;
import pp.monopoly.client.gui.FigureControl;
import static pp.util.FloatMath.TWO_PI;
import static pp.util.FloatMath.cos;
import static pp.util.FloatMath.sin;
import static pp.util.FloatMath.sqrt;
/**
* Manages the rendering and visual aspects of the sea and sky in the Battleship game.
* This state is responsible for setting up and updating the sea, sky, and lighting
* conditions, and controls the camera to create a dynamic view of the game environment.
*/
public class BoardAppState extends MonopolyAppState {
/**
* The path to the unshaded texture material.
*/
private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
/**
* The path to the sea texture material.
*/
private static final String BoardTexture = "Pictures/board2.png"; //NON-NLS
/**
* The root node for all visual elements in this state.
*/
private final Node viewNode = new Node("view"); //NON-NLS
/**
* The node containing the scene elements, such as the sea surface.
*/
private final Node sceneNode = new Node("scene"); //NON-NLS
/**
* Synchronizes the buildings's visual representation with the game logic.
*/
private BobTheBuilder bobTheBuilder;
/**
* The pop-up manager for displaying messages and notifications.
*/
private PopUpManager popUpManager;;
private Vector3f currentTarget = new Vector3f(0f,0,0f);
/**
* Initializes the state by setting up the sky, lights, and other visual components.
* This method is called when the state is first attached to the state manager.
*
* @param stateManager the state manager
* @param application the application
*/
@Override
public void initialize(AppStateManager stateManager, Application application) {
super.initialize(stateManager, application);
popUpManager = new PopUpManager(getApp());
viewNode.attachChild(sceneNode);
setupLights();
setupSky();
}
/**
* Enables the sea and sky state, setting up the scene and registering any necessary listeners.
* This method is called when the state is set to active.
*/
@Override
protected void enableState() {
getApp().getRootNode().detachAllChildren();
getApp().getGuiNode().detachAllChildren();
new Toolbar(getApp()).open();
sceneNode.detachAllChildren();
setupScene();
if (bobTheBuilder == null) {
bobTheBuilder = new BobTheBuilder(getApp(), sceneNode);
getGameLogic().addListener(bobTheBuilder);
}
getApp().getRootNode().attachChild(viewNode);
}
//TODO remove this only for camera testing
private static final float ABOVE_SEA_LEVEL = 10f;
private static final float INCLINATION = 2.5f;
private float cameraAngle;
/**
* Adjusts the camera position and orientation to create a circular motion around
* the center of the map. This provides a dynamic view of the sea and surrounding environment.
*/
private void adjustCamera() {
final Board board = getGameLogic().getBoard();
final float mx = 0.5f * board.getWidth();
final float my = 0.5f * board.getHeight();
final float radius = 2f * sqrt(mx * mx + my + my);
final float cos = radius * cos(cameraAngle);
final float sin = radius * sin(cameraAngle);
final float x = mx - cos;
final float y = my - sin;
final Camera camera = getApp().getCamera();
camera.setLocation(new Vector3f(30,20,0));
camera.lookAt(new Vector3f(getCurrentTarget()),
Vector3f.UNIT_Y);
camera.update();
}
/**
* Disables the sea and sky state, removing visual elements from the scene and unregistering listeners.
* This method is called when the state is set to inactive.
*/
@Override
protected void disableState() {
getApp().getRootNode().detachChild(viewNode);
if (bobTheBuilder != null) {
getGameLogic().removeListener(bobTheBuilder);
bobTheBuilder = null;
}
}
/**
* Updates the state each frame, moving the camera to simulate it circling around the map.
*
* @param tpf the time per frame (seconds)
*/
@Override
public void update(float tpf) {
super.update(tpf);
//TODO remove this only for camera testing
cameraAngle += TWO_PI * 0.05f * tpf;
adjustCamera();
}
/**
* Sets up the lighting for the scene, including directional and ambient lights.
* Also configures shadows to enhance the visual depth of the scene.
*/
private void setupLights() {
final AssetManager assetManager = getApp().getAssetManager();
final DirectionalLightShadowRenderer shRend = new DirectionalLightShadowRenderer(assetManager, 2048, 3);
shRend.setLambda(0.55f);
shRend.setShadowIntensity(0.6f);
shRend.setEdgeFilteringMode(EdgeFilteringMode.Bilinear);
getApp().getViewPort().addProcessor(shRend);
final DirectionalLight sun = new DirectionalLight();
sun.setDirection(new Vector3f(-1f, -0.7f, -1f).normalizeLocal());
viewNode.addLight(sun);
shRend.setLight(sun);
final AmbientLight ambientLight = new AmbientLight(new ColorRGBA(1f, 1f, 1f, 1f));
viewNode.addLight(ambientLight);
}
/**
* Sets up the sky in the scene using a skybox with textures for all six directions.
* This creates a realistic and immersive environment for the sea.
*/
private void setupSky() {
final AssetManager assetManager = getApp().getAssetManager();
final Texture west = assetManager.loadTexture("Pictures/Backdrop/west.jpg"); //NON-NLS
final Texture east = assetManager.loadTexture("Pictures/Backdrop/ost.jpg"); //NON-NLS
final Texture north = assetManager.loadTexture("Pictures/Backdrop/nord.jpg"); //NON-NLS
final Texture south = assetManager.loadTexture("Pictures/Backdrop/sued.jpg"); //NON-NLS
final Texture up = assetManager.loadTexture("Pictures/Backdrop/sued.jpg"); //NON-NLS
final Texture down = assetManager.loadTexture("Pictures/Backdrop/sued.jpg"); //NON-NLS
final Spatial sky = SkyFactory.createSky(assetManager, west, east, north, south, up, down);
// sky.rotate(0, PI, 0);
viewNode.attachChild(sky);
}
/**
* Sets up the sea surface in the scene. This includes creating the sea mesh,
* applying textures, and enabling shadows.
*/
private void setupScene() {
final Board board = getGameLogic().getBoard();
final float x = board.getWidth();
final float y = board.getHeight();
final Box seaMesh = new Box(y, 0.1f, x);
final Geometry seaGeo = new Geometry("sea", seaMesh); //NON-NLS
seaGeo.setLocalTranslation(new Vector3f(0, -0.1f, 0));
Quaternion rotation = new Quaternion();
rotation.fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y);
seaGeo.setLocalRotation(rotation);
final Material seaMat = new Material(getApp().getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
Texture texture = getApp().getAssetManager().loadTexture("Pictures/board2.png");
seaMat.setTexture("DiffuseMap", texture);
seaGeo.setMaterial(seaMat);
seaGeo.setShadowMode(ShadowMode.CastAndReceive);
TangentBinormalGenerator.generate(seaGeo);
sceneNode.attachChild(createCardDeck());
sceneNode.attachChild(seaGeo);
// Schneefall hinzufügen
addSnowEffect(sceneNode);
}
private Node createCardDeck() {
Node cardDeck = new Node("cardDeck");
Spatial card = getApp().getAssetManager().loadModel("models/Kartendecks/Ereigniskarten_Deck.j3o");
card.setLocalTranslation(5.5f, 0, 2.7f);
card.setLocalScale(4.1f);
card.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.QUARTER_PI, Vector3f.UNIT_Y));
Spatial card2 = getApp().getAssetManager().loadModel("models/Kartendecks/Gemeinschaftskarten_Deck.j3o");
card2.setLocalTranslation(-1.4f, 0, -3.8f);
card2.setLocalScale(4.1f);
card2.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.QUARTER_PI , Vector3f.UNIT_Y));
cardDeck.attachChild(card);
cardDeck.attachChild(card2);
return cardDeck;
}
public Vector3f getCurrentTarget(){
return currentTarget;
}
private void addSnowEffect(Node parentNode) {
// ParticleEmitter für Schnee
ParticleEmitter snowEmitter = new ParticleEmitter("Snow", ParticleMesh.Type.Triangle, 5000);
Material snowMat = new Material(getApp().getAssetManager(), "Common/MatDefs/Misc/Particle.j3md");
snowMat.setTexture("Texture", getApp().getAssetManager().loadTexture("Textures/snowflake.png")); // Schneeflocken-Textur
snowEmitter.setMaterial(snowMat);
// Eigenschaften für Schneepartikel
snowEmitter.setImagesX(1);
snowEmitter.setImagesY(1);
snowEmitter.setEndColor(new ColorRGBA(1f, 1f, 1f, 0.5f)); // Weiß, halbtransparent
snowEmitter.setStartColor(new ColorRGBA(1f, 1f, 1f, 1f)); // Vollweiß
snowEmitter.setStartSize(0.1f);
snowEmitter.setEndSize(0.2f);
snowEmitter.setGravity(0, 0.5f, 0); // Langsames Fallen
snowEmitter.setLowLife(3f);
snowEmitter.setHighLife(15f);
snowEmitter.getParticleInfluencer().setInitialVelocity(new Vector3f(0, -1, 0));
snowEmitter.getParticleInfluencer().setVelocityVariation(0.3f);
// Spawn-Bereich für Schneeflocken definieren
snowEmitter.setParticlesPerSec(200);
snowEmitter.setLocalTranslation(0, 10, 0);
snowEmitter.setShape(new EmitterSphereShape(new Vector3f(0, 0, 0), 15)); // Bereich von -15 bis 15
// Emitter zur Szene hinzufügen
parentNode.attachChild(snowEmitter);
}
}

View File

@@ -1,203 +0,0 @@
package pp.monopoly.client;
import static pp.util.PreferencesUtils.getPreferences;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.prefs.Preferences;
import com.jme3.app.Application;
import com.jme3.app.state.AbstractAppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetLoadException;
import com.jme3.asset.AssetNotFoundException;
import com.jme3.audio.AudioData;
import com.jme3.audio.AudioNode;
/**
* Handles the background and secondary music in the game.
* Allows playing, stopping, and toggling between background music and a secondary track.
*/
public class GameMusic extends AbstractAppState {
private static final Logger LOGGER = System.getLogger(pp.monopoly.client.GameMusic.class.getName());
private static final Preferences PREFERENCES = getPreferences(pp.monopoly.client.GameMusic.class);
private static final String ENABLED_PREF = "enabled"; // NON-NLS
private static final String VOLUME_PREF = "volume"; // NON-NLS
private AudioNode mainMusic;
private AudioNode secondaryMusic;
private boolean isMainMusicPlaying = false;
private boolean isSecondaryMusicPlaying = false;
/**
* Initializes the GameMusic app state and loads the background music.
*
* @param stateManager The state manager
* @param app The application instance
*/
@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
mainMusic = loadSound(app, "Sound/background.ogg");
secondaryMusic = loadSound(app, "Sound/ChooseYourCharakter.ogg");
setVolume(volumeInPreferences());
if (isEnabled()) {
playMainMusic();
}
}
/**
* Loads a sound from the specified file.
*
* @param app The application
* @param name The name of the sound file.
* @return The loaded AudioNode.
*/
private AudioNode loadSound(Application app, String name) {
try {
final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
sound.setLooping(true);
sound.setPositional(false);
return sound;
}
catch (AssetLoadException | AssetNotFoundException ex) {
LOGGER.log(Level.ERROR, ex.getMessage(), ex);
}
return null;
}
/**
* Plays the main music.
*/
public void playMainMusic() {
if (!isEnabled()) {
return; // Sound is disabled
}
if (mainMusic != null && !isMainMusicPlaying) {
mainMusic.play();
isMainMusicPlaying = true;
}
}
/**
* Stops the main music.
*/
private void stopMainMusic() {
if (mainMusic != null && isMainMusicPlaying) {
mainMusic.stop();
isMainMusicPlaying = false;
}
}
/**
* Plays the secondary music and stops the main music.
*
*/
public void playSecondaryMusic() {
if (!isEnabled()) {
stopAllMusic();
return;
}
if (isSecondaryMusicPlaying) {
return; // Secondary music is already playing
}
stopMainMusic();
if (secondaryMusic != null) {
secondaryMusic.setVolume(volumeInPreferences());
secondaryMusic.play();
isSecondaryMusicPlaying = true;
}
}
/**
* Stops the secondary music.
*/
public void stopSecondaryMusic() {
if (secondaryMusic != null && isSecondaryMusicPlaying) {
secondaryMusic.stop();
isSecondaryMusicPlaying = false;
}
}
/**
* Toggles between the background music and the secondary track.
* If the secondary track is playing, it stops and resumes the background music.
* If the background music is playing, it pauses and plays the secondary track.
*
*/
public void toggleMusic() {
if (!isEnabled()) {
stopAllMusic();
return;
}
if (isSecondaryMusicPlaying) {
stopSecondaryMusic();
playMainMusic();
} else {
stopMainMusic();
playSecondaryMusic();
}
}
/**
* Sets the audio volume for both the main and secondary tracks.
*
* @param vol The volume level (0.0f to 1.0f)
*/
public void setVolume(float vol) {
if (mainMusic != null) mainMusic.setVolume(vol);
if (secondaryMusic != null) secondaryMusic.setVolume(vol);
PREFERENCES.putFloat(VOLUME_PREF, vol);
}
/**
* Stops all music (both main and secondary).
*/
public void stopAllMusic() {
stopMainMusic();
stopSecondaryMusic();
}
/**
* Enables or disables the sound system.
* When disabled, all music stops.
*
* @param enabled {@code true} to enable, {@code false} to disable
*/
@Override
public void setEnabled(boolean enabled) {
if (isEnabled() == enabled) return; // Avoid redundant operations
PREFERENCES.putBoolean(ENABLED_PREF, enabled);
if (enabled) {
playMainMusic();
} else {
stopAllMusic();
}
super.setEnabled(enabled);
LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); // NON-NLS
PREFERENCES.putBoolean(ENABLED_PREF, enabled);
}
/**
* Retrieves the current sound volume preference.
*
* @return The volume level (0.0f to 1.0f)
*/
public static float volumeInPreferences() {
return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
}
/**
* Checks if sound is enabled in the preferences.
*
* @return {@code true} if sound is enabled, {@code false} otherwise
*/
public static boolean enabledInPreferences() {
return PREFERENCES.getBoolean(ENABLED_PREF, true);
}
}

View File

@@ -27,38 +27,13 @@ import static pp.util.PreferencesUtils.getPreferences;
* An application state that plays sounds.
*/
public class GameSound extends AbstractAppState implements GameEventListener {
/**
* Logger instance for logging messages related to GameSound.
*/
private static final Logger LOGGER = System.getLogger(GameSound.class.getName());
/**
* Preferences instance for managing GameSound-related settings.
*/
private static final Preferences PREFERENCES = getPreferences(GameSound.class);
/**
* Preference key for enabling or disabling GameSound.
*/
private static final String ENABLED_PREF = "enabled"; //NON-NLS
/**
* Preference key for storing the volume level of GameSound.
*/
private static final String VOLUME_PREF = "volume"; //NON-NLS
private AudioNode passStartSound;
private AudioNode eventCardSound;
private AudioNode gulagSound;
private AudioNode diceRollSound;
private AudioNode moneyCollectSound;
private AudioNode moneyLostSound;
private AudioNode tradeAcceptedSound;
private AudioNode tradeRejectedSound;
private AudioNode winnerSound;
private AudioNode looserSound;
private AudioNode buttonSound;
private AudioNode splashSound;
private AudioNode shipDestroyedSound;
private AudioNode explosionSound;
/**
* Checks if sound is enabled in the preferences.
@@ -76,15 +51,6 @@ public class GameSound extends AbstractAppState implements GameEventListener {
setEnabled(!isEnabled());
}
/**
* Checks if sound is enabled in the preferences.
*
* @return float to which the volume is set
*/
public static float volumeInPreferences() {
return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
}
/**
* Sets the enabled state of this AppState.
* Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)}
@@ -109,18 +75,6 @@ public class GameSound extends AbstractAppState implements GameEventListener {
@Override
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
passStartSound = loadSound(app, "Sound/Effects/passStart.ogg");
eventCardSound = loadSound(app, "Sound/Effects/eventCard.ogg");
gulagSound = loadSound(app, "Sound/Effects/gulag.ogg");
diceRollSound = loadSound(app, "Sound/Effects/diceRoll.ogg");
moneyCollectSound = loadSound(app, "Sound/Effects/moneyCollect.ogg");
moneyLostSound = loadSound(app, "Sound/Effects/moneyLost.ogg");
tradeAcceptedSound = loadSound(app, "Sound/Effects/tradeAccepted.ogg");
tradeRejectedSound = loadSound(app, "Sound/Effects/tradeRejected.ogg");
winnerSound = loadSound(app, "Sound/Effects/winner.ogg");
looserSound = loadSound(app, "Sound/Effects/loser.ogg");
buttonSound = loadSound(app, "Sound/Effects/button.ogg");
setVolume(volumeInPreferences());
}
/**
@@ -144,121 +98,32 @@ public class GameSound extends AbstractAppState implements GameEventListener {
}
/**
* Plays the passStart sound effect.
* Plays the splash sound effect.
*/
public void passStart() {
if (isEnabled() && passStartSound != null)
passStartSound.playInstance();
}
/**
* Plays the eventCard sound effect.
*/
public void eventCard() {
if (isEnabled() && eventCardSound != null)
eventCardSound.playInstance();
}
/**
* Plays the gulag sound effect.
*/
public void gulag() {
if (isEnabled() && gulagSound != null)
gulagSound.playInstance();
}
/**
* Plays the diceRoll sound effect.
*/
public void diceRoll() {
if (isEnabled() && diceRollSound != null)
diceRollSound.playInstance();
}
/**
* Plays the moneyCollect sound effect.
*/
public void moneyCollect() {
if (isEnabled() && moneyCollectSound != null)
moneyCollectSound.playInstance();
}
/**
* Plays the moneyLost sound effect.
*/
public void moneyLost() {
if (isEnabled() && moneyLostSound != null)
moneyLostSound.playInstance();
}
/**
* Plays the tradeAccepted sound effect.
*/
public void tradeAccepted() {
if (isEnabled() && tradeAcceptedSound != null)
tradeAcceptedSound.playInstance();
}
/**
* Plays the tradeRejected sound effect.
*/
public void tradeRejected() {
if (isEnabled() && tradeRejectedSound != null)
tradeRejectedSound.playInstance();
}
/**
* Plays the winner sound effect.
*/
public void winner() {
if (isEnabled() && winnerSound != null)
winnerSound.playInstance();
}
/**
* Plays the looser sound effect.
*/
public void looser() {
if (isEnabled() && looserSound != null)
looserSound.playInstance();
}
/**
* Plays the button sound effect.
*/
public void button() {
if (isEnabled() && buttonSound != null)
buttonSound.playInstance();
}
/**
* Sets the volume of the sounds
* @param vol the volume to which the sounds should be set
*/
public void setVolume(float vol){
passStartSound.setVolume(vol);
eventCardSound.setVolume(vol);
gulagSound.setVolume(vol);
diceRollSound.setVolume(vol);
moneyCollectSound.setVolume(vol);
moneyLostSound.setVolume(vol);
tradeAcceptedSound.setVolume(vol);
tradeRejectedSound.setVolume(vol);
winnerSound.setVolume(vol);
looserSound.setVolume(vol);
buttonSound.setVolume(vol);
PREFERENCES.putFloat(VOLUME_PREF, vol);
public void splash() {
if (isEnabled() && splashSound != null)
splashSound.playInstance();
}
/**
* Overrides {@link SoundEvent#notifyListener(GameEventListener)}
* @param event the received event
* Plays the explosion sound effect.
*/
public void explosion() {
if (isEnabled() && explosionSound != null)
explosionSound.playInstance();
}
/**
* Plays sound effect when a ship has been destroyed.
*/
public void shipDestroyed() {
if (isEnabled() && shipDestroyedSound != null)
shipDestroyedSound.playInstance();
}
@Override
public void receivedEvent(SoundEvent event) {
switch (event.sound()) {
case PASS_START -> passStart();
case EVENT_CARD -> eventCard();
case GULAG -> gulag();
case DICE_ROLL -> diceRoll();
case MONEY_COLLECTED -> moneyCollect();
case MONEY_LOST -> moneyLost();
case TRADE_ACCEPTED -> tradeAccepted();
case TRADE_REJECTED -> tradeRejected();
case WINNER -> winner();
case LOSER -> looser();
case BUTTON -> button();
}
}
}

View File

@@ -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();
}
}

View File

@@ -1,497 +1,210 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.client;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.LogManager;
import java.awt.Image;
import javax.imageio.ImageIO;
import com.jme3.app.DebugKeysAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.app.StatsAppState;
import com.jme3.font.BitmapFont;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.system.AppSettings;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.style.BaseStyles;
import pp.dialog.DialogBuilder;
import pp.dialog.DialogManager;
import pp.graphics.Draw;
import static pp.monopoly.Resources.lookup;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.client.gui.StartMenu;
import pp.monopoly.client.gui.TestWorld;
import pp.monopoly.game.client.ClientGameLogic;
import pp.monopoly.game.client.MonopolyClient;
import pp.monopoly.game.client.ServerConnection;
import pp.monopoly.message.client.NotificationAnswer;
import pp.monopoly.notification.ClientStateEvent;
import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.InfoTextEvent;
import pp.monopoly.notification.Sound;
/**
* The main class for the Monopoly client application.
* It manages the initialization, input setup, GUI setup, and game states for the client.
*/
public class MonopolyApp extends SimpleApplication implements MonopolyClient, GameEventListener {
/**
* Logger for logging messages within the application.
*/
private static final Logger LOGGER = System.getLogger(MonopolyApp.class.getName());
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;
/**
* Path to the styles script for GUI elements.
*/
private static final String STYLES_SCRIPT = "Interface/Lemur/pp-styles.groovy"; //NON-NLS
/**
* Path to the font resource used in the GUI.
*/
private static final String FONT = "Interface/Fonts/Default.fnt"; //NON-NLS
/**
* Path to the client configuration file, if one exists.
*/
private static final File CONFIG_FILE = new File("client.properties");
/**
* Input mapping name for mouse clicks.
*/
public static final String CLICK = "CLICK";
/**
* Input mapping name for the Escape key.
*/
private static final String ESC = "ESC";
/**
* Manager for handling dialogs within the application.
*/
private final DialogManager dialogManager = new DialogManager(this);
/**
* The server connection instance, used for communicating with the game server.
*/
private final ServerConnection serverConnection;
/**
* Instance of the {@link Draw} class for rendering graphics.
*/
private Draw draw;
/**
* Text display at the top of the GUI for showing information to the user.
*/
private BitmapText topText;
/**
* Executor service for handling asynchronous tasks within the application.
*/
private ExecutorService executor;
/**
* Handler for managing the client's game logic.
*/
private final ClientGameLogic logic;
/**
* Configuration settings for the Battleship client application.
*/
private final MonopolyAppConfig config;
/**
* Listener for handling actions triggered by the Escape key.
*/
private final ActionListener escapeListener = (name, isPressed, tpf) -> escape(isPressed);
/**
* Listener for handeling Demo Mode (Minas mode)
*/
private final ActionListener f8Listener = (name, isPressed, tpf) -> handleF8(isPressed);
static {
// Configure logging
LogManager manager = LogManager.getLogManager();
try {
manager.readConfiguration(new FileInputStream("logging.properties"));
LOGGER.log(Level.INFO, "Successfully read logging properties"); //NON-NLS
}
catch (IOException e) {
LOGGER.log(Level.INFO, e.getMessage());
}
}
/**
* Starts the Monopoly application.
*
* @param args Command-line arguments for launching the application.
*/
public static void main(String[] args) {
new MonopolyApp().start();
}
/**
* Constructs a new {@code MonopolyApp} instance.
* Initializes the configuration, server connection, and game logic listeners.
*/
public MonopolyApp() {
this.draw = new Draw(assetManager);
config = new MonopolyAppConfig();
config.readFromIfExists(CONFIG_FILE);
serverConnection = makeServerConnection();
serverConnection = new NetworkSupport(this);
logic = new ClientGameLogic(serverConnection);
logic.addListener(this);
setShowSettings(config.getShowSettings());
setSettings(makeSettings());
}
/**
* Creates and configures application settings from the client configuration.
*
* @return A configured {@link AppSettings} object.
*/
private AppSettings makeSettings() {
final AppSettings settings = new AppSettings(true);
settings.setTitle(lookup("monopoly.name"));
try {
settings.setIcons(new Image[]{ImageIO.read(new File("src/main/resources/icons/Uniman.png"))});
}
catch (IOException e) {
LOGGER.log(Level.ERROR, e.getMessage());
}
settings.setResolution(config.getResolutionWidth(), config.getResolutionHeight());
settings.setFullscreen(config.fullScreen());
settings.setUseRetinaFrameBuffer(config.useRetinaFrameBuffer());
settings.setGammaCorrection(config.useGammaCorrection());
return settings;
}
/**
* Factory method for creating a server connection based on the current
* client configuration.
*
* @return A {@link ServerConnection} instance, which could be a real or mock server.
*/
private ServerConnection makeServerConnection() {
return new NetworkSupport(this);
}
/**
* Returns the dialog manager responsible for managing in-game dialogs.
*
* @return The {@link DialogManager} instance.
*/
public DialogManager getDialogManager() {
return dialogManager;
}
/**
* Returns the game logic handler for the client.
*
* @return The {@link ClientGameLogic} instance.
*/
@Override
public ClientGameLogic getGameLogic() {
return logic;
}
/**
* Returns the current configuration settings for the Monopoly client.
*
* @return The {@link pp.monopoly.game.client.MonopolyClientConfig} instance.
*/
@Override
public MonopolyAppConfig getConfig() {
return config;
}
/**
* Initializes the application.
* Sets up input mappings, GUI, game states, and connects to the server.
*/
@Override
public void simpleInitApp() {
setPauseOnLostFocus(false);
draw = new Draw(assetManager);
setupInput();
setupStates();
setupGui();
new StartMenu(this).open();
public ClientGameLogic getGameLogic() {
return logic;
}
/**
* Sets up the graphical user interface (GUI) for the application.
*/
private void setupGui() {
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);
BaseStyles.loadGlassStyle();
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);
final int height = context.getSettings().getHeight();
topText.setLocalTranslation(10f, height - 10f, 0f);
topText.setColor(config.getTopColor());
topText.setLocalTranslation(10, settings.getHeight() - 10, 0);
guiNode.attachChild(topText);
}
/**
* Configures input mappings and sets up listeners for user interactions.
*/
private void setupInput() {
inputManager.deleteMapping(INPUT_MAPPING_EXIT);
inputManager.setCursorVisible(false);
inputManager.addMapping(ESC, new KeyTrigger(KeyInput.KEY_ESCAPE));
inputManager.addMapping(CLICK, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addMapping("F8", new KeyTrigger(KeyInput.KEY_F8));
inputManager.addListener(f8Listener, "F8");
inputManager.addListener(escapeListener, ESC);
inputManager.setCursorVisible(true);
inputManager.addMapping("ESC", new KeyTrigger(KeyInput.KEY_ESCAPE));
inputManager.addListener(escapeListener, "ESC");
}
/**
* Handles the action on alt m for demo mode
* @param isPressed {@code true} is alt + m is pressed, {@code false} otherwise
*/
private void handleF8(boolean isPressed) {
private void handleEscape(boolean isPressed) {
if (isPressed) {
LOGGER.log(Level.INFO, "F detected."); // Debug logging
getGameLogic().send(new NotificationAnswer("hack"));
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);
}
}
}
/**
* Initializes and attaches the necessary application states for the game.
*/
private void setupStates() {
if (config.getShowStatistics()) {
final BitmapFont normalFont = assetManager.loadFont(FONT); //NON-NLS
final StatsAppState stats = new StatsAppState(guiNode, normalFont);
stateManager.attach(stats);
private void blockInputs() {
if (!inputBlocked) {
System.out.println("Blockiere Eingaben...");
inputManager.setCursorVisible(true); // Cursor sichtbar machen
inputManager.clearMappings(); // Alle Mappings entfernen
inputBlocked = true;
}
flyCam.setEnabled(false);
stateManager.detach(stateManager.getState(StatsAppState.class));
stateManager.detach(stateManager.getState(DebugKeysAppState.class));
attachGameSound();
attachGameMusic();
stateManager.attach(new BoardAppState());
}
/**
* Attaches the game sound state and sets its initial enabled state.
*/
private void attachGameSound() {
final GameSound gameSound = new GameSound();
logic.addListener(gameSound);
gameSound.setEnabled(GameSound.enabledInPreferences());
stateManager.attach(gameSound);
public void unblockInputs() {
if (inputBlocked) {
System.out.println("Aktiviere Eingaben...");
setupInput(); // Standard-Eingaben neu registrieren
inputBlocked = false;
}
}
/**
* Attaches the background music state and sets its initial enabled state.
*/
private void attachGameMusic() {
final GameMusic gameSound = new GameMusic();
gameSound.setEnabled(GameMusic.enabledInPreferences());
stateManager.attach(gameSound);
public void setInfoText(String text) {
topText.setText(text);
}
/**
* Updates the application state every frame.
* This method is called once per frame during the game loop.
*
* @param tpf Time per frame in seconds.
*/
@Override
public void simpleUpdate(float tpf) {
super.simpleUpdate(tpf);
dialogManager.update(tpf);
logic.update(tpf);
stateManager.update(tpf);
public void receivedEvent(InfoTextEvent event) {
setInfoText(event.key());
}
/**
* Handles the Escape key action to either close the top dialog or show the main menu.
*
* @param isPressed Indicates whether the Escape key is pressed.
*/
public void escape(boolean isPressed) {
if (!isPressed) return;
if (dialogManager.showsDialog())
dialogManager.escape();
else
new SettingsMenu(this).open();
@Override
public void stop(boolean waitFor) {
if (executor != null) executor.shutdownNow();
serverConnection.disconnect();
super.stop(waitFor);
}
public DialogManager getDialogManager() {
return dialogManager;
}
/**
* Returns the {@link Draw} instance used for rendering graphical elements in the game.
*
* @return The {@link Draw} instance.
*/
public Draw getDraw() {
return draw;
}
/**
* Tries to connect
*/
public void connect() {
serverConnection.connect();
}
/**
* Handles a request to close the application.
* If the request is initiated by pressing ESC, this parameter is true.
*
* @param esc If true, the request is due to the ESC key being pressed.
*/
@Override
public void requestClose(boolean esc) { /* do nothing */ }
/**
* Closes the application, displaying a confirmation dialog if the client is connected to a server.
*/
public void closeApp() {
if (serverConnection.isConnected())
confirmDialog(lookup("confirm.leaving"), this::close);
else
close();
}
/**
* Closes the application, disconnecting from the server and stopping the application.
*/
private void close() {
serverConnection.disconnect();
stop();
}
/**
* Updates the informational text displayed in the GUI.
*
* @param text The information text to display.
*/
public void setInfoText(String text) {
LOGGER.log(Level.DEBUG, "setInfoText {0}", text); //NON-NLS
topText.setText(text);
}
/**
* Updates the informational text in the GUI based on the key received in an {@link InfoTextEvent}.
*
* @param event The {@link InfoTextEvent} containing the key for the text to display.
*/
@Override
public void receivedEvent(InfoTextEvent event) {
LOGGER.log(Level.DEBUG, "received info text {0}", event.key()); //NON-NLS
setInfoText(lookup(event.key()));
}
/**
* Handles client state events to update the game states accordingly.
*
* @param event The {@link ClientStateEvent} representing the state change.
*/
@Override
public void receivedEvent(ClientStateEvent event) {
stateManager.getState(BoardAppState.class).setEnabled(logic.isTurn());
}
/**
* Returns the executor service used for handling multithreaded tasks.
*
* @return The {@link ExecutorService} instance.
*/
public ExecutorService getExecutor() {
if (executor == null)
executor = Executors.newCachedThreadPool();
return executor;
}
/**
* Stops the application, shutting down the executor service and halting execution.
*
* @param waitFor If true, waits for the application to stop before returning.
*/
@Override
public void stop(boolean waitFor) {
if (executor != null) executor.shutdownNow();
super.stop(waitFor);
public void closeApp() {
stop();
}
/**
* Displays a confirmation dialog with a specified question and action for the "Yes" button.
*
* @param question The question to display in the dialog.
* @param yesAction The action to perform if "Yes" is selected.
*/
public void confirmDialog(String question, Runnable yesAction) {
DialogBuilder.simple(dialogManager)
.setTitle(lookup("dialog.question"))
.setText(question)
.setOkButton(lookup("button.yes"), d -> {
getGameLogic().playSound(Sound.BUTTON); // Play sound
yesAction.run(); // Execute the original yesAction
})
.setNoButton(lookup("button.no"), d -> getGameLogic().playSound(Sound.BUTTON))
.build()
.open();
}
/**
* Displays an error dialog with the specified error message.
*
* @param errorMessage The error message to display in the dialog.
*/
public void errorDialog(String errorMessage) {
DialogBuilder.simple(dialogManager)
.setTitle(lookup("dialog.error"))
.setText(errorMessage)
.setOkButton(lookup("button.ok"), d -> getGameLogic().playSound(Sound.BUTTON))
.build()
.open();
.setTitle("Fehler")
.setText(errorMessage)
.setOkButton("OK")
.build()
.open();
}
/**
* Disconnects the current server connection.
*
* This method delegates the disconnection operation to the `disconnect` method of the
* `serverConnection` object.
*
*/
public void disconnect() {
serverConnection.disconnect();
public void setSettingsMenuOpen(boolean isOpen) {
this.isSettingsMenuOpen = isOpen;
}
/**
* Retrieves the unique identifier associated with the server connection.
*
* Checks if a Server is connected and returns 0 if there is no connection
*
* @return the ID of the connected Server instance.
*/
public int getId() {
if (serverConnection != null && serverConnection instanceof NetworkSupport) return ((NetworkSupport) serverConnection).getId();
return 0;
@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
}
}

View File

@@ -106,26 +106,6 @@ public class MonopolyAppConfig extends MonopolyClientConfig {
@Property("overlay.top.color") //NON-NLS
private ColorRGBA topColor = ColorRGBA.White;
private ColorRGBA applyGammaCorrection(ColorRGBA color) {
return new ColorRGBA(
correctGamma(color.r),
correctGamma(color.g),
correctGamma(color.b),
color.a // Alpha bleibt unverändert
);
}
private float correctGamma(float channel) {
// Formel: ((RGB / 255)^2.2) * 255
float normalized = channel / 255.0f; // RGB normalisieren (0-1)
float gammaCorrected = (float) Math.pow(normalized, 2.2); // ^2.2
return gammaCorrected * 255.0f; // Zurückskalieren auf 0-255
}
private float correctGamma(float channel, float gamma) {
return (float) Math.pow(channel, gamma);
}
/**
* Creates a default {@code MonopolyAppConfig} with predefined values.
*/

View File

@@ -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());
}
}

View File

@@ -1,40 +1,30 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.client;
import java.io.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.client.gui.CreateGameMenu;
import pp.monopoly.game.client.ServerConnection;
import pp.monopoly.message.client.ClientMessage;
import pp.monopoly.message.server.ServerMessage;
import java.io.IOException;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import static pp.monopoly.Resources.lookup;
/**
* Manages the network connection for the Monopoly application.
* Handles connecting to and disconnecting from the server, and sending messages.
*/
public class NetworkSupport implements MessageListener<Client>, ClientStateListener, ServerConnection {
class NetworkSupport implements MessageListener<Client>, ClientStateListener, ServerConnection {
private static final Logger LOGGER = System.getLogger(NetworkSupport.class.getName());
private final MonopolyApp app;
private Client client;
/**
* Constructs a NetworkSupport instance for the given Monopoly application.
* Constructs a NetworkSupport instance for the Monopoly application.
*
* @param app The Monopoly application instance.
*/
@@ -42,22 +32,12 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
this.app = app;
}
/**
* Return the client connections Id
*
* @return the client id
*/
public int getId() {
if (client == null) return 0;
return client.getId();
}
/**
* Returns the Monopoly application instance.
*
* @return Monopoly application instance
*/
public MonopolyApp getApp() {
MonopolyApp getApp() {
return app;
}
@@ -77,8 +57,9 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
*/
@Override
public void connect() {
if (client == null)
new CreateGameMenu(this).open();
if (client == null) {
new NetworkDialog(this).open();
}
}
/**
@@ -89,7 +70,7 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
if (client == null) return;
client.close();
client = null;
LOGGER.log(Level.INFO, "client closed"); //NON-NLS
LOGGER.log(Level.INFO, "Client connection closed.");
}
/**
@@ -99,9 +80,10 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
* @param port The server's port.
* @throws IOException If an I/O error occurs when creating the client.
*/
public void initNetwork(String host, int port) throws IOException {
if (client != null)
throw new IllegalStateException("trying to join a game again");
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);
@@ -116,9 +98,10 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
*/
@Override
public void messageReceived(Client client, Message message) {
LOGGER.log(Level.INFO, "message received from server: {0}", message); //NON-NLS
if (message instanceof ServerMessage serverMessage)
LOGGER.log(Level.INFO, "Message received from server: {0}", message);
if (message instanceof ServerMessage serverMessage) {
app.enqueue(() -> serverMessage.accept(app.getGameLogic()));
}
}
/**
@@ -128,7 +111,7 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
*/
@Override
public void clientConnected(Client client) {
LOGGER.log(Level.INFO, "Client connected: {0}", client); //NON-NLS
LOGGER.log(Level.INFO, "Successfully connected to server: {0}", client);
}
/**
@@ -139,13 +122,9 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
*/
@Override
public void clientDisconnected(Client client, DisconnectInfo disconnectInfo) {
LOGGER.log(Level.INFO, "Client {0} disconnected: {1}", client, disconnectInfo); //NON-NLS
if (this.client != client)
throw new IllegalArgumentException("parameter value must be client");
LOGGER.log(Level.INFO, "client still connected: {0}", client.isConnected()); //NON-NLS
LOGGER.log(Level.INFO, "Disconnected from server: {0}", disconnectInfo);
this.client = null;
disconnect();
app.enqueue(() -> app.setInfoText(lookup("lost.connection.to.server")));
app.enqueue(() -> app.setInfoText("Verbindung zum Server verloren."));
}
/**
@@ -155,10 +134,11 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
*/
@Override
public void send(ClientMessage message) {
LOGGER.log(Level.INFO, "sending {0}", message); //NON-NLS
if (client == null)
app.errorDialog(lookup("lost.connection.to.server"));
else
LOGGER.log(Level.INFO, "Sending message to server: {0}", message);
if (client == null) {
app.errorDialog("Verbindung zum Server verloren.");
} else {
client.send(message);
}
}
}

View File

@@ -1,97 +0,0 @@
package pp.monopoly.client;
import java.util.Timer;
import java.util.TimerTask;
import pp.monopoly.client.gui.popups.AcceptTrade;
import pp.monopoly.client.gui.popups.BuildingPropertyCard;
import pp.monopoly.client.gui.popups.ConfirmTrade;
import pp.monopoly.client.gui.popups.EventCardPopup;
import pp.monopoly.client.gui.popups.FoodFieldCard;
import pp.monopoly.client.gui.popups.GateFieldCard;
import pp.monopoly.client.gui.popups.Gulag;
import pp.monopoly.client.gui.popups.GulagInfo;
import pp.monopoly.client.gui.popups.LooserPopUp;
import pp.monopoly.client.gui.popups.NoMoneyWarning;
import pp.monopoly.client.gui.popups.ReceivedRent;
import pp.monopoly.client.gui.popups.RejectTrade;
import pp.monopoly.client.gui.popups.Rent;
import pp.monopoly.client.gui.popups.TimeOut;
import pp.monopoly.client.gui.popups.WinnerPopUp;
import pp.monopoly.message.server.NotificationMessage;
import pp.monopoly.message.server.TradeReply;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.model.fields.FoodField;
import pp.monopoly.model.fields.GateField;
import pp.monopoly.notification.EventCardEvent;
import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.PopUpEvent;
public class PopUpManager implements GameEventListener {
private final MonopolyApp app;
public PopUpManager(MonopolyApp app) {
this.app = app;
app.getGameLogic().addListener(this);
}
@Override
public void receivedEvent(PopUpEvent event) {
if (event.msg().equals("Buy")) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
app.enqueue(() -> {
int field = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
Object fieldObject = app.getGameLogic().getBoardManager().getFieldAtIndex(field);
if (fieldObject instanceof BuildingProperty) {
new BuildingPropertyCard(app).open();
} else if (fieldObject instanceof GateField) {
new GateFieldCard(app).open();
} else if (fieldObject instanceof FoodField) {
new FoodFieldCard(app).open();
}
});
}
}, 2500);
} else if (event.msg().equals("Winner")) {
new WinnerPopUp(app).open();
} else if (event.msg().equals("Looser")) {
new LooserPopUp(app).open();
} else if (event.msg().equals("timeout")) {
new TimeOut(app).open();
} else if (event.msg().equals("tradeRequest")) {
new ConfirmTrade(app).open();
} else if (event.msg().equals("goingToJail")) {
new Gulag(app).open();
} else if (event.msg().equals("NoMoneyWarning")) {
new NoMoneyWarning(app).open();
} else if(event.msg().equals("rent")) {
new Rent(app, ( (NotificationMessage) event.message()).getRentOwner(), ( (NotificationMessage) event.message()).getRentAmount() ).open();
} else if (event.msg().equals("jailtryagain")) {
new GulagInfo(app, 1).open();
} else if (event.msg().equals("jailpay")) {
new GulagInfo(app, 3).open();
} else if (event.msg().equals("tradepos")) {
new AcceptTrade(app, (TradeReply) event.message()).open();
} else if (event.msg().equals("tradeneg")) {
new RejectTrade(app, (TradeReply) event.message()).open();
} else if (event.msg().equals("ReceivedRent")) {
new ReceivedRent(app, ( (NotificationMessage) event.message()).getRentOwner(), ( (NotificationMessage) event.message()).getRentAmount() ).open();
}
}
@Override
public void receivedEvent(EventCardEvent event) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
app.enqueue(() -> new EventCardPopup(app, event.description()).open());
}
}, 2500);
}
}

View File

@@ -0,0 +1,182 @@
package pp.monopoly.client;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.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);
}
}

View File

@@ -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());
}
}
}

View File

@@ -1,147 +0,0 @@
package pp.monopoly.client.gui;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
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.model.Figure;
import pp.monopoly.model.Hotel;
import pp.monopoly.model.House;
import pp.monopoly.model.Item;
import pp.monopoly.notification.UpdatePlayerView;
public class BobTheBuilder extends GameBoardSynchronizer {
private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS
private static final String COLOR = "Color"; //NON-NLS
private static final String FIGURE = "figure"; //NON-NLS
private static final String HOUSE = "house"; //NON-NLS
private static final String HOTEL = "hotel"; //NON-NLS
private final MonopolyApp app;
public BobTheBuilder(MonopolyApp app, Node root) {
super(app.getGameLogic().getBoard(), root);
this.app = app;
addExisting();
}
@Override
public Spatial visit(Figure figure) {
final Node node = new Node(FIGURE);
Spatial spatial = createFigure(figure);
node.attachChild(spatial);
// Setze die Position basierend auf der Feld-ID
node.setLocalTranslation(figure.getPos());
// Setze die Anfangsrotation auf 90 Grad nach links
Quaternion initialRotation = new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y);
node.setLocalRotation(initialRotation);
node.addControl(new FigureControl(node, figure, app));
return node;
}
@Override
public Spatial visit(Hotel hotel) {
final Node node = new Node(HOTEL);
node.attachChild(createHotel(hotel));
// Setze die Position basierend auf der Feld-ID
node.setLocalTranslation(hotel.getPos());
// Setze die Rotation basierend auf der Feld-ID
node.setLocalRotation(hotel.getRot().toQuaternion());
return node;
}
@Override
public Spatial visit(House house) {
final Node node = new Node(HOUSE);
node.attachChild(createHouse(house));
// Setze die Position basierend auf der Feld-ID
node.setLocalTranslation(house.getPos());
// Setze die Rotation basierend auf der Feld-ID
node.setLocalRotation(house.getAlignment());
return node;
}
private Spatial createFigure(Figure figure) {
// Lade das Modell
Spatial model = app.getAssetManager().loadModel("models/" + "Spielfiguren/" + figure.getType() + "/" + figure.getType() + ".j3o");
// Skaliere und positioniere das Modell
model.scale(0.5f);
return model;
}
private Spatial createHotel(Hotel hotel) {
Spatial model = app.getAssetManager().loadModel("models/Hotel/Hotel.j3o");
model.scale(0.2f);
model.setShadowMode(ShadowMode.CastAndReceive);
return model;
}
private Spatial createHouse(House house) {
Spatial model = app.getAssetManager().loadModel("models/Haus/"+house.getStage()+"Haus.j3o");
model.scale(0.5f);
model.setShadowMode(ShadowMode.CastAndReceive);
return model;
}
/**
* Creates a simple box to represent a battleship that is not of the "King George V" type.
*
* @param ship the battleship to be represented
* @return the geometry representing the battleship as a box
*/
private Spatial createBox(Item item) {
final Box box = new Box(3,
3f,
3);
final Geometry geometry = new Geometry(FIGURE, box);
geometry.setMaterial(createColoredMaterial(ColorRGBA.Blue));
geometry.setShadowMode(ShadowMode.CastAndReceive);
geometry.setLocalTranslation(0, 2, 0);
return geometry;
}
/**
* Creates a new {@link Material} with the specified color.
* If the color includes transparency (i.e., alpha value less than 1),
* the material's render state is set to use alpha blending, allowing for
* semi-transparent rendering.
*
* @param color the {@link ColorRGBA} to be applied to the material. If the alpha value
* of the color is less than 1, the material will support transparency.
* @return a {@link Material} instance configured with the specified color and,
* if necessary, alpha blending enabled.
*/
private Material createColoredMaterial(ColorRGBA color) {
final Material material = new Material(app.getAssetManager(), UNSHADED);
if (color.getAlpha() < 1f)
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
material.setColor(COLOR, color);
return material;
}
}

View File

@@ -1,221 +0,0 @@
package pp.monopoly.client.gui;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import com.simsilica.lemur.*;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.popups.BuyHouse;
import pp.monopoly.client.gui.popups.RepayMortage;
import pp.monopoly.client.gui.popups.SellHouse;
import pp.monopoly.client.gui.popups.TakeMortage;
import pp.monopoly.notification.Sound;
/**
* Represents the building administration menu in the Monopoly application.
* <p>
* Provides options to manage properties, including building houses, demolishing houses,
* taking mortgages, repaying mortgages, and viewing an overview of owned properties.
* </p>
*/
public class BuildingAdminMenu extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the menu's UI components. */
private final Container mainContainer;
/** Background geometry for the menu. */
private Geometry background;
/** Button to navigate back to the previous menu. */
private final Button backButton = new Button("Zurück");
/** Button to build houses on properties. */
private final Button buildButton = new Button("Bauen");
/** Button to demolish houses on properties. */
private final Button demolishButton = new Button("Abriss");
/** Button to take out a mortgage on properties. */
private final Button takeMortgageButton = new Button("Hypothek aufnehmen");
/** Button to repay a mortgage on properties. */
private final Button payMortgageButton = new Button("Hypothek bezahlen");
/** Button to open the property overview menu. */
private final Button overviewButton = new Button("Übersicht");
/**
* Constructs the building administration menu.
*
* @param app the Monopoly application instance
*/
public BuildingAdminMenu(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
// Background Image
addBackgroundImage();
// Main container for the UI components
mainContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
mainContainer.setPreferredSize(new Vector3f(800, 600, 0));
mainContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(1, 1, 1, 0.7f))); // Translucent white background
// Add header
mainContainer.addChild(createHeaderContainer());
// Add content
mainContainer.addChild(createContent());
// Attach main container to GUI node
app.getGuiNode().attachChild(mainContainer);
mainContainer.setLocalTranslation(
(app.getCamera().getWidth() - mainContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + mainContainer.getPreferredSize().y) / 2,
7
);
}
/**
* Creates the header container.
*
* @return The header container.
*/
private Container createHeaderContainer() {
Container headerContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
headerContainer.setPreferredSize(new Vector3f(800, 100, 0));
Label headerLabel = headerContainer.addChild(new Label("Grundstücke Verwalten", new ElementId("header")));
headerLabel.setFontSize(45);
headerLabel.setInsets(new Insets3f(10, 10, 10, 10));
return headerContainer;
}
/**
* Creates the main content container with columns for Overview, Build, and Mortgage.
*
* @return The content container.
*/
private Container createContent() {
Container contentContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
contentContainer.setPreferredSize(new Vector3f(800, 500, 0));
// Overview Column
Container overviewColumn = new Container(new SpringGridLayout(Axis.Y, Axis.X));
overviewColumn.addChild(new Label("Übersicht:")).setFontSize(30);
//Building Overview Button
overviewButton.setPreferredSize(new Vector3f(200, 50, 0));
overviewButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
new PropertyOverviewMenu(app).open();
}));
overviewColumn.addChild(overviewButton);
// Back Button
backButton.setPreferredSize(new Vector3f(200, 50, 0));
backButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
overviewColumn.addChild(backButton);
contentContainer.addChild(overviewColumn);
// Build Column
Container buildColumn = new Container(new SpringGridLayout(Axis.Y, Axis.X));
buildColumn.addChild(new Label("Bauen:")).setFontSize(30);
// Build Houses Button
buildButton.setPreferredSize(new Vector3f(200, 50, 0));
buildButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
new BuyHouse(app).open();
}));
buildColumn.addChild(buildButton);
//Demolish Houses Button
demolishButton.setPreferredSize(new Vector3f(200, 50, 0));
demolishButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
new SellHouse(app).open();
}));
buildColumn.addChild(demolishButton);
contentContainer.addChild(buildColumn);
// Mortgage Column
Container mortgageColumn = new Container(new SpringGridLayout(Axis.Y, Axis.X));
mortgageColumn.addChild(new Label("Hypotheken:")).setFontSize(30);
// Lend Mortgage Button
takeMortgageButton.setPreferredSize(new Vector3f(200, 50, 0));
takeMortgageButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
new TakeMortage(app).open();
}));
mortgageColumn.addChild(takeMortgageButton);
//Repay Mortgage Button
payMortgageButton.setPreferredSize(new Vector3f(200, 50, 0));
payMortgageButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
new RepayMortage(app).open();
}));
mortgageColumn.addChild(payMortgageButton);
contentContainer.addChild(mortgageColumn);
return contentContainer;
}
/**
* Adds a background image to the dialog.
*/
private void addBackgroundImage() {
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
background = new Geometry("Background", quad);
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
backgroundMaterial.setTexture("ColorMap", backgroundImage);
background.setMaterial(backgroundMaterial);
background.setLocalTranslation(0, 0, 6); // Position behind other GUI elements
app.getGuiNode().attachChild(background);
}
/**
* Closes the building administration menu and detaches its elements from the GUI.
*/
@Override
public void close() {
app.getGuiNode().detachChild(mainContainer);
app.getGuiNode().detachChild(background);
super.close();
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
/**
* Periodic updates for the menu, if required.
*
* @param delta Time since the last update in seconds.
*/
@Override
public void update(float delta) {
// Periodic updates if necessary
}
}

View File

@@ -1,66 +1,59 @@
package pp.monopoly.client.gui;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
/**
* Controls the movement of the camera within the scene.
* Steuert die Kamerabewegung in der Szene.
*/
public class CameraController {
private final Camera camera;
private final float height = 25; // Height of the camera above the game board
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
/**
* Constructor for the CameraController.
* Konstruktor für den CameraController.
*
* @param camera The camera to be controlled
* @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) {
public CameraController(Camera camera, Vector3f center, float radius, float height, float speed) {
this.camera = camera;
setPosition(0);
camera.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
this.center = center;
this.radius = radius;
this.height = height;
this.speed = speed;
this.angle = 0; // Starte bei Winkel 0
}
/**
* Updates the camera's position and orientation.
* Aktualisiert die Kameraposition und -ausrichtung.
*
* @param tpf Time per frame
* @param tpf Zeit pro Frame
*/
public void update(float tpf) {
camera.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
}
// 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
}
/**
* Sets the camera's position based on the field ID.
*
* @param fieldID The ID of the field to which the camera should move
*/
public void setPosition(int fieldID) {
camera.setLocation(fieldIdToVector(fieldID));
}
// 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;
/**
* Sets the camera's position using specific coordinates.
*
* @param x The X-coordinate of the new camera position
* @param y The Y-coordinate of the new camera position
*/
public void setPosition(float x, float y) {
camera.setLocation(new Vector3f(x, height, y));
}
// Setze die Kameraposition
camera.setLocation(new Vector3f(x, y, z));
/**
* Maps a field ID to its corresponding position in the game world.
*
* @param fieldID The ID of the field
* @return The position of the field as a {@link Vector3f}
* @throws IllegalArgumentException If the field ID is invalid
*/
private Vector3f fieldIdToVector(int fieldID) {
if (fieldID <= 10) return new Vector3f(30, height, 0);
if (fieldID <= 20) return new Vector3f(0, height, 30);
if (fieldID <= 30) return new Vector3f(-30, height, 0);
if (fieldID <= 40) return new Vector3f(0, height, -30);
else throw new IllegalArgumentException();
// Lasse die Kamera auf den Fokuspunkt blicken
camera.lookAt(center, Vector3f.UNIT_Y);
}
}

View File

@@ -1,60 +0,0 @@
//package pp.monopoly.client.gui;
//
//import com.jme3.input.InputManager;
//import com.jme3.input.KeyInput;
//import com.jme3.input.controls.ActionListener;
//import com.jme3.input.controls.KeyTrigger;
//
///**
// * Handhabt die Eingaben für die Kamera.
// */
//public class CameraInputHandler {
//
// private CameraController cameraController; // Kamera-Controller
//
// /**
// * Konstruktor für den CameraInputHandler.
// *
// * @param cameraController Der Kamera-Controller, der gesteuert werden soll.
// * @param inputManager Der InputManager, um Eingaben zu registrieren.
// */
// public CameraInputHandler(CameraController cameraController, InputManager inputManager) {
// if (cameraController == null || inputManager == null) {
// throw new IllegalArgumentException("CameraController und InputManager dürfen nicht null sein");
// }
// this.cameraController = cameraController;
//
// // Mappings für Kamerasteuerung
// inputManager.addMapping("FocusCurrentPlayer", new KeyTrigger(KeyInput.KEY_1)); // Modus 1
// inputManager.addMapping("FocusSelf", new KeyTrigger(KeyInput.KEY_2)); // Modus 2
// inputManager.addMapping("FreeCam", new KeyTrigger(KeyInput.KEY_3)); // Modus 3
//
// // Listener für die Kameramodi
// inputManager.addListener(actionListener, "FocusCurrentPlayer", "FocusSelf", "FreeCam");
// }
//
// /**
// * ActionListener für die Kamerasteuerung.
// */
// private final ActionListener actionListener = (name, isPressed, tpf) -> {
// if (!isPressed) return;
//
// // Umschalten der Kamera-Modi basierend auf der Eingabe
// switch (name) {
// case "FocusCurrentPlayer" -> {
// cameraController.setMode(CameraController.CameraMode.FOCUS_CURRENT_PLAYER);
// System.out.println("Kameramodus: Fokus auf aktuellen Spieler");
// }
// case "FocusSelf" -> {
// cameraController.setMode(CameraController.CameraMode.FOCUS_SELF);
// System.out.println("Kameramodus: Fokus auf eigene Figur");
// }
// case "FreeCam" -> {
// cameraController.setMode(CameraController.CameraMode.FREECAM);
// System.out.println("Kameramodus: Freie Kamera");
// }
// default -> System.err.println("Unbekannter Kameramodus: " + name);
// }
// };
//}
//

View File

@@ -1,224 +0,0 @@
package pp.monopoly.client.gui;
import java.util.Set;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import com.simsilica.lemur.*;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.core.VersionedList;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.game.server.Player;
import pp.monopoly.message.client.ViewAssetsRequest;
import pp.monopoly.model.TradeHandler;
import pp.monopoly.notification.Sound;
public class ChoosePartner extends Dialog {
private final MonopolyApp app;
private Selector<String> playerSelector;
private final Button cancelButton = new Button("Abbrechen");
private final Button confirmButton = new Button("Bestätigen");
private final Container mainContainer;
private Container lowerLeftMenu;
private Container lowerRightMenu;
private Geometry background;
private TradeHandler tradeHandler;
private VersionedReference<Set<Integer>> selectionRef; // Reference to track selector changes
private String lastSelected = ""; // To keep track of the last selected value
QuadBackgroundComponent translucentWhiteBackground =
new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f));
/**
* Constructs the ChoosePartner dialog.
*
* @param app The Monopoly application instance.
*/
public ChoosePartner(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
tradeHandler = new TradeHandler(app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()));
app.getGameLogic().send(new ViewAssetsRequest());
// Background Image
addBackgroundImage();
// Main container for the UI components
mainContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
mainContainer.setPreferredSize(new Vector3f(1000, 600, 0));
mainContainer.setBackground(translucentWhiteBackground);
// Add title with background
Label headerLabel = mainContainer.addChild(new Label("Wähle deinen Handelspartner:", new ElementId("label-Bold")));
headerLabel.setFontSize(40);
headerLabel.setBackground(new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f)));
// Dropdown for player selection
mainContainer.addChild(createDropdown());
// Add buttons
mainContainer.addChild(createButtonContainer());
// Attach main container to GUI node
app.getGuiNode().attachChild(mainContainer);
mainContainer.setLocalTranslation(
(app.getCamera().getWidth() - mainContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + mainContainer.getPreferredSize().y) / 2,
4
);
// Initialize selection reference for tracking changes
selectionRef = playerSelector.getSelectionModel().createReference();
}
/**
* Creates the dropdown menu for selecting a partner.
*
* @return The dropdown container.
*/
private Container createDropdown() {
Container dropdownContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
dropdownContainer.setPreferredSize(new Vector3f(100, 80, 0));
dropdownContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(ColorRGBA.Black)));
VersionedList<String> playerOptions = new VersionedList<>();
for (Player player : app.getGameLogic().getPlayerHandler().getPlayers()) {
if (player.getId() != app.getId()) {
playerOptions.add(player.getName() + " (ID: " + player.getId() + ")");
}
}
playerSelector = new Selector<>(playerOptions, "glass");
dropdownContainer.addChild(playerSelector);
Vector3f dimens = dropdownContainer.getPreferredSize();
Vector3f dimens2 = playerSelector.getPopupContainer().getPreferredSize();
dimens2.setX(dimens.getX());
playerSelector.getPopupContainer().setPreferredSize(new Vector3f(200, 200, 3));
playerSelector.setLocalTranslation(0, 0, 5);
// Set initial selection
if (!playerOptions.isEmpty()) {
onDropdownSelectionChanged(playerOptions.get(0));
}
return dropdownContainer;
}
/**
* Creates the button container with cancel and confirm buttons.
*
* @return The button container.
*/
private Container createButtonContainer() {
Container buttonContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
buttonContainer.setBackground(translucentWhiteBackground);
// "Abbrechen" button
lowerLeftMenu = new Container();
cancelButton.setPreferredSize(new Vector3f(200, 60, 0));
cancelButton.setFontSize(30);
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
lowerLeftMenu.addChild(cancelButton);
// Position the container near the bottom-left corner
lowerLeftMenu.setLocalTranslation(new Vector3f(120, 170, 5)); // Adjust X and Y to align with the bottom-left corner
app.getGuiNode().attachChild(lowerLeftMenu);
// "Bestätigen" button
lowerRightMenu = new Container();
confirmButton.setPreferredSize(new Vector3f(200, 60, 0));
confirmButton.setFontSize(30);
confirmButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
new TradeMenu(app, tradeHandler).open();
}));
lowerRightMenu.addChild(confirmButton);
// Position the container near the bottom-right corner
lowerRightMenu.setLocalTranslation(new Vector3f(app.getCamera().getWidth() - 320, 170, 5)); // X: 220px from the right, Y: 50px above the bottom
app.getGuiNode().attachChild(lowerRightMenu);
return buttonContainer;
}
/**
* Adds a background image to the dialog.
*/
private void addBackgroundImage() {
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
background = new Geometry("Background", quad);
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
backgroundMaterial.setTexture("ColorMap", backgroundImage);
background.setMaterial(backgroundMaterial);
background.setLocalTranslation(0, 0, 3); // Position behind other GUI elements
app.getGuiNode().attachChild(background);
}
/**
* Handles the escape action for the dialog.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
/**
* Updates the dialog periodically, called by the dialog manager.
*
* @param delta The time elapsed since the last update.
*/
@Override
public void update(float delta) {
// Check if the selection has changed
if (selectionRef.update()) {
String selected = playerSelector.getSelectedItem();
if (!selected.equals(lastSelected)) {
lastSelected = selected;
onDropdownSelectionChanged(selected);
}
}
}
@Override
public void close() {
app.getGuiNode().detachChild(playerSelector);
app.getGuiNode().detachChild(lowerLeftMenu);
app.getGuiNode().detachChild(lowerRightMenu);
app.getGuiNode().detachChild(mainContainer);
app.getGuiNode().detachChild(background);
super.close();
}
/**
* Callback for when the dropdown selection changes.
*/
private void onDropdownSelectionChanged(String selected) {
app.getGameLogic().playSound(Sound.BUTTON);
int idStart = selected.indexOf("(ID: ") + 5; // Find start of the ID
int idEnd = selected.indexOf(")", idStart); // Find end of the ID
String idStr = selected.substring(idStart, idEnd); // Extract the ID as a string
int playerId = Integer.parseInt(idStr); // Convert the ID to an integer
// Find the player by ID
Player selectedPlayer = app.getGameLogic().getPlayerHandler().getPlayerById(playerId);
if (selectedPlayer != null) {
tradeHandler.setReceiver(selectedPlayer); // Set the receiver in TradeHandler
}
}
}

View File

@@ -1,226 +1,121 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.client.gui;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.SpringGridLayout;
import static pp.monopoly.Resources.lookup;
import com.simsilica.lemur.style.ElementId;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.NetworkSupport;
import pp.monopoly.notification.Sound;
import pp.monopoly.server.MonopolyServer;
import pp.dialog.Dialog;
import pp.dialog.DialogBuilder;
import pp.monopoly.client.StartMenu;
/**
* Represents a dialog for setting up a network connection in the Battleship game.
* Allows users to specify the host and port for connecting to a game server.
* CreateGameMenu class represents the menu for creating a new game.
*/
public class CreateGameMenu extends Dialog {
private static final Logger LOGGER = System.getLogger(CreateGameMenu.class.getName());
private static final String LOCALHOST = "localhost"; //NON-NLS
private static final String DEFAULT_PORT = "42069"; //NON-NLS
private final NetworkSupport network;
private final TextField host = new TextField(LOCALHOST);
private final TextField port = new TextField(DEFAULT_PORT);
private final Button serverButton = new Button("Selber hosten");
private final Button cancelButton = new Button("Abbrechen");
private final Button joinButton = new Button("Beitreten");
private String hostname;
private int portNumber;
private Future<Object> connectionFuture;
private Dialog progressDialog;
public class CreateGameMenu {
private final MonopolyApp app;
private final Container menuContainer;
private Geometry background;
/**
* Constructs a new CreateGameMenu.
* Konstruktor für das CreateGameMenu.
*
* @param network The NetworkSupport instance to be used for network operations.
* @param app Die Hauptanwendung (MonopolyApp)
*/
public CreateGameMenu(NetworkSupport network) {
super(network.getApp().getDialogManager());
this.network = network;
host.setSingleLine(true);
host.setPreferredWidth(400f);
port.setSingleLine(true);
public CreateGameMenu(MonopolyApp app) {
this.app = app;
final MonopolyApp app = network.getApp();
// Hintergrundbild laden und hinzufügen
addBackgroundImage();
int screenWidth = app.getContext().getSettings().getWidth();
int screenHeight = app.getContext().getSettings().getHeight();
// 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
// Set up the background image
// Titel
Label title = menuContainer.addChild(new Label("Neues Spiel"));
title.setFontSize(48);
// Eingabefelder-Container
Container inputContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X)));
inputContainer.setPreferredSize(new Vector3f(200, 150, 0)); // Eingabefelder nicht ganz so breit
inputContainer.setLocalTranslation(20, 0, 0); // Abstand vom Rand
inputContainer.addChild(new Label("Server-Adresse:"));
TextField serverAddressField = inputContainer.addChild(new TextField("localhost"));
serverAddressField.setPreferredWidth(400); // Breite des Textfelds
inputContainer.addChild(new Label("Port:"));
TextField portField = inputContainer.addChild(new TextField("42069"));
portField.setPreferredWidth(400); // Breite des Textfelds
// Button-Container
Container buttonContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
buttonContainer.setPreferredSize(new Vector3f(400, 50, 0));
buttonContainer.setLocalTranslation(20, 0, 0); // Abstand vom Rand
// "Abbrechen"-Button
Button cancelButton = buttonContainer.addChild(new Button("Abbrechen"));
cancelButton.setPreferredSize(new Vector3f(120, 40, 0));
cancelButton.addClickCommands(source -> goBackToStartMenu());
// "Spiel hosten"-Button
Button hostButton = buttonContainer.addChild(new Button("Spiel hosten"));
hostButton.setPreferredSize(new Vector3f(120, 40, 0));
hostButton.addClickCommands(source -> {
closeCreateGameMenu(); // Schließt das Menü
app.startTestWorld(); // Starte die TestWorld im selben Fenster
});
// "Beitreten"-Button
Button joinButton = buttonContainer.addChild(new Button("Beitreten"));
joinButton.setPreferredSize(new Vector3f(120, 40, 0));
// Placeholder für die Beitrittslogik
// Zentrierung des Containers
menuContainer.setLocalTranslation(
(app.getCamera().getWidth() - menuContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + menuContainer.getPreferredSize().y) / 2,
1 // Höhere Z-Ebene für den Vordergrund
);
app.getGuiNode().attachChild(menuContainer);
}
/**
* Lädt das Hintergrundbild und fügt es als geometrische Ebene hinzu.
*/
private void addBackgroundImage() {
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
Quad quad = new Quad(screenWidth, screenHeight);
Geometry background = new Geometry("Background", quad);
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); // Ensure it is behind other GUI elements
background.setLocalTranslation(0, 0, -1); // Hintergrundebene
app.getGuiNode().attachChild(background);
addChild(new Label("Spiel erstellen", new ElementId("header"))); //NON-NLS
final Container input = new Container(new SpringGridLayout());
input.addChild(new Label(lookup("host.name") + ": "));
input.addChild(host, 1);
input.addChild(new Label(lookup("port.number") + ": "));
input.addChild(port, 1);
addChild(input);
// "Abbrechen"-Button
cancelButton.setPreferredSize(new Vector3f(120, 40, 0));
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
this.close();
new StartMenu(network.getApp()).open();
}));
addChild(cancelButton);
// "Selber hosten"-Button
serverButton.addClickCommands(s -> ifTopDialog( () -> {
network.getApp().getGameLogic().playSound(Sound.BUTTON);
startServerInThread();
} ));
addChild(serverButton);
// "Beitreten"-Button
joinButton.setPreferredSize(new Vector3f(120, 40, 0));
joinButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
connect();
}));
addChild(joinButton);
}
/**
* Handles the action for the connect button in the connection dialog.
* Tries to parse the port number and initiate connection to the server.
* Geht zum Startmenü zurück, wenn "Abbrechen" angeklickt wird.
*/
private void connect() {
LOGGER.log(Level.INFO, "connect to host={0}, port={1}", host, port); //NON-NLS
try {
hostname = host.getText().trim().isEmpty() ? LOCALHOST : host.getText();
portNumber = Integer.parseInt(port.getText());
openProgressDialog();
connectionFuture = network.getApp().getExecutor().submit(this::initNetwork);
}
catch (NumberFormatException e) {
network.getApp().errorDialog(lookup("port.must.be.integer"));
}
private void goBackToStartMenu() {
closeCreateGameMenu(); // Schließt das Menü
StartMenu.createStartMenu(app); // Zeige das Startmenü
}
/**
* Creates a dialog indicating that the connection is in progress.
* Entfernt das CreateGameMenu und dessen Hintergrund.
*/
private void openProgressDialog() {
progressDialog = DialogBuilder.simple(network.getApp().getDialogManager())
.setText(lookup("label.connecting"))
.build();
progressDialog.open();
}
/**
* Tries to initialize the network connection.
*
* @throws RuntimeException If an error occurs when creating the client.
*/
private Object initNetwork() {
try {
network.initNetwork(hostname, portNumber);
return null;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public void escape() {
new SettingsMenu(network.getApp()).open();
}
/**
* This method is called by {@linkplain pp.dialog.DialogManager#update(float)} for periodically
* updating this dialog. T
*/
@Override
public void update(float delta) {
if (connectionFuture != null && connectionFuture.isDone())
try {
connectionFuture.get();
success();
}
catch (ExecutionException e) {
failure(e.getCause());
}
catch (InterruptedException e) {
LOGGER.log(Level.WARNING, "Interrupted!", e); //NON-NLS
Thread.currentThread().interrupt();
}
}
/**
* Handles a successful connection to the game server.
*/
private void success() {
connectionFuture = null;
progressDialog.close();
this.close();
new LobbyMenu(network.getApp()).open();
}
/**
* Handles a failed connection attempt.
*
* @param e The cause of the failure.
*/
private void failure(Throwable e) {
connectionFuture = null;
progressDialog.close();
network.getApp().errorDialog(lookup("server.connection.failed"));
network.getApp().setInfoText(e.getLocalizedMessage());
}
/**
* Starts the server in a separate thread.
*/
private void startServerInThread() {
serverButton.setEnabled(false);
Thread serverThread = new Thread(() -> {
try {
MonopolyServer.main(null);
} catch (Exception e) {
serverButton.setEnabled(true);
LOGGER.log(Level.ERROR, "Server could not be started", e);
network.getApp().errorDialog("Could not start server: " + e.getMessage());
}
});
serverThread.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
connect();
private void closeCreateGameMenu() {
app.getGuiNode().detachChild(menuContainer); // Entfernt den Menü-Container
app.getGuiNode().detachChild(background); // Entfernt das Hintergrundbild
}
}

View File

@@ -1,160 +0,0 @@
package pp.monopoly.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.control.AbstractControl;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.game.client.ClientGameLogic;
import pp.monopoly.model.Figure;
import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.UpdatePlayerView;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.LinkedList;
import java.util.Queue;
public class FigureControl extends AbstractControl implements GameEventListener {
private static final Logger LOGGER = System.getLogger(FigureControl.class.getName());
private final Figure figure;
private final Node spatial;
private final MonopolyApp app;
private Queue<Vector3f> path; // Path to follow
private Vector3f currentTarget;
private float animationTime = 0f; // Time elapsed for the current movement
private final float durationPerField = 0.5f; // Time to move between fields
private float delayTime = 3f; // Verzögerung in Sekunden
private float delayElapsed = 0f; // Zeit, die seit dem Start der Verzögerung vergangen ist
public FigureControl(Node spatial, Figure figure, MonopolyApp app) {
super();
this.figure = figure;
this.spatial = spatial;
this.app = app;
this.path = new LinkedList<>();
app.getGameLogic().addListener(this);
}
@Override
protected void controlUpdate(float tpf) {
if (delayTime > 0) {
delayElapsed += tpf;
if (delayElapsed < delayTime) {
return; // Warte, bis die Verzögerung abgeschlossen ist
}
delayTime = 0; // Verzögerung abgeschlossen
System.out.println("Delay completed. Starting animation...");
}
if (currentTarget == null && !path.isEmpty()) {
// Hole das nächste Ziel aus dem Pfad
currentTarget = path.poll();
animationTime = 0f;
// Prüfe, ob eine Drehung erforderlich ist (Felder 0, 10, 20, 30)
int currentField = figure.getCurrentFieldID();
int nextField = nextField(currentField);
if ((nextField(currentField) == 10) ||
(nextField(currentField) == 20) ||
(nextField(currentField) == 30) ||
(nextField(currentField) == 0)) {
Quaternion rotation = spatial.getLocalRotation();
Quaternion turnRight = new Quaternion().fromAngleAxis(-FastMath.HALF_PI, Vector3f.UNIT_Y);
spatial.setLocalRotation(rotation.mult(turnRight));
}
System.out.println("Next target: " + currentTarget);
}
if (currentTarget != null) {
animationTime += tpf;
Vector3f startPosition = spatial.getLocalTranslation();
Vector3f interpolatedPosition = startPosition.interpolateLocal(
currentTarget,
animationTime / durationPerField
);
// Hüpfeffekt hinzufügen
float hopHeight = 0.5f * FastMath.sin(FastMath.PI * (animationTime / durationPerField));
interpolatedPosition.setY(hopHeight );
spatial.setLocalTranslation(interpolatedPosition);
// Ziel erreicht
if (animationTime >= durationPerField) {
spatial.setLocalTranslation(currentTarget);
figure.moveTo(currentTarget); // Synchronisiere die interne Position
currentTarget = null; // Setze Ziel zurück
System.out.println("Target reached.");
}
}
}
private int nextField(int currentField) {
return (currentField + 1) % 40;
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
// No rendering logic required
}
public void setPath(int startField, int endField) {
LOGGER.log(Level.TRACE, "setPath called with startField: {0} to endField {1}", startField, endField);
path.clear();
for (int fieldId = startField; fieldId != endField; fieldId = (fieldId + 1) % 40) {
Vector3f position = figure.calculateFieldPosition(fieldId);
LOGGER.log(Level.DEBUG, "Adding postition to path: {0}", position);
path.add(position);
}
Vector3f finalPosition = figure.calculateFieldPosition(endField);
path.add(finalPosition);
LOGGER.log(Level.DEBUG, "Final position added to path: {0}", finalPosition);
LOGGER.log(Level.TRACE, "Path size: {0}", path.size());
}
@Override
public void receivedEvent(UpdatePlayerView event) {
LOGGER.log(Level.TRACE, "receivedEvent called with event: {0}", event);
int newPos = app.getGameLogic().getPlayerHandler().getPlayerById(figure.getId()).getFieldID();
int currentField = figure.getCurrentFieldID();
if (currentField == newPos) {
LOGGER.log(Level.DEBUG, "No movement required. Current field: {0}, New field: {1}", currentField, newPos);
return;
}
LOGGER.log(Level.DEBUG, "Movement required. Current field: {0}, New field: {1}", currentField, newPos);
setPath(currentField, newPos);
delayTime = 3f; // Verzögerung zurücksetzen
delayElapsed = 0f; // Timer zurücksetzen
}
}

View File

@@ -1,87 +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 pp.monopoly.model.Item;
import pp.monopoly.model.Visitor;
import pp.monopoly.notification.DiceRollEvent;
import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.ItemAddedEvent;
import pp.monopoly.notification.ItemRemovedEvent;
import pp.monopoly.notification.UpdatePlayerView;
import 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.view.ModelViewSynchronizer;
import pp.monopoly.model.Rotation;
import static pp.util.FloatMath.HALF_PI;
import static pp.util.FloatMath.PI;
/**
* Abstract base class for synchronizing the visual representation of a {@link Board} with its model state.
* This class handles the addition and removal of items from the board, ensuring that changes in the model
* are accurately reflected in the view.
* <p>
* Subclasses are responsible for providing the specific implementation of how each item in the map
* is represented visually by implementing the {@link Visitor} interface.
* </p>
* 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.
*/
abstract class GameBoardSynchronizer extends ModelViewSynchronizer<Item> implements Visitor<Spatial>, GameEventListener {
// The board that this synchronizer is responsible for
protected final Board board;
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 new GameBoardSynchronizer.
* Initializes the synchronizer with the provided board and the root node for attaching view representations.
* Constructs a {@code GameBoardSynchronizer} object with the specified application, root node, and ship map.
*
* @param map the board to be synchronized
* @param root the root node to which the view representations of the board items are attached
* @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
*/
protected GameBoardSynchronizer(Board board, Node root) {
super(root);
this.board = board;
public GameBoardSynchronizer(MonopolyApp app, Node root, Board board) {
super(board, root);
this.app = app;
this.particleFactory = new ParticleEffectFactory(app);
addExisting();
}
/**
* Translates a model item into its corresponding visual representation.
* The specific visual representation is determined by the concrete implementation of the {@link Visitor} interface.
* 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 item the item from the model to be translated
* @return the visual representation of the item as a {@link Spatial}
* @param ship the battleship to be represented
* @return the node containing the graphical representation of the battleship
*/
@Override
protected Spatial translate(Item item) {
return item.accept(this);
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;
}
/**
* Adds the existing items from the board to the view.
* This method should be called during initialization to ensure that all current items in the board
* are visually represented.
*/
protected void addExisting() {
board.getItems().forEach(this::add);
}
/**
* Handles the event when an item is removed from the ship map.
* Removes the visual representation of the item from the view if it belongs to the synchronized ship map.
* Creates a simple box to represent a battleship that is not of the "King George V" type.
*
* @param event the event indicating that an item has been removed from the ship map
* @param ship the battleship to be represented
* @return the geometry representing the battleship as a box
*/
@Override
public void receivedEvent(ItemRemovedEvent event) {
if (board == event.board())
delete(event.item());
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;
}
/**
* Handles the event when an item is added to the ship map.
* Adds the visual representation of the new item to the view if it belongs to the synchronized ship map.
* 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 event the event indicating that an item has been added to the ship map
* @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.
*/
@Override
public void receivedEvent(ItemAddedEvent event) {
if (board == event.board()){
add(event.item());
}
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;
};
}
}

View File

@@ -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ü
}
}

View File

@@ -1,93 +0,0 @@
package pp.monopoly.client.gui;
import com.jme3.texture.Texture;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Command;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.game.server.Player;
import pp.monopoly.game.server.PlayerColor;
import pp.monopoly.notification.Sound;
public class ImageButton extends Button {
private final MonopolyApp app;
private final String functionality;
private final PlayerColor playerColor;
public ImageButton(String functionality, MonopolyApp app) {
super("", "button-clear");
this.app = app;
this.functionality = functionality;
this.playerColor = Player.getColor(app.getId());
updateButtonAppearance(ButtonState.ENABLED);
addButtonCommands();
}
/**
* Updates the button's appearance based on its state.
*
* @param state the current button state
*/
private void updateButtonAppearance(ButtonState state) {
setBackgroundTexture(state.name().toLowerCase());
}
/**
* Adds button commands for state-specific actions like hover, press, enable, and disable.
*/
private void addButtonCommands() {
addCommands(ButtonAction.Enabled, source -> updateButtonAppearance(ButtonState.ENABLED));
addCommands(ButtonAction.Disabled, source -> updateButtonAppearance(ButtonState.DISABLED));
addCommands(ButtonAction.Hover, source -> {
if (isEnabled()) {
updateButtonAppearance(ButtonState.HOVER);
}
});
addCommands(ButtonAction.HighlightOff, source -> updateButtonAppearance(isEnabled() ? ButtonState.ENABLED : ButtonState.DISABLED));
addCommands(ButtonAction.Up, source -> updateButtonAppearance(isEnabled() ? ButtonState.ENABLED : ButtonState.DISABLED));
addCommands(ButtonAction.Down, source -> {
if (isEnabled()) {
app.getGameLogic().playSound(Sound.BUTTON);
}
});
}
/**
* Sets the background texture for the button based on the given state.
*
* @param state the button state (e.g., "enabled", "disabled", "hover")
*/
private void setBackgroundTexture(String state) {
String texturePath = buildTexturePath(state);
Texture texture = app.getAssetManager().loadTexture(texturePath);
setBackground(new QuadBackgroundComponent(texture));
}
/**
* Builds the file path for the button texture.
*
* @param state the button state (e.g., "enabled", "disabled", "hover")
* @return the full file path to the texture
*/
private String buildTexturePath(String state) {
return String.format("Pictures/Buttons/Button_%s_%s_%s.png", functionality, playerColor.getColorName(), state);
}
/**
* Button states for handling appearance transitions.
*/
private enum ButtonState {
ENABLED, DISABLED, HOVER
}
public void addClickCommands( Command<? super Button> command ) {
super.addCommands(ButtonAction.Down, command);
}
@SuppressWarnings("unchecked") // because Java doesn't like var-arg generics
public void addClickCommands( Command<? super Button>... commands ) {
super.addCommands(ButtonAction.Down, commands);
}
}

View File

@@ -1,345 +0,0 @@
package pp.monopoly.client.gui;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.scene.shape.Sphere;
import com.jme3.texture.Texture;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Insets3f;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Selector;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.core.VersionedList;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.GameMusic;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.game.server.Player;
import pp.monopoly.message.client.PlayerReady;
import pp.monopoly.notification.Sound;
import java.util.Set;
/**
* Represents the lobby menu in the Monopoly application.
* <p>
* Provides functionality for player configuration, including input for starting capital,
* player name, and figure selection, as well as options to ready up or exit the game.
* </p>
*/
public class LobbyMenu extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the lobby menu UI. */
private final Container menuContainer;
/** Background geometry for the menu. */
private Geometry background;
/** Colored circle displayed between input fields and dropdown menus. */
private Geometry circle;
/** Container for the lower-left section of the menu. */
private Container lowerLeftMenu;
/** Container for the lower-right section of the menu. */
private Container lowerRightMenu;
/** Text field for entering the player's name. */
private TextField playerInputField;
/** Text field for entering the starting capital. */
private TextField startingCapital = new TextField("15000");
/** Selected player figure. */
private String figure;
private VersionedReference<Set<Integer>> selectionRef;
private Selector<String> figureDropdown;
/**
* Constructs the lobby menu for player configuration.
*
* @param app the Monopoly application instance
*/
public LobbyMenu(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
GameMusic music = app.getStateManager().getState(GameMusic.class);
if (music != null && music.isEnabled()) {
music.playSecondaryMusic();
}
playerInputField = new TextField("Spieler "+(app.getId()+1));
// Hintergrundbild laden und hinzufügen
addBackgroundImage();
QuadBackgroundComponent translucentWhiteBackground =
new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f));
menuContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
menuContainer.setPreferredSize(new Vector3f(1000, 600, 0)); // Fixed size of the container
menuContainer.setBackground(translucentWhiteBackground);
// Create a smaller horizontal container for the label, input field, and spacers
Container horizontalContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
horizontalContainer.setPreferredSize(new Vector3f(600, 40, 0)); // Adjust container size
horizontalContainer.setBackground(null);
Label title = horizontalContainer.addChild(new Label("Startkapital:", new ElementId("label-Bold")));
title.setFontSize(40);
// Add a spacer between the title and the input field
Label spacerBeforeInput = horizontalContainer.addChild(new Label("")); // Invisible spacer
spacerBeforeInput.setPreferredSize(new Vector3f(20, 1, 0)); // Width of the spacer
// Add an input field (TextField)
horizontalContainer.addChild(startingCapital);
startingCapital.setPreferredWidth(100); // Set the width of the input field
startingCapital.setPreferredSize(new Vector3f(150, 50, 0));
startingCapital.setInsets(new Insets3f(5, 10, 5, 10)); // Add padding around the text inside the field
// Add a spacer after the input field
Label spacerAfterInput = horizontalContainer.addChild(new Label("")); // Invisible spacer
spacerAfterInput.setPreferredSize(new Vector3f(20, 1, 0)); // Width of the spacer
menuContainer.setLocalTranslation(
(app.getCamera().getWidth() - menuContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + menuContainer.getPreferredSize().y) / 2,
1
);
app.getGuiNode().attachChild(menuContainer);
// Dropdowns and Labels
Container dropdownContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
dropdownContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(ColorRGBA.Black)));
dropdownContainer.setPreferredSize(new Vector3f(800, 200, 0));
dropdownContainer.setBackground(null);
dropdownContainer.setInsets(new Insets3f(10, 0, 0, 0));
// Player Input Field
Container playerInputContainer = dropdownContainer.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X)));
playerInputContainer.addChild(new Label("Spieler:"));
playerInputContainer.setBackground(null);
playerInputField.setPreferredSize(new Vector3f(100, 20, 0));
playerInputField.setInsets(new Insets3f(5, 10, 5, 10)); // Add padding for the text inside the field
playerInputField.setBackground(new QuadBackgroundComponent(ColorRGBA.Black));
playerInputContainer.addChild(playerInputField);
// Spacer (Center Circle Area)
Label spacer = dropdownContainer.addChild(new Label(""));
spacer.setPreferredSize(new Vector3f(200, 200, 0)); // Adjust this to fit the center graphic
// Figur Dropdown
Container figureDropdownContainer = dropdownContainer.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X)));
figureDropdownContainer.addChild(new Label("Figur:"));
figureDropdownContainer.setBackground(null);
VersionedList<String> figures = new VersionedList<>();
figures.add("Computer");
figures.add("Flugzeug");
figures.add("Jägermeister");
figures.add("Katze");
figures.add("OOP");
figures.add("Handyholster");
figures.add("Panzer");
figureDropdown = new Selector<>(figures, "glass");
figureDropdown.setBackground(new QuadBackgroundComponent(ColorRGBA.DarkGray));
figureDropdown.setPreferredSize(new Vector3f(100, 20, 0));
figureDropdownContainer.addChild(figureDropdown);
Vector3f dimens = dropdownContainer.getPreferredSize();
Vector3f dimens2 = figureDropdown.getPopupContainer().getPreferredSize();
dimens2.setX( dimens.getX() );
figureDropdown.getPopupContainer().setPreferredSize(new Vector3f(200,200,5));
// Create selection ref for updating
selectionRef = figureDropdown.getSelectionModel().createReference();
// Set default
figureDropdown.getSelectionModel().setSelection(0);
onDropdownSelectionChanged(figureDropdown);
Container buttonContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
buttonContainer.setPreferredSize(new Vector3f(100, 40, 0));
buttonContainer.setInsets(new Insets3f(20, 0, 10, 0)); // Add spacing above the buttons
buttonContainer.setBackground(null);
// Lower-left container for "Abbrechen" button
lowerLeftMenu = new Container();
Button cancelButton = new Button("Beenden");
cancelButton.setPreferredSize(new Vector3f(200, 60, 0)); // Set size to match the appearance in the image
cancelButton.setFontSize(18); // Adjust font size
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
app.closeApp();
app.getGameLogic().playSound(Sound.BUTTON);
}));
lowerLeftMenu.addChild(cancelButton);
// Position the container near the bottom-left corner
lowerLeftMenu.setLocalTranslation(new Vector3f(120, 170, 3)); // Adjust X and Y to align with the bottom-left corner
app.getGuiNode().attachChild(lowerLeftMenu);
// Lower-right container for "Bereit" button
lowerRightMenu = new Container();
Button readyButton = new Button("Bereit");
readyButton.setPreferredSize(new Vector3f(200, 60, 0)); // Set size to match the appearance in the image
readyButton.setFontSize(18); // Adjust font size
readyButton.setBackground(new QuadBackgroundComponent(ColorRGBA.Green)); // Add color to match the style
readyButton.addClickCommands(s -> ifTopDialog(() -> {
music.toggleMusic();
toggleReady();
app.getGameLogic().playSound(Sound.BUTTON);
readyButton.setBackground(new QuadBackgroundComponent(ColorRGBA.DarkGray));
}));
lowerRightMenu.addChild(readyButton);
// Position the container near the bottom-right corner
lowerRightMenu.setLocalTranslation(new Vector3f(app.getCamera().getWidth() - 320, 170, 3)); // X: 220px from the right, Y: 50px above the bottom
app.getGuiNode().attachChild(lowerRightMenu);
// Add a colored circle between the input field and the dropdown menu
circle = createCircle(); // 50 is the diameter, Red is the color
circle.setLocalTranslation(new Vector3f(
(app.getCamera().getWidth()) / 2, // Center horizontally
(app.getCamera().getHeight() / 2) - 90, // Adjust Y position
2 // Ensure it's in front of the background but behind the dropdown
));
app.getGuiNode().attachChild(circle); // Attach to the GUI node
// Zentrierung des Containers
menuContainer.setLocalTranslation(
(app.getCamera().getWidth() - menuContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + menuContainer.getPreferredSize().y) / 2,
1 // Höhere Z-Ebene für den Vordergrund
);
app.getGuiNode().attachChild(menuContainer);
}
/**
* Adds a background image to the lobby menu.
*/
private void addBackgroundImage() {
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/lobby.png");
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
background = new Geometry("Background", quad);
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
backgroundMaterial.setTexture("ColorMap", backgroundImage);
background.setMaterial(backgroundMaterial);
background.setLocalTranslation(0, 0, -1); // Hintergrundebene
app.getGuiNode().attachChild(background);
}
/**
* Creates a circle graphic element for the menu.
*
* @return the created circle geometry
*/
private Geometry createCircle() {
Sphere sphere = new Sphere(90,90,60.0f);
Geometry circleGeometry = new Geometry("Circle", sphere);
// Create a material with a solid color
Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
material.setColor("Color", Player.getColor(app.getId()).getColor()); // Set the desired color
circleGeometry.setMaterial(material);
return circleGeometry;
}
/**
* Toggles the player's ready state and sends the configuration to the server.
*/
private void toggleReady() {
app.getGameLogic().send(new PlayerReady(true, playerInputField.getText(), figure, Integer.parseInt(startingCapital.getText())));
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
/**
* Updates the menu at regular intervals.
* Checks if the dropdown selection has been updated and invokes the selection change handler.
*
* @param tpf Time per frame, in seconds, since the last update.
*/
@Override
public void update(float tpf) {
if (selectionRef.update()) {
onDropdownSelectionChanged(figureDropdown);
}
}
/**
* Closes the current menu and transitions music playback.
* Stops the secondary music (if playing) and resumes the main background music
* if music is enabled in the preferences. Ensures smooth transitions in audio.
*/
@Override
public void close() {
GameMusic music = app.getStateManager().getState(GameMusic.class);
if (music != null) {
music.stopSecondaryMusic();
if (music.isEnabled()) {
music.playMainMusic();
}
}
super.close();
}
/**
* Updates the selected figure based on the dropdown menu selection.
*
* @param selector the selected figure
*/
private void onDropdownSelectionChanged(Selector<String> selector) {
app.getGameLogic().playSound(Sound.BUTTON);
switch (selector.getSelectedItem()) {
case "Jägermeister":
figure = "Jaegermeister";
break;
case "Handyholster":
figure = "Holster";
break;
default:
figure = selector.getSelectedItem();
break;
}
}
/**
* Functional interface for handling selection changes in dropdown menus.
*
* @param <T> the type of the selection
*/
@FunctionalInterface
private interface SelectionActionListener<T> {
/**
* Triggered when the selection changes.
*
* @param selection the new selection
*/
void onSelectionChanged(T selection);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -1,271 +0,0 @@
package pp.monopoly.client.gui;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import com.simsilica.lemur.*;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.game.server.Player;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.model.fields.FoodField;
import pp.monopoly.model.fields.GateField;
import pp.monopoly.model.fields.PropertyField;
import pp.monopoly.notification.Sound;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* PropertyOverviewMenu is a dialog for displaying the player's properties in the game.
*/
public class PropertyOverviewMenu extends Dialog {
private final MonopolyApp app;
private final Container mainContainer;
private final Container displayContainer;
private final Slider horizontalSlider;
private final List<Container> cards;
/**
* Constructs the PropertyOverviewMenu dialog.
*
* @param app The Monopoly application instance.
*/
public PropertyOverviewMenu(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
// Make the menu fullscreen
Vector3f screenSize = new Vector3f(app.getCamera().getWidth(), app.getCamera().getHeight(), 0);
// Main container for the menu layout
mainContainer = new Container();
mainContainer.setPreferredSize(screenSize); // Make fullscreen
mainContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.8f))); // Semi-transparent background
// Header label
Label headerLabel = mainContainer.addChild(new Label("Meine Grundstücke", new ElementId("header")));
headerLabel.setFontSize(40);
// Central display container (to hold the "Gebäude" cards)
displayContainer = mainContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y, FillMode.Even, FillMode.None))); // Horizontal layout
displayContainer.setPreferredSize(new Vector3f(screenSize.x - 300, 550, 0)); // Take up the remaining width
displayContainer.setBackground(new QuadBackgroundComponent(ColorRGBA.White));
displayContainer.setLocalTranslation(0, 0, 11);
// Add some placeholder "Gebäude" cards to the display container
cards = new ArrayList<>();
populatePlayerProperties();
// Initially add only visible cards to the displayContainer
refreshVisibleCards(0);
// Horizontal slider for scrolling through cards
horizontalSlider = mainContainer.addChild(new Slider(Axis.X));
horizontalSlider.setPreferredSize(new Vector3f(screenSize.x - 300, 20, 5));
horizontalSlider.setModel(new DefaultRangedValueModel(0, Math.max(0, cards.size() - 5), 0));
horizontalSlider.addControl(new SliderValueChangeListener());
// Add the "Zurück" button at the bottom
Button backButton = mainContainer.addChild(new Button("Zurück", new ElementId("button")));
backButton.setPreferredSize(new Vector3f(200, 60, 0));
backButton.addClickCommands(source -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Attach the main container to the GUI node
app.getGuiNode().attachChild(mainContainer);
mainContainer.setLocalTranslation(
0,
app.getCamera().getHeight(), // Align to the top
10
);
}
/**
* Fetches the player's properties and populates them into the cards list.
*/
private void populatePlayerProperties() {
// Fetch the current player
Player currentPlayer = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId());
// Fetch the player's properties using their indices
List<PropertyField> fields = new ArrayList<>();
for (Integer i : currentPlayer.getProperties()) {
fields.add((PropertyField) app.getGameLogic().getBoardManager().getFieldAtIndex(i));
}
// Iterate through the fetched properties
for (PropertyField property : fields.stream().sorted(Comparator.comparingInt(PropertyField::getId)).collect(Collectors.toList())) {
if (property instanceof BuildingProperty) {
BuildingProperty building = (BuildingProperty) property;
cards.add(createBuildingCard(building));
} else if (property instanceof FoodField) {
FoodField foodField = (FoodField) property;
cards.add(createFoodFieldCard(foodField));
} else if (property instanceof GateField) {
GateField gateField = (GateField) property;
cards.add(createGateFieldCard(gateField));
}
}
}
/**
* Creates a card for BuildingProperty with detailed rent and cost information.
*/
private Container createBuildingCard(BuildingProperty field) {
Container card = new Container();
card.setPreferredSize(new Vector3f(200, 300, 2)); // Match the size of the BuildingPropertyCard
card.setBackground(new QuadBackgroundComponent(field.getColor().getColor()));
card.setInsets(new Insets3f(5, 5, 5, 5)); // Add 5-pixel inset
card.addChild(new Label(field.getName(), new ElementId("label-Bold"))).setFontSize(25);
// Add property details
Container propertyValuesContainer = card.addChild(new Container());
propertyValuesContainer.addChild(new Label("Grundstückswert: " + field.getPrice()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
propertyValuesContainer.addChild(new Label("Miete allein: " + field.getAllRent().get(0)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("- mit 1 Haus: " + field.getAllRent().get(1)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("- mit 2 Häuser: " + field.getAllRent().get(2)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("- mit 3 Häuser: " + field.getAllRent().get(3)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("- mit 4 Häuser: " + field.getAllRent().get(4)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("- mit 1 Hotel: " + field.getAllRent().get(5)+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("- 1 Haus kostet: " + field.getHousePrice()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
Label hypo = new Label("Hypothek: " + field.getHypo()+ " EUR", new ElementId("label-Text"));
hypo.setFontSize(14);
if (field.isMortgaged()) hypo.setColor(ColorRGBA.Red);
propertyValuesContainer.addChild(hypo);
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
return card;
}
/**
* Creates a card for FoodField with dynamic pricing and rent details.
*/
private Container createFoodFieldCard(FoodField field) {
Container card = new Container();
card.setPreferredSize(new Vector3f(200, 300, 2)); // Adjust card size
card.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f))); // Dark background for card
card.setInsets(new Insets3f(5, 5, 5, 5)); // Add 5-pixel inset
// Title Section
Label title = card.addChild(new Label(field.getName(), new ElementId("label-Bold")));
title.setFontSize(30);
title.setColor(ColorRGBA.White); // Ensure readability on dark background
// Property Values Section
Container propertyValuesContainer = card.addChild(new Container());
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); // Grey background
propertyValuesContainer.addChild(new Label("Preis: " + field.getPrice()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
propertyValuesContainer.addChild(new Label("Wenn man Besitzer der", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label(field.getName() + " ist, so ist", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("die Miete 40x Würfelwert.", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
propertyValuesContainer.addChild(new Label("Besitzer beider", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("Restaurants zahlt", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("100x Würfelwert.", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14); // Leerzeile
propertyValuesContainer.addChild(new Label("Hypothek: " + field.getHypo()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
return card;
}
/**
* Creates a card for GateField with rent details for owning multiple gates.
*/
private Container createGateFieldCard(GateField field) {
Container card = new Container();
card.setPreferredSize(new Vector3f(200, 300, 2)); // Adjust card size
card.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Light grey background
card.setInsets(new Insets3f(5, 5, 5, 5)); // Add 5-pixel inset
// Title Section
Label title = card.addChild(new Label(field.getName(), new ElementId("label-Bold")));
title.setFontSize(30);
title.setColor(ColorRGBA.Black);
// Property Values Section
Container propertyValuesContainer = card.addChild(new Container());
propertyValuesContainer.addChild(new Label("Preis: " + field.getPrice()+ " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("Miete: 250 EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("2 Bahnhof besitzt: 500 EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("3 Bahnhof besitzt: 1000 EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("4 Bahnhof besitzt: 2000 EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text"))).setFontSize(14);
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); // Dark grey background
return card;
}
/**
* Updates the visible cards in the displayContainer based on the slider value.
*
* @param startIndex The starting index of the visible cards.
*/
private void refreshVisibleCards(int startIndex) {
displayContainer.clearChildren(); // Remove all current children
int maxVisible = 5; // Number of cards to display at once
for (int i = startIndex; i < startIndex + maxVisible && i < cards.size(); i++) {
displayContainer.addChild(cards.get(i));
}
}
/**
* Custom listener for slider value changes.
* Extends {@link AbstractControl} to respond to updates in the slider's value and refresh
* the visible cards accordingly.
*/
private class SliderValueChangeListener extends AbstractControl {
@Override
protected void controlUpdate(float tpf) {
// Get the slider's current value and refresh visible cards
int sliderValue = (int) ((Slider) getSpatial()).getModel().getValue();
refreshVisibleCards(sliderValue);
}
/**
* Overrides the rendering logic for the control.
* <p>
* This implementation does not require any rendering operations, so the method is left empty.
*
* @param renderManager the {@link RenderManager} handling the rendering process.
* @param viewPort the {@link ViewPort} associated with the rendering context.
*/
@Override
protected void controlRender(RenderManager renderManager, ViewPort viewPort) {
// No rendering logic needed
}
}
/**
* Closes the dialog and detaches it from the GUI node.
*/
@Override
public void close() {
app.getGuiNode().detachChild(mainContainer);
super.close();
}
}

View File

@@ -1,146 +1,100 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.client.gui;
import java.util.prefs.Preferences;
import com.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.monopoly.client.GameMusic;
import pp.monopoly.client.GameSound;
import pp.monopoly.client.MonopolyApp;
import pp.dialog.Dialog;
import pp.dialog.StateCheckboxModel;
import pp.monopoly.notification.Sound;
import static pp.util.PreferencesUtils.getPreferences;
import pp.monopoly.client.MonopolyApp;
/**
* The Menu class represents the main menu in the Monopoly game application.
* It extends the Dialog class and provides functionalities for Volume adjustment, Sound adjustment,
* returning to the game, and quitting the application.
* SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann.
*/
public class SettingsMenu extends Dialog {
/**
* Preferences instance for storing and retrieving settings specific to the SettingsMenu.
*/
private static final Preferences PREFERENCES = getPreferences(SettingsMenu.class);
/**
* Reference to the main Monopoly application instance.
*/
private final MonopolyApp app;
private final Geometry overlayBackground;
private final Container settingsContainer;
/**
* Slider control for adjusting the music volume.
*/
private final VolumeSlider musicSlider;
/**
* Slider control for adjusting the sound effects volume.
*/
private final SoundSlider soundSlider;
/**
* Checkbox for toggling sound effects.
*/
private final Checkbox soundCheckbox;
/**
* Checkbox for toggling background music.
*/
private final Checkbox musicCheckbox;
/**
* Constructs the Menu dialog for the Monopoly application.
*
* @param app the MonopolyApp instance
*/
public SettingsMenu(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
musicSlider = new VolumeSlider(app.getStateManager().getState(GameMusic.class));
soundSlider = new SoundSlider(app.getStateManager().getState(GameSound.class));
addChild(new Label("Einstellungen", new ElementId("settings-title"))); //NON-NLS
addChild(new Label("Sound Effekte", new ElementId("label"))); //NON-NLS
addChild(soundSlider);
// Halbtransparentes Overlay hinzufügen
overlayBackground = createOverlayBackground();
app.getGuiNode().attachChild(overlayBackground);
soundCheckbox = new Checkbox("Soundeffekte an / aus", new StateCheckboxModel(app, GameSound.class));
addChild(soundCheckbox);
// Hauptcontainer für das Menü
settingsContainer = new Container();
settingsContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f)));
addChild(new Label("Hintergrund Musik", new ElementId("label"))); //NON-NLS
// Titel
Label settingsTitle = settingsContainer.addChild(new Label("Einstellungen", new ElementId("settings-title")));
settingsTitle.setFontSize(48);
musicCheckbox = new Checkbox("Musik an / aus", new StateCheckboxModel(app, GameMusic.class));
musicCheckbox.addClickCommands(s -> toggleMusicPreference());
addChild(musicCheckbox);
// Effekt-Sound: Slider und Checkbox
Container effectSoundContainer = settingsContainer.addChild(new Container());
effectSoundContainer.addChild(new Label("Effekt Sound", new ElementId("label")));
effectSoundContainer.addChild(new Slider());
effectSoundContainer.addChild(new Checkbox("Aktivieren")).setChecked(true);
addChild(musicSlider);
// Hintergrundmusik: Slider und Checkbox
Container backgroundMusicContainer = settingsContainer.addChild(new Container());
backgroundMusicContainer.addChild(new Label("Hintergrund Musik", new ElementId("label")));
backgroundMusicContainer.addChild(new Slider());
backgroundMusicContainer.addChild(new Checkbox("Aktivieren")).setChecked(true);
addChild(new Button("Zurück zum Spiel", new ElementId("button"))).addClickCommands(s -> ifTopDialog(() -> {
this.close(); // Close the StartMenu dialog
app.getGameLogic().playSound(Sound.BUTTON);
}));
addChild(new Button("Beenden", new ElementId("button"))).addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.closeApp();
}));
update();
// Beenden-Button
Button quitButton = settingsContainer.addChild(new Button("Beenden", new ElementId("menu-button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(source -> app.stop());
// Zentriere das Menü
settingsContainer.setLocalTranslation(
(app.getCamera().getWidth() - settingsContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + settingsContainer.getPreferredSize().y) / 2,
1
);
app.getGuiNode().attachChild(settingsContainer);
}
/**
* Toggles the music preference based on the state of the musicCheckbox.
* Enables or disables background music and starts playback if enabled.
*/
private void toggleMusicPreference() {
boolean enabled = musicCheckbox.isChecked();
GameMusic gameMusic = app.getStateManager().getState(GameMusic.class);
if (gameMusic != null) {
gameMusic.setEnabled(enabled);
if (enabled) {
gameMusic.playMainMusic();
}
}
}
/**
* Updates the state of the music checkbox to match the current preferences.
* This ensures the checkbox reflects the actual enabled/disabled state of the music.
*/
@Override
public void update() {
GameMusic gameMusic = app.getStateManager().getState(GameMusic.class);
if (gameMusic != null) {
musicCheckbox.setChecked(gameMusic.isEnabled());
}
}
/**
* Updates UI elements such as sliders and synchronizes the state of the settings menu.
* Erstellt einen halbtransparenten Hintergrund für das Menü.
*
* @param delta the time in seconds since the last update
* @return Geometrie des Overlays
*/
@Override
public void update(float delta) {
musicSlider.update();
soundSlider.update();
update();
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;
}
/**
* As an escape action, this method closes the menu if it is the top dialog.
* Schließt das Menü und entfernt die GUI-Elemente.
*/
@Override
public void escape() {
close();
public void close() {
System.out.println("Schließe SettingsMenu..."); // Debugging-Ausgabe
app.getGuiNode().detachChild(settingsContainer); // Entferne das Menü
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
app.setSettingsMenuOpen(false); // Menü als geschlossen markieren
app.unblockInputs(); // Eingaben wieder aktivieren
System.out.println("SettingsMenu geschlossen."); // Debugging-Ausgabe
}
}

View File

@@ -1,38 +0,0 @@
package pp.monopoly.client.gui;
import com.simsilica.lemur.Slider;
import pp.monopoly.client.GameSound;
public class SoundSlider extends Slider {
/**
* Manages sound effects for the game.
*/
private final pp.monopoly.client.GameSound sound;
/**
* Volume level for the game sounds.
*/
private double vol;
/**
* Constructs the Volume Slider for the Menu dialog
* @param sound the Effects sound instance
*/
public SoundSlider(GameSound sound) {
super();
this.sound = sound;
vol = GameSound.volumeInPreferences();
getModel().setPercent(vol);
}
/**
* When triggered it updates the volume to the value set with the slider
*/
public void update() {
if (vol != getModel().getPercent()) {
vol = getModel().getPercent();
sound.setVolume( (float) vol);
}
}
}

View File

@@ -1,124 +0,0 @@
package pp.monopoly.client.gui;
import com.jme3.material.Material;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.notification.Sound;
/**
* Constructs the startup menu dialog for the Monopoly application.
*/
public class StartMenu extends Dialog {
private final MonopolyApp app;
/**
* Constructs the Startup Menu dialog for the Monopoly application.
*
* @param app the MonopolyApp instance
*/
public StartMenu(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
int screenWidth = app.getContext().getSettings().getWidth();
int screenHeight = app.getContext().getSettings().getHeight();
// Set up the background image
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
Quad quad = new Quad(screenWidth, screenHeight);
Geometry background = new Geometry("Background", quad);
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
backgroundMaterial.setTexture("ColorMap", backgroundImage);
background.setMaterial(backgroundMaterial);
background.setLocalTranslation(0, 0, -1);
app.getGuiNode().attachChild(background);
Container centerMenu = new Container(new SpringGridLayout(Axis.Y, Axis.X));
Button startButton = new Button("Spielen");
startButton.setPreferredSize(new Vector3f(190, 60, 0));
startButton.setFontSize(40);
startButton.setTextHAlignment(HAlignment.Center);
startButton.addClickCommands(s -> ifTopDialog(() -> {
close();
app.getGameLogic().playSound(Sound.BUTTON);
app.connect();
}));
centerMenu.addChild(startButton);
centerMenu.setLocalTranslation(new Vector3f(screenWidth / 2f - centerMenu.getPreferredSize().x / 2f,
screenHeight / 2f - 280 + centerMenu.getPreferredSize().y / 2f,
0));
app.getGuiNode().attachChild(centerMenu);
// Load the Monopoly logo as a texture
Texture logoTexture = app.getAssetManager().loadTexture("Pictures/logo-monopolyw.png");
// Create a container for the logo
Container logoContainer = new Container();
QuadBackgroundComponent logoBackground = new QuadBackgroundComponent(logoTexture);
logoContainer.setBackground(logoBackground);
float logoWidth = 512;
float logoHeight = 128;
logoContainer.setPreferredSize(new Vector3f(logoWidth, logoHeight, 0));
logoContainer.setLocalTranslation(new Vector3f(
screenWidth / 2f - logoWidth / 2f,
screenHeight / 2f + 200,
0
));
app.getGuiNode().attachChild(logoContainer);
Texture unibwTexture = app.getAssetManager().loadTexture("Pictures/logo-unibw.png");
Container unibwContainer = new Container();
QuadBackgroundComponent unibwBackground = new QuadBackgroundComponent(unibwTexture);
unibwContainer.setBackground(unibwBackground);
float unibwWidth = 512;
float unibwHeight = 128;
unibwContainer.setPreferredSize(new Vector3f(unibwWidth, unibwHeight, 0));
unibwContainer.setLocalTranslation(new Vector3f(
screenWidth / 2f - unibwWidth / 2f,
screenHeight / 2f + 100,
0
));
// Attach the container to the GUI node
app.getGuiNode().attachChild(unibwContainer);
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
/**
* Closes the startup menu and detaches all GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachAllChildren();
super.close();
}
}

View File

@@ -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/board.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);
}
}

View File

@@ -1,569 +1,168 @@
package pp.monopoly.client.gui;
import com.jme3.input.event.MouseButtonEvent;
import com.jme3.input.event.MouseMotionEvent;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import java.util.Random;
import com.jme3.font.BitmapText;
import com.jme3.math.Vector3f;
import com.jme3.scene.Spatial;
import com.jme3.texture.Texture;
import com.jme3.scene.Geometry;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.VAlignment;
import com.simsilica.lemur.component.IconComponent;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.event.MouseEventControl;
import com.simsilica.lemur.event.MouseListener;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.popups.Bankrupt;
import pp.monopoly.game.server.Player;
import pp.monopoly.game.server.PlayerHandler;
import pp.monopoly.message.client.EndTurn;
import pp.monopoly.message.client.RollDice;
import pp.monopoly.notification.ButtonStatusEvent;
import pp.monopoly.notification.DiceRollEvent;
import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.Sound;
import pp.monopoly.notification.UpdatePlayerView;
/**
* Represents the toolbar interface in the Monopoly application.
* Provides game controls, player information, and event handling.
* Toolbar Klasse, die am unteren Rand der Szene angezeigt wird.
* Die Buttons bewegen den Würfel auf dem Spielfeld.
*/
public class Toolbar extends Dialog implements GameEventListener {
public class Toolbar {
/** The Monopoly application instance*/
private final MonopolyApp app;
/** The container representing the toolbar interface */
private final Container toolbarContainer;
/** The container representing the player overview information */
private Container overviewContainer;
/** The container representing the player account information */
private Container accountContainer;
/** The player handler instance */
private PlayerHandler playerHandler;
/** The label representing the left dice */
private Label imageLabel;
/** The label representing the right dice */
private Label imageLabel2;
/** The flag to check if the dice can be rolled */
private boolean canRollDice = false;
/** The trade button */
private Button tradeButton;
/** The property menu button */
private Button propertyMenuButton;
/** The end turn button */
private Button endTurnButton;
/** The latest incoming Dice Roll Event */
private DiceRollEvent latestDiceRollEvent = null;
/** The flag to check if the bankrupt pop up is already shown */
private boolean bankruptPopUp = false;
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
/**
* Constructs a new {@code Toolbar} for the given {@code MonopolyApp}.
* Konstruktor für die Toolbar.
*
* @param app The {@code MonopolyApp} instance to create the toolbar for.
* @param app Die Hauptanwendung (MonopolyApp)
* @param cube Der Würfel, der bewegt werden soll
*/
public Toolbar(MonopolyApp app) {
super(app.getDialogManager());
public Toolbar(MonopolyApp app, Geometry cube) {
this.app = app;
this.cube = cube;
app.getGameLogic().addListener(this);
this.playerHandler = app.getGameLogic().getPlayerHandler();
// Erstelle die Toolbar
toolbarContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
toolbarContainer = setupToolbar();
// 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);
endTurnButton.setEnabled(false);
// Erstelle die Position-Anzeige
positionText = createPositionDisplay();
updatePositionDisplay(); // Initialisiere die Anzeige mit der Startposition
}
/**
* Sets up the toolbar interface with the game controls, player information, and event handling.
* 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.
*
* @return The container representing the toolbar interface.
* @param label Der Text des Buttons
* @param step Schrittweite (+1 für vorwärts, -1 für rückwärts)
*/
private Container setupToolbar() {
Container container = new Container(new SpringGridLayout(Axis.X, Axis.Y), "toolbar");
container.setLocalTranslation(0, 200, 0);
container.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 200, 0));
Texture backgroundToolbar = app.getAssetManager().loadTexture("Pictures/toolbarbg.png");
QuadBackgroundComponent background = new QuadBackgroundComponent(backgroundToolbar);
background.setMargin(0, 0); // Removes any internal margin
container.setBackground(background);
setupBorders(container);
setupSpacer(container);
setupPlayerInfoSection(container);
setupDiceSection(container);
setupActionMenu(container);
return container;
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);
}
/**
* Sets up the borders for the toolbar interface.
* 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 container The container representing the toolbar interface.
* @param step Schrittweite (+1 für vorwärts, -1 für rückwärts oder andere Werte)
*/
private void setupBorders(Container container) {
addBorder(0, 205, app.getCamera().getWidth(), 5, ColorRGBA.DarkGray); // Top
addBorder(0, 5, app.getCamera().getWidth(), 10, ColorRGBA.DarkGray); // Bottom
addBorder(0, 200, 8, 210, ColorRGBA.DarkGray); // Left
addBorder(app.getCamera().getWidth() - 5, 200, 8, 210, ColorRGBA.DarkGray); // Right
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 + ")");
}
/**
* Adds a border to the toolbar interface with the specified dimensions and color.
* Berechnet die neue Position des Würfels basierend auf der aktuellen Brettseite und Position.
*
* @param x The x-coordinate of the border.
* @param y The y-coordinate of the border.
* @param width The width of the border.
* @param height The height of the border.
* @param color The color of the border.
* @param position Aktuelle Position auf dem Spielfeld
* @return Die berechnete Position als Vector3f
*/
private void addBorder(float x, float y, float width, float height, ColorRGBA color) {
Container border = new Container();
border.setPreferredSize(new Vector3f(width, height, 0));
border.setBackground(new QuadBackgroundComponent(color));
border.setLocalTranslation(x, y, 3);
app.getGuiNode().attachChild(border);
}
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
private void setupSpacer(Container container) {
Container spacer = container.addChild(new Container());
spacer.setPreferredSize(new Vector3f(20, 10, 0));
spacer.setBackground(null);
}
/**
* Sets up the player information section of the toolbar interface.
*
* @param parentContainer The container representing the toolbar interface.
*/
private void setupPlayerInfoSection(Container parentContainer) {
Container playerInfoSection = parentContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
playerInfoSection.setPreferredSize(new Vector3f(600, 300, 0)); // Adjust size for both containers
Texture backgroundTexture = app.getAssetManager().loadTexture("Pictures/"+ Player.getColor(app.getId()).getColorName()+ "Background.png");
QuadBackgroundComponent background = new QuadBackgroundComponent(backgroundTexture);
playerInfoSection.setBackground(background);
accountContainer = playerInfoSection.addChild(new Container());
accountContainer.setPreferredSize(new Vector3f(300, 300, 0));
accountContainer.setBackground(null);
overviewContainer = playerInfoSection.addChild(new Container());
overviewContainer.setPreferredSize(new Vector3f(300, 300, 0));
overviewContainer.setBackground(null);
refreshPlayerView();
}
/**
* Sets up the dice section of the toolbar interface.
*
* @param container The container representing the toolbar interface.
*/
private void setupDiceSection(Container container) {
Container diceContainer = container.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
diceContainer.addChild(createDiceDisplay());
diceContainer.setBackground(null);
}
/**
* Sets up the action menu of the toolbar interface.
*
* @param container The container representing the toolbar interface.
*/
private void setupActionMenu(Container container) {
Container menuContainer = container.addChild(new Container());
menuContainer.addChild(createTradeButton());
menuContainer.addChild(createPropertyMenuButton());
menuContainer.addChild(createEndTurnButton());
menuContainer.setBackground(null);
}
/**
* Returns the color of the current player.
*
* @return The color of the current player.
*/
private ColorRGBA getCurrentPlayerColor() {
Player currentPlayer = playerHandler.getPlayerById(app.getId());
return Player.getColor(currentPlayer.getId()).getColor();
}
/**
* Creates the dice display section of the toolbar interface.
*
* @return The container representing the dice display section.
*/
private Container createDiceDisplay() {
Container horizontalContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
horizontalContainer.setPreferredSize(new Vector3f(200, 150, 0));
imageLabel = createDiceLabel("Pictures/dice/one.png");
imageLabel2 = createDiceLabel("Pictures/dice/two.png");
horizontalContainer.setBackground(null);
horizontalContainer.addChild(createDiceContainer(imageLabel));
horizontalContainer.addChild(createDiceContainer(imageLabel2));
// Add mouse event control for click handling
MouseEventControl.addListenersToSpatial(horizontalContainer, new MouseListener() {
@Override
public void mouseButtonEvent(MouseButtonEvent event, Spatial target, Spatial capture) {
if (event.isPressed()) {
handleDiceRoll();
}
}
@Override
public void mouseEntered(MouseMotionEvent event, Spatial target, Spatial capture) {
// Do nothing
}
@Override
public void mouseExited(MouseMotionEvent event, Spatial target, Spatial capture) {
// Do nothing
}
@Override
public void mouseMoved(MouseMotionEvent event, Spatial target, Spatial capture) {
// Do nothing
}
});
return horizontalContainer;
}
/**
* Creates a dice label with the specified icon path.
*
* @param iconPath The path to the icon image.
* @return The label representing the dice.
*/
private Label createDiceLabel(String iconPath) {
Label label = new Label("");
IconComponent icon = new IconComponent(iconPath);
icon.setIconSize(new Vector2f(80, 80));
label.setIcon(icon);
return label;
}
/**
* Creates a dice container with the specified label.
*
* @param label The label representing the dice.
* @return The container representing the dice.
*/
private Container createDiceContainer(Label label) {
Container container = new Container();
container.setBackground(null);
container.setPreferredSize(new Vector3f(100, 150, 0));
container.addChild(label);
return container;
}
/**
* Handles the dice roll event.
*/
private void handleDiceRoll() {
ifTopDialog(() -> {
if (!canRollDice) return;
canRollDice = false;
if (endTurnButton != null) endTurnButton.setEnabled(true);
startDiceAnimation();
app.getGameLogic().send(new RollDice());
app.getGameLogic().playSound(Sound.BUTTON);
});
}
private Button createTradeButton() {
String iconPath = "icons/icon-handeln.png";
// createActionButton(playerColor, "icons/icon-handeln.png", 100, () -> new ChoosePartner(app).open());
tradeButton = new ImageButton("generic", app);
tradeButton.setPreferredSize(new Vector3f(150, 50, 0));
IconComponent icon = new IconComponent(iconPath);
icon.setHAlignment(HAlignment.Center);
icon.setVAlignment(VAlignment.Center);
icon.setIconSize(new Vector2f(75 , 75));
tradeButton.setIcon(icon);
tradeButton.addClickCommands(s -> ifTopDialog(() -> {
new ChoosePartner(app).open();
}));
return tradeButton;
}
private Button createPropertyMenuButton() {
String iconPath = "icons/icon-gebaude.png";
propertyMenuButton = new ImageButton("generic", app);
propertyMenuButton.setPreferredSize(new Vector3f(150, 50, 0));
IconComponent icon = new IconComponent(iconPath);
icon.setHAlignment(HAlignment.Center);
icon.setVAlignment(VAlignment.Center);
icon.setIconSize(new Vector2f(50 , 50));
propertyMenuButton.setIcon(icon);
propertyMenuButton.addClickCommands(s -> ifTopDialog(() -> {
new BuildingAdminMenu(app).open();
}));
return propertyMenuButton;
}
private Button createEndTurnButton() {
// return createActionButton(playerColor, "icons/icon-zugbeenden.png", 75, () -> handleEndTurn());
String iconPath = "icons/icon-zugbeenden.png";
endTurnButton = new ImageButton("generic", app);
endTurnButton.setPreferredSize(new Vector3f(150, 50, 0));
IconComponent icon = new IconComponent(iconPath);
icon.setHAlignment(HAlignment.Center);
icon.setVAlignment(VAlignment.Center);
icon.setIconSize(new Vector2f(50 , 50));
endTurnButton.setIcon(icon);
endTurnButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().send(new EndTurn());
receivedEvent(new ButtonStatusEvent(false));
}));
return endTurnButton;
}
/**
* Creates a background with the specified color.
*
* @param color The color of the background.
* @return The background component.
*/
private QuadBackgroundComponent createButtonBackground(ColorRGBA color) {
QuadBackgroundComponent background = new QuadBackgroundComponent(color);
Texture gradient = app.getAssetManager().loadTexture("Textures/gradient.png");
if (gradient != null) background.setTexture(gradient);
return background;
}
/**
* Handles the end turn event.
*/
private void handleEndTurn() {
Player currentPlayer = playerHandler.getPlayerById(app.getId());
if (currentPlayer.getAccountBalance() < 0 && !bankruptPopUp) {
new Bankrupt(app).open();
bankruptPopUp = true;
} else {
bankruptPopUp = false;
app.getGameLogic().send(new EndTurn());
receivedEvent(new ButtonStatusEvent(false));
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);
}
}
/**
* Starts the dice animation.
*/
private void startDiceAnimation() {
long startTime = System.currentTimeMillis();
new Thread(() -> {
try {
animateDice(startTime);
if (latestDiceRollEvent != null) {
showFinalDiceResult(latestDiceRollEvent);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
/**
* Animates the dice roll.
* Erstellt die Anzeige für die aktuelle Position.
*
* @param startTime The start time of the animation.
* @throws InterruptedException If the animation is interrupted.
* @return Das BitmapText-Objekt für die Anzeige
*/
private void animateDice(long startTime) throws InterruptedException {
int[] currentFace = {1};
while (System.currentTimeMillis() - startTime < 2000) {
currentFace[0] = (currentFace[0] % 6) + 1;
updateDiceIcons(currentFace[0]);
Thread.sleep(100);
}
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;
}
/**
* Updates the dice icons with the specified face.
*
* @param face The face of the dice.
* Aktualisiert die Anzeige für die aktuelle Position.
*/
private void updateDiceIcons(int face) {
app.enqueue(() -> {
setDiceIcon(imageLabel, diceToString(face));
setDiceIcon(imageLabel2, diceToString((face % 6) + 1));
});
private void updatePositionDisplay() {
positionText.setText("Feld-ID: " + currentPosition);
}
/**
* Shows the final dice result.
*
* @param event The dice roll event.
* Entfernt die Toolbar.
*/
private void showFinalDiceResult(DiceRollEvent event) {
app.enqueue(() -> {
setDiceIcon(imageLabel, diceToString(event.a()));
setDiceIcon(imageLabel2, diceToString(event.b()));
});
}
/**
* Sets the dice icon with the specified image path.
*
* @param label The label representing the dice.
* @param imagePath The path to the icon image.
*/
private void setDiceIcon(Label label, String imagePath) {
IconComponent icon = new IconComponent(imagePath);
icon.setIconSize(new Vector2f(80, 80));
label.setIcon(icon);
}
/**
* Converts the dice number to a string representation.
*
* @param i The dice number.
* @return The string representation of the dice number.
*/
private String diceToString(int i) {
return "Pictures/dice/" + switch (i) {
case 1 -> "one";
case 2 -> "two";
case 3 -> "three";
case 4 -> "four";
case 5 -> "five";
case 6 -> "six";
default -> throw new IllegalArgumentException("Invalid dice number: " + i);
} + ".png";
}
/**
* Handles dice roll events and updates the dice display.
*
* @param event the dice roll event containing dice values
*/
@Override
public void receivedEvent(DiceRollEvent event) {
latestDiceRollEvent = event;
}
/**
* Updates the player view by refreshing the player information displayed on the toolbar.
*
* @param event the update player view event
*/
@Override
public void receivedEvent(UpdatePlayerView event) {
playerHandler = app.getGameLogic().getPlayerHandler();
refreshPlayerView();
}
/**
* Refreshes the player view.
*/
private void refreshPlayerView() {
accountContainer.clearChildren();
overviewContainer.clearChildren();
addAccountDetails();
addOverviewDetails();
accountContainer.setBackground(null);
overviewContainer.setBackground(null);
}
/**
* Adds the account details to the player view.
*/
private void addAccountDetails() {
Player currentPlayer = playerHandler.getPlayerById(app.getId());
accountContainer.addChild(new Label("Kontostand", new ElementId("label-toolbar")));
accountContainer.addChild(new Label(currentPlayer.getAccountBalance() + " EUR", new ElementId("label-account")));
accountContainer.addChild(new Label("Gulag Karten", new ElementId("label-toolbar")));
accountContainer.addChild(new Label(String.valueOf(currentPlayer.getNumJailCard()), new ElementId("label-account")));
}
/**
* Adds the overview details to the player view.
*/
private void addOverviewDetails() {
overviewContainer.addChild(new Label("Übersicht", new ElementId("label-toolbar")));
for (Player player : playerHandler.getPlayers()) {
if (player.getId() != app.getId()) {
Label playerLabel = new Label(
player.getName() + ": " + player.getAccountBalance() + " EUR",
new ElementId("label-player")
);
playerLabel.setColor(Player.getColor(player.getId()).getColor());
overviewContainer.addChild(playerLabel);
}
}
}
/**
* Updates the status of toolbar buttons based on the provided button status event.
* Disables or enables buttons such as trade, property menu, and end turn based on the player's turn status.
*
* @param event the button status event indicating whether the buttons should be enabled
*/
@Override
public void receivedEvent(ButtonStatusEvent event) {
boolean enabled = event.buttonsEnabled();
canRollDice = enabled;
tradeButton.setEnabled(enabled);
propertyMenuButton.setEnabled(enabled);
endTurnButton.setEnabled(false);
}
/**
* Closes the toolbar, detaching it from the GUI.
*/
@Override
public void close() {
public void remove() {
app.getGuiNode().detachChild(toolbarContainer);
super.close();
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
/**
* Updates the toolbar by refreshing player information.
*/
@Override
public void update() {
refreshPlayerView();
super.update();
app.getGuiNode().detachChild(positionText);
}
}

View File

@@ -1,406 +0,0 @@
package pp.monopoly.client.gui;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture;
import com.simsilica.lemur.*;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.core.VersionedList;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.message.client.TradeOffer;
import pp.monopoly.model.TradeHandler;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.model.fields.PropertyField;
import pp.monopoly.notification.Sound;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Represents the trade menu dialog in the Monopoly application.
* <p>
* Facilitates trade interactions between players, including selection of properties,
* currency, and special cards for offers and requests.
* </p>
*/
public class TradeMenu extends Dialog {
/** The Monopoly application instance. */
private final MonopolyApp app;
/** The trade handler managing trade logic. */
private final TradeHandler tradeHandler;
/** Main container for the trade menu UI. */
private final Container mainContainer;
/** Background geometry for the menu. */
private Geometry background;
private Selector<String> leftBuildingSelector, leftSpecialCardSelector;
private Selector<String> rightBuildingSelector, rightSpecialCardSelector;
private Label leftSelectionsLabel, rightSelectionsLabel;
private TextField leftCurrencyInput, rightCurrencyInput;
private VersionedReference<Set<Integer>> leftBuildingRef, rightBuildingRef;
private VersionedReference<Set<Integer>> leftCardRef, rightCardRef;
private Set<String> rightselBuildings = new HashSet<>();
private Set<String> leftselBuildings = new HashSet<>();
private static final ColorRGBA TRANSLUCENT_WHITE = new ColorRGBA(1, 1, 1, 0.5f);
/**
* Constructs the trade menu dialog.
*
* @param app the Monopoly application instance
* @param tradeHandler the handler managing trade interactions
*/
public TradeMenu(MonopolyApp app, TradeHandler tradeHandler) {
super(app.getDialogManager());
this.app = app;
this.tradeHandler = tradeHandler;
addBackgroundImage();
mainContainer = createMainContainer();
app.getGuiNode().attachChild(mainContainer);
positionMainContainer();
initializeReferences();
}
/** Creates the main container for the trade menu UI. */
private Container createMainContainer() {
Container container = new Container(new SpringGridLayout(Axis.Y, Axis.X));
container.setPreferredSize(new Vector3f(1200, 800, 0));
container.setBackground(new QuadBackgroundComponent(TRANSLUCENT_WHITE));
container.addChild(createHeader());
container.addChild(createMainContent());
return container;
}
/** Creates the header label for the trade menu. */
private Label createHeader() {
Label header = new Label("Handelsmenü", new ElementId("label-Bold"));
header.setFontSize(50);
header.setInsets(new Insets3f(10, 10, 10, 10));
header.setBackground(new QuadBackgroundComponent(TRANSLUCENT_WHITE));
return header;
}
/** Creates the main content section of the trade menu. */
private Container createMainContent() {
Container mainContent = new Container(new SpringGridLayout(Axis.X, Axis.Y));
mainContent.setPreferredSize(new Vector3f(1200, 700, 0));
mainContent.addChild(createTradeColumn("Wähle Handelsobjekt:", true));
mainContent.addChild(createMiddleSection());
mainContent.addChild(createTradeColumn("Wähle Zielobjekt:", false));
return mainContent;
}
/** Sets the trade data based on the current selections. */
private void setTrades() {
String leftCurreny = leftCurrencyInput.getText().equals("")? "0" : leftCurrencyInput.getText();
String rightCurreny = rightCurrencyInput.getText().equals("")? "0" : rightCurrencyInput.getText();
tradeHandler.setOfferedAmount(Integer.parseInt(leftCurreny));
tradeHandler.setRequestedAmount(Integer.parseInt(rightCurreny));
tradeHandler.setOfferedJailCards(Integer.parseInt(leftSpecialCardSelector.getSelectedItem()));
tradeHandler.setRequestedJailCards(Integer.parseInt(rightSpecialCardSelector.getSelectedItem()));
Set<PropertyField> offeredProperties = new HashSet<>();
for (String propertyString : leftselBuildings) {
offeredProperties.add( (PropertyField) app.getGameLogic().getBoardManager().getFieldByName(propertyString));
}
Set<PropertyField> requestedProperties = new HashSet<>();
for (String propertyString : rightselBuildings) {
requestedProperties.add( (PropertyField) app.getGameLogic().getBoardManager().getFieldByName(propertyString));
}
tradeHandler.setOfferedProperties(offeredProperties);
tradeHandler.setRequestedProperties(requestedProperties);
}
/**
* Creates a trade column for either the sender or receiver.
*
* @param label the label text for the column
* @param isLeft true if the column is for the sender; false otherwise
*/
private Container createTradeColumn(String label, boolean isLeft) {
Container column = new Container(new SpringGridLayout(Axis.Y, Axis.X));
column.setBackground(new QuadBackgroundComponent(ColorRGBA.White));
Label columnLabel = column.addChild(new Label(label));
columnLabel.setFontSize(24);
columnLabel.setBackground(new QuadBackgroundComponent(TRANSLUCENT_WHITE));
column.addChild(new Label("Gebäude:"));
Selector<String> buildingSelector = createPropertySelector(isLeft);
column.addChild(buildingSelector);
column.addChild(new Label("Währung:"));
TextField currencyInput = createCurrencyInput();
column.addChild(currencyInput);
column.addChild(new Label("Sonderkarten:"));
Selector<String> specialCardSelector = createSpecialCardSelector(isLeft);
styleSelector(specialCardSelector);
column.addChild(specialCardSelector);
assignSelectors(buildingSelector, specialCardSelector, currencyInput, isLeft);
return column;
}
/**
* Creates a property selector for buildings.
*
* @param isLeft true if the selector is for the sender; false otherwise
* @return the created property selector
*/
private Selector<String> createPropertySelector(boolean isLeft) {
VersionedList<String> properties = new VersionedList<>();
for (PropertyField field : getPropertyFields(isLeft)) {
properties.add(field.getName());
}
Selector<String> selector = new Selector<>(properties, "glass");
styleSelector(selector);
return selector;
}
/**
* Creates a selector for special cards.
*
* @param isLeft true if the selector is for the sender; false otherwise
* @return the created special card selector
*/
private Selector<String> createSpecialCardSelector(boolean isLeft) {
VersionedList<String> numbers = new VersionedList<>();
for (int i = 0; i <= app.getGameLogic().getPlayerHandler().getPlayerById(isLeft ? tradeHandler.getReceiver().getId() : tradeHandler.getSender().getId()).getNumJailCard(); i++) {
numbers.add(i + "");
}
Selector<String> selector = new Selector<>(numbers, "glass");
styleSelector(selector);
return selector;
}
/**
* Retrieves the property fields owned by the respective player.
*
* @param isLeft true if retrieving fields for the sender; false otherwise
* @return an iterable of property fields
*/
private Iterable<PropertyField> getPropertyFields(boolean isLeft) {
Set<PropertyField> building = app.getGameLogic()
.getBoardManager()
.getPropertyFields(app.getGameLogic()
.getPlayerHandler()
.getPlayerById(isLeft ? tradeHandler.getSender().getId() : tradeHandler.getReceiver().getId())
.getProperties())
.stream()
.filter(p -> p instanceof BuildingProperty)
.map(p -> (BuildingProperty) p)
.filter(p -> p.getHouses() == 0)
.collect(Collectors.toSet());
Set<PropertyField> rest = app.getGameLogic()
.getBoardManager()
.getPropertyFields(app.getGameLogic()
.getPlayerHandler()
.getPlayerById(isLeft ? tradeHandler.getSender().getId() : tradeHandler.getReceiver().getId())
.getProperties())
.stream()
.filter(p -> !(p instanceof BuildingProperty))
.collect(Collectors.toSet());
List<PropertyField> combinedProperties = new ArrayList<>();
combinedProperties.addAll(building);
combinedProperties.addAll(rest);
combinedProperties = combinedProperties.stream().sorted(Comparator.comparingInt(PropertyField::getId)).collect(Collectors.toList());
return combinedProperties;
}
/** Creates a text field for currency input. */
private TextField createCurrencyInput() {
TextField currencyInput = new TextField("0");
styleTextField(currencyInput);
return currencyInput;
}
/** Creates the middle section containing buttons and summary fields. */
private Container createMiddleSection() {
Container middleSection = new Container(new SpringGridLayout(Axis.Y, Axis.X));
middleSection.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f)));
Label middleLabelTop = middleSection.addChild(new Label("Meine Gebäude:"));
middleLabelTop.setFontSize(24);
middleLabelTop.setTextVAlignment(VAlignment.Center);
middleLabelTop.setTextHAlignment(HAlignment.Center);
middleLabelTop.setInsets(new Insets3f(5, 5, 5, 5));
leftSelectionsLabel = middleSection.addChild(new Label(""));
leftSelectionsLabel.setPreferredSize(new Vector3f(600, 50, 0));
Container buttons = middleSection.addChild(new Container(new SpringGridLayout()));
Button cancel = new Button("Abbrechen");
cancel.addClickCommands(s -> ifTopDialog(() -> {
close();
app.getGameLogic().playSound(Sound.BUTTON);
}));
Button trade = new Button("Handeln");
trade.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
setTrades();
app.getGameLogic().send(new TradeOffer(tradeHandler));
close();
}));
buttons.addChild(cancel);
buttons.addChild(trade);
Label middleLabelBottom = middleSection.addChild(new Label("Gebäude des Gegenspielers:"));
middleLabelBottom.setFontSize(24);
middleLabelBottom.setTextVAlignment(VAlignment.Center);
middleLabelBottom.setTextHAlignment(HAlignment.Center);
middleLabelBottom.setInsets(new Insets3f(5, 5, 5, 5));
rightSelectionsLabel = middleSection.addChild(new Label(""));
rightSelectionsLabel.setPreferredSize(new Vector3f(600, 50, 0));
return middleSection;
}
/** Styles the given selector with insets and background color. */
private void styleSelector(Selector<String> selector) {
selector.setInsets(new Insets3f(5, 10, 5, 10));
selector.setBackground(new QuadBackgroundComponent(ColorRGBA.Black));
}
/** Styles the given text field with insets and background color. */
private void styleTextField(TextField textField) {
textField.setInsets(new Insets3f(5, 10, 5, 10));
textField.setBackground(new QuadBackgroundComponent(ColorRGBA.Black));
textField.setPreferredSize(new Vector3f(300, 30, 0));
}
/**
* Assigns selectors and input fields to either the left or right column.
*
* @param buildingSelector the building selector
* @param specialCardSelector the special card selector
* @param currencyInput the currency input field
* @param isLeft true if assigning to the left column; false otherwise
*/
private void assignSelectors(Selector<String> buildingSelector, Selector<String> specialCardSelector, TextField currencyInput, boolean isLeft) {
if (isLeft) {
leftBuildingSelector = buildingSelector;
leftSpecialCardSelector = specialCardSelector;
leftCurrencyInput = currencyInput;
} else {
rightBuildingSelector = buildingSelector;
rightSpecialCardSelector = specialCardSelector;
rightCurrencyInput = currencyInput;
}
}
/** Positions the main container at the center of the screen. */
private void positionMainContainer() {
mainContainer.setLocalTranslation(
(app.getCamera().getWidth() - mainContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + mainContainer.getPreferredSize().y) / 2,
7
);
}
/** Adds a background image to the trade menu. */
private void addBackgroundImage() {
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
background = new Geometry("Background", quad);
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
backgroundMaterial.setTexture("ColorMap", backgroundImage);
background.setMaterial(backgroundMaterial);
background.setLocalTranslation(0, 0, 6);
app.getGuiNode().attachChild(background);
}
/** Initializes references for tracking UI changes. */
private void initializeReferences() {
leftBuildingRef = leftBuildingSelector.getSelectionModel().createReference();
leftCardRef = leftSpecialCardSelector.getSelectionModel().createReference();
rightBuildingRef = rightBuildingSelector.getSelectionModel().createReference();
rightCardRef = rightSpecialCardSelector.getSelectionModel().createReference();
}
/**
* Updates the trade menu state, including selected properties and values.
*
* @param delta the time elapsed since the last update
*/
@Override
public void update(float delta) {
if (leftBuildingRef.update() || leftCardRef.update()) {
updateSelections(leftSelectionsLabel, leftBuildingSelector, true);
}
if (rightBuildingRef.update() || rightCardRef.update()) {
updateSelections(rightSelectionsLabel, rightBuildingSelector, false);
}
}
/**
* Updates the displayed selections for properties.
*
* @param target the target label to update
* @param building the building selector
* @param isLeft true if updating the left column; false otherwise
*/
private void updateSelections(Label target, Selector<String> building, boolean isLeft) {
StringBuilder buildingText = new StringBuilder();
if (isLeft) {
if (leftselBuildings.contains(building.getSelectedItem())) {
leftselBuildings.remove(building.getSelectedItem()); // Remove if already selected
} else {
leftselBuildings.add(building.getSelectedItem()); // Add if not already selected
}
for (String property : leftselBuildings) {
buildingText.append(property).append(", ");
}
} else {
if (rightselBuildings.contains(building.getSelectedItem())) {
rightselBuildings.remove(building.getSelectedItem()); // Remove if already selected
} else {
rightselBuildings.add(building.getSelectedItem()); // Add if not already selected
}
for (String property : rightselBuildings) {
buildingText.append(property).append(", ");
}
}
target.setText(buildingText.toString().replaceAll(", $", ""));
}
/** Opens the settings menu when the escape key is pressed. */
@Override
public void escape() {
new SettingsMenu(app).open();
}
/** Closes the trade menu and detaches UI elements. */
@Override
public void close() {
app.getGuiNode().detachChild(mainContainer);
app.getGuiNode().detachChild(background);
super.close();
}
}

View File

@@ -1,37 +0,0 @@
package pp.monopoly.client.gui;
import com.simsilica.lemur.Slider;
import pp.monopoly.client.GameMusic;
/**
* The VolumeSlider class represents the Volume Slider in the Menu.
* It extends the Slider class and provides functionalities for setting the music volume,
* with the help of the Slider in the GUI
*/
public class VolumeSlider extends Slider {
private final pp.monopoly.client.GameMusic music;
private double vol;
/**
* Constructs the Volume Slider for the Menu dialog
* @param music the music instance
*/
public VolumeSlider(GameMusic music) {
super();
this.music = music;
vol = GameMusic.volumeInPreferences();
getModel().setPercent(vol);
}
/**
* When triggered it updates the volume to the value set with the slider
*/
public void update() {
if (vol != getModel().getPercent()) {
vol = getModel().getPercent();
music.setVolume( (float) vol);
}
}
}

View File

@@ -1,504 +0,0 @@
/*
* $Id$
*
* Copyright (c) 2014, Simsilica, LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package pp.monopoly.client.gui.hslider;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.FillMode;
import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.HAlignment;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.core.GuiControl;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.grid.GridModel;
import com.simsilica.lemur.style.Attributes;
import com.simsilica.lemur.style.ElementId;
import com.simsilica.lemur.style.StyleDefaults;
import com.simsilica.lemur.style.Styles;
/**
*
* @author Paul Speed
*/
public class GridPanel extends Panel {
public static final String ELEMENT_ID = "grid";
private GridModel<Panel> model;
private VersionedReference<GridModel<Panel>> modelRef;
private SpringGridLayout layout;
private int visibleRows = 5;
private int visibleColumns = 5;
private int row = 0;
private int column = 0;
private Float alpha; // for setting to new children
private Float[] columnwidths = null;
private Float[] rowheight = null;
private boolean widthsupdate = false;
private HAlignment [] columnHalignement = null;
public GridPanel(GridModel<Panel> model ) {
this(true, model, new ElementId(ELEMENT_ID), null);
}
public GridPanel(GridModel<Panel> model, String style ) {
this(true, model, new ElementId(ELEMENT_ID), style);
}
public GridPanel(GridModel<Panel> model, ElementId elementId, String style ) {
this(true, model, elementId, style);
}
protected GridPanel(boolean applyStyles, GridModel<Panel> model,
ElementId elementId, String style ) {
super(false, elementId, style);
this.layout = new SpringGridLayout(Axis.Y, Axis.X,
FillMode.ForcedEven,
FillMode.ForcedEven);
getControl(GuiControl.class).setLayout(layout);
if( applyStyles ) {
Styles styles = GuiGlobals.getInstance().getStyles();
styles.applyStyles(this, elementId, style);
}
setModel(model);
}
// change the layout if necessary
public void setLayout(SpringGridLayout lay) {
this.layout = lay;
getControl(GuiControl.class).getLayout().clearChildren();
getControl(GuiControl.class).setLayout(layout);
this.modelRef = null;
if( this.model != null ) {
this.modelRef = model.createReference();
refreshGrid();
}
}
@StyleDefaults(ELEMENT_ID)
public static void initializeDefaultStyles( Attributes attrs ) {
}
public void setModel( GridModel<Panel> model ) {
if( this.model == model ) {
return;
}
if( this.model != null ) {
// Clear the old panel
getControl(GuiControl.class).getLayout().clearChildren();
this.modelRef = null;
}
this.model = model;
if( this.model != null ) {
this.modelRef = model.createReference();
refreshGrid();
}
}
public GridModel<Panel> getModel() {
return model;
}
public void setRow( int row ) {
setLocation(row, column);
}
public int getRow() {
return row;
}
public void setColumn( int column ) {
setLocation(row, column);
}
public int getColumn() {
return column;
}
public Panel getCell( int r, int c ) {
r = r - row;
c = c - column;
if( r < 0 || c < 0 || r >= visibleRows || c >= visibleColumns ) {
return null;
}
return (Panel)layout.getChild(r, c);
}
public void setLocation( int row, int column ) {
if( this.row == row && this.column == column ) {
return;
}
this.row = row;
this.column = column;
refreshGrid();
}
public void setVisibleSize( int rows, int columns ) {
this.visibleRows = rows;
this.visibleColumns = columns;
getControl(GuiControl.class).getLayout().clearChildren();
refreshGrid();
}
public void setVisibleRows( int rows ) {
setVisibleSize(rows, visibleColumns);
}
public int getVisibleRows() {
return visibleRows;
}
public void setVisibleColumns( int columns ) {
setVisibleSize(visibleRows, columns);
}
public int getVisibleColumns() {
return visibleColumns;
}
public void setAlpha( float alpha, boolean recursive ) {
this.alpha = alpha;
super.setAlpha(alpha, recursive);
}
protected void refreshGrid() {
widthsupdate = false;
if( model == null ) {
getControl(GuiControl.class).getLayout().clearChildren();
return;
}
for( int r = row; r < row + visibleRows; r++ ) {
for( int c = column; c < column + visibleColumns; c++ ) {
Node existing = layout.getChild(r-row, c-column);
if( r < 0 || r >= model.getRowCount() || c < 0 || c >= model.getColumnCount() ) {
// Out of bounds
layout.addChild(null, r-row, c-column);
} else {
Panel child = model.getCell(r, c, (Panel)existing);
// we check if there is a size for either rowheight or columnwidth stored
Float ytemp = null;
Float xtemp = null;
if (columnwidths !=null) {
if (columnwidths.length > c) {
if (columnwidths[c] != null) xtemp = columnwidths[c];
}
}
if (rowheight != null) {
if (rowheight.length > r) {
if (rowheight[r] !=null) ytemp = rowheight[r];
}
}
// and only if, we set the preferred size
if (ytemp !=null || xtemp != null) {
child.setPreferredSize(null);
if (xtemp != null) child.setPreferredSize(child.getPreferredSize().clone().setX(xtemp));
if (ytemp !=null) child.setPreferredSize(child.getPreferredSize().clone().setY(ytemp));
}
if (columnHalignement != null) {
if (columnHalignement.length > c) {
if (columnHalignement[c] != null) {
// we only set the alignement for "text" elements attached to the listbox
// for others e.g. progressbar etc. we should call the element and do the changes there
if (child instanceof Button) ((Button) child).setTextHAlignment(columnHalignement[c]);
if (child instanceof Label) ((Label) child).setTextHAlignment(columnHalignement[c]);
if (child instanceof TextField)
((TextField) child).setTextHAlignment(columnHalignement[c]);
} else if (child instanceof Button) ((Button) child).setTextHAlignment(HAlignment.Left);
} else if (child instanceof Button) ((Button) child).setTextHAlignment(HAlignment.Left);
} else if (child instanceof Button) ((Button) child).setTextHAlignment(HAlignment.Left);
if( child != existing ) {
// Make sure new children pick up the alpha of the container
if( alpha != null && alpha != 1 ) {
child.setAlpha(alpha);
}
layout.addChild(child, r-row, c-column);
}
}
}
}
}
@Override
public void updateLogicalState( float tpf ) {
super.updateLogicalState(tpf);
if( modelRef.update() ) {
refreshGrid();
}
if (widthsupdate) refreshGrid();
}
@Override
public String toString() {
return getClass().getName() + "[elementId=" + getElementId() + "]";
}
public Float[] getColumnwidths() {
return columnwidths;
}
public Float[] getRowheights() {
return rowheight;
}
public void setRowheight(Float rowheight, int rownumber_starting_with_0){
// adds or replaces a columnwidth
if (rownumber_starting_with_0 < 0) return;
int size = rownumber_starting_with_0+1;
if (this.rowheight != null) {
size = Math.max(size, this.rowheight.length);
}
preparegridsizes(size,false);
setcheckedsize(rowheight,rownumber_starting_with_0,true,true);
}
public void setColumnwidths(Float columnwidth, int columnnumber_starting_with_0) {
// adds or replaces a columnwidth
if (columnnumber_starting_with_0 <0) return;
int size = columnnumber_starting_with_0+1;
if (this.columnwidths != null) {
size = Math.max(size, this.columnwidths.length);
}
preparegridsizes(size,true);
setcheckedsize(columnwidth,columnnumber_starting_with_0,true,false);
}
public void setRowheight (Float[] rowheights_or_null) {
// replaces the given rowheights and only keep the number of rows given
setRowheight(rowheights_or_null,true);
}
public void setColumnwidths (Float[] columnwidths_or_null) {
// replaces the given columnwidths and only keep the number of columns given
setColumnwidths(columnwidths_or_null,true);
}
public void setRowheight(Float[] rowheights_or_null, boolean write_null_values) {
// replaces the given rowheights and only keep the number of rows given
// null values either overwrite or will be ignored
Integer tmp = null;
if (rowheights_or_null != null) tmp = rowheights_or_null.length;
if (preparegridsizes(tmp,false)) {
for (int i = 0; i <tmp;i++) {
setcheckedsize(rowheights_or_null[i],i,write_null_values,true);
}
}
}
public void setColumnwidths(Float[] columnwidths_or_null, boolean write_null_values) {
// replaces the given columnwidths_or_null and only keep the number of columns given
// null values either overwrite or will be ignored
Integer tmp = null;
if (columnwidths_or_null != null) tmp = columnwidths_or_null.length;
if (preparegridsizes(tmp,true)) {
for (int i = 0; i <tmp;i++) {
setcheckedsize(columnwidths_or_null[i],i,write_null_values,false);
}
}
}
private boolean preparegridsizes(Integer givenfieldsize, boolean width) {
// prepares and adjust the size of the field(s) that hold columnwidth or rowheights
if (givenfieldsize == null) {
if (width) { //either columns or rows will be set null
columnwidths =null;
} else {
rowheight = null;
}
for (Spatial p: this.getChildren()) {
Panel x = (Panel) p;
x.setPreferredSize(null); //we set all sizes to 0 as we will recalculate them with next update
}
widthsupdate = true;
return false; // no more activity needed
} else {
Float[] tmp;
tmp = new Float[givenfieldsize];
if (width) {
if (columnwidths == null) {
columnwidths = tmp;
return true;
}
int i = 0;
for (Float z:columnwidths) {
tmp[i] =z;
i++;
if (i>= givenfieldsize) break;
}
columnwidths = tmp; // we get what we have
} else {
if (rowheight == null) {
rowheight = tmp;
return true;
}
int i = 0;
for (Float z:rowheight) {
tmp[i] =z;
i++;
if (i>= givenfieldsize) break;
}
rowheight = tmp; // we get what we have
}
return true;
}
}
private void setcheckedsize(Float size, int row_or_column, boolean override, boolean i_am_a_row) {
if (override || (size !=null)) {
if (i_am_a_row) {
rowheight[row_or_column] = size;
} else {
columnwidths[row_or_column] = size;
}
widthsupdate = true;
}
}
// public void setRowheight(Float rowheight_or_null){
// if (rowheight_or_null<=0) return;
// this.rowheight = rowheight_or_null;
// widthsupdate = true;
// }
public void setHalignements(HAlignment[] hals) {
setHalignements(hals,false);
}
public void setHalignements(HAlignment[] hals, boolean overridestandard) {
if (checkexistinghal(hals)) {
int i =0;
for (HAlignment z:hals) {
setHalignementchecked(z,i,overridestandard);
i++;
if (i>=columnHalignement.length) return; // we ignore given HAlignement that is out of column bound
}
if (!overridestandard ) return;
for (int u = i; u<columnHalignement.length;u++) {
setHalignementchecked(null,u,overridestandard); // override existing HAlignements
}
}
}
public void setHalignements(HAlignment hal, int column) {
checkexistinghal(new HAlignment[]{HAlignment.Center});
if (column < columnHalignement.length) setHalignementchecked(hal,column,true);
}
private void setHalignementchecked(HAlignment hal, int column,boolean override) {
if (override || (hal !=null)) {
columnHalignement[column] = hal;
widthsupdate = true;
}
}
private boolean checkexistinghal(HAlignment[] givenfield) {
// delete the given Halignements
if (givenfield == null) {
columnHalignement =null;
for (Spatial p: this.getChildren()) {
Button x = (Button)((Panel) p);
x.setTextHAlignment(HAlignment.Left); //standard HAlignement
}
widthsupdate = true;
return false;
}
// or prepare if we have no columnHalignement yet
HAlignment[] tmp;
if (columnHalignement == null) {
tmp = new HAlignment[model.getColumnCount()];
columnHalignement = tmp;
} else {
if (!(columnHalignement.length ==model.getColumnCount() )) {
tmp = new HAlignment[model.getColumnCount()];
int i = 0;
for (HAlignment z:columnHalignement) {
tmp[i] =z;
i++;
if (i>=model.getColumnCount()) break;
}
columnHalignement = tmp;
}
}
return true;
}
public HAlignment[] getColumnHalignement() {
return columnHalignement;
}
}

View File

@@ -1,135 +0,0 @@
/*
* $Id$
*
* Copyright (c) 2014, Simsilica, LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package pp.monopoly.client.gui.hslider;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import com.simsilica.lemur.core.VersionedObject;
import com.simsilica.lemur.core.VersionedReference;
/**
*
* @author Paul Speed
*/
public class VersionedList<T> extends AbstractList<T>
implements VersionedObject<List<T>> {
private long version = 0;
private List<T> list;
protected VersionedList(List<T> items, boolean copy ) {
if( copy ) {
list = new ArrayList<T>();
list.addAll(items);
} else {
this.list = items;
}
}
public VersionedList() {
this(new ArrayList<T>(), false);
}
public VersionedList(List<T> items ) {
this(items, true);
}
/**
* Wraps a list in a VersionedList instead of copying it.
* This is useful for cases where a VersionedList is required
* but strict versioning is not, for example, passing a static list
* to a ListBox. Changes to the wrapped list obviously don't
* trigger version changes in the wrapper. Only changes through
* the wrapper will increment the version.
*/
public static <T> VersionedList<T> wrap( List<T> list ) {
return new VersionedList<T>(list, false);
}
public void incrementVersion() { // changed this to public
version++;
}
@Override
public long getVersion() {
return version;
}
@Override
public List<T> getObject() {
return this;
}
@Override
public VersionedReference<List<T>> createReference() {
return new VersionedReference<List<T>>(this);
}
@Override
public T get( int i ) {
return list.get(i);
}
@Override
public int size() {
return list.size();
}
@Override
public T set( int i, T val ) {
T result = list.set(i, val);
incrementVersion();
return result;
}
@Override
public void add( int i, T val ) {
list.add(i, val);
incrementVersion();
}
@Override
public T remove( int i ) {
T result = list.remove(i);
incrementVersion();
return result;
}
}

View File

@@ -1,144 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.message.server.TradeReply;
import pp.monopoly.notification.Sound;
/**
* Represents a confirmation dialog that appears when a trade is accepted .
* <p>
* Displays a message to the user informing them that the trade they proposed has been accepted,
* along with a confirmation button to close the dialog.
* </p>
*/
public class AcceptTrade extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the dialog. */
private final Geometry overlayBackground;
/** Container for the warning message content. */
private final Container noMoneyWarningContainer;
/** Background container providing a border for the dialog. */
private final Container backgroundContainer;
/**
* Constructs the accept trade dialog.
*
* @param app the Monopoly application instance
* @param msg the trade reply message containing details about the trade
*/
public AcceptTrade(MonopolyApp app, TradeReply msg) {
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 die Warnung
noMoneyWarningContainer = new Container();
noMoneyWarningContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
noMoneyWarningContainer.setPreferredSize(new Vector3f(550,250,10));
float padding = 10; // Passt den backgroundContainer an die Größe des bankruptContainers an
backgroundContainer.setPreferredSize(noMoneyWarningContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label gateFieldTitle = noMoneyWarningContainer.addChild(new Label("Handel angenommen!", new ElementId("warning-title")));
gateFieldTitle.setFontSize(48);
gateFieldTitle.setColor(ColorRGBA.Black);
// Text, der im Popup steht
Container textContainer = noMoneyWarningContainer.addChild(new Container());
textContainer.addChild(new Label("Du hast Spieler"+ " " + msg.getTradeHandler().getReceiver().getName() + " " + "einen Handel vorgeschlagen", new ElementId("label-Text")));
textContainer.addChild(new Label("", new ElementId("label-Text")));
textContainer.addChild(new Label("Der Handel wurde angenommen", new ElementId("label-Text")));
textContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Passt den textContainer an die Größe des bankruptContainers an
textContainer.setPreferredSize(noMoneyWarningContainer.getPreferredSize().addLocal(-250,-200,0));
// Beenden-Button
Button quitButton = noMoneyWarningContainer.addChild(new Button("Bestätigen", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(source -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Zentriere das Popup
noMoneyWarningContainer.setLocalTranslation(
(app.getCamera().getWidth() - noMoneyWarningContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + noMoneyWarningContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - noMoneyWarningContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + noMoneyWarningContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(noMoneyWarningContainer);
}
/**
* Creates a semi-transparent background overlay for the dialog.
*
* @return the geometry of the overlay
*/
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;
}
/**
* Closes the dialog and removes the associated GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(noMoneyWarningContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
/**
* Handles the escape key action by closing the dialog.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,133 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
/**
* Bankrupt is a Warning-Popup which appears when the balance is negative at the end of a player´s turn
*/
public class Bankrupt extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the popup. */
private final Geometry overlayBackground;
/** Main container for the bankruptcy warning content. */
private final Container bankruptContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer;
/**
* Constructs the bankruptcy warning popup.
*
* @param app the Monopoly application instance
*/
public Bankrupt(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 die Warnung
bankruptContainer = new Container();
bankruptContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
bankruptContainer.setPreferredSize(new Vector3f(550,250,10));
float padding = 10; // Passt den backgroundContainer an die Größe des bankruptContainers an
backgroundContainer.setPreferredSize(bankruptContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label gateFieldTitle = bankruptContainer.addChild(new Label("Vorsicht !", new ElementId("warning-title")));
gateFieldTitle.setFontSize(48);
gateFieldTitle.setColor(ColorRGBA.Black);
// Text, der im Popup steht
Container textContainer = bankruptContainer.addChild(new Container());
textContainer.addChild(new Label("Du hast noch einen negativen Kontostand. Wenn du jetzt deinen Zug beendest, gehst du Bankrott und verlierst das Spiel!\n"+
"Dieses PopUp wird nicht erneut angezeigt!", new ElementId("label-Text")));
textContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Passt den textContainer an die Größe des bankruptContainers an
textContainer.setPreferredSize(bankruptContainer.getPreferredSize().addLocal(-250,-200,0));
// Beenden-Button
Button quitButton = bankruptContainer.addChild(new Button("Bestätigen", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(source -> ifTopDialog(this::close));
// Zentriere das Popup
bankruptContainer.setLocalTranslation(
(app.getCamera().getWidth() - bankruptContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + bankruptContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - bankruptContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + bankruptContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(bankruptContainer);
}
/**
* Creates a semi-transparent background overlay for the popup.
*
* @return the geometry of the overlay
*/
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;
}
/**
* Closes the menu and removes the GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(bankruptContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
/**
* Handles the escape key action by closing the popup.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,127 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.math.ColorRGBA;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.message.client.BuyPropertyResponse;
import pp.monopoly.model.fields.BoardManager;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.notification.Sound;
/**
* BuildingPropertyCard creates the popup for field information
*/
public class BuildingPropertyCard extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the building property information. */
private final Container buildingPropertyContainer;
/** Background container providing a border for the property card. */
private final Container backgroundContainer;
/**
* Constructs a property card popup displaying details about the building property.
*
* @param app the Monopoly application instance
*/
public BuildingPropertyCard(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
//Generate the corresponding field
int index = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
BuildingProperty field = (BuildingProperty) new BoardManager().getFieldAtIndex(index);
// Create the background container
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
attachChild(backgroundContainer);
// Hauptcontainer für die Gebäudekarte
buildingPropertyContainer = new Container();
buildingPropertyContainer.setBackground(new QuadBackgroundComponent(field.getColor().getColor()));
float padding = 10; // Passt den backgroundContainer an die Größe des buildingPropertyContainer an
backgroundContainer.setPreferredSize(buildingPropertyContainer.getPreferredSize().addLocal(padding, padding, 0));
//Titel
Label settingsTitle = buildingPropertyContainer.addChild(new Label( field.getName(), new ElementId("label-Bold")));
settingsTitle.setBackground(new QuadBackgroundComponent(field.getColor().getColor()));
settingsTitle.setFontSize(48);
// Text, der auf der Karte steht
// Die Preise werden dynamisch dem BoardManager entnommen
Container propertyValuesContainer = buildingPropertyContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label("„Grundstückswert: " + field.getPrice() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("„Miete allein: " + field.getAllRent().get(0)+ " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 1 Haus: " + field.getAllRent().get(1) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 2 Häuser: " + field.getAllRent().get(2) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 3 Häuser: " + field.getAllRent().get(3) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 4 Häuser: " + field.getAllRent().get(4) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 1 Hotel: " + field.getAllRent().get(5) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-1 Haus kostet: " + field.getHousePrice()+ " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Beenden-Button
Button quitButton = buildingPropertyContainer.addChild(new Button("Beenden", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Kaufen-Button
Button buyButton = buildingPropertyContainer.addChild(new Button("Kaufen", new ElementId("button")));
buyButton.setFontSize(32);
buyButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
app.getGameLogic().send(new BuyPropertyResponse());
}));
// Zentriere das Popup
buildingPropertyContainer.setLocalTranslation(
(app.getCamera().getWidth() - buildingPropertyContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + buildingPropertyContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - buildingPropertyContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + buildingPropertyContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(buildingPropertyContainer);
}
/**
* Closes the popup and removes the associated GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(buildingPropertyContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
super.close();
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@@ -1,248 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Selector;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.core.VersionedList;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.game.server.Player;
import pp.monopoly.message.client.AlterProperty;
import pp.monopoly.model.fields.BoardManager;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.notification.Sound;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* BuyHouse is a popup which appears when a player clicks on the "Buy House"-button in the BuildingAdminMenu
*/
public class BuyHouse extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the "Buy House" popup UI. */
private final Container buyHouseContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer;
/** Label to display selected properties. */
private Label selectionDisplay;
/** Reference for tracking dropdown selection changes. */
private VersionedReference<Set<Integer>> selectionRef;
/** Dropdown selector for choosing properties to build houses on. */
private Selector<String> propertySelector;
/** Set of selected properties for house purchase. */
private Set<String> selectedProperties = new HashSet<>();
/** Label displaying the total cost of building houses on the selected properties. */
private Label cost = new Label("0", new ElementId("label-Text"));
/**
* Constructs the "Buy House" popup.
*
* @param app the Monopoly application instance
*/
public BuyHouse(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
// Create the background container
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
attachChild(backgroundContainer);
// Main container for the menu
buyHouseContainer = new Container();
buyHouseContainer.setPreferredSize(new Vector3f(800, 600, 0));
float padding = 10; // Adjust backgroundContainer size to match buyHouseContainer
backgroundContainer.setPreferredSize(buyHouseContainer.getPreferredSize().addLocal(padding, padding, 0));
// Title
Label title = buyHouseContainer.addChild(new Label("Gebäude Kaufen", new ElementId("label-Bold")));
title.setFontSize(48);
title.setColor(ColorRGBA.Black);
// Divide buyHouseContainer into three sub-containers
Container upContainer = buyHouseContainer.addChild(new Container());
Container middleContainer = buyHouseContainer.addChild(new Container());
Container downContainer = buyHouseContainer.addChild(new Container());
// Upper section text
upContainer.addChild(new Label("„Grundstück wählen:", new ElementId("label-Text")));
upContainer.addChild(new Label("", new ElementId("label-Text"))); // Empty line
upContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
middleContainer.setPreferredSize(new Vector3f(100, 150, 0));
middleContainer.setBackground(new QuadBackgroundComponent(ColorRGBA.Orange));
// Add the property selector dropdown
middleContainer.addChild(createPropertyDropdown());
downContainer.addChild(new Label("", new ElementId("label-Text"))); // Empty line
downContainer.addChild(new Label("Kosten:", new ElementId("label-Text"))); // Label for cost
downContainer.addChild(cost); // Cost details
downContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Cancel button
Button cancelButton = buyHouseContainer.addChild(new Button("Abbrechen", new ElementId("button")));
cancelButton.setFontSize(32);
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Confirm button
Button confirmButton = buyHouseContainer.addChild(new Button("Bestätigen", new ElementId("button")));
confirmButton.setFontSize(32);
confirmButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
AlterProperty msg = new AlterProperty("BuyHouse");
msg.setProperties(selectedProperties.stream().map(p -> app.getGameLogic().getBoardManager().getFieldByName(p).getId()).map(p -> (Integer) p).collect(Collectors.toSet()));
app.getGameLogic().send(msg);
close();
}));
// Center the popup
buyHouseContainer.setLocalTranslation(
(app.getCamera().getWidth() - buyHouseContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + buyHouseContainer.getPreferredSize().y) / 2,
8
);
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - buyHouseContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + buyHouseContainer.getPreferredSize().y + padding) / 2,
7
);
app.getGuiNode().attachChild(buyHouseContainer);
}
/**
* Creates a dropdown menu for selecting properties eligible for house construction.
*
* @return The container holding the dropdown menu.
*/
private Container createPropertyDropdown() {
Container dropdownContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
dropdownContainer.setPreferredSize(new Vector3f(300, 200, 0));
dropdownContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)));
VersionedList<String> propertyOptions = new VersionedList<>();
List<BuildingProperty> playerProperties = getPlayerProperties();
// Populate the dropdown with property names
for (BuildingProperty property : playerProperties) {
propertyOptions.add(property.getName());
}
propertySelector = new Selector<>(propertyOptions, "glass");
dropdownContainer.addChild(propertySelector);
// Track selection changes
selectionRef = propertySelector.getSelectionModel().createReference();
// Initialize the selection display here
selectionDisplay = new Label("");
selectionDisplay.setPreferredSize(new Vector3f(300, 30, 0));
selectionDisplay.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
dropdownContainer.addChild(selectionDisplay); // Add it to the dropdown container
// Set initial selection
if (!propertyOptions.isEmpty()) {
onDropdownSelectionChanged(propertySelector);
}
return dropdownContainer;
}
/**
* Retrieves the list of properties owned by the current player that are eligible for house construction.
*
* @return A list of {@link BuildingProperty} objects owned by the player.
*/
private List<BuildingProperty> getPlayerProperties() {
Player self = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId());
BoardManager boardManager = app.getGameLogic().getBoardManager();
return boardManager.getPropertyFields(self.getProperties()).stream()
.filter(property -> property instanceof BuildingProperty)
.map(property -> (BuildingProperty) property)
.filter(property -> app.getGameLogic().getBoardManager().canBuild(property))
.collect(Collectors.toList());
}
/**
* Periodically updates the popup, tracking selection changes in the dropdown menu.
*
* @param delta Time since the last update in seconds.
*/
@Override
public void update(float delta) {
if(selectionRef.update()) {
onDropdownSelectionChanged(propertySelector);
}
}
/**
* Handles property selection changes in the dropdown menu.
*
* @param playerProperties the dropdown selector for the player's properties
*/
private void onDropdownSelectionChanged(Selector<String> playerProperties) {
String selected = playerProperties.getSelectedItem();
app.getGameLogic().playSound(Sound.BUTTON);
if (selectedProperties.contains(selected)) {
selectedProperties.remove(selected);
} else {
selectedProperties.add(selected);
}
int cost = 0;
for (String s : selectedProperties) {
cost += ((BuildingProperty) app.getGameLogic().getBoardManager().getFieldByName(s)).getHousePrice();
}
String display = String.join(" | ", selectedProperties);
selectionDisplay.setText(display);
this.cost.setText(cost+"");
}
/**
* Closes the popup and removes its GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(buyHouseContainer);
app.getGuiNode().detachChild(backgroundContainer);
super.close();
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@@ -1,149 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.math.ColorRGBA;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.client.gui.TradeMenu;
import pp.monopoly.message.client.TradeResponse;
import pp.monopoly.model.TradeHandler;
import pp.monopoly.model.fields.PropertyField;
import pp.monopoly.notification.Sound;
/**
* ConfirmTrade is a popup which appears when a trade is proposed to this certain player.
*/
public class ConfirmTrade extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the "Confirm Trade" popup UI. */
private final Container confirmTradeContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer;
/** The trade handler managing the details of the trade proposal. */
private TradeHandler tradeHandler;
/**
* Constructs the "Confirm Trade" popup.
*
* @param app the Monopoly application instance
*/
public ConfirmTrade(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
tradeHandler = app.getGameLogic().getTradeHandler();
// Create the background container
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
attachChild(backgroundContainer);
confirmTradeContainer = new Container();
float padding = 10;
backgroundContainer.setPreferredSize(confirmTradeContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label title = confirmTradeContainer.addChild(new Label( "Handel", new ElementId("warning-title")));
title.setFontSize(48);
title.setColor(ColorRGBA.Black);
StringBuilder offeredProperties = new StringBuilder();
for (PropertyField field : tradeHandler.getOfferedProperties()) {
offeredProperties.append(field.getName());
offeredProperties.append(", ");
}
StringBuilder requestedProperties = new StringBuilder();
for (PropertyField field : tradeHandler.getRequestedProperties()) {
requestedProperties.append(field.getName());
requestedProperties.append(", ");
}
// Text, der auf der Karte steht
Container propertyValuesContainer = confirmTradeContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label("„Spieler " + " " + tradeHandler.getSender().getName() + " " +" möchte:", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("- " + offeredProperties, new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("- " + tradeHandler.getOfferedAmount() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("- " + tradeHandler.getOfferedJailCards() +" Sonderkarten", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("gegen:", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("- "+ requestedProperties, new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("- "+ tradeHandler.getRequestedAmount() +" EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("- "+ tradeHandler.getRequestedJailCards() +" Sonderkarten", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("tauschen, willst du das Angebot annehmen?", new ElementId("label-Text")));
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Ablehnen-Button
Button declineButton = confirmTradeContainer.addChild(new Button("Ablehnen", new ElementId("button")));
declineButton.setFontSize(32);
declineButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.getGameLogic().send(new TradeResponse(false, tradeHandler));
close();
}));
// Verhandeln-Button
Button negotiateButton = confirmTradeContainer.addChild(new Button("Verhandeln", new ElementId("button")));
negotiateButton.setFontSize(32);
negotiateButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
TradeHandler t = new TradeHandler(app.getGameLogic().getPlayerHandler().getPlayerById(tradeHandler.getSender().getId()));
t.setReceiver(app.getGameLogic().getPlayerHandler().getPlayerById(tradeHandler.getReceiver().getId()));
new TradeMenu(app, t).open();
}));
// Confirm-Button
Button confirmButton = confirmTradeContainer.addChild(new Button("Bestätigen", new ElementId("button")));
confirmButton.setFontSize(32);
confirmButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.getGameLogic().send(new TradeResponse(true, tradeHandler));
close();
}));
// Zentriere das Menü
confirmTradeContainer.setLocalTranslation(
(app.getCamera().getWidth() - confirmTradeContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + confirmTradeContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Menü
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - confirmTradeContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + confirmTradeContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(confirmTradeContainer);
}
/**
* Closes the popup and removes its GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(confirmTradeContainer);
app.getGuiNode().detachChild(backgroundContainer);
super.close();
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@@ -1,133 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.notification.Sound;
/**
* EventCardPopup is a popup which appears when a certain EventCard is triggered by entering a EventCardField
*/
public class EventCardPopup extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the popup. */
private final Geometry overlayBackground;
/** Main container for the event card information. */
private final Container eventCardContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer;
/**
* Constructs the EventCardPopup to display the details of a triggered event card.
*
* @param app the Monopoly application instance
* @param description the description of the triggered event card
*/
public EventCardPopup(MonopolyApp app, String description) {
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);
eventCardContainer = new Container();
eventCardContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
eventCardContainer.setPreferredSize(new Vector3f(550,400,10));
float padding = 10;
backgroundContainer.setPreferredSize(eventCardContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label gateFieldTitle = eventCardContainer.addChild(new Label("Ereigniskarte", new ElementId("settings-title")));
gateFieldTitle.setFontSize(48);
gateFieldTitle.setColor(ColorRGBA.Black);
// Text, der auf der Karte steht
// Die Erklärungsfelder werden automatisch den descriptions der Message entnommen
Container propertyValuesContainer = eventCardContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label(description, new ElementId("label-Text")));
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
propertyValuesContainer.setPreferredSize(new Vector3f(300,200,0));
// Beenden-Button
Button quitButton = eventCardContainer.addChild(new Button("Jawohl", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(source -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Zentriere das Popup
eventCardContainer.setLocalTranslation(
(app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(eventCardContainer);
}
/**
* Creates a semi-transparent background overlay for the popup.
*
* @return the geometry of the overlay
*/
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));
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
overlay.setMaterial(material);
overlay.setLocalTranslation(0, 0, 0);
return overlay;
}
/**
* Closes the popup and removes its associated GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(eventCardContainer);
app.getGuiNode().detachChild(backgroundContainer);
app.getGuiNode().detachChild(overlayBackground);
super.close();
}
/**
* Handles the escape key action by closing the popup.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,150 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.message.client.BuyPropertyResponse;
import pp.monopoly.model.fields.FoodField;
import pp.monopoly.notification.Sound;
/**
* FoodFieldCard creates the popup for field information
*/
public class FoodFieldCard extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the popup. */
private final Geometry overlayBackground;
/** Main container for the food field information. */
private final Container foodFieldContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer;
/**
* Constructs a FoodFieldCard popup displaying details about a food field.
*
* @param app the Monopoly application instance
*/
public FoodFieldCard(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
int index = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
FoodField field = (FoodField) app.getGameLogic().getBoardManager().getFieldAtIndex(index);
overlayBackground = createOverlayBackground();
app.getGuiNode().attachChild(overlayBackground);
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
attachChild(backgroundContainer);
foodFieldContainer = new Container();
foodFieldContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f)));
float padding = 10;
backgroundContainer.setPreferredSize(foodFieldContainer.getPreferredSize().addLocal(padding, padding, 0));
Label settingsTitle = foodFieldContainer.addChild(new Label(field.getName(), new ElementId("label-Bold")));
settingsTitle.setFontSize(48);
settingsTitle.setColor(ColorRGBA.White);
Container propertyValuesContainer = foodFieldContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label("„Preis: " + field.getPrice() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))); // Leerzeile
propertyValuesContainer.addChild(new Label("„Wenn man Besitzer des", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label(field.getName()+" ist, so ist die", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Miete 40-mal so hoch, wie", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Augen auf den zwei Würfeln sind.", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))); // Leerzeile
propertyValuesContainer.addChild(new Label("„Wenn man Besitzer beider", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Restaurants ist, so ist die", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Miete 100-mal so hoch, wie", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Augen auf den zwei Würfeln sind.", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))); // Leerzeile
propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Beenden-Button
Button quitButton = foodFieldContainer.addChild(new Button("Beenden", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Kaufen-Button
Button buyButton = foodFieldContainer.addChild(new Button("Kaufen", new ElementId("button")));
buyButton.setFontSize(32);
buyButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.getGameLogic().send(new BuyPropertyResponse());
close();
}));
// Zentriere das Popup
foodFieldContainer.setLocalTranslation(
(app.getCamera().getWidth() - foodFieldContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + foodFieldContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - foodFieldContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + foodFieldContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(foodFieldContainer);
}
/**
* Creates a semi-transparent background overlay for the popup.
*
* @return the geometry of the overlay
*/
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;
}
/**
* Closes the popup and removes its associated GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(foodFieldContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@@ -1,125 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.math.ColorRGBA;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.message.client.BuyPropertyResponse;
import pp.monopoly.model.fields.GateField;
import pp.monopoly.notification.Sound;
/**
* GateFieldCard creates the popup for field information
*/
public class GateFieldCard extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the gate field information. */
private final Container gateFieldContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer;
/**
* Constructs a GateFieldCard popup displaying details about a gate field.
*
* @param app the Monopoly application instance
*/
public GateFieldCard(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
//Generate the corresponfing field
int index = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
GateField field = (GateField) app.getGameLogic().getBoardManager().getFieldAtIndex(index);
// Create the background container
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
attachChild(backgroundContainer);
// Hauptcontainer für die Gebäudekarte
gateFieldContainer = new Container();
gateFieldContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
float padding = 10; // Passt den backgroundContainer an die Größe des gateFieldContainers an
backgroundContainer.setPreferredSize(gateFieldContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel, bestehend aus dynamischen Namen anhand der ID und der Schriftfarbe/größe
Label gateFieldTitle = gateFieldContainer.addChild(new Label(field.getName(), new ElementId("label-Bold")));
gateFieldTitle.setFontSize(48);
gateFieldTitle.setColor(ColorRGBA.Black);
// Text, der auf der Karte steht
// Die Preise werden dynamisch dem BoardManager entnommen
Container propertyValuesContainer = gateFieldContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label("„Preis: " + field.getPrice() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Miete: 250 EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("2 Bahnhof besitzt: 500 EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("3 Bahnhof besitzt: 1000 EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("4 Bahnhof besitzt: 2000 EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Beenden-Button
Button quitButton = gateFieldContainer.addChild(new Button("Beenden", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Kaufen-Button
Button buyButton = gateFieldContainer.addChild(new Button("Kaufen", new ElementId("button")));
buyButton.setFontSize(32);
buyButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.getGameLogic().send(new BuyPropertyResponse());
close();
}));
// Zentriere das Popup
gateFieldContainer.setLocalTranslation(
(app.getCamera().getWidth() - gateFieldContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + gateFieldContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - gateFieldContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + gateFieldContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(gateFieldContainer);
}
/**
* Closes the popup and removes its associated GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(gateFieldContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
super.close();
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@@ -1,132 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.notification.Sound;
/**
* Gulag is a warning popup triggered when a player lands on the "Wache" field in the Monopoly game.
* <p>
* This popup informs the player that they are being sent to the Gulag and includes a confirmation button.
* </p>
*/
public class Gulag extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the popup. */
private final Geometry overlayBackground;
/** Main container for the Gulag warning message. */
private final Container gulagContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer;
/**
* Constructs the Gulag popup, displaying a warning when a player lands on the "Wache" field.
*
* @param app the Monopoly application instance
*/
public Gulag(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 die Warnung
gulagContainer = new Container();
gulagContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
gulagContainer.setPreferredSize(new Vector3f(550,250,10));
float padding = 10; // Passt den backgroundContainer an die Größe des bankruptContainers an
backgroundContainer.setPreferredSize(gulagContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label gateFieldTitle = gulagContainer.addChild(new Label("Du kommst ins Gulag!", new ElementId("warning-title")));
gateFieldTitle.setFontSize(48);
gateFieldTitle.setColor(ColorRGBA.Black);
// Beenden-Button
Button quitButton = gulagContainer.addChild(new Button("Jawohl Gulag", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(source -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Zentriere das Popup
gulagContainer.setLocalTranslation(
(app.getCamera().getWidth() - gulagContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + gulagContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - gulagContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + gulagContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(gulagContainer);
}
/**
* Creates a semi-transparent overlay background for the popup.
*
* @return the geometry of the overlay
*/
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;
}
/**
* Closes the popup and removes its associated GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(gulagContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
/**
* Handles the escape action to close the popup.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,136 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.math.ColorRGBA;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.message.client.NotificationAnswer;
import pp.monopoly.notification.Sound;
/**
* GulagInfo is a popup that provides options for a player who is stuck in the "Gulag" (jail) field.
* <p>
* This dialog offers multiple actions, including paying a bribe, using a "Get Out of Jail" card, or waiting.
* </p>
*/
public class GulagInfo extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the Gulag information dialog. */
private final Container gulagInfoContainer;
/** Background container providing a styled border around the dialog. */
private final Container backgroundContainer;
/**
* Constructs a GulagInfo popup that provides the player with options for getting out of the "Gulag" field.
*
* @param app the Monopoly application instance
* @param trys the number of failed attempts to roll doubles for release
*/
public GulagInfo(MonopolyApp app, int trys) {
super(app.getDialogManager());
this.app = app;
// Create the background container
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
attachChild(backgroundContainer);
// Hauptcontainer für das Bestätigungspopup
gulagInfoContainer = new Container();
float padding = 10; // Passt den backgroundContainer an die Größe des confirmTradeContainer an
backgroundContainer.setPreferredSize(gulagInfoContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label title = gulagInfoContainer.addChild(new Label( "Gulag", new ElementId("warning-title")));
title.setFontSize(48);
title.setColor(ColorRGBA.Black);
// Text, der auf der Karte steht
// Die Werte werden dem Handel entnommen (Iwas auch immer da dann ist)
Container propertyValuesContainer = gulagInfoContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label("„Du sitzt im Gefänginis und kommst nicht raus ...", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Es sei denn, du ...", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("- bestichst die Wache mit 500 EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("- löst eine Gulag-Frei-Karte ein", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("- wartest 3 Runden und bezahlst dann", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("- oder du würfelst einen Pasch", new ElementId("label-Text")));
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Bezahlen-Button
Button payButton = gulagInfoContainer.addChild(new Button("Bestechungsgeld bezahlen", new ElementId("button")));
payButton.setFontSize(32);
payButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.getGameLogic().send(new NotificationAnswer("PayJail"));
close();
}));
// Ereigniskarte-Button
Button eventCardButton = gulagInfoContainer.addChild(new Button("Ereigniskarte nutzen", new ElementId("button")));
eventCardButton.setFontSize(32);
eventCardButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.getGameLogic().send(new NotificationAnswer("UseJailCard"));
close();
}));
// Schließen-Button
Button closeButton = gulagInfoContainer.addChild(new Button("Schließen", new ElementId("button")));
closeButton.setFontSize(32);
closeButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Zentriere das Menü
gulagInfoContainer.setLocalTranslation(
(app.getCamera().getWidth() - gulagInfoContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + gulagInfoContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Menü
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - gulagInfoContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + gulagInfoContainer.getPreferredSize().y+ padding) / 2,
7
);
if(app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getNumJailCard() == 0) {
eventCardButton.setEnabled(false);
}
if(trys == 3) {
closeButton.setEnabled(false);
}
app.getGuiNode().attachChild(gulagInfoContainer);
}
/**
* Closes the GulagInfo popup and removes its GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(gulagInfoContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
super.close();
}
/**
* Handles the escape action to close the GulagInfo dialog.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@@ -1,145 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.IconComponent;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.notification.Sound;
/**
* LooserPopUp is a dialog that appears when a player loses the game.
* <p>
* This popup provides a message of encouragement and an option to quit the game.
* </p>
*/
public class LooserPopUp extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the dialog. */
private final Geometry overlayBackground;
/** Main container for the "Looser" dialog UI. */
private final Container LooserContainer;
/** Background container providing a styled border around the main dialog. */
private final Container backgroundContainer;
/**
* Constructs a new WinnerPopUp dialog.
*
* @param app The MonopolyApp instance.
*/
public LooserPopUp(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 die Warnung
LooserContainer = new Container();
LooserContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
LooserContainer.setPreferredSize(new Vector3f(355,600,10));
float padding = 10; // Passt den backgroundContainer an die Größe des bankruptContainers an
backgroundContainer.setPreferredSize(LooserContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label looserTitle = LooserContainer.addChild(new Label("Schade, du hast leider verloren, die nächste Runde wird besser!", new ElementId("header")));
looserTitle.setFontSize(25);
// looserTitle.setColor(ColorRGBA.Black);
// Create the image container
Container imageContainer = LooserContainer.addChild(new Container());
imageContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); // Dark gray background
imageContainer.setPreferredSize(LooserContainer.getPreferredSize().addLocal(-250, -200, 0)); // Adjust size for layout
// Add the image to the container
Label imageLabel = new Label(""); // Create a label for the image
IconComponent icon = new IconComponent("Pictures/MonopolyLooser.png"); // Load the image as an icon
icon.setIconScale(0.7f); // Scale the image appropriately
imageLabel.setIcon(icon);
imageContainer.addChild(imageLabel); // Add the image label to the container
//Beenden Button
Button quitButton = LooserContainer.addChild(new Button("Spiel Beenden", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.closeApp();
}));
// Zentriere das Popup
LooserContainer.setLocalTranslation(
(app.getCamera().getWidth() - LooserContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + LooserContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - LooserContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + LooserContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(LooserContainer);
}
/**
* Creates a semi-transparent background overlay for the popup.
*
* @return The overlay geometry.
*/
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(RenderState.BlendMode.Alpha);
overlay.setMaterial(material);
overlay.setLocalTranslation(0, 0, 0);
return overlay;
}
/**
* Closes the LooserPopUp dialog and removes its GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(LooserContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
/**
* Handles the escape action to close the dialog.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,141 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.notification.Sound;
/**
* NoMoneyWarning is a warning popup that appears when a player tries to perform
* an action they cannot afford due to insufficient funds, such as attempting
* to purchase a property or building.
* <p>
* This dialog notifies the player of their lack of funds and provides a single
* confirmation button to close the dialog.
* </p>
*/
public class NoMoneyWarning extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the warning popup. */
private final Geometry overlayBackground;
/** Main container for the NoMoneyWarning dialog UI. */
private final Container noMoneyWarningContainer;
/** Background container providing a styled border around the main dialog. */
private final Container backgroundContainer;
/**
* Constructs a new NoMoneyWarning dialog.
*
* @param app The MonopolyApp instance.
*/
public NoMoneyWarning(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 die Warnung
noMoneyWarningContainer = new Container();
noMoneyWarningContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
noMoneyWarningContainer.setPreferredSize(new Vector3f(550,250,10));
float padding = 10; // Passt den backgroundContainer an die Größe des bankruptContainers an
backgroundContainer.setPreferredSize(noMoneyWarningContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label gateFieldTitle = noMoneyWarningContainer.addChild(new Label("Na, schon wieder Pleite?", new ElementId("warning-title")));
gateFieldTitle.setFontSize(38);
gateFieldTitle.setColor(ColorRGBA.Black);
// Text, der im Popup steht
Container textContainer = noMoneyWarningContainer.addChild(new Container());
textContainer.addChild(new Label("Du hast nicht genug Geld, um dieses Gebäude zu kaufen", new ElementId("label-Text")));
textContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Passt den textContainer an die Größe des bankruptContainers an
textContainer.setPreferredSize(noMoneyWarningContainer.getPreferredSize().addLocal(-250,-200,0));
// Bestätigen-Button
Button quitButton = noMoneyWarningContainer.addChild(new Button("Bestätigen", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(source -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Zentriere das Popup
noMoneyWarningContainer.setLocalTranslation(
(app.getCamera().getWidth() - noMoneyWarningContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + noMoneyWarningContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - noMoneyWarningContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + noMoneyWarningContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(noMoneyWarningContainer);
}
/**
* Creates a semi-transparent overlay background for the dialog.
*
* @return The geometry representing the overlay background.
*/
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;
}
/**
* Closes the menu and removes the GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(noMoneyWarningContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
/**
* Handles the escape action to close the dialog.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,166 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.notification.Sound;
/**
* Rent is a popup that is triggered when a player lands on a property owned by another player
* and needs to pay rent in the Monopoly application.
* <p>
* Displays the rent amount and the recipient player's name, with an option to confirm the payment.
* </p>
*/
public class ReceivedRent extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the popup. */
private final Geometry overlayBackground;
/** Main container for the rent information and action. */
private final Container rentContainer;
/** Background container providing a border for the rent popup. */
private final Container backgroundContainer;
/**
* Constructs the Rent popup displaying the rent amount and recipient player.
*
* @param app the Monopoly application instance
* @param playerName the name of the player to whom the rent is owed
* @param amount the amount of rent to be paid
*/
public ReceivedRent(MonopolyApp app, String playerName, int amount) {
super(app.getDialogManager());
this.app = app;
// Create the overlay
overlayBackground = createOverlayBackground();
app.getGuiNode().attachChild(overlayBackground);
// Create and position the background container
backgroundContainer = createBackgroundContainer();
app.getGuiNode().attachChild(backgroundContainer);
// Create and position the rent container
rentContainer = createRentContainer(playerName, amount);
app.getGuiNode().attachChild(rentContainer);
centerContainers();
}
/**
* Creates a semi-transparent overlay background.
*
* @return the overlay geometry
*/
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)); // Semi-transparent black
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
overlay.setMaterial(material);
overlay.setLocalTranslation(0, 0, 0);
return overlay;
}
/**
* Creates the background container with styling.
*
* @return the styled background container
*/
private Container createBackgroundContainer() {
Container container = new Container();
container.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Light gray background
return container;
}
/**
* Creates the main rent container with title, text, and button.
*
* @param playerName the name of the player to whom the rent is owed
* @param amount the rent amount
* @return the rent container
*/
private Container createRentContainer(String playerName, int amount) {
Container container = new Container();
container.setBackground(new QuadBackgroundComponent(ColorRGBA.Gray));
container.setPreferredSize(new Vector3f(550, 250, 10));
// Title
Label title = container.addChild(new Label("Miete!", new ElementId("warning-title")));
title.setFontSize(48);
title.setColor(ColorRGBA.Black);
// Rent message
Container textContainer = container.addChild(new Container());
textContainer.addChild(new Label(playerName+ " zahlt dir " + amount + " EUR Miete",
new ElementId("label-Text")));
textContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
textContainer.setPreferredSize(container.getPreferredSize().addLocal(-250, -200, 0));
// Payment button
Button payButton = container.addChild(new Button("Bestätigen", new ElementId("button")));
payButton.setFontSize(32);
payButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
return container;
}
/**
* Centers the rent and background containers on the screen.
*/
private void centerContainers() {
float padding = 10;
// Center rent container
rentContainer.setLocalTranslation(
(app.getCamera().getWidth() - rentContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + rentContainer.getPreferredSize().y) / 2,
8
);
// Center background container with padding
backgroundContainer.setPreferredSize(rentContainer.getPreferredSize().addLocal(padding, padding, 0));
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - backgroundContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + backgroundContainer.getPreferredSize().y) / 2,
7
);
}
/**
* Closes the popup and removes GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(rentContainer);
app.getGuiNode().detachChild(backgroundContainer);
app.getGuiNode().detachChild(overlayBackground);
super.close();
}
/**
* Handles the escape action to close the dialog.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,145 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.message.server.TradeReply;
import pp.monopoly.notification.Sound;
/**
* RejectTrade is a popup that appears when a trade proposal is rejected by another player
* in the Monopoly application.
* <p>
* Displays a message indicating that the proposed trade has been declined, along with
* details of the involved players and provides an option to close the popup.
* </p>
*/
public class RejectTrade extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the popup. */
private final Geometry overlayBackground;
/** Main container for the rejection message content. */
private final Container noMoneyWarningContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer;
/**
* Constructs the RejectTrade popup displaying the rejection of a trade proposal.
*
* @param app the Monopoly application instance
* @param msg the trade reply message containing details about the rejected trade
*/
public RejectTrade(MonopolyApp app, TradeReply msg) {
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 die Warnung
noMoneyWarningContainer = new Container();
noMoneyWarningContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
noMoneyWarningContainer.setPreferredSize(new Vector3f(550,250,10));
float padding = 10; // Passt den backgroundContainer an die Größe des bankruptContainers an
backgroundContainer.setPreferredSize(noMoneyWarningContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label gateFieldTitle = noMoneyWarningContainer.addChild(new Label("Handel abgelehnt!", new ElementId("warning-title")));
gateFieldTitle.setFontSize(48);
gateFieldTitle.setColor(ColorRGBA.Black);
// Text, der im Popup steht
Container textContainer = noMoneyWarningContainer.addChild(new Container());
textContainer.addChild(new Label("Du hast Spieler"+ " " + msg.getTradeHandler().getReceiver().getName() + " " + "einen Handel vorgeschlagen", new ElementId("label-Text")));
textContainer.addChild(new Label("", new ElementId("label-Text")));
textContainer.addChild(new Label("Der Handel wurde abgelehnt", new ElementId("label-Text")));
textContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Passt den textContainer an die Größe des bankruptContainers an
textContainer.setPreferredSize(noMoneyWarningContainer.getPreferredSize().addLocal(-250,-200,0));
// Beenden-Button
Button quitButton = noMoneyWarningContainer.addChild(new Button("Bestätigen", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(source -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Zentriere das Popup
noMoneyWarningContainer.setLocalTranslation(
(app.getCamera().getWidth() - noMoneyWarningContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + noMoneyWarningContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - noMoneyWarningContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + noMoneyWarningContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(noMoneyWarningContainer);
}
/**
* Creates a semi-transparent background overlay for the popup.
*
* @return the geometry of the overlay
*/
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;
}
/**
* Closes the menu and removes the GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(noMoneyWarningContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
/**
* Handles the escape key action by closing the popup.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,167 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.notification.Sound;
/**
* Rent is a popup that is triggered when a player lands on a property owned by another player
* and needs to pay rent in the Monopoly application.
* <p>
* Displays the rent amount and the recipient player's name, with an option to confirm the payment.
* </p>
*/
public class Rent extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the popup. */
private final Geometry overlayBackground;
/** Main container for the rent information and action. */
private final Container rentContainer;
/** Background container providing a border for the rent popup. */
private final Container backgroundContainer;
/**
* Constructs the Rent popup displaying the rent amount and recipient player.
*
* @param app the Monopoly application instance
* @param playerName the name of the player to whom the rent is owed
* @param amount the amount of rent to be paid
*/
public Rent(MonopolyApp app, String playerName, int amount) {
super(app.getDialogManager());
this.app = app;
// Create the overlay
overlayBackground = createOverlayBackground();
app.getGuiNode().attachChild(overlayBackground);
// Create and position the background container
backgroundContainer = createBackgroundContainer();
app.getGuiNode().attachChild(backgroundContainer);
// Create and position the rent container
rentContainer = createRentContainer(playerName, amount);
app.getGuiNode().attachChild(rentContainer);
centerContainers();
}
/**
* Creates a semi-transparent overlay background.
*
* @return the overlay geometry
*/
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)); // Semi-transparent black
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
overlay.setMaterial(material);
overlay.setLocalTranslation(0, 0, 0);
return overlay;
}
/**
* Creates the background container with styling.
*
* @return the styled background container
*/
private Container createBackgroundContainer() {
Container container = new Container();
container.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Light gray background
return container;
}
/**
* Creates the main rent container with title, text, and button.
*
* @param playerName the name of the player to whom the rent is owed
* @param amount the rent amount
* @return the rent container
*/
private Container createRentContainer(String playerName, int amount) {
Container container = new Container();
container.setBackground(new QuadBackgroundComponent(ColorRGBA.Gray));
container.setPreferredSize(new Vector3f(550, 250, 10));
// Title
Label title = container.addChild(new Label("Miete!", new ElementId("warning-title")));
title.setFontSize(48);
title.setColor(ColorRGBA.Black);
// Rent message
Container textContainer = container.addChild(new Container());
textContainer.addChild(new Label("Du musst " + amount + " EUR Miete an " + playerName + " zahlen",
new ElementId("label-Text")));
textContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
textContainer.setPreferredSize(container.getPreferredSize().addLocal(-250, -200, 0));
// Payment button
Button payButton = container.addChild(new Button("Überweisen", new ElementId("button")));
payButton.setFontSize(32);
payButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
return container;
}
/**
* Centers the rent and background containers on the screen.
*/
private void centerContainers() {
float padding = 10;
// Center rent container
rentContainer.setLocalTranslation(
(app.getCamera().getWidth() - rentContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + rentContainer.getPreferredSize().y) / 2,
8
);
// Center background container with padding
backgroundContainer.setPreferredSize(rentContainer.getPreferredSize().addLocal(padding, padding, 0));
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - backgroundContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + backgroundContainer.getPreferredSize().y) / 2,
7
);
}
/**
* Closes the popup and removes GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(rentContainer);
app.getGuiNode().detachChild(backgroundContainer);
app.getGuiNode().detachChild(overlayBackground);
super.close();
}
/**
* Handles the escape action to close the dialog.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,252 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Selector;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.core.VersionedList;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.game.server.Player;
import pp.monopoly.message.client.AlterProperty;
import pp.monopoly.model.fields.BoardManager;
import pp.monopoly.model.fields.PropertyField;
import pp.monopoly.notification.Sound;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* RepayMortage is a popup that appears when a player selects the "Repay Mortgage" option
* in the Building Administration Menu.
* <p>
* This popup allows the player to select mortgaged properties and repay their mortgages,
* showing the total cost of the repayment. Includes options to confirm or cancel the repayment.
* </p>
*/
public class RepayMortage extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the mortgage repayment menu. */
private final Container repayMortageContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer;
/** Label to display selected properties. */
private Label selectionDisplay;
/** Reference to track property selections in the dropdown menu. */
private VersionedReference<Set<Integer>> selectionRef;
/** Dropdown menu for selecting mortgaged properties. */
private Selector<String> propertySelector;
/** Set of selected properties for mortgage repayment. */
private Set<String> selectedProperties = new HashSet<>();
/** Label displaying the total repayment cost. */
private Label cost = new Label("0", new ElementId("label-Text"));
/**
* Constructs the RepayMortage popup for handling mortgage repayment.
*
* @param app the Monopoly application instance
*/
public RepayMortage(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
// Create the background container
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
attachChild(backgroundContainer);
// Hauptcontainer für das Menü
repayMortageContainer = new Container();
repayMortageContainer.setPreferredSize(new Vector3f(800, 600, 0));
float padding = 10; // Passt den backgroundContainer an die Größe des sellhouseContainers an
backgroundContainer.setPreferredSize(repayMortageContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label title = repayMortageContainer.addChild(new Label( "Hypothek Abbezahlen", new ElementId("label-Bold")));
title.setFontSize(48);
title.setColor(ColorRGBA.Black);
//Unterteilund des sellHouseContainer in drei "Untercontainer"
Container upContainer = repayMortageContainer.addChild(new Container());
Container middleContainer = repayMortageContainer.addChild(new Container());
Container downContainer = repayMortageContainer.addChild(new Container());
// Text, der auf der Karte steht
upContainer.addChild(new Label("„Grundstück wählen:", new ElementId("label-Text")));
upContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
upContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
middleContainer.setPreferredSize(new Vector3f(100, 150, 0));
middleContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)));
middleContainer.addChild(createPropertyDropdown());
downContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
downContainer.addChild(new Label("Kosten:", new ElementId("label-Text")));// Leerzeile
downContainer.addChild(cost);
downContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Beenden-Button
Button cancelButton = repayMortageContainer.addChild(new Button("Abbrechen", new ElementId("button")));
cancelButton.setFontSize(32);
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Kaufen-Button
Button confirmButton = repayMortageContainer.addChild(new Button("Bestätigen", new ElementId("button")));
confirmButton.setFontSize(32);
confirmButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
AlterProperty msg = new AlterProperty("RepayMortage");
msg.setProperties(selectedProperties.stream().map(p -> app.getGameLogic().getBoardManager().getFieldByName(p).getId()).map(p -> (Integer) p).collect(Collectors.toSet()));
app.getGameLogic().send(msg);
close();
}));
// Zentriere das Popup
repayMortageContainer.setLocalTranslation(
(app.getCamera().getWidth() - repayMortageContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + repayMortageContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - repayMortageContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + repayMortageContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(repayMortageContainer);
}
/**
* Creates a dropdown menu for selecting a property.
*
* @return The dropdown container.
*/
private Container createPropertyDropdown() {
Container dropdownContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
dropdownContainer.setPreferredSize(new Vector3f(300, 200, 0));
dropdownContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)));
VersionedList<String> propertyOptions = new VersionedList<>();
List<PropertyField> playerProperties = getPlayerProperties();
// Populate the dropdown with property names
for (PropertyField property : playerProperties) {
propertyOptions.add(property.getName());
}
propertySelector = new Selector<>(propertyOptions, "glass");
dropdownContainer.addChild(propertySelector);
// Track selection changes
selectionRef = propertySelector.getSelectionModel().createReference();
// Initialize the selection display here
selectionDisplay = new Label(""); // Create TextField for displaying selections
selectionDisplay.setPreferredSize(new Vector3f(300, 30, 0));
selectionDisplay.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
dropdownContainer.addChild(selectionDisplay); // Add it to the dropdown container
// Set initial selection
if (!propertyOptions.isEmpty()) {
onDropdownSelectionChanged(propertySelector);
}
return dropdownContainer;
}
/**
* Retrieves the list of properties owned by the current player.
*
* @return List of PropertyField objects owned by the player.
*/
private List<PropertyField> getPlayerProperties() {
Player self = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId());
BoardManager boardManager = app.getGameLogic().getBoardManager();
return boardManager.getPropertyFields(self.getProperties()).stream()
.filter(property -> property instanceof PropertyField)
.map(property -> (PropertyField) property)
.filter(p -> p.isMortgaged())
.collect(Collectors.toList());
}
/**
* Updates the UI based on selection changes in the dropdown menu.
*
* @param delta the time elapsed since the last update
*/
@Override
public void update(float delta) {
if(selectionRef.update()) {
onDropdownSelectionChanged(propertySelector);
}
}
/**
* Handles changes in the property selection and updates the total repayment cost.
*
* @param playerProperties the dropdown menu for selecting properties
*/
private void onDropdownSelectionChanged(Selector<String> playerProperties) {
String selected = playerProperties.getSelectedItem();
app.getGameLogic().playSound(Sound.BUTTON);
if (selectedProperties.contains(selected)) {
selectedProperties.remove(selected);
} else {
selectedProperties.add(selected);
}
int cost = 0;
for (String s : selectedProperties) {
cost += ((PropertyField) app.getGameLogic().getBoardManager().getFieldByName(s)).getHypo();
}
String display = String.join(" | ", selectedProperties);
selectionDisplay.setText(display);
this.cost.setText(cost+"");
}
/**
* Closes the popup and removes its associated GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(repayMortageContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
super.close();
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@@ -1,49 +0,0 @@
package pp.monopoly.client.gui.popups;
/*
* $Id$
*
* Copyright (c) 2013-2013 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import com.jme3.scene.Spatial;
/**
* Notified when the current selection changes.
*
* @author Paul Speed
*/
public interface SelectionListener {
public void selectionChanged( Spatial selection, Spatial previous );
}

View File

@@ -1,251 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Selector;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.core.VersionedList;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.game.server.Player;
import pp.monopoly.message.client.AlterProperty;
import pp.monopoly.model.fields.BoardManager;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.notification.Sound;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* SellHouse is a popup that appears when a player clicks on the "Demolish" button
* in the BuildingAdminMenu.
* <p>
* This dialog allows players to select their properties and demolish houses or hotels
* for a partial refund of their purchase cost.
* </p>
*/
public class SellHouse extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the SellHouse dialog UI. */
private final Container sellhouseContainer;
/** Background container providing a styled border around the main dialog. */
private final Container backgroundContainer;
/** Label to display selected properties. */
private Label selectionDisplay;
/** Reference to track selection changes in the property selector. */
private VersionedReference<Set<Integer>> selectionRef;
/** Dropdown selector for displaying available properties. */
private Selector<String> propertySelector;
/** Set of properties selected for selling. */
private Set<String> selectedProperties = new HashSet<>();
/** Label to display the total refund amount for the selected properties. */
private Label cost = new Label("0", new ElementId("label-Text"));
/**
* Constructs a new SellHouse dialog.
*
* @param app The MonopolyApp instance.
*/
public SellHouse(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
// Create the background container
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
attachChild(backgroundContainer);
// Hauptcontainer für das Menü
sellhouseContainer = new Container();
sellhouseContainer.setPreferredSize(new Vector3f(800, 600, 0));
float padding = 10; // Passt den backgroundContainer an die Größe des sellhouseContainers an
backgroundContainer.setPreferredSize(sellhouseContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label title = sellhouseContainer.addChild(new Label( "Gebäude Abreißen", new ElementId("label-Bold")));
title.setFontSize(48);
title.setColor(ColorRGBA.Black);
//Unterteilung des sellHouseContainer in drei "Untercontainer"
Container upContainer = sellhouseContainer.addChild(new Container());
Container middleContainer = sellhouseContainer.addChild(new Container());
Container downContainer = sellhouseContainer.addChild(new Container());
// Text, der auf der Karte steht
upContainer.addChild(new Label("„Grundstück wählen:", new ElementId("label-Text")));
upContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
upContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
middleContainer.setPreferredSize(new Vector3f(100, 150, 0));
middleContainer.setBackground(new QuadBackgroundComponent(ColorRGBA.Orange));
middleContainer.addChild(createPropertyDropdown());
downContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
downContainer.addChild(new Label("Erstattung:", new ElementId("label-Text")));// Leerzeile
downContainer.addChild(cost); // Cost details
downContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Beenden-Button
Button cancelButton = sellhouseContainer.addChild(new Button("Abbrechen", new ElementId("button")));
cancelButton.setFontSize(32);
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Kaufen-Button
Button confirmButton = sellhouseContainer.addChild(new Button("Bestätigen", new ElementId("button")));
confirmButton.setFontSize(32);
confirmButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
AlterProperty msg = new AlterProperty("SellHouse");
msg.setProperties(selectedProperties.stream().map(p -> app.getGameLogic().getBoardManager().getFieldByName(p).getId()).map(p -> (Integer) p).collect(Collectors.toSet()));
app.getGameLogic().send(msg);
close();
}));
// Zentriere das Popup
sellhouseContainer.setLocalTranslation(
(app.getCamera().getWidth() - sellhouseContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + sellhouseContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - sellhouseContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + sellhouseContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(sellhouseContainer);
}
/**
* Creates a dropdown menu for selecting a property.
*
* @return The dropdown container.
*/
private Container createPropertyDropdown() {
Container dropdownContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
dropdownContainer.setPreferredSize(new Vector3f(300, 200, 0));
dropdownContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)));
VersionedList<String> propertyOptions = new VersionedList<>();
List<BuildingProperty> playerProperties = getPlayerProperties();
// Populate the dropdown with property names
for (BuildingProperty property : playerProperties) {
propertyOptions.add(property.getName());
}
propertySelector = new Selector<>(propertyOptions, "glass");
dropdownContainer.addChild(propertySelector);
// Track selection changes
selectionRef = propertySelector.getSelectionModel().createReference();
// Initialize the selection display here
selectionDisplay = new Label("");
selectionDisplay.setPreferredSize(new Vector3f(300, 30, 0));
selectionDisplay.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
dropdownContainer.addChild(selectionDisplay); // Add it to the dropdown container
// Set initial selection
if (!propertyOptions.isEmpty()) {
onDropdownSelectionChanged(propertySelector);
}
return dropdownContainer;
}
/**
* Retrieves the list of properties owned by the current player.
*
* @return List of BuildingProperty objects owned by the player.
*/
private List<BuildingProperty> getPlayerProperties() {
Player self = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId());
BoardManager boardManager = app.getGameLogic().getBoardManager();
return boardManager.getPropertyFields(self.getProperties()).stream()
.filter(property -> property instanceof BuildingProperty)
.map(property -> (BuildingProperty) property)
.filter(p -> app.getGameLogic().getBoardManager().canSell(p))
.collect(Collectors.toList());
}
/**
* Updates the dialog UI, tracking changes in the property selection.
*
* @param delta Time since the last update.
*/
@Override
public void update(float delta) {
if(selectionRef.update()) {
onDropdownSelectionChanged(propertySelector);
}
}
/**
* Handles changes to the property selection.
*
* @param playerProperties The dropdown menu's selection state.
*/
private void onDropdownSelectionChanged(Selector<String> playerProperties) {
String selected = playerProperties.getSelectedItem();
app.getGameLogic().playSound(Sound.BUTTON);
if (selectedProperties.contains(selected)) {
selectedProperties.remove(selected);
} else {
selectedProperties.add(selected);
}
int cost = 0;
for (String s : selectedProperties) {
cost += ((BuildingProperty) app.getGameLogic().getBoardManager().getFieldByName(s)).getHousePrice() / 2;
}
String display = String.join(" | ", selectedProperties);
selectionDisplay.setText(display);
this.cost.setText(cost+"");
}
/**
* Closes the dialog and removes GUI elements from the screen.
*/
@Override
public void close() {
app.getGuiNode().detachChild(sellhouseContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
super.close();
}
/**
* Handles the escape action to close the dialog.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@@ -1,261 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Selector;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.core.VersionedList;
import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.game.server.Player;
import pp.monopoly.message.client.AlterProperty;
import pp.monopoly.model.fields.BoardManager;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.model.fields.PropertyField;
import pp.monopoly.notification.Sound;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
/**
* TakeMortage is a popup that appears when a player clicks on the "Take Mortage" button
* in the BuildingAdminMenu.
* <p>
* This popup allows the player to select properties and take a mortgage on them
* to gain financial benefit during gameplay.
* </p>
*/
public class TakeMortage extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Main container for the TakeMortage dialog UI. */
private final Container takeMortageContainer;
/** Background container providing a styled border around the main dialog. */
private final Container backgroundContainer;
/** Label to display selected properties. */
private Label selectionDisplay;
/** Reference to track selection changes in the property selector. */
private VersionedReference<Set<Integer>> selectionRef;
/** Dropdown selector for displaying available properties. */
private Selector<String> propertySelector;
/** Set of properties selected for mortgaging. */
private Set<String> selectedProperties = new HashSet<>();
/** Label to display the total mortgage amount. */
private Label cost = new Label("0", new ElementId("label-Text"));
/**
* Constructs a new TakeMortage dialog.
*
* @param app The MonopolyApp instance.
*/
public TakeMortage(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
// Create the background container
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
attachChild(backgroundContainer);
// Hauptcontainer für das Menü
takeMortageContainer = new Container();
takeMortageContainer.setPreferredSize(new Vector3f(800, 600, 0));
float padding = 10; // Passt den backgroundContainer an die Größe des sellhouseContainers an
backgroundContainer.setPreferredSize(takeMortageContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label title = takeMortageContainer.addChild(new Label( "Hypothek aufnehmen", new ElementId("label-Bold")));
title.setFontSize(48);
title.setColor(ColorRGBA.Black);
//Unterteilund des sellHouseContainer in drei "Untercontainer"
Container upContainer = takeMortageContainer.addChild(new Container());
Container middleContainer = takeMortageContainer.addChild(new Container());
Container downContainer = takeMortageContainer.addChild(new Container());
// Text, der auf der Karte steht
upContainer.addChild(new Label("„Grundstück wählen:", new ElementId("label-Text")));
upContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
upContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
middleContainer.setPreferredSize(new Vector3f(100, 150, 0));
middleContainer.setBackground(new QuadBackgroundComponent(ColorRGBA.Orange));
middleContainer.addChild(createPropertyDropdown());
downContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
downContainer.addChild(new Label("Erstattung:", new ElementId("label-Text")));// Leerzeile
downContainer.addChild(cost);
downContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Beenden-Button
Button cancelButton = takeMortageContainer.addChild(new Button("Abbrechen", new ElementId("button")));
cancelButton.setFontSize(32);
cancelButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Kaufen-Button
Button confirmButton = takeMortageContainer.addChild(new Button("Bestätigen", new ElementId("button")));
confirmButton.setFontSize(32);
confirmButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
AlterProperty msg = new AlterProperty("TakeMortage");
msg.setProperties(selectedProperties.stream().map(p -> app.getGameLogic().getBoardManager().getFieldByName(p).getId()).map(p -> (Integer) p).collect(Collectors.toSet()));
app.getGameLogic().send(msg);
close();
}));
// Zentriere das Popup
takeMortageContainer.setLocalTranslation(
(app.getCamera().getWidth() - takeMortageContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + takeMortageContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - takeMortageContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + takeMortageContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(takeMortageContainer);
}
/**
* Creates a dropdown menu for selecting a property.
*
* @return The dropdown container with property options.
*/
private Container createPropertyDropdown() {
Container dropdownContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
dropdownContainer.setPreferredSize(new Vector3f(300, 200, 0));
dropdownContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.0f, 0.0f, 0.0f, 1.0f)));
VersionedList<String> propertyOptions = new VersionedList<>();
List<PropertyField> playerProperties = getPlayerProperties();
// Populate the dropdown with property names
for (PropertyField property : playerProperties) {
if(property instanceof BuildingProperty) {
if (((BuildingProperty)property).getHouses()!=0) {
break;
}
}
propertyOptions.add(property.getName());
}
propertySelector = new Selector<>(propertyOptions, "glass");
dropdownContainer.addChild(propertySelector);
// Track selection changes
selectionRef = propertySelector.getSelectionModel().createReference();
// Initialize the selection display here
selectionDisplay = new Label(""); // Create TextField for displaying selections
selectionDisplay.setPreferredSize(new Vector3f(300, 30, 0));
selectionDisplay.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
dropdownContainer.addChild(selectionDisplay); // Add it to the dropdown container
// Set initial selection
if (!propertyOptions.isEmpty()) {
onDropdownSelectionChanged(propertySelector);
}
return dropdownContainer;
}
/**
* Retrieves the list of properties owned by the current player.
* <p>
* Only properties that are not currently mortgaged are included.
* </p>
*
* @return List of eligible PropertyField objects owned by the player.
*/
private List<PropertyField> getPlayerProperties() {
Player self = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId());
BoardManager boardManager = app.getGameLogic().getBoardManager();
return boardManager.getPropertyFields(self.getProperties()).stream()
.filter(property -> property instanceof PropertyField)
.map(property -> (PropertyField) property)
.filter(p -> !p.isMortgaged())
.collect(Collectors.toList());
}
/**
* Updates the dialog UI, tracking changes in the property selection.
*
* @param delta Time since the last update.
*/
@Override
public void update(float delta) {
if(selectionRef.update()) {
onDropdownSelectionChanged(propertySelector);
}
}
/**
* Handles changes to the property selection.
*
* @param playerProperties The dropdown menu's selection state.
*/
private void onDropdownSelectionChanged(Selector<String> playerProperties) {
String selected = playerProperties.getSelectedItem();
app.getGameLogic().playSound(Sound.BUTTON);
if (selectedProperties.contains(selected)) {
selectedProperties.remove(selected);
} else {
selectedProperties.add(selected);
}
int cost = 0;
for (String s : selectedProperties) {
cost += ((PropertyField) app.getGameLogic().getBoardManager().getFieldByName(s)).getHypo();
}
String display = String.join(" | ", selectedProperties);
selectionDisplay.setText(display);
this.cost.setText(cost+"");
}
/**
* Closes the dialog and removes GUI elements from the screen.
*/
@Override
public void close() {
app.getGuiNode().detachChild(takeMortageContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
super.close();
}
/**
* Handles the escape action to close the dialog.
*/
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@@ -1,139 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.notification.Sound;
/**
* TimeOut is a warning popup triggered when the connection to the server is interrupted.
* <p>
* This popup informs the user about the loss of connection and provides an option to acknowledge it.
* </p>
*/
public class TimeOut extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the dialog. */
private final Geometry overlayBackground;
/** Main container for the TimeOut dialog UI. */
private final Container timeOutContainer;
/** Background container providing a styled border around the main dialog. */
private final Container backgroundContainer;
/**
* Constructs a new TimeOut dialog to notify the user about a connection loss.
*
* @param app The MonopolyApp instance.
*/
public TimeOut(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 die Warnung
timeOutContainer = new Container();
timeOutContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
timeOutContainer.setPreferredSize(new Vector3f(550,250,10));
float padding = 10; // Passt den backgroundContainer an die Größe des bankruptContainers an
backgroundContainer.setPreferredSize(timeOutContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label gateFieldTitle = timeOutContainer.addChild(new Label("Vorsicht !", new ElementId("warning-title")));
gateFieldTitle.setFontSize(48);
gateFieldTitle.setColor(ColorRGBA.Black);
// Text, der auf der Karte steht
Container textContainer = timeOutContainer.addChild(new Container());
textContainer.addChild(new Label("Du hast die Verbindung verloren und kannst nichts dagegen machen. Akzeptiere einfach, dass du verloren hast!", new ElementId("label-Text")));
textContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
textContainer.setPreferredSize(timeOutContainer.getPreferredSize().addLocal(-250,-200,0));
// Beenden-Button
Button quitButton = timeOutContainer.addChild(new Button("Bestätigen", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(source -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Zentriere das Popup
timeOutContainer.setLocalTranslation(
(app.getCamera().getWidth() - timeOutContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + timeOutContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - timeOutContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + timeOutContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(timeOutContainer);
}
/**
* Creates a semi-transparent background overlay for the popup.
*
* @return The overlay geometry.
*/
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;
}
/**
* Closes the TimeOut dialog and removes its GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(timeOutContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
/**
* Handles the escape action to close the dialog.
*/
@Override
public void escape() {
close();
}
}

View File

@@ -1,143 +0,0 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.IconComponent;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.notification.Sound;
/**
* WinnerPopUp is a dialog displayed when a player wins the Monopoly game.
* <p>
* This popup congratulates the player for their victory and provides an option to quit the game.
* </p>
*/
public class WinnerPopUp extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app;
/** Semi-transparent overlay background for the dialog. */
private final Geometry overlayBackground;
/** Main container for the "Winner" dialog UI. */
private final Container WinnerContainer;
/** Background container providing a styled border around the main dialog. */
private final Container backgroundContainer;
/**
* Constructs a new WinnerPopUp dialog.
*
* @param app The MonopolyApp instance.
*/
public WinnerPopUp(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 die Warnung
WinnerContainer = new Container();
WinnerContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
WinnerContainer.setPreferredSize(new Vector3f(440,600,10));
float padding = 10; // Passt den backgroundContainer an die Größe des bankruptContainers an
backgroundContainer.setPreferredSize(WinnerContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel
Label winnerTitle = WinnerContainer.addChild(new Label("Herzlichen Glückwunsch, du bist der neue Monopoly Champion!", new ElementId("header")));
winnerTitle.setFontSize(25);
// winnerTitle.setColor(ColorRGBA.Black);
// Create the image container
Container imageContainer = WinnerContainer.addChild(new Container());
imageContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); // Dark gray background
imageContainer.setPreferredSize(WinnerContainer.getPreferredSize().addLocal(-250, -200, 0)); // Adjust size for layout
// Add the image to the container
Label imageLabel = new Label(""); // Create a label for the image
IconComponent icon = new IconComponent("Pictures/MonopolyWinner.png"); // Load the image as an icon
icon.setIconScale(1); // Scale the image appropriately
imageLabel.setIcon(icon);
imageContainer.addChild(imageLabel); // Add the image label to the container
Button quitButton = WinnerContainer.addChild(new Button("Spiel Beenden", new ElementId("button")));
quitButton.setFontSize(32);
quitButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.closeApp();
}));
// Zentriere das Popup
WinnerContainer.setLocalTranslation(
(app.getCamera().getWidth() - WinnerContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + WinnerContainer.getPreferredSize().y) / 2,
8
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - WinnerContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + WinnerContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(WinnerContainer);
}
/**
* Creates a semi-transparent background overlay for the popup.
*
* @return The overlay geometry.
*/
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(RenderState.BlendMode.Alpha);
overlay.setMaterial(material);
overlay.setLocalTranslation(0, 0, 0);
return overlay;
}
/**
* Closes the WinnerPopUp dialog and removes its GUI elements.
*/
@Override
public void close() {
app.getGuiNode().detachChild(WinnerContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
/**
* Handles the escape action to close the dialog.
*/
@Override
public void escape() {
close();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Some files were not shown because too many files have changed in this diff Show More