refactor Worlds and client States

This commit is contained in:
Johannes Schmelz 2024-12-08 01:18:35 +00:00
parent 2e256f4ff5
commit e102d22f14
172 changed files with 11159 additions and 3133 deletions

View File

@ -64,6 +64,16 @@ selector("label-Bold", "pp") {
textHAlignment = HAlignment.Center textHAlignment = HAlignment.Center
textVAlignment = VAlignment.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 = 30
color = new ColorRGBA(ColorRGBA.White)
textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center
} }
selector("label-Text", "pp") { selector("label-Text", "pp") {
insets = new Insets3f(2, 2, 2, 2) insets = new Insets3f(2, 2, 2, 2)
@ -71,6 +81,19 @@ selector("label-Text", "pp") {
color = buttonEnabledColor 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") { selector("header", "pp") {
font = font("Interface/Fonts/Metropolis/Metropolis-Bold-42.fnt") font = font("Interface/Fonts/Metropolis/Metropolis-Bold-42.fnt")
insets = new Insets3f(2, 2, 2, 2) insets = new Insets3f(2, 2, 2, 2)
@ -257,6 +280,16 @@ selector("settings-title", "pp") {
textHAlignment = HAlignment.Center textHAlignment = HAlignment.Center
textVAlignment = VAlignment.Center textVAlignment = VAlignment.Center
} }
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") { selector("menu-button", "pp") {
fontSize = 40 // Set font size fontSize = 40 // Set font size
@ -292,3 +325,44 @@ selector("selector.item.label", "hover") {
background = new QuadBackgroundComponent(new ColorRGBA(0.2f, 0.6f, 1.0f, 0.9f)) // Highlighted background background = new QuadBackgroundComponent(new ColorRGBA(0.2f, 0.6f, 1.0f, 0.9f)) // Highlighted background
} }
def enabledCommandToolbar = new Command<Button>() {
void execute(Button source) {
if (source.isEnabled()){
source.setColor(ColorRGBA.White)
def orangeBackground = new QuadBackgroundComponent(color(1, 0.5, 0, 1)); // Orange background
source.setBackground(orangeBackground);
} else{
source.setColor(ColorRGBA.White)
def grayBackground = new QuadBackgroundComponent(ColorRGBA.Gray); // Gray background
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
}

View File

@ -1,5 +1,6 @@
plugins { plugins {
id 'buildlogic.jme-application-conventions' id 'buildlogic.jme-application-conventions'
id 'com.github.johnrengelman.shadow' version '8.1.1'
} }
description = 'Monopoly Client' description = 'Monopoly Client'
@ -26,3 +27,12 @@ application {
mainClass = 'pp.monopoly.client.MonopolyApp' mainClass = 'pp.monopoly.client.MonopolyApp'
applicationName = 'monopoly' applicationName = 'monopoly'
} }
shadowJar {
manifest {
attributes(
'Main-Class': 'pp.monopoly.client.MonopolyApp'
)
}
}

View File

@ -0,0 +1,217 @@
package pp.monopoly.client;
import com.jme3.app.Application;
import com.jme3.app.state.AppStateManager;
import com.jme3.asset.AssetManager;
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.Vector2f;
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.model.Board;
import pp.monopoly.client.gui.BobTheBuilder;
import pp.monopoly.client.gui.Toolbar;
import static pp.util.FloatMath.PI;
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;;
/**
* 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(), getApp().getRootNode());
System.out.println("LISTENER IS REGISTEDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
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(x, ABOVE_SEA_LEVEL, y));
camera.lookAt(new Vector3f(0,0, 0),
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/left.jpg"); //NON-NLS
final Texture east = assetManager.loadTexture("Pictures/Backdrop/right.jpg"); //NON-NLS
final Texture north = assetManager.loadTexture("Pictures/Backdrop/front.jpg"); //NON-NLS
final Texture south = assetManager.loadTexture("Pictures/Backdrop/back.jpg"); //NON-NLS
final Texture up = assetManager.loadTexture("Pictures/Backdrop/up.jpg"); //NON-NLS
final Texture down = assetManager.loadTexture("Pictures/Backdrop/down.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); //NONs-NLS
seaGeo.setLocalTranslation(new Vector3f(0, -0.1f, 0));
Quaternion rotation = new com.jme3.math.Quaternion();
rotation.fromAngleAxis(FastMath.HALF_PI, com.jme3.math.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(seaGeo);
}
}

View File

@ -0,0 +1,99 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.client;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import com.jme3.input.controls.ActionListener;
import com.jme3.scene.Node;
import pp.monopoly.client.gui.TestWorld;
/**
* Represents the state responsible for managing the battle interface within the Battleship game.
* This state handles the display and interaction of the battle map, including the opponent's map.
* It manages GUI components, input events, and the layout of the interface when this state is enabled.
*/
public class GameAppState extends MonopolyAppState {
private static final Logger LOGGER = System.getLogger(MonopolyAppState.class.getName());
/**
* A listener for handling click events in the battle interface.
* When a click is detected, it triggers the corresponding actions on the opponent's map.
*/
private final ActionListener clickListener = (name, isPressed, tpf) -> click(isPressed);
/**
* The root node for all GUI components in the battle state.
*/
private final Node battleNode = new Node("Game"); //NON-NLS
/**
* A view representing the opponent's map in the GUI.
*/
private TestWorld testWorld;
/**
* Enables the battle state by initializing, laying out, and adding GUI components.
* Attaches the components to the GUI node and registers input listeners.
*/
@Override
protected void enableState() {
LOGGER.log(Level.DEBUG, "Enabling game state");
battleNode.detachAllChildren();
initializeGuiComponents();
addGuiComponents();
getApp().getGuiNode().attachChild(battleNode);
}
/**
* Disables the battle state by removing GUI components and unregistering input listeners.
* Also handles cleanup of resources, such as the opponent's map view.
*/
@Override
protected void disableState() {
getApp().getGuiNode().detachChild(battleNode);
getApp().getInputManager().removeListener(clickListener);
}
/**
* Initializes the GUI components used in the battle state.
* Creates the opponent's map view and adds a grid overlay to it.
*/
private void initializeGuiComponents() {
// Initialisiere TestWorld mit Spielern
testWorld = new TestWorld(getApp());
testWorld.initializeScene();
}
/**
* Adds the initialized GUI components to the battle node.
* Currently, it attaches the opponent's map view to the node.
*/
private void addGuiComponents() {
}
/**
* Handles click events in the battle interface. If the event indicates a click (not a release),
* it translates the cursor position to the model's coordinate system and triggers the game logic
* for interacting with the opponent's map.
*
* @param isPressed whether the mouse button is currently pressed (true) or released (false)
*/
private void click(boolean isPressed) {
}
@Override
public void update(float tpf) {
super.update(tpf);
}
}

View File

@ -15,49 +15,34 @@ import com.jme3.audio.AudioData;
import com.jme3.audio.AudioNode; import com.jme3.audio.AudioNode;
/** /**
* Handles the background music beeing played. Is able to start and stop the music. Set the Volume of the Audio. * 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{ public class GameMusic extends AbstractAppState {
private static final Logger LOGGER = System.getLogger(GameMusic.class.getName()); private static final Logger LOGGER = System.getLogger(GameMusic.class.getName());
private static final Preferences PREFERENCES = getPreferences(GameMusic.class); private static final Preferences PREFERENCES = getPreferences(GameMusic.class);
private static final String ENABLED_PREF = "enabled"; //NON-NLS private static final String ENABLED_PREF = "enabled"; // NON-NLS
private static final String VOLUME_PREF = "volume"; //NON-NLS private static final String VOLUME_PREF = "volume"; // NON-NLS
private AudioNode music; private AudioNode mainMusic;
private AudioNode secondaryMusic;
private boolean isMainMusicPlaying = false;
private boolean isSecondaryMusicPlaying = false;
/** /**
* Checks if sound is enabled in the preferences. * Initializes the GameMusic app state and loads the background music.
*
* @return {@code true} if sound is enabled, {@code false} otherwise.
*/
public static boolean enabledInPreferences() {
return PREFERENCES.getBoolean(ENABLED_PREF, true);
}
/**
* Checks if sound is enabled in the preferences.
*
* @return float to which the volume is set
*/
public static float volumeInPreferences() {
return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
}
/**
* Initializes the sound effects for the game.
* Overrides {@link AbstractAppState#initialize(AppStateManager, Application)}
* *
* @param stateManager The state manager * @param stateManager The state manager
* @param app The application * @param app The application instance
*/ */
@Override @Override
public void initialize(AppStateManager stateManager, Application app) { public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app); super.initialize(stateManager, app);
music = loadSound(app, "Sound/background.ogg"); mainMusic = loadSound(app, "Sound/background.ogg");
secondaryMusic = loadSound(app, "Sound/ChooseYourCharakter.ogg");
setVolume(volumeInPreferences()); setVolume(volumeInPreferences());
music.setLooping(true); if (isEnabled()) {
if (isEnabled() && music != null) { playMainMusic();
music.play();
} }
} }
@ -71,7 +56,7 @@ public class GameMusic extends AbstractAppState{
private AudioNode loadSound(Application app, String name) { private AudioNode loadSound(Application app, String name) {
try { try {
final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer); final AudioNode sound = new AudioNode(app.getAssetManager(), name, AudioData.DataType.Buffer);
sound.setLooping(false); sound.setLooping(true);
sound.setPositional(false); sound.setPositional(false);
return sound; return sound;
} }
@ -82,41 +67,129 @@ public class GameMusic extends AbstractAppState{
} }
/** /**
* Sets the enabled state of this AppState. * Plays the main music.
* Overrides {@link com.jme3.app.state.AbstractAppState#setEnabled(boolean)} */
private 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.
* *
* @param enabled {@code true} to enable the AppState, {@code false} to disable it. * @param app The application instance
* @param secondaryMusicFile The file path of the secondary audio file
*/
private void playSecondaryMusic() {
if(!isEnabled()) {
return;
}
if (isSecondaryMusicPlaying) {
return; // Secondary music is already playing
}
stopMainMusic();
if (secondaryMusic != null) {
secondaryMusic.setVolume(volumeInPreferences());
secondaryMusic.play();
isSecondaryMusicPlaying = true;
}
}
/**
* Stops the secondary music.
*/
private 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.
*
* @param app The application instance
* @param secondaryMusicFile The file path of the secondary audio file
*/
public void toggleMusic() {
if(!isEnabled()) {
return;
}
if (isSecondaryMusicPlaying) {
stopSecondaryMusic();
playMainMusic();
} else {
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);
}
/**
* Enables or disables the sound system.
* When disabled, all music stops.
*
* @param enabled {@code true} to enable, {@code false} to disable
*/ */
@Override @Override
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
if (isEnabled() == enabled) return; if (isEnabled() == enabled) return;
if (music != null) { if (enabled) {
if (enabled) { playMainMusic();
music.play(); } else {
} else { stopMainMusic();
music.stop(); stopSecondaryMusic();
}
} }
super.setEnabled(enabled); super.setEnabled(enabled);
LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); //NON-NLS LOGGER.log(Level.INFO, "Sound enabled: {0}", enabled); // NON-NLS
PREFERENCES.putBoolean(ENABLED_PREF, enabled); PREFERENCES.putBoolean(ENABLED_PREF, enabled);
} }
/** /**
* Toggles the game sound on or off. * Retrieves the current sound volume preference.
*
* @return The volume level (0.0f to 1.0f)
*/ */
public void toggleSound() { public static float volumeInPreferences() {
setEnabled(!isEnabled()); return PREFERENCES.getFloat(VOLUME_PREF, 0.5f);
} }
/** /**
* Sets the volume of music * Checks if sound is enabled in the preferences.
* @param vol the volume to which the music should be set *
* @return {@code true} if sound is enabled, {@code false} otherwise
*/ */
public void setVolume(float vol){ public static boolean enabledInPreferences() {
music.setVolume(vol); return PREFERENCES.getBoolean(ENABLED_PREF, true);
PREFERENCES.putFloat(VOLUME_PREF, vol);
} }
} }

View File

