From b3eaa64cd7a89a2cb73318a2ef9f69040efc541e Mon Sep 17 00:00:00 2001 From: Julian Kropff Date: Sat, 25 Apr 2026 18:36:35 +0200 Subject: [PATCH] Add: Casono Logo to popups --- .../gameuicomponents/TaskbarController.java | 181 ++++++++++++++---- 1 file changed, 142 insertions(+), 39 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java index 967879a..e72d51b 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarController.java @@ -10,11 +10,16 @@ import ch.unibas.dmi.dbis.cs108.casono.client.network.LobbyClient; import ch.unibas.dmi.dbis.cs108.casono.client.ui.lobbyui.Casinomainui; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Optional; import javafx.fxml.FXML; import javafx.scene.control.Alert; import javafx.scene.control.Button; +import javafx.scene.control.ButtonBar; +import javafx.scene.control.ButtonType; import javafx.scene.control.Label; import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; @@ -61,6 +66,9 @@ public class TaskbarController { private static final String STYLE_RED_INPUT = "red-input-field"; private static final double SCALE_NORMAL = 1.0; private static final int DEALER_OFFSET = 3; + private static final String LOGO_PATH = "/images/logoinverted.png"; + private static final String LOGO_PATH_MAIN = "/images/logo.png"; + private static final double ALERT_LOGO_HEIGHT = 40.0; private boolean inputActionAllowed; private int lastReferenceBet = BIG_BLIND; @@ -620,57 +628,31 @@ public class TaskbarController { } GameState state = ensureLatestStateForAction(actionType.name().toLowerCase()); - if (state == null) { - return; - } - - if (isHandFinished(state)) { - LOGGER.info("Action {} ignored: hand is already finished", actionType); - update(state, myPlayerId); + if (state == null || isHandFinished(state)) { + if (state != null) { + LOGGER.info("Action {} ignored: hand finished", actionType); + update(state, myPlayerId); + } return; } Player me = findCurrentPlayer(state); if (me == null) { - LOGGER.error("Action {} blocked: player missing in state", actionType); + LOGGER.error("Action {} blocked: player missing", actionType); return; } - int callTarget = effectiveCallTarget(state); - int targetBet; - if (actionType == ActionType.CALL_BUTTON) { - targetBet = callTarget; - } else if (actionType == ActionType.RAISE_BUTTON) { - targetBet = safeDouble(callTarget); - } else { - Integer fromInput = parseInputTarget(taskbarInput.getText()); - if (fromInput == null) { - return; - } - targetBet = fromInput; + Integer targetBet = resolveTargetBet(actionType, state); + if (targetBet == null) { + return; } ValidationResult validation = validateTarget(state, me, targetBet); - if (!validation.valid) { - refreshBetInputUi(); + if (!handleValidation(validation, actionType)) { return; } - if (validation.warning) { - showWarningDialog("WARNING", validation.message); - } - - if (state.currentBet <= 0) { - gameService.bet(targetBet); - LOGGER.info("Player BET (target={})", targetBet); - } else if (targetBet == callTarget) { - gameService.call(); - LOGGER.info("Player CALL (target={})", targetBet); - } else { - int raiseBy = Math.max(0, targetBet - state.currentBet); - gameService.raise(raiseBy); - LOGGER.info("Player RAISE by {} (target={})", raiseBy, targetBet); - } + executeAction(state, targetBet); if (targetBet > 0) { lastReferenceBet = targetBet; @@ -680,6 +662,88 @@ public class TaskbarController { refreshGame(); } + /** + * Resolves the target bet amount based on the type of action being submitted and the current + * game state. + * + * @param actionType The type of action being submitted, which determines how the target bet is + * calculated. + * @param state The current GameState object representing the state of the game, which is used + * to determine the effective call target and to validate the input for the bet amount. + * @return An Integer representing the resolved target bet amount for the action being + * submitted, or null if the input is invalid or cannot be resolved based on the action type + * and game state. + */ + private Integer resolveTargetBet(ActionType actionType, GameState state) { + int callTarget = effectiveCallTarget(state); + + if (actionType == ActionType.CALL_BUTTON) { + return callTarget; + } + + if (actionType == ActionType.RAISE_BUTTON) { + return safeDouble(callTarget); + } + + return parseInputTarget(taskbarInput.getText()); + } + + /** + * Handles the validation result of a proposed action, including displaying warnings and + * refreshing the UI if the action is blocked or cancelled. + * + * @param validation The ValidationResult object representing the outcome of validating the + * proposed action, which includes whether the action is valid, if it triggers a warning, + * and any associated messages. + * @param actionType The type of action being processed, used for logging purposes to indicate + * which action is being validated and potentially blocked or cancelled. + * @return A boolean value indicating whether the action is valid and can proceed (true) or if + * it is blocked or cancelled due to validation failure or user cancellation after a warning + * (false). + */ + private boolean handleValidation(ValidationResult validation, ActionType actionType) { + if (!validation.valid) { + refreshBetInputUi(); + return false; + } + + if (validation.warning) { + boolean confirmed = showWarningDialog("WARNING", validation.message); + if (!confirmed) { + LOGGER.info("Action {} cancelled after warning", actionType); + refreshBetInputUi(); + return false; + } + } + return true; + } + + /** + * Executes the player action by sending the appropriate command to the GameService based on the + * target bet and the current game state. + * + * @param state The current GameState object representing the state of the game, which includes + * information about the current bet and the player's status, used to determine how to + * execute the action based on the target bet. + * @param targetBet The integer value representing the target bet amount for the action being + * executed, which is used to determine + */ + private void executeAction(GameState state, int targetBet) { + int callTarget = effectiveCallTarget(state); + + if (state.currentBet <= 0) { + gameService.bet(targetBet); + LOGGER.info("BET {}", targetBet); + } else if (targetBet == callTarget) { + gameService.call(); + LOGGER.info("CALL {}", targetBet); + } else { + int raiseBy = Math.max(0, targetBet - state.currentBet); + gameService.raise(raiseBy); + LOGGER.info("RAISE {} (target={})", raiseBy, targetBet); + } + } + /** * Ensures that the latest game state is available for processing a player action. If the last * known state is null, it attempts to fetch the current state from the GameService. @@ -851,12 +915,50 @@ public class TaskbarController { * @param content The string representing the content of the warning message, which is displayed * in the body of the dialog. */ - private void showWarningDialog(String title, String content) { + private boolean showWarningDialog(String title, String content) { Alert alert = new Alert(Alert.AlertType.WARNING); alert.setTitle(title); alert.setHeaderText(null); alert.setContentText(content); - alert.showAndWait(); + ButtonType proceedButton = new ButtonType("CONTINUE"); + ButtonType cancelButton = new ButtonType("CANCEL", ButtonBar.ButtonData.CANCEL_CLOSE); + alert.getButtonTypes().setAll(proceedButton, cancelButton); + applyAlertBranding(alert); + Optional result = alert.showAndWait(); + return result.isPresent() && result.get() == proceedButton; + } + + /** Applies Casono icon and logo to alert dialogs when resources are available. */ + private void applyAlertBranding(Alert alert) { + if (alert == null) { + return; + } + + try (var iconStream = TaskbarController.class.getResourceAsStream(LOGO_PATH); + var mainLogoStream = TaskbarController.class.getResourceAsStream(LOGO_PATH_MAIN)) { + if (iconStream != null) { + Image icon = new Image(iconStream); + if (!icon.isError() + && alert.getDialogPane() != null + && alert.getDialogPane().getScene() != null + && alert.getDialogPane().getScene().getWindow() + instanceof javafx.stage.Stage stage) { + stage.getIcons().add(icon); + } + } + + if (mainLogoStream != null) { + Image mainLogo = new Image(mainLogoStream); + if (!mainLogo.isError()) { + ImageView logoView = new ImageView(mainLogo); + logoView.setFitHeight(ALERT_LOGO_HEIGHT); + logoView.setPreserveRatio(true); + alert.setGraphic(logoView); + } + } + } catch (Exception e) { + LOGGER.warn("Could not load alert logo resources: {}", e.getMessage()); + } } /** @@ -1173,6 +1275,7 @@ public class TaskbarController { alert.setTitle("Info"); alert.setHeaderText(null); alert.setContentText("No active server connection for highscores."); + applyAlertBranding(alert); alert.showAndWait(); return; }