diff --git a/build.gradle b/build.gradle index 99005a1..62621a0 100644 --- a/build.gradle +++ b/build.gradle @@ -26,7 +26,7 @@ repositories { javafx { version = "25.0.2" - modules = ['javafx.controls', 'javafx.fxml', 'javafx.base', 'javafx.graphics', 'javafx.web'] + modules = ['javafx.controls', 'javafx.fxml', 'javafx.base', 'javafx.graphics', 'javafx.web', 'javafx.media'] } dependencies { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/Main.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/Main.java index 04046b2..9bbd40a 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/Main.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/Main.java @@ -52,7 +52,7 @@ public final class Main { } private static void printUsage() { - Logger logger = LogManager.getLogger(Main.class); + Logger logger = LogManager.getLogger(Main.class.getSimpleName()); logger.fatal( """ Usage: diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java index 897b487..024775d 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ClientApp.java @@ -15,7 +15,7 @@ import org.apache.logging.log4j.Logger; */ public class ClientApp { - private static final Logger LOGGER = LogManager.getLogger(ClientApp.class); + private static final Logger LOGGER = LogManager.getLogger(ClientApp.class.getSimpleName()); /** Shared client connection used when a username is provided at startup. */ private static volatile ClientService sharedClientService; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java index 58fb787..4d19dcd 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/chat/ChatController.java @@ -88,7 +88,7 @@ public class ChatController { chatModelMap = new LinkedHashMap<>(); localUserList = new ArrayList<>(); this.chatBoxController = new ChatBoxController(username, this); - this.logger = LogManager.getLogger(ChatController.class); + this.logger = LogManager.getLogger(ChatController.class.getSimpleName()); this.serverEventListener = this::handleServerEvent; this.activeChatControllers = new HashMap<>(); @@ -124,7 +124,7 @@ public class ChatController { chatModelMap = new LinkedHashMap<>(); localUserList = new ArrayList<>(); this.chatBoxController = new ChatBoxController(username, this); - this.logger = LogManager.getLogger(ChatController.class); + this.logger = LogManager.getLogger(ChatController.class.getSimpleName()); this.serverEventListener = this::handleServerEvent; this.activeChatControllers = new HashMap<>(); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/game/GameState.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/game/GameState.java index b462ca5..433ccef 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/game/GameState.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/game/GameState.java @@ -18,4 +18,6 @@ public class GameState { public int dealer; public int activePlayer; public int winnerIndex = -1; + public List winnerNames = new ArrayList<>(); + public int potPerWinner; } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ChatClient.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ChatClient.java index d46ce22..c20d244 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ChatClient.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ChatClient.java @@ -26,7 +26,7 @@ public class ChatClient implements ChatClientInterface { */ public ChatClient(ClientService clientService) { this.clientService = clientService; - this.logger = LogManager.getLogger(ChatClient.class); + this.logger = LogManager.getLogger(ChatClient.class.getSimpleName()); } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java index 2ab6f0c..9c0ffdd 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/ClientService.java @@ -60,7 +60,7 @@ public class ClientService { public ClientService(String ip, int port) { this.idGenerator = new AtomicInteger(0); - this.logger = LogManager.getLogger(ClientService.class); + this.logger = LogManager.getLogger(ClientService.class.getSimpleName()); this.offlineMode = false; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java index 42b4073..2a6cbc1 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java @@ -111,6 +111,7 @@ public class GameClient { GameState s = new GameState(); s.players = new ArrayList<>(); s.communityCards = new ArrayList<>(); + s.winnerNames = new ArrayList<>(); ParseState state = new ParseState(); for (String raw : input.split("\n")) { @@ -155,6 +156,14 @@ public class GameClient { s.winnerIndex = intVal(l); yield true; } + case String l when l.startsWith("WINNER_NAME=") -> { + s.winnerNames.add(value(l)); + yield true; + } + case String l when l.startsWith("POT_PER_WINNER=") -> { + s.potPerWinner = intVal(l); + yield true; + } case String l when l.startsWith("HIGHSCORE=") -> { s.highscoreEntries.add(value(l)); yield true; @@ -224,6 +233,13 @@ public class GameClient { return true; } + if (line.startsWith("ID=")) { + if (state.currentPlayer.getId() == null) { + state.currentPlayer.setId(PlayerId.of(value(line))); + } + return true; + } + if (line.startsWith("CHIPS=")) { state.currentPlayer.setChips(intVal(line)); return true; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/LobbyClient.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/LobbyClient.java index 1ca2274..f0d48cd 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/LobbyClient.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/LobbyClient.java @@ -244,6 +244,33 @@ public class LobbyClient { return result; } + /** + * Fetches the current lobby members from GET_LOBBY_STATUS and returns their usernames in order. + * + * @param lobbyId id of the lobby to inspect + * @return list of usernames currently known in the lobby + */ + public List fetchLobbyPlayerNames(int lobbyId) { + List lines = client.processCommand("GET_LOBBY_STATUS ID=" + lobbyId); + List params = ClientService.convertToRequestParameters(lines); + + List names = new ArrayList<>(); + for (RequestParameter parameter : params) { + if (!"USERNAME".equalsIgnoreCase(parameter.key())) { + continue; + } + String name = parameter.value(); + if (name != null) { + String trimmed = name.trim(); + if (!trimmed.isEmpty() && !names.contains(trimmed)) { + names.add(trimmed); + } + } + } + + return names; + } + /** Simple data holder for lobby metadata returned by the server. */ public static final class LobbyInfo { public final int id; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java index 2b9610c..7eb38b1 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameController.java @@ -10,7 +10,10 @@ import ch.unibas.dmi.dbis.cs108.casono.client.game.Player; import ch.unibas.dmi.dbis.cs108.casono.client.game.PlayerId; import ch.unibas.dmi.dbis.cs108.casono.client.game.PlayerState; import ch.unibas.dmi.dbis.cs108.casono.client.network.ClientService; +import ch.unibas.dmi.dbis.cs108.casono.client.network.LobbyClient; +import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.NotebookController; import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.PlayerStatusController; +import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.SettingsController; import ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents.TaskbarController; import ch.unibas.dmi.dbis.cs108.casono.ui.sound.SoundManager; import java.io.IOException; @@ -22,6 +25,7 @@ import java.util.concurrent.CompletableFuture; import java.util.logging.Logger; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.image.Image; @@ -61,8 +65,13 @@ public class CasinoGameController { private GameService gameService; private PlayerId myPlayerId; + private String myPlayerName; @FXML private TaskbarController taskbarIncludeController; private TaskbarController taskbarController; + @FXML private NotebookController notebookIncludeController; + private NotebookController notebookController; + @FXML private SettingsController settingsIncludeController; + // private SettingsController settingsController; private Image dealerImage; private javafx.animation.Timeline timeline; private boolean gameStarted = false; @@ -75,6 +84,7 @@ public class CasinoGameController { public static final double CHAT_CONTAINER_CONSTANT = 6.0; private static final int TOTAL_SLOTS = 5; private static final int PLAYER_SLOTS = 2; + private static final int MAX_OPPONENTS_TO_RENDER = 3; private int pot = 0; private static final String BACKSIDE = "/images/card-background-3.png"; private static final String DEALER_IMAGE_PATH = "/images/chip-dealer-blue-3.png"; @@ -117,6 +127,8 @@ public class CasinoGameController { private static final double COMMUNITY_CARD_WIDTH_RATIO = 0.11; private static final double PLAYER_CARD_HEIGHT_RATIO = 0.30; private static final double PLAYER_CARD_WIDTH_RATIO = 0.15; + private static final double DEALER_HEIGHT_RATIO = 0.13; + private static final double DEALER_WIDTH_RATIO = 0.13; private static final double PLAYER_CARDS_Y_OFFSET_FACTOR = 0.10; private static final int[] CHIP_VALUES = { 100000, 50000, 20000, 10000, @@ -141,6 +153,12 @@ public class CasinoGameController { private static final long CHIP_SCALE_DURATION_MS = 220; private static final long CHIP_DROP_DURATION_MS = 250; private static final long CHIP_STAGGER_DELAY_MULTIPLIER = 35L; + private static final int MAX_POT_ROWS = 2; + private static final double POT_CHIP_H_GAP = 6.0; + private static final double POT_CHIP_V_GAP = 6.0; + private static final double POT_WIDTH_FACTOR = 0.45; + private static final double FALLBACK_POT_WIDTH = 500.0; + private static final double FALLBACK_CHIP_WIDTH = 36.0; private static final double CHAT_WIDTH = 400; private static final double CHAT_HEIGHT = 600; private static final String ACTIVE_CARD_BOX_STYLE_CLASS = "player-cards-active-turn"; @@ -158,6 +176,8 @@ public class CasinoGameController { private static final int SMALL_BLIND = 100; private static final int BIG_BLIND = 200; private static final int NO_BET = 0; + private boolean myDealerIconSizeBound; + private java.util.List lobbyPlayerNames = java.util.List.of(); /** Standard constructor. Used by FXML. */ public CasinoGameController() { @@ -264,6 +284,16 @@ public class CasinoGameController { if (taskbarController != null && gameService != null && myPlayerId != null) { taskbarController.setGameService(gameService, myPlayerId); } + + notebookController = resolveNotebookController(); + if (taskbarController != null && notebookController != null) { + taskbarController.setNotebookController(notebookController); + } + + if (taskbarController != null && settingsIncludeController != null) { + taskbarController.setSettingsController(settingsIncludeController); + settingsIncludeController.setThemeChangeListener(this::applyTheme); + } configureTaskbarLobbyActionAnnouncements(); if (communityCardsBox == null) { @@ -285,6 +315,7 @@ public class CasinoGameController { } myDealerIcon.setVisible(false); + styleMyDealerIcon(); tableText.setWrapText(true); javafx.stage.Screen screen = javafx.stage.Screen.getPrimary(); @@ -301,6 +332,8 @@ public class CasinoGameController { renderPlayerCards(List.of()); chatContainer.toFront(); + + javafx.application.Platform.runLater(() -> applyTheme("standard")); } /** @@ -509,6 +542,7 @@ public class CasinoGameController { .thenAccept( state -> { if (state == null) { + refreshLobbyOpponentsBeforeGameStart(); LOGGER.info("No state received yet."); return; } @@ -526,6 +560,36 @@ public class CasinoGameController { }); } + /** + * Refresh the list of opponents in the lobby before the game starts by fetching the latest + * player. + */ + private void refreshLobbyOpponentsBeforeGameStart() { + if (gameService != null && gameService.peekStateOrNull() != null) { + return; + } + if (chatClientService == null || chatLobbyId < 0) { + return; + } + + try { + LobbyClient lobbyClient = new LobbyClient(chatClientService); + List latestNames = lobbyClient.fetchLobbyPlayerNames(chatLobbyId); + if (latestNames == null) { + latestNames = List.of(); + } + if (latestNames.equals(lobbyPlayerNames)) { + return; + } + + List namesToRender = List.copyOf(latestNames); + javafx.application.Platform.runLater(() -> setLobbyPlayerNames(namesToRender)); + } catch (Exception e) { + String msg = "Could not refresh lobby player names: " + e.getMessage(); + LOGGER.fine(msg); + } + } + /** * Updates all visual UI components with the data from the provided GameState. This includes * community cards, pot, player information, and the taskbar. @@ -669,12 +733,12 @@ public class CasinoGameController { */ private List getMyCards(List players) { - if (myPlayerId == null || players == null) { + if (players == null) { return List.of(); } for (Player p : players) { - if (p.getId() != null && p.getId().equals(myPlayerId)) { + if (isCurrentPlayer(p)) { return p.getCards() != null ? p.getCards() : List.of(); } } @@ -689,13 +753,13 @@ public class CasinoGameController { */ private void updatePlayerCards(GameState s) { - if (s.players == null || myPlayerId == null) { + if (s.players == null) { renderPlayerCards(List.of()); return; } for (Player p : s.players) { - if (p.getId().equals(myPlayerId)) { + if (isCurrentPlayer(p)) { renderPlayerCards(p.getCards()); return; } @@ -720,7 +784,18 @@ public class CasinoGameController { String winnerName = resolveWinnerName(s); if (winnerName != null && !winnerName.isBlank()) { - tableText.setText("Winner: " + winnerName); + StringBuilder winnerText = new StringBuilder("Winner"); + + if (s.winnerNames != null && s.winnerNames.size() > 1) { + winnerText.append("s: ").append(String.join(", ", s.winnerNames)); + if (s.potPerWinner > 0) { + winnerText.append(" (").append(s.potPerWinner).append(" each)"); + } + } else { + winnerText.append(": ").append(winnerName); + } + + tableText.setText(winnerText.toString()); return; } @@ -818,13 +893,13 @@ public class CasinoGameController { */ private void updateTaskbar(GameState s) { TaskbarController controller = resolveTaskbarController(); - if (controller == null || myPlayerId == null) { + if (controller == null) { return; } if (gameService != null) { controller.setGameService(gameService, myPlayerId); } - controller.update(s, myPlayerId); + controller.update(s, myPlayerName); } /** @@ -880,30 +955,51 @@ public class CasinoGameController { * opponents. */ private java.util.List buildOpponents(List players) { - java.util.List opponents = new java.util.ArrayList<>(); - boolean meFound = false; - - for (Player p : players) { - if (p == null) { - continue; - } - - PlayerId pid = p.getId(); - boolean isMe = myPlayerId != null && myPlayerId.equals(pid); - - if (isMe) { - meFound = true; - } else { - opponents.add(p); - } + if (players == null || players.isEmpty()) { + return java.util.List.of(); } - if (!meFound) { - opponents.clear(); - opponents.addAll(players); + int myIndex = findCurrentPlayerIndex(players); + if (myIndex < 0) { + java.util.List fallback = new java.util.ArrayList<>(); + for (Player player : players) { + if (player != null) { + fallback.add(player); + } + } + return fallback; } - return opponents; + java.util.List ordered = new java.util.ArrayList<>(Math.max(0, players.size() - 1)); + for (int offset = 1; offset < players.size(); offset++) { + int index = (myIndex + offset) % players.size(); + Player player = players.get(index); + if (player != null) { + ordered.add(player); + } + } + return ordered; + } + + /** + * Find the index of the current player in the provided list of players by comparing their + * PlayerId with myPlayerId. + * + * @param players The list of players to search through, which may include the current player + * and their opponents. + * @return The index of the current player in the list if found, or -1 if the current player is + * not present in the list or if the list is null or empty. + */ + private int findCurrentPlayerIndex(List players) { + if (players == null || players.isEmpty()) { + return -1; + } + for (int i = 0; i < players.size(); i++) { + if (isCurrentPlayer(players.get(i))) { + return i; + } + } + return -1; } /** @@ -1018,12 +1114,12 @@ public class CasinoGameController { clearActiveTurnHighlights(); Player activePlayer = getPlayerAtIndex(players, activePlayerIndex); - if (activePlayer == null || activePlayer.getId() == null) { + if (activePlayer == null) { setTaskbarTurnHighlight(false); return; } - if (myPlayerId != null && myPlayerId.equals(activePlayer.getId())) { + if (isCurrentPlayer(activePlayer)) { setPlayerCardsTurnHighlighted(true); setTaskbarTurnHighlight(true); return; @@ -1152,11 +1248,7 @@ public class CasinoGameController { } Player winner = resolveWinner(s); - boolean isLocalWinner = - winner != null - && winner.getId() != null - && myPlayerId != null - && myPlayerId.equals(winner.getId()); + boolean isLocalWinner = winner != null && isCurrentPlayer(winner); if (!isLocalWinner && lastWinnerName != null && !lastWinnerName.isBlank()) { String localName = @@ -1303,11 +1395,11 @@ public class CasinoGameController { } Player dealerPlayer = s.players.get(s.dealer); - if (dealerPlayer == null || dealerPlayer.getId() == null) { + if (dealerPlayer == null) { return; } - if (myPlayerId != null && myPlayerId.equals(dealerPlayer.getId())) { + if (isCurrentPlayer(dealerPlayer)) { myDealerIcon.setVisible(true); return; } @@ -1340,10 +1432,17 @@ public class CasinoGameController { * otherwise. */ private boolean samePlayer(Player a, Player b) { - if (a == null || b == null || a.getId() == null || b.getId() == null) { + if (a == null || b == null) { return false; } - return a.getId().equals(b.getId()); + + String aName = normalizeIdentifier(a.getName()); + String bName = normalizeIdentifier(b.getName()); + if (aName != null && bName != null) { + return aName.equals(bName); + } + + return a.getId() != null && b.getId() != null && a.getId().equals(b.getId()); } /** @@ -1374,8 +1473,8 @@ public class CasinoGameController { view.setImage(image); - view.setOnMouseEntered(e -> animateCardHover(view, true)); - view.setOnMouseExited(e -> animateCardHover(view, false)); + // view.setOnMouseEntered(e -> animateCardHover(view, true)); + // view.setOnMouseExited(e -> animateCardHover(view, false)); view.setOpacity(CARD_START_OPACITY); @@ -1418,8 +1517,8 @@ public class CasinoGameController { view.setImage(image); - view.setOnMouseEntered(e -> animateCardHover(view, true)); - view.setOnMouseExited(e -> animateCardHover(view, false)); + // view.setOnMouseEntered(e -> animateCardHover(view, true)); + // view.setOnMouseExited(e -> animateCardHover(view, false)); view.setOpacity(CARD_START_OPACITY); @@ -1432,6 +1531,47 @@ public class CasinoGameController { } } + /** Apply styling to the myDealerIcon ImageView. */ + private void styleMyDealerIcon() { + if (myDealerIcon == null || playerCardsBox == null) { + return; + } + + myDealerIcon.setPreserveRatio(true); + myDealerIcon.setSmooth(true); + + javafx.scene.Scene scene = playerCardsBox.getScene(); + if (scene != null) { + bindMyDealerIconSize(scene); + } else { + playerCardsBox + .sceneProperty() + .addListener( + (obs, oldScene, newScene) -> { + if (newScene != null) { + bindMyDealerIconSize(newScene); + } + }); + } + } + + /** + * Bind the size of the myDealerIcon ImageView to the dimensions of the scene using predefined + * ratios. + * + * @param scene The JavaFX Scene to which the myDealerIcon belongs, used for binding the size + * properties. + */ + private void bindMyDealerIconSize(javafx.scene.Scene scene) { + if (myDealerIconSizeBound || scene == null || myDealerIcon == null) { + return; + } + + myDealerIcon.fitHeightProperty().bind(scene.heightProperty().multiply(DEALER_HEIGHT_RATIO)); + myDealerIcon.fitWidthProperty().bind(scene.widthProperty().multiply(DEALER_WIDTH_RATIO)); + myDealerIconSizeBound = true; + } + /** * Load an image from the given path safely, falling back to a default backside image if the * specified image cannot be found. @@ -1599,28 +1739,31 @@ public class CasinoGameController { * Animate a card hover effect by scaling and lifting the card when hovered and resetting it * when not hovered. * + *

However, we have removed the hover animations from the cards and the pot for improved + * handling and a more stable Game UI. + * * @param view The ImageView representing the card to animate on hover. * @param hover A boolean indicating whether the card is being hovered (true) or not (false). */ - private void animateCardHover(ImageView view, boolean hover) { - - double scale = hover ? HOVER_SCALE_ON : HOVER_SCALE_OFF; - double lift = hover ? HOVER_LIFT_ON : HOVER_LIFT_OFF; - - javafx.animation.ScaleTransition st = - new javafx.animation.ScaleTransition( - javafx.util.Duration.millis(HOVER_DURATION_MS), view); - st.setToX(scale); - st.setToY(scale); - - javafx.animation.TranslateTransition tt = - new javafx.animation.TranslateTransition( - javafx.util.Duration.millis(HOVER_DURATION_MS), view); - tt.setToY(lift); - - st.play(); - tt.play(); - } + // private void animateCardHover(ImageView view, boolean hover) { + // + // double scale = hover ? HOVER_SCALE_ON : HOVER_SCALE_OFF; + // double lift = hover ? HOVER_LIFT_ON : HOVER_LIFT_OFF; + // + // javafx.animation.ScaleTransition st = + // new javafx.animation.ScaleTransition( + // javafx.util.Duration.millis(HOVER_DURATION_MS), view); + // st.setToX(scale); + // st.setToY(scale); + // + // javafx.animation.TranslateTransition tt = + // new javafx.animation.TranslateTransition( + // javafx.util.Duration.millis(HOVER_DURATION_MS), view); + // tt.setToY(lift); + // + // st.play(); + // tt.play(); + // } /** * Position the player's hole cards box on the screen based on a predefined offset factor @@ -1688,13 +1831,26 @@ public class CasinoGameController { potBox.getChildren().clear(); + int chipsPerRow = resolveChipsPerPotRow(); + int maxVisibleChips = chipsPerRow * MAX_POT_ROWS; + HBox topRow = createPotRow(); + HBox bottomRow = createPotRow(); + VBox rows = new VBox(POT_CHIP_V_GAP, topRow, bottomRow); + rows.setAlignment(Pos.CENTER); + potBox.getChildren().add(rows); + int remaining = pot; int index = 0; + boolean limitReached = false; for (int chipValue : CHIP_VALUES) { while (remaining >= chipValue) { + if (index >= maxVisibleChips) { + limitReached = true; + break; + } ImageView chip = new ImageView(loadImageSafe("/images/chip-" + chipValue + "-5.png")); @@ -1706,18 +1862,67 @@ public class CasinoGameController { styleChip(chip); - potBox.getChildren().add(chip); + if (index < chipsPerRow) { + topRow.getChildren().add(chip); + } else { + bottomRow.getChildren().add(chip); + } animateChipAppear(chip, index); remaining -= chipValue; index++; } + + if (limitReached) { + break; + } } // LOGGER.info("POT RENDERED: " + pot + " -> remaining: " + remaining); } + /** + * Create a new HBox to represent a row of chips in the pot display, with predefined horizontal. + * + * @return A new HBox instance configured for displaying a row of chips in the pot, with + * appropriate spacing and alignment. + */ + private HBox createPotRow() { + HBox row = new HBox(POT_CHIP_H_GAP); + row.setAlignment(Pos.CENTER); + return row; + } + + /** + * Calculate the number of chips that can be displayed per row in the pot based on the available + * width. + * + * @return The calculated number of chips that can fit in a single row of the pot display. + */ + private int resolveChipsPerPotRow() { + double availableWidth = potBox != null ? potBox.getWidth() : 0.0; + if (availableWidth <= 0 && potBox != null && potBox.getScene() != null) { + availableWidth = potBox.getScene().getWidth() * POT_WIDTH_FACTOR; + } + if (availableWidth <= 0) { + availableWidth = FALLBACK_POT_WIDTH; + } + + double estimatedChipWidth = FALLBACK_CHIP_WIDTH; + if (potBox != null && potBox.getScene() != null) { + estimatedChipWidth = + Math.max(FALLBACK_CHIP_WIDTH, potBox.getScene().getWidth() * CHIP_WIDTH_RATIO); + } + + int chipsPerRow = + (int) + Math.floor( + (availableWidth + POT_CHIP_H_GAP) + / (estimatedChipWidth + POT_CHIP_H_GAP)); + return Math.max(1, chipsPerRow); + } + /** * Apply styling to the given ImageView for chips, including CSS classes and size bindings. * @@ -1817,13 +2022,131 @@ public class CasinoGameController { */ public void setMyPlayerId(PlayerId id) { this.myPlayerId = id; + this.myPlayerName = id != null ? normalizeIdentifier(id.value()) : null; TaskbarController controller = resolveTaskbarController(); if (controller != null && gameService != null && myPlayerId != null) { controller.setGameService(gameService, myPlayerId); } + renderLobbyOpponentsIfNeeded(); configureTaskbarLobbyActionAnnouncements(); } + /** + * Sets lobby player names to be shown before the first game-state update is available. + * + * @param names player names currently present in the lobby + */ + public void setLobbyPlayerNames(List names) { + if (names == null || names.isEmpty()) { + this.lobbyPlayerNames = java.util.List.of(); + } else { + this.lobbyPlayerNames = java.util.List.copyOf(names); + } + renderLobbyOpponentsIfNeeded(); + } + + /** + * Render the opponents in the lobby based on the list of lobby player names, but only if the + * game service is not yet initialized or if there is no game state available. + */ + private void renderLobbyOpponentsIfNeeded() { + if (gameService != null && gameService.peekStateOrNull() != null) { + return; + } + + if (lobbyPlayerNames == null || lobbyPlayerNames.isEmpty()) { + return; + } + + java.util.List opponents = new java.util.ArrayList<>(); + for (String rawName : lobbyPlayerNames) { + String normalized = normalizeIdentifier(rawName); + if (normalized == null) { + continue; + } + if (myPlayerName != null && myPlayerName.equals(normalized)) { + continue; + } + if (!opponents.contains(rawName.trim())) { + opponents.add(rawName.trim()); + } + if (opponents.size() >= MAX_OPPONENTS_TO_RENDER) { + break; + } + } + + setLobbyOpponent(player1Controller, opponents, 0); + setLobbyOpponent(player2Controller, opponents, 1); + setLobbyOpponent(player3Controller, opponents, 2); + } + + /** + * Set the lobby opponent information in the given PlayerStatusController slot based on the list + * of opponent names and the specified index. + * + * @param slot The PlayerStatusController instance representing the UI slot for an opponent, + * which will be updated with the opponent's name if available. + * @param opponents The list of opponent names currently in the lobby, which may be null or + * contain fewer entries than the index. + * @param index The index of the opponent in the opponents list to be displayed in the given + * slot, where 0 corresponds to the first opponent, 1 to the second, and so on. + */ + private void setLobbyOpponent(PlayerStatusController slot, List opponents, int index) { + if (slot == null) { + return; + } + + if (opponents == null || index >= opponents.size()) { + slot.setPlayer(null); + safeRefresh(slot); + return; + } + + slot.setPlayer(null); + slot.updatePlayer(opponents.get(index), 0); + slot.setDealer(false); + slot.setTurnHighlighted(false); + } + + /** + * Determine if the given Player object represents the current player based on the player's name + * and ID compared to the stored myPlayerName and myPlayerId values. + * + * @param player The Player object to check, which may be null or contain name and ID + * information. + * @return true if the given Player is identified as the current player based on name or ID + * matching, false otherwise. + */ + private boolean isCurrentPlayer(Player player) { + if (player == null) { + return false; + } + + String candidateName = normalizeIdentifier(player.getName()); + if (myPlayerName != null && candidateName != null && myPlayerName.equals(candidateName)) { + return true; + } + + return myPlayerId != null && player.getId() != null && myPlayerId.equals(player.getId()); + } + + /** + * Normalize a player name or identifier by trimming whitespace and converting to lowercase, + * returning null if the result is empty. + * + * @param value The raw player name or identifier to normalize, which may be null or contain + * leading/trailing whitespace. + * @return The normalized identifier in lowercase without leading/trailing whitespace, or null + * if the input is null or empty after trimming. + */ + private String normalizeIdentifier(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed.toLowerCase(); + } + /** * Configure the TaskbarController to announce lobby actions by sending chat messages when * certain actions occur in the game, such as winning a hand. @@ -1881,4 +2204,86 @@ public class CasinoGameController { } return null; } + + /** + * Resolve the NotebookController for displaying tips and game information. + * + * @return The NotebookController instance, which may be the directly injected + * notebookController or the one obtained from the notebookIncludeController. + */ + private NotebookController resolveNotebookController() { + if (notebookController != null) { + return notebookController; + } + if (notebookIncludeController != null) { + notebookController = notebookIncludeController; + return notebookController; + } + return null; + } + + /** + * Resolve the SettingsController for theme switching. + * + * @return The SettingsController instance, which may be the directly injected + * settingsController or the one obtained from the settingsIncludeController. + */ + public SettingsController getSettingsController() { + return settingsIncludeController; + } + + /** + * Applies a UI theme by switching the application's stylesheet. + * + * @param theme The theme name: "standard", "blackwhite", or "glass" + */ + private void applyTheme(String theme) { + if (theme == null || theme.isBlank()) { + LOGGER.warning("Theme is null or empty -> ignoring theme change"); + return; + } + + javafx.scene.Scene scene = casinoTable != null ? casinoTable.getScene() : null; + + if (scene == null) { + LOGGER.warning("Scene not ready yet -> cannot apply theme: " + theme); + return; + } + + String cssPath = + switch (theme.toLowerCase()) { + case "mainuiparquet" -> "/ui-structure/Casinogameui-parquet.css"; + case "blackwhite" -> "/ui-structure/Casinogameui-blackwhite.css"; + case "glass" -> "/ui-structure/Casinogameui-glass.css"; + case "lightglass" -> "/ui-structure/Casinogameui-light-glass.css"; + default -> "/ui-structure/Casinogameui.css"; + }; + + java.net.URL cssUrl = getClass().getResource(cssPath); + + if (cssUrl == null) { + LOGGER.severe("CSS resource not found: " + cssPath); + return; + } + + try { + String cssExternalForm = cssUrl.toExternalForm(); + + scene.getStylesheets() + .removeIf( + s -> + s.contains("Casinogameui.css") + || s.contains("Casinogameui-parquet.css") + || s.contains("Casinogameui-blackwhite.css") + || s.contains("Casinogameui-glass.css") + || s.contains("Casinogameui-light-glass.css")); + + scene.getStylesheets().add(cssExternalForm); + + LOGGER.info("Theme switched successfully to: " + theme); + + } catch (Exception e) { + LOGGER.severe("Failed to apply theme '" + theme + "': " + e.getMessage()); + } + } } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameUI.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameUI.java index c82dfe1..9b437ef 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameUI.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/CasinoGameUI.java @@ -6,6 +6,7 @@ import ch.unibas.dmi.dbis.cs108.casono.client.game.GameService; import ch.unibas.dmi.dbis.cs108.casono.client.game.PlayerId; import ch.unibas.dmi.dbis.cs108.casono.client.network.ClientService; import ch.unibas.dmi.dbis.cs108.casono.client.network.GameClient; +import ch.unibas.dmi.dbis.cs108.casono.client.network.LobbyClient; import java.io.IOException; import java.util.UUID; import java.util.logging.Logger; @@ -130,6 +131,13 @@ public class CasinoGameUI extends Application { controller.setMyPlayerId(PlayerId.of(effectiveUsername)); controller.setChatContext(effectiveUsername, clientService, lobbyId); + try { + LobbyClient lobbyClient = new LobbyClient(clientService); + controller.setLobbyPlayerNames(lobbyClient.fetchLobbyPlayerNames(lobbyId)); + } catch (Exception e) { + LOG.fine("Could not preload lobby player names: " + e.getMessage()); + } + controller.startChat(chatController); Scene scene = new Scene(root, DEFAULT_WIDTH, DEFAULT_HEIGHT); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java index 6a360a3..c915ab2 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/CasinoBrowserController.java @@ -68,7 +68,8 @@ public class CasinoBrowserController { private static final CookieManager COOKIE_MANAGER = new CookieManager(null, CookiePolicy.ACCEPT_ORIGINAL_SERVER); - private static final Logger LOGGER = LogManager.getLogger(CasinoBrowserController.class); + private static final Logger LOGGER = + LogManager.getLogger(CasinoBrowserController.class.getSimpleName()); private static final ObservableList URL_SUGGESTIONS = FXCollections.observableArrayList(TRUSTED_DOMAINS); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/HighscoreViewController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/HighscoreViewController.java index e395b68..9045b81 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/HighscoreViewController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/HighscoreViewController.java @@ -14,7 +14,8 @@ import org.apache.logging.log4j.Logger; /** Controller for the highscore popup window. */ public class HighscoreViewController { - private static final Logger LOGGER = LogManager.getLogger(HighscoreViewController.class); + private static final Logger LOGGER = + LogManager.getLogger(HighscoreViewController.class.getSimpleName()); @FXML private ListView highscoreList; @FXML private Label statusLabel; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/NotebookController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/NotebookController.java new file mode 100644 index 0000000..b8da88a --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/NotebookController.java @@ -0,0 +1,269 @@ +package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents; + +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; +import javafx.scene.text.TextFlow; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Controller for the movable notebook (Tips) display within the Casono Game UI. + * + *

Responsible for: - Drag-and-drop movement of the notebook, - Display of general gaming tips, - + * Toggle visibility of the notebook. + */ +public class NotebookController { + + private static final Logger LOGGER = + LogManager.getLogger(NotebookController.class.getSimpleName()); + + @FXML private VBox notebook; + @FXML private ScrollPane tipsContent; + @FXML private VBox tipsList; + @FXML private Button toggleButton; + + private double xOffset = 0; + private double yOffset = 0; + private static final double NOTEBOOK_SCALE = 0.95; + private static final double SCALE_NORMAL = 1.0; + private boolean isVisible = true; + + /** Standard constructor. Used by FXML. */ + public NotebookController() { + // default constructor for FXML + } + + /** Initialize the notebook with default tips. */ + @FXML + private void initialize() { + if (tipsList != null) { + loadDefaultTips(); + } + } + + /** Load default poker tips into the notebook. */ + private void loadDefaultTips() { + tipsList.getChildren().clear(); + + String[] tips = { + "TEXAS HOLD'EM GRUNDLAGEN\n\nJeder Spieler erhält 2 verdeckte Karten. " + + "Zusätzlich gibt es 5 Gemeinschaftskarten.\n" + + "Ziel: Beste 5-Karten-Hand bilden oder alle Gegner zum Fold bringen.\n" + + "Startstack: 20000 Chips ($)", + "BLINDS & DEALER\n\nSmall Blind: 100 Chips | Big Blind: 200 Chips\n" + + "Die Blinds erzeugen den Startpot und " + + "müssen vor jeder Runde gezahlt werden.\n" + + "Der Dealer-Button bestimmt die Positionen " + + "sowie die Reihenfolge der Aktionen.", + "SPIELABLAUF\n\nPreflop → Flop (3 Karten) → Turn (4. Karte) → River (5. Karte)\n" + + "Preflop beginnt links vom Big Blind.\n" + + "Nach dem Flop beginnt die Action links vom Dealer (aktive Spieler).", + "AKTIONEN\n\nFold = aussteigen\nCall = mitgehen\nRaise = erhöhen\n" + + "Alle Aktionen müssen in korrekter Reihenfolge (Acting in Turn) erfolgen.", + "POSITION IST ENTSCHEIDEND\n\nEarly Position: nur starke Hände spielen\n" + + "Middle Position: etwas breiter spielen\n" + + "Late Position: größter Vorteil durch mehr Informationen", + "SHOWDOWN\n\nWenn nach der letzten Setzrunde mehrere Spieler übrig sind:\n" + + "Beste 5-Karten-Kombination aus Handkarten + Gemeinschaftskarten gewinnt.", + "STRATEGIE & DENKEN\n\nPoker ist Strategie + Psychologie + Mathematik\n" + + "Entscheidungen basieren auf Wahrscheinlichkeiten, " + + "Position und Gegnerverhalten.", + "WICHTIGE REGELN\n\n- Acting in Turn ist Pflicht\n" + + "- Jeder Spieler spielt nur seine eigene Hand (One Player One Hand)\n" + + "- Ungültige Einsätze werden blockiert oder korrigiert\n" + + "- Reihenfolge am Tisch muss immer eingehalten werden", + "HÄUFIGE FEHLER\n\n❌ Zu viele Hände spielen\n" + + "❌ Position ignorieren\n❌ Zu oft callen statt folden\n" + + "❌ Stärke der Gegner unterschätzen\n❌ Tilt (emotional spielen)", + "TOP 10 POKERHÄNDE\n\n" + + "1. 👑 Royal Flush\n" + + "A♠ K♠ Q♠ J♠ 10♠\n\n" + + "2. 🔥 Straight Flush\n" + + "9♥ 8♥ 7♥ 6♥ 5♥\n\n" + + "3. 💥 Four of a Kind (Poker)\n" + + "A♣ A♦ A♥ A♠ K♠\n\n" + + "4. 🏠 Full House\n" + + "K♣ K♦ K♥ 9♠ 9♦\n\n" + + "5. 🌊 Flush\n" + + "A♥ J♥ 8♥ 5♥ 2♥\n\n" + + "6. ➡ Straight\n" + + "10♣ 9♦ 8♠ 7♥ 6♣\n\n" + + "7. 🎯 Three of a Kind (Drilling)\n" + + "Q♣ Q♦ Q♥ 7♠ 2♦\n\n" + + "8. 👥 Two Pair\n" + + "J♣ J♦ 4♠ 4♥ A♣\n\n" + + "9. 👍 One Pair\n" + + "8♣ 8♦ K♠ J♥ 3♣\n\n" + + "10. 🃏 High Card\n" + + "A♠ J♦ 9♣ 5♥ 2♠" + }; + + for (String tip : tips) { + TextFlow tipBox = createTipElement(tip); + tipsList.getChildren().add(tipBox); + } + } + + private static final int LINE_SPACING = 4; + private static final int TIP_WIDTH = 280; + + /** + * Create a styled text element for a single tip. + * + * @param tipText The text of the tip to display. + * @return A TextFlow element containing the formatted tip. + */ + private TextFlow createTipElement(String tipText) { + Text text = new Text(tipText); + text.setStyle("-fx-font-size: 12; -fx-font-family: 'Segoe UI', Arial;"); + TextFlow flow = new TextFlow(text); + flow.setStyle( + "-fx-padding: 10; -fx-background-color: #f5f5f5; " + + "-fx-border-radius: 5; -fx-margin: 5;"); + flow.setLineSpacing(LINE_SPACING); + flow.setPrefWidth(TIP_WIDTH); + return flow; + } + + /** + * Called when the notebook header is pressed with the mouse. Saves the relative position for + * later correct repositioning. + * + * @param event The mouse event + */ + @FXML + private void onNotebookPressed(MouseEvent event) { + if (notebook == null || event == null) { + return; + } + xOffset = event.getSceneX() - notebook.getLayoutX(); + yOffset = event.getSceneY() - notebook.getLayoutY(); + } + + /** + * Called while dragging the notebook with the mouse. Updates the position and slightly scales + * the notebook for visual feedback. + * + * @param event The mouse event + */ + @FXML + private void onNotebookDragged(MouseEvent event) { + if (notebook == null || event == null || notebook.getScene() == null) { + return; + } + + notebook.setScaleX(NOTEBOOK_SCALE); + notebook.setScaleY(NOTEBOOK_SCALE); + + double targetX = event.getSceneX() - xOffset; + double targetY = event.getSceneY() - yOffset; + + double maxX = Math.max(0, notebook.getScene().getWidth() - scaledNodeWidth()); + double maxY = Math.max(0, notebook.getScene().getHeight() - scaledNodeHeight()); + + notebook.setLayoutX(clamp(targetX, 0, maxX)); + notebook.setLayoutY(clamp(targetY, 0, maxY)); + } + + /** + * Called when the mouse cursor is released over the notebook. Resets the notebook scaling to + * normal size. + * + * @param event The mouse event + */ + @FXML + private void onNotebookReleased(MouseEvent event) { + if (notebook == null) { + return; + } + notebook.setScaleX(SCALE_NORMAL); + notebook.setScaleY(SCALE_NORMAL); + + if (notebook.getScene() == null) { + return; + } + + double maxX = Math.max(0, notebook.getScene().getWidth() - scaledNodeWidth()); + double maxY = Math.max(0, notebook.getScene().getHeight() - scaledNodeHeight()); + + notebook.setLayoutX(clamp(notebook.getLayoutX(), 0, maxX)); + notebook.setLayoutY(clamp(notebook.getLayoutY(), 0, maxY)); + } + + /** Close the notebook window from the header minus button. */ + @FXML + private void onToggleNotebook() { + if (notebook == null) { + return; + } + isVisible = false; + notebook.setVisible(false); + notebook.setManaged(false); + } + + /** Shows the tips notebook (makes it visible if currently hidden). */ + public void showTips() { + if (notebook == null) { + return; + } + + if (!isVisible) { + isVisible = true; + notebook.setVisible(true); + notebook.setManaged(true); + } + + if (tipsContent != null) { + tipsContent.setVisible(true); + tipsContent.setManaged(true); + } + + if (toggleButton != null) { + toggleButton.setText("−"); + } + } + + /** + * Calculates the scaled width of the notebook node based on its current bounds and scale + * factor. + * + * @return The scaled width of the notebook node. + */ + private double scaledNodeWidth() { + double width = notebook.getBoundsInLocal().getWidth(); + if (width <= 0) { + width = notebook.prefWidth(-1); + } + return Math.max(0, width * notebook.getScaleX()); + } + + /** + * Calculates the scaled height of the notebook node based on its current bounds and scale + * factor. + * + * @return The scaled height of the notebook node. + */ + private double scaledNodeHeight() { + double height = notebook.getBoundsInLocal().getHeight(); + if (height <= 0) { + height = notebook.prefHeight(-1); + } + return Math.max(0, height * notebook.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)); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/PlayerStatusController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/PlayerStatusController.java index 48738c2..0252763 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/PlayerStatusController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/PlayerStatusController.java @@ -20,15 +20,19 @@ public class PlayerStatusController { private static final Logger LOGGER = Logger.getLogger(PlayerStatusController.class.getName()); - @FXML private Label playerName; - @FXML private Label playerMoney; - @FXML private ImageView dealerIcon; - @FXML private Pane parent; - @FXML private VBox playerStatusBox; - @FXML private HBox statusInnerBoxTop; - @FXML private HBox statusInnerBoxBottom; + @FXML Label playerName; + @FXML Label playerMoney; + @FXML ImageView dealerIcon; + @FXML ImageView playerProfileImage; + @FXML Pane parent; + @FXML VBox playerStatusBox; + @FXML HBox statusInnerBoxTop; + @FXML HBox statusInnerBoxBottom; private Image dealerImage; + // Profile images (loaded from resources/images/profile-picture) + private Image profileUserImage; + private Image profileDealerImage; private Player player; private boolean turnHighlighted; private static final String DEALER_IMAGE_PATH = "/images/chip-dealer-blue-5.png"; @@ -53,6 +57,26 @@ public class PlayerStatusController { LOGGER.log(Level.SEVERE, "Error loading dealer image", e); } + try { + var base = "/images/profile-picture/"; + var uUser = getClass().getResource(base + "poker_user_491.png"); + var uDealer = getClass().getResource(base + "poker_dealer_471.png"); + + if (uUser != null) { + profileUserImage = new Image(uUser.toExternalForm(), true); + } + if (uDealer != null) { + profileDealerImage = new Image(uDealer.toExternalForm(), true); + } + + if (playerProfileImage != null && profileUserImage != null) { + playerProfileImage.setImage(profileUserImage); + } + + } catch (Exception e) { + LOGGER.log(Level.FINE, "Could not load profile images", e); + } + dealerIcon.layoutXProperty().bind(parent.widthProperty().multiply(DEALER_ICON_X_FACTOR)); dealerIcon.setVisible(false); } @@ -115,14 +139,27 @@ public class PlayerStatusController { * @return true if both represent the same player. */ public boolean hasPlayer(Player candidate) { - if (player == null - || candidate == null - || player.getId() == null - || candidate.getId() == null) { + if (player == null || candidate == null) { return false; } - return player.getId().equals(candidate.getId()); + String currentName = normalizeIdentifier(player.getName()); + String candidateName = normalizeIdentifier(candidate.getName()); + if (currentName != null && candidateName != null) { + return currentName.equals(candidateName); + } + + return player.getId() != null + && candidate.getId() != null + && player.getId().equals(candidate.getId()); + } + + private String normalizeIdentifier(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed.toLowerCase(); } /** @@ -147,6 +184,14 @@ public class PlayerStatusController { if (isDealer && dealerImage != null) { dealerIcon.setImage(dealerImage); } + + if (playerProfileImage != null) { + if (isDealer && profileDealerImage != null) { + playerProfileImage.setImage(profileDealerImage); + } else if (profileUserImage != null) { + playerProfileImage.setImage(profileUserImage); + } + } } /** diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/SettingsController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/SettingsController.java new file mode 100644 index 0000000..b62d5c3 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/SettingsController.java @@ -0,0 +1,209 @@ +package ch.unibas.dmi.dbis.cs108.casono.client.ui.gameui.gameuicomponents; + +import java.util.function.Consumer; +import javafx.fxml.FXML; +import javafx.scene.control.RadioButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.VBox; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * Controller for the settings display within the poker UI. Handles theme selection and drag + * movement. + */ +public class SettingsController { + + private static final Logger LOGGER = + LogManager.getLogger(SettingsController.class.getSimpleName()); + + @FXML private VBox settingsBox; + @FXML private RadioButton themeStandard; + @FXML private RadioButton themeStandardParquet; + @FXML private RadioButton themeBlackWhite; + @FXML private RadioButton themeGlass; + @FXML private RadioButton themeLightGlass; + private boolean visible = true; + + private double xOffset; + private double yOffset; + + private static final double SCALE_DRAG = 0.95; + private static final double SCALE_NORMAL = 1.0; + + private String currentTheme = "standard"; + private Consumer themeChangeListener; + + /** Initialize the settings controller, set up theme toggle group and listeners. */ + @FXML + private void initialize() { + setupThemeToggle(); + } + + /** Set up the theme toggle group and listeners for the theme selection radio buttons. */ + private void setupThemeToggle() { + if (themeStandard == null + || themeStandardParquet == null + || themeBlackWhite == null + || themeGlass == null + || themeLightGlass == null) { + LOGGER.warn("Theme buttons not injected from FXML"); + return; + } + + ToggleGroup group = new ToggleGroup(); + + themeStandard.setToggleGroup(group); + themeStandardParquet.setToggleGroup(group); + themeBlackWhite.setToggleGroup(group); + themeGlass.setToggleGroup(group); + themeLightGlass.setToggleGroup(group); + + themeStandard.setSelected(true); + + group.selectedToggleProperty() + .addListener( + (obs, oldVal, newVal) -> { + if (newVal == themeStandard) { + setTheme("standard"); + } else if (newVal == themeStandardParquet) { + setTheme("mainuiparquet"); + } else if (newVal == themeBlackWhite) { + setTheme("blackwhite"); + } else if (newVal == themeGlass) { + setTheme("glass"); + } else if (newVal == themeLightGlass) { + setTheme("lightglass"); + } + }); + } + + /** + * Set the current theme and notify the listener of the change. + * + * @param theme The new theme to set (e.g., "standard", "blackwhite", "glass"). + */ + private void setTheme(String theme) { + currentTheme = theme; + + if (themeChangeListener != null) { + themeChangeListener.accept(theme); + } + + LOGGER.info("Theme changed to: " + theme); + } + + /** + * Handle the mouse press event on the settings box to prepare for dragging. + * + * @param event The MouseEvent triggered when the user presses the mouse button on the settings + * box. + */ + @FXML + private void onSettingsPressed(MouseEvent event) { + if (settingsBox == null) { + return; + } + + xOffset = event.getSceneX() - settingsBox.getLayoutX(); + yOffset = event.getSceneY() - settingsBox.getLayoutY(); + } + + /** + * Handle the mouse drag event on the settings box to allow dragging it around the scene. + * + * @param event The MouseEvent triggered when the user drags the mouse while pressing on the + * settings box. + */ + @FXML + private void onSettingsDragged(MouseEvent event) { + if (settingsBox == null || settingsBox.getScene() == null) { + return; + } + + settingsBox.setScaleX(SCALE_DRAG); + settingsBox.setScaleY(SCALE_DRAG); + + double x = event.getSceneX() - xOffset; + double y = event.getSceneY() - yOffset; + + double maxX = settingsBox.getScene().getWidth() - settingsBox.getWidth(); + double maxY = settingsBox.getScene().getHeight() - settingsBox.getHeight(); + + settingsBox.setLayoutX(clamp(x, 0, maxX)); + settingsBox.setLayoutY(clamp(y, 0, maxY)); + } + + /** Close the settings box from the header minus button. */ + @FXML + private void onToggleSettings() { + if (settingsBox == null) { + return; + } + + visible = !visible; + + settingsBox.setVisible(visible); + settingsBox.setManaged(visible); + } + + /** + * Handle the mouse release event on the settings box to reset its scale after dragging. + * + * @param event The MouseEvent triggered when the user releases the mouse button after dragging + * the settings box. + */ + @FXML + private void onSettingsReleased(MouseEvent event) { + if (settingsBox == null || settingsBox.getScene() == null) { + return; + } + + settingsBox.setScaleX(SCALE_NORMAL); + settingsBox.setScaleY(SCALE_NORMAL); + } + + /** + * Set a listener to be notified when the theme changes. The listener will receive the new theme + * as a string. + * + * @param listener A Consumer that accepts a String representing the new theme (e.g., + * "standard", "blackwhite", "glass"). + */ + public void setThemeChangeListener(Consumer listener) { + this.themeChangeListener = listener; + } + + /** Show the settings box by setting its visibility and managed properties to true. */ + public void show() { + if (settingsBox == null) { + return; + } + + settingsBox.setVisible(true); + settingsBox.setManaged(true); + } + + /** Hide the settings box by setting its visibility and managed properties to false. */ + public void hide() { + if (settingsBox == null) { + return; + } + + settingsBox.setVisible(false); + settingsBox.setManaged(false); + } + + /** + * Utility method to clamp a value between a minimum and maximum range. + * + * @param value The value to clamp. + * @param min The minimum allowed value. + * @param max The maximum allowed value. + * @return The clamped value, guaranteed to be between min and max. + */ + private double clamp(double value, double min, double max) { + return Math.max(min, Math.min(value, max)); + } +} 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 53583f2..f59ba14 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 @@ -40,7 +40,8 @@ import org.apache.logging.log4j.Logger; */ public class TaskbarController { - private static final Logger LOGGER = LogManager.getLogger(TaskbarController.class); + private static final Logger LOGGER = + LogManager.getLogger(TaskbarController.class.getSimpleName()); @FXML private HBox taskbar; @FXML private TextField taskbarInput; @@ -52,15 +53,19 @@ public class TaskbarController { private GameService gameService; private PlayerId myPlayerId; + private String myPlayerName; private GameState lastState; private double xOffset = 0; private double yOffset = 0; + private NotebookController notebookController; + private SettingsController settingsController; private static final double TASKBAR_SCALE = 0.95; private static final double PREFLOP_BLOCK_RATIO = 0.50; private static final double FLOP_WARN_RATIO = 0.50; private static final double FLOP_BLOCK_RATIO = 0.80; private static final double TURN_RIVER_WARN_RATIO = 0.80; private static final double TURN_RIVER_BLOCK_RATIO = 1.00; + private static final int SMALLEST_VALUE = 5; private static final int SMALL_BLIND = 100; private static final int BIG_BLIND = 200; private static final String STYLE_YELLOW_BUTTON = "yellow-button"; @@ -81,6 +86,9 @@ public class TaskbarController { private static final int FIRST_PLAYER_MAX_BET = 3000; private static final int MAX_INPUT_LENGTH = 6; private static final double MAX_CHIP_PERCENT = 0.30; + private int lastPotSnapshot = 0; + private int lastObservedIncrease = 0; + private int lastRaiseIncrement = 0; /** Standard constructor. Used by FXML. */ public TaskbarController() { @@ -231,6 +239,17 @@ public class TaskbarController { public void setGameService(GameService gameService, PlayerId myPlayerId) { this.gameService = gameService; this.myPlayerId = myPlayerId; + this.myPlayerName = myPlayerId != null ? normalizeIdentifier(myPlayerId.value()) : null; + } + + /** Sets the NotebookController reference for showing/hiding tips. */ + public void setNotebookController(NotebookController notebookController) { + this.notebookController = notebookController; + } + + /** Sets the SettingsController reference for theme switching. */ + public void setSettingsController(SettingsController settingsController) { + this.settingsController = settingsController; } /** Sets an optional callback that publishes simple action labels to lobby chat. */ @@ -341,6 +360,37 @@ public class TaskbarController { return "RAISE +" + raiseBy + " TO " + raiseamout; } + /** + * Resolves the typed input as the additional amount the player wants to contribute. + * + *

If the player has already invested chips in the current betting round, the typed value is + * interpreted as an additional amount on top of that investment. This lets the user enter the + * amount they still need to put in directly (for example: already invested 200, type 400 -> + * total target 600). + * + * @param state current game state + * @param rawInput text from the input field + * @return total target bet, or null if the input is invalid + */ + private Integer resolveTypedContributionTarget(GameState state, String rawInput) { + Integer input = parseInputTarget(rawInput); + if (input == null) { + return null; + } + + if (state == null || state.players == null) { + return input; + } + + Player me = findCurrentPlayer(state); + if (me == null) { + return input; + } + + int alreadyInvested = Math.max(0, me.getBet()); + return alreadyInvested > 0 ? alreadyInvested + input : input; + } + /** * Updates the taskbar based on the current game state and the player's status. * @@ -353,24 +403,44 @@ public class TaskbarController { * and whether they are out of the game. */ public void update(GameState state, PlayerId myPlayerId) { + this.myPlayerId = myPlayerId; + this.myPlayerName = myPlayerId != null ? normalizeIdentifier(myPlayerId.value()) : null; + update(state); + } - if (state == null || state.players == null || myPlayerId == null) { + /** + * Updates the taskbar based on the current game state and the player's name. + * + * @param state current game state + * @param myPlayerName local player name + */ + public void update(GameState state, String myPlayerName) { + this.myPlayerName = normalizeIdentifier(myPlayerName); + update(state); + } + + /** + * Updates the taskbar based on the current game state. + * + * @param state current game state + */ + private void update(GameState state) { + + if (state == null || state.players == null) { LOGGER.warn("Cannot update taskbar: invalid input"); return; } this.lastState = state; + Player me = findCurrentPlayer(state); + + synchronizeCurrentBetIfNeeded(state, me); + if (state.currentBet > 0) { lastReferenceBet = state.currentBet; } - Player me = - state.players.stream() - .filter(p -> myPlayerId.equals(p.getId())) - .findFirst() - .orElse(null); - if (me == null) { LOGGER.error("Player not found in GameState!"); return; @@ -394,6 +464,52 @@ public class TaskbarController { refreshBetInputUi(); } + /** + * Synchronizes the current bet in the game state if there has been a change in the pot size + * that is not yet reflected in the current bet. + * + * @param state The current GameState object representing the state of the game. + * @param me The Player object representing the current player, used to determine their bet + * status and whether they are the first to act in the current phase. + */ + private void synchronizeCurrentBetIfNeeded(GameState state, Player me) { + + if (state == null || me == null) { + return; + } + + int currentPot = Math.max(0, state.pot); + int potDiff = currentPot - lastPotSnapshot; + + lastPotSnapshot = currentPot; + + if (potDiff <= 0) { + return; + } + + LOGGER.debug("Pot changed: +" + potDiff); + + lastRaiseIncrement = Math.max(lastRaiseIncrement, potDiff); + + boolean isTurnOrRiver = isTurnOrRiver(state.phase); + + boolean isSpecialActor = isFirstPlayerOfPhase(state, me) && isTurnOrRiver; + + if (isSpecialActor) { + + LOGGER.info("SPECIAL TURN/RIVER LOGIC ACTIVE, potDiff=" + potDiff); + + if (potDiff > state.currentBet) { + + LOGGER.info("Updating currentBet -> potDiff: " + potDiff); + + state.currentBet = potDiff; + + lastReferenceBet = potDiff; + } + } + } + /** * Applies the availability of actions (Bet, Call, Raise, Fold) based on the current game state. * @@ -444,7 +560,7 @@ public class TaskbarController { return; } - if (isFirstPlayerOfPhase(state, me)) { + if (isFirstFlopPlayer(state, me)) { setActionEnabled(betButton, true); setActionEnabled(callButton, false); // setActionEnabled(foldButton, false); @@ -630,6 +746,53 @@ public class TaskbarController { return isInitialPreflopBlindLayout(state); } + /** + * Checks if the current player is the first to act on the flop. + * + * @param state The current GameState containing phase, dealer and active player information. + * @param me The current player. + * @return true if the player is the first player to act on the flop. + */ + private boolean isFirstFlopPlayer(GameState state, Player me) { + + if (state == null || me == null || state.players == null) { + return false; + } + + if (!isFlop(state.phase)) { + return false; + } + + int size = state.players.size(); + + if (size < 2) { + return false; + } + + int myIndex = state.players.indexOf(me); + + if (myIndex < 0) { + return false; + } + + int firstIndex = (state.dealer + 1) % size; + + for (int i = 0; i < size; i++) { + + int candidate = (firstIndex + i) % size; + + Player p = state.players.get(candidate); + + if (p != null && p.getState() != PlayerState.FOLDED && p.getChips() > 0) { + + firstIndex = candidate; + break; + } + } + + return state.activePlayer == firstIndex && myIndex == firstIndex; + } + /** * Checks if the current player is the first to act in the current phase of the game, based on * the dealer position and the active player index. @@ -877,7 +1040,8 @@ public class TaskbarController { return input != null ? input : callTarget + BIG_BLIND; } - return parseInputTarget(taskbarInput.getText()); + return resolveTypedContributionTarget( + state, taskbarInput != null ? taskbarInput.getText() : null); } /** @@ -1012,7 +1176,7 @@ public class TaskbarController { try { int value = Integer.parseInt(text.trim()); - if (value % SMALL_BLIND != 0) { + if (value % SMALLEST_VALUE != 0) { return null; } @@ -1034,7 +1198,7 @@ public class TaskbarController { * current game state. */ private ValidationResult validateTypedAmount(GameState state, String text) { - Integer target = parseInputTarget(text); + Integer target = resolveTypedContributionTarget(state, text); if (target == null || state == null) { return ValidationResult.blocked("Invalid input"); } @@ -1497,15 +1661,56 @@ public class TaskbarController { submitAction(ActionType.INPUT); } + /** + * Finds the current player in the given game state based on the player's name or ID. + * + * @param state The current GameState object representing the state of the game, which includes + * a list of players and their details. + * @return The Player object representing the current player if found in the game state. + */ private Player findCurrentPlayer(GameState state) { - if (state == null || state.players == null || myPlayerId == null) { + if (state == null || state.players == null) { return null; } - return state.players.stream() - .filter(player -> player != null && myPlayerId.equals(player.getId())) - .findFirst() - .orElse(null); + return state.players.stream().filter(this::isCurrentPlayer).findFirst().orElse(null); + } + + /** + * Checks if the given player matches the current player's identity based on name or ID. + * + * @param player The Player object to check against the current player's identity, which + * includes the player's name and ID. + * @return A boolean value indicating whether the given player matches the current player's + * identity. + */ + private boolean isCurrentPlayer(Player player) { + if (player == null) { + return false; + } + + String playerName = normalizeIdentifier(player.getName()); + if (myPlayerName != null && playerName != null && myPlayerName.equals(playerName)) { + return true; + } + + return myPlayerId != null && myPlayerId.equals(player.getId()); + } + + /** + * Normalizes a player identifier (name or ID) by trimming whitespace and converting to + * lowercase. + * + * @param value The string value representing a player identifier, such as a name or ID. + * @return A normalized version of the player identifier, where leading and trailing whitespace + * is removed. + */ + private String normalizeIdentifier(String value) { + if (value == null) { + return null; + } + String trimmed = value.trim(); + return trimmed.isEmpty() ? null : trimmed.toLowerCase(); } /** @@ -1557,6 +1762,46 @@ public class TaskbarController { } } + /** Opens the integrated Casono Web Browser. */ + @FXML + private void onBrowserButtonClickWiki() { + try { + + CasinoBrowserController.open("https://www.wikipedia.org/"); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** Opens the integrated Casono Web Browser. */ + @FXML + private void onBrowserButtonClickBrave() { + try { + + CasinoBrowserController.open("https://search.brave.com"); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** Opens the tips notebook if it's currently closed. */ + @FXML + private void onShowTipsButtonClick() { + if (notebookController != null) { + notebookController.showTips(); + } + } + + /** Opens the settings box for theme selection. */ + @FXML + private void onSettingsButtonClick() { + if (settingsController != null) { + settingsController.show(); + } + } + /** Opens the highscore popup window from the taskbar. */ @FXML private void onHighscoreButtonClick() { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java index 0201504..c51722a 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/CasinomainuiController.java @@ -31,7 +31,8 @@ import org.apache.logging.log4j.Logger; /** Controller for the Casono main UI lobby. Handles UI initialization and user actions. */ public class CasinomainuiController { - private static final Logger LOGGER = LogManager.getLogger(CasinomainuiController.class); + private static final Logger LOGGER = + LogManager.getLogger(CasinomainuiController.class.getSimpleName()); @FXML private AnchorPane rootPane; @FXML private Label titleLabel; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManager.java index 81d1a84..b572975 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/lobbyui/LobbyButtonGridManager.java @@ -35,7 +35,8 @@ public class LobbyButtonGridManager { private static final int MAX_BUTTONS = 8; private static final int GUEST_ID_LENGTH = 8; - private static final Logger LOGGER = LogManager.getLogger(LobbyButtonGridManager.class); + private static final Logger LOGGER = + LogManager.getLogger(LobbyButtonGridManager.class.getSimpleName()); private static final String BUTTON_FALLBACK_IMAGE = "/images/lobbypictures/error.png"; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java index a9a46c7..51e022d 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/ServerApp.java @@ -99,7 +99,7 @@ public class ServerApp { public static void start(String arg) { int port = Integer.parseInt(arg); - Logger logger = LogManager.getLogger(ServerApp.class); + Logger logger = LogManager.getLogger(ServerApp.class.getSimpleName()); logger.info("Starting server at port {}", port); EventBus eventBus = new EventBus(); diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java index f55fd36..7b6e33d 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java @@ -6,6 +6,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Rank; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Suit; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.Player; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.showdown.CardsSpeakRule; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GamePhase; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.highscore.HighscoreService; @@ -13,8 +14,11 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestCo import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBody; import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBodyBuilder; +import java.util.ArrayList; +import java.util.LinkedHashSet; import java.util.List; import java.util.Objects; +import java.util.Set; /** * Response carrying a snapshot of the current game state using the project's wire format. @@ -51,10 +55,16 @@ public class GetGameStateResponse extends SuccessResponse { builder.param("CURRENT_BET", computeGlobalCurrentBet(state)); builder.param("DEALER", state.getDealerIndex()); builder.param("ACTIVE_PLAYER", state.getCurrentPlayerIndex()); + int winnerIndex = computeWinnerIndex(state, game); builder.param("WINNER", winnerIndex); - appendWinnerToHighscoresIfFinished(state, winnerIndex); + List winnerNames = computeWinnerNames(state, game); + for (String name : winnerNames) { + builder.param("WINNER_NAME", name); + } + + appendWinnerToHighscoresIfFinished(state, winnerNames); appendHighscoreEntries(builder); appendCommunityCards(builder, state); @@ -64,8 +74,14 @@ public class GetGameStateResponse extends SuccessResponse { return builder.build(); } - private static void appendWinnerToHighscoresIfFinished(GameState state, int winnerIndex) { - if (winnerIndex < 0 || state.getPhase() != GamePhase.FINISHED) { + private static void appendWinnerToHighscoresIfFinished( + GameState state, List winnerNames) { + if (winnerNames == null || winnerNames.isEmpty()) { + return; + } + + GamePhase phase = state.getPhase(); + if (phase != GamePhase.SHOWDOWN && phase != GamePhase.FINISHED) { return; } @@ -73,12 +89,22 @@ public class GetGameStateResponse extends SuccessResponse { return; } - Player winner = findPlayerByIndex(state, winnerIndex); - if (winner == null || winner.getId() == null || winner.getId().value() == null) { - return; + // Keep ordering stable and persist tied winners as a single shared highscore + // entry. Compute the per-winner share here (pot is split equally among + // winners) + int potPerWinner = computePotPerWinner(state, winnerNames.size()); + Set uniqueWinners = new LinkedHashSet<>(winnerNames); + List formattedWinners = new ArrayList<>(); + for (String name : uniqueWinners) { + if (name != null && !name.isBlank()) { + formattedWinners.add(name + " (" + Math.max(0, potPerWinner) + ")"); + } } - HighscoreService.getInstance().appendWinner(winner.getId().value()); + if (!formattedWinners.isEmpty()) { + HighscoreService.getInstance() + .appendFormattedEntry(String.join(", ", formattedWinners)); + } } private static Player findPlayerByIndex(GameState state, int winnerIndex) { @@ -120,6 +146,33 @@ public class GetGameStateResponse extends SuccessResponse { return -1; } + /** Computes the list of all winner names at showdown/finished phase. */ + private static List computeWinnerNames(GameState state, GameController game) { + GamePhase phase = state.getPhase(); + if (phase != GamePhase.SHOWDOWN && phase != GamePhase.FINISHED) { + return new ArrayList<>(); + } + + CardsSpeakRule showdown = new CardsSpeakRule(); + List winners = showdown.determineWinners(state); + + List winnerNames = new ArrayList<>(); + for (Player winner : winners) { + if (winner != null && winner.getId() != null && winner.getId().value() != null) { + winnerNames.add(winner.getId().value()); + } + } + return winnerNames; + } + + /** Computes the pot share per winner. */ + private static int computePotPerWinner(GameState state, int numWinners) { + if (numWinners <= 0 || state.getPot() == null) { + return 0; + } + return state.getPot().getAmount() / numWinners; + } + private static int computeGlobalCurrentBet(GameState state) { int globalCurrentBet = 0; for (Player p : state.getPlayers()) { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/lobby/join_lobby/JoinLobbyHandler.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/lobby/join_lobby/JoinLobbyHandler.java index 169b027..e557c3c 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/lobby/join_lobby/JoinLobbyHandler.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/lobby/join_lobby/JoinLobbyHandler.java @@ -23,7 +23,8 @@ import org.apache.logging.log4j.Logger; public class JoinLobbyHandler extends CommandHandler { private final LobbyManager lobbyManager; private final UserRegistry userRegistry; - private static final Logger LOGGER = LogManager.getLogger(JoinLobbyHandler.class); + private static final Logger LOGGER = + LogManager.getLogger(JoinLobbyHandler.class.getSimpleName()); /** * Create a new {@link JoinLobbyHandler}. diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/evaluator/HandEvaluator.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/evaluator/HandEvaluator.java index daf293e..62e6f98 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/evaluator/HandEvaluator.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/evaluator/HandEvaluator.java @@ -2,6 +2,7 @@ package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Card; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Suit; +import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.List; @@ -40,6 +41,9 @@ public class HandEvaluator { private static final int START_INDEX = 1; private static final int CARD_DIFFERENCE = 1; private static final int PREVIOUS_INDEX_OFFSET = -1; + private static final int TWO_KICKERS = 2; + private static final int ONE_KICKER = 1; + private static final int THREE_KICKERS = 3; /** * Evaluates a list of cards and determines the best possible hand rank. @@ -62,7 +66,7 @@ public class HandEvaluator { return straightFlush; } - HandRank fourKind = checkFourOfAKind(rankCount); + HandRank fourKind = checkFourOfAKind(rankCount, ranks); if (fourKind != null) { return fourKind; } @@ -80,17 +84,17 @@ public class HandEvaluator { return new HandRank(HandRank.Type.STRAIGHT, ranks); } - HandRank threeKind = checkThreeOfAKind(rankCount); + HandRank threeKind = checkThreeOfAKind(rankCount, ranks); if (threeKind != null) { return threeKind; } - HandRank twoPair = checkTwoPair(rankCount); + HandRank twoPair = checkTwoPair(rankCount, ranks); if (twoPair != null) { return twoPair; } - HandRank onePair = checkOnePair(rankCount); + HandRank onePair = checkOnePair(rankCount, ranks); if (onePair != null) { return onePair; } @@ -200,20 +204,26 @@ public class HandEvaluator { } /** - * Helper method to check for a four of a kind hand rank. + * Helper method to check for a four of a kind hand rank. Stores the quad rank and 1 kicker for + * tie-breaking. * * @param rankCount A map where the key is the card rank and the value is the count of * occurrences. + * @param allRanks A sorted list of all card ranks in the hand. * @return A HandRank object representing the four of a kind hand rank, or null if not found. */ - private static HandRank checkFourOfAKind(Map rankCount) { + private static HandRank checkFourOfAKind(Map rankCount, List allRanks) { if (!rankCount.containsValue(FOUR_OF_A_KIND_COUNT)) { return null; } int quad = getRank(rankCount, FOUR_OF_A_KIND); + List kickers = getKickers(allRanks, List.of(quad), ONE_KICKER); - return new HandRank(HandRank.Type.FOUR_OF_A_KIND, List.of(quad)); + List result = new ArrayList<>(List.of(quad)); + result.addAll(kickers); + + return new HandRank(HandRank.Type.FOUR_OF_A_KIND, result); } /** @@ -255,30 +265,39 @@ public class HandEvaluator { } /** - * Helper method to check for a three of a kind hand rank. + * Helper method to check for a three of a kind hand rank. Stores the trips rank and 2 kickers + * for tie-breaking. * * @param rankCount A map where the key is the card rank and the value is the count of * occurrences. + * @param allRanks A sorted list of all card ranks in the hand. * @return A HandRank object representing the three of a kind hand rank, or null if not found. */ - private static HandRank checkThreeOfAKind(Map rankCount) { + private static HandRank checkThreeOfAKind( + Map rankCount, List allRanks) { if (!rankCount.containsValue(THREE_OF_A_KIND_COUNT)) { return null; } int tripsRank = getRank(rankCount, THREE_OF_A_KIND_RANK); + List kickers = getKickers(allRanks, List.of(tripsRank), TWO_KICKERS); - return new HandRank(HandRank.Type.THREE_OF_A_KIND, List.of(tripsRank)); + List result = new ArrayList<>(List.of(tripsRank)); + result.addAll(kickers); + + return new HandRank(HandRank.Type.THREE_OF_A_KIND, result); } /** - * Helper method to check for a two pair hand rank. + * Helper method to check for a two pair hand rank. Stores the two pair ranks and 1 kicker for + * tie-breaking. The kicker is the highest remaining card that is not part of either pair. * * @param rankCount A map where the key is the card rank and the value is the count of * occurrences. + * @param allRanks A sorted list of all card ranks in the hand. * @return A HandRank object representing the two pair hand rank, or null if not found. */ - private static HandRank checkTwoPair(Map rankCount) { + private static HandRank checkTwoPair(Map rankCount, List allRanks) { long pairCount = rankCount.values().stream().filter(v -> v == PAIR).count(); @@ -294,17 +313,24 @@ public class HandEvaluator { .limit(PAIR) .toList(); - return new HandRank(HandRank.Type.TWO_PAIR, pairRanks); + List kickers = getKickers(allRanks, pairRanks, ONE_KICKER); + + List result = new ArrayList<>(pairRanks); + result.addAll(kickers); + + return new HandRank(HandRank.Type.TWO_PAIR, result); } /** - * Helper method to check for a one pair hand rank. + * Helper method to check for a one pair hand rank. Stores the pair rank and 3 kickers for + * tie-breaking. * * @param rankCount A map where the key is the card rank and the value is the count of * occurrences. + * @param allRanks A sorted list of all card ranks in the hand. * @return A HandRank object representing the one pair hand rank, or null if not found. */ - private static HandRank checkOnePair(Map rankCount) { + private static HandRank checkOnePair(Map rankCount, List allRanks) { long pairCount = rankCount.values().stream().filter(v -> v == PAIR).count(); @@ -313,8 +339,12 @@ public class HandEvaluator { } int pair = getRank(rankCount, PAIR); + List kickers = getKickers(allRanks, List.of(pair), THREE_KICKERS); - return new HandRank(HandRank.Type.ONE_PAIR, List.of(pair)); + List result = new ArrayList<>(List.of(pair)); + result.addAll(kickers); + + return new HandRank(HandRank.Type.ONE_PAIR, result); } /** @@ -333,6 +363,26 @@ public class HandEvaluator { .orElse(DEFAULT_VALUE); } + /** + * Helper method to extract kickers from the list of all card ranks. Excludes the ranks already + * used in the main hand combination. + * + * @param allRanks A sorted list of all card ranks in the hand. + * @param excludeRanks A list of ranks to exclude (e.g., ranks used in pairs, trips, etc.). + * @param numKickers The number of kickers to extract. + * @return A list of kicker ranks, sorted in descending order. + */ + private static List getKickers( + List allRanks, List excludeRanks, int numKickers) { + + Set excludeSet = new HashSet<>(excludeRanks); + + return allRanks.stream() + .filter(rank -> !excludeSet.contains(rank)) + .limit(numKickers) + .toList(); + } + /** * Helper method to determine if a list of card ranks forms a straight. * diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/showdown/CardsSpeakRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/showdown/CardsSpeakRule.java index 2be2607..fa602c1 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/showdown/CardsSpeakRule.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/showdown/CardsSpeakRule.java @@ -5,7 +5,6 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Card; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator.HandEvaluator; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator.HandRank; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.Player; -import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerStatus; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import java.util.ArrayList; @@ -43,14 +42,28 @@ public class CardsSpeakRule implements Rule { */ public Player determineWinner(GameState state) { + List winners = determineWinners(state); + return winners.isEmpty() ? null : winners.get(0); + } + + /** + * Determines all players that share the best hand rank. + * + * @param state The current state of the game. + * @return The list of winners, ordered by appearance in the state. + */ + public List determineWinners(GameState state) { + List players = new ArrayList<>(state.getPlayers()); - Player bestPlayer = null; HandRank bestRank = null; + List winners = new ArrayList<>(); for (Player player : players) { - if (player.getStatus() == PlayerStatus.FOLDED) { + // Use folded flag from game flow to stay consistent with winner calculation in + // controller. + if (player.isFolded()) { continue; } @@ -62,13 +75,15 @@ public class CardsSpeakRule implements Rule { HandRank rank = HandEvaluator.evaluate(cards); if (bestRank == null || rank.compareTo(bestRank) > 0) { - bestRank = rank; - bestPlayer = player; + winners.clear(); + winners.add(player); + } else if (rank.compareTo(bestRank) == 0) { + winners.add(player); } } - return bestPlayer; + return winners; } /** @@ -81,15 +96,23 @@ public class CardsSpeakRule implements Rule { */ public void awardPot(GameState state) { - Player winner = determineWinner(state); + List winners = determineWinners(state); - if (winner == null) { + if (winners.isEmpty()) { return; } int pot = state.getPot().getAmount(); + int share = winners.isEmpty() ? 0 : pot / winners.size(); + int remainder = winners.isEmpty() ? 0 : pot % winners.size(); - winner.addChips(pot); + for (int i = 0; i < winners.size(); i++) { + Player winner = winners.get(i); + if (winner == null) { + continue; + } + winner.addChips(share + (i < remainder ? 1 : 0)); + } state.getPot().reset(); } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java index 929b3e3..f47f5a5 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java @@ -18,6 +18,7 @@ public final class HighscoreService { private static final DateTimeFormatter DISPLAY_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); private static final int MAX_RETURNED_ENTRIES = 100; + private static final int MIN_MONEY_COLUMN_COUNT = 3; private static final String LINE_SEPARATOR = "\t"; private final Path storagePath; @@ -32,6 +33,11 @@ public final class HighscoreService { } public synchronized void appendWinner(String winnerName) { + appendWinner(winnerName, 0); + } + + /** Append a winner with an optional money value (money may be 0). */ + public synchronized void appendWinner(String winnerName, int money) { if (winnerName == null) { return; } @@ -41,6 +47,44 @@ public final class HighscoreService { return; } + String line; + if (money > 0) { + line = + Instant.now() + + LINE_SEPARATOR + + sanitized + + LINE_SEPARATOR + + money + + System.lineSeparator(); + } else { + // Keep legacy two-column format for backward compatibility + line = Instant.now() + LINE_SEPARATOR + sanitized + System.lineSeparator(); + } + + try { + Files.createDirectories(storagePath.getParent()); + Files.writeString( + storagePath, + line, + StandardCharsets.UTF_8, + StandardOpenOption.CREATE, + StandardOpenOption.APPEND); + } catch (IOException ignored) { + // Highscore persistence must never break the game state response path. + } + } + + /** Append a preformatted highscore entry such as "Lars (1000), Jona (1000)". */ + public synchronized void appendFormattedEntry(String entryText) { + if (entryText == null) { + return; + } + + String sanitized = sanitizeName(entryText); + if (sanitized.isEmpty()) { + return; + } + String line = Instant.now() + LINE_SEPARATOR + sanitized + System.lineSeparator(); try { @@ -74,15 +118,25 @@ public final class HighscoreService { if (raw == null || raw.isBlank()) { continue; } - - String[] parts = raw.split(LINE_SEPARATOR, 2); + // Support both legacy format (TIMESTAMP \t NAME) and new format (TIMESTAMP \t + // NAME \t MONEY) + String[] parts = raw.split(LINE_SEPARATOR); if (parts.length < 2) { continue; } try { Instant ts = Instant.parse(parts[0].trim()); - String display = DISPLAY_FORMATTER.format(ts) + " | " + parts[1].trim(); + String name = parts[1].trim(); + String display = DISPLAY_FORMATTER.format(ts) + " | " + name; + if (parts.length >= MIN_MONEY_COLUMN_COUNT) { + try { + int money = Integer.parseInt(parts[2].trim()); + display = display + " | $" + money; + } catch (NumberFormatException nfe) { + // ignore malformed money + } + } formatted.add(display); } catch (Exception ignored) { // Skip malformed lines and keep all valid entries. diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyCleanupJob.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyCleanupJob.java index 47c0413..713993c 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyCleanupJob.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/lobby/LobbyCleanupJob.java @@ -14,7 +14,8 @@ import org.apache.logging.log4j.Logger; /** Periodically removes expired empty lobbies and notifies connected sessions. */ public class LobbyCleanupJob implements Runnable { - private static final Logger LOGGER = LogManager.getLogger(LobbyCleanupJob.class); + private static final Logger LOGGER = + LogManager.getLogger(LobbyCleanupJob.class.getSimpleName()); private final LobbyManager lobbyManager; private final SessionManager sessionManager; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserCleanupJob.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserCleanupJob.java index 32cfbb4..9b520ab 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserCleanupJob.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserCleanupJob.java @@ -15,7 +15,7 @@ public class UserCleanupJob implements Runnable { private final Duration reconnectThreshold; public UserCleanupJob(UserRegistry registry, Duration reconnectThreshold) { - this.logger = LogManager.getLogger(UserCleanupJob.class); + this.logger = LogManager.getLogger(UserCleanupJob.class.getSimpleName()); this.registry = registry; this.reconnectThreshold = reconnectThreshold; } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserFactory.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserFactory.java index 0a41b0c..30f232a 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserFactory.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/user/UserFactory.java @@ -9,7 +9,7 @@ import org.apache.logging.log4j.Logger; public class UserFactory { private final UserRegistry registry; private final AtomicInteger anonymousCounter = new AtomicInteger(1); - private static final Logger LOGGER = LogManager.getLogger(UserFactory.class); + private static final Logger LOGGER = LogManager.getLogger(UserFactory.class.getSimpleName()); /** * Creates a new UserFactory backed by the given registry. diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java index 83a39d3..65e3780 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/NetworkManager.java @@ -26,7 +26,7 @@ public class NetworkManager implements Runnable { */ public NetworkManager(Integer port, SessionManager sessionManager, CommandRouter router) { this.port = port; - this.logger = LogManager.getLogger(NetworkManager.class); + this.logger = LogManager.getLogger(NetworkManager.class.getSimpleName()); this.thread = new Thread(this, "networkManager"); this.running = true; this.sessionManager = sessionManager; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionDisconnectJob.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionDisconnectJob.java index 6fd481d..04e90cc 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionDisconnectJob.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionDisconnectJob.java @@ -15,7 +15,7 @@ public class SessionDisconnectJob implements Runnable { public SessionDisconnectJob( SessionManager sessionManager, EventBus eventBus, Duration timeoutThreshold) { - this.logger = LogManager.getLogger(SessionDisconnectJob.class); + this.logger = LogManager.getLogger(SessionDisconnectJob.class.getSimpleName()); this.sessionManager = sessionManager; this.eventBus = eventBus; this.timeoutThreshold = timeoutThreshold; diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java index 569b828..1797668 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionManager.java @@ -24,7 +24,7 @@ public class SessionManager { public SessionManager(EventBus eventBus, CommandParserDispatcher dispatcher) { this.sessions = new ConcurrentHashMap<>(); this.eventBus = eventBus; - this.logger = LogManager.getLogger(SessionManager.class); + this.logger = LogManager.getLogger(SessionManager.class.getSimpleName()); this.dispatcher = dispatcher; } diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java index 4d5e353..585cd54 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionReader.java @@ -41,7 +41,7 @@ public class SessionReader implements Runnable { this.router = session.getRouter(); this.logger = LogManager.getLogger( - SessionReader.class.toString() + "-" + session.getId().value()); + SessionReader.class.getSimpleName() + "-" + session.getId().value()); } @Override diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java index c59abb3..20c11e0 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/network/sessions/SessionWriter.java @@ -18,7 +18,7 @@ public class SessionWriter implements Runnable { this.queue = session.getResponseQueue(); this.logger = LogManager.getLogger( - SessionReader.class.toString() + "-" + session.getId().value()); + SessionReader.class.getSimpleName() + "-" + session.getId().value()); } public void run() { diff --git a/src/main/resources/images/background.png b/src/main/resources/images/background.png index 2b84fa7..6788c65 100644 Binary files a/src/main/resources/images/background.png and b/src/main/resources/images/background.png differ diff --git a/src/main/resources/images/glass-walpaper-casono.png b/src/main/resources/images/glass-walpaper-casono.png new file mode 100644 index 0000000..ff38a64 Binary files /dev/null and b/src/main/resources/images/glass-walpaper-casono.png differ diff --git a/src/main/resources/images/parquet-black.png b/src/main/resources/images/parquet-black.png new file mode 100644 index 0000000..23b4ed7 Binary files /dev/null and b/src/main/resources/images/parquet-black.png differ diff --git a/src/main/resources/images/parquet.png b/src/main/resources/images/parquet.png new file mode 100644 index 0000000..7ab9c8b Binary files /dev/null and b/src/main/resources/images/parquet.png differ diff --git a/src/main/resources/images/profile-picture/poker_dealer_471.png b/src/main/resources/images/profile-picture/poker_dealer_471.png new file mode 100644 index 0000000..6df5761 Binary files /dev/null and b/src/main/resources/images/profile-picture/poker_dealer_471.png differ diff --git a/src/main/resources/images/profile-picture/poker_user_491.png b/src/main/resources/images/profile-picture/poker_user_491.png new file mode 100644 index 0000000..8e0047c Binary files /dev/null and b/src/main/resources/images/profile-picture/poker_user_491.png differ diff --git a/src/main/resources/images/rug-red.png b/src/main/resources/images/rug-red.png new file mode 100644 index 0000000..815fcda Binary files /dev/null and b/src/main/resources/images/rug-red.png differ diff --git a/src/main/resources/ui-structure/Casinogameui-blackwhite.css b/src/main/resources/ui-structure/Casinogameui-blackwhite.css new file mode 100644 index 0000000..779c2b6 --- /dev/null +++ b/src/main/resources/ui-structure/Casinogameui-blackwhite.css @@ -0,0 +1,726 @@ +/*main box css*/ + +.root { + -fx-background-color: #000000; + -fx-font-family: "Monospaced", "Courier New"; /* font */ +} + +.background { + -fx-background-image: url("/images/parquet-black.png"); + -fx-background-size: cover; /* Image fills the entire window */ + /* Centers the image and prevents it from repeating in a tile-like pattern. */ + -fx-background-position: center center; + -fx-background-repeat: no-repeat; +} + +.info-text { + -fx-text-fill: #ffffff; + -fx-font-size: 16px; +} + +.casino-table { + -fx-background-color: #000000; + -fx-background-radius: 210; + -fx-border-radius: 200; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 12; + /*-fx-min-height: 60vh;*/ + /*-fx-max-height: 60vh;*/ + /*-fx-min-width: 90vh;*/ + /*-fx-max-width: 90vh;*/ + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 15, 15); +} + +.dealer-box { + -fx-background-color: #ffffff; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-background-radius: 0; + -fx-border-radius: 0; + -fx-min-width: 70; + -fx-min-height: 100; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 4, 4); +} + +.table-title { + -fx-text-fill: #ffffff; + -fx-font-size: 28px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255), 0, 0, 3, 3);*/ +} + +.taskbar { + -fx-background-color: #000000; + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 10 25; + -fx-cursor: move; + -fx-effect: dropshadow(one-pass-box, rgba(0,0,0,0.8), 0, 0, 5, 5); +} + +.taskbar:hover { + -fx-border-color: #ffffff; +} + +.taskbar.player-active-turn { + /* Active turn should only change the shadow to avoid flicker/transparency jumps. */ + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +.black-input-field { + -fx-background-color: #000000 !important; + -fx-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: #333333 !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.black-input-field:hover { + -fx-translate-y: -3 !important; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3) !important; +} + +.black-input-field:focused { + -fx-border-color: #ffffff !important; + /*-fx-background-color: #000000;*/ +} + +.gray-input-field { + -fx-background-color: #000000 !important; + -fx-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: #333333 !important; + /*-fx-color-color: #000000;*/ + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.gray-input-field:hover { + -fx-translate-y: -3 !important; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3) !important; +} + +.gray-input-field:focused { + -fx-border-color: #ffffff !important; + /*-fx-background-color: #333333;*/ +} + +.yellow-input-field { + -fx-background-color: #b8860b !important; + -fx-text-fill: #ffffff !important; + -fx-prompt-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: #ffd700 !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.yellow-input-field:hover { + -fx-translate-y: -3 !important; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3) !important; +} + +/*.yellow-input-field:focused {*/ +/* -fx-border-color: #ffd700;*/ +/* -fx-background-color: #b8860b;*/ +/*}*/ + +.red-input-field { + -fx-background-color: #8b0000 !important; + -fx-text-fill: #ffffff !important; + -fx-prompt-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: #ff4444 !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.red-input-field:hover { + -fx-translate-y: -3 !important; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3) !important; +} + +/*.red-input-field:focused {*/ +/* -fx-border-color: #ff4444;*/ +/* -fx-background-color: #8b0000;*/ +/*}*/ + +.yellow-button { + -fx-background-color: #b8860b !important; + -fx-border-color: #ffd700 !important; + -fx-text-fill: #ffffff !important; +} + +.red-button { + -fx-background-color: #8b0000 !important; + -fx-border-color: #ff4444 !important; + -fx-text-fill: #ffffff !important; +} + +.gray-button { + -fx-background-color: #000000 !important; + -fx-border-color: #333333 !important; + -fx-text-fill: #ffffff !important; +} + +.gray-button .label { + -fx-text-fill: #ffffff !important; +} + +.green-button { + -fx-background-color: #000000 !important; + -fx-border-color: #333333 !important; + -fx-text-fill: #ffffff !important; +} + +.gray-button, .yellow-button, .red-button, .green-button { + -fx-background-radius: 12 !important; + -fx-border-radius: 12 !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-border-width: 2 !important; + -fx-padding: 6 15 !important; + -fx-cursor: hand !important; +} + +.gray-button:hover, .yellow-button:hover, .red-button:hover, .green-button:hover { + -fx-translate-y: -3 !important; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3) !important; +} + +.community-cards-box { + -fx-alignment: center !important; + -fx-spacing: 12 !important; + -fx-padding: 15 !important; + -fx-background-color: transparent !important; +} + +.community-card { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3); + + -fx-alignment: center; + -fx-spacing: 12; + -fx-padding: 20; + -fx-background-color: linear-gradient(to bottom, #0b3d2e, #06261d); + -fx-background-radius: 16; + -fx-scale-x: 1; + -fx-scale-y: 1; + -fx-translate-y: 0; + -fx-opacity: 1; +} + +/*.community-card:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.player-card { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3); + + -fx-alignment: center; + -fx-spacing: 12; + -fx-padding: 20; + -fx-background-color: linear-gradient(to bottom, #0b3d2e, #06261d); + -fx-background-radius: 16; + -fx-scale-x: 1; + -fx-scale-y: 1; + -fx-translate-y: 0; + -fx-opacity: 1; +} + +/*.player-card:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.player-card.player-cards-active-turn { + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +.dealer-box.player-cards-active-turn { + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +/*.dealer-box:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.chips-icon { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3); +} + +/*.chips-icon:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.dealer-icon { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3); +} + +.player-cards-box { + -fx-padding: 10; +} + +/*.player-cards-box.player-cards-active-turn {*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.money-value { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.pot-title { + -fx-text-fill: #ffffff; + -fx-font-size: 14px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3);*/ +} + +.pot-box { + -fx-padding: 5; +} + +.player-status-pane { + -fx-background-color: rgba(13, 158, 59, 0); + -fx-background-radius: 15; + -fx-border-radius: 15; + /* -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 2; + -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.5), 10, 0, 0, 4);*/ +} + +.player-status-pane:hover { + -fx-translate-y: -3; +} + +.status-inner-box-top { + -fx-background-color: #000000; + -fx-background-radius: 19; + -fx-border-radius: 15; + -fx-padding: 5 10; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.status-inner-box-midle { + -fx-background-color: rgba(13, 158, 59, 0); +} + +.status-inner-box-bottom { + -fx-background-color: #000000; + -fx-background-radius: 19; + -fx-border-radius: 15; + -fx-padding: 5 10; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.status-inner-box-top.player-status-active-turn { + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +.status-inner-box-bottom.player-status-active-turn { + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +.status-label-small { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.status-value-text { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.status-value-money { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.status-circle { + -fx-background-color: #000000; + -fx-background-radius: 50; + -fx-border-color: #ffffff; + -fx-border-width: 1; + -fx-border-radius: 50; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.browser-root { + -fx-background-image: url("/images/background.png"); + -fx-background-size: cover; + -fx-background-position: center; + -fx-background-color: #0b2d15; +} + +.browser-web-view { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 0; + -fx-cursor: default; + -fx-effect: dropshadow(one-pass-box, rgba(0,0,0,0.8), 0, 0, 5, 5); +} + +.web-view { + -fx-background-color: transparent; +} + +.taskbar-browser { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 10 25; + -fx-cursor: default; + -fx-effect: dropshadow(one-pass-box, rgba(0,0,0,0.8), 0, 0, 5, 5); +} + +/*.taskbar-browser:hover {*/ +/* -fx-border-color: #ffffff;*/ +/*}*/ + +.security-label { + -fx-text-fill: #000000; + -fx-font-weight: bold; + -fx-font-size: 16px; +} + +.context-menu { + -fx-background-color: #333333 !important; + -fx-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: #333333 !important; + -fx-color-color: #cccccc !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.context-menu .menu-item { + -fx-background-color: #333333 !important; +} + +.context-menu .menu-item .label { + -fx-text-fill: #ffffff !important; + -fx-font-size: 13px !important; +} + +/*.context-menu .menu-item:focused {*/ +/* -fx-background-color: #333333;*/ +/*}*/ + +.context-menu .menu-item:hover .label, +.context-menu .menu-item:focused .label { + /*-fx-text-fill: #b8860b;*/ + -fx-font-size: 15px !important; +} + +.notebook { + -fx-background-color: #000000; + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgb(255, 255, 255), 0, 0, 5, 5); + /*-fx-cursor: move;*/ + + -fx-padding: 0 0 15 0; +} + +.notebook:hover { + -fx-border-color: #ffffff; +} + +/*.notebook.player-active-turn {*/ +/* !* Active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.notebook-header { + -fx-background-color: #000000; + -fx-background-radius: 25 25 0 0; + -fx-padding: 8 10; + -fx-cursor: move; +} + +.notebook-title { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, rgb(255, 255, 255), 0, 0, 3, 3);*/ +} + +.notebook-content { + -fx-background-color: transparent; + -fx-padding: 0; +} + +.notebook-content > .viewport { + -fx-background-color: #000000; + -fx-background-radius: 0 0 25 25; +} + + +.scroll-pane { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +.scroll-pane .scroll-bar:vertical, +.scroll-pane .scroll-bar:horizontal { + -fx-background-color: transparent; +} + +.scroll-pane .scroll-bar .track { + -fx-background-color: #000000; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb { + -fx-background-color: #333333; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb:hover { + -fx-background-color: #666666; +} + +.notebook-tips-list { + -fx-padding: 10; + -fx-spacing: 10; + -fx-background-color: transparent; +} + +.notebook-tips-list .text-flow { + -fx-padding: 12; + -fx-background-color: #000000; + -fx-background-radius: 15; +} + +.settings-box { + -fx-background-color: #000000; + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgb(255, 255, 255), 0, 0, 5, 5); + /*-fx-cursor: move;*/ + -fx-padding: 0 0 15 0; +} + +.settings-box:hover { + -fx-border-color: #ffffff; +} + +/*.settings-box.player-active-turn {*/ +/* !* Active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.settings-header { + -fx-background-color: transparent; + -fx-background-radius: 20 20 0 0; + -fx-padding: 10 15; + -fx-cursor: move; + -fx-border-width: 0 0 1 0; +} + +.settings-label { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, rgb(255, 255, 255), 0, 0, 3, 3);*/ +} + +.settings-content { + -fx-padding: 15 20 20 20; + -fx-spacing: 12; +} + +.settings-content { + -fx-background-color: transparent; + -fx-text-fill: #ffffff; + -fx-font-size: 13px; +} + +.radio-button { + -fx-text-fill: #ffffff; + -fx-font-size: 13px; + + -fx-padding: 6 8; + -fx-background-radius: 10; +} + +.radio-button:hover { + -fx-font-size: 15px; +} + +.radio-button:selected { + -fx-text-fill: #b8860b; +} + +.radio-button .radio { + -fx-background-color: #b8860b; + /*-fx-border-width: 3;*/ + /*-fx-border-color: #b8860b;*/ + /*-fx-border-radius: 50%;*/ +} + +.radio-button:selected .radio { + -fx-background-color: #b8860b; + -fx-effect: dropshadow(gaussian, rgba(184,134,11,0.8), 6, 0.3, 0, 0); +} + +.settings-box { + -fx-background-insets: 0; +} + +.settings-content { + -fx-background-color: transparent; +} + +/*chat box css*/ + +.chat-box { + -fx-background-color: #000000 !important; + -fx-background-radius: 58; + -fx-border-radius: 50; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10 !important; + -fx-border-width: 12; + -fx-padding: 20; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255), 0, 0, 15, 15) !important; +} + +.tab-chat-box { + -fx-background-color: #000000 !important; + -fx-background-radius: 23 !important; + -fx-border-radius: 20 !important; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10 !important; + -fx-border-width: 3 !important; + -fx-padding: 20; +} + +.chat-header { + -fx-text-fill: #ffffff !important; + -fx-font-size: 22px; + -fx-font-weight: bold; + -fx-padding: 0 0 10 0; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 0, 0) !important; +} + +.chat-separator { + -fx-background-color: #ffffff !important; + -fx-min-height: 4px; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 0, 0) !important; +} + +.tab-pane .tab-header-area .tab-header-background { + -fx-opacity: 0; + -fx-background-color: #000000 !important; + -fx-background-radius: 18 !important; + -fx-border-radius: 10 !important; + -fx-border-color: #000000 !important; + -fx-border-width: 5; + -fx-padding: 10; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0), 0, 0, 0, 0) !important; + -fx-min-height: 10; +} + +.tab-pane .tab { + -fx-background-color: #000000 !important; + -fx-background-radius: 18 !important; + -fx-border-radius: 10 !important; + -fx-border-color: #000000 !important; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0), 0, 0, 0, 0) !important; + -fx-border-width: 5; + -fx-padding: 6 15; +} + +.tab-pane .tab:selected { + -fx-background-color: #000000 !important; + -fx-background-radius: 18 !important; + -fx-border-radius: 10 !important; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10 !important; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0), 0, 0, 0, 0) !important; + -fx-border-width: 3 !important; + -fx-padding: 6 15; +} + +.tab-pane .tab .tab-close-button { + -fx-text-fill: #ffffff; +} + +.tab-pane .tab-header-area .headers-region { + -fx-background-color: #000000 !important; +} + +.tab .tab-label { + -fx-alignment: CENTER; + -fx-text-fill: #ffffff; + -fx-font-size: 12px; + -fx-font-weight: bold; +} + +.tab:selected .tab-label { + -fx-alignment: CENTER; + -fx-text-fill: #ffffff; +} + +.menu-button { + -fx-background-color: #000000 !important; + -fx-border-color: #333333 !important; + -fx-text-fill: #ffffff !important; + -fx-background-radius: 12 !important; + -fx-border-radius: 12 !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-border-width: 2 !important; + -fx-padding: 6 15 !important; + -fx-cursor: hand !important; +} + +.menu-button .label { + -fx-text-fill: #ffffff !important; +} + +.menu-button:hover { + -fx-translate-y: -3 !important; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.8), 0, 0, 3, 3) !important; +} + +.menu-button .menu-item { + -fx-background-color: #333333 !important; + -fx-border-color: #333333 !important; + -fx-text-fill: #ffffff; +} diff --git a/src/main/resources/ui-structure/Casinogameui-glass.css b/src/main/resources/ui-structure/Casinogameui-glass.css new file mode 100644 index 0000000..e36d99d --- /dev/null +++ b/src/main/resources/ui-structure/Casinogameui-glass.css @@ -0,0 +1,1123 @@ +/*main box css*/ + +.root { + -fx-background-color: #000000; + -fx-font-family: "Monospaced", "Courier New"; /* font */ +} + +.background { + -fx-background-color: #000000; + -fx-background-image: url("/images/glass-walpaper-casono.png"); + -fx-background-size: cover; /* Image fills the entire window */ + /* Centers the image and prevents it from repeating in a tile-like pattern. */ + -fx-background-position: center center; + -fx-background-repeat: no-repeat; +} + +.info-text { + -fx-text-fill: #ffffff; + -fx-font-size: 16px; +} + +.casino-table { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(255 ,255 ,255 ,0.18), + rgba(255 ,255 ,255 ,0.08) + ); + -fx-background-radius: 210; + -fx-border-radius: 200; + -fx-border-color: rgba(255, 255, 255, 0.25); + -fx-border-width: 12; + /*-fx-min-height: 60vh;*/ + /*-fx-max-height: 60vh;*/ + /*-fx-min-width: 90vh;*/ + /*-fx-max-width: 90vh;*/ + -fx-effect: + dropshadow( + gaussian, + rgba(0 ,0 ,0 , 0.25), + 40, + 0.2, + 0, + 12 + ); +} + +.dealer-box { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(255 ,255 ,255 ,0.38), + rgba(255 ,255 ,255 ,0.18) + ); + + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-background-radius: 28; + -fx-border-radius: 0; + -fx-min-width: 70; + -fx-min-height: 100; + -fx-effect: + dropshadow( + gaussian, + rgba(0 ,0 ,0 , 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.table-title { + -fx-text-fill: #ffffff; + -fx-font-size: 28px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255), 0, 0, 3, 3);*/ +} + +.taskbar { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(255, 255, 255, 0.38), + rgba(255, 255, 255, 0.18) + ); + + -fx-background-radius: 28; + -fx-border-radius: 28; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-padding: 14 30; + -fx-effect: + dropshadow( + gaussian, + rgba(0, 0, 0, 0.45), + 40, + 0.2, + 0, + 12 + ); + + -fx-cursor: move; +} + +.taskbar:hover { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(255, 255, 255, 0.44), + rgba(255, 255, 255, 0.22) + ); + + -fx-border-color: + rgba(255, 255, 255, 0.55); + +} + +.taskbar.player-active-turn { + /* Active turn should only change the shadow to avoid flicker/transparency jumps. */ + -fx-border-color: rgba(184, 134, 11, 0.40); + -fx-effect: + dropshadow( + gaussian, + rgba(184, 134, 11, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.black-input-field { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ) !important; + -fx-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(255, 255, 255, 0.40) !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.black-input-field:hover { + -fx-translate-y: -3 !important; + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.44), + rgba(0, 0, 0, 0.22) + ) !important; +} + +.black-input-field:focused { + -fx-border-color: rgba(255, 255, 255, 0.55) !important; + /*-fx-background-color: #000000;*/ +} + +.gray-input-field { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ) !important; + -fx-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(255, 255, 255, 0.40) !important; + /*-fx-color-color: #000000;*/ + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.gray-input-field:hover { + -fx-translate-y: -3 !important; + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.45), + 40, + 0.2, + 0, + 12 + ) !important; +} + +.gray-input-field:focused { + -fx-border-color: rgba(255, 255, 255, 0.55) !important; + /*-fx-background-color: #333333;*/ +} + +.yellow-input-field { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(184, 134, 11, 0.38), + rgba(184, 134, 11, 0.18) + ) !important; + -fx-text-fill: #ffffff !important; + -fx-prompt-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(255, 215, 0, 0.40) !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.yellow-input-field:hover { + -fx-translate-y: -3 !important; + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.55), + 40, + 0.2, + 0, + 12 + ) !important; +} + +/*.yellow-input-field:focused {*/ +/* -fx-border-color: #ffd700;*/ +/* -fx-background-color: #b8860b;*/ +/*}*/ + +.red-input-field { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(139, 0, 0, 0.38), + rgba(139, 0, 0, 0.18) + ) !important; + -fx-text-fill: #ffffff !important; + -fx-prompt-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(255, 68, 68, 0.40) !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.red-input-field:hover { + -fx-translate-y: -3 !important; + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.55), + 40, + 0.2, + 0, + 12 + ) !important; +} + +/*.red-input-field:focused {*/ +/* -fx-border-color: #ff4444;*/ +/* -fx-background-color: #8b0000;*/ +/*}*/ + +.yellow-button { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(184, 134, 11, 0.38), + rgba(184, 134, 11, 0.18) + ) !important; + -fx-border-color: rgba(255, 215, 0, 0.40) !important; + -fx-text-fill: #ffffff !important; +} + +.red-button { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(139, 0, 0, 0.38), + rgba(139, 0, 0, 0.18) + ) !important; + -fx-border-color: rgba(255, 68, 68, 0.40) !important; + -fx-text-fill: #ffffff !important; +} + +.gray-button { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ) !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-text-fill: #ffffff !important; +} + +.gray-button .label { + -fx-text-fill: #ffffff !important; +} + +.green-button { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ) !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-text-fill: #ffffff !important; +} + +.gray-button, .yellow-button, .red-button, .green-button { + -fx-background-radius: 12 !important; + -fx-border-radius: 12 !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-border-width: 2 !important; + -fx-padding: 6 15 !important; + -fx-cursor: hand !important; +} + +.gray-button:hover, .yellow-button:hover, .red-button:hover, .green-button:hover { + -fx-translate-y: -3 !important; + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.45), + 40, + 0.2, + 0, + 12 + ) !important; +} + +.community-cards-box { + -fx-alignment: center !important; + -fx-spacing: 12 !important; + -fx-padding: 15 !important; + -fx-background-color: transparent !important; +} + +.community-card { + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.45), + 40, + 0.2, + 0, + 12 + ) !important; + + -fx-alignment: center; + -fx-spacing: 12; + -fx-padding: 20; + -fx-background-color: linear-gradient(to bottom, #0b3d2e, #06261d); + -fx-background-radius: 16; + -fx-scale-x: 1; + -fx-scale-y: 1; + -fx-translate-y: 0; + -fx-opacity: 1; +} + +.community-card:hover { + -fx-translate-y: -3; +} + +.player-card { + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.45), + 40, + 0.2, + 0, + 12 + ) !important; + + -fx-alignment: center; + -fx-spacing: 12; + -fx-padding: 20; + -fx-background-color: linear-gradient(to bottom, #0b3d2e, #06261d); + -fx-background-radius: 16; + -fx-scale-x: 1; + -fx-scale-y: 1; + -fx-translate-y: 0; + -fx-opacity: 1; +} + +.player-card:hover { + -fx-translate-y: -3; +} + +.player-card.player-cards-active-turn { + -fx-effect: + dropshadow( + gaussian, + rgba(184, 134, 11, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.dealer-box.player-cards-active-turn { + -fx-effect: + dropshadow( + gaussian, + rgba(184, 134, 11, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +/*.dealer-box:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.chips-icon { + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.45), + 40, + 0.2, + 0, + 12 + ) !important; +} + +/*.chips-icon:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.dealer-icon { + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.45), + 40, + 0.2, + 0, + 12 + ) !important; +} + +.player-cards-box { + -fx-padding: 10; +} + +/*.player-cards-box.player-cards-active-turn {*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.money-value { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.pot-title { + -fx-text-fill: #ffffff; + -fx-font-size: 14px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3);*/ +} + +.pot-box { + -fx-padding: 5; +} + +.player-status-pane { + -fx-background-color: transparent; + -fx-background-radius: 15; + -fx-border-radius: 15; + /* -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 2; + -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.5), 10, 0, 0, 4);*/ +} + +.player-status-pane:hover { + -fx-translate-y: -3; +} + +.status-inner-box-top { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ) !important; + -fx-background-radius: 19; + -fx-border-radius: 15; + -fx-padding: 5 10; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-effect: + dropshadow( + gaussian, + rgba(0, 0, 0, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.status-inner-box-midle { + -fx-background-color: rgba(13, 158, 59, 0); +} + +.status-inner-box-bottom { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ) !important; + -fx-background-radius: 19; + -fx-border-radius: 15; + -fx-padding: 5 10; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-effect: + dropshadow( + gaussian, + rgba(0, 0, 0, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.status-inner-box-top.player-status-active-turn { + -fx-effect: + dropshadow( + gaussian, + rgba(184, 134, 11, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.status-inner-box-bottom.player-status-active-turn { + -fx-effect: + dropshadow( + gaussian, + rgba(184, 134, 11, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.status-label-small { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.status-value-text { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.status-value-money { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.status-circle { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ) !important; + -fx-background-radius: 50; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 1; + -fx-border-radius: 50; + -fx-effect: + dropshadow( + gaussian, + rgba(0, 0, 0, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.browser-root { + -fx-background-image: url("/images/background.png"); + -fx-background-size: cover; + -fx-background-position: center; + -fx-background-color: #0b2d15; +} + +.browser-web-view { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 0; + -fx-cursor: default; + -fx-effect: + dropshadow( + gaussian, + rgba(0, 0, 0, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.web-view { + -fx-background-color: transparent; +} + +.taskbar-browser { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 10 25; + -fx-cursor: default; + -fx-effect: + dropshadow( + gaussian, + rgba(0, 0, 0, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +/*.taskbar-browser:hover {*/ +/* -fx-border-color: #ffffff;*/ +/*}*/ + +.security-label { + -fx-text-fill: #000000; + -fx-font-weight: bold; + -fx-font-size: 16px; +} + +.context-menu { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(51, 51, 51, 0.38), + rgba(51, 51, 51, 0.18) + ) !important; + -fx-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-color-color: #cccccc !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.context-menu .menu-item { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(51, 51, 51, 0.38), + rgba(51, 51, 51, 0.18) + ) !important; +} + +.context-menu .menu-item .label { + -fx-text-fill: #ffffff !important; + -fx-font-size: 13px !important; +} + +/*.context-menu .menu-item:focused {*/ +/* -fx-background-color: #333333;*/ +/*}*/ + +.context-menu .menu-item:hover .label, +.context-menu .menu-item:focused .label { + /*-fx-text-fill: #b8860b;*/ + -fx-font-size: 15px !important; +} + +.notebook { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.45), + 40, + 0.2, + 0, + 12 + ) !important; + /*-fx-cursor: move;*/ + -fx-padding: 0 0 15 0; +} + +.notebook:hover { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.44), + rgba(0, 0, 0, 0.22) + ); + + -fx-border-color: + rgba(255, 255, 255, 0.55); +} + +/*.notebook.player-active-turn {*/ +/* !* active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-border-color:*/ +/* rgba(184, 134, 11, 0.55);*/ +/* -fx-effect:*/ +/* dropshadow(*/ +/* gaussian,*/ +/* rgba(184, 134, 11, 0.45),*/ +/* 40,*/ +/* 0.2,*/ +/* 0,*/ +/* 12*/ +/* );*/ +/*}*/ + +.notebook-header { + -fx-background-color: transparent !important; + -fx-background-radius: 25 25 0 0; + -fx-padding: 8 10; + -fx-cursor: move; +} + +.notebook-title { + -fx-text-fill: #ffffff; + -fx-background-color: transparent !important; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, rgb(255, 255, 255), 0, 0, 3, 3);*/ +} + +.notebook-content { + -fx-background-color: transparent; + -fx-padding: 0; +} + +.notebook-content > .viewport { + -fx-background-color: transparent !important; + -fx-background-radius: 0 0 25 25; +} + + +.scroll-pane { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +.scroll-pane .scroll-bar:vertical, +.scroll-pane .scroll-bar:horizontal { + -fx-background-color: transparent; +} + +.scroll-pane .scroll-bar .track { + -fx-background-color: transparent !important; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(200, 200, 200, 0.38), + rgba(200, 200, 200, 0.18) + ) !important; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb:hover { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(255, 255, 255, 0.38), + rgba(255, 255, 255, 0.18) + ) !important; +} + +.notebook-tips-list { + -fx-padding: 10; + -fx-spacing: 10; + -fx-background-color: transparent; +} + +.notebook-tips-list .text-flow { + -fx-padding: 12; + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ); + -fx-background-radius: 15; +} + +.settings-box { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.45), + 40, + 0.2, + 0, + 12 + ) !important; + /*-fx-cursor: move;*/ + -fx-padding: 0 0 15 0; +} + +.settings-box:hover { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.44), + rgba(0, 0, 0, 0.22) + ); + + -fx-border-color: + rgba(255, 255, 255, 0.55); +} + +/*.settings-box.player-active-turn {*/ +/* !* Active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-border-color:*/ +/* rgba(184, 134, 11, 0.55);*/ +/* -fx-effect:*/ +/* dropshadow(*/ +/* gaussian,*/ +/* rgba(184, 134, 11, 0.45),*/ +/* 40,*/ +/* 0.2,*/ +/* 0,*/ +/* 12*/ +/* );*/ +/*}*/ + +.settings-header { + -fx-background-color: transparent; + -fx-background-radius: 20 20 0 0; + -fx-padding: 10 15; + -fx-cursor: move; + -fx-border-width: 0 0 1 0; +} + +.settings-label { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, rgb(255, 255, 255), 0, 0, 3, 3);*/ +} + +.settings-content { + -fx-padding: 15 20 20 20; + -fx-spacing: 12; +} + +.settings-content { + -fx-background-color: transparent; + -fx-text-fill: #ffffff; + -fx-font-size: 13px; +} + +.radio-button { + -fx-text-fill: #ffffff; + -fx-font-size: 13px; + + -fx-padding: 6 8; + -fx-background-radius: 10; +} + +.radio-button:hover { + -fx-font-size: 15px; +} + +.radio-button:selected { + -fx-text-fill: #ffffff; +} + +.radio-button .radio { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(184, 134, 11, 0.38), + rgba(184, 134, 11, 0.18) + ); + /*-fx-border-width: 3;*/ + /*-fx-border-color: #b8860b;*/ + /*-fx-border-radius: 50%;*/ +} + +.radio-button:selected .radio { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(184, 134, 11, 0.38), + rgba(184, 134, 11, 0.18) + ); + -fx-effect: + dropshadow( + gaussian, + rgba(184, 134, 11, 0.45), + 40, + 0.2, + 0, + 12 + ); +} + +.settings-box { + -fx-background-insets: 0; +} + +.settings-content { + -fx-background-color: transparent; +} + +/*chat box css*/ + +.chat-box { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(255, 255, 255, 0.18), + rgba(255, 255, 255, 0.08) + ) !important; + -fx-background-radius: 58; + -fx-border-radius: 50; + -fx-border-color: rgba(255, 255, 255, 0.25) !important; + -fx-border-width: 12; + -fx-padding: 20; + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.25), + 40, + 0.2, + 0, + 12 + ) !important; +} + +.tab-chat-box { + -fx-background-color: transparent !important; + -fx-background-radius: 23 !important; + -fx-border-radius: 20 !important; + -fx-border-color: rgba(255, 255, 255, 0.40) !important; + -fx-border-width: 3 !important; + -fx-padding: 20; +} + +.chat-header { + -fx-text-fill: #ffffff !important; + -fx-font-size: 22px; + -fx-font-weight: bold; + -fx-padding: 0 0 10 0; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 0, 0) !important; +} + +.chat-separator { + -fx-background-color: transparent !important; + -fx-min-height: 4px; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 0, 0) !important; +} + +.tab-header-area .tab-header-background { + -fx-background-color: transparent !important; + -fx-background-radius: 18 !important; + -fx-border-radius: 10 !important; + -fx-border-color: rgba(0, 0, 0, 0) !important; + -fx-border-width: 5; + -fx-padding: 10; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0), 0, 0, 0, 0) !important; + -fx-min-height: 10; +} + +.tab-pane .tab { + -fx-background-color: transparent !important; + -fx-background-radius: 18 !important; + -fx-border-radius: 10 !important; + -fx-border-color: rgba(255, 255, 255, 0) !important; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0), 0, 0, 0, 0) !important; + -fx-border-width: 5; + -fx-padding: 6 15; +} + +.tab-pane .tab:selected { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ) !important; + -fx-background-radius: 18 !important; + -fx-border-radius: 10 !important; + -fx-border-color: rgba(255, 255, 255, 0.40) !important; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0), 0, 0, 0, 0) !important; + -fx-border-width: 3 !important; + -fx-padding: 6 15; +} + +.tab-pane .tab .tab-close-button { + -fx-text-fill: #ffffff !important; +} + +.tab-pane .tab-header-area .headers-region { + -fx-background-color: transparent !important; +} + +.tab .tab-label { + -fx-alignment: CENTER; + -fx-text-fill: #ffffff; + -fx-font-size: 12px; + -fx-font-weight: bold; +} + +.tab:selected .tab-label { + -fx-alignment: CENTER; + -fx-text-fill: #ffffff; +} + +.menu-button { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18) + ) !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-text-fill: #ffffff !important; + -fx-background-radius: 12 !important; + -fx-border-radius: 12 !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-border-width: 2 !important; + -fx-padding: 6 15 !important; + -fx-cursor: hand !important; +} + +.menu-button .label { + -fx-text-fill: #ffffff !important; +} + +.menu-button:hover { + -fx-translate-y: -3 !important; + -fx-effect: + dropshadow( + gaussian, + rgba(255, 255, 255, 0.45), + 40, + 0.2, + 0, + 12 + ) !important; +} + +.menu-button .menu-item { + -fx-background-color: + linear-gradient( + to bottom right, + rgba(51, 51, 51, 0.38), + rgba(51, 51, 51, 0.18) + ) !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-text-fill: #ffffff; +} diff --git a/src/main/resources/ui-structure/Casinogameui-light-glass.css b/src/main/resources/ui-structure/Casinogameui-light-glass.css new file mode 100644 index 0000000..4a32bbf --- /dev/null +++ b/src/main/resources/ui-structure/Casinogameui-light-glass.css @@ -0,0 +1,846 @@ +/*main box css*/ + +.root { + -fx-background-color: #000000; + -fx-font-family: "Monospaced", "Courier New"; +} + +.background { + -fx-background-color: #000000; + -fx-background-image: url("/images/glass-walpaper-casono.png"); + -fx-background-size: cover; + /* Centers the image and prevents it from repeating in a tile-like pattern. */ + -fx-background-position: center center; + -fx-background-repeat: no-repeat; +} + +.info-text { + -fx-text-fill: #ffffff; + -fx-font-size: 16px; +} + +.casino-table { + -fx-background-color: + linear-gradient(to bottom right, + rgba(255, 255, 255, 0.18), + rgba(255, 255, 255, 0.08)); + -fx-background-radius: 210; + -fx-border-radius: 200; + -fx-border-color: rgba(255, 255, 255, 0.25); + -fx-border-width: 12; + /*-fx-min-height: 60vh;*/ + /*-fx-max-height: 60vh;*/ + /*-fx-min-width: 90vh;*/ + /*-fx-max-width: 90vh;*/ + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.30), 12, 0, 0, 4); +} + +.dealer-box { + -fx-background-color: + linear-gradient(to bottom right, + rgba(255, 255, 255, 0.38), + rgba(255, 255, 255, 0.18)); + + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-background-radius: 28; + -fx-border-radius: 0; + -fx-min-width: 70; + -fx-min-height: 100; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.35), 10, 0, 0, 3); +} + +.table-title { + -fx-text-fill: #ffffff; + -fx-font-size: 28px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255), 0, 0, 3, 3);*/ +} + +.taskbar { + -fx-background-color: + linear-gradient(to bottom right, + rgba(255, 255, 255, 0.38), + rgba(255, 255, 255, 0.18)); + + -fx-background-radius: 28; + -fx-border-radius: 28; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-padding: 14 30; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.30), 10, 0, 0, 3); + + -fx-cursor: move; +} + +.taskbar:hover { + -fx-background-color: + linear-gradient(to bottom right, + rgba(255, 255, 255, 0.44), + rgba(255, 255, 255, 0.22)); + + -fx-border-color: + rgba(255, 255, 255, 0.55); +} + +.taskbar.player-active-turn { + /* Active turn should only change the shadow to avoid flicker/transparency jumps. */ + -fx-border-color: rgba(184, 134, 11, 0.40); + -fx-effect: dropshadow(one-pass-box, rgba(184, 134, 11, 0.30), 10, 0, 0, 3); +} + +.black-input-field { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)) !important; + -fx-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(255, 255, 255, 0.40) !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.black-input-field:hover { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.44), + rgba(0, 0, 0, 0.22)) !important; +} + +.black-input-field:focused { + -fx-border-color: rgba(255, 255, 255, 0.55) !important; + /*-fx-background-color: #000000;*/ +} + +.gray-input-field { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)) !important; + -fx-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(255, 255, 255, 0.40) !important; + /*-fx-color-color: #000000;*/ + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.gray-input-field:hover { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; +} + +.gray-input-field:focused { + -fx-border-color: rgba(255, 255, 255, 0.55) !important; + /*-fx-background-color: #333333;*/ +} + +.yellow-input-field { + -fx-background-color: + linear-gradient(to bottom right, + rgba(184, 134, 11, 0.38), + rgba(184, 134, 11, 0.18)) !important; + -fx-text-fill: #ffffff !important; + -fx-prompt-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(255, 215, 0, 0.40) !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.yellow-input-field:hover { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; +} + +/*.yellow-input-field:focused {*/ +/* -fx-border-color: #ffd700;*/ +/* -fx-background-color: #b8860b;*/ +/*}*/ + +.red-input-field { + -fx-background-color: + linear-gradient(to bottom right, + rgba(139, 0, 0, 0.38), + rgba(139, 0, 0, 0.18)) !important; + -fx-text-fill: #ffffff !important; + -fx-prompt-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(255, 68, 68, 0.40) !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.red-input-field:hover { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; +} + +/*.red-input-field:focused {*/ +/* -fx-border-color: #ff4444;*/ +/* -fx-background-color: #8b0000;*/ +/*}*/ + +.yellow-button { + -fx-background-color: + linear-gradient(to bottom right, + rgba(184, 134, 11, 0.38), + rgba(184, 134, 11, 0.18)) !important; + -fx-border-color: rgba(255, 215, 0, 0.40) !important; + -fx-text-fill: #ffffff !important; +} + +.red-button { + -fx-background-color: + linear-gradient(to bottom right, + rgba(139, 0, 0, 0.38), + rgba(139, 0, 0, 0.18)) !important; + -fx-border-color: rgba(255, 68, 68, 0.40) !important; + -fx-text-fill: #ffffff !important; +} + +.gray-button { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)) !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-text-fill: #ffffff !important; +} + +.gray-button .label { + -fx-text-fill: #ffffff !important; +} + +.green-button { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)) !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-text-fill: #ffffff !important; +} + +.gray-button, +.yellow-button, +.red-button, +.green-button { + -fx-background-radius: 12 !important; + -fx-border-radius: 12 !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-border-width: 2 !important; + -fx-padding: 6 15 !important; + -fx-cursor: hand !important; +} + +.gray-button:hover, +.yellow-button:hover, +.red-button:hover, +.green-button:hover { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; +} + +.community-cards-box { + -fx-alignment: center !important; + -fx-spacing: 12 !important; + -fx-padding: 15 !important; + -fx-background-color: transparent !important; +} + +.community-card { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; + + -fx-alignment: center; + -fx-spacing: 12; + -fx-padding: 20; + -fx-background-color: linear-gradient(to bottom, #0b3d2e, #06261d); + -fx-background-radius: 16; + -fx-scale-x: 1; + -fx-scale-y: 1; + -fx-translate-y: 0; + -fx-opacity: 1; +} + +.community-card:hover {} + +.player-card { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; + + -fx-alignment: center; + -fx-spacing: 12; + -fx-padding: 20; + -fx-background-color: linear-gradient(to bottom, #0b3d2e, #06261d); + -fx-background-radius: 16; + -fx-scale-x: 1; + -fx-scale-y: 1; + -fx-translate-y: 0; + -fx-opacity: 1; +} + +.player-card:hover {} + +.player-card.player-cards-active-turn { + -fx-effect: dropshadow(one-pass-box, rgba(184, 134, 11, 0.30), 10, 0, 0, 3); +} + +.dealer-box.player-cards-active-turn { + -fx-effect: dropshadow(one-pass-box, rgba(184, 134, 11, 0.30), 10, 0, 0, 3); +} + +/*.dealer-box:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.chips-icon { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; +} + +/*.chips-icon:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.dealer-icon { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; +} + +.player-cards-box { + -fx-padding: 10; +} + +/*.player-cards-box.player-cards-active-turn {*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.money-value { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.pot-title { + -fx-text-fill: #ffffff; + -fx-font-size: 14px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3);*/ +} + +.pot-box { + -fx-padding: 5; +} + +.player-status-pane { + -fx-background-color: transparent; + -fx-background-radius: 15; + -fx-border-radius: 15; + /* -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; +-fx-border-width: 2; +-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.5), 10, 0, 0, 4);*/ +} + +.player-status-pane:hover {} + +.status-inner-box-top { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)) !important; + -fx-background-radius: 19; + -fx-border-radius: 15; + -fx-padding: 5 10; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.30), 10, 0, 0, 3); +} + +.status-inner-box-midle { + -fx-background-color: rgba(13, 158, 59, 0); +} + +.status-inner-box-bottom { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)) !important; + -fx-background-radius: 19; + -fx-border-radius: 15; + -fx-padding: 5 10; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.30), 10, 0, 0, 3); +} + +.status-inner-box-top.player-status-active-turn { + -fx-effect: dropshadow(one-pass-box, rgba(184, 134, 11, 0.30), 10, 0, 0, 3); +} + +.status-inner-box-bottom.player-status-active-turn { + -fx-effect: dropshadow(one-pass-box, rgba(184, 134, 11, 0.30), 10, 0, 0, 3); +} + +.status-label-small { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.status-value-text { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.status-value-money { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2);*/ +} + +.status-circle { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)) !important; + -fx-background-radius: 50; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 1; + -fx-border-radius: 50; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.30), 10, 0, 0, 3); +} + +.browser-root { + -fx-background-image: url("/images/background.png"); + -fx-background-size: cover; + -fx-background-position: center; + -fx-background-color: #0b2d15; +} + +.browser-web-view { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 0; + -fx-cursor: default; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.30), 10, 0, 0, 3); +} + +.web-view { + -fx-background-color: transparent; +} + +.taskbar-browser { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 10 25; + -fx-cursor: default; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.30), 10, 0, 0, 3); +} + +/*.taskbar-browser:hover {*/ +/* -fx-border-color: #ffffff;*/ +/*}*/ + +.security-label { + -fx-text-fill: #000000; + -fx-font-weight: bold; + -fx-font-size: 16px; +} + +.context-menu { + -fx-background-color: + linear-gradient(to bottom right, + rgba(51, 51, 51, 0.38), + rgba(51, 51, 51, 0.18)) !important; + -fx-text-fill: #ffffff !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-background-radius: 8 !important; + -fx-border-radius: 8 !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-color-color: #cccccc !important; + -fx-border-width: 2 !important; + -fx-padding: 5 12 !important; +} + +.context-menu .menu-item { + -fx-background-color: + linear-gradient(to bottom right, + rgba(51, 51, 51, 0.38), + rgba(51, 51, 51, 0.18)) !important; +} + +.context-menu .menu-item .label { + -fx-text-fill: #ffffff !important; + -fx-font-size: 13px !important; +} + +/*.context-menu .menu-item:focused {*/ +/* -fx-background-color: #333333;*/ +/*}*/ + +.context-menu .menu-item:hover .label, +.context-menu .menu-item:focused .label { + /*-fx-text-fill: #b8860b;*/ + -fx-font-size: 15px !important; +} + +.notebook { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; + /*-fx-cursor: move;*/ + -fx-padding: 0 0 15 0; +} + +.notebook:hover { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.44), + rgba(0, 0, 0, 0.22)); + + -fx-border-color: + rgba(255, 255, 255, 0.55); +} + +/*.notebook.player-active-turn {*/ +/* !* active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-border-color:*/ +/* rgba(184, 134, 11, 0.55);*/ +/* -fx-effect:*/ +/* dropshadow(*/ +/* gaussian,*/ +/* rgba(184, 134, 11, 0.45),*/ +/* 40,*/ +/* 0.2,*/ +/* 0,*/ +/* 12*/ +/* );*/ +/*}*/ + +.notebook-header { + -fx-background-color: transparent !important; + -fx-background-radius: 25 25 0 0; + -fx-padding: 8 10; + -fx-cursor: move; +} + +.notebook-title { + -fx-text-fill: #ffffff; + -fx-background-color: transparent !important; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, rgb(255, 255, 255), 0, 0, 3, 3);*/ +} + +.notebook-content { + -fx-background-color: transparent; + -fx-padding: 0; +} + +.notebook-content>.viewport { + -fx-background-color: transparent !important; + -fx-background-radius: 0 0 25 25; +} + + +.scroll-pane { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +.scroll-pane .scroll-bar:vertical, +.scroll-pane .scroll-bar:horizontal { + -fx-background-color: transparent; +} + +.scroll-pane .scroll-bar .track { + -fx-background-color: transparent !important; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb { + -fx-background-color: + linear-gradient(to bottom right, + rgba(200, 200, 200, 0.38), + rgba(200, 200, 200, 0.18)) !important; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb:hover { + -fx-background-color: + linear-gradient(to bottom right, + rgba(255, 255, 255, 0.38), + rgba(255, 255, 255, 0.18)) !important; +} + +.notebook-tips-list { + -fx-padding: 10; + -fx-spacing: 10; + -fx-background-color: transparent; +} + +.notebook-tips-list .text-flow { + -fx-padding: 12; + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)); + -fx-background-radius: 15; +} + +.settings-box { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: rgba(255, 255, 255, 0.40); + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; + /*-fx-cursor: move;*/ + -fx-padding: 0 0 15 0; +} + +.settings-box:hover { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.44), + rgba(0, 0, 0, 0.22)); + + -fx-border-color: + rgba(255, 255, 255, 0.55); +} + +/*.settings-box.player-active-turn {*/ +/* !* Active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-border-color:*/ +/* rgba(184, 134, 11, 0.55);*/ +/* -fx-effect:*/ +/* dropshadow(*/ +/* gaussian,*/ +/* rgba(184, 134, 11, 0.45),*/ +/* 40,*/ +/* 0.2,*/ +/* 0,*/ +/* 12*/ +/* );*/ +/*}*/ + +.settings-header { + -fx-background-color: transparent; + -fx-background-radius: 20 20 0 0; + -fx-padding: 10 15; + -fx-cursor: move; + -fx-border-width: 0 0 1 0; +} + +.settings-label { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + /*-fx-effect: dropshadow(one-pass-box, rgb(255, 255, 255), 0, 0, 3, 3);*/ +} + +.settings-content { + -fx-padding: 15 20 20 20; + -fx-spacing: 12; +} + +.settings-content { + -fx-background-color: transparent; + -fx-text-fill: #ffffff; + -fx-font-size: 13px; +} + +.radio-button { + -fx-text-fill: #ffffff; + -fx-font-size: 13px; + + -fx-padding: 6 8; + -fx-background-radius: 10; +} + +.radio-button:hover { + -fx-font-size: 15px; +} + +.radio-button:selected { + -fx-text-fill: #ffffff; +} + +.radio-button .radio { + -fx-background-color: + linear-gradient(to bottom right, + rgba(184, 134, 11, 0.38), + rgba(184, 134, 11, 0.18)); + /*-fx-border-width: 3;*/ + /*-fx-border-color: #b8860b;*/ + /*-fx-border-radius: 50%;*/ +} + +.radio-button:selected .radio { + -fx-background-color: + linear-gradient(to bottom right, + rgba(184, 134, 11, 0.38), + rgba(184, 134, 11, 0.18)); + -fx-effect: dropshadow(one-pass-box, rgba(184, 134, 11, 0.30), 10, 0, 0, 3); +} + +.settings-box { + -fx-background-insets: 0; +} + +.settings-content { + -fx-background-color: transparent; +} + +/*chat box css*/ + +.chat-box { + -fx-background-color: + linear-gradient(to bottom right, + rgba(255, 255, 255, 0.18), + rgba(255, 255, 255, 0.08)) !important; + -fx-background-radius: 58; + -fx-border-radius: 50; + -fx-border-color: rgba(255, 255, 255, 0.25) !important; + -fx-border-width: 12; + -fx-padding: 20; + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.20), 10, 0, 0, 3) !important; +} + +.tab-chat-box { + -fx-background-color: transparent !important; + -fx-background-radius: 23 !important; + -fx-border-radius: 20 !important; + -fx-border-color: rgba(255, 255, 255, 0.40) !important; + -fx-border-width: 3 !important; + -fx-padding: 20; +} + +.chat-header { + -fx-text-fill: #ffffff !important; + -fx-font-size: 22px; + -fx-font-weight: bold; + -fx-padding: 0 0 10 0; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 0, 0) !important; +} + +.chat-separator { + -fx-background-color: transparent !important; + -fx-min-height: 4px; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 0, 0) !important; +} + +.tab-header-area .tab-header-background { + -fx-background-color: transparent !important; + -fx-background-radius: 18 !important; + -fx-border-radius: 10 !important; + -fx-border-color: rgba(0, 0, 0, 0) !important; + -fx-border-width: 5; + -fx-padding: 10; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0), 0, 0, 0, 0) !important; + -fx-min-height: 10; +} + +.tab-pane .tab { + -fx-background-color: transparent !important; + -fx-background-radius: 18 !important; + -fx-border-radius: 10 !important; + -fx-border-color: rgba(255, 255, 255, 0) !important; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0), 0, 0, 0, 0) !important; + -fx-border-width: 5; + -fx-padding: 6 15; +} + +.tab-pane .tab:selected { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)) !important; + -fx-background-radius: 18 !important; + -fx-border-radius: 10 !important; + -fx-border-color: rgba(255, 255, 255, 0.40) !important; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0), 0, 0, 0, 0) !important; + -fx-border-width: 3 !important; + -fx-padding: 6 15; +} + +.tab-pane .tab .tab-close-button { + -fx-text-fill: #ffffff !important; +} + +.tab-pane .tab-header-area .headers-region { + -fx-background-color: transparent !important; +} + +.tab .tab-label { + -fx-alignment: CENTER; + -fx-text-fill: #ffffff; + -fx-font-size: 12px; + -fx-font-weight: bold; +} + +.tab:selected .tab-label { + -fx-alignment: CENTER; + -fx-text-fill: #ffffff; +} + +.menu-button { + -fx-background-color: + linear-gradient(to bottom right, + rgba(0, 0, 0, 0.38), + rgba(0, 0, 0, 0.18)) !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-text-fill: #ffffff !important; + -fx-background-radius: 12 !important; + -fx-border-radius: 12 !important; + -fx-font-family: "Courier New" !important; + -fx-font-weight: bold !important; + -fx-border-width: 2 !important; + -fx-padding: 6 15 !important; + -fx-cursor: hand !important; +} + +.menu-button .label { + -fx-text-fill: #ffffff !important; +} + +.menu-button:hover { + -fx-effect: dropshadow(one-pass-box, rgba(255, 255, 255, 0.25), 10, 0, 0, 3) !important; +} + +.menu-button .menu-item { + -fx-background-color: + linear-gradient(to bottom right, + rgba(51, 51, 51, 0.38), + rgba(51, 51, 51, 0.18)) !important; + -fx-border-color: rgba(51, 51, 51, 0.40) !important; + -fx-text-fill: #ffffff; +} diff --git a/src/main/resources/ui-structure/Casinogameui-parquet.css b/src/main/resources/ui-structure/Casinogameui-parquet.css new file mode 100644 index 0000000..43ee157 --- /dev/null +++ b/src/main/resources/ui-structure/Casinogameui-parquet.css @@ -0,0 +1,613 @@ +.root { + -fx-background-color: #000000; + -fx-font-family: "Monospaced", "Courier New"; /* font */ +} + +.background { + -fx-background-image: url("/images/parquet.png"); + -fx-background-size: cover; /* Image fills the entire window */ + /* Centers the image and prevents it from repeating in a tile-like pattern. */ + -fx-background-position: center center; + -fx-background-repeat: no-repeat; +} + +.info-text { + -fx-text-fill: #ffffff; + -fx-font-size: 16px; +} + +.casino-table { + -fx-background-color: #0d9e3b; + -fx-background-radius: 210; + -fx-border-radius: 200; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 12; + /*-fx-min-height: 60vh;*/ + /*-fx-max-height: 60vh;*/ + /*-fx-min-width: 90vh;*/ + /*-fx-max-width: 90vh;*/ + -fx-effect: dropshadow(one-pass-box, rgba(0,0,0,0.8), 0, 0, 15, 15); +} + +.dealer-box { + -fx-background-color: #ffffff; + -fx-border-color: #000000; + -fx-border-width: 3; + -fx-background-radius: 0; + -fx-border-radius: 0; + -fx-min-width: 70; + -fx-min-height: 100; + -fx-effect: dropshadow(one-pass-box, rgba(0,0,0,0.8), 0, 0, 4, 4); +} + +.table-title { + -fx-text-fill: #ffffff; + -fx-font-size: 28px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3); +} + +.taskbar { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 10 25; + -fx-cursor: move; + -fx-effect: dropshadow(one-pass-box, rgba(0,0,0,0.8), 0, 0, 5, 5); +} + +.taskbar:hover { + -fx-border-color: #ffffff; +} + +.taskbar.player-active-turn { + /* Active turn should only change the shadow to avoid flicker/transparency jumps. */ + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +.black-input-field { + -fx-background-color: #1a1a1a; + -fx-text-fill: #ffffff; + -fx-font-family: "Courier New"; + -fx-font-weight: bold; + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-border-color: #444444; + -fx-border-width: 2; + -fx-padding: 5 12; +} + +.black-input-field:hover { + -fx-translate-y: -3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + + +.black-input-field:focused { + -fx-border-color: #ffffff; + -fx-background-color: #000000; +} + +.gray-input-field { + -fx-background-color: #333333; + -fx-text-fill: #ffffff; + -fx-font-family: "Courier New"; + -fx-font-weight: bold; + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-border-color: #666666; + -fx-color-color: #cccccc; + -fx-border-width: 2; + -fx-padding: 5 12; +} + +.gray-input-field:hover { + -fx-translate-y: -3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.gray-input-field:focused { + -fx-border-color: #ffffff; + -fx-background-color: #333333; +} + +.yellow-input-field { + -fx-background-color: #b8860b; + -fx-text-fill: #ffffff; + -fx-prompt-text-fill: #ffffff; + -fx-font-family: "Courier New"; + -fx-font-weight: bold; + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-border-color: #ffd700; + -fx-border-width: 2; + -fx-padding: 5 12; +} + +.yellow-input-field:hover { + -fx-translate-y: -3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.yellow-input-field:focused { + -fx-border-color: #ffffff; + -fx-background-color: #b8860b; +} + +.red-input-field { + -fx-background-color: #8b0000; + -fx-text-fill: #ffffff; + -fx-prompt-text-fill: #ffffff; + -fx-font-family: "Courier New"; + -fx-font-weight: bold; + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-border-color: #ff4444; + -fx-border-width: 2; + -fx-padding: 5 12; +} + +.red-input-field:hover { + -fx-translate-y: -3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.red-input-field:focused { + -fx-border-color: #ffffff; + -fx-background-color: #8b0000; +} + +.yellow-button { + -fx-background-color: #b8860b; + -fx-border-color: #ffd700; + -fx-text-fill: #ffffff; +} + +.red-button { + -fx-background-color: #8b0000; + -fx-border-color: #ff4444; + -fx-text-fill: #ffffff; +} + +.gray-button { + -fx-background-color: #333333; + -fx-border-color: #666666; + /*-fx-text-fill: #cccccc;*/ + -fx-text-fill: #ffffff; +} + +.gray-button .label { + -fx-text-fill: #ffffff; +} + +.green-button { + -fx-background-color: #144523; + -fx-border-color: #0d9e3b; + -fx-text-fill: #ffffff; +} + +.gray-button, .yellow-button, .red-button, .green-button { + -fx-background-radius: 12; + -fx-border-radius: 12; + -fx-font-family: "Courier New"; + -fx-font-weight: bold; + -fx-border-width: 2; + -fx-padding: 6 15; + -fx-cursor: hand; +} + +.gray-button:hover, .yellow-button:hover, .red-button:hover, .green-button:hover { + -fx-translate-y: -3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.community-cards-box { + -fx-alignment: center; + -fx-spacing: 12; + -fx-padding: 15; + -fx-background-color: transparent; +} + +.community-card { + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); + + -fx-alignment: center; + -fx-spacing: 12; + -fx-padding: 20; + -fx-background-color: linear-gradient(to bottom, #0b3d2e, #06261d); + -fx-background-radius: 16; + -fx-scale-x: 1; + -fx-scale-y: 1; + -fx-translate-y: 0; + -fx-opacity: 1; +} + +/*.community-card:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.player-card { + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); + + -fx-alignment: center; + -fx-spacing: 12; + -fx-padding: 20; + -fx-background-color: linear-gradient(to bottom, #0b3d2e, #06261d); + -fx-background-radius: 16; + -fx-scale-x: 1; + -fx-scale-y: 1; + -fx-translate-y: 0; + -fx-opacity: 1; +} + +/*.player-card:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.player-card.player-cards-active-turn { + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +.dealer-box.player-cards-active-turn { + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +/*.dealer-box:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.chips-icon { + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +/*.chips-icon:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + +.dealer-icon { + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.player-cards-box { + -fx-padding: 10; +} + +/*.player-cards-box.player-cards-active-turn {*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.money-value { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2); +} + +.pot-title { + -fx-text-fill: #ffffff; + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3); +} + +.pot-box { + -fx-padding: 5; +} + +.player-status-pane { + -fx-background-color: rgba(13, 158, 59, 0); + -fx-background-radius: 15; + -fx-border-radius: 15; + /* -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 2; + -fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.5), 10, 0, 0, 4);*/ +} + +.player-status-pane:hover { + -fx-translate-y: -3; +} + +.status-inner-box-top { + -fx-background-color: rgba(13, 158, 59); + -fx-background-radius: 19; + -fx-border-radius: 15; + -fx-padding: 5 10; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.status-inner-box-midle { + -fx-background-color: rgba(13, 158, 59, 0); +} + +.status-inner-box-bottom { + -fx-background-color: rgba(13, 158, 59); + -fx-background-radius: 19; + -fx-border-radius: 15; + -fx-padding: 5 10; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.status-inner-box-top.player-status-active-turn { + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +.status-inner-box-bottom.player-status-active-turn { + -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); +} + +.status-label-small { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2); +} + +.status-value-text { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2); +} + +.status-value-money { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 2, 2); +} + +.status-circle { + -fx-background-color: #000000; + -fx-background-radius: 50; + -fx-border-color: #ffffff; + -fx-border-width: 1; + -fx-border-radius: 50; + -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); +} + +.browser-root { + -fx-background-image: url("/images/background.png"); + -fx-background-size: cover; + -fx-background-position: center; + -fx-background-color: #0b2d15; +} + +.browser-web-view { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 0; + -fx-cursor: default; + -fx-effect: dropshadow(one-pass-box, rgba(0,0,0,0.8), 0, 0, 5, 5); +} + +.web-view { + -fx-background-color: transparent; +} + +.taskbar-browser { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-padding: 10 25; + -fx-cursor: default; + -fx-effect: dropshadow(one-pass-box, rgba(0,0,0,0.8), 0, 0, 5, 5); +} + +/*.taskbar-browser:hover {*/ +/* -fx-border-color: #ffffff;*/ +/*}*/ + +.security-label { + -fx-text-fill: #000000; + -fx-font-weight: bold; + -fx-font-size: 16px; +} + +.context-menu { + -fx-background-color: #333333; + -fx-text-fill: #ffffff; + -fx-font-family: "Courier New"; + -fx-font-weight: bold; + -fx-background-radius: 8; + -fx-border-radius: 8; + -fx-border-color: #333333; + -fx-color-color: #cccccc; + -fx-border-width: 2; + -fx-padding: 5 12; +} + +.context-menu .menu-item { + -fx-background-color: #333333; +} + +.context-menu .menu-item .label { + -fx-text-fill: #ffffff; + -fx-font-size: 13px; +} + +/*.context-menu .menu-item:focused {*/ +/* -fx-background-color: #333333;*/ +/*}*/ + +.context-menu .menu-item:hover .label, +.context-menu .menu-item:focused .label { + /*-fx-text-fill: #b8860b;*/ + -fx-font-size: 15px; +} + +.notebook { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 5, 5); + /*-fx-cursor: move;*/ + + -fx-padding: 0 0 15 0; +} + +.notebook:hover { + -fx-border-color: #ffffff; +} + +/*.notebook.player-active-turn {*/ +/* !* Active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.notebook-header { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 25 25 0 0; + -fx-padding: 8 10; + -fx-cursor: move; +} + +.notebook-title { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3); +} + +.notebook-content { + -fx-background-color: transparent; + -fx-padding: 0; +} + +.notebook-content > .viewport { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 0 0 25 25; +} + + +.scroll-pane { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +.scroll-pane .scroll-bar:vertical, +.scroll-pane .scroll-bar:horizontal { + -fx-background-color: transparent; +} + +.scroll-pane .scroll-bar .track { + /*-fx-background-color: #5c3d10;*/ + -fx-background-color: transparent; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb { + -fx-background-color: #3d260a; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb:hover { + -fx-background-color: #2a1b07; +} + +.notebook-tips-list { + -fx-padding: 10; + -fx-spacing: 10; + -fx-background-color: transparent; +} + +.notebook-tips-list .text-flow { + -fx-padding: 12; + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 15; +} + +.settings-box { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 5, 5); + /*-fx-cursor: move;*/ + -fx-padding: 0 0 15 0; +} + +.settings-box:hover { + -fx-border-color: #ffffff; +} + +/*.settings-box.player-active-turn {*/ +/* !* Active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.settings-header { + -fx-background-color: transparent; + -fx-background-radius: 20 20 0 0; + -fx-padding: 10 15; + -fx-cursor: move; + -fx-border-width: 0 0 1 0; +} + +.settings-label { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3); +} + +.settings-content { + -fx-padding: 15 20 20 20; + -fx-spacing: 12; +} + +.settings-content { + -fx-background-color: transparent; + -fx-text-fill: #e8e8e8; + -fx-font-size: 13px; +} + +.radio-button { + -fx-text-fill: #ffffff; + -fx-font-size: 13px; + + -fx-padding: 6 8; + -fx-background-radius: 10; +} + +.radio-button:hover { + -fx-font-size: 15px; +} + +.radio-button:selected { + /*-fx-text-fill: #b8860b;*/ + -fx-text-fill: #ffffff; +} + +.radio-button .radio { + -fx-background-color: #b8860b; + /*-fx-border-width: 3;*/ + /*-fx-border-color: #b8860b;*/ + /*-fx-border-radius: 50%;*/ +} + +.radio-button:selected .radio { + -fx-background-color: #b8860b; + -fx-effect: dropshadow(gaussian, rgba(184,134,11,0.8), 6, 0.3, 0, 0); +} + +.settings-box { + -fx-background-insets: 0; +} + +.settings-content { + -fx-background-color: transparent; +} diff --git a/src/main/resources/ui-structure/Casinogameui.css b/src/main/resources/ui-structure/Casinogameui.css index 4ac89cc..5d133bf 100644 --- a/src/main/resources/ui-structure/Casinogameui.css +++ b/src/main/resources/ui-structure/Casinogameui.css @@ -174,7 +174,12 @@ .gray-button { -fx-background-color: #333333; -fx-border-color: #666666; - -fx-text-fill: #cccccc; + /*-fx-text-fill: #cccccc;*/ + -fx-text-fill: #ffffff; +} + +.gray-button .label { + -fx-text-fill: #ffffff; } .green-button { @@ -219,6 +224,10 @@ -fx-opacity: 1; } +/*.community-card:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + .player-card { -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); @@ -233,6 +242,10 @@ -fx-opacity: 1; } +/*.player-card:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + .player-card.player-cards-active-turn { -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); } @@ -241,10 +254,17 @@ -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5); } +/*.dealer-box:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ + .chips-icon { -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); } +/*.chips-icon:hover {*/ +/* -fx-translate-y: -3;*/ +/*}*/ .dealer-icon { -fx-effect: dropshadow(one-pass-box, rgba(0, 0, 0, 0.8), 0, 0, 3, 3); @@ -266,9 +286,10 @@ } .pot-title { - -fx-text-fill: gold; + -fx-text-fill: #ffffff; -fx-font-size: 14px; -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3); } .pot-box { @@ -410,6 +431,183 @@ -fx-background-color: #333333; } -.context-menu .menu-item:focused { - -fx-background-color: #333333; +.context-menu .menu-item .label { + -fx-text-fill: #ffffff; + -fx-font-size: 13px; +} + +/*.context-menu .menu-item:focused {*/ +/* -fx-background-color: #333333;*/ +/*}*/ + +.context-menu .menu-item:hover .label, +.context-menu .menu-item:focused .label { + /*-fx-text-fill: #b8860b;*/ + -fx-font-size: 15px; +} + +.notebook { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 5, 5); + /*-fx-cursor: move;*/ + + -fx-padding: 0 0 15 0; +} + +.notebook:hover { + -fx-border-color: #ffffff; +} + +/*.notebook.player-active-turn {*/ +/* !* Active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.notebook-header { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 25 25 0 0; + -fx-padding: 8 10; + -fx-cursor: move; +} + +.notebook-title { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3); +} + +.notebook-content { + -fx-background-color: transparent; + -fx-padding: 0; +} + +.notebook-content > .viewport { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 0 0 25 25; +} + + +.scroll-pane { + -fx-background-color: transparent; + -fx-border-color: transparent; +} + +.scroll-pane .scroll-bar:vertical, +.scroll-pane .scroll-bar:horizontal { + -fx-background-color: transparent; +} + +.scroll-pane .scroll-bar .track { + /*-fx-background-color: #5c3d10;*/ + -fx-background-color: transparent; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb { + -fx-background-color: #3d260a; + -fx-background-radius: 5; +} + +.scroll-pane .scroll-bar .thumb:hover { + -fx-background-color: #2a1b07; +} + +.notebook-tips-list { + -fx-padding: 10; + -fx-spacing: 10; + -fx-background-color: transparent; +} + +.notebook-tips-list .text-flow { + -fx-padding: 12; + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 15; +} + +.settings-box { + -fx-background-color: rgb(13, 158, 59); + -fx-background-radius: 23; + -fx-border-radius: 20; + -fx-border-color: #5c3d10 #3d260a #3d260a #5c3d10; + -fx-border-width: 3; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 5, 5); + /*-fx-cursor: move;*/ + -fx-padding: 0 0 15 0; +} + +.settings-box:hover { + -fx-border-color: #ffffff; +} + +/*.settings-box.player-active-turn {*/ +/* !* Active turn should only change the shadow to avoid flicker/transparency jumps. *!*/ +/* -fx-effect: dropshadow(one-pass-box, rgb(184, 134, 11), 0, 0, 5, 5);*/ +/*}*/ + +.settings-header { + -fx-background-color: transparent; + -fx-background-radius: 20 20 0 0; + -fx-padding: 10 15; + -fx-cursor: move; + -fx-border-width: 0 0 1 0; +} + +.settings-label { + -fx-text-fill: #ffffff; + -fx-font-size: 18px; + -fx-font-weight: bold; + -fx-effect: dropshadow(one-pass-box, #000000, 0, 0, 3, 3); +} + +.settings-content { + -fx-padding: 15 20 20 20; + -fx-spacing: 12; +} + +.settings-content { + -fx-background-color: transparent; + -fx-text-fill: #e8e8e8; + -fx-font-size: 13px; +} + +.radio-button { + -fx-text-fill: #ffffff; + -fx-font-size: 13px; + + -fx-padding: 6 8; + -fx-background-radius: 10; +} + +.radio-button:hover { + -fx-font-size: 15px; +} + +.radio-button:selected { + /*-fx-text-fill: #b8860b;*/ + -fx-text-fill: #ffffff; +} + +.radio-button .radio { + -fx-background-color: #b8860b; + /*-fx-border-width: 3;*/ + /*-fx-border-color: #b8860b;*/ + /*-fx-border-radius: 50%;*/ +} + +.radio-button:selected .radio { + -fx-background-color: #b8860b; + -fx-effect: dropshadow(gaussian, rgba(184,134,11,0.8), 6, 0.3, 0, 0); +} + +.settings-box { + -fx-background-insets: 0; +} + +.settings-content { + -fx-background-color: transparent; } diff --git a/src/main/resources/ui-structure/Casinogameui.fxml b/src/main/resources/ui-structure/Casinogameui.fxml index e225aa1..f8269ec 100644 --- a/src/main/resources/ui-structure/Casinogameui.fxml +++ b/src/main/resources/ui-structure/Casinogameui.fxml @@ -7,11 +7,11 @@ - + styleClass="background"> @@ -41,6 +41,8 @@ alignment="CENTER" styleClass="casino-table" spacing="20" + mouseTransparent="false" + pickOnBounds="false" maxWidth="Infinity" maxHeight="Infinity"> @@ -67,7 +69,7 @@ - + + + + + + + + + + + diff --git a/src/main/resources/ui-structure/Casinomainui.fxml b/src/main/resources/ui-structure/Casinomainui.fxml index b5df10b..0b734ee 100644 --- a/src/main/resources/ui-structure/Casinomainui.fxml +++ b/src/main/resources/ui-structure/Casinomainui.fxml @@ -4,7 +4,7 @@ - + + + + + + + + + +