@ -27,9 +27,25 @@ import static pp.util.PreferencesUtils.getPreferences;
* An application state that plays sounds. * An application state that plays sounds.
*/ */
public class GameSound extends AbstractAppState implements GameEventListener { 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()); 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); private static final Preferences PREFERENCES = getPreferences(GameSound.class);
/**
* Preference key for enabling or disabling GameSound.
*/
private static final String ENABLED_PREF = "enabled"; //NON-NLS 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 static final String VOLUME_PREF = "volume"; //NON-NLS
private AudioNode passStartSound; private AudioNode passStartSound;
@ -223,6 +239,11 @@ public class GameSound extends AbstractAppState implements GameEventListener {
PREFERENCES.putFloat(VOLUME_PREF, vol); PREFERENCES.putFloat(VOLUME_PREF, vol);
} }
/**
* Overrides {@link SoundEvent#notifyListener(GameEventListener)}
* @param event the received event
*/
@Override @Override
public void receivedEvent(SoundEvent event) { public void receivedEvent(SoundEvent event) {
switch (event.sound()) { switch (event.sound()) {

View File

@ -7,6 +7,15 @@
package pp.monopoly.client; 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 com.jme3.app.DebugKeysAppState; import com.jme3.app.DebugKeysAppState;
import com.jme3.app.SimpleApplication; import com.jme3.app.SimpleApplication;
import com.jme3.app.StatsAppState; import com.jme3.app.StatsAppState;
@ -19,41 +28,25 @@ import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger; import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.system.AppSettings; import com.jme3.system.AppSettings;
import com.simsilica.lemur.GuiGlobals; import com.simsilica.lemur.GuiGlobals;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.style.BaseStyles; import com.simsilica.lemur.style.BaseStyles;
import pp.monopoly.game.client.MonopolyClient;
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.SettingsMenu;
import pp.monopoly.client.gui.StartMenu; import pp.monopoly.client.gui.StartMenu;
import pp.monopoly.client.gui.TestWorld;
import pp.monopoly.client.gui.popups.BuildingPropertyCard;
import pp.monopoly.client.gui.popups.BuyCard;
import pp.monopoly.client.gui.popups.EventCard;
import pp.monopoly.client.gui.popups.FoodFieldCard;
import pp.monopoly.client.gui.popups.GateFieldCard;
import pp.monopoly.game.client.ClientGameLogic; import pp.monopoly.game.client.ClientGameLogic;
import pp.monopoly.game.client.MonopolyClient;
import pp.monopoly.game.client.ServerConnection; import pp.monopoly.game.client.ServerConnection;
import pp.monopoly.message.client.NotificationAnswer;
import pp.monopoly.notification.ClientStateEvent; import pp.monopoly.notification.ClientStateEvent;
import pp.monopoly.notification.GameEventListener; import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.InfoTextEvent; import pp.monopoly.notification.InfoTextEvent;
import pp.monopoly.notification.Sound; import pp.monopoly.notification.Sound;
import pp.dialog.Dialog;
import pp.dialog.DialogBuilder;
import pp.dialog.DialogManager;
import pp.graphics.Draw;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.System.Logger;
import java.lang.System.Logger.Level;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.LogManager;
import static pp.monopoly.Resources.lookup;
/** /**
* The main class for the Battleship client application. * The main class for the Monopoly client application.
* It manages the initialization, input setup, GUI setup, and game states for the client. * It manages the initialization, input setup, GUI setup, and game states for the client.
*/ */
public class MonopolyApp extends SimpleApplication implements MonopolyClient, GameEventListener { public class MonopolyApp extends SimpleApplication implements MonopolyClient, GameEventListener {
@ -128,16 +121,10 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga
*/ */
private final ActionListener escapeListener = (name, isPressed, tpf) -> escape(isPressed); private final ActionListener escapeListener = (name, isPressed, tpf) -> escape(isPressed);
//TODO temp for testing /**
private EventCard eventCard; * Listener for handeling Demo Mode (Minas mode)
private BuildingPropertyCard buildingProperty; */
private FoodFieldCard foodField; private final ActionListener f8Listener = (name, isPressed, tpf) -> handleF8(isPressed);
private GateFieldCard gateField;
private BuyCard buyCard;
private boolean isBuyCardPopupOpen = false;
private final ActionListener BListener = (name, isPressed, tpf) -> handleB(isPressed);
private final ActionListener TListener = (name, isPressed, tpf) -> handleT(isPressed);
private TestWorld testWorld;
static { static {
// Configure logging // Configure logging
@ -152,7 +139,7 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga
} }
/** /**
* Starts the Battleship application. * Starts the Monopoly application.
* *
* @param args Command-line arguments for launching the application. * @param args Command-line arguments for launching the application.
*/ */
@ -164,7 +151,7 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga
* Constructs a new {@code MonopolyApp} instance. * Constructs a new {@code MonopolyApp} instance.
* Initializes the configuration, server connection, and game logic listeners. * Initializes the configuration, server connection, and game logic listeners.
*/ */
private MonopolyApp() { public MonopolyApp() {
config = new MonopolyAppConfig(); config = new MonopolyAppConfig();
config.readFromIfExists(CONFIG_FILE); config.readFromIfExists(CONFIG_FILE);
serverConnection = makeServerConnection(); serverConnection = makeServerConnection();
@ -219,9 +206,9 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga
} }
/** /**
* Returns the current configuration settings for the Battleship client. * Returns the current configuration settings for the Monopoly client.
* *
* @return The {@link BattleshipClientConfig} instance. * @return The {@link pp.monopoly.game.client.MonopolyClientConfig} instance.
*/ */
@Override @Override
public MonopolyAppConfig getConfig() { public MonopolyAppConfig getConfig() {
@ -266,45 +253,23 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga
inputManager.setCursorVisible(false); inputManager.setCursorVisible(false);
inputManager.addMapping(ESC, new KeyTrigger(KeyInput.KEY_ESCAPE)); inputManager.addMapping(ESC, new KeyTrigger(KeyInput.KEY_ESCAPE));
inputManager.addMapping(CLICK, new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); 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.addListener(escapeListener, ESC);
//TODO tmp for testing
inputManager.addMapping("B", new KeyTrigger(KeyInput.KEY_B));
inputManager.addListener(BListener, "B");
inputManager.addMapping("T", new KeyTrigger(KeyInput.KEY_T));
inputManager.addListener(TListener, "T");
} }
//logik zum wechselnden erscheinen und verschwinden beim drücken von B //TODO süäter entfernen /**
private void handleB(boolean isPressed) { * 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) {
if (isPressed) { if (isPressed) {
Dialog tmp = new BuyCard(this); LOGGER.log(Level.INFO, "F detected."); // Debug logging
if (eventCard != null && isBuyCardPopupOpen) { getGameLogic().send(new NotificationAnswer("hack"));
// Schließe das SettingsMenu
System.out.println("Schließe BuyCardPopup...");
eventCard.close();
eventCard = null;
tmp.open();
} else {
// Öffne das SettingsMenu
System.out.println("Öffne BuyCardPopup...");
eventCard = new EventCard(this);
eventCard.open();
dialogManager.close(tmp);
}
} }
} }
//logik zum wechselnden erscheinen und verschwinden beim drücken von B //TODO süäter entfernen
private void handleT(boolean isPressed) {
if (isPressed) {
testWorld = new TestWorld(this);
testWorld.initializeScene();
}
}
/** /**
* Initializes and attaches the necessary application states for the game. * Initializes and attaches the necessary application states for the game.
*/ */
@ -320,6 +285,7 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga
attachGameSound(); attachGameSound();
attachGameMusic(); attachGameMusic();
stateManager.attach(new BoardAppState());
} }
/** /**
@ -352,11 +318,7 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga
super.simpleUpdate(tpf); super.simpleUpdate(tpf);
dialogManager.update(tpf); dialogManager.update(tpf);
logic.update(tpf); logic.update(tpf);
stateManager.update(tpf);
//TODO testing replace later
if (testWorld != null) {
testWorld.update(tpf);
}
} }
/** /**
@ -443,6 +405,7 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga
*/ */
@Override @Override
public void receivedEvent(ClientStateEvent event) { public void receivedEvent(ClientStateEvent event) {
stateManager.getState(BoardAppState.class).setEnabled(logic.isTurn());
} }
/** /**
@ -500,7 +463,26 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga
.open(); .open();
} }
/**
* Disconnects the current server connection.
*
* This method delegates the disconnection operation to the `disconnect` method of the
* `serverConnection` object.
*
*/
public void disconnect() { public void disconnect() {
serverConnection.disconnect(); serverConnection.disconnect();
} }
/**
* 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;
}
} }

View File

@ -25,7 +25,7 @@ import java.lang.System.Logger.Level;
import static pp.monopoly.Resources.lookup; import static pp.monopoly.Resources.lookup;
/** /**
* Manages the network connection for the Battleship application. * Manages the network connection for the Monopoly application.
* Handles connecting to and disconnecting from the server, and sending messages. * Handles connecting to and disconnecting from the server, and sending messages.
*/ */
public class NetworkSupport implements MessageListener<Client>, ClientStateListener, ServerConnection { public class NetworkSupport implements MessageListener<Client>, ClientStateListener, ServerConnection {
@ -34,9 +34,9 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
private Client client; private Client client;
/** /**
* Constructs a NetworkSupport instance for the given Battleship application. * Constructs a NetworkSupport instance for the given Monopoly application.
* *
* @param app The Battleship application instance. * @param app The Monopoly application instance.
*/ */
public NetworkSupport(MonopolyApp app) { public NetworkSupport(MonopolyApp app) {
this.app = app; this.app = app;
@ -44,6 +44,7 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
/** /**
* Return the client connections Id * Return the client connections Id
*
* @return the client id * @return the client id
*/ */
public int getId() { public int getId() {
@ -52,9 +53,9 @@ public class NetworkSupport implements MessageListener<Client>, ClientStateListe
} }
/** /**
* Returns the Battleship application instance. * Returns the Monopoly application instance.
* *
* @return Battleship application instance * @return Monopoly application instance
*/ */
public MonopolyApp getApp() { public MonopolyApp getApp() {
return app; return app;

View File

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

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

@ -0,0 +1,159 @@
package pp.monopoly.client.gui;
import static com.jme3.material.Materials.LIGHTING;
import java.util.stream.Collectors;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import pp.monopoly.client.MonopolyApp;
import pp.monopoly.game.server.Player;
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);
node.attachChild(createFigure(figure));
// Setze die Position basierend auf der Feld-ID
node.setLocalTranslation(figure.getPos());
// Setze die Rotation basierend auf der Feld-ID
node.setLocalRotation(figure.getRot().toQuaternion());
// node.addControl(new FigureControl(figure));
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;
}
@Override
public void receivedEvent(UpdatePlayerView event) {
board.removePlayers();
//TODO transition animation
for (Player player : app.getGameLogic().getPlayerHandler().getPlayers()) {
board.add(player.getFigure());
}
for (Item item : board.getItems().stream().filter(p -> p instanceof Figure).collect(Collectors.toList())) {
add(item);
}
}
}

View File

@ -0,0 +1,221 @@
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,60 +1,66 @@
package pp.monopoly.client.gui; package pp.monopoly.client.gui;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera; import com.jme3.renderer.Camera;
/** /**
* Steuert die Kamerabewegung in der Szene. * Controls the movement of the camera within the scene.
*/ */
public class CameraController { public class CameraController {
private final Camera camera; private final Camera camera;
private final Vector3f center; // Fokuspunkt der Kamera private final float height = 25; // Height of the camera above the game board
private final float radius; // Radius der Kreisbewegung
private final float height; // Höhe der Kamera über dem Spielfeld
private final float speed; // Geschwindigkeit der Kamerabewegung
private float angle; // Aktueller Winkel in der Kreisbewegung
/** /**
* Konstruktor für den CameraController. * Constructor for the CameraController.
* *
* @param camera Die Kamera, die gesteuert werden soll * @param camera The camera to be controlled
* @param center Der Mittelpunkt der Kreisbewegung (Fokuspunkt)
* @param radius Der Radius der Kreisbewegung
* @param height Die Höhe der Kamera über dem Fokuspunkt
* @param speed Die Geschwindigkeit der Kamerabewegung
*/ */
public CameraController(Camera camera, Vector3f center, float radius, float height, float speed) { public CameraController(Camera camera) {
this.camera = camera; this.camera = camera;
this.center = center; setPosition(0);
this.radius = radius; camera.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
this.height = height;
this.speed = speed;
this.angle = 0; // Starte bei Winkel 0
} }
/** /**
* Aktualisiert die Kameraposition und -ausrichtung. * Updates the camera's position and orientation.
* *
* @param tpf Zeit pro Frame * @param tpf Time per frame
*/ */
public void update(float tpf) { public void update(float tpf) {
camera.lookAt(center, Vector3f.UNIT_Y); camera.lookAt(Vector3f.ZERO, Vector3f.UNIT_Y);
} }
/**
* 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) { public void setPosition(int fieldID) {
camera.setLocation(fieldIdToVector(fieldID)); camera.setLocation(fieldIdToVector(fieldID));
} }
/**
* 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) { public void setPosition(float x, float y) {
camera.setLocation(new Vector3f(x,height,y)); camera.setLocation(new Vector3f(x, height, y));
} }
/**
* 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) { private Vector3f fieldIdToVector(int fieldID) {
if (fieldID <= 10) return new Vector3f(30,height,0); if (fieldID <= 10) return new Vector3f(30, height, 0);
if (fieldID <= 20) return new Vector3f(0, height, 30); if (fieldID <= 20) return new Vector3f(0, height, 30);
if (fieldID <= 30) return new Vector3f(-30, height, 0); if (fieldID <= 30) return new Vector3f(-30, height, 0);
if (fieldID <= 40) return new Vector3f(0, height, -30); if (fieldID <= 40) return new Vector3f(0, height, -30);
else throw new IllegalArgumentException(); else throw new IllegalArgumentException();
} }
} }

View File

@ -0,0 +1,60 @@
//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,54 +1,162 @@
package pp.monopoly.client.gui; package pp.monopoly.client.gui;
import java.util.Set;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad; import com.jme3.scene.shape.Quad;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import com.simsilica.lemur.Axis; import com.simsilica.lemur.*;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.QuadBackgroundComponent; import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout; 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 com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog; import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp; 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 { public class ChoosePartner extends Dialog {
private final MonopolyApp app; private final MonopolyApp app;
private final Container menuContainer; 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 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) { public ChoosePartner(MonopolyApp app) {
super(app.getDialogManager()); super(app.getDialogManager());
this.app = app; this.app = app;
tradeHandler = new TradeHandler(app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()));
app.getGameLogic().send(new ViewAssetsRequest());
// Hintergrundbild laden und hinzufügen // Background Image
addBackgroundImage(); addBackgroundImage();
QuadBackgroundComponent translucentWhiteBackground = // Main container for the UI components
new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f)); mainContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X));
mainContainer.setPreferredSize(new Vector3f(1000, 600, 0));
mainContainer.setBackground(translucentWhiteBackground);
menuContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X)); // Add title with background
menuContainer.setPreferredSize(new Vector3f(1000, 600, 0)); // Fixed size of the container Label headerLabel = mainContainer.addChild(new Label("Wähle deinen Handelspartner:", new ElementId("label-Bold")));
menuContainer.setBackground(translucentWhiteBackground); headerLabel.setFontSize(40);
headerLabel.setBackground(new QuadBackgroundComponent(new ColorRGBA(1.0f, 1.0f, 1.0f, 0.5f)));
// Create a smaller horizontal container for the label, input field, and spacers // Dropdown for player selection
Container horizontalContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y))); mainContainer.addChild(createDropdown());
horizontalContainer.setPreferredSize(new Vector3f(600, 40, 0)); // Adjust container size
horizontalContainer.setBackground(null);
Label title = horizontalContainer.addChild(new Label("Wähle deinen Handelspartner:", new ElementId("label-Bold"))); // Add buttons
title.setFontSize(40); 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();
} }
/** /**
* Lädt das Hintergrundbild und fügt es als geometrische Ebene hinzu. * 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() { private void addBackgroundImage() {
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png"); Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png");
@ -57,8 +165,60 @@ public class ChoosePartner extends Dialog {
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
backgroundMaterial.setTexture("ColorMap", backgroundImage); backgroundMaterial.setTexture("ColorMap", backgroundImage);
background.setMaterial(backgroundMaterial); background.setMaterial(backgroundMaterial);
background.setLocalTranslation(0, 0, -1); // Hintergrundebene background.setLocalTranslation(0, 0, 3); // Position behind other GUI elements
app.getGuiNode().attachChild(background); 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

@ -66,7 +66,7 @@ public class CreateGameMenu extends Dialog {
final MonopolyApp app = network.getApp(); final MonopolyApp app = network.getApp();
int screenWidth = app.getContext().getSettings().getWidth(); int screenWidth = app.getContext().getSettings().getWidth();
int screenHeight = app.getContext().getSettings().getHeight(); int screenHeight = app.getContext().getSettings().getHeight();
// Set up the background image // Set up the background image

View File

@ -1,124 +1,87 @@
////////////////////////////////////////
// Programming project code
// UniBw M, 2022, 2023, 2024
// www.unibw.de/inf2
// (c) Mark Minas (mark.minas@unibw.de)
////////////////////////////////////////
package pp.monopoly.client.gui; 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.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box; import pp.monopoly.model.Item;
import pp.monopoly.model.Visitor;
import pp.monopoly.client.MonopolyApp; import pp.monopoly.notification.DiceRollEvent;
import pp.monopoly.game.server.PlayerColor; import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.ItemAddedEvent;
import pp.monopoly.notification.ItemRemovedEvent;
import pp.monopoly.notification.UpdatePlayerView;
import pp.monopoly.model.Board; import pp.monopoly.model.Board;
import pp.monopoly.model.Figure; import pp.monopoly.model.Figure;
import pp.monopoly.model.Rotation; import pp.view.ModelViewSynchronizer;
import static pp.util.FloatMath.HALF_PI;
import static pp.util.FloatMath.PI;
/** /**
* The {@code GameBoardSynchronizer} class is responsible for synchronizing the graphical * Abstract base class for synchronizing the visual representation of a {@link Board} with its model state.
* representation of the ships and shots on the sea map with the underlying data model. * This class handles the addition and removal of items from the board, ensuring that changes in the model
* It extends the {@link BoardSynchronizer} to provide specific synchronization * are accurately reflected in the view.
* logic for the sea map. * <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>
*/ */
class GameBoardSynchronizer extends BoardSynchronizer { abstract class GameBoardSynchronizer extends ModelViewSynchronizer<Item> implements Visitor<Spatial>, GameEventListener {
private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS // The board that this synchronizer is responsible for
private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md"; protected final Board board;
private static final String COLOR = "Color"; //NON-NLS
private static final String FIGURE = "figure"; //NON-NLS
private final MonopolyApp app;
private final ParticleEffectFactory particleFactory;
/** /**
* Constructs a {@code GameBoardSynchronizer} object with the specified application, root node, and ship map. * Constructs a new GameBoardSynchronizer.
* Initializes the synchronizer with the provided board and the root node for attaching view representations.
* *
* @param app the Battleship application * @param map the board to be synchronized
* @param root the root node to which graphical elements will be attached * @param root the root node to which the view representations of the board items are attached
* @param map the ship map containing the ships and shots
*/ */
public GameBoardSynchronizer(MonopolyApp app, Node root, Board board) { protected GameBoardSynchronizer(Board board, Node root) {
super(board, root); super(root);
this.app = app; this.board = board;
this.particleFactory = new ParticleEffectFactory(app);
addExisting();
} }
/** /**
* Visits a {@link Battleship} and creates a graphical representation of it. * Translates a model item into its corresponding visual representation.
* The representation is either a 3D model or a simple box depending on the * The specific visual representation is determined by the concrete implementation of the {@link Visitor} interface.
* type of battleship.
* *
* @param ship the battleship to be represented * @param item the item from the model to be translated
* @return the node containing the graphical representation of the battleship * @return the visual representation of the item as a {@link Spatial}
*/ */
public Spatial visit(Figure figure) { @Override
final Node node = new Node(FIGURE); protected Spatial translate(Item item) {
node.attachChild(createBox(figure)); return item.accept(this);
// compute the center of the ship in world coordinates
final float x = 1;
final float z = 1;
node.setLocalTranslation(x, 0f, z);
return node;
} }
/** /**
* Creates a simple box to represent a battleship that is not of the "King George V" type. * 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
* @param ship the battleship to be represented * are visually represented.
* @return the geometry representing the battleship as a box
*/ */
private Spatial createBox(Figure figure) { protected void addExisting() {
final Box box = new Box(0.5f * (figure.getMaxY() - figure.getMinY()) + 0.3f, board.getItems().forEach(this::add);
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 removed from the ship map.
* Removes the visual representation of the item from the view if it belongs to the synchronized ship map.
*
* @param event the event indicating that an item has been removed from the ship map
*/
@Override
public void receivedEvent(ItemRemovedEvent event) {
if (board == event.board())
delete(event.item());
} }
/** /**
* Creates a new {@link Material} with the specified color. * Handles the event when an item is added to the ship map.
* If the color includes transparency (i.e., alpha value less than 1), * Adds the visual representation of the new item to the view if it belongs to the synchronized ship map.
* 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 * @param event the event indicating that an item has been added to the ship map
* 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) { @Override
final Material material = new Material(app.getAssetManager(), UNSHADED); public void receivedEvent(ItemAddedEvent event) {
if (color.getAlpha() < 1f) if (board == event.board()){
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); add(event.item());
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,39 @@
package pp.monopoly.client.gui;
import com.jme3.texture.Texture;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.monopoly.client.MonopolyApp;
public class ImageButton extends Button {
private final String file;
private static MonopolyApp app;
public ImageButton( String s, String file, MonopolyApp app ) {
this(s, true, new ElementId(ELEMENT_ID), null, file, app);
}
public ImageButton( String s, String style, String file, MonopolyApp app ) {
this(s, true, new ElementId(ELEMENT_ID), style, file, app);
}
public ImageButton( String s, ElementId elementId, String file, MonopolyApp app ) {
this(s, true, elementId, null, file, app);
}
public ImageButton( String s, ElementId elementId, String style, String file, MonopolyApp app ) {
this(s, true, elementId, style, file, app);
}
protected ImageButton( String s, boolean applyStyles, ElementId elementId, String style, String file, MonopolyApp app ) {
super(s, false, elementId, style);
this.file = file;
ImageButton.app = app;
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/Buttons/"+file+".png");
setBackground(new QuadBackgroundComponent(backgroundImage));
}
}

View File

@ -1,7 +1,5 @@
package pp.monopoly.client.gui; package pp.monopoly.client.gui;
import com.jme3.app.Application;
import com.jme3.app.state.BaseAppState;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
@ -24,29 +22,66 @@ import com.simsilica.lemur.core.VersionedReference;
import com.simsilica.lemur.style.ElementId; import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog; import pp.dialog.Dialog;
import pp.monopoly.client.GameMusic;
import pp.monopoly.client.MonopolyApp; import pp.monopoly.client.MonopolyApp;
import pp.monopoly.game.server.Player;
import pp.monopoly.message.client.PlayerReady; import pp.monopoly.message.client.PlayerReady;
import pp.monopoly.notification.Sound; import pp.monopoly.notification.Sound;
import java.util.Set; 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 { public class LobbyMenu extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app; private final MonopolyApp app;
/** Main container for the lobby menu UI. */
private final Container menuContainer; private final Container menuContainer;
/** Background geometry for the menu. */
private Geometry background; private Geometry background;
/** Colored circle displayed between input fields and dropdown menus. */
private Geometry circle; private Geometry circle;
/** Container for the lower-left section of the menu. */
private Container lowerLeftMenu; private Container lowerLeftMenu;
/** Container for the lower-right section of the menu. */
private Container lowerRightMenu; private Container lowerRightMenu;
private TextField playerInputField = new TextField("Spieler 1"); /** Text field for entering the player's name. */
private TextField playerInputField;
/** Text field for entering the starting capital. */
private TextField startingCapital = new TextField("15000"); private TextField startingCapital = new TextField("15000");
/** Selected player figure. */
private String 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) { public LobbyMenu(MonopolyApp app) {
super(app.getDialogManager()); super(app.getDialogManager());
this.app = app; this.app = app;
GameMusic music = app.getStateManager().getState(GameMusic.class);
music.toggleMusic();
playerInputField = new TextField("Spieler "+(app.getId()+1));
// Hintergrundbild laden und hinzufügen // Hintergrundbild laden und hinzufügen
addBackgroundImage(); addBackgroundImage();
@ -88,6 +123,7 @@ public class LobbyMenu extends Dialog {
// Dropdowns and Labels // Dropdowns and Labels
Container dropdownContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y))); 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.setPreferredSize(new Vector3f(800, 200, 0));
dropdownContainer.setBackground(null); dropdownContainer.setBackground(null);
dropdownContainer.setInsets(new Insets3f(10, 0, 0, 0)); dropdownContainer.setInsets(new Insets3f(10, 0, 0, 0));
@ -111,19 +147,28 @@ public class LobbyMenu extends Dialog {
figureDropdownContainer.setBackground(null); figureDropdownContainer.setBackground(null);
VersionedList<String> figures = new VersionedList<>(); VersionedList<String> figures = new VersionedList<>();
figures.add("Laptop"); figures.add("Computer");
figures.add("Flugzeug"); figures.add("Flugzeug");
figures.add("Jägermeister"); figures.add("Jägermeister");
figures.add("Katze"); figures.add("Katze");
figures.add("OOP"); figures.add("OOP");
figures.add("Handyholster"); figures.add("Handyholster");
Selector<String> figureDropdown = new Selector<>(figures, "glass");
figureDropdown = new Selector<>(figures, "glass");
figureDropdown.setBackground(new QuadBackgroundComponent(ColorRGBA.DarkGray)); figureDropdown.setBackground(new QuadBackgroundComponent(ColorRGBA.DarkGray));
figureDropdown.setPreferredSize(new Vector3f(100, 20, 0)); figureDropdown.setPreferredSize(new Vector3f(100, 20, 0));
figureDropdownContainer.addChild(figureDropdown); figureDropdownContainer.addChild(figureDropdown);
Vector3f dimens = dropdownContainer.getPreferredSize();
Vector3f dimens2 = figureDropdown.getPopupContainer().getPreferredSize();
dimens2.setX( dimens.getX() );
figureDropdown.getPopupContainer().setPreferredSize(new Vector3f(200,200,5));
addSelectionActionListener(figureDropdown, this::onDropdownSelectionChanged); // 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))); Container buttonContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
buttonContainer.setPreferredSize(new Vector3f(100, 40, 0)); buttonContainer.setPreferredSize(new Vector3f(100, 40, 0));
@ -151,8 +196,10 @@ public class LobbyMenu extends Dialog {
readyButton.setFontSize(18); // Adjust font size readyButton.setFontSize(18); // Adjust font size
readyButton.setBackground(new QuadBackgroundComponent(ColorRGBA.Green)); // Add color to match the style readyButton.setBackground(new QuadBackgroundComponent(ColorRGBA.Green)); // Add color to match the style
readyButton.addClickCommands(s -> ifTopDialog(() -> { readyButton.addClickCommands(s -> ifTopDialog(() -> {
music.toggleMusic();
toggleReady(); toggleReady();
app.getGameLogic().playSound(Sound.BUTTON); app.getGameLogic().playSound(Sound.BUTTON);
readyButton.setBackground(new QuadBackgroundComponent(ColorRGBA.DarkGray));
})); }));
lowerRightMenu.addChild(readyButton); lowerRightMenu.addChild(readyButton);
@ -161,7 +208,7 @@ public class LobbyMenu extends Dialog {
app.getGuiNode().attachChild(lowerRightMenu); app.getGuiNode().attachChild(lowerRightMenu);
// Add a colored circle between the input field and the dropdown menu // Add a colored circle between the input field and the dropdown menu
circle = createCircle( ColorRGBA.Red); // 50 is the diameter, Red is the color circle = createCircle(); // 50 is the diameter, Red is the color
circle.setLocalTranslation(new Vector3f( circle.setLocalTranslation(new Vector3f(
(app.getCamera().getWidth()) / 2, // Center horizontally (app.getCamera().getWidth()) / 2, // Center horizontally
(app.getCamera().getHeight() / 2) - 90, // Adjust Y position (app.getCamera().getHeight() / 2) - 90, // Adjust Y position
@ -176,12 +223,13 @@ public class LobbyMenu extends Dialog {
1 // Höhere Z-Ebene für den Vordergrund 1 // Höhere Z-Ebene für den Vordergrund
); );
app.getGuiNode().attachChild(menuContainer); app.getGuiNode().attachChild(menuContainer);
} }
/** /**
* Lädt das Hintergrundbild und fügt es als geometrische Ebene hinzu. * Adds a background image to the lobby menu.
*/ */
private void addBackgroundImage() { private void addBackgroundImage() {
Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/lobby.png"); Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/lobby.png");
@ -195,102 +243,79 @@ public class LobbyMenu extends Dialog {
app.getGuiNode().attachChild(background); app.getGuiNode().attachChild(background);
} }
private Geometry createCircle(ColorRGBA color) { /**
* Creates a circle graphic element for the menu.
*
* @return the created circle geometry
*/
private Geometry createCircle() {
Sphere sphere = new Sphere(90,90,60.0f); Sphere sphere = new Sphere(90,90,60.0f);
Geometry circleGeometry = new Geometry("Circle", sphere); Geometry circleGeometry = new Geometry("Circle", sphere);
// Create a material with a solid color // Create a material with a solid color
Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
material.setColor("Color", color); // Set the desired color material.setColor("Color", Player.getColor(app.getId()).getColor()); // Set the desired color
circleGeometry.setMaterial(material); circleGeometry.setMaterial(material);
return circleGeometry; return circleGeometry;
} }
/** /**
* Schaltet den "Bereit"-Status um. * Toggles the player's ready state and sends the configuration to the server.
*/ */
private void toggleReady() { private void toggleReady() {
app.getGameLogic().send(new PlayerReady(true, playerInputField.getText(), figure, Integer.parseInt(startingCapital.getText()))); app.getGameLogic().send(new PlayerReady(true, playerInputField.getText(), figure, Integer.parseInt(startingCapital.getText())));
} }
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override @Override
public void escape() { public void escape() {
new SettingsMenu(app).open(); new SettingsMenu(app).open();
} }
/** @Override
* Adds a custom action listener to the Selector. public void update(float tpf) {
*/ if (selectionRef.update()) {
private void addSelectionActionListener(Selector<String> selector, SelectionActionListener<String> listener) { onDropdownSelectionChanged(figureDropdown);
VersionedReference<Set<Integer>> selectionRef = selector.getSelectionModel().createReference();
app.getStateManager().attach(new BaseAppState() {
@Override
public void update(float tpf) {
if (selectionRef.update()) {
String selected = selectionRef.get().toString();
System.out.println(selected);
listener.onSelectionChanged(selected);
}
}
@Override
protected void initialize(Application app) {
}
@Override
protected void cleanup(Application app) {
}
@Override
protected void onEnable() {
}
@Override
protected void onDisable() {
}
});
}
/**
* Callback for when the dropdown selection changes.
*/
private void onDropdownSelectionChanged(String selected) {
System.out.println("Selected: " + selected);
app.getGameLogic().playSound(Sound.BUTTON);
switch (selected) {
case "[0]":
figure = "Laptop";
break;
case "[1]":
figure = "Flugzeug";
break;
case "[2]":
figure = "Jägermeister";
break;
case "[3]":
figure = "Katze";
break;
case "[4]":
figure = "OOP";
break;
case "[5]":
figure = "Handyholster";
break;
default:
break;
} }
} }
/** /**
* Functional interface for a selection action listener. * Updates the selected figure based on the dropdown menu selection.
*
* @param selected 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;
}
System.out.println("FIGUR:::::"+figure);
}
/**
* Functional interface for handling selection changes in dropdown menus.
*
* @param <T> the type of the selection
*/ */
@FunctionalInterface @FunctionalInterface
private interface SelectionActionListener<T> { private interface SelectionActionListener<T> {
/**
* Triggered when the selection changes.
*
* @param selection the new selection
*/
void onSelectionChanged(T selection); void onSelectionChanged(T selection);
} }
} }

View File

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

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

@ -14,7 +14,6 @@ import com.simsilica.lemur.Checkbox;
import com.simsilica.lemur.Label; import com.simsilica.lemur.Label;
import com.simsilica.lemur.style.ElementId; import com.simsilica.lemur.style.ElementId;
import static pp.monopoly.Resources.lookup;
import pp.monopoly.client.GameMusic; import pp.monopoly.client.GameMusic;
import pp.monopoly.client.GameSound; import pp.monopoly.client.GameSound;
import pp.monopoly.client.MonopolyApp; import pp.monopoly.client.MonopolyApp;
@ -25,15 +24,30 @@ import pp.monopoly.notification.Sound;
import static pp.util.PreferencesUtils.getPreferences; import static pp.util.PreferencesUtils.getPreferences;
/** /**
* The Menu class represents the main menu in the Battleship game application. * The Menu class represents the main menu in the Monopoly game application.
* It extends the Dialog class and provides functionalities for loading, saving, * It extends the Dialog class and provides functionalities for Volume adjustment, Sound adjustment,
* returning to the game, and quitting the application. * returning to the game, and quitting the application.
*/ */
public class SettingsMenu extends Dialog { public class SettingsMenu extends Dialog {
/**
* Preferences instance for storing and retrieving settings specific to the SettingsMenu.
*/
private static final Preferences PREFERENCES = getPreferences(SettingsMenu.class); private static final Preferences PREFERENCES = getPreferences(SettingsMenu.class);
private static final String LAST_PATH = "last.file.path";
/**
* Reference to the main Monopoly application instance.
*/
private final MonopolyApp app; private final MonopolyApp app;
/**
* Slider control for adjusting the music volume.
*/
private final VolumeSlider musicSlider; private final VolumeSlider musicSlider;
/**
* Slider control for adjusting the sound effects volume.
*/
private final SoundSlider soundSlider; private final SoundSlider soundSlider;
/** /**

View File

@ -5,7 +5,14 @@ import pp.monopoly.client.GameSound;
public class SoundSlider extends Slider { public class SoundSlider extends Slider {
/**
* Manages sound effects for the game.
*/
private final pp.monopoly.client.GameSound sound; private final pp.monopoly.client.GameSound sound;
/**
* Volume level for the game sounds.
*/
private double vol; private double vol;
/** /**
@ -20,7 +27,7 @@ public class SoundSlider extends Slider {
} }
/** /**
* when triggered it updates the volume to the value set with the slider * When triggered it updates the volume to the value set with the slider
*/ */
public void update() { public void update() {
if (vol != getModel().getPercent()) { if (vol != getModel().getPercent()) {

View File

@ -18,7 +18,6 @@ import pp.monopoly.notification.Sound;
/** /**
* Constructs the startup menu dialog for the Monopoly application. * Constructs the startup menu dialog for the Monopoly application.
import pp.monopoly.client.gui.GameMenu;
*/ */
public class StartMenu extends Dialog { public class StartMenu extends Dialog {
private final MonopolyApp app; private final MonopolyApp app;
@ -42,25 +41,24 @@ public class StartMenu extends Dialog {
Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
backgroundMaterial.setTexture("ColorMap", backgroundImage); backgroundMaterial.setTexture("ColorMap", backgroundImage);
background.setMaterial(backgroundMaterial); background.setMaterial(backgroundMaterial);
background.setLocalTranslation(0, 0, -1); // Ensure it is behind other GUI elements background.setLocalTranslation(0, 0, -1);
app.getGuiNode().attachChild(background); app.getGuiNode().attachChild(background);
// Center container for title and play button
Container centerMenu = new Container(new SpringGridLayout(Axis.Y, Axis.X)); Container centerMenu = new Container(new SpringGridLayout(Axis.Y, Axis.X));
Button startButton = new Button("Spielen"); Button startButton = new Button("Spielen");
startButton.setPreferredSize(new Vector3f(190, 60, 0)); // Increase button size (width, height) startButton.setPreferredSize(new Vector3f(190, 60, 0));
startButton.setFontSize(40); // Set the font size for the button text startButton.setFontSize(40);
startButton.setTextHAlignment(HAlignment.Center); // Center the text horizontally startButton.setTextHAlignment(HAlignment.Center);
startButton.addClickCommands(s -> ifTopDialog(() -> { startButton.addClickCommands(s -> ifTopDialog(() -> {
this.close(); // Close the StartMenu dialog close();
app.connect(); // Perform the connection logic
app.getGameLogic().playSound(Sound.BUTTON); app.getGameLogic().playSound(Sound.BUTTON);
app.connect();
})); }));
centerMenu.addChild(startButton); centerMenu.addChild(startButton);
// Position the center container in the middle of the screen
centerMenu.setLocalTranslation(new Vector3f(screenWidth / 2f - centerMenu.getPreferredSize().x / 2f, centerMenu.setLocalTranslation(new Vector3f(screenWidth / 2f - centerMenu.getPreferredSize().x / 2f,
screenHeight / 2f - 280 + centerMenu.getPreferredSize().y / 2f, screenHeight / 2f - 280 + centerMenu.getPreferredSize().y / 2f,
0)); 0));
@ -74,38 +72,32 @@ public class StartMenu extends Dialog {
QuadBackgroundComponent logoBackground = new QuadBackgroundComponent(logoTexture); QuadBackgroundComponent logoBackground = new QuadBackgroundComponent(logoTexture);
logoContainer.setBackground(logoBackground); 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 logoWidth = 512;
float logoHeight = 128; // Adjust these values based on the logo dimensions float logoHeight = 128;
logoContainer.setPreferredSize(new Vector3f(logoWidth, logoHeight, 0)); logoContainer.setPreferredSize(new Vector3f(logoWidth, logoHeight, 0));
// Position the container at the center of the screen
logoContainer.setLocalTranslation(new Vector3f( logoContainer.setLocalTranslation(new Vector3f(
screenWidth / 2f - logoWidth / 2f, screenWidth / 2f - logoWidth / 2f,
screenHeight / 2f + 200, // Adjust this value for vertical position screenHeight / 2f + 200,
0 0
)); ));
// Attach the container to the GUI node
app.getGuiNode().attachChild(logoContainer); app.getGuiNode().attachChild(logoContainer);
// Load the Unibw logo as a texture
Texture unibwTexture = app.getAssetManager().loadTexture("Pictures/logo-unibw.png"); Texture unibwTexture = app.getAssetManager().loadTexture("Pictures/logo-unibw.png");
// Create a container for the Unibw logo
Container unibwContainer = new Container(); Container unibwContainer = new Container();
QuadBackgroundComponent unibwBackground = new QuadBackgroundComponent(unibwTexture); QuadBackgroundComponent unibwBackground = new QuadBackgroundComponent(unibwTexture);
unibwContainer.setBackground(unibwBackground); unibwContainer.setBackground(unibwBackground);
// Set the size of the container to fit the Unibw logo float unibwWidth = 512;
float unibwWidth = 512; // Adjust these values based on the logo dimensions float unibwHeight = 128;
float unibwHeight = 128; // Adjust these values based on the logo dimensions
unibwContainer.setPreferredSize(new Vector3f(unibwWidth, unibwHeight, 0)); unibwContainer.setPreferredSize(new Vector3f(unibwWidth, unibwHeight, 0));
// Position the container slightly below the Monopoly logo
unibwContainer.setLocalTranslation(new Vector3f( unibwContainer.setLocalTranslation(new Vector3f(
screenWidth / 2f - unibwWidth / 2f, screenWidth / 2f - unibwWidth / 2f,
screenHeight / 2f + 100, // Adjust this value for vertical position screenHeight / 2f + 100,
0 0
)); ));
@ -113,11 +105,17 @@ public class StartMenu extends Dialog {
app.getGuiNode().attachChild(unibwContainer); app.getGuiNode().attachChild(unibwContainer);
} }
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override @Override
public void escape() { public void escape() {
new SettingsMenu(app).open(); new SettingsMenu(app).open();
} }
/**
* Closes the startup menu and detaches all GUI elements.
*/
@Override @Override
public void close() { public void close() {
app.getGuiNode().detachAllChildren(); app.getGuiNode().detachAllChildren();

View File

@ -1,94 +1,565 @@
package pp.monopoly.client.gui; package pp.monopoly.client.gui;
import java.util.concurrent.Executors; import java.util.ArrayList;
import java.util.concurrent.ScheduledExecutorService; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.Timer;
import java.util.TimerTask;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material; import com.jme3.material.Material;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry; import com.jme3.renderer.RenderManager;
import com.jme3.scene.shape.Box; import com.jme3.renderer.ViewPort;
import com.jme3.scene.control.AbstractControl;
import com.jme3.texture.Texture; import com.jme3.texture.Texture;
import pp.monopoly.client.MonopolyApp; import pp.monopoly.client.MonopolyApp;
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.game.server.Player;
import pp.monopoly.game.server.PlayerHandler;
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;
import pp.monopoly.notification.UpdatePlayerView;
/** /**
* TestWorld zeigt eine einfache Szene mit einem texturierten Quadrat. * TestWorld zeigt eine einfache Szene mit Spielfeld und Spielfiguren.
* Die Kamera wird durch den CameraController gesteuert.
*/ */
public class TestWorld { public class TestWorld implements GameEventListener {
private final MonopolyApp app; private final MonopolyApp app;
private CameraController cameraController; // Steuert die Kamera private PlayerHandler playerHandler;
private CameraController cameraController;
private Toolbar toolbar;
private List<String> existingHouses = new ArrayList<>();
/** /**
* Konstruktor für TestWorld. * Konstruktor für die TestWorld.
* *
* @param app Die Hauptanwendung (MonopolyApp) * @param app Die Hauptanwendung
*/ */
public TestWorld(MonopolyApp app) { public TestWorld(MonopolyApp app) {
this.app = app; this.app = app;
this.playerHandler = app.getGameLogic().getPlayerHandler();
app.getGameLogic().addListener(this);
cameraController = new CameraController(app.getCamera());
} }
/** /**
* Initialisiert die Szene und startet die Kamerabewegung. * Initialisiert die Szene mit Spielfeld und Figuren.
*/ */
public void initializeScene() { public void initializeScene() {
app.getGuiNode().detachAllChildren(); // Entferne GUI // Entferne bestehende Inhalte
app.getRootNode().detachAllChildren(); // Entferne andere Szenenobjekte app.getGuiNode().detachAllChildren();
app.getRootNode().detachAllChildren();
setSkyColor(); // Setze den Himmel auf hellblau System.out.println("Szene initialisiert.");
createBoard(); // Erstelle das Spielfeld
// Erstelle den CameraController //Füge Inhalte ein
cameraController = new CameraController( setSkyColor();
app.getCamera(), // Die Kamera der App createBoard();
Vector3f.ZERO, // Fokus auf die Mitte des Spielfelds addLighting();
4, // Radius des Kreises createPlayerFigures();
15, // Höhe der Kamera toolbar = new Toolbar(app);
0 // Geschwindigkeit der Bewegung toolbar.open();
);
// Füge die Toolbar hinzu
new Toolbar(app).open();
cameraController.setPosition(0);
}
/**
* 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. * Setzt die Hintergrundfarbe der Szene auf hellblau.
*/ */
private void setSkyColor() { private void setSkyColor() {
app.getViewPort().setBackgroundColor(new ColorRGBA(0.5f, 0.7f, 1.0f, 1.0f)); // Hellblauer Himmel app.getViewPort().setBackgroundColor(new com.jme3.math.ColorRGBA(0.5f, 0.7f, 1.0f, 1.0f));
} }
/** /**
* Erstelle das Spielfeld. * Erstellt das Spielfeld und fügt es zur Szene hinzu.
*/ */
private void createBoard() { private void createBoard() {
// Erstelle ein Quadrat try {
Box box = new Box(10, 0.1f, 10); // Dünnes Quadrat für die Textur com.jme3.scene.shape.Box box = new com.jme3.scene.shape.Box(10, 0.1f, 10);
Geometry geom = new Geometry("Board", box); com.jme3.scene.Geometry geom = new com.jme3.scene.Geometry("Board", box);
// Setze das Material mit Textur Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); Texture texture = app.getAssetManager().loadTexture("Pictures/board2.png");
Texture texture = app.getAssetManager().loadTexture("Pictures/board2.png"); mat.setTexture("DiffuseMap", texture);
mat.setTexture("ColorMap", texture); geom.setMaterial(mat);
geom.setMaterial(mat);
app.getRootNode().attachChild(geom); geom.setLocalTranslation(0, -0.1f, 0);
com.jme3.math.Quaternion rotation = new com.jme3.math.Quaternion();
rotation.fromAngleAxis(FastMath.HALF_PI, com.jme3.math.Vector3f.UNIT_Y);
geom.setLocalRotation(rotation);
app.getRootNode().attachChild(geom);
} catch (Exception e) {
System.err.println("Fehler beim Erstellen des Spielfelds: " + e.getMessage());
}
}
private void addLighting() {
// Direktionales Licht
DirectionalLight sun = new DirectionalLight();
sun.setColor(ColorRGBA.White);
sun.setDirection(new Vector3f(-0.5f, -0.7f, -1.0f).normalizeLocal());
app.getRootNode().addLight(sun);
// Umgebungslicht
AmbientLight ambient = new AmbientLight();
ambient.setColor(new ColorRGBA(0.6f, 0.6f, 0.6f, 1.0f));
app.getRootNode().addLight(ambient);
}
private com.jme3.math.Quaternion calculateRotationForField(int fieldID) {
com.jme3.math.Quaternion rotation = new com.jme3.math.Quaternion();
// Berechne die Rotation basierend auf der Feld-ID
if (fieldID >= 0 && fieldID <= 9) {
// Untere Seite (0-9)
rotation.fromAngleAxis(0, Vector3f.UNIT_Y); // Richtung: nach oben
} else if (fieldID >= 10 && fieldID <= 19) {
// Rechte Seite (10-19)
rotation.fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y); // Richtung: nach links
} else if (fieldID >= 20 && fieldID <= 29) {
// Obere Seite (20-29)
rotation.fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y); // Richtung: nach unten
} else if (fieldID >= 30 && fieldID <= 39) {
// Linke Seite (30-39)
rotation.fromAngleAxis(3 * FastMath.HALF_PI, Vector3f.UNIT_Y); // Richtung: nach rechts
}
// Korrigiere die Richtung für die Quadranten 1019 und 3039 (gegenüberliegende Richtung)
if ((fieldID >= 10 && fieldID <= 19) || (fieldID >= 30 && fieldID <= 39)) {
com.jme3.math.Quaternion oppositeDirection = new com.jme3.math.Quaternion();
oppositeDirection.fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y); // 180° drehen
rotation = rotation.multLocal(oppositeDirection);
}
// Füge zusätzliche 90° nach links hinzu
com.jme3.math.Quaternion leftTurn = new com.jme3.math.Quaternion();
leftTurn.fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y); // 90° nach links
rotation = rotation.multLocal(leftTurn);
return rotation;
}
/**
* Erstellt die Spielfiguren basierend auf der bereits bekannten Spielerliste.
*/
private void createPlayerFigures() {
for (Player player : playerHandler.getPlayers()) {
try {
// Lade das Modell
com.jme3.scene.Spatial model = app.getAssetManager().loadModel(
"models/" + "Spielfiguren/" + player.getFigure().getType() + "/" + player.getFigure().getType() + ".j3o");
// Skaliere und positioniere das Modell
model.setLocalScale(0.5f);
Vector3f startPosition = calculateFieldPosition(player.getFieldID(), player.getId());
model.setLocalTranslation(startPosition);
// Setze die Rotation basierend auf der Feld-ID
model.setLocalRotation(calculateRotationForField(player.getFieldID()));
model.setName("PlayerFigure_" + player.getId());
// Füge das Modell zur Szene hinzu
app.getRootNode().attachChild(model);
} catch (Exception e) {
System.err.println("Fehler beim Laden des Modells für Spieler " + player.getId() + ": " + e.getMessage());
}
}
}
private Vector3f calculateFieldPosition(int fieldID, int playerIndex) {
float offset = 0.1f;
float baseX = 0.0f;
float baseZ = 0.0f;
switch (fieldID) {
case 0: baseX = -9.1f; baseZ = -9.1f; break;
case 1: baseX = -6.5f; baseZ = -9.1f; break;
case 2: baseX = -4.9f; baseZ = -9.1f; break;
case 3: baseX = -3.3f; baseZ = -9.1f; break;
case 4: baseX = -1.6f; baseZ = -9.1f; break;
case 5: baseX = 0.0f; baseZ = -9.1f; break;
case 6: baseX = 1.6f; baseZ = -9.1f; break;
case 7: baseX = 3.3f; baseZ = -9.1f; break;
case 8: baseX = 4.9f; baseZ = -9.1f; break;
case 9: baseX = 6.5f; baseZ = -9.1f; break;
case 10: baseX = 9.1f; baseZ = -9.1f; break;
case 11: baseX = 9.1f; baseZ = -6.5f; break;
case 12: baseX = 9.1f; baseZ = -4.9f; break;
case 13: baseX = 9.1f; baseZ = -3.3f; break;
case 14: baseX = 9.1f; baseZ = -1.6f; break;
case 15: baseX = 9.1f; baseZ = 0.0f; break;
case 16: baseX = 9.1f; baseZ = 1.6f; break;
case 17: baseX = 9.1f; baseZ = 3.3f; break;
case 18: baseX = 9.1f; baseZ = 4.9f; break;
case 19: baseX = 9.1f; baseZ = 6.5f; break;
case 20: baseX = 9.1f; baseZ = 9.1f; break;
case 21: baseX = 6.5f; baseZ = 9.1f; break;
case 22: baseX = 4.9f; baseZ = 9.1f; break;
case 23: baseX = 3.3f; baseZ = 9.1f; break;
case 24: baseX = 1.6f; baseZ = 9.1f; break;
case 25: baseX = 0.0f; baseZ = 9.1f; break;
case 26: baseX = -1.6f; baseZ = 9.1f; break;
case 27: baseX = -3.3f; baseZ = 9.1f; break;
case 28: baseX = -4.9f; baseZ = 9.1f; break;
case 29: baseX = -6.5f; baseZ = 9.1f; break;
case 30: baseX = -9.1f; baseZ = 9.1f; break;
case 31: baseX = -9.1f; baseZ = 6.5f; break;
case 32: baseX = -9.1f; baseZ = 4.9f; break;
case 33: baseX = -9.1f; baseZ = 3.3f; break;
case 34: baseX = -9.1f; baseZ = 1.6f; break;
case 35: baseX = -9.1f; baseZ = 0.0f; break;
case 36: baseX = -9.1f; baseZ = -1.6f; break;
case 37: baseX = -9.1f; baseZ = -3.3f; break;
case 38: baseX = -9.1f; baseZ = -4.9f; break;
case 39: baseX = -9.1f; baseZ = -6.5f; break;
default: throw new IllegalArgumentException("Ungültige Feld-ID: " + fieldID);
}
float xOffset = (playerIndex % 2) * offset;
float zOffset = (playerIndex / 2) * offset;
return new Vector3f(baseX + xOffset, 0, baseZ + zOffset);
}
private void movePlayerFigure(Player player) {
int playerIndexOnField = calculatePlayerIndexOnField(player.getFieldID(), player.getId());
String figureName = "PlayerFigure_" + player.getId();
com.jme3.scene.Spatial figure = app.getRootNode().getChild(figureName);
if (figure != null) {
// Füge einen Delay hinzu (z.B. 3 Sekunden)
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
app.enqueue(() -> {
// Setze die Position
Vector3f targetPosition = calculateFieldPosition(player.getFieldID(), player.getId());
figure.setLocalTranslation(targetPosition);
// Aktualisiere die Rotation basierend auf der Feld-ID
figure.setLocalRotation(calculateRotationForField(player.getFieldID()));
});
}
}, 3000); // 3000 Millisekunden Delay
} else {
System.err.println("Figur für Spieler " + player.getId() + " nicht gefunden.");
}
}
private int getFieldIDFromPosition(Vector3f position) {
for (int fieldID = 0; fieldID < 40; fieldID++) {
Vector3f fieldPosition = calculateFieldPosition(fieldID, 0);
if (fieldPosition.distance(position) < 0.5f) { // Toleranz für Positionserkennung
return fieldID;
}
}
throw new IllegalArgumentException("Position entspricht keinem gültigen Feld: " + position);
}
/**
* Berechnet den Eckpunkt basierend auf Start- und Zielposition.
*
* @param startPosition Die Startposition der Figur.
* @param targetPosition Die Zielposition der Figur.
* @return Die Position der Ecke, die passiert werden muss.
*/
private Vector3f calculateCornerPosition(Vector3f startPosition, Vector3f targetPosition) {
// Ziel: Immer entlang der Spielfeldkante navigieren
float deltaX = targetPosition.x - startPosition.x;
float deltaZ = targetPosition.z - startPosition.z;
// Überprüfen, ob Bewegung entlang X oder Z-Koordinate zuerst erfolgen soll
if (deltaX != 0 && deltaZ != 0) {
if (Math.abs(deltaX) > Math.abs(deltaZ)) {
// Bewegung entlang X zuerst
return new Vector3f(targetPosition.x, 0, startPosition.z);
} else {
// Bewegung entlang Z zuerst
return new Vector3f(startPosition.x, 0, targetPosition.z);
}
} else {
// Bewegung ist bereits entlang einer Achse (keine Ecke erforderlich)
return targetPosition;
}
}
private List<Vector3f> calculatePath(int startFieldID, int targetFieldID, int playerIndex) {
List<Vector3f> pathPoints = new ArrayList<>();
// Bewegung im Uhrzeigersinn
if (startFieldID < targetFieldID) {
for (int i = startFieldID; i <= targetFieldID; i++) {
// Füge Ecken hinzu, falls sie überschritten werden
if (i == 10 || i == 20 || i == 30 || i == 0) {
pathPoints.add(calculateFieldPosition(i, playerIndex));
}
}
} else {
// Bewegung über das Ende des Spielfelds hinaus (z.B. von 39 zu 5)
for (int i = startFieldID; i < 40; i++) {
if (i == 10 || i == 20 || i == 30 || i == 0) {
pathPoints.add(calculateFieldPosition(i, playerIndex));
}
}
for (int i = 0; i <= targetFieldID; i++) {
if (i == 10 || i == 20 || i == 30 || i == 0) {
pathPoints.add(calculateFieldPosition(i, playerIndex));
}
}
}
// Füge das Ziel hinzu
pathPoints.add(calculateFieldPosition(targetFieldID, playerIndex));
return pathPoints;
}
private int calculatePlayerIndexOnField(int fieldID, int playerID) {
List<Player> playersOnField = playerHandler.getPlayers().stream()
.filter(p -> p.getFieldID() == fieldID)
.toList();
for (int i = 0; i < playersOnField.size(); i++) {
if (playersOnField.get(i).getId() == playerID) {
return i;
}
}
return 0;
}
private void animateMovementAlongPath(com.jme3.scene.Spatial figure, List<Vector3f> pathPoints) {
float animationDurationPerSegment = 2.5f; // Langsamere Animation (2.5 Sekunden pro Segment)
int[] currentSegment = {0};
app.enqueue(() -> {
figure.addControl(new AbstractControl() {
private float elapsedTime = 0.0f;
@Override
protected void controlUpdate(float tpf) {
if (currentSegment[0] >= pathPoints.size() - 1) {
this.setEnabled(false); // Animation abgeschlossen
return;
}
elapsedTime += tpf;
float progress = Math.min(elapsedTime / animationDurationPerSegment, 1.0f);
Vector3f start = pathPoints.get(currentSegment[0]);
Vector3f end = pathPoints.get(currentSegment[0] + 1);
Vector3f interpolatedPosition = start.interpolateLocal(end, progress);
figure.setLocalTranslation(interpolatedPosition);
if (progress >= 1.0f) {
elapsedTime = 0.0f;
currentSegment[0]++;
}
}
@Override
protected void controlRender(RenderManager rm, ViewPort vp) {
// Nicht benötigt
}
});
});
}
@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();
}
}
private Vector3f calculateBuildingPosition(int fieldID) {
float baseX = 0.0f;
float baseZ = 0.0f;
switch (fieldID) {
case 0: baseX = -8.4f; baseZ = -7.7f; break;
case 1: baseX = -6.3f; baseZ = -7.7f; break;
case 2: baseX = -4.7f; baseZ = -7.7f; break;
case 3: baseX = -3.1f; baseZ = -7.7f; break;
case 4: baseX = -1.4f; baseZ = -7.7f; break;
case 5: baseX = 0.2f; baseZ = -7.7f; break;
case 6: baseX = 1.8f; baseZ = -7.7f; break;
case 7: baseX = 3.5f; baseZ = -7.7f; break;
case 8: baseX = 5.1f; baseZ = -7.7f; break;
case 9: baseX = 6.7f; baseZ = -7.7f; break;
case 10: baseX = 8.2f; baseZ = -7.7f; break;
case 11: baseX = 8.2f; baseZ = -6.5f; break; //passt
case 12: baseX = 8.2f; baseZ = -4.9f; break; //passt
case 13: baseX = 8.2f; baseZ = -3.3f; break; //passt
case 14: baseX = 8.2f; baseZ = -1.6f; break; //passt
case 15: baseX = 8.2f; baseZ = 0.0f; break; //passt
case 16: baseX = 8.2f; baseZ = 1.6f; break; //passt
case 17: baseX = 8.2f; baseZ = 3.3f; break; //passt
case 18: baseX = 8.2f; baseZ = 4.9f; break; //passt
case 19: baseX = 8.2f; baseZ = 6.5f; break; //passt
case 20: baseX = 8.2f; baseZ = 7.7f; break;
case 21: baseX = 6.5f; baseZ = 7.7f; break;
case 22: baseX = 4.9f; baseZ = 7.7f; break;
case 23: baseX = 3.3f; baseZ = 7.7f; break;
case 24: baseX = 1.6f; baseZ = 7.7f; break;
case 25: baseX = 0.0f; baseZ = 7.7f; break;
case 26: baseX = -1.6f; baseZ = 7.7f; break;
case 27: baseX = -3.3f; baseZ = 7.7f; break;
case 28: baseX = -4.9f; baseZ = 7.7f; break;
case 29: baseX = -6.5f; baseZ = 7.7f; break;
case 30: baseX = -7.2f; baseZ = 7.7f; break;
case 31: baseX = -7.2f; baseZ = 6.5f; break;
case 32: baseX = -7.2f; baseZ = 4.9f; break;
case 33: baseX = -7.2f; baseZ = 3.3f; break;
case 34: baseX = -7.2f; baseZ = 1.6f; break;
case 35: baseX = -7.2f; baseZ = 0.0f; break;
case 36: baseX = -7.2f; baseZ = -1.6f; break;
case 37: baseX = -7.2f; baseZ = -3.3f; break;
case 38: baseX = -7.2f; baseZ = -4.9f; break;
case 39: baseX = -7.2f; baseZ = -6.5f; break;
default: throw new IllegalArgumentException("Ungültige Feld-ID: " + fieldID);
}
return new Vector3f(baseX, 0, baseZ);
}
private void updateHousesOnBoard() {
app.enqueue(() -> {
List<BuildingProperty> propertiesWithBuildings = app.getGameLogic().getBoardManager().getPropertiesWithBuildings();
for (BuildingProperty property : propertiesWithBuildings) {
int houseCount = property.getHouses();
int hotelCount = property.getHotel();
String uniqueIdentifier = "Building_" + property.getId() + "_" + (hotelCount > 0 ? "Hotel" : houseCount);
if (existingHouses.contains(uniqueIdentifier)) continue;
try {
String modelPath = hotelCount > 0
? "models/Hotel/Hotel.j3o"
: "models/Haus/" + houseCount + "Haus.j3o";
com.jme3.scene.Spatial buildingModel = app.getAssetManager().loadModel(modelPath);
Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Light/Lighting.j3md");
buildingModel.setMaterial(mat);
buildingModel.setLocalScale(0.5f);
Vector3f position = calculateBuildingPosition(property.getId()).add(0, 0.5f, 0);
buildingModel.setLocalTranslation(position);
com.jme3.math.Quaternion rotation = new com.jme3.math.Quaternion();
if (property.getId() >= 1 && property.getId() <= 10) {
rotation.fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_Y);
} else if (property.getId() >= 21 && property.getId() <= 30) {
rotation.fromAngleAxis(3 * FastMath.HALF_PI, Vector3f.UNIT_Y);
} else if (property.getId() >= 31 && property.getId() <= 39) {
rotation.fromAngleAxis(FastMath.PI, Vector3f.UNIT_Y);
}
buildingModel.setLocalRotation(rotation);
buildingModel.setName(uniqueIdentifier);
app.getRootNode().attachChild(buildingModel);
existingHouses.add(uniqueIdentifier);
} catch (Exception e) {
System.err.println("Fehler beim Hinzufügen eines Gebäudes: " + e.getMessage());
}
}
});
}
@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);
}
@Override
public void receivedEvent(UpdatePlayerView event) {
this.playerHandler = app.getGameLogic().getPlayerHandler();
for (Player player : playerHandler.getPlayers()) {
movePlayerFigure(player);
}
updateHousesOnBoard();
} }
} }

View File

@ -1,292 +1,465 @@
package pp.monopoly.client.gui; package pp.monopoly.client.gui;
import java.util.Random;
import com.jme3.font.BitmapText;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f; import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.simsilica.lemur.*; 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.Label;
import com.simsilica.lemur.VAlignment;
import com.simsilica.lemur.component.IconComponent; import com.simsilica.lemur.component.IconComponent;
import com.simsilica.lemur.component.QuadBackgroundComponent; import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.component.SpringGridLayout; import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.style.ElementId; import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog; import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp; 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.Sound;
import pp.monopoly.notification.UpdatePlayerView;
/** /**
* Toolbar Klasse, die am unteren Rand der Szene angezeigt wird. * Represents the toolbar interface in the Monopoly application.
* Die Buttons bewegen den Würfel auf dem Spielfeld. * <p>
* This class provides game controls, player information, and event handling
* for actions such as dice rolling, trading, and ending turns.
* Implements {@link GameEventListener} to respond to game events.
* </p>
*/ */
public class Toolbar extends Dialog { public class Toolbar extends Dialog implements GameEventListener {
private final MonopolyApp app;
private final Container toolbarContainer;
private final BitmapText positionText; // Anzeige für die aktuelle Position
private final float boardLimit = 0.95f; // Grenzen des Bretts
private final float stepSize = 0.18f; // Schrittgröße pro Bewegung
private int currentPosition = 0; // Aktuelle Position auf dem Spielfeld
private final int positionsPerSide = 10; // Anzahl der Positionen pro Seite
private final Random random = new Random(); // Zufallsgenerator für den Würfelwurf
/** /**
* Konstruktor für die Toolbar. * Reference to the Monopoly application instance.
*/
private final MonopolyApp app;
/**
* The main container for the toolbar interface.
*/
private final Container toolbarContainer;
/**
* Container for displaying an overview of other players.
*/
private Container overviewContainer;
/**
* Container for displaying account-related information.
*/
private Container accountContainer;
/**
* Handles player-related data and actions.
*/
private PlayerHandler playerHandler;
/**
* Label for the first dice display.
*/
private Label imageLabel;
/**
* Label for the second dice display.
*/
private Label imageLabel2;
/**
* Button for rolling the dice.
*/
private Button diceButton;
/**
* Button for initiating trades.
*/
private Button tradeButton;
/**
* Button for accessing the property menu.
*/
private Button propertyMenuButton;
/**
* Button for ending the player's turn.
*/
private Button endTurnButton;
/**
* Stores the most recent dice roll event.
*/
private DiceRollEvent latestDiceRollEvent = null;
/**Indicates if the bankrupt PopUp has already been shown */
private boolean bankruptPopUp = false;
/**
* Constructs the toolbar for the Monopoly application.
* <p>
* Initializes the toolbar interface, adds event listeners, and sets up
* the GUI elements such as dice, buttons, and player information displays.
* </p>
* *
* @param app Die Hauptanwendung (MonopolyApp) * @param app the Monopoly application instance
*/ */
public Toolbar(MonopolyApp app) { public Toolbar(MonopolyApp app) {
super(app.getDialogManager()); super(app.getDialogManager());
this.app = app; this.app = app;
// Erstelle die Toolbar app.getGameLogic().addListener(this);
toolbarContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y), "toolbar"); this.playerHandler = app.getGameLogic().getPlayerHandler();
// Setze die Position am unteren Rand und die Breite toolbarContainer = createToolbarContainer();
toolbarContainer.setLocalTranslation( app.getGuiNode().attachChild(toolbarContainer);
0, // Links bündig }
200, // Höhe über dem unteren Rand
0 // Z-Ebene private Container createToolbarContainer() {
); Container container = new Container(new SpringGridLayout(Axis.X, Axis.Y), "toolbar");
toolbarContainer.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 200, 0)); // Volle Breite 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);
container.setBackground(background);
// Spielerfarbe abrufen
Player currentPlayer = playerHandler.getPlayerById(app.getId());
ColorRGBA playerColor = Player.getColor(currentPlayer.getId()).getColor();
// Oberer Balken
Container playerColorBar = new Container();
playerColorBar.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 15, 0)); // Höhe des oberen Balkens
playerColorBar.setBackground(new QuadBackgroundComponent(playerColor));
playerColorBar.setLocalTranslation(0, 210, 3); // Position über der Toolbar
app.getGuiNode().attachChild(playerColorBar);
// unterer Balken
Container playerColorBarbot = new Container();
playerColorBarbot.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 10, 0)); // Höhe des oberen Balkens
playerColorBarbot.setBackground(new QuadBackgroundComponent(playerColor));
playerColorBarbot.setLocalTranslation(0, 10, 3); // Position über der Toolbar
app.getGuiNode().attachChild(playerColorBarbot);
// Linker Balken
Container leftBar = new Container();
leftBar.setPreferredSize(new Vector3f(10, 210, 0)); // Breite 10, Höhe 210
leftBar.setBackground(new QuadBackgroundComponent(playerColor));
leftBar.setLocalTranslation(0, 200, 3); // Position am linken Rand
app.getGuiNode().attachChild(leftBar);
// Rechter Balken
Container rightBar = new Container();
rightBar.setPreferredSize(new Vector3f(10, 210, 0)); // Breite 10, Höhe 210
rightBar.setBackground(new QuadBackgroundComponent(playerColor));
rightBar.setLocalTranslation(app.getCamera().getWidth() - 10, 200, 2); // Position am rechten Rand
app.getGuiNode().attachChild(rightBar);
// Übersicht und Konto
accountContainer = container.addChild(new Container());
overviewContainer = container.addChild(new Container());
receivedEvent(new UpdatePlayerView()); // Initiale Aktualisierung
// Würfel-Bereich
container.addChild(createDiceSection());
// Aktionsmenü
Container menuContainer = container.addChild(new Container());
menuContainer.addChild(createTradeButton());
menuContainer.addChild(createPropertyMenuButton());
menuContainer.addChild(createEndTurnButton());
menuContainer.setBackground(null);
return container;
}
// Füge Buttons zur Toolbar hinzu private Container createDiceSection() {
//initializeButtons(); Container diceContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
diceContainer.addChild(createDiceDisplay());
// Menü-Container: Ein Nested-Container für Kontostand und "Meine Gulag Frei Karten"
Container accountContainer = toolbarContainer.addChild(new Container());
accountContainer.addChild(new Label("Kontostand", new ElementId("label-Bold")));
accountContainer.addChild(new Label("6666€", new ElementId("label-Text"))); //TODO Variable hier einsetzen
accountContainer.addChild(new Label("Gulag Frei Karten", new ElementId("label-Bold")));
accountContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Add a spacer between accountContainer and overviewContainer
Panel spacer = new Panel(); // Create an empty panel as a spacer
spacer.setPreferredSize(new Vector3f(5, 0, 0)); // Adjust the width as needed
spacer.setBackground(null);
toolbarContainer.addChild(spacer);
// Menü-Container: Ein Container für Übersicht
Container overviewContainer = toolbarContainer.addChild(new Container());
overviewContainer.addChild(new Label("Übersicht", new ElementId("label-Bold")));
overviewContainer.addChild(new Label("„Spieler 1“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen
overviewContainer.addChild(new Label("„Spieler 2“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen
overviewContainer.addChild(new Label("„Spieler 3“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen
overviewContainer.addChild(new Label("„Spieler 4“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen
overviewContainer.addChild(new Label("„Spieler 5“: 1244€", new ElementId("label-Text")));//TODO Variable hier einsetzen
overviewContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Menü-Container: Ein Container für Würfel
Container diceContainer = toolbarContainer.addChild(new Container());
diceContainer.setLayout(new SpringGridLayout(Axis.X, Axis.Y));
// Create a horizontal container to align leftContainer and rightContainer side by side
Container horizontalContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
horizontalContainer.setPreferredSize(new Vector3f(200, 150, 0)); // Adjust size as needed
// Create the first container (leftContainer)
Container leftContainer = new Container();
leftContainer.setPreferredSize(new Vector3f(100, 150, 0)); // Adjust size as needed
Label imageLabel = new Label("");
IconComponent icon = new IconComponent("Pictures/dice/one.png"); // Icon mit Textur erstellen
icon.setIconSize(new Vector2f(100,100)); // Skalierung des Bildes
imageLabel.setIcon(icon);
Label imageLabel2 = new Label("");
IconComponent icon2 = new IconComponent("Pictures/dice/two.png"); // Icon mit Textur erstellen
icon2.setIconSize(new Vector2f(100,100)); // Skalierung des Bildes
imageLabel2.setIcon(icon2);
// Create the second container (rightContainer)
Container rightContainer = new Container();
rightContainer.setPreferredSize(new Vector3f(100, 150, 0)); // Adjust size as needed
leftContainer.setBackground(null);
rightContainer.setBackground(null);
diceContainer.setBackground(null); diceContainer.setBackground(null);
horizontalContainer.setBackground(null);
imageLabel.setTextVAlignment(VAlignment.Center); diceButton = new Button("Würfeln", new ElementId("button-toolbar"));
imageLabel.setTextHAlignment(HAlignment.Center); diceButton.setPreferredSize(new Vector3f(200, 50, 0));
imageLabel2.setTextVAlignment(VAlignment.Center);
imageLabel2.setTextHAlignment(HAlignment.Center);
leftContainer.addChild(imageLabel);
rightContainer.addChild(imageLabel2);
// Add leftContainer and rightContainer to the horizontal container
horizontalContainer.addChild(leftContainer);
horizontalContainer.addChild(rightContainer);
// Add the horizontalContainer to the diceContainer (top section)
diceContainer.addChild(horizontalContainer);
// Add the Würfeln button directly below the horizontalContainer
Button diceButton = new Button("Würfeln");
diceButton.setPreferredSize(new Vector3f(200, 50, 0)); // Full width for Würfeln button
diceButton.addClickCommands(s -> ifTopDialog(() -> { diceButton.addClickCommands(s -> ifTopDialog(() -> {
rollDice(); diceButton.setEnabled(false);
endTurnButton.setEnabled(true);
startDiceAnimation();
app.getGameLogic().send(new RollDice());
app.getGameLogic().playSound(Sound.BUTTON); app.getGameLogic().playSound(Sound.BUTTON);
})); }));
diceContainer.addChild(diceButton); diceContainer.addChild(diceButton);
return diceContainer;
// Menü-Container: Ein Nested-Container für Handeln, Grundstücke und Zug beenden
Container menuContainer = toolbarContainer.addChild(new Container());
menuContainer.addChild(new Button("Handeln"));
menuContainer.addChild(new Button("Grundstücke"));
menuContainer.addChild(new Button("Zug beenden"));
menuContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Füge die Toolbar zur GUI hinzu
app.getGuiNode().attachChild(toolbarContainer);
// Erstelle die Position-Anzeige
positionText = createPositionDisplay();
updatePositionDisplay(); // Initialisiere die Anzeige mit der Startposition
} }
/** private Container createDiceDisplay() {
* Initialisiert die Buttons in der Toolbar. Container horizontalContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
*/ horizontalContainer.setPreferredSize(new Vector3f(200, 150, 0));
private void initializeButtons() { horizontalContainer.setBackground(null);
addTradeMenuButton(); // Bewegung nach vorne
addEndTurnButton(); // Bewegung nach hinten
addDiceRollButton(); // Würfel-Button
imageLabel = createDiceLabel("Pictures/dice/one.png");
imageLabel2 = createDiceLabel("Pictures/dice/two.png");
horizontalContainer.addChild(createDiceContainer(imageLabel));
horizontalContainer.addChild(createDiceContainer(imageLabel2));
return horizontalContainer;
} }
/** private Label createDiceLabel(String iconPath) {
* Fügt einen Button mit einer Bewegung hinzu. Label label = new Label("");
* IconComponent icon = new IconComponent(iconPath);
* @param label Der Text des Buttons icon.setIconSize(new Vector2f(100, 100));
* @param step Schrittweite (+1 für vorwärts, -1 für rückwärts) label.setIcon(icon);
*/ return label;
}
/*private void addButton(String label, int step) { private Container createDiceContainer(Label label) {
Button button = new Button(label); Container container = new Container();
button.setPreferredSize(new Vector3f(150, 50, 0)); // Größe der Buttons container.setBackground(null);
button.addClickCommands(source -> moveCube(step)); container.setPreferredSize(new Vector3f(100, 150, 0));
toolbarContainer.addChild(button); container.addChild(label);
}*/ return container;
}
/** private Button createTradeButton() {
* Fügt den Würfel-Button hinzu, der die Figur entsprechend der gewürfelten Zahl bewegt.
*/ tradeButton = new Button("", new ElementId("button-toolbar"));
private Button addDiceRollButton() { tradeButton.setPreferredSize(new Vector3f(150, 50, 0));
Button diceButton = new Button("Würfeln");
diceButton.setPreferredSize(new Vector3f(50, 20, 0)); String iconTradePath = "icons/icon-handeln.png";
diceButton.addClickCommands(s -> ifTopDialog(() -> { IconComponent iconTrade = new IconComponent(iconTradePath);
rollDice(); iconTrade.setHAlignment(HAlignment.Center);
iconTrade.setVAlignment(VAlignment.Center);
iconTrade.setIconSize(new Vector2f(100, 100));
tradeButton.setIcon(iconTrade);
tradeButton.setFontSize(40);
// Add click behavior
tradeButton.addClickCommands(source -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON); app.getGameLogic().playSound(Sound.BUTTON);
}));
toolbarContainer.addChild(diceButton);
return diceButton;
}
private void addTradeMenuButton() {
Button diceButton = new Button("Handeln");
diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons
diceButton.addClickCommands(s -> {
rollDice();
app.getGameLogic().playSound(Sound.BUTTON);
this.close();
System.out.println("test");
new ChoosePartner(app).open(); new ChoosePartner(app).open();
});
toolbarContainer.addChild(diceButton);
}// TODO Funktion der Buttons Überarbeiten und prüfen
private void addEndTurnButton() {
Button diceButton = new Button("Grundstücke");
diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons
diceButton.addClickCommands(s -> ifTopDialog(() -> {
rollDice();
app.getGameLogic().playSound(Sound.BUTTON);
})); }));
toolbarContainer.addChild(diceButton);
return tradeButton;
} }
private void addPropertyMenuButton() { private Button createPropertyMenuButton() {
Button diceButton = new Button("Zug beenden"); propertyMenuButton = new Button("", new ElementId("button-toolbar"));
diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons propertyMenuButton.setPreferredSize(new Vector3f(150, 50, 0));
diceButton.addClickCommands(s -> ifTopDialog(() -> {
rollDice(); String iconBuildingPath = "icons/icon-gebaude.png";
IconComponent iconBuilding = new IconComponent(iconBuildingPath);
iconBuilding.setHAlignment(HAlignment.Center);
iconBuilding.setVAlignment(VAlignment.Center);
iconBuilding.setIconSize(new Vector2f(75, 75));
propertyMenuButton.setIcon(iconBuilding);
propertyMenuButton.setFontSize(30);
propertyMenuButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON); app.getGameLogic().playSound(Sound.BUTTON);
new BuildingAdminMenu(app).open();
})); }));
toolbarContainer.addChild(diceButton); return propertyMenuButton;
}
private Button createEndTurnButton() {
endTurnButton = new Button("", new ElementId("button-toolbar"));
endTurnButton.setFontSize(28);
endTurnButton.setPreferredSize(new Vector3f(150, 50, 0));
String iconEndTurnPath = "icons/icon-zugbeenden.png";
IconComponent iconEndTurn = new IconComponent(iconEndTurnPath);
iconEndTurn.setHAlignment(HAlignment.Center);
iconEndTurn.setVAlignment(VAlignment.Center);
iconEndTurn.setIconSize(new Vector2f(75, 75));
endTurnButton.setIcon(iconEndTurn);
endTurnButton.addClickCommands(s -> ifTopDialog(() -> {
app.getGameLogic().playSound(Sound.BUTTON);
if (app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getAccountBalance() < 0 && !bankruptPopUp) {
new Bankrupt(app).open();
bankruptPopUp = true;
} else {
bankruptPopUp = false;
app.getGameLogic().send(new EndTurn());
receivedEvent(new ButtonStatusEvent(false));
}
}));
return endTurnButton;
}
private void startDiceAnimation() {
long startTime = System.currentTimeMillis();
new Thread(() -> {
try {
animateDice(startTime);
if (latestDiceRollEvent != null) {
showFinalDiceResult(latestDiceRollEvent);
}
} catch (InterruptedException e) {
System.err.println("Dice animation interrupted: " + e.getMessage());
}
}).start();
} }
/** /**
* Simuliert einen Würfelwurf und bewegt die Figur entsprechend. * Animates the dice roll by cycling through dice images.
*/ */
private void rollDice() { private void animateDice(long startTime) throws InterruptedException {
int diceRoll = random.nextInt(6) + 1; // Zahl zwischen 1 und 6 int[] currentFace = {1};
System.out.println("Gewürfelt: " + diceRoll); while (System.currentTimeMillis() - startTime < 2000) { // Animation duration
} currentFace[0] = (currentFace[0] % 6) + 1;
/** String rotatingImage1 = diceToString(currentFace[0]);
* Berechnet die neue Position des Würfels basierend auf der aktuellen Brettseite und Position. String rotatingImage2 = diceToString((currentFace[0] % 6) + 1);
*
* @param position Aktuelle Position auf dem Spielfeld app.enqueue(() -> {
* @return Die berechnete Position als Vector3f setDiceIcon(imageLabel, rotatingImage1);
*/ setDiceIcon(imageLabel2, rotatingImage2);
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 Thread.sleep(100); // Time between frame updates
switch (side) {
case 0: // Unten (positive x-Achse)
return new Vector3f(-boardLimit + offset * stepSize, 0.1f, -boardLimit + 0.05f);
case 1: // Rechts (positive z-Achse)
return new Vector3f(boardLimit - 0.05f, 0.1f, -boardLimit + offset * stepSize);
case 2: // Oben (negative x-Achse)
return new Vector3f(boardLimit - offset * stepSize, 0.1f, boardLimit - 0.05f);
case 3: // Links (negative z-Achse)
return new Vector3f(-boardLimit + 0.05f, 0.1f, boardLimit - offset * stepSize);
default:
throw new IllegalArgumentException("Ungültige Position: " + position);
} }
} }
/** /**
* Erstellt die Anzeige für die aktuelle Position. * Displays the final dice result after animation.
* *
* @return Das BitmapText-Objekt für die Anzeige * @param event the dice roll event containing the result
*/ */
private BitmapText createPositionDisplay() { private void showFinalDiceResult(DiceRollEvent event) {
BitmapText text = new BitmapText(app.getAssetManager().loadFont("Interface/Fonts/Default.fnt"), false); app.enqueue(() -> {
text.setSize(20); // Schriftgröße setDiceIcon(imageLabel, diceToString(event.a()));
text.setLocalTranslation(10, app.getCamera().getHeight() - 10, 0); // Oben links setDiceIcon(imageLabel2, diceToString(event.b()));
app.getGuiNode().attachChild(text); });
return text; }
private void setDiceIcon(Label label, String imagePath) {
IconComponent icon = new IconComponent(imagePath);
icon.setIconSize(new Vector2f(80, 80)); // Set consistent dice size
label.setIcon(icon);
}
private String diceToString(int i) {
switch (i) {
case 1: return "Pictures/dice/one.png";
case 2: return "Pictures/dice/two.png";
case 3: return "Pictures/dice/three.png";
case 4: return "Pictures/dice/four.png";
case 5: return "Pictures/dice/five.png";
case 6: return "Pictures/dice/six.png";
default: throw new IllegalArgumentException("Invalid dice number: " + i);
}
} }
/** /**
* Aktualisiert die Anzeige für die aktuelle Position. * Handles dice roll events by updating the dice display.
*
* @param event the dice roll event containing dice values
*/ */
private void updatePositionDisplay() { @Override
positionText.setText("Feld-ID: " + currentPosition); public void receivedEvent(DiceRollEvent event) {
latestDiceRollEvent = event;
} }
/**
* Updates the player view with the latest account and overview data.
*
* @param event the update event for the player view
*/
@Override
public void receivedEvent(UpdatePlayerView event) {
playerHandler = app.getGameLogic().getPlayerHandler();
System.out.println("Update Player View");
accountContainer.clearChildren();
overviewContainer.clearChildren();
accountContainer.addChild(new Label("Kontostand", new ElementId("label-toolbar")));
accountContainer.addChild(new Label(
playerHandler.getPlayerById(app.getId()).getAccountBalance() + " EUR",
new ElementId("label-account")
));
accountContainer.addChild(new Label("Gulag Karten", new ElementId("label-toolbar")));
accountContainer.addChild(new Label(
playerHandler.getPlayerById(app.getId()).getNumJailCard() + "",
new ElementId("label-account")
));
accountContainer.setBackground(null);
overviewContainer.addChild(new Label("Übersicht", new ElementId("label-toolbar")));
for (Player player : playerHandler.getPlayers()) {
if (player.getId() != app.getId()) {
// Spielerfarbe abrufen
ColorRGBA playerColor = (Player.getColor(player.getId()).getColor());
// Label für den Spieler erstellen
Label playerLabel = new Label(
player.getName() + ": " + player.getAccountBalance() + " EUR",
new ElementId("label-Text")
);
// Farbe setzen
playerLabel.setColor(playerColor);
// Label zum Container hinzufügen
overviewContainer.addChild(playerLabel);
}
}
overviewContainer.setBackground(null);
}
/**
* Updates the enabled status of toolbar buttons based on the event.
*
* @param event the button status event
*/
@Override
public void receivedEvent(ButtonStatusEvent event) {
boolean enabled = event.buttonsEnabled();
diceButton.setEnabled(enabled);
tradeButton.setEnabled(enabled);
propertyMenuButton.setEnabled(enabled);
endTurnButton.setEnabled(false);
}
/**
* Closes the toolbar and detaches it from the GUI.
*/
@Override @Override
public void close() { public void close() {
app.getGuiNode().detachChild(toolbarContainer); app.getGuiNode().detachChild(toolbarContainer);
app.getGuiNode().detachChild(positionText);
super.close(); super.close();
} }
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override @Override
public void escape() { public void escape() {
new SettingsMenu(app).open(); new SettingsMenu(app).open();
} }
/**
* Updates the toolbar by refreshing player information.
*/
@Override
public void update() {
receivedEvent(new UpdatePlayerView());
super.update();
}
} }

