various site improvements
authorGeoffrey Allott <geoffrey@allott.email>
Thu, 18 Mar 2021 20:14:11 +0000 (20:14 +0000)
committerGeoffrey Allott <geoffrey@allott.email>
Thu, 18 Mar 2021 20:14:11 +0000 (20:14 +0000)
site/modules/card.js
site/modules/chatroom.js
site/modules/poker.js
site/modules/socket.js
site/style/chatroom.css
site/style/game-list.css
site/style/mainmenu.css
site/style/poker.css

index 4a3771d1cd6ddc2d6bfd1787cb4df526ffa207f8..be14c0f06ddf2519ed3d0865ffe670f74cc6262c 100644 (file)
@@ -25,9 +25,26 @@ export function short_suit(suit) {
     }
 }
 
+export function pretty_suit(suit) {
+    switch (suit) {
+        case "Clubs": return "♣";
+        case "Diamonds": return "♢";
+        case "Hearts": return "♢";
+        case "Spades": return "♠";
+    }
+}
+
+export function short_card(card) {
+    return short_rank(card.rank) + short_suit(card.suit);
+}
+
+export function card_name(card) {
+    return short_rank(card.rank) + pretty_suit(card.suit);
+}
+
 export function card_href(card) {
     if (card === null) return "img/card-back-blue.svg";
-    return "img/cards.svg#" + short_rank(card.rank) + short_suit(card.suit);
+    return "img/cards.svg#" + short_card(card);
 }
 
 export function suit_href(suit) {
index 9a80c6c611f0aa3964400d4d153d9dd52defcb03..0137fd623ba7d87a16573a36bbbec373588a319a 100644 (file)
@@ -1,3 +1,5 @@
+import { card_name, pretty_suit } from "./card.js";
+
 export class Chatroom {
     constructor(container, summary, actions, username, send) {
         this.container = container;
@@ -18,11 +20,6 @@ export class Chatroom {
             this.chatroom_input.value = "";
         };
         this.chatroom.append(this.chatroom_input);
-        const close = document.createElement("button");
-        close.classList.add("chatroom-close");
-        close.innerText = "✗";
-        close.onclick = () => this.send({type: "TakeAction", action: {action: "Leave"}});
-        this.chatroom.append(close);
         this.container.append(this.chatroom);
 
         for (const user_action of this.actions) {
@@ -34,19 +31,85 @@ export class Chatroom {
         }
     }
 
+    set_error_text(text) {
+        // TODO
+    }
+
     take_action(user_action, initialising) {
         const is_at_end = this.chat.scrollTop + this.chat.clientHeight == this.chat.scrollHeight;
         switch (user_action.action.action) {
             case "Join":
                 this.seats.set(user_action.username, user_action.action.seat);
-                this.chat.append(this.join_element(user_action.username));
+                this.chat.append(this.join_element(user_action.username, "Joined"));
                 break;
             case "Message":
                 this.chat.append(this.message_element(user_action.username, user_action.action.message));
                 break;
+            case "PostBlind":
+                if (user_action.action.chips === 0) {
+                    this.chat.append(this.info_element(user_action.username, "Posts ghost blind"));
+                } else {
+                    this.chat.append(this.info_element(user_action.username, "Posts blind of " + user_action.action.chips));
+                }
+                break;
+            case "Bet":
+                this.chat.append(this.info_element(user_action.username, "Bets " + user_action.action.chips));
+                break;
+            case "Fold":
+                this.chat.append(this.info_element(user_action.username, "Folds"));
+                break;
+            case "TimeoutFold":
+                this.chat.append(this.info_element(user_action.username, "Folds due to timeout"));
+                break;
+            case "NextToDeal":
+                this.chat.append(this.info_element(user_action.username, "Next to deal"));
+                break;
+            case "ReceiveCard":
+                if (user_action.action.card === null) {
+                    this.chat.append(this.info_element(user_action.username, "Receives card"));
+                } else {
+                    this.chat.append(this.info_element(user_action.username, "Receives " + card_name(user_action.action.card)));
+                }
+                break;
+            case "CommunityCard":
+                this.chat.append(this.info_element(user_action.username, "Deals " + card_name(user_action.action.card)));
+                break;
+            case "EndDeal":
+                this.chat.append(this.info_element(user_action.username, "Ends deal"));
+                break;
+            case "RevealCard":
+                this.chat.append(this.info_element(user_action.username, "Reveals " + card_name(user_action.action.card)));
+            case "PlayCard":
+                this.chat.append(this.info_element(user_action.username, "Plays " + card_name(user_action.action.card)));
+                break;
+            case "CutCard":
+                this.chat.append(this.info_element(user_action.username, "Cuts " + card_name(user_action.action.card)));
+                break;
+            case "ChooseTrumps":
+                this.chat.append(this.info_element(user_action.username, "Chooses " + pretty_suit(user_action.action.suit) + " as trumps"));
+                break;
+            case "WinTrick":
+                this.chat.append(this.info_element(user_action.username, "Wins trick"));
+                break;
+            case "WinCall":
+                this.chat.append(this.info_element(user_action.username, "Wins the call"));
+                break;
+            case "WinHand":
+                this.chat.append(this.info_element(
+                    user_action.username,
+                    "Wins " + user_action.action.chips + " chips" +
+                        (user_action.action.hand === null ? "" : " with " + user_action.action.hand)
+                ));
+                break;
+            case "WinGame":
+                this.chat.append(this.win_game_element(user_action.username, "Wins the game"));
+                break;
+            case "KnockedOut":
+                this.chat.append(this.leave_element(user_action.username, "Knocked out"));
+                break;
             case "Leave":
                 this.seats.delete(user_action.username);
-                this.chat.append(this.leave_element(user_action.username));
+                this.chat.append(this.leave_element(user_action.username, "Left"));
                 if (!initialising && user_action.username === this.username) {
                     this.send({type: "JoinLobby", filter: ""});
                 }
@@ -63,45 +126,37 @@ export class Chatroom {
         }
     }
 
-    join_element(username) {
-        const join_element = document.createElement("div");
-        join_element.classList.add("chatroom-join");
+    chat_element(message_class, username, message) {
+        const chat_element = document.createElement("div");
+        chat_element.classList.add(message_class);
         const username_element = document.createElement("div");
         username_element.classList.add("chatroom-username");
         username_element.innerText = username;
-        join_element.append(username_element);
+        chat_element.append(username_element);
         const text_element = document.createElement("div");
         text_element.classList.add("chatroom-text");
-        text_element.innerText = "Joined";
-        join_element.append(text_element);
-        return join_element;
+        text_element.innerText = message;
+        chat_element.append(text_element);
+        return chat_element;
+    }
+
+    join_element(username, message) {
+        return this.chat_element("chatroom-join", username, message);
     }
 
     message_element(username, message) {
-        const message_element = document.createElement("div");
-        message_element.classList.add("chatroom-message");
-        const username_element = document.createElement("div");
-        username_element.classList.add("chatroom-username");
-        username_element.innerText = username;
-        message_element.append(username_element);
-        const text_element = document.createElement("div");
-        text_element.classList.add("chatroom-text");
-        text_element.innerText = message;
-        message_element.append(text_element);
-        return message_element;
+        return this.chat_element("chatroom-message", username, message);
     }
 
-    leave_element(username) {
-        const leave_element = document.createElement("div");
-        leave_element.classList.add("chatroom-leave");
-        const username_element = document.createElement("div");
-        username_element.classList.add("chatroom-username");
-        username_element.innerText = username;
-        leave_element.append(username_element);
-        const text_element = document.createElement("div");
-        text_element.classList.add("chatroom-text");
-        text_element.innerText = "Left";
-        leave_element.append(text_element);
-        return leave_element;
+    info_element(username, message) {
+        return this.chat_element("chatroom-info", username, message);
+    }
+
+    win_game_element(username, message) {
+        return this.chat_element("chatroom-win-game", username, message);
+    }
+
+    leave_element(username, message) {
+        return this.chat_element("chatroom-leave", username, message);
     }
 }
index 6891836efa51788fb1a9828282d9947a083770de..b1ebce1c4b2a2aeebcafc1d07509d7b2044dc79e 100644 (file)
@@ -75,6 +75,16 @@ export class TexasHoldEm {
         this.bet_size_area.setAttribute("width", "38");
         this.bet_size_area.setAttribute("height", "16");
         this.bet_size_area.classList.add("bet-size-area");
+        this.bet_size_area.onclick = () => {
+            const chips = prompt("Chips to bet", this.bet_size_text.textContent);
+            if (chips !== null && !isNaN(Number(chips))) {
+                const bet = Math.max(this.min_bet(), Math.min(this.all_in_bet(), Number(chips)));
+                this.bet_size_text.textContent = Number(chips);
+                const p = (bet - this.min_bet()) / (this.all_in_bet() - this.min_bet());
+                const x = Math.max(360, Math.min(475, Math.sqrt(p) * 115 + 360));
+                this.bet_slider_thumb.setAttribute("x", x);
+            }
+        };
         this.svg.append(this.bet_size_area);
 
         this.bet_size = document.createElementNS(svgns, "text");
@@ -217,9 +227,10 @@ export class TexasHoldEm {
         this.fold_control.classList.toggle("active", this.active === this.username && this.chips_to_call() > 0);
         this.call_control.classList.toggle("active", this.active === this.username);
         this.call_control_text.textContent = this.active === this.username && this.chips_to_call() == 0 ? "Check" : "Call";
-        this.bet_control.classList.toggle("active", this.active === this.username);
-        this.bet_size_area.classList.toggle("active", this.active === this.username);
-        this.bet_slider.classList.toggle("active", this.active === this.username);
+        const can_bet = this.active === this.username && this.chips_to_call() < this.all_in_bet();
+        this.bet_control.classList.toggle("active", can_bet);
+        this.bet_size_area.classList.toggle("active", can_bet);
+        this.bet_slider.classList.toggle("active", can_bet);
         this.bet_size_text.textContent = this.min_bet();
         this.bet_slider_thumb.setAttribute("x", 360);
         for (const [username, [user, stack, active, bet]] of this.user_icons) {
@@ -347,7 +358,7 @@ export class TexasHoldEm {
     }
 
     min_bet() {
-        return this.chips_to_call(); /* + this.min_raise() TODO */
+        return Math.min(this.all_in_bet(), this.chips_to_call() + this.min_raise());
     }
 
     all_in_bet() {
index 42e7553bd6014813f91618cb582bd30c58c609e1..180f85fe84d469dcd5aa027598614152a35bce68 100644 (file)
@@ -18,6 +18,7 @@ export class Socket {
         this.socket.onmessage = (msg) => this.onmessage(msg);
         this.socket.onclose = () => this.onclose();
         this.state = "Connecting";
+        this.last_filter = "";
 
         this.scheduled_actions = [];
         this.schedule_timeout = null;
@@ -67,8 +68,10 @@ export class Socket {
                 this.state = "Connected";
                 break;
             case "LoginSuccess":
+            case "LeaveLobbySuccess":
                 this.hide_login();
                 this.game = new MainMenu(this.container, message => this.send(message));
+                this.container.append(this.close_button());
                 this.state = "LoggedIn";
                 break;
             case "JoinLobbyFailure":
@@ -76,7 +79,9 @@ export class Socket {
                 this.state = "LoggedIn";
                 break;
             case "JoinLobbySuccess":
+                this.last_filter = message.filter;
                 this.game = new GameList(this.container, message.filter, message.games, message => this.send(message))
+                this.container.append(this.close_button());
                 this.state = "InLobby";
                 break;
             case "NewGame":
@@ -220,6 +225,28 @@ export class Socket {
         };
     }
 
+    close_button() {
+        const close = document.createElement("button");
+        close.classList.add("game-close");
+        close.innerText = "✗";
+        close.onclick = () => {
+            switch (this.state) {
+                case "LoggedIn":
+                    this.send({type: "Logout"});
+                    break;
+                case "InLobby":
+                    this.send({type: "LeaveLobby"});
+                    break;
+                case "InGame":
+                    this.send({type: "TakeAction", action: {action: "Leave"}});
+                    this.send({type: "LeaveGame"});
+                    this.send({type: "JoinLobby", filter: this.last_filter});
+                    break;
+            }
+        };
+        return close;
+    }
+
     create_game_display(summary, actions) {
         this.container.textContent = "";
         let Format;
@@ -232,6 +259,7 @@ export class Socket {
                 return;
         }
         this.game = new Format(this.container, summary, actions, this.auth.username, message => this.send(message));
+        this.container.append(this.close_button());
     }
 
     onclose() {
index cc089521a0dc0bef3645f6fcb0cccb31c00f49af..4ff23a55f69659c07064a595bf90a0667a2f5125 100644 (file)
@@ -21,7 +21,7 @@
     align-self: end;
 }
 
-.chatroom-join, .chatroom-message, .chatroom-leave {
+.chatroom-join, .chatroom-message, .chatroom-info, .chatroom-leave, .chatroom-win-game {
     display: flex;
 }
 
     border-radius: 3vw;
 }
 
+.chatroom-info > .chatroom-username {
+    background-color: skyblue;
+    color: black;
+}
+
 .chatroom-join > .chatroom-username {
     background-color: limegreen;
 }
     background-color: red;
 }
 
+.chatroom-win-game > .chatroom-username {
+    background: repeating-radial-gradient(circle at center, #a17f1a 0vw, gold, #a17f1a 2vw);
+    color: black;
+}
+
 .chatroom-text {
     justify-self: right;
     text-align: left;
     margin: 0.5vw;
     width: calc(100% - 36vw);
 }
-
-.chatroom-close {
-    display: block;
-    position: absolute;
-    right: 0;
-    top: 0;
-    font-size: inherit;
-}
index 8e52758318eb6badc500388965d19db2d60b64f7..4096aebc144a382cc17f333599f9445568c30ac0 100644 (file)
 .game-title {
     font-weight: bold;
 }
+
+.game-close {
+    display: block;
+    position: absolute;
+    right: 0;
+    top: 0;
+    font-size: 5vw;
+}
index 0965336d39b6b30ecabd872a0417b94d289d8980..68a5a719b19d28cfee0d141bd1f7bc7c8904f00f 100644 (file)
@@ -1,7 +1,8 @@
 .main-menu {
     display: flex;
     flex-wrap: wrap;
-    align-items: flex-start;
+    align-items: center;
+    justify-content: center;
 }
 
 .game-format-icon {
index 4d0fae2bd1a10c8a928cb562a83acbaf0b92e6fc..beb9a58d3e271687a847c1460652334cb11e0088 100644 (file)
@@ -1,3 +1,8 @@
+.texas-hold-em * {
+    -webkit-user-select: none;
+    user-select: none;
+}
+
 .controls {
     fill: grey;
     stroke: black;
     stroke: black;
     stroke-width: 1px;
     transition: fill 0.5s;
+    pointer-events: none;
 }
 
 .bet-size-area.active {
     fill: #ffffff;
+    pointer-events: auto;
 }
 
 .bet-slider-area {