diff --git a/documents/docs/game-engine/casono-markdown-render-engine.js b/documents/docs/game-engine/casono-markdown-render-engine.js
new file mode 100644
index 0000000..966dbba
--- /dev/null
+++ b/documents/docs/game-engine/casono-markdown-render-engine.js
@@ -0,0 +1,84 @@
+const marked = {
+ parse: function (md) {
+ if (!md) return "";
+
+ let toc = [];
+
+ md = md.replace(/^(#{1,3})\s+(.*)$/gim, (m, hashes, title) => {
+ const level = hashes.length;
+
+ const id = title
+ .toLowerCase()
+ .trim()
+ .replace(/[^\wäöüß]+/g, "-")
+ .replace(/-+/g, "-")
+ .replace(/^-|-$/g, "");
+
+ toc.push({ level, title, id });
+
+ return `
+
${escapeHtml(code)}`;
+ });
+
+ md = md.replace(/!\[(.*?)\]\((.*?)\)/gim,
+ `$1`
+ );
+
+ md = md.replace(/\*\*(.*?)\*\*/gim, "$1");
+ md = md.replace(/\*(.*?)\*/gim, "$1");
+
+ md = md.replace(/^\s*-\s(.+)$/gim, "")
+ .replace(/^(?!
TODO: It still needs to be fixed that the taskbar cannot disappear out of the window. - * * @param event Das Mausereignis */ @FXML private void onTaskbarDragged(MouseEvent event) { - taskbar.setLayoutX(event.getSceneX() - xOffset); - taskbar.setLayoutY(event.getSceneY() - yOffset); + if (taskbar == null || event == null || taskbar.getScene() == null) { + return; + } taskbar.setScaleX(TASKBAR_SCALE); taskbar.setScaleY(TASKBAR_SCALE); + + double targetX = event.getSceneX() - xOffset; + double targetY = event.getSceneY() - yOffset; + + double maxX = Math.max(0, taskbar.getScene().getWidth() - scaledNodeWidth()); + double maxY = Math.max(0, taskbar.getScene().getHeight() - scaledNodeHeight()); + + taskbar.setLayoutX(clamp(targetX, 0, maxX)); + taskbar.setLayoutY(clamp(targetY, 0, maxY)); } /** @@ -894,8 +1292,62 @@ public class TaskbarController { */ @FXML private void onTaskbarReleased(MouseEvent event) { + if (taskbar == null) { + return; + } taskbar.setScaleX(SCALE_NORMAL); taskbar.setScaleY(SCALE_NORMAL); + + if (taskbar.getScene() == null) { + return; + } + + double maxX = Math.max(0, taskbar.getScene().getWidth() - scaledNodeWidth()); + double maxY = Math.max(0, taskbar.getScene().getHeight() - scaledNodeHeight()); + + taskbar.setLayoutX(clamp(taskbar.getLayoutX(), 0, maxX)); + taskbar.setLayoutY(clamp(taskbar.getLayoutY(), 0, maxY)); + } + + /** + * Calculates the scaled width of the taskbar node based on its current bounds and scale factor. + * + * @return The scaled width of the taskbar node, ensuring it is non-negative and accounts for + * the current scale applied to the node. + */ + private double scaledNodeWidth() { + double width = taskbar.getBoundsInLocal().getWidth(); + if (width <= 0) { + width = taskbar.prefWidth(-1); + } + return Math.max(0, width * taskbar.getScaleX()); + } + + /** + * Calculates the scaled height of the taskbar node based on its current bounds and scale + * factor. + * + * @return The scaled height of the taskbar node, ensuring it is non-negative and accounts for + * the current scale applied to the node. + */ + private double scaledNodeHeight() { + double height = taskbar.getBoundsInLocal().getHeight(); + if (height <= 0) { + height = taskbar.prefHeight(-1); + } + return Math.max(0, height * taskbar.getScaleY()); + } + + /** + * Clamps a value between a minimum and maximum bound. + * + * @param value the value to clamp + * @param min the lower bound + * @param max the upper bound + * @return the clamped value in the range [min, max] + */ + private double clamp(double value, double min, double max) { + return Math.max(min, Math.min(value, max)); } /** @@ -928,7 +1380,7 @@ public class TaskbarController { /** Called when the Call button is clicked. */ @FXML private void onInputPlayerCall() { - submitPresetInputAndProcess("call"); + submitAction(ActionType.CALL_BUTTON); } /** Called when the Fold button is clicked. */ @@ -949,6 +1401,7 @@ public class TaskbarController { gameService.fold(); LOGGER.info("Player FOLD"); + announceLobbyAction("FOLD"); refreshGame(); } @@ -1062,6 +1515,9 @@ public class TaskbarController { GameState refreshed = gameService.refresh(); if (refreshed != null) { lastState = refreshed; + if (refreshed.currentBet > 0 && refreshed.currentBet >= lastReferenceBet) { + lastReferenceBet = refreshed.currentBet; + } return refreshed; } } catch (Exception e) { @@ -1074,12 +1530,24 @@ public class TaskbarController { /** * Opens the integrated Casono web browser. * - *
TODO: Replace the start URL with the official project website (e.g., Tips & Tricks page) - * once the content for strategies and support is available. + *
once the content for strategies and support is available.
*/
@FXML
private void onBrowserButtonClick() {
- CasinoBrowserController.open("wikipedia.org");
+ try {
+ Path path =
+ Paths.get(
+ System.getProperty("user.dir"),
+ "documents",
+ "docs",
+ "game-engine",
+ "manual.html");
+
+ CasinoBrowserController.open(path.toUri().toString());
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
}
/** Opens the highscore popup window from the taskbar. */
@@ -1092,6 +1560,7 @@ public class TaskbarController {
alert.setTitle("Info");
alert.setHeaderText(null);
alert.setContentText("No active server connection for highscores.");
+ applyAlertBranding(alert);
alert.showAndWait();
return;
}
@@ -1124,4 +1593,23 @@ public class TaskbarController {
LOGGER.error("Could not open highscore window from taskbar: {}", e.getMessage());
}
}
+
+ /**
+ * Highlight or unhighlight the taskbar when it is the player's turn.
+ *
+ * @param highlighted true to highlight, false to remove highlight
+ */
+ public void setTurnHighlighted(boolean highlighted) {
+ if (taskbar == null) {
+ return;
+ }
+
+ if (highlighted) {
+ if (!taskbar.getStyleClass().contains("player-active-turn")) {
+ taskbar.getStyleClass().add("player-active-turn");
+ }
+ } else {
+ taskbar.getStyleClass().remove("player-active-turn");
+ }
+ }
}
diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateHandler.java
index d9fb584..c0d00b9 100644
--- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateHandler.java
+++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateHandler.java
@@ -10,9 +10,16 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
/** Handler for GET_GAME_STATE: returns pot, phase, community cards and per-player info. */
public class GetGameStateHandler extends CommandHandler