View File

@ -1,168 +0,0 @@
package pp.monopoly.client.gui;
import java.util.Random;
import com.jme3.font.BitmapText;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.component.SpringGridLayout;
import pp.monopoly.client.MonopolyApp;
/**
* Toolbar Klasse, die am unteren Rand der Szene angezeigt wird.
* Die Buttons bewegen den Würfel auf dem Spielfeld.
*/
public class Toolbar2 {
private final MonopolyApp app;
private final Container toolbarContainer;
private final Geometry cube; // Referenz auf den Würfel
private final BitmapText positionText; // Anzeige für die aktuelle Position
private final float boardLimit = 0.95f; // Grenzen des Bretts
private final float stepSize = 0.18f; // Schrittgröße pro Bewegung
private int currentPosition = 0; // Aktuelle Position auf dem Spielfeld
private final int positionsPerSide = 10; // Anzahl der Positionen pro Seite
private final Random random = new Random(); // Zufallsgenerator für den Würfelwurf
/**
* Konstruktor für die Toolbar.
*
* @param app Die Hauptanwendung (MonopolyApp)
* @param cube Der Würfel, der bewegt werden soll
*/
public Toolbar2(MonopolyApp app, Geometry cube) {
this.app = app;
this.cube = cube;
// Erstelle die Toolbar
toolbarContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y));
// Setze die Position am unteren Rand und die Breite
toolbarContainer.setLocalTranslation(
0, // Links bündig
100, // Höhe über dem unteren Rand
0 // Z-Ebene
);
toolbarContainer.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 100, 0)); // Volle Breite
// Füge Buttons zur Toolbar hinzu
initializeButtons();
// Füge die Toolbar zur GUI hinzu
app.getGuiNode().attachChild(toolbarContainer);
// Erstelle die Position-Anzeige
positionText = createPositionDisplay();
updatePositionDisplay(); // Initialisiere die Anzeige mit der Startposition
}
/**
* Initialisiert die Buttons in der Toolbar.
*/
private void initializeButtons() {
addButton("Vorwärts", 1); // Bewegung nach vorne
addButton("Rückwärts", -1); // Bewegung nach hinten
addDiceRollButton(); // Würfel-Button
}
/**
* Fügt einen Button mit einer Bewegung hinzu.
*
* @param label Der Text des Buttons
* @param step Schrittweite (+1 für vorwärts, -1 für rückwärts)
*/
private void addButton(String label, int step) {
Button button = new Button(label);
button.setPreferredSize(new Vector3f(150, 50, 0)); // Größe der Buttons
button.addClickCommands(source -> moveCube(step));
toolbarContainer.addChild(button);
}
/**
* Fügt den Würfel-Button hinzu, der die Figur entsprechend der gewürfelten Zahl bewegt.
*/
private void addDiceRollButton() {
Button diceButton = new Button("Würfeln");
diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons
diceButton.addClickCommands(source -> rollDice());
toolbarContainer.addChild(diceButton);
}
/**
* Simuliert einen Würfelwurf und bewegt die Figur entsprechend.
*/
private void rollDice() {
int diceRoll = random.nextInt(6) + 1; // Zahl zwischen 1 und 6
System.out.println("Gewürfelt: " + diceRoll);
moveCube(diceRoll); // Bewege die Figur um die gewürfelte Zahl
}
/**
* Bewegt den Würfel basierend auf der aktuellen Position auf dem Brett.
*
* @param step Schrittweite (+1 für vorwärts, -1 für rückwärts oder andere Werte)
*/
private void moveCube(int step) {
currentPosition = (currentPosition + step + 4 * positionsPerSide) % (4 * positionsPerSide);
Vector3f newPosition = calculatePosition(currentPosition);
cube.setLocalTranslation(newPosition);
updatePositionDisplay(); // Aktualisiere die Positionsanzeige
System.out.println("Würfelposition: " + newPosition + " (Feld-ID: " + currentPosition + ")");
}
/**
* Berechnet die neue Position des Würfels basierend auf der aktuellen Brettseite und Position.
*
* @param position Aktuelle Position auf dem Spielfeld
* @return Die berechnete Position als Vector3f
*/
private Vector3f calculatePosition(int position) {
int side = position / positionsPerSide; // Seite des Bretts (0 = unten, 1 = rechts, 2 = oben, 3 = links)
int offset = position % positionsPerSide; // Position auf der aktuellen Seite
switch (side) {
case 0: // Unten (positive x-Achse)
return new Vector3f(-boardLimit + offset * stepSize, 0.1f, -boardLimit + 0.05f);
case 1: // Rechts (positive z-Achse)
return new Vector3f(boardLimit - 0.05f, 0.1f, -boardLimit + offset * stepSize);
case 2: // Oben (negative x-Achse)
return new Vector3f(boardLimit - offset * stepSize, 0.1f, boardLimit - 0.05f);
case 3: // Links (negative z-Achse)
return new Vector3f(-boardLimit + 0.05f, 0.1f, boardLimit - offset * stepSize);
default:
throw new IllegalArgumentException("Ungültige Position: " + position);
}
}
/**
* Erstellt die Anzeige für die aktuelle Position.
*
* @return Das BitmapText-Objekt für die Anzeige
*/
private BitmapText createPositionDisplay() {
BitmapText text = new BitmapText(app.getAssetManager().loadFont("Interface/Fonts/Default.fnt"), false);
text.setSize(20); // Schriftgröße
text.setLocalTranslation(10, app.getCamera().getHeight() - 10, 0); // Oben links
app.getGuiNode().attachChild(text);
return text;
}
/**
* Aktualisiert die Anzeige für die aktuelle Position.
*/
private void updatePositionDisplay() {
positionText.setText("Feld-ID: " + currentPosition);
}
/**
* Entfernt die Toolbar.
*/
public void remove() {
app.getGuiNode().detachChild(toolbarContainer);
app.getGuiNode().detachChild(positionText);
}
}

