From: Geoffrey Allott Date: Sun, 4 Jun 2023 22:28:30 +0000 (+0100) Subject: add cribbage game to site X-Git-Url: https://git.pointlesshacks.com/?a=commitdiff_plain;h=762c6758e53bd685023ebc9043a05ea738c16439;p=pokerwave.git add cribbage game to site --- diff --git a/site/img/cribbage.svg b/site/img/cribbage.svg new file mode 100644 index 0000000..8ee50ef --- /dev/null +++ b/site/img/cribbage.svg @@ -0,0 +1,3 @@ + + + diff --git a/site/modules/chatroom.js b/site/modules/chatroom.js index ddd669a..7911d01 100644 --- a/site/modules/chatroom.js +++ b/site/modules/chatroom.js @@ -112,6 +112,12 @@ export class Chatroom { case "PlayCard": this.chat.append(this.info_element(user_action.username, "Plays " + card_name(user_action.action.card))); break; + case "Pass": + this.chat.append(this.info_element(user_action.username, "Passes")); + break; + case "Score": + this.chat.append(this.info_element(user_action.username, "Scores " + user_action.action.points + ": " + user_action.action.reason)); + break; case "CutCard": this.chat.append(this.info_element(user_action.username, "Cuts " + card_name(user_action.action.card))); break; @@ -124,6 +130,13 @@ export class Chatroom { case "WinCall": this.chat.append(this.info_element(user_action.username, "Wins the call")); break; + case "PutInBox": + if (user_action.action.card === null) { + this.chat.append(this.info_element(user_action.username, "Puts a card in the box")); + } else { + this.chat.append(this.info_element(user_action.username, "Puts " + card_name(user_action.action.card) + " in the box")); + } + break; case "WinHand": this.chat.append(this.info_element( user_action.username, diff --git a/site/modules/cribbage.js b/site/modules/cribbage.js new file mode 100644 index 0000000..2c4aeba --- /dev/null +++ b/site/modules/cribbage.js @@ -0,0 +1,285 @@ +import { create_svg_element } from "./svg.js"; +import { card_href } from "./card.js"; +import { GameWithChat } from "./gamechat.js"; +import { CongratulateWinner } from "./winner.js"; + +export class Cribbage extends GameWithChat { + constructor(container, summary, actions, username, send, close) { + super(container, summary, actions, username, send, close); + this.svg.classList.add("cribbage"); + + this.user_icons = new Map(); + this.hands = new Map(); + this.played = new Map(); + this.scores = new Map(); + this.community = []; + this.box = []; + + this.active = null; + this.dealer = null; + + this.pass_control = create_svg_element(this.svg, "rect", ["pass-control", "active"], [["x", "400"], ["y", "400"], ["width", "120"], ["height", "50"], ["rx", "10"]]); + this.pass_control.onclick = () => { + this.send({type: "TakeAction", action: {action: "Pass"}}); + }; + this.pass_control_label = create_svg_element(this.svg, "text", ["pass-control-label"], [["x", "460"], ["y", "430"]]); + this.pass_control_text = document.createTextNode("Pass"); + this.pass_control_label.append(this.pass_control_text); + + this.take_initial_actions(actions); + } + + redraw_players() { + const active_player = this.active; + for (const [username, [icon, score, active]] of this.user_icons) { + if (!this.seats.has(username)) { + this.svg.removeChild(icon); + this.svg.removeChild(score); + this.svg.removeChild(active); + this.user_icons.delete(username); + } else { + const seat = this.seats.get(username); + const angle = this.player_angle(seat); + const x = 240 - 180 * Math.sin(angle); + const y = 250 + 120 * Math.cos(angle) + 50 * Math.sign(Math.cos(angle)); + icon.setAttribute("x", x); + icon.setAttribute("y", y); + score.childNodes[0].nodeValue = this.scores.get(username); + score.setAttribute("x", 240 - 120 * Math.sin(angle)); + score.setAttribute("y", 250 + 70 * Math.cos(angle)); + active.classList.toggle("active", active_player === username); + active.setAttribute("cx", x - 10); + active.setAttribute("cy", y - 5); + } + } + for (const [username, seat] of this.seats) { + if (!this.user_icons.has(username)) { + const angle = this.player_angle(seat); + const x = 240 - 180 * Math.sin(angle); + const y = 250 + 120 * Math.cos(angle) + 50 * (Math.sign(Math.cos(angle)) || 1); + + const icon = create_svg_element(this.svg, "text", [], [["x", x], ["y", y]]); + icon.append(document.createTextNode(username)); + + const score = create_svg_element(this.svg, "text", [], [["x", 240 - 120 * Math.sin(angle)], ["y", 250 + 70 * Math.cos(angle)]]); + score.append(document.createTextNode(this.scores.get(username) || "")); + + const active = create_svg_element(this.svg, "circle", ["active-indicator"], [["cx", x - 10], ["cy", y - 5], ["r", 5]]); + active.classList.toggle("active", active_player === username); + + this.user_icons.set(username, [icon, score, active]); + } + } + } + + card_image(username, card) { + const image = create_svg_element(this.svg, card === null ? "image" : "use", [], [["width", "45"], ["height", "70"], ["href", card_href(card)]]); + if (username === this.username) { + image.onclick = () => this.send({ + type: "TakeAction", + action: { + action: this.community.length === 0 ? "PutInBox" : "PlayCard", + card: card + } + }); + } + return image; + } + + redraw_cards() { + for (const [username, cards] of this.hands) { + const seat = this.seats.get(username); + const angle = this.player_angle(seat); + const offset = cards.length * 10; + let x = 227.5 + offset - 180 * Math.sin(angle); + const y = 210 + 120 * Math.cos(angle); + for (const {card, image} of cards) { + image.classList.toggle("my-card", username === this.username); + image.setAttribute("x", x); + image.setAttribute("y", y); + x -= 20; + } + } + for (const [username, cards] of this.played) { + const seat = this.seats.get(username); + const angle = this.player_angle(seat); + const offset = cards.length * 10; + const x = 227.5 + offset - 90 * Math.sin(angle); + const y = 210 + 30 * Math.cos(angle); + let adjust = 0; + for (const {card, image} of cards) { + image.classList.toggle("my-card", username === this.username); + image.setAttribute("x", x + 10 * Math.sin(adjust)); + image.setAttribute("y", y + 10 * Math.cos(adjust)); + adjust += 77; + } + } + let x = 120.0; + for (const {card, image} of this.community) { + const y = 210; + image.setAttribute("x", x); + image.setAttribute("y", y); + x -= 20; + } + x = 400.0; + for (const {card, image} of this.box) { + const y = 210; + image.setAttribute("x", x); + image.setAttribute("y", y); + x -= 20; + } + } + + remove_card(username, card) { + const hand = this.hands.get(username); + const card_index = hand.findIndex(c => c.card !== null && c.card.suit === card.suit && c.card.rank === card.rank); + const null_index = hand.findIndex(c => c.card === null); + if (card_index !== -1) { + return hand.splice(card_index, 1)[0]; + } else if (null_index !== -1) { + return hand.splice(null_index, 1)[0]; + } else { + return undefined; + } + } + + reveal_card(username, card) { + const hand = this.hands.get(username); + const found_card = hand.find(c => c.card !== null && c.card.suit === card.suit && c.card.rank === card.rank) || hand.find(c => c.card === null); + const found_card_box = this.box.find(c => c.card !== null && c.card.suit === card.suit && c.card.rank === card.rank) || this.box.find(c => c.card === null); + if (found_card !== undefined) { + this.svg.removeChild(found_card.image); + found_card.card = card; + found_card.image = this.card_image(username, card); + } else if (found_card_box !== undefined) { + this.svg.removeChild(found_card_box.image); + found_card_box.card = card; + found_card_box.image = this.card_image(null, card); + } + } + + take_action(user_action) { + super.take_action(user_action); + switch (user_action.action.action) { + case "Message": + break; + case "Join": + this.hands.set(user_action.username, []); + this.scores.set(user_action.username, 0); + this.redraw_players(); + break; + case "Leave": + this.hands.delete(user_action.username); + this.scores.delete(user_action.username); + this.redraw_players(); + break; + case "NextToDeal": + this.dealer = user_action.username; + for (const card of this.community) { + this.svg.removeChild(card.image); + } + for (const card of this.box) { + this.svg.removeChild(card.image); + } + this.community = []; + this.box = []; + for (const [username, hand] of this.hands) { + for (const card of hand) { + this.svg.removeChild(card.image); + } + } + this.hands.clear(); + this.active = this.player_after(user_action.username); + this.redraw_players(); + break; + case "ReceiveCard": + const card = { + card: user_action.action.card, + image: this.card_image(user_action.username, user_action.action.card), + }; + if (!this.hands.has(user_action.username)) { + this.hands.set(user_action.username, []); + } + this.hands.get(user_action.username).push(card); + this.redraw_cards(); + break; + case "Score": + if (!this.scores.has(user_action.username)) { + this.scores.set(user_action.username, 0); + } + this.set_info_text(user_action.username + " scores " + user_action.action.points + ": " + user_action.action.reason); + this.scores.set(user_action.username, this.scores.get(user_action.username) + user_action.action.points); + if ([...this.hands.values()].every(hand => hand.length === 0)) { + this.hands = this.played; + this.played = new Map(); + for (const [username, hand] of this.hands) { + for (const card of hand) { + this.svg.removeChild(card.image); + card.card = null; + card.image = this.card_image(username, null); + } + } + this.redraw_cards(); + } + this.redraw_players(); + break; + case "PlayCard": + const played = this.remove_card(user_action.username, user_action.action.card); + if (played !== undefined) { + this.svg.removeChild(played.image); + if (!this.played.has(user_action.username)) { + this.played.set(user_action.username, []); + } + this.played.get(user_action.username).push({ + card: user_action.action.card, + image:this.card_image(user_action.username, user_action.action.card), + }); + } + /* fallthrough */ + case "Pass": + this.active = user_action.username; + do { + this.active = this.player_after(this.active); + } while (this.active !== user_action.username && this.hands.has(this.active) && this.hands.get(this.active).length === 0); + this.redraw_cards(); + this.redraw_players(); + break; + case "RevealCard": + this.reveal_card(user_action.username, user_action.action.card); + this.redraw_cards(); + break; + case "PutInBox": + const removed = this.remove_card(user_action.username, user_action.action.card); + if (removed !== undefined) { + this.svg.removeChild(removed.image); + this.box.push({card: null, image: this.card_image(null, null)}); + } + this.redraw_cards(); + break; + case "CommunityCard": + const turnup = { + card: user_action.action.card, + image: this.card_image(null, user_action.action.card), + }; + this.community.push(turnup); + this.active = this.player_after(this.dealer); + this.redraw_cards(); + this.redraw_players(); + break; + case "EndDeal": + this.active = user_action.username; + this.redraw_players(); + break; + case "WinGame": + this.active = null; + this.redraw_cards(); + this.redraw_players(); + new CongratulateWinner(this.svg, user_action.username); + break; + default: + console.error("Unhandled action for cribbage", user_action); + this.set_error_text("Unhandled action for cribbage: " + user_action.action.action); + break; + } + } +} diff --git a/site/modules/game.js b/site/modules/game.js new file mode 100644 index 0000000..31a0eb0 --- /dev/null +++ b/site/modules/game.js @@ -0,0 +1,68 @@ +export class Game { + constructor(container, summary, actions, username, send, close) { + this.container = container; + this.summary = summary; + this.username = username; + this.send = send; + this.seats = new Map(); + } + + take_initial_actions(actions) { + for (const user_action of actions) { + this.take_action(user_action); + } + + if (!this.seats.has(this.username)) { + this.send({type: "TakeAction", action: {action: "Join", seat: 0, chips: 0}}); + } + } + + player_angle(seat) { + const my_seat = this.seats.get(this.username) || 0; + const num_seats = Math.max(...this.seats.values()) + 1; + const rel_seat = my_seat > seat ? num_seats + seat - my_seat : seat - my_seat; + if (num_seats % 4 === 0) { + if (rel_seat < num_seats / 4) { + return rel_seat * 2 * Math.PI / (num_seats + 2); + } else if (rel_seat >= num_seats / 4 && rel_seat <= 3 * num_seats / 4) { + return (rel_seat + 1) * 2 * Math.PI / (num_seats + 2); + } else { + return (rel_seat + 2) * 2 * Math.PI / (num_seats + 2); + } + } else { + return rel_seat * 2 * Math.PI / num_seats; + } + } + + player_after(username) { + const in_seat = this.seats.get(username); + let [player_after, player_after_seat] = [username, null]; + for (const [username, seat] of this.seats) { + if (seat > in_seat && (player_after_seat === null || seat < player_after_seat)) { + player_after = username; + player_after_seat = seat; + } + } + if (player_after_seat === null) for (const [username, seat] of this.seats) { + if (player_after_seat === null || seat < player_after_seat) { + player_after = username; + player_after_seat = seat; + } + } + return player_after; + } + + + take_action(user_action) { + switch (user_action.action.action) { + case "Join": + this.seats.set(user_action.username, user_action.action.seat); + break; + case "Leave": + this.seats.delete(user_action.username); + break; + default: + break; + } + } +} diff --git a/site/modules/gamechat.js b/site/modules/gamechat.js new file mode 100644 index 0000000..14eb32c --- /dev/null +++ b/site/modules/gamechat.js @@ -0,0 +1,74 @@ +import { create_svg_element } from "./svg.js"; +import { Game } from "./game.js"; +import { Chatroom } from "./chatroom.js"; + +export class GameWithChat extends Game { + constructor(container, summary, actions, username, send, close) { + super(container, summary, actions, username, send, close); + + this.svg = create_svg_element(container, "svg", [], [["viewBox", "0 0 500 500"]]); + this.background = create_svg_element(this.svg, "rect", [], [["width", "500"], ["height", "500"], ["fill", "#404040"]]); + + this.error_text_timeout = null; + this.error = create_svg_element(this.svg, "text", ["game-error"], [["x", "20"], ["y", "450"]]); + this.error_lines = []; + this.error_text = []; + const num_error_lines = 2; + for (let i = 0; i < num_error_lines; i++) { + const line = create_svg_element(this.error, "tspan", [], [["x", "20"], ["dy", "20"]]); + const text = document.createTextNode(""); + line.append(text); + this.error_lines.push(line); + this.error_text.push(text); + } + + this.close_control = create_svg_element(this.svg, "rect", ["close-control"], [["x", "460"], ["y", "5"], ["width", "35"], ["height", "35"], ["rx", "2"]]) + this.close_control.onclick = close; + this.close_image = create_svg_element(this.svg, "image", ["close-image"], [["x", "465"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/close.svg"]]); + + this.chat_control = create_svg_element(this.svg, "rect", ["chat-control"], [["x", "420"], ["y", "5"], ["width", "35"], ["height", "35"], ["rx", "2"]]) + this.chat_control.onclick = () => this.chatroom_container.classList.toggle("hidden"); + this.chat_image = create_svg_element(this.svg, "image", ["chat-image"], [["x", "425"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/chat.svg"]]); + + this.chatroom_container = document.createElement("div"); + this.chatroom_container.classList.add("embedded-chatroom"); + const send_chat_messages_only = message => { + if (message.type === "TakeAction" && message.action.action === "Message") { + send(message); + } + }; + this.chatroom = new Chatroom(this.chatroom_container, summary, [], username, send_chat_messages_only, null); + this.container.append(this.chatroom_container); + } + + set_text(style, text) { + this.error.setAttribute("class", style); + const lines = text.split("\n", this.error_text.length); + for (const text of this.error_text) { + text.textContent = ""; + } + for (let i = 0; i < lines.length; i++) { + this.error_text[i + this.error_text.length - lines.length].textContent = lines[i]; + } + if (this.error_text_timeout !== null) clearTimeout(this.error_text_timeout); + this.error_text_timeout = setTimeout(() => { + for (const text of this.error_text) { + text.textContent = ""; + } + this.error_text_timeout = null; + }, 5000); + } + + set_info_text(text) { + this.set_text("game-info", text); + } + + set_error_text(text) { + this.set_text("game-error", text); + } + + take_action(user_action) { + super.take_action(user_action); + this.chatroom.take_action(user_action); + } +} diff --git a/site/modules/gamelist.js b/site/modules/gamelist.js index c9f401e..d90322b 100644 --- a/site/modules/gamelist.js +++ b/site/modules/gamelist.js @@ -187,7 +187,7 @@ export class GameList { format_row.append(format_title); const format_value = document.createElement("td"); const format_input = document.createElement("select"); - for (const format of ["KnockOutWhist", "TexasHoldEm", "Chatroom"]) { + for (const format of ["KnockOutWhist", "Cribbage", "TexasHoldEm", "Chatroom"]) { const game_format = document.createElement("option"); game_format.setAttribute("value", format); game_format.innerText = format; @@ -206,13 +206,14 @@ export class GameList { table.append(format_row); const fields = [ - { name: "start_time", display: "Start Time", value: "", type: "datetime", formats: ["TexasHoldEm", "KnockOutWhist"] }, - { name: "max_players", display: "Max Players", value: 2, type: "number", formats: ["TexasHoldEm", "KnockOutWhist"] }, + { name: "start_time", display: "Start Time", value: "", type: "datetime", formats: ["TexasHoldEm", "KnockOutWhist", "Cribbage"] }, + { name: "max_players", display: "Max Players", value: 2, type: "number", formats: ["TexasHoldEm", "KnockOutWhist", "Cribbage"] }, { name: "small_blind", display: "Small Blind", value: 25, type: "number", formats: ["TexasHoldEm"] }, { name: "starting_stack", display: "Starting Stack", value: 5000, step: 1000, type: "number", formats: ["TexasHoldEm"] }, { name: "round_length", display: "Round Length", value: "", type: "duration", formats: ["TexasHoldEm"] }, { name: "tournament_length", display: "Tournament Length", value: "", type: "duration", formats: ["TexasHoldEm"] }, { name: "action_timeout", display: "Action Timeout", value: "", type: "duration", formats: ["TexasHoldEm"] }, + { name: "target_score", display: "Target Score", value: 121, type: "number", formats: ["Cribbage"] }, ]; for (const {name, display, value, type, step, formats} of fields) { diff --git a/site/modules/mainmenu.js b/site/modules/mainmenu.js index 4c75b8e..605a8e6 100644 --- a/site/modules/mainmenu.js +++ b/site/modules/mainmenu.js @@ -30,6 +30,17 @@ export class MainMenu { knock_out_whist.onclick = () => this.send({type: "JoinLobby", filter: "format: KnockOutWhist and created_in_last: 2:00:00"}); menu_container.append(knock_out_whist); + const cribbage = document.createElement("div"); + cribbage.classList.add("game-format-icon"); + const cribbage_img = document.createElement("img"); + cribbage_img.setAttribute("src", "img/cribbage.svg"); + const cribbage_text = document.createElement("p"); + cribbage_text.innerText = "Cribbage"; + cribbage.append(cribbage_img); + cribbage.append(cribbage_text); + cribbage.onclick = () => this.send({type: "JoinLobby", filter: "format: Cribbage and created_in_last: 2:00:00"}); + menu_container.append(cribbage); + const chatroom = document.createElement("div"); chatroom.classList.add("game-format-icon"); const chatroom_img = document.createElement("img"); diff --git a/site/modules/poker.js b/site/modules/poker.js index c5c75f1..a645a7a 100644 --- a/site/modules/poker.js +++ b/site/modules/poker.js @@ -1,25 +1,8 @@ -const svgns = "http://www.w3.org/2000/svg"; - +import { svgns, create_svg_element } from "./svg.js"; import { card_href, suit_href } from "./card.js"; import { Chatroom } from "./chatroom.js"; import { CongratulateWinner } from "./winner.js"; -function set_element_attributes(element, attributes) { - for (const [attr, value] of attributes) { - element.setAttribute(attr, value); - } -} - -function create_svg_element(parent, name, class_list, attributes) { - const element = document.createElementNS(svgns, name); - set_element_attributes(element, attributes); - for (const cls of class_list) { - element.classList.add(cls); - } - parent.append(element); - return element; -} - export class BetControls { constructor(game) { this.game = game @@ -87,7 +70,7 @@ export class BetControls { if (chips !== null && !isNaN(Number(chips))) { game.send({type: "TakeAction", action: {action: "Bet", chips: Number(chips)}}); } - } + }; this.bet_control_label = create_svg_element(this.game.svg, "text", ["bet-control-label"], [["x", "380"], ["y", "566"]]); this.bet_control_text = document.createTextNode("Bet"); @@ -165,11 +148,11 @@ export class TexasHoldEm { this.close_control = create_svg_element(this.svg, "rect", ["close-control"], [["x", "460"], ["y", "5"], ["width", "35"], ["height", "35"], ["rx", "2"]]) this.close_control.onclick = close; - this.close_image = create_svg_element(this.svg, "image", ["close-image"], [["x", "465"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/close.svg"]]) + this.close_image = create_svg_element(this.svg, "image", ["close-image"], [["x", "465"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/close.svg"]]); this.chat_control = create_svg_element(this.svg, "rect", ["chat-control"], [["x", "420"], ["y", "5"], ["width", "35"], ["height", "35"], ["rx", "2"]]) this.chat_control.onclick = () => this.chatroom_container.classList.toggle("hidden"); - this.chat_image = create_svg_element(this.svg, "image", ["chat-image"], [["x", "425"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/chat.svg"]]) + this.chat_image = create_svg_element(this.svg, "image", ["chat-image"], [["x", "425"], ["y", "10"], ["width", "25"], ["height", "25"], ["href", "img/chat.svg"]]); this.chatroom_container = document.createElement("div"); this.chatroom_container.classList.add("embedded-chatroom"); diff --git a/site/modules/socket.js b/site/modules/socket.js index b10cb31..83fb3c0 100644 --- a/site/modules/socket.js +++ b/site/modules/socket.js @@ -1,9 +1,8 @@ -const svgns = "http://www.w3.org/2000/svg"; - import { Auth } from "./auth.js"; import { MainMenu } from "./mainmenu.js"; import { GameList } from "./gamelist.js"; import { Chatroom } from "./chatroom.js"; +import { Cribbage } from "./cribbage.js"; import { KnockOutWhist } from "./whist.js"; import { TexasHoldEm } from "./poker.js"; @@ -128,6 +127,7 @@ export class Socket { this.send({type: "TakeAction", action: {action: "Join", seat: message.action.action.seat + 1, chips: message.action.action.chips}}); } else { console.error("Taking action failed: " + message.reason, message.action); + console.error("This.game: ", this.game); this.game.set_error_text(message.reason); } break; @@ -183,6 +183,7 @@ export class Socket { return 1000; case "RevealCard": return 1000; + case "Score": case "CutCard": return 3000; case "PlayCard": @@ -301,6 +302,7 @@ export class Socket { let Format; switch (this.game.summary.settings.format) { case "Chatroom": Format = Chatroom; break; + case "Cribbage": Format = Cribbage; break; case "KnockOutWhist": Format = KnockOutWhist; break; case "TexasHoldEm": Format = TexasHoldEm; break; default: diff --git a/site/modules/svg.js b/site/modules/svg.js new file mode 100644 index 0000000..a571542 --- /dev/null +++ b/site/modules/svg.js @@ -0,0 +1,17 @@ +export const svgns = "http://www.w3.org/2000/svg"; + +function set_element_attributes(element, attributes) { + for (const [attr, value] of attributes) { + element.setAttribute(attr, value); + } +} + +export function create_svg_element(parent, name, class_list, attributes) { + const element = document.createElementNS(svgns, name); + set_element_attributes(element, attributes); + for (const cls of class_list) { + element.classList.add(cls); + } + parent.append(element); + return element; +} diff --git a/site/modules/whist.js b/site/modules/whist.js index 0a3170d..f7b4503 100644 --- a/site/modules/whist.js +++ b/site/modules/whist.js @@ -1,5 +1,4 @@ -const svgns = "http://www.w3.org/2000/svg"; - +import { svgns, create_svg_element } from "./svg.js"; import { card_href, suit_href } from "./card.js"; import { Chatroom } from "./chatroom.js"; import { CongratulateWinner } from "./winner.js"; diff --git a/site/modules/winner.js b/site/modules/winner.js index 50bc0fb..330a6de 100644 --- a/site/modules/winner.js +++ b/site/modules/winner.js @@ -1,5 +1,4 @@ -const svgns = "http://www.w3.org/2000/svg"; - +import { svgns, create_svg_element } from "./svg.js"; import { card_href, FIFTY_TWO_CARD_DECK } from "./card.js"; import { random_int } from "./random.js"; diff --git a/site/style.css b/site/style.css index 0c86456..b614cf9 100644 --- a/site/style.css +++ b/site/style.css @@ -3,6 +3,7 @@ @import url("style/game-list.css"); @import url("style/game.css"); @import url("style/chatroom.css"); +@import url("style/cribbage.css"); @import url("style/poker.css"); @import url("style/whist.css"); @import url("style/winner.css"); diff --git a/site/style/cribbage.css b/site/style/cribbage.css new file mode 100644 index 0000000..8cb96f8 --- /dev/null +++ b/site/style/cribbage.css @@ -0,0 +1,22 @@ +.pass-control { + fill: #808080; + stroke: black; + stroke-width: 1px; + transition: fill 0.5s; + pointer-events: none; + cursor: pointer; +} + +.pass-control.active { + fill: #8080ff; + pointer-events: auto; +} + +.pass-control.active:hover { + fill: #a0a0ff; +} + +.pass-control-label { + pointer-events: none; + text-anchor: middle; +} diff --git a/site/style/game-list.css b/site/style/game-list.css index 95b79a5..fa0e216 100644 --- a/site/style/game-list.css +++ b/site/style/game-list.css @@ -78,6 +78,14 @@ display: table-row; } +.game-popup > table.Cribbage tr.game-field { + display: none; +} + +.game-popup > table.Cribbage tr.game-field.Cribbage { + display: table-row; +} + .game-popup input, .game-popup select { width: 100%; height: 100%;