diff --git a/.gitignore b/.gitignore index a3152bd..e252cad 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ build # VSC bin +.vscode # IntelliJ *.iml diff --git a/Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy b/Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy index 68a0f6e..3ad66c1 100644 --- a/Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy +++ b/Projekte/jme-common/src/main/resources/Interface/Lemur/pp-styles.groovy @@ -20,8 +20,6 @@ def sliderBgColor = color(0.5, 0.75, 0.75, 1) def gradientColor = color(0.5, 0.75, 0.85, 0.5) def tabbuttonEnabledColor = color(0.4, 0.45, 0.5, 1) def solidWhiteBackground = new QuadBackgroundComponent(color(1, 1, 1, 1)) // Solid white -def greyBackground = color(0.8, 0.8, 0.8, 1) // Grey background color -def redBorderColor = color(1, 0, 0, 1) // Red border color diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java index d92f348..02f926d 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/MonopolyApp.java @@ -9,7 +9,6 @@ import com.jme3.font.BitmapText; import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.KeyTrigger; - import com.jme3.system.AppSettings; import com.simsilica.lemur.GuiGlobals; import com.simsilica.lemur.Label; @@ -25,7 +24,6 @@ import pp.monopoly.game.client.MonopolyClient; import pp.monopoly.game.client.ServerConnection; import pp.monopoly.notification.GameEventListener; import pp.monopoly.notification.InfoTextEvent; -import pp.monopoly.server.MonopolyServer; public class MonopolyApp extends SimpleApplication implements MonopolyClient, GameEventListener { private BitmapText topText; @@ -40,8 +38,6 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga private TestWorld testWorld; private boolean isSettingsMenuOpen = false; private boolean inputBlocked = false; - private MonopolyServer monopolyServer; - /** * Path to the styles script for GUI elements. */ @@ -98,10 +94,6 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga StartMenu.createStartMenu(this); } - - - - private void setupGui() { BitmapFont normalFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); topText = new BitmapText(normalFont); @@ -116,7 +108,7 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga inputManager.addListener(escapeListener, "ESC"); } - void handleEscape(boolean isPressed) { + private void handleEscape(boolean isPressed) { if (isPressed) { if (settingsMenu != null && isSettingsMenuOpen) { // Schließe das SettingsMenu @@ -215,21 +207,4 @@ public class MonopolyApp extends SimpleApplication implements MonopolyClient, Ga guiNode.detachAllChildren(); // Entferne die GUI StartMenu.createStartMenu(this); // Zeige das Startmenü erneut } - - /** - * Startet den Server in einem neuen Thread. - */ - public void startServer() { - new Thread(() -> { - try { - monopolyServer = new MonopolyServer(); // Erstelle Serverinstanz - } catch (Exception e) { - e.printStackTrace(); - } - }).start(); - } - - public MonopolyServer getMonopolyServer() { - return monopolyServer; - } } diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java index 302bd55..e1796cc 100644 --- a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/StartMenu.java @@ -1,6 +1,7 @@ package pp.monopoly.client; import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.shape.Quad; diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CameraController.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CameraController.java new file mode 100644 index 0000000..314ebb6 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CameraController.java @@ -0,0 +1,59 @@ +package pp.monopoly.client.gui; + +import com.jme3.math.FastMath; +import com.jme3.math.Vector3f; +import com.jme3.renderer.Camera; + +/** + * Steuert die Kamerabewegung in der Szene. + */ +public class CameraController { + private final Camera camera; + private final Vector3f center; // Fokuspunkt der Kamera + private final float radius; // Radius der Kreisbewegung + private final float height; // Höhe der Kamera über dem Spielfeld + private final float speed; // Geschwindigkeit der Kamerabewegung + private float angle; // Aktueller Winkel in der Kreisbewegung + + /** + * Konstruktor für den CameraController. + * + * @param camera Die Kamera, die gesteuert werden soll + * @param center Der Mittelpunkt der Kreisbewegung (Fokuspunkt) + * @param radius Der Radius der Kreisbewegung + * @param height Die Höhe der Kamera über dem Fokuspunkt + * @param speed Die Geschwindigkeit der Kamerabewegung + */ + public CameraController(Camera camera, Vector3f center, float radius, float height, float speed) { + this.camera = camera; + this.center = center; + this.radius = radius; + this.height = height; + this.speed = speed; + this.angle = 0; // Starte bei Winkel 0 + } + + /** + * Aktualisiert die Kameraposition und -ausrichtung. + * + * @param tpf Zeit pro Frame + */ + public void update(float tpf) { + // Aktualisiere den Winkel basierend auf der Geschwindigkeit + angle += speed * tpf; + if (angle >= FastMath.TWO_PI) { + angle -= FastMath.TWO_PI; // Winkel zurücksetzen, um Überläufe zu vermeiden + } + + // Berechne die neue Position der Kamera + float x = center.x + radius * FastMath.cos(angle); + float z = center.z + radius * FastMath.sin(angle); + float y = center.y + height; + + // Setze die Kameraposition + camera.setLocation(new Vector3f(x, y, z)); + + // Lasse die Kamera auf den Fokuspunkt blicken + camera.lookAt(center, Vector3f.UNIT_Y); + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java new file mode 100644 index 0000000..5472da4 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/CreateGameMenu.java @@ -0,0 +1,121 @@ +package pp.monopoly.client.gui; + +import com.jme3.material.Material; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Quad; +import com.jme3.texture.Texture; +import com.simsilica.lemur.Axis; +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.TextField; +import com.simsilica.lemur.component.SpringGridLayout; + +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.client.StartMenu; + +/** + * CreateGameMenu class represents the menu for creating a new game. + */ +public class CreateGameMenu { + private final MonopolyApp app; + private final Container menuContainer; + private Geometry background; + + /** + * Konstruktor für das CreateGameMenu. + * + * @param app Die Hauptanwendung (MonopolyApp) + */ + public CreateGameMenu(MonopolyApp app) { + this.app = app; + + // Hintergrundbild laden und hinzufügen + addBackgroundImage(); + + // Hauptcontainer für das Menü + menuContainer = new Container(new SpringGridLayout(Axis.Y, Axis.X)); + menuContainer.setPreferredSize(new Vector3f(600, 400, 0)); // Feste Größe des Containers + + // Titel + Label title = menuContainer.addChild(new Label("Neues Spiel")); + title.setFontSize(48); + + // Eingabefelder-Container + Container inputContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X))); + inputContainer.setPreferredSize(new Vector3f(200, 150, 0)); // Eingabefelder nicht ganz so breit + inputContainer.setLocalTranslation(20, 0, 0); // Abstand vom Rand + + inputContainer.addChild(new Label("Server-Adresse:")); + TextField serverAddressField = inputContainer.addChild(new TextField("localhost")); + serverAddressField.setPreferredWidth(400); // Breite des Textfelds + + inputContainer.addChild(new Label("Port:")); + TextField portField = inputContainer.addChild(new TextField("42069")); + portField.setPreferredWidth(400); // Breite des Textfelds + + // Button-Container + Container buttonContainer = menuContainer.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y))); + buttonContainer.setPreferredSize(new Vector3f(400, 50, 0)); + buttonContainer.setLocalTranslation(20, 0, 0); // Abstand vom Rand + + // "Abbrechen"-Button + Button cancelButton = buttonContainer.addChild(new Button("Abbrechen")); + cancelButton.setPreferredSize(new Vector3f(120, 40, 0)); + cancelButton.addClickCommands(source -> goBackToStartMenu()); + + // "Spiel hosten"-Button + Button hostButton = buttonContainer.addChild(new Button("Spiel hosten")); + hostButton.setPreferredSize(new Vector3f(120, 40, 0)); + hostButton.addClickCommands(source -> { + closeCreateGameMenu(); // Schließt das Menü + app.startTestWorld(); // Starte die TestWorld im selben Fenster + }); + + // "Beitreten"-Button + Button joinButton = buttonContainer.addChild(new Button("Beitreten")); + joinButton.setPreferredSize(new Vector3f(120, 40, 0)); + // Placeholder für die Beitrittslogik + + // Zentrierung des Containers + menuContainer.setLocalTranslation( + (app.getCamera().getWidth() - menuContainer.getPreferredSize().x) / 2, + (app.getCamera().getHeight() + menuContainer.getPreferredSize().y) / 2, + 1 // Höhere Z-Ebene für den Vordergrund + ); + + app.getGuiNode().attachChild(menuContainer); + } + + /** + * Lädt das Hintergrundbild und fügt es als geometrische Ebene hinzu. + */ + private void addBackgroundImage() { + Texture backgroundImage = app.getAssetManager().loadTexture("Pictures/unibw-Bib2.png"); + Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); + background = new Geometry("Background", quad); + Material backgroundMaterial = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + backgroundMaterial.setTexture("ColorMap", backgroundImage); + background.setMaterial(backgroundMaterial); + background.setLocalTranslation(0, 0, -1); // Hintergrundebene + + app.getGuiNode().attachChild(background); + } + + /** + * Geht zum Startmenü zurück, wenn "Abbrechen" angeklickt wird. + */ + private void goBackToStartMenu() { + closeCreateGameMenu(); // Schließt das Menü + StartMenu.createStartMenu(app); // Zeige das Startmenü + } + + /** + * Entfernt das CreateGameMenu und dessen Hintergrund. + */ + private void closeCreateGameMenu() { + app.getGuiNode().detachChild(menuContainer); // Entfernt den Menü-Container + app.getGuiNode().detachChild(background); // Entfernt das Hintergrundbild + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java new file mode 100644 index 0000000..637f7ab --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/GameBoardSynchronizer.java @@ -0,0 +1,124 @@ +//////////////////////////////////////// +// Programming project code +// UniBw M, 2022, 2023, 2024 +// www.unibw.de/inf2 +// (c) Mark Minas (mark.minas@unibw.de) +//////////////////////////////////////// + +package pp.monopoly.client.gui; + +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import com.jme3.renderer.queue.RenderQueue.ShadowMode; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.shape.Box; + +import pp.monopoly.client.MonopolyApp; +import pp.monopoly.game.server.PlayerColor; +import pp.monopoly.model.Board; +import pp.monopoly.model.Figure; +import pp.monopoly.model.Rotation; +import static pp.util.FloatMath.HALF_PI; +import static pp.util.FloatMath.PI; + +/** + * The {@code GameBoardSynchronizer} class is responsible for synchronizing the graphical + * representation of the ships and shots on the sea map with the underlying data model. + * It extends the {@link BoardSynchronizer} to provide specific synchronization + * logic for the sea map. + */ +class GameBoardSynchronizer extends BoardSynchronizer { + private static final String UNSHADED = "Common/MatDefs/Misc/Unshaded.j3md"; //NON-NLS + private static final String LIGHTING = "Common/MatDefs/Light/Lighting.j3md"; + private static final String COLOR = "Color"; //NON-NLS + private static final String FIGURE = "figure"; //NON-NLS + + private final MonopolyApp app; + private final ParticleEffectFactory particleFactory; + + /** + * Constructs a {@code GameBoardSynchronizer} object with the specified application, root node, and ship map. + * + * @param app the Battleship application + * @param root the root node to which graphical elements will be attached + * @param map the ship map containing the ships and shots + */ + public GameBoardSynchronizer(MonopolyApp app, Node root, Board board) { + super(board, root); + this.app = app; + this.particleFactory = new ParticleEffectFactory(app); + addExisting(); + } + + /** + * Visits a {@link Battleship} and creates a graphical representation of it. + * The representation is either a 3D model or a simple box depending on the + * type of battleship. + * + * @param ship the battleship to be represented + * @return the node containing the graphical representation of the battleship + */ + public Spatial visit(Figure figure) { + final Node node = new Node(FIGURE); + node.attachChild(createBox(figure)); + // compute the center of the ship in world coordinates + final float x = 1; + final float z = 1; + node.setLocalTranslation(x, 0f, z); + return node; + } + + /** + * Creates a simple box to represent a battleship that is not of the "King George V" type. + * + * @param ship the battleship to be represented + * @return the geometry representing the battleship as a box + */ + private Spatial createBox(Figure figure) { + final Box box = new Box(0.5f * (figure.getMaxY() - figure.getMinY()) + 0.3f, + 0.3f, + 0.5f * (figure.getMaxX() - figure.getMinX()) + 0.3f); + final Geometry geometry = new Geometry(FIGURE, box); + geometry.setMaterial(createColoredMaterial(PlayerColor.PINK.getColor())); + geometry.setShadowMode(ShadowMode.CastAndReceive); + + return geometry; + } + + /** + * Creates a new {@link Material} with the specified color. + * If the color includes transparency (i.e., alpha value less than 1), + * the material's render state is set to use alpha blending, allowing for + * semi-transparent rendering. + * + * @param color the {@link ColorRGBA} to be applied to the material. If the alpha value + * of the color is less than 1, the material will support transparency. + * @return a {@link Material} instance configured with the specified color and, + * if necessary, alpha blending enabled. + */ + private Material createColoredMaterial(ColorRGBA color) { + final Material material = new Material(app.getAssetManager(), UNSHADED); + if (color.getAlpha() < 1f) + material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + material.setColor(COLOR, color); + return material; + } + + /** + * Calculates the rotation angle for the specified rotation. + * + * @param rot the rotation of the battleship + * @return the rotation angle in radians + */ + private static float calculateRotationAngle(Rotation rot) { + return switch (rot) { + case RIGHT -> HALF_PI; + case DOWN -> 0f; + case LEFT -> -HALF_PI; + case UP -> PI; + }; + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/ParticleEffectFactory.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/ParticleEffectFactory.java new file mode 100644 index 0000000..790400c --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/ParticleEffectFactory.java @@ -0,0 +1,22 @@ +package pp.monopoly.client.gui; + +import com.jme3.effect.ParticleMesh.Type; + +import pp.monopoly.client.MonopolyApp; + +/** + * Factory class responsible for creating particle effects used in the game. + * This centralizes the creation of various types of particle emitters. + */ +public class ParticleEffectFactory { + private static final int COUNT_FACTOR = 1; + private static final float COUNT_FACTOR_F = 1f; + private static final boolean POINT_SPRITE = true; + private static final Type EMITTER_TYPE = POINT_SPRITE ? Type.Point : Type.Triangle; + + private final MonopolyApp app; + + ParticleEffectFactory(MonopolyApp app) { + this.app = app; + } +} \ No newline at end of file diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SettingsMenu.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SettingsMenu.java new file mode 100644 index 0000000..6997c45 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/SettingsMenu.java @@ -0,0 +1,100 @@ +package pp.monopoly.client.gui; + +import com.jme3.material.Material; +import com.jme3.material.RenderState.BlendMode; +import com.jme3.math.ColorRGBA; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Quad; +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Checkbox; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.Label; +import com.simsilica.lemur.Slider; +import com.simsilica.lemur.component.QuadBackgroundComponent; +import com.simsilica.lemur.style.ElementId; + +import pp.dialog.Dialog; +import pp.monopoly.client.MonopolyApp; + +/** + * SettingsMenu ist ein Overlay-Menü, das durch ESC aufgerufen werden kann. + */ +public class SettingsMenu extends Dialog { + private final MonopolyApp app; + private final Geometry overlayBackground; + private final Container settingsContainer; + + public SettingsMenu(MonopolyApp app) { + super(app.getDialogManager()); + this.app = app; + + // Halbtransparentes Overlay hinzufügen + overlayBackground = createOverlayBackground(); + app.getGuiNode().attachChild(overlayBackground); + + // Hauptcontainer für das Menü + settingsContainer = new Container(); + settingsContainer.setBackground(new QuadBackgroundComponent(new ColorRGBA(0.1f, 0.1f, 0.1f, 0.9f))); + + // Titel + Label settingsTitle = settingsContainer.addChild(new Label("Einstellungen", new ElementId("settings-title"))); + settingsTitle.setFontSize(48); + + // Effekt-Sound: Slider und Checkbox + Container effectSoundContainer = settingsContainer.addChild(new Container()); + effectSoundContainer.addChild(new Label("Effekt Sound", new ElementId("label"))); + effectSoundContainer.addChild(new Slider()); + effectSoundContainer.addChild(new Checkbox("Aktivieren")).setChecked(true); + + // Hintergrundmusik: Slider und Checkbox + Container backgroundMusicContainer = settingsContainer.addChild(new Container()); + backgroundMusicContainer.addChild(new Label("Hintergrund Musik", new ElementId("label"))); + backgroundMusicContainer.addChild(new Slider()); + backgroundMusicContainer.addChild(new Checkbox("Aktivieren")).setChecked(true); + + // Beenden-Button + Button quitButton = settingsContainer.addChild(new Button("Beenden", new ElementId("menu-button"))); + quitButton.setFontSize(32); + quitButton.addClickCommands(source -> app.stop()); + + // Zentriere das Menü + settingsContainer.setLocalTranslation( + (app.getCamera().getWidth() - settingsContainer.getPreferredSize().x) / 2, + (app.getCamera().getHeight() + settingsContainer.getPreferredSize().y) / 2, + 1 + ); + + app.getGuiNode().attachChild(settingsContainer); + } + + /** + * Erstellt einen halbtransparenten Hintergrund für das Menü. + * + * @return Geometrie des Overlays + */ + private Geometry createOverlayBackground() { + Quad quad = new Quad(app.getCamera().getWidth(), app.getCamera().getHeight()); + Geometry overlay = new Geometry("Overlay", quad); + Material material = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + material.setColor("Color", new ColorRGBA(0, 0, 0, 0.5f)); // Halbtransparent + material.getAdditionalRenderState().setBlendMode(BlendMode.Alpha); + overlay.setMaterial(material); + overlay.setLocalTranslation(0, 0, 0); + return overlay; + } + + /** + * Schließt das Menü und entfernt die GUI-Elemente. + */ + @Override + public void close() { + System.out.println("Schließe SettingsMenu..."); // Debugging-Ausgabe + app.getGuiNode().detachChild(settingsContainer); // Entferne das Menü + app.getGuiNode().detachChild(overlayBackground); // Entferne das Overlay + app.setSettingsMenuOpen(false); // Menü als geschlossen markieren + app.unblockInputs(); // Eingaben wieder aktivieren + System.out.println("SettingsMenu geschlossen."); // Debugging-Ausgabe + } + + +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/TestWorld.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/TestWorld.java new file mode 100644 index 0000000..50a2a23 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/TestWorld.java @@ -0,0 +1,107 @@ +package pp.monopoly.client.gui; + +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.shape.Box; +import com.jme3.texture.Texture; + +import pp.monopoly.client.MonopolyApp; + +/** + * TestWorld zeigt eine einfache Szene mit einem texturierten Quadrat. + * Die Kamera wird durch den CameraController gesteuert. + */ +public class TestWorld { + + private final MonopolyApp app; + private CameraController cameraController; // Steuert die Kamera + private Geometry cube; // Spielfigur + + /** + * Konstruktor für TestWorld. + * + * @param app Die Hauptanwendung (MonopolyApp) + */ + public TestWorld(MonopolyApp app) { + this.app = app; + } + + /** + * Initialisiert die Szene und startet die Kamerabewegung. + */ + public void initializeScene() { + app.getGuiNode().detachAllChildren(); // Entferne GUI + app.getRootNode().detachAllChildren(); // Entferne andere Szenenobjekte + + setSkyColor(); // Setze den Himmel auf hellblau + createBoard(); // Erstelle das Spielfeld + createCube(); // Füge den Würfel hinzu + + // Erstelle den CameraController + cameraController = new CameraController( + app.getCamera(), // Die Kamera der App + Vector3f.ZERO, // Fokus auf die Mitte des Spielfelds + 5, // Radius des Kreises + 3, // Höhe der Kamera + 0.5f // Geschwindigkeit der Bewegung + ); + + // Füge die Toolbar hinzu + new Toolbar(app, cube); + } + + /** + * Aktualisiert die Kameraposition. + * + * @param tpf Zeit pro Frame + */ + public void update(float tpf) { + if (cameraController != null) { + cameraController.update(tpf); + } + } + + /** + * Setzt die Hintergrundfarbe der Szene auf hellblau. + */ + private void setSkyColor() { + app.getViewPort().setBackgroundColor(new ColorRGBA(0.5f, 0.7f, 1.0f, 1.0f)); // Hellblauer Himmel + } + + /** + * Erstelle das Spielfeld. + */ + private void createBoard() { + // Erstelle ein Quadrat + Box box = new Box(1, 0.01f, 1); // Dünnes Quadrat für die Textur + Geometry geom = new Geometry("Board", box); + + // Setze das Material mit Textur + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + Texture texture = app.getAssetManager().loadTexture("Pictures/board.png"); + mat.setTexture("ColorMap", texture); + geom.setMaterial(mat); + + app.getRootNode().attachChild(geom); + } + + /** + * Erstellt den Würfel (Spielfigur) in der Szene. + */ + private void createCube() { + Box box = new Box(0.05f, 0.05f, 0.05f); // Kleinere Größe für Spielfigur + cube = new Geometry("Cube", box); + + // Setze das Material für den Würfel + Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md"); + mat.setColor("Color", ColorRGBA.Blue); // Blau gefärbter Würfel + cube.setMaterial(mat); + + // Setze den Startpunkt des Würfels + cube.setLocalTranslation(0.8999999f, 0.1f, -0.9f); + + app.getRootNode().attachChild(cube); + } +} diff --git a/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar.java b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar.java new file mode 100644 index 0000000..5a9a3f0 --- /dev/null +++ b/Projekte/monopoly/client/src/main/java/pp/monopoly/client/gui/Toolbar.java @@ -0,0 +1,168 @@ +package pp.monopoly.client.gui; + +import java.util.Random; + +import com.jme3.font.BitmapText; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.simsilica.lemur.Axis; +import com.simsilica.lemur.Button; +import com.simsilica.lemur.Container; +import com.simsilica.lemur.component.SpringGridLayout; + +import pp.monopoly.client.MonopolyApp; + +/** + * Toolbar Klasse, die am unteren Rand der Szene angezeigt wird. + * Die Buttons bewegen den Würfel auf dem Spielfeld. + */ +public class Toolbar { + + private final MonopolyApp app; + private final Container toolbarContainer; + private final Geometry cube; // Referenz auf den Würfel + private final BitmapText positionText; // Anzeige für die aktuelle Position + private final float boardLimit = 0.95f; // Grenzen des Bretts + private final float stepSize = 0.18f; // Schrittgröße pro Bewegung + private int currentPosition = 0; // Aktuelle Position auf dem Spielfeld + private final int positionsPerSide = 10; // Anzahl der Positionen pro Seite + private final Random random = new Random(); // Zufallsgenerator für den Würfelwurf + + /** + * Konstruktor für die Toolbar. + * + * @param app Die Hauptanwendung (MonopolyApp) + * @param cube Der Würfel, der bewegt werden soll + */ + public Toolbar(MonopolyApp app, Geometry cube) { + this.app = app; + this.cube = cube; + + // Erstelle die Toolbar + toolbarContainer = new Container(new SpringGridLayout(Axis.X, Axis.Y)); + + // Setze die Position am unteren Rand und die Breite + toolbarContainer.setLocalTranslation( + 0, // Links bündig + 100, // Höhe über dem unteren Rand + 0 // Z-Ebene + ); + toolbarContainer.setPreferredSize(new Vector3f(app.getCamera().getWidth(), 100, 0)); // Volle Breite + + // Füge Buttons zur Toolbar hinzu + initializeButtons(); + + // Füge die Toolbar zur GUI hinzu + app.getGuiNode().attachChild(toolbarContainer); + + // Erstelle die Position-Anzeige + positionText = createPositionDisplay(); + updatePositionDisplay(); // Initialisiere die Anzeige mit der Startposition + } + + /** + * Initialisiert die Buttons in der Toolbar. + */ + private void initializeButtons() { + addButton("Vorwärts", 1); // Bewegung nach vorne + addButton("Rückwärts", -1); // Bewegung nach hinten + addDiceRollButton(); // Würfel-Button + } + + /** + * Fügt einen Button mit einer Bewegung hinzu. + * + * @param label Der Text des Buttons + * @param step Schrittweite (+1 für vorwärts, -1 für rückwärts) + */ + private void addButton(String label, int step) { + Button button = new Button(label); + button.setPreferredSize(new Vector3f(150, 50, 0)); // Größe der Buttons + button.addClickCommands(source -> moveCube(step)); + toolbarContainer.addChild(button); + } + + /** + * Fügt den Würfel-Button hinzu, der die Figur entsprechend der gewürfelten Zahl bewegt. + */ + private void addDiceRollButton() { + Button diceButton = new Button("Würfeln"); + diceButton.setPreferredSize(new Vector3f(150, 50, 0)); // Größe des Buttons + diceButton.addClickCommands(source -> rollDice()); + toolbarContainer.addChild(diceButton); + } + + /** + * Simuliert einen Würfelwurf und bewegt die Figur entsprechend. + */ + private void rollDice() { + int diceRoll = random.nextInt(6) + 1; // Zahl zwischen 1 und 6 + System.out.println("Gewürfelt: " + diceRoll); + moveCube(diceRoll); // Bewege die Figur um die gewürfelte Zahl + } + + /** + * Bewegt den Würfel basierend auf der aktuellen Position auf dem Brett. + * + * @param step Schrittweite (+1 für vorwärts, -1 für rückwärts oder andere Werte) + */ + private void moveCube(int step) { + currentPosition = (currentPosition + step + 4 * positionsPerSide) % (4 * positionsPerSide); + Vector3f newPosition = calculatePosition(currentPosition); + cube.setLocalTranslation(newPosition); + updatePositionDisplay(); // Aktualisiere die Positionsanzeige + System.out.println("Würfelposition: " + newPosition + " (Feld-ID: " + currentPosition + ")"); + } + + /** + * Berechnet die neue Position des Würfels basierend auf der aktuellen Brettseite und Position. + * + * @param position Aktuelle Position auf dem Spielfeld + * @return Die berechnete Position als Vector3f + */ + private Vector3f calculatePosition(int position) { + int side = position / positionsPerSide; // Seite des Bretts (0 = unten, 1 = rechts, 2 = oben, 3 = links) + int offset = position % positionsPerSide; // Position auf der aktuellen Seite + + switch (side) { + case 0: // Unten (positive x-Achse) + return new Vector3f(-boardLimit + offset * stepSize, 0.1f, -boardLimit + 0.05f); + case 1: // Rechts (positive z-Achse) + return new Vector3f(boardLimit - 0.05f, 0.1f, -boardLimit + offset * stepSize); + case 2: // Oben (negative x-Achse) + return new Vector3f(boardLimit - offset * stepSize, 0.1f, boardLimit - 0.05f); + case 3: // Links (negative z-Achse) + return new Vector3f(-boardLimit + 0.05f, 0.1f, boardLimit - offset * stepSize); + default: + throw new IllegalArgumentException("Ungültige Position: " + position); + } + } + + /** + * Erstellt die Anzeige für die aktuelle Position. + * + * @return Das BitmapText-Objekt für die Anzeige + */ + private BitmapText createPositionDisplay() { + BitmapText text = new BitmapText(app.getAssetManager().loadFont("Interface/Fonts/Default.fnt"), false); + text.setSize(20); // Schriftgröße + text.setLocalTranslation(10, app.getCamera().getHeight() - 10, 0); // Oben links + app.getGuiNode().attachChild(text); + return text; + } + + /** + * Aktualisiert die Anzeige für die aktuelle Position. + */ + private void updatePositionDisplay() { + positionText.setText("Feld-ID: " + currentPosition); + } + + /** + * Entfernt die Toolbar. + */ + public void remove() { + app.getGuiNode().detachChild(toolbarContainer); + app.getGuiNode().detachChild(positionText); + } +} diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_blau.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_blau.j30 new file mode 100644 index 0000000..eb3b559 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_blau.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_gelb.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_gelb.j30 new file mode 100644 index 0000000..e0156e1 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_gelb.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_grün.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_grün.j30 new file mode 100644 index 0000000..e442c42 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_grün.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_rosa.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_rosa.j30 new file mode 100644 index 0000000..cb0c503 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_rosa.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_rot.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_rot.j30 new file mode 100644 index 0000000..ec70137 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_rot.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Models/Würfel_schwarz.j30 b/Projekte/monopoly/client/src/main/resources/Models/Würfel_schwarz.j30 new file mode 100644 index 0000000..d61fbdf Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Models/Würfel_schwarz.j30 differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/board.png b/Projekte/monopoly/client/src/main/resources/Pictures/board.png new file mode 100644 index 0000000..6e3afea Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/board.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/unibw-Bib.png b/Projekte/monopoly/client/src/main/resources/Pictures/unibw-Bib.png new file mode 100644 index 0000000..38ba294 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/unibw-Bib.png differ diff --git a/Projekte/monopoly/client/src/main/resources/Pictures/unibw-Bib2.png b/Projekte/monopoly/client/src/main/resources/Pictures/unibw-Bib2.png new file mode 100644 index 0000000..5973960 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/Pictures/unibw-Bib2.png differ diff --git a/Projekte/monopoly/client/src/main/resources/icons/test.ico b/Projekte/monopoly/client/src/main/resources/icons/test.ico new file mode 100644 index 0000000..0203f61 Binary files /dev/null and b/Projekte/monopoly/client/src/main/resources/icons/test.ico differ