View File

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

@ -25,7 +25,7 @@ public class VolumeSlider extends Slider {
} }
/** /**
* when triggered it updates the volume to the value set with the slider * When triggered it updates the volume to the value set with the slider
*/ */
public void update() { public void update() {
if (vol != getModel().getPercent()) { if (vol != getModel().getPercent()) {

View File

@ -0,0 +1,504 @@
/*
* $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

@ -0,0 +1,135 @@
/*
* $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

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

@ -0,0 +1,132 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.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!", 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,10 +1,7 @@
package pp.monopoly.client.gui.popups; 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.ColorRGBA;
import com.jme3.scene.Geometry; import com.simsilica.lemur.Button;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Container; import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label; import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent; import com.simsilica.lemur.component.QuadBackgroundComponent;
@ -13,41 +10,52 @@ import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog; import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp; import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu; 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.model.fields.BuildingProperty;
import pp.monopoly.notification.Sound;
/** /**
* TODO Kommentare fixen * BuildingPropertyCard creates the popup for field information
* SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann.
*/ */
public class BuildingPropertyCard extends Dialog { public class BuildingPropertyCard extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app; private final MonopolyApp app;
private final Geometry overlayBackground;
private final Container buildingPropertyContainer;
private final Container backgroundContainer;
private int index = 37;
/** 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) { public BuildingPropertyCard(MonopolyApp app) {
super(app.getDialogManager()); super(app.getDialogManager());
this.app = app; this.app = app;
//Generate the corresponfing field //Generate the corresponding field
BuildingProperty field = (BuildingProperty) app.getGameLogic().getBoardManager().getFieldAtIndex(index); int index = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
BuildingProperty field = (BuildingProperty) new BoardManager().getFieldAtIndex(index);
// Halbtransparentes Overlay hinzufügen
overlayBackground = createOverlayBackground();
app.getGuiNode().attachChild(overlayBackground);
// Create the background container // Create the background container
backgroundContainer = new Container(); backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
app.getGuiNode().attachChild(backgroundContainer); attachChild(backgroundContainer);
// Hauptcontainer für die Gebäudekarte // Hauptcontainer für die Gebäudekarte
buildingPropertyContainer = new Container(); buildingPropertyContainer = new Container();
buildingPropertyContainer.setBackground(new QuadBackgroundComponent(field.getColor().getColor())); 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));
Label settingsTitle = buildingPropertyContainer.addChild(new Label( field.getName(), new ElementId("settings-title"))); //Titel
Label settingsTitle = buildingPropertyContainer.addChild(new Label( field.getName(), new ElementId("label-Bold")));
settingsTitle.setBackground(new QuadBackgroundComponent(field.getColor().getColor()));
settingsTitle.setFontSize(48); settingsTitle.setFontSize(48);
// Text, der auf der Karte steht // Text, der auf der Karte steht
@ -66,28 +74,31 @@ public class BuildingPropertyCard extends Dialog {
propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", 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))); propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
//TODO eventuell diese Stelle löschen, da nur die BuyCard Kaufen und beenden hat
/*
// Beenden-Button // Beenden-Button
Button quitButton = foodFieldContainer.addChild(new Button("Beenden", new ElementId("button"))); Button quitButton = buildingPropertyContainer.addChild(new Button("Beenden", new ElementId("button")));
quitButton.setFontSize(32); quitButton.setFontSize(32);
quitButton.addClickCommands(s -> ifTopDialog( () -> {
System.err.println("Button does something?");
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Kaufen-Button // Kaufen-Button
Button buyButton = foodFieldContainer.addChild(new Button("Kaufen", new ElementId("button"))); Button buyButton = buildingPropertyContainer.addChild(new Button("Kaufen", new ElementId("button")));
buyButton.setFontSize(32); buyButton.setFontSize(32);
*/ buyButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
app.getGameLogic().send(new BuyPropertyResponse());
}));
float padding = 10; // Padding around the settingsContainer for the background // Zentriere das Popup
backgroundContainer.setPreferredSize(buildingPropertyContainer.getPreferredSize().addLocal(padding, padding, 0));
// Zentriere das Menü
buildingPropertyContainer.setLocalTranslation( buildingPropertyContainer.setLocalTranslation(
(app.getCamera().getWidth() - buildingPropertyContainer.getPreferredSize().x) / 2, (app.getCamera().getWidth() - buildingPropertyContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + buildingPropertyContainer.getPreferredSize().y) / 2, (app.getCamera().getHeight() + buildingPropertyContainer.getPreferredSize().y) / 2,
8 8
); );
// Zentriere das Popup
backgroundContainer.setLocalTranslation( backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - buildingPropertyContainer.getPreferredSize().x - padding) / 2, (app.getCamera().getWidth() - buildingPropertyContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + buildingPropertyContainer.getPreferredSize().y+ padding) / 2, (app.getCamera().getHeight() + buildingPropertyContainer.getPreferredSize().y+ padding) / 2,
@ -98,38 +109,20 @@ public class BuildingPropertyCard extends Dialog {
} }
/** /**
* Erstellt einen halbtransparenten Hintergrund für das Menü. * Closes the popup and removes the associated GUI elements.
*
* @return Geometrie des Overlays
*/
private Geometry createOverlayBackground() {
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
Geometry overlay = new Geometry("Overlay", quad);
Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
overlay.setMaterial(material);
overlay.setLocalTranslation(0, 0, 0);
return overlay;
}
/**
* Schließt das Menü und entfernt die GUI-Elemente.
*/ */
@Override @Override
public void close() { public void close() {
app.getGuiNode().detachChild(buildingPropertyContainer); // Entferne das Menü app.getGuiNode().detachChild(buildingPropertyContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close(); super.close();
} }
public void setIndex(int index) {
this.index = index;
}
/**
* Opens the settings menu when the escape key is pressed.
*/
@Override @Override
public void escape() { public void escape() {
new SettingsMenu(app).open(); new SettingsMenu(app).open();
} }
} }

View File

@ -1,128 +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.model.fields.BoardManager;
import pp.monopoly.model.fields.BuildingProperty;
/**
* SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann.
*/
public class BuyCard extends Dialog {
private final MonopolyApp app;
private final Geometry overlayBackground;
private final Container buyCardContainer;
private final Container backgroundContainer;
private int index = 37;
public BuyCard(MonopolyApp app) {
super(app.getDialogManager());
this.app = app;
//Generate the corresponfing field
BuildingProperty field = (BuildingProperty) new BoardManager().getFieldAtIndex(index);
// Halbtransparentes Overlay hinzufügen
overlayBackground = createOverlayBackground();
app.getGuiNode().attachChild(overlayBackground);
// Create the background container
backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
app.getGuiNode().attachChild(backgroundContainer);
// Hauptcontainer für die Gebäudekarte
buyCardContainer = new Container();
buyCardContainer.setBackground(new QuadBackgroundComponent(field.getColor().getColor()));
Label settingsTitle = buyCardContainer.addChild(new Label( field.getName(), new ElementId("settings-title")));
settingsTitle.setFontSize(48);
// Text, der auf der Karte steht
// Die Preise werden dynamisch dem BoardManager entnommen
Container propertyValuesContainer = buyCardContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label("„Grundstückswert: " + field.getPrice() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("„Miete allein: " + field.getAllRent().get(0)+ " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 1 Haus: " + field.getAllRent().get(1) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 2 Häuser: " + field.getAllRent().get(2) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 3 Häuser: " + field.getAllRent().get(3) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 4 Häuser: " + field.getAllRent().get(4) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-mit 1 Hotel: " + field.getAllRent().get(5) + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„-1 Haus kostet: " + field.getHousePrice()+ " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));// Leerzeile
propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
// Beenden-Button
Button quitButton = buyCardContainer.addChild(new Button("Beenden", new ElementId("button")));
quitButton.setFontSize(32);
// Kaufen-Button
Button buyButton = buyCardContainer.addChild(new Button("Kaufen", new ElementId("button")));
buyButton.setFontSize(32);
float padding = 10; // Padding around the settingsContainer for the background
backgroundContainer.setPreferredSize(buyCardContainer.getPreferredSize().addLocal(padding, padding, 0));
// Zentriere das Menü
buyCardContainer.setLocalTranslation(
(app.getCamera().getWidth() - buyCardContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + buyCardContainer.getPreferredSize().y) / 2,
8
);
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - buyCardContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + buyCardContainer.getPreferredSize().y+ padding) / 2,
7
);
app.getGuiNode().attachChild(buyCardContainer);
}
/**
* Erstellt einen halbtransparenten Hintergrund für das Menü.
*
* @return Geometrie des Overlays
*/
private Geometry createOverlayBackground() {
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
Geometry overlay = new Geometry("Overlay", quad);
Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
overlay.setMaterial(material);
overlay.setLocalTranslation(0, 0, 0);
return overlay;
}
/**
* Schließt das Menü und entfernt die GUI-Elemente.
*/
@Override
public void close() {
app.getGuiNode().detachChild(buyCardContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close();
}
@Override
public void escape() {
new SettingsMenu(app).open();
}
}

View File

@ -0,0 +1,247 @@
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;
/** TextField to display selected properties. */
private TextField 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("warning-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,
9
);
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - buyHouseContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + buyHouseContainer.getPreferredSize().y + padding) / 2,
8
);
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(ColorRGBA.Orange));
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 TextField(""); // Create TextField for displaying selections
selectionDisplay.setPreferredSize(new Vector3f(300, 30, 0));
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

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

@ -13,26 +13,35 @@ import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId; import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog; import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp; import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu; import pp.monopoly.notification.Sound;
import pp.monopoly.model.card.Card; // TODO für den Import der Queue notwendig
import pp.monopoly.model.card.DeckHelper;
/** /**
* SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann. * EventCardPopup is a popup which appears when a certain EventCard is triggered by entering a EventCardField
*/ */
public class EventCard extends Dialog { public class EventCardPopup extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app; private final MonopolyApp app;
/** Semi-transparent overlay background for the popup. */
private final Geometry overlayBackground; private final Geometry overlayBackground;
/** Main container for the event card information. */
private final Container eventCardContainer; private final Container eventCardContainer;
/** Background container providing a border for the popup. */
private final Container backgroundContainer; private final Container backgroundContainer;
public EventCard(MonopolyApp app) { /**
* 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()); super(app.getDialogManager());
this.app = app; this.app = app;
//Generate the corresponfing field
Card card = new DeckHelper().drawCard(); // TODO nimmt die Karten gerade unabhängig aus dem DeckHelper
// Halbtransparentes Overlay hinzufügen // Halbtransparentes Overlay hinzufügen
overlayBackground = createOverlayBackground(); overlayBackground = createOverlayBackground();
app.getGuiNode().attachChild(overlayBackground); app.getGuiNode().attachChild(overlayBackground);
@ -42,64 +51,61 @@ public class EventCard extends Dialog {
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
app.getGuiNode().attachChild(backgroundContainer); app.getGuiNode().attachChild(backgroundContainer);
// Hauptcontainer für die Gebäudekarte
eventCardContainer = new Container(); eventCardContainer = new Container();
eventCardContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); eventCardContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
eventCardContainer.setPreferredSize(new Vector3f(550,400,10)); eventCardContainer.setPreferredSize(new Vector3f(550,400,10));
float padding = 10;
backgroundContainer.setPreferredSize(eventCardContainer.getPreferredSize().addLocal(padding, padding, 0));
// Titel // Titel
// Die Namen werden dynamisch dem BoardManager entnommen
Label gateFieldTitle = eventCardContainer.addChild(new Label("Ereigniskarte", new ElementId("settings-title"))); Label gateFieldTitle = eventCardContainer.addChild(new Label("Ereigniskarte", new ElementId("settings-title")));
gateFieldTitle.setFontSize(48); gateFieldTitle.setFontSize(48);
gateFieldTitle.setColor(ColorRGBA.Black); gateFieldTitle.setColor(ColorRGBA.Black);
// Text, der auf der Karte steht // Text, der auf der Karte steht
// Die Preise werden dynamisch dem BoardManager entnommen // Die Erklärungsfelder werden automatisch den descriptions der Message entnommen
Container propertyValuesContainer = eventCardContainer.addChild(new Container()); Container propertyValuesContainer = eventCardContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label(card.getDescription(), new ElementId("label-Text"))); propertyValuesContainer.addChild(new Label(description, new ElementId("label-Text")));
propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f))); propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
propertyValuesContainer.setPreferredSize(new Vector3f(300,200,10)); propertyValuesContainer.setPreferredSize(new Vector3f(300,200,10));
// Beenden-Button // Beenden-Button
Button quitButton = eventCardContainer.addChild(new Button("Jawohl", new ElementId("button"))); Button quitButton = eventCardContainer.addChild(new Button("Jawohl", new ElementId("button")));
quitButton.setFontSize(32); quitButton.setFontSize(32);
quitButton.addClickCommands(source -> close()); quitButton.addClickCommands(source -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Zentriere das Popup
// TODO Kaufen-Button wird nicht mehr benötigt, prüfen ob weg kann
//Button buyButton = buyCardContainer.addChild(new Button("Kaufen", new ElementId("button")));
//buyButton.setFontSize(32);
float padding = 10; // Padding around the settingsContainer for the background
backgroundContainer.setPreferredSize(eventCardContainer.getPreferredSize().addLocal(padding, padding, 0));
// Zentriere das Menü
eventCardContainer.setLocalTranslation( eventCardContainer.setLocalTranslation(
(app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x) / 2, (app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y) / 2, (app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y) / 2,
8 10
); );
// Zentriere das Popup
backgroundContainer.setLocalTranslation( backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x - padding) / 2, (app.getCamera().getWidth() - eventCardContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y+ padding) / 2, (app.getCamera().getHeight() + eventCardContainer.getPreferredSize().y+ padding) / 2,
7 9
); );
app.getGuiNode().attachChild(eventCardContainer); app.getGuiNode().attachChild(eventCardContainer);
} }
/** /**
* Erstellt einen halbtransparenten Hintergrund für das Menü. * Creates a semi-transparent background overlay for the popup.
* *
* @return Geometrie des Overlays * @return the geometry of the overlay
*/ */
private Geometry createOverlayBackground() { private Geometry createOverlayBackground() {
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
Geometry overlay = new Geometry("Overlay", quad); Geometry overlay = new Geometry("Overlay", quad);
Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f));
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
overlay.setMaterial(material); overlay.setMaterial(material);
overlay.setLocalTranslation(0, 0, 0); overlay.setLocalTranslation(0, 0, 0);
@ -107,18 +113,21 @@ public class EventCard extends Dialog {
} }
/** /**
* Schließt das Menü und entfernt die GUI-Elemente. * Closes the popup and removes its associated GUI elements.
*/ */
@Override @Override
public void close() { public void close() {
app.getGuiNode().detachChild(eventCardContainer); // Entferne das Menü app.getGuiNode().detachChild(eventCardContainer);
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand app.getGuiNode().detachChild(backgroundContainer);
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay app.getGuiNode().detachChild(overlayBackground);
super.close(); super.close();
} }
/**
* Handles the escape key action by closing the popup.
*/
@Override @Override
public void escape() { public void escape() {
new SettingsMenu(app).open(); close();
} }
} }

View File

@ -5,6 +5,7 @@ import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA; import com.jme3.math.ColorRGBA;
import com.jme3.scene.Geometry; import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Quad; import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Button;
import com.simsilica.lemur.Container; import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label; import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent; import com.simsilica.lemur.component.QuadBackgroundComponent;
@ -13,45 +14,55 @@ import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog; import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp; import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu; import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.message.client.BuyPropertyResponse;
import pp.monopoly.model.fields.FoodField; import pp.monopoly.model.fields.FoodField;
import pp.monopoly.notification.Sound;
/** /**
* FoodFieldCard erstellt die Geböudekarte vom Brandl und der Truppenküche * FoodFieldCard creates the popup for field information
*/ */
public class FoodFieldCard extends Dialog { public class FoodFieldCard extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app; private final MonopolyApp app;
private final Geometry overlayBackground;
private final Container foodFieldContainer;
private final Container backgroundContainer;
private int index = 12;
/** 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) { public FoodFieldCard(MonopolyApp app) {
super(app.getDialogManager()); super(app.getDialogManager());
this.app = app; this.app = app;
//Generate the corresponfing field int index = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
FoodField field = (FoodField) app.getGameLogic().getBoardManager().getFieldAtIndex(index); FoodField field = (FoodField) app.getGameLogic().getBoardManager().getFieldAtIndex(index);
// Halbtransparentes Overlay hinzufügen
overlayBackground = createOverlayBackground(); overlayBackground = createOverlayBackground();
app.getGuiNode().attachChild(overlayBackground); app.getGuiNode().attachChild(overlayBackground);
// Create the background container
backgroundContainer = new Container(); backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
app.getGuiNode().attachChild(backgroundContainer); attachChild(backgroundContainer);
// Hauptcontainer für die Gebäudekarte
foodFieldContainer = new Container(); foodFieldContainer = new Container();
foodFieldContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f))); 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")));
// Titel, bestehend aus dynamischen Namen anhand der ID und der Schriftfarbe/größe
Label settingsTitle = foodFieldContainer.addChild(new Label(field.getName(), new ElementId("settings-title")));
settingsTitle.setFontSize(48); settingsTitle.setFontSize(48);
settingsTitle.setColor(ColorRGBA.White);
// Text, der auf der Karte steht
Container propertyValuesContainer = foodFieldContainer.addChild(new Container()); Container propertyValuesContainer = foodFieldContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label("„Preis: " + field.getPrice() + " EUR", new ElementId("label-Text"))); 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("", new ElementId("label-Text"))); // Leerzeile
@ -68,29 +79,30 @@ public class FoodFieldCard extends Dialog {
propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", 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))); propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
//TODO eventuell diese Stelle löschen, da nur die BuyCard Kaufen und beenden hat
/*
// Beenden-Button // Beenden-Button
Button quitButton = foodFieldContainer.addChild(new Button("Beenden", new ElementId("button"))); Button quitButton = foodFieldContainer.addChild(new Button("Beenden", new ElementId("button")));
quitButton.setFontSize(32); quitButton.setFontSize(32);
quitButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Kaufen-Button // Kaufen-Button
Button buyButton = foodFieldContainer.addChild(new Button("Kaufen", new ElementId("button"))); Button buyButton = foodFieldContainer.addChild(new Button("Kaufen", new ElementId("button")));
buyButton.setFontSize(32); buyButton.setFontSize(32);
*/ buyButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.getGameLogic().send(new BuyPropertyResponse());
close();
}));
float padding = 10; // Padding around the settingsContainer for the background // Zentriere das Popup
backgroundContainer.setPreferredSize(foodFieldContainer.getPreferredSize().addLocal(padding, padding, 0));
// Zentriere das Menü
foodFieldContainer.setLocalTranslation( foodFieldContainer.setLocalTranslation(
(app.getCamera().getWidth() - foodFieldContainer.getPreferredSize().x) / 2, (app.getCamera().getWidth() - foodFieldContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + foodFieldContainer.getPreferredSize().y) / 2, (app.getCamera().getHeight() + foodFieldContainer.getPreferredSize().y) / 2,
8 8
); );
// Zentriere das Popup
backgroundContainer.setLocalTranslation( backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - foodFieldContainer.getPreferredSize().x - padding) / 2, (app.getCamera().getWidth() - foodFieldContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + foodFieldContainer.getPreferredSize().y+ padding) / 2, (app.getCamera().getHeight() + foodFieldContainer.getPreferredSize().y+ padding) / 2,
@ -101,9 +113,9 @@ public class FoodFieldCard extends Dialog {
} }
/** /**
* Erstellt einen halbtransparenten Hintergrund für das Menü. * Creates a semi-transparent background overlay for the popup.
* *
* @return Geometrie des Overlays * @return the geometry of the overlay
*/ */
private Geometry createOverlayBackground() { private Geometry createOverlayBackground() {
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
@ -117,7 +129,7 @@ public class FoodFieldCard extends Dialog {
} }
/** /**
* Schließt das Menü und entfernt die GUI-Elemente. * Closes the popup and removes its associated GUI elements.
*/ */
@Override @Override
public void close() { public void close() {
@ -127,10 +139,9 @@ public class FoodFieldCard extends Dialog {
super.close(); super.close();
} }
public void setIndex(int index) { /**
this.index = index; * Opens the settings menu when the escape key is pressed.
} */
@Override @Override
public void escape() { public void escape() {
new SettingsMenu(app).open(); new SettingsMenu(app).open();

View File

@ -1,10 +1,7 @@
package pp.monopoly.client.gui.popups; 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.ColorRGBA;
import com.jme3.scene.Geometry; import com.simsilica.lemur.Button;
import com.jme3.scene.shape.Quad;
import com.simsilica.lemur.Container; import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label; import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.QuadBackgroundComponent; import com.simsilica.lemur.component.QuadBackgroundComponent;
@ -12,41 +9,50 @@ import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog; import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp; import pp.monopoly.client.MonopolyApp;
import pp.monopoly.client.gui.SettingsMenu; import pp.monopoly.client.gui.SettingsMenu;
import pp.monopoly.message.client.BuyPropertyResponse;
import pp.monopoly.model.fields.GateField; import pp.monopoly.model.fields.GateField;
import pp.monopoly.notification.Sound;
/** /**
* SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann. * GateFieldCard creates the popup for field information
*/ */
public class GateFieldCard extends Dialog { public class GateFieldCard extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app; private final MonopolyApp app;
private final Geometry overlayBackground;
private final Container gateFieldContainer;
private final Container backgroundContainer;
private int index = 5;
/** 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) { public GateFieldCard(MonopolyApp app) {
super(app.getDialogManager()); super(app.getDialogManager());
this.app = app; this.app = app;
//Generate the corresponfing field //Generate the corresponfing field
int index = app.getGameLogic().getPlayerHandler().getPlayerById(app.getId()).getFieldID();
GateField field = (GateField) app.getGameLogic().getBoardManager().getFieldAtIndex(index); GateField field = (GateField) app.getGameLogic().getBoardManager().getFieldAtIndex(index);
// Halbtransparentes Overlay hinzufügen
overlayBackground = createOverlayBackground();
app.getGuiNode().attachChild(overlayBackground);
// Create the background container // Create the background container
backgroundContainer = new Container(); backgroundContainer = new Container();
backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background backgroundContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); // Darker background
app.getGuiNode().attachChild(backgroundContainer); attachChild(backgroundContainer);
// Hauptcontainer für die Gebäudekarte // Hauptcontainer für die Gebäudekarte
gateFieldContainer = new Container(); gateFieldContainer = new Container();
gateFieldContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f))); gateFieldContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.8657f, 0.8735f, 0.8892f, 1.0f)));
// Titel float padding = 10; // Passt den backgroundContainer an die Größe des gateFieldContainers an
// Die Namen werden dynamisch dem BoardManager entnommen backgroundContainer.setPreferredSize(gateFieldContainer.getPreferredSize().addLocal(padding, padding, 0));
Label gateFieldTitle = gateFieldContainer.addChild(new Label(field.getName(), new ElementId("settings-title")));
// 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.setFontSize(48);
gateFieldTitle.setColor(ColorRGBA.Black); gateFieldTitle.setColor(ColorRGBA.Black);
@ -55,36 +61,41 @@ public class GateFieldCard extends Dialog {
Container propertyValuesContainer = gateFieldContainer.addChild(new Container()); Container propertyValuesContainer = gateFieldContainer.addChild(new Container());
propertyValuesContainer.addChild(new Label("„Preis: " + field.getPrice() + " EUR", new ElementId("label-Text"))); propertyValuesContainer.addChild(new Label("„Preis: " + field.getPrice() + " EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("", new ElementId("label-Text"))); propertyValuesContainer.addChild(new Label("", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Wenn man 1 Bahnhof besitzt: 250 EUR", new ElementId("label-Text"))); propertyValuesContainer.addChild(new Label("Miete: 250 EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Wenn man 2 Bahnhöfe besitzt: 500 EUR", new ElementId("label-Text"))); propertyValuesContainer.addChild(new Label("Wenn man", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Wenn man 3 Bahnhöfe besitzt: 1000 EUR", new ElementId("label-Text"))); propertyValuesContainer.addChild(new Label("2 Bahnhof besitzt: 500 EUR", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("Wenn man 4 Bahnhöfe besitzt: 2000 EUR", new ElementId("label-Text"))); propertyValuesContainer.addChild(new Label("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("", new ElementId("label-Text")));
propertyValuesContainer.addChild(new Label("„Hypothek: " + field.getHypo() + " EUR", 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))); propertyValuesContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.4657f, 0.4735f, 0.4892f, 1.0f)));
//TODO eventuell diese Stelle löschen, da nur die BuyCard Kaufen und beenden hat
/*
// Beenden-Button // Beenden-Button
Button quitButton = foodFieldContainer.addChild(new Button("Beenden", new ElementId("button"))); Button quitButton = gateFieldContainer.addChild(new Button("Beenden", new ElementId("button")));
quitButton.setFontSize(32); quitButton.setFontSize(32);
quitButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
close();
}));
// Kaufen-Button // Kaufen-Button
Button buyButton = foodFieldContainer.addChild(new Button("Kaufen", new ElementId("button"))); Button buyButton = gateFieldContainer.addChild(new Button("Kaufen", new ElementId("button")));
buyButton.setFontSize(32); buyButton.setFontSize(32);
*/ buyButton.addClickCommands(s -> ifTopDialog( () -> {
app.getGameLogic().playSound(Sound.BUTTON);
app.getGameLogic().send(new BuyPropertyResponse());
close();
}));
float padding = 10; // Padding around the settingsContainer for the background // Zentriere das Popup
backgroundContainer.setPreferredSize(gateFieldContainer.getPreferredSize().addLocal(padding, padding, 0));
// Zentriere das Menü
gateFieldContainer.setLocalTranslation( gateFieldContainer.setLocalTranslation(
(app.getCamera().getWidth() - gateFieldContainer.getPreferredSize().x) / 2, (app.getCamera().getWidth() - gateFieldContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + gateFieldContainer.getPreferredSize().y) / 2, (app.getCamera().getHeight() + gateFieldContainer.getPreferredSize().y) / 2,
8 8
); );
// Zentriere das Popup
backgroundContainer.setLocalTranslation( backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - gateFieldContainer.getPreferredSize().x - padding) / 2, (app.getCamera().getWidth() - gateFieldContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + gateFieldContainer.getPreferredSize().y+ padding) / 2, (app.getCamera().getHeight() + gateFieldContainer.getPreferredSize().y+ padding) / 2,
@ -95,36 +106,18 @@ public class GateFieldCard extends Dialog {
} }
/** /**
* Erstellt einen halbtransparenten Hintergrund für das Menü. * Closes the popup and removes its associated GUI elements.
*
* @return Geometrie des Overlays
*/
private Geometry createOverlayBackground() {
Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
Geometry overlay = new Geometry("Overlay", quad);
Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent
material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
overlay.setMaterial(material);
overlay.setLocalTranslation(0, 0, 0);
return overlay;
}
/**
* Schließt das Menü und entfernt die GUI-Elemente.
*/ */
@Override @Override
public void close() { public void close() {
app.getGuiNode().detachChild(gateFieldContainer); // Entferne das Menü app.getGuiNode().detachChild(gateFieldContainer); // Entferne das Menü
app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand app.getGuiNode().detachChild(backgroundContainer); //Entfernt Rand
app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay
super.close(); super.close();
} }
public void setIndex(int index) { /**
this.index = index; * Opens the settings menu when the escape key is pressed.
} */
@Override @Override
public void escape() { public void escape() {
new SettingsMenu(app).open(); new SettingsMenu(app).open();

View File

@ -0,0 +1,132 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.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

@ -0,0 +1,136 @@
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,52 +1,145 @@
package pp.monopoly.client.gui.popups; 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.Button;
import com.simsilica.lemur.Container; import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label; import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.IconComponent; import com.simsilica.lemur.component.IconComponent;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog; import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp; 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 { public class LooserPopUp extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app; 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 NetworkDialog. * Constructs a new WinnerPopUp dialog.
* *
* @param network The NetworkSupport instance to be used for network operations. * @param app The MonopolyApp instance.
*/ */
public LooserPopUp(MonopolyApp app) { public LooserPopUp(MonopolyApp app) {
super(app.getDialogManager()); super(app.getDialogManager());
this.app = app; this.app = app;
initializeDialog();
// 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);
} }
/** /**
* Initializes the dialog with input fields and connection buttons. * Creates a semi-transparent background overlay for the popup.
*
* @return The overlay geometry.
*/ */
private void initializeDialog() { private Geometry createOverlayBackground() {
Container inputContainer = new Container(); Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
Geometry overlay = new Geometry("Overlay", quad);
// Titel und Eingabefelder für Host und Port Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
inputContainer.addChild(new Label("Schade, du hast leider verloren!")); material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent
inputContainer.addChild(new Label("Die nächste Runde wird besser!")); material.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
overlay.setMaterial(material);
Label imageLabel = new Label(""); overlay.setLocalTranslation(0, 0, 0);
IconComponent icon = new IconComponent("Pictures/MonopolyLooser.png"); // Icon mit Textur erstellen return overlay;
icon.setIconScale(1); // Skalierung des Bildes
imageLabel.setIcon(icon);
// Setze das Icon im Label
inputContainer.addChild(imageLabel);
Button cancelButton = inputContainer.addChild(new Button("Spiel beenden"));
cancelButton.addClickCommands(source -> ifTopDialog(app::closeApp));
inputContainer.setLocalTranslation(300, 800, 0);
app.getGuiNode().attachChild(inputContainer);
} }
}
/**
* 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

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

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

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

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

@ -0,0 +1,251 @@
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;
/** Text field to display selected properties. */
private TextField 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("warining-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(ColorRGBA.Orange));
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,
9
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - repayMortageContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + repayMortageContainer.getPreferredSize().y+ padding) / 2,
8
);
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(ColorRGBA.Orange));
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 TextField(""); // Create TextField for displaying selections
selectionDisplay.setPreferredSize(new Vector3f(300, 30, 0));
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

@ -0,0 +1,256 @@
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;
/** Text field to display selected properties. */
private TextField 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("warining-Bold")));
title.setFontSize(48);
title.setColor(ColorRGBA.Black);
//Unterteilund 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");
for (String string : selectedProperties) {
System.out.println(string);
}
msg.setProperties(selectedProperties.stream().map(p -> app.getGameLogic().getBoardManager().getFieldByName(p).getId()).map(p -> (Integer) p).collect(Collectors.toSet()));
for (Integer integer : msg.getProperties()) {
System.out.println("ID des verkaufs: "+integer);
}
app.getGameLogic().send(msg);
close();
}));
// Zentriere das Popup
sellhouseContainer.setLocalTranslation(
(app.getCamera().getWidth() - sellhouseContainer.getPreferredSize().x) / 2,
(app.getCamera().getHeight() + sellhouseContainer.getPreferredSize().y) / 2,
9
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - sellhouseContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + sellhouseContainer.getPreferredSize().y+ padding) / 2,
8
);
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(ColorRGBA.Orange));
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 TextField(""); // Create TextField for displaying selections
selectionDisplay.setPreferredSize(new Vector3f(300, 30, 0));
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

@ -0,0 +1,260 @@
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;
/** Text field to display selected properties. */
private TextField 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("warining-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,
9
);
// Zentriere das Popup
backgroundContainer.setLocalTranslation(
(app.getCamera().getWidth() - takeMortageContainer.getPreferredSize().x - padding) / 2,
(app.getCamera().getHeight() + takeMortageContainer.getPreferredSize().y+ padding) / 2,
8
);
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(ColorRGBA.Orange));
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 TextField(""); // Create TextField for displaying selections
selectionDisplay.setPreferredSize(new Vector3f(300, 30, 0));
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

@ -0,0 +1,139 @@
package pp.monopoly.client.gui.popups;
import com.jme3.material.Material;
import com.jme3.material.RenderState.BlendMode;
import com.jme3.math.ColorRGBA;
import com.jme3.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,51 +1,143 @@
package pp.monopoly.client.gui.popups; 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.Button;
import com.simsilica.lemur.Container; import com.simsilica.lemur.Container;
import com.simsilica.lemur.Label; import com.simsilica.lemur.Label;
import com.simsilica.lemur.component.IconComponent; import com.simsilica.lemur.component.IconComponent;
import com.simsilica.lemur.component.QuadBackgroundComponent;
import com.simsilica.lemur.style.ElementId;
import pp.dialog.Dialog; import pp.dialog.Dialog;
import pp.monopoly.client.MonopolyApp; 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 { public class WinnerPopUp extends Dialog {
/** Reference to the Monopoly application instance. */
private final MonopolyApp app; 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 NetworkDialog. * Constructs a new WinnerPopUp dialog.
* *
* @param app The NetworkSupport instance to be used for network operations. * @param app The MonopolyApp instance.
*/ */
public WinnerPopUp(MonopolyApp app) { public WinnerPopUp(MonopolyApp app) {
super(app.getDialogManager()); super(app.getDialogManager());
this.app = app; this.app = app;
initializeDialog();
// 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);
} }
/** /**
* Initializes the dialog with input fields and connection buttons. * Creates a semi-transparent background overlay for the popup.
*
* @return The overlay geometry.
*/ */
private void initializeDialog() { private Geometry createOverlayBackground() {
Container inputContainer = new Container(); Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight());
Geometry overlay = new Geometry("Overlay", quad);
// Titel und Eingabefelder für Host und Port Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
inputContainer.addChild(new Label("Herlichen Glückwunsch!")); material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent
inputContainer.addChild(new Label("Du,bist der Monopoly Champion!!!")); material.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
overlay.setMaterial(material);
Label imageLabel = new Label(""); overlay.setLocalTranslation(0, 0, 0);
IconComponent icon = new IconComponent("Pictures/MonopolyWinner.png"); // Icon mit Textur erstellen return overlay;
icon.setIconScale(1); // Skalierung des Bildes
imageLabel.setIcon(icon);
// Setze das Icon im Label
inputContainer.addChild(imageLabel);
Button cancelButton = inputContainer.addChild(new Button("Spiel beenden"));
cancelButton.addClickCommands(source -> ifTopDialog(app::closeApp));
inputContainer.setLocalTranslation(300, 800, 0);
app.getGuiNode().attachChild(inputContainer);
} }
}
/**
* 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.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

View File

@ -34,13 +34,13 @@ public class MonopolyConfig extends Config {
* The width of the game map in terms of grid units. * The width of the game map in terms of grid units.
*/ */
@Property("map.width") @Property("map.width")
private int mapWidth = 12; private int mapWidth = 10;
/** /**
* The height of the game map in terms of grid units. * The height of the game map in terms of grid units.
*/ */
@Property("map.height") @Property("map.height")
private int mapHeight = 12; private int mapHeight = 10;
/** /**
* Creates an instance of {@code MonopolyConfig} with default settings. * Creates an instance of {@code MonopolyConfig} with default settings.

View File

@ -1,8 +1,40 @@
package pp.monopoly.game.client; package pp.monopoly.game.client;
public class ActiveState extends ClientState{ import pp.monopoly.game.server.Player;
import pp.monopoly.message.server.NextPlayerTurn;
import pp.monopoly.message.server.ViewAssetsResponse;
/**
* Represents the active client state in the Monopoly game.
* Extends {@link ClientState}.
*/
public class ActiveState extends ClientState {
/**
* Constructs an ActiveState with the specified game logic.
*
* @param logic the client-side game logic associated with this state
* used to manage game interactions and transitions
*/
ActiveState(ClientGameLogic logic) { ActiveState(ClientGameLogic logic) {
super(logic); super(logic);
} }
}
@Override
boolean isTurn() {
return true;
}
@Override
void recivedNextPlayerTurn(NextPlayerTurn msg) {
logic.getBoard().clear();
for (Player player : logic.getPlayerHandler().getPlayers()) {
logic.getBoard().add(player.getFigure());
}
}
@Override
void recivedViewAssetsResponse(ViewAssetsResponse msg) {
}
}

View File

@ -6,14 +6,17 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import pp.monopoly.game.server.Player; import pp.monopoly.game.server.Player;
import pp.monopoly.game.server.PlayerHandler;
import pp.monopoly.message.client.ClientMessage; import pp.monopoly.message.client.ClientMessage;
import pp.monopoly.message.server.BuyPropertyResponse; import pp.monopoly.message.server.BuildInfo;
import pp.monopoly.message.server.BuyPropertyRequest;
import pp.monopoly.message.server.DiceResult; import pp.monopoly.message.server.DiceResult;
import pp.monopoly.message.server.EventDrawCard; import pp.monopoly.message.server.EventDrawCard;
import pp.monopoly.message.server.GameOver; import pp.monopoly.message.server.GameOver;
import pp.monopoly.message.server.GameStart; import pp.monopoly.message.server.GameStart;
import pp.monopoly.message.server.JailEvent; import pp.monopoly.message.server.JailEvent;
import pp.monopoly.message.server.NextPlayerTurn; import pp.monopoly.message.server.NextPlayerTurn;
import pp.monopoly.message.server.NotificationMessage;
import pp.monopoly.message.server.PlayerStatusUpdate; import pp.monopoly.message.server.PlayerStatusUpdate;
import pp.monopoly.message.server.ServerInterpreter; import pp.monopoly.message.server.ServerInterpreter;
import pp.monopoly.message.server.TimeOutWarning; import pp.monopoly.message.server.TimeOutWarning;
@ -21,15 +24,24 @@ import pp.monopoly.message.server.TradeReply;
import pp.monopoly.message.server.TradeRequest; import pp.monopoly.message.server.TradeRequest;
import pp.monopoly.message.server.ViewAssetsResponse; import pp.monopoly.message.server.ViewAssetsResponse;
import pp.monopoly.model.Board; import pp.monopoly.model.Board;
import pp.monopoly.model.IntPoint; import pp.monopoly.model.Hotel;
import pp.monopoly.model.House;
import pp.monopoly.model.TradeHandler;
import pp.monopoly.model.fields.BoardManager; import pp.monopoly.model.fields.BoardManager;
import pp.monopoly.model.fields.BuildingProperty;
import pp.monopoly.notification.ClientStateEvent; import pp.monopoly.notification.ClientStateEvent;
import pp.monopoly.notification.DiceRollEvent;
import pp.monopoly.notification.ButtonStatusEvent;
import pp.monopoly.notification.EventCardEvent;
import pp.monopoly.notification.GameEvent; import pp.monopoly.notification.GameEvent;
import pp.monopoly.notification.GameEventBroker; import pp.monopoly.notification.GameEventBroker;
import pp.monopoly.notification.GameEventListener; import pp.monopoly.notification.GameEventListener;
import pp.monopoly.notification.InfoTextEvent; import pp.monopoly.notification.InfoTextEvent;
import pp.monopoly.notification.ItemAddedEvent;
import pp.monopoly.notification.PopUpEvent;
import pp.monopoly.notification.Sound; import pp.monopoly.notification.Sound;
import pp.monopoly.notification.SoundEvent; import pp.monopoly.notification.SoundEvent;
import pp.monopoly.notification.UpdatePlayerView;
/** /**
* Controls the client-side game logic for Monopoly. * Controls the client-side game logic for Monopoly.
@ -47,15 +59,18 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
private final List<GameEventListener> listeners = new ArrayList<>(); private final List<GameEventListener> listeners = new ArrayList<>();
/** The game board representing the player's current state. */ /** The game board representing the player's current state. */
private Board board; private Board board = new Board(10, 10, this);
/** The current state of the client game logic. */ /** The current state of the client game logic. */
private ClientState state = new LobbyState(this); private ClientState state = new LobbyState(this);
private List<Player> players; private PlayerHandler playerHandler;
private BoardManager boardManager = new BoardManager(); private BoardManager boardManager = new BoardManager();
private TradeHandler tradeHandler;
/** /**
* Constructs a ClientGameLogic with the specified sender object. * Constructs a ClientGameLogic with the specified sender object.
* *
@ -94,8 +109,8 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
state.entry(); state.entry();
} }
public List<Player> getPlayers() { public PlayerHandler getPlayerHandler() {
return players; return playerHandler;
} }
/** /**
@ -107,13 +122,8 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
return board; return board;
} }
/** public TradeHandler getTradeHandler() {
* Moves the preview figure to the specified position. return tradeHandler;
*
* @param pos the new position for the preview figure
*/
public void movePreview(IntPoint pos) {
state.movePreview(pos);
} }
/** /**
@ -144,7 +154,6 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
LOGGER.log(Level.ERROR, "trying to send {0} with sender==null", msg); //NON-NLS LOGGER.log(Level.ERROR, "trying to send {0} with sender==null", msg); //NON-NLS
} else { } else {
clientSender.send(msg); clientSender.send(msg);
System.out.println("Message gesendet");
} }
} }
@ -191,157 +200,125 @@ public class ClientGameLogic implements ServerInterpreter, GameEventBroker {
state.update(delta); state.update(delta);
} }
/** public boolean isTurn() {
* Handles the response for buying a property. return state.isTurn();
*
* @param msg the message containing the buy property response
*/
@Override
public void received(BuyPropertyResponse msg) {
if (msg.isSuccessful()) {
setInfoText("You successfully bought " + msg.getPropertyName() + "!");
playSound(Sound.MONEY_LOST);
} else {
setInfoText("Unable to buy " + msg.getPropertyName() + ". Reason: " + msg.getReason());
}
} }
/**
* Handles the result of a dice roll.
*
* @param msg the message containing the dice roll result
*/
@Override @Override
public void received(DiceResult msg) { public void received(DiceResult msg) {
setInfoText("You rolled a " + msg.calcTotal() + "!");
//Set the dice images
playSound(Sound.DICE_ROLL); playSound(Sound.DICE_ROLL);
notifyListeners(new DiceRollEvent(msg.getRollResult().get(0), msg.getRollResult().get(1)));
} }
/**
* Handles drawing an event card.
*
* @param msg the message containing the drawn card details
*/
@Override @Override
public void received(EventDrawCard msg) { public void received(EventDrawCard msg) {
setInfoText("Event card drawn: " + msg.getCardDescription()); notifyListeners(new EventCardEvent(msg.getCardDescription()));
// Kartenlogik
playSound(Sound.EVENT_CARD);
} }
/**
* Handles the game over message.
*
* @param msg the message containing game over details
*/
@Override @Override
public void received(GameOver msg) { public void received(GameOver msg) {
if (msg.isWinner()) { if (msg.isWinner()) {
setInfoText("Congratulations! You have won the game!"); notifyListeners(new PopUpEvent("Winner", msg));
//Winner popup
playSound(Sound.WINNER); playSound(Sound.WINNER);
} else { } else {
setInfoText("Game over. Better luck next time!"); notifyListeners(new PopUpEvent("Looser", msg));
// Looser popup
playSound(Sound.LOSER); playSound(Sound.LOSER);
} }
} }
/**
* Handles the start of the game.
*
* @param msg the game start message
*/
@Override @Override
public void received(GameStart msg) { public void received(GameStart msg) {
players = msg.getPlayers(); playerHandler = msg.getPlayerHandler();
setInfoText("The game has started! Good luck!");
setState(new WaitForTurnState(this)); setState(new WaitForTurnState(this));
for (Player player : playerHandler.getPlayers()) {
board.add(player.getFigure());
}
notifyListeners(new ButtonStatusEvent(false));
notifyListeners(new UpdatePlayerView());
} }
/**
* Handles jail-related events.
*
* @param msg the message containing jail event details
*/
@Override @Override
public void received(JailEvent msg) { public void received(JailEvent msg) {
if (msg.isGoingToJail()) { if (msg.isGoingToJail()) {
setInfoText("You are sent to jail!");
playSound(Sound.GULAG); playSound(Sound.GULAG);
} else { notifyListeners(new PopUpEvent("goingToJail", msg));
setInfoText("You are out of jail!");
} }
} }
/**
* Updates the status of a player.
*
* @param msg the message containing player status update details
*/
@Override @Override
public void received(PlayerStatusUpdate msg) { public void received(PlayerStatusUpdate msg) {
playerHandler = msg.getPlayerHandler();
setInfoText("Player " + msg.getPlayerName() + " status updated: " + msg.getStatus()); System.out.println("Update Player");
notifyListeners(new UpdatePlayerView());
} }
/**
* Handles timeout warnings.
*
* @param msg the message containing timeout warning details
*/
@Override @Override
public void received(TimeOutWarning msg) { public void received(TimeOutWarning msg) {
setInfoText("Warning! Time is running out. You have " + msg.getRemainingTime() + " seconds left."); notifyListeners(new PopUpEvent("timeout", msg));
} }
/**
* Displays the player's assets in response to a server query.
*
* @param msg the message containing the player's assets
*/
@Override @Override
public void received(ViewAssetsResponse msg) { public void received(ViewAssetsResponse msg) {
setInfoText("Your current assets are being displayed."); boardManager = msg.getboard();
} }
/**
* Handles trade replies from other players.
*
* @param msg the message containing the trade reply
*/
@Override @Override
public void received(TradeReply msg) { public void received(TradeReply msg) {
if (msg.getTradeHandler().getStatus()) { if (msg.isAccepted()) {
setInfoText("Trade accepted by " + msg.getTradeHandler().getReceiver().getName() + ".");
playSound(Sound.TRADE_ACCEPTED); playSound(Sound.TRADE_ACCEPTED);
notifyListeners(new PopUpEvent("tradepos", msg));
} else { } else {
setInfoText("Trade rejected by " + msg.getTradeHandler().getReceiver().getName() + ".");
playSound(Sound.TRADE_REJECTED); playSound(Sound.TRADE_REJECTED);
notifyListeners(new PopUpEvent("tradeneg", msg));
} }
} }
/**
* Handles trade requests from other players.
*
* @param msg the message containing the trade request details
*/
@Override @Override
public void received(TradeRequest msg) { public void received(TradeRequest msg) {
setInfoText("Trade offer received from " + msg.getTradeHandler().getSender().getName()); tradeHandler = msg.getTradeHandler();
// playSound(Sound.TRADE_REQUEST); no sound effect notifyListeners(new PopUpEvent("tradeRequest", msg));
// notifyListeners();
} }
/**
* Handles the transition to the next player's turn.
*
* @param msg the message indicating it's the next player's turn
*/
@Override @Override
public void received(NextPlayerTurn msg) { public void received(NextPlayerTurn msg) {
setInfoText("It's your turn!");
setState(new ActiveState(this)); setState(new ActiveState(this));
notifyListeners(new ButtonStatusEvent(true));
} }
@Override
public void received(BuyPropertyRequest msg) {
notifyListeners(new PopUpEvent("Buy", msg));
}
@Override
public void received(NotificationMessage msg) {
if (msg.getKeyWord().equals("rent")) {
notifyListeners(new PopUpEvent("rent", msg));
playSound(Sound.MONEY_LOST);
} else if (msg.getKeyWord().equals("jailpay")) {
playSound(Sound.MONEY_LOST);
notifyListeners(new PopUpEvent(msg.getKeyWord(), msg));
} else if(msg.getKeyWord().equals("NoMoneyWarning")) {
notifyListeners(new PopUpEvent("NoMoneyWarning", msg));
} else if (msg.getKeyWord().equals("jailtryagain")) {
notifyListeners(new PopUpEvent("jailtryagain", msg));
} else if(msg.getKeyWord().equals("ReceivedRent")) {
playSound(Sound.MONEY_COLLECTED);
notifyListeners(new PopUpEvent("ReceivedRent", msg));
}
}
@Override
public void received(BuildInfo msg) {
System.out.println("TRIGGER BUILD INFO");
if (msg.isAdded()) {
BuildingProperty property = ((BuildingProperty)boardManager.getFieldAtIndex(msg.getId()));
if (property.getHotel() == 1 ) {
board.add(new Hotel(property.getId()));
} else {
board.add(new House( property.getHouses(), property.getId()));
}
}
}
} }

View File

@ -6,24 +6,29 @@
//////////////////////////////////////// ////////////////////////////////////////
package pp.monopoly.game.client; package pp.monopoly.game.client;
import pp.monopoly.model.IntPoint;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.lang.System.Logger.Level; import java.lang.System.Logger.Level;
import pp.monopoly.message.server.GameStart;
import pp.monopoly.message.server.NextPlayerTurn;
import pp.monopoly.message.server.NotificationMessage;
import pp.monopoly.message.server.PlayerStatusUpdate;
import pp.monopoly.message.server.ViewAssetsResponse;
/** /**
* Defines the behavior and state transitions for the client-side game logic. * Defines the behavior and state transitions for the client-side game logic in Monopoly.
* Different states of the game logic implement this interface to handle various game events and actions. * Different states of the game logic implement this abstract class to handle various game events and actions.
*/ */
abstract class ClientState { abstract class ClientState {
/** /**
* The game logic object. * The game logic object managing the client-side state.
*/ */
final ClientGameLogic logic; final ClientGameLogic logic;
/** /**
* Constructs a client state of the specified game logic. * Constructs a client state for the specified game logic.
* *
* @param logic the game logic * @param logic the game logic
*/ */
@ -49,31 +54,53 @@ abstract class ClientState {
} }
/** /**
* Checks if the battle state should be shown. * Checks if the player's turn should be shown in the current state.
* *
* @return true if the battle state should be shown, false otherwise * @return true if the player's turn should be shown, false otherwise
*/ */
boolean showTurn() { boolean isTurn() {
return false; return false;
} }
/** /**
* Moves the preview figure to the specified position. * Starts the battle based on the server message.
* *
* @param pos the new position for the preview figure * @param msg the message indicating whose turn it is to shoot
*/ */
void movePreview(IntPoint pos) { void receivedGameStart(GameStart msg) {
ClientGameLogic.LOGGER.log(Level.DEBUG, "movePreview has no effect in {0}", getName()); //NON-NLS ClientGameLogic.LOGGER.log(Level.ERROR, "receivedGameStart not allowed in {0}", getName()); //NON-NLS
} }
/** /**
* Loads a map from the specified file. * Updateds the view based on the new player status.
* *
* @param file the file to load the map from * @param msg the message containing the new player status
* @throws IOException if the map cannot be loaded in the current state
*/ */
void loadMap(File file) throws IOException { void recivedPlayerStatusUpdate(PlayerStatusUpdate msg) {
throw new IOException("You are not allowed to load a map in this state of the game"); ClientGameLogic.LOGGER.log(Level.ERROR, "recivedPlayerStatusUpdate not allowed in {0}", getName()); //NON-NLS
}
void recivedNextPlayerTurn(NextPlayerTurn msg) {
ClientGameLogic.LOGGER.log(Level.ERROR, "recivedNextPlayerTurn not allowed in {0}", getName()); //NON-NLS
}
void recivedNotificationMessage(NotificationMessage msg) {
ClientGameLogic.LOGGER.log(Level.ERROR, "recivedNotificationMessage not allowed in {0}", getName()); //NON-NLS
}
void recivedViewAssetsResponse(ViewAssetsResponse msg) {
ClientGameLogic.LOGGER.log(Level.ERROR, "recivedViewAssetsResponse not allowed in {0}", getName()); //NON-NLS
}
/**
* Loads a game configuration from the specified file.
*
* @param file the file to load the game configuration from
* @throws IOException if the configuration cannot be loaded in the current state
*/
void loadGameConfig(File file) throws IOException {
throw new IOException("You are not allowed to load a game configuration in this state of the game.");
} }
/** /**
@ -81,5 +108,7 @@ abstract class ClientState {
* *
* @param delta time in seconds since the last update call * @param delta time in seconds since the last update call
*/ */
void update(float delta) { /* do nothing by default */ } void update(float delta) {
// Default implementation does nothing
}
} }

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