--- /dev/null
+import { create_svg_element } from "./svg.js";
+
+export class BetControls {
+ constructor(game) {
+ this.game = game
+ this.mouse_clicked = false;
+ document.addEventListener("mousedown", () => this.mouse_clicked = true);
+ document.addEventListener("mouseup", () => this.mouse_clicked = false);
+
+ this.slider_min = 120;
+ this.slider_max = 430;
+
+ this.background = create_svg_element(this.game.svg, "rect", ["controls"], [["x", "50"], ["y", "500"], ["width", "400"], ["height", "96"], ["rx", "15"]])
+ this.bet_size_area = create_svg_element(this.game.svg, "rect", ["bet-size-area"], [["x", "57"], ["y", "507"], ["width", "58"], ["height", "22"]])
+ 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.game.min_bet(), Math.min(this.game.all_in_bet(), Number(chips)));
+ this.bet_size_text.textContent = Number(chips);
+ const p = (bet - this.game.min_bet()) / (this.game.all_in_bet() - this.game.min_bet());
+ const x = Math.max(this.slider_min, Math.min(this.slider_max, Math.sqrt(p) * (this.slider_max - this.slider_min) + this.slider_min));
+ this.bet_slider_thumb.setAttribute("x", x);
+ }
+ };
+
+ this.bet_size = create_svg_element(this.game.svg, "text", ["bet-size"], [["x", "86"], ["y", "522"]])
+ this.bet_size_text = document.createTextNode("150");
+ this.bet_size.append(this.bet_size_text);
+
+ const point = this.game.svg.createSVGPoint();
+ const move_slider = e => {
+ point.x = e.clientX;
+ point.y = e.clientY;
+ const coords = point.matrixTransform(this.game.svg.getScreenCTM().inverse());
+ const x = Math.max(this.slider_min, Math.min(this.slider_max, coords.x - 5));
+ const p = Math.pow((x - this.slider_min) / (this.slider_max - this.slider_min), 2);
+ const bet = Math.round(this.game.min_bet() * (1 - p) + this.game.all_in_bet() * p);
+ this.bet_size_text.textContent = bet;
+ this.bet_slider_thumb.setAttribute("x", x);
+ };
+
+ this.bet_slider = create_svg_element(this.game.svg, "g", ["bet-slider"], []);
+ this.bet_slider_area = create_svg_element(this.bet_slider, "rect", ["bet-slider-area"], [["x", this.slider_min], ["y", "507"], ["width", this.slider_max - this.slider_min], ["height", "22"]]);
+ this.bet_slider_track = create_svg_element(this.bet_slider, "rect", ["bet-slider-track"], [["x", this.slider_min], ["y", "515"], ["width", this.slider_max - this.slider_min], ["height", "6"]]);
+ this.bet_slider_thumb = create_svg_element(this.bet_slider, "rect", ["bet-slider-thumb"], [["x", this.slider_min], ["y", "509"], ["width", "10"], ["height", "18"]]);
+ this.bet_slider_area.onmousedown = this.bet_slider_track.onmousedown = this.bet_slider_thumb.onmousedown = move_slider;
+ this.bet_slider_area.onmousemove = this.bet_slider_track.onmousemove = this.bet_slider_thumb.onmousemove = e => { if (this.mouse_clicked) move_slider(e); };
+ this.bet_slider_area.ontouchmove = this.bet_slider_track.ontouchmove = this.bet_slider_thumb.ontouchmove = e => move_slider(e.touches.item(0));
+
+ this.fold_control = create_svg_element(this.game.svg, "rect", ["fold-control"], [["x", "60"], ["y", "536"], ["width", "120"], ["height", "50"], ["rx", "10"]])
+ this.fold_control.onclick = () => this.game.send({type: "TakeAction", action: {action: "Fold"}});
+
+ this.fold_control_label = create_svg_element(this.game.svg, "text", ["fold-control-label"], [["x", "120"], ["y", "566"]])
+ this.fold_control_text = document.createTextNode("Fold");
+ this.fold_control_label.append(this.fold_control_text);
+
+ this.call_control = create_svg_element(this.game.svg, "rect", ["call-control"], [["x", "190"], ["y", "536"], ["width", "120"], ["height", "50"], ["rx", "10"]])
+ this.call_control.onclick = () => this.game.send({type: "TakeAction", action: {action: "Bet", chips: this.game.chips_to_call()}});
+
+ this.call_control_label = create_svg_element(this.game.svg, "text", ["call-control-label"], [["x", "250"], ["y", "566"]]);
+ this.call_control_text = document.createTextNode("Call");
+ this.call_control_label.append(this.call_control_text);
+
+ this.bet_control = create_svg_element(this.game.svg, "rect", ["bet-control"], [["x", "320"], ["y", "536"], ["width", "120"], ["height", "50"], ["rx", "10"]])
+ this.bet_control.onclick = () => {
+ const chips = +this.bet_size_text.textContent;
+ 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");
+ this.bet_control_label.append(this.bet_control_text);
+ }
+
+ redraw() {
+ this.fold_control.classList.toggle("active", this.game.active === this.game.username && this.game.chips_to_call() > 0);
+ this.call_control.classList.toggle("active", this.game.active === this.game.username);
+ this.call_control_text.textContent = this.game.active === this.game.username && this.game.chips_to_call() == 0 ? "Check" : "Call";
+ const can_bet = this.game.active === this.game.username && this.game.chips_to_call() < this.game.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.game.min_bet();
+ this.bet_slider_thumb.setAttribute("x", this.slider_min);
+ }
+};
+
this.dealer = null;
this.dealing = false;
- const table = create_svg_element(this.svg, "ellipse", [], [["cx", "250"], ["cy", "253"], ["rx", "250"], ["ry", "110"], ["fill", "#604010"]]);
- const felt = create_svg_element(this.svg, "ellipse", [], [["cx", "250"], ["cy", "250"], ["rx", "240"], ["ry", "100"], ["fill", "green"]]);
-
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"}});
}
}
- next_active_player(username) {
- console.log('Finding player after', username);
- let active = username;
- do {
- active = this.player_after(active);
- console.log('.. got ', active);
- } while (active !== username && (this.passed.has(active) || this.hands.has(active) && this.hands.get(active).length === 0));
- return active;
- }
-
clear_pegging() {
this.count = 0;
this.passed.clear();
this.hands.clear();
this.played.clear();
this.passed.clear();
- this.active = this.player_after(user_action.username);
+ this.active = this.player_after(user_action.username, _ => true);
this.redraw_players();
break;
case "ReceiveCard":
break;
case "PlayCard":
this.count += value_ace_low(user_action.action.card.rank);
- if (this.count === 31) {
- this.clear_pegging();
- }
const played = this.remove_card(user_action.username, user_action.action.card);
if (played !== undefined) {
this.svg.removeChild(played.image);
image:this.card_image(user_action.username, user_action.action.card),
});
}
- this.active = this.next_active_player(user_action.username);
+ if (this.count === 31) {
+ this.clear_pegging();
+ }
+ this.active = this.player_after(user_action.username, username => !this.passed.has(username) || this.hands.has(username) && this.hands.get(username).length > 0);
this.redraw_cards();
this.redraw_players();
break;
if (this.passed.size === this.hands.size) {
this.clear_pegging();
}
- this.active = this.next_active_player(user_action.username);
+ this.active = this.player_after(user_action.username, username => !this.passed.has(username) || this.hands.has(username) && this.hands.get(username).length > 0);
this.redraw_cards();
this.redraw_players();
break;
image: this.card_image(null, user_action.action.card),
};
this.community.push(turnup);
- this.active = this.player_after(this.dealer);
+ this.active = this.player_after(this.dealer, _ => true);
this.redraw_cards();
this.redraw_players();
break;
}
if (!this.seats.has(this.username)) {
- this.send({type: "TakeAction", action: {action: "Join", seat: 0, chips: 0}});
+ const chips = this.summary.settings.starting_stack || 0;
+ this.send({type: "TakeAction", action: {action: "Join", seat: 0, chips: chips}});
}
}
}
}
- player_after(username) {
+ player_after(username, is_playing) {
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)) {
+ if (is_playing(username) && 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) {
+ if (is_playing(username) && player_after_seat === null || seat < player_after_seat) {
player_after = username;
player_after_seat = seat;
}
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.svg = create_svg_element(container, "svg", [], [["viewBox", "0 0 500 600"]]);
+ this.background = create_svg_element(this.svg, "rect", [], [["width", "500"], ["height", "600"], ["fill", "#404040"]]);
this.error_text_timeout = null;
this.error = create_svg_element(this.svg, "text", ["game-error"], [["x", "20"], ["y", "450"]]);
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"]]);
+ const table = create_svg_element(this.svg, "ellipse", [], [["cx", "250"], ["cy", "253"], ["rx", "250"], ["ry", "110"], ["fill", "#604010"]]);
+ const felt = create_svg_element(this.svg, "ellipse", [], [["cx", "250"], ["cy", "250"], ["rx", "240"], ["ry", "100"], ["fill", "green"]]);
+
this.chatroom_container = document.createElement("div");
this.chatroom_container.classList.add("embedded-chatroom");
const send_chat_messages_only = message => {
-import { svgns, create_svg_element } from "./svg.js";
+import { create_svg_element } from "./svg.js";
import { card_href, suit_href } from "./card.js";
-import { Chatroom } from "./chatroom.js";
+import { GameWithChat } from "./gamechat.js";
+import { BetControls } from "./betcontrols.js";
import { CongratulateWinner } from "./winner.js";
-export class BetControls {
- constructor(game) {
- this.game = game
- this.mouse_clicked = false;
- document.addEventListener("mousedown", () => this.mouse_clicked = true);
- document.addEventListener("mouseup", () => this.mouse_clicked = false);
-
- this.slider_min = 120;
- this.slider_max = 430;
-
- this.background = create_svg_element(this.game.svg, "rect", ["controls"], [["x", "50"], ["y", "500"], ["width", "400"], ["height", "96"], ["rx", "15"]])
- this.bet_size_area = create_svg_element(this.game.svg, "rect", ["bet-size-area"], [["x", "57"], ["y", "507"], ["width", "58"], ["height", "22"]])
- 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.game.min_bet(), Math.min(this.game.all_in_bet(), Number(chips)));
- this.bet_size_text.textContent = Number(chips);
- const p = (bet - this.game.min_bet()) / (this.game.all_in_bet() - this.game.min_bet());
- const x = Math.max(this.slider_min, Math.min(this.slider_max, Math.sqrt(p) * (this.slider_max - this.slider_min) + this.slider_min));
- this.bet_slider_thumb.setAttribute("x", x);
- }
- };
-
- this.bet_size = create_svg_element(this.game.svg, "text", ["bet-size"], [["x", "86"], ["y", "522"]])
- this.bet_size_text = document.createTextNode("150");
- this.bet_size.append(this.bet_size_text);
-
- const point = this.game.svg.createSVGPoint();
- const move_slider = e => {
- point.x = e.clientX;
- point.y = e.clientY;
- const coords = point.matrixTransform(this.game.svg.getScreenCTM().inverse());
- const x = Math.max(this.slider_min, Math.min(this.slider_max, coords.x - 5));
- const p = Math.pow((x - this.slider_min) / (this.slider_max - this.slider_min), 2);
- const bet = Math.round(this.game.min_bet() * (1 - p) + this.game.all_in_bet() * p);
- this.bet_size_text.textContent = bet;
- this.bet_slider_thumb.setAttribute("x", x);
- };
-
- this.bet_slider = create_svg_element(this.game.svg, "g", ["bet-slider"], []);
- this.bet_slider_area = create_svg_element(this.bet_slider, "rect", ["bet-slider-area"], [["x", String(this.slider_min)], ["y", "507"], ["width", String(this.slider_max - this.slider_min)], ["height", "22"]])
- this.bet_slider_track = create_svg_element(this.bet_slider, "rect", ["bet-slider-track"], [["x", String(this.slider_min)], ["y", "515"], ["width", String(this.slider_max - this.slider_min)], ["height", "6"]])
- this.bet_slider_thumb = create_svg_element(this.bet_slider, "rect", ["bet-slider-thumb"], [["x", String(this.slider_min)], ["y", "509"], ["width", "10"], ["height", "18"]])
- this.bet_slider_area.onmousedown = this.bet_slider_track.onmousedown = this.bet_slider_thumb.onmousedown = move_slider;
- this.bet_slider_area.onmousemove = this.bet_slider_track.onmousemove = this.bet_slider_thumb.onmousemove = e => { if (this.mouse_clicked) move_slider(e); };
- this.bet_slider_area.ontouchmove = this.bet_slider_track.ontouchmove = this.bet_slider_thumb.ontouchmove = e => move_slider(e.touches.item(0));
-
- this.fold_control = create_svg_element(this.game.svg, "rect", ["fold-control"], [["x", "60"], ["y", "536"], ["width", "120"], ["height", "50"], ["rx", "10"]])
- this.fold_control.onclick = () => this.game.send({type: "TakeAction", action: {action: "Fold"}});
-
- this.fold_control_label = create_svg_element(this.game.svg, "text", ["fold-control-label"], [["x", "120"], ["y", "566"]])
- this.fold_control_text = document.createTextNode("Fold");
- this.fold_control_label.append(this.fold_control_text);
-
- this.call_control = create_svg_element(this.game.svg, "rect", ["call-control"], [["x", "190"], ["y", "536"], ["width", "120"], ["height", "50"], ["rx", "10"]])
- this.call_control.onclick = () => this.game.send({type: "TakeAction", action: {action: "Bet", chips: this.game.chips_to_call()}});
-
- this.call_control_label = create_svg_element(this.game.svg, "text", ["call-control-label"], [["x", "250"], ["y", "566"]]);
- this.call_control_text = document.createTextNode("Call");
- this.call_control_label.append(this.call_control_text);
-
- this.bet_control = create_svg_element(this.game.svg, "rect", ["bet-control"], [["x", "320"], ["y", "536"], ["width", "120"], ["height", "50"], ["rx", "10"]])
- this.bet_control.onclick = () => {
- const chips = +this.bet_size_text.textContent;
- 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");
- this.bet_control_label.append(this.bet_control_text);
- }
-
- redraw() {
- this.fold_control.classList.toggle("active", this.game.active === this.game.username && this.game.chips_to_call() > 0);
- this.call_control.classList.toggle("active", this.game.active === this.game.username);
- this.call_control_text.textContent = this.game.active === this.game.username && this.game.chips_to_call() == 0 ? "Check" : "Call";
- const can_bet = this.game.active === this.game.username && this.game.chips_to_call() < this.game.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.game.min_bet();
- this.bet_slider_thumb.setAttribute("x", this.slider_min);
- }
-};
-
-export class TexasHoldEm {
+export class TexasHoldEm extends GameWithChat {
constructor(container, summary, actions, username, send, close) {
- this.container = container;
- this.summary = summary;
- this.actions = actions;
- this.username = username;
- this.send = send;
- this.seats = new Map();
+ super(container, summary, actions, username, send, close);
+ this.svg.classList.add("texas-hold-em");
+
this.stacks = new Map();
this.hands = new Map();
this.bets = new Map();
this.user_icons = new Map();
this.small_blind = this.summary.settings.small_blind;
this.big_blind = this.small_blind * 2;
- this.error_text_timeout = null;
-
- this.svg = create_svg_element(this.container, "svg", ["texas-hold-em"], [["viewBox", "0 0 500 600"]])
-
- const background = create_svg_element(this.svg, "rect", [], [["width", "500"], ["height", "600"], ["fill", ["#404040"]]]);
-
- 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 = document.createElementNS(svgns, "tspan");
- const text = document.createTextNode("");
- line.setAttribute("x", "20");
- line.setAttribute("dy", "20");
- line.append(text);
- this.error.append(line);
- this.error_lines.push(line);
- this.error_text.push(text);
- }
-
- const table = create_svg_element(this.svg, "ellipse", [], [["cx", "250"], ["cy", "253"], ["rx", "250"], ["ry", "110"], ["fill", "#604010"]]);
- const felt = create_svg_element(this.svg, "ellipse", [], [["cx", "250"], ["cy", "250"], ["rx", "240"], ["ry", "100"], ["fill", "green"]]);
const pot_size = create_svg_element(this.svg, "text", ["pot-size"], [["x", "400"], ["y", "250"]]);
this.pot_size_text = document.createTextNode("");
this.bet_controls = new BetControls(this)
- 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);
-
- for (const user_action of this.actions) {
- this.take_action(user_action);
- }
-
- if (!this.seats.has(this.username)) {
- this.send({type: "TakeAction", action: {action: "Join", seat: 0, chips: this.summary.settings.starting_stack}});
- }
- }
-
- 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);
- }
-
- player_angle(seat) {
- const rel_seat = this.my_seat > seat ? this.num_seats + seat - this.my_seat : seat - this.my_seat;
- if (this.num_seats % 4 === 0) {
- if (rel_seat < this.num_seats / 4) {
- return rel_seat * 2 * Math.PI / (this.num_seats + 2);
- } else if (rel_seat >= this.num_seats / 4 && rel_seat <= 3 * this.num_seats / 4) {
- return (rel_seat + 1) * 2 * Math.PI / (this.num_seats + 2);
- } else {
- return (rel_seat + 2) * 2 * Math.PI / (this.num_seats + 2);
- }
- } else {
- return rel_seat * 2 * Math.PI / this.num_seats;
- }
+ this.take_initial_actions(actions);
}
redraw_players() {
}
for (const [username, seat] of this.seats) {
if (!this.user_icons.has(username)) {
- const user = document.createElementNS(svgns, "text");
- const text = document.createTextNode(username);
- user.append(text);
const angle = this.player_angle(seat);
const x = 240 - 180 * Math.sin(angle);
const y = 240 + 120 * Math.cos(angle) + 60 * (Math.sign(Math.cos(angle)) || 1);
- user.setAttribute("x", x);
- user.setAttribute("y", y);
- this.svg.append(user);
+ const user = create_svg_element(this.svg, "text", [], [["x", x], ["y", y]]);
+ const text = document.createTextNode(username);
+ user.append(text);
- const stack = document.createElementNS(svgns, "text");
+ const stack = create_svg_element(this.svg, "text", [], [["x", x], ["y", y + 20]]);
const stack_text = document.createTextNode(this.stacks.get(username));
stack.append(stack_text);
- stack.setAttribute("x", x);
- stack.setAttribute("y", y + 20);
- this.svg.append(stack);
- const active = document.createElementNS(svgns, "circle");
- active.setAttribute("cx", x - 10);
- active.setAttribute("cy", y - 5);
- active.setAttribute("r", 5);
- active.classList.add("active-indicator");
+ const active = create_svg_element(this.svg, "circle", ["active-indicator"], [["cx", x - 10], ["cy", y - 5], ["r", 5]]);
active.classList.toggle("active", this.active === username);
- this.svg.append(active);
- const bet = document.createElementNS(svgns, "text");
+ const bet = create_svg_element(this.svg, "text", [], [["x", 240 - 120 * Math.sin(angle)], ["y", 250 + 70 * Math.cos(angle)]]);
const bet_text = document.createTextNode(this.bets.get(username) || "");
bet.append(bet_text);
- bet.setAttribute("x", 240 - 120 * Math.sin(angle));
- bet.setAttribute("y", 250 + 70 * Math.cos(angle));
- this.svg.append(bet);
this.user_icons.set(username, [user, stack, active, bet]);
}
}
card_image(username, card) {
- const image = document.createElementNS(svgns, card === null ? "image" : "use");
- image.setAttribute("width", "45");
- image.setAttribute("height", "70");
- image.setAttribute("href", card_href(card));
- this.svg.append(image);
- return image;
- }
-
- player_after(username, is_playing) {
- const in_seat = this.seats.get(username);
- let [player_after, player_after_seat] = [null, null];
- for (const [username, seat] of this.seats) {
- if (is_playing(username) && 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 (is_playing(username) && (player_after_seat === null || seat < player_after_seat)) {
- player_after = username;
- player_after_seat = seat;
- }
- }
- return player_after;
+ return create_svg_element(this.svg, card === null ? "image" : "use", [], [["width", "45"], ["height", "70"], ["href", card_href(card)]]);
}
chips_to_call() {
}
take_action(user_action) {
- this.chatroom.take_action(user_action);
+ super.take_action(user_action);
switch (user_action.action.action) {
case "Message":
break;
case "Join":
- this.seats.set(user_action.username, user_action.action.seat);
this.stacks.set(user_action.username, user_action.action.chips);
this.hands.set(user_action.username, []);
- this.num_seats = Math.max(...this.seats.values()) + 1;
- this.my_seat = this.seats.get(this.username) || 0;
this.redraw_players();
break;
case "Leave":
- this.seats.delete(user_action.username);
this.stacks.delete(user_action.username);
this.hands.delete(user_action.username);
this.bets.delete(user_action.username);
- this.num_seats = Math.max(...this.seats.values()) + 1;
- this.my_seat = this.seats.get(this.username) || 0;
this.redraw_players();
break;
case "KnockedOut":
}
this.hands.delete(user_action.username);
this.bets.delete(user_action.username);
- const small_blind = this.player_after(this.dealer, () => true);
- const big_blind = this.player_after(small_blind, () => true);
+ const small_blind = this.player_after(this.dealer, _ => true);
+ const big_blind = this.player_after(small_blind, _ => true);
if (this.stacks.length <= 1) {
this.seats.delete(user_action.username);
} else if (user_action.username === this.dealer) {
this.redraw_players();
break;
case "EndDeal":
- this.active = this.player_after(user_action.username, username => true);
+ this.active = this.player_after(user_action.username, _ => true);
this.redraw_players();
break;
case "WinGame":
import { svgns, create_svg_element } from "./svg.js";
import { card_href, suit_href } from "./card.js";
-import { Chatroom } from "./chatroom.js";
+import { GameWithChat } from "./gamechat.js";
import { CongratulateWinner } from "./winner.js";
-export class KnockOutWhist {
+export class KnockOutWhist extends GameWithChat {
constructor(container, summary, actions, username, send, close) {
- this.container = container;
- this.summary = summary;
- this.username = username;
- this.send = send;
- this.seats = new Map();
+ super(container, summary, actions, username, send, close);
+ this.svg.classList.add("knock-out-whist");
+
this.hands = new Map();
this.tricks_won = new Map();
this.community = [];
this.trick = [];
this.user_icons = new Map();
- this.error_text_timeout = null;
-
- this.svg = document.createElementNS(svgns, "svg");
- this.svg.classList.add("knock-out-whist");
- this.svg.setAttribute("viewBox", "0 0 500 500");
-
- const background = document.createElementNS(svgns, "rect");
- background.setAttribute("width", "500");
- background.setAttribute("height", "500");
- background.setAttribute("fill", "#404040");
- this.svg.append(background);
- this.error = document.createElementNS(svgns, "text");
- this.error.setAttribute("x", "20");
- this.error.setAttribute("y", "450");
- this.error.classList.add("game-error");
- this.error_lines = [];
- this.error_text = [];
- const num_error_lines = 2;
- for (let i = 0; i < num_error_lines; i++) {
- const line = document.createElementNS(svgns, "tspan");
- const text = document.createTextNode("");
- line.setAttribute("x", "20");
- line.setAttribute("dy", "20");
- line.append(text);
- this.error.append(line);
- this.error_lines.push(line);
- this.error_text.push(text);
- }
- this.svg.append(this.error);
-
- const table = document.createElementNS(svgns, "ellipse");
- table.setAttribute("cx", "250");
- table.setAttribute("cy", "253");
- table.setAttribute("rx", "250");
- table.setAttribute("ry", "110");
- table.setAttribute("fill", "#604010");
- this.svg.append(table);
-
- const felt = document.createElementNS(svgns, "ellipse");
- felt.setAttribute("cx", "250");
- felt.setAttribute("cy", "250");
- felt.setAttribute("rx", "240");
- felt.setAttribute("ry", "100");
- felt.setAttribute("fill", "green");
- this.svg.append(felt);
-
- const suits = document.createElementNS(svgns, "rect");
- suits.setAttribute("x", "340");
- suits.setAttribute("y", "235");
- suits.setAttribute("width", "100");
- suits.setAttribute("height", "30");
- suits.setAttribute("rx", "10");
- suits.setAttribute("fill", "darkgreen");
- this.svg.append(suits);
+ const suits = create_svg_element(this.svg, "rect", [], [["x", "340"], ["y", "235"], ["width", "100"], ["height", "30"], ["rx", "10"], ["fill", "darkgreen"]]);
this.glyphs = new Map();
let x = 340;
for (const suit of ["Clubs", "Diamonds", "Hearts", "Spades"]) {
- const highlight = document.createElementNS(svgns, "circle");
- highlight.setAttribute("cx", x + 12.5);
- highlight.setAttribute("cy", "250");
- highlight.setAttribute("r", "10");
- highlight.classList.add("suit-highlight");
+ const highlight = create_svg_element(this.svg, "circle", ["suit-highlight"], [["cx", x + 12.5], ["cy", "250"], ["r", "10"]]);
highlight.onclick = () => this.send({type: "TakeAction", action: {action: "ChooseTrumps", suit: suit}});
- this.svg.append(highlight);
this.glyphs.set(suit, highlight);
- const glyph = document.createElementNS(svgns, "use");
- glyph.setAttribute("x", x);
- glyph.setAttribute("y", "235");
- glyph.setAttribute("width", "25");
- glyph.setAttribute("height", "30");
- glyph.setAttribute("href", suit_href(suit));
- glyph.classList.add("suit-glyph");
- this.svg.append(glyph);
+ const glyph = create_svg_element(this.svg, "use", ["suit-glyph"], [["x", x], ["y", "235"], ["width", "25"], ["height", "30"], ["href", suit_href(suit)]]);
x += 25;
}
- this.close_control = document.createElementNS(svgns, "rect");
- this.close_control.setAttribute("x", "460");
- this.close_control.setAttribute("y", "5");
- this.close_control.setAttribute("width", "35");
- this.close_control.setAttribute("height", "35");
- this.close_control.setAttribute("rx", "2");
- this.close_control.onclick = close;
- this.close_control.classList.add("close-control");
- this.svg.append(this.close_control);
- this.close_image = document.createElementNS(svgns, "image");
- this.close_image.setAttribute("x", "465");
- this.close_image.setAttribute("y", "10");
- this.close_image.setAttribute("width", "25");
- this.close_image.setAttribute("height", "25");
- this.close_image.setAttribute("href", "img/close.svg");
- this.close_image.classList.add("close-image");
- this.svg.append(this.close_image);
-
- this.chat_control = document.createElementNS(svgns, "rect");
- this.chat_control.setAttribute("x", "420");
- this.chat_control.setAttribute("y", "5");
- this.chat_control.setAttribute("width", "35");
- this.chat_control.setAttribute("height", "35");
- this.chat_control.setAttribute("rx", "2");
- this.chat_control.onclick = () => this.chatroom_container.classList.toggle("hidden");
- this.chat_control.classList.add("chat-control");
- this.svg.append(this.chat_control);
- this.chat_image = document.createElementNS(svgns, "image");
- this.chat_image.setAttribute("x", "425");
- this.chat_image.setAttribute("y", "10");
- this.chat_image.setAttribute("width", "25");
- this.chat_image.setAttribute("height", "25");
- this.chat_image.setAttribute("href", "img/chat.svg");
- this.chat_image.classList.add("chat-image");
- this.svg.append(this.chat_image);
-
- this.container.append(this.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);
-
- 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}});
- }
- }
-
- 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);
- }
-
- player_angle(seat) {
- const rel_seat = this.my_seat > seat ? this.num_seats + seat - this.my_seat : seat - this.my_seat;
- if (this.num_seats % 4 === 0) {
- if (rel_seat < this.num_seats / 4) {
- return rel_seat * 2 * Math.PI / (this.num_seats + 2);
- } else if (rel_seat >= this.num_seats / 4 && rel_seat <= 3 * this.num_seats / 4) {
- return (rel_seat + 1) * 2 * Math.PI / (this.num_seats + 2);
- } else {
- return (rel_seat + 2) * 2 * Math.PI / (this.num_seats + 2);
- }
- } else {
- return rel_seat * 2 * Math.PI / this.num_seats;
- }
+ this.take_initial_actions(actions);
}
redraw_players() {
}
card_image(username, card) {
- const image = document.createElementNS(svgns, card === null ? "image" : "use");
- image.setAttribute("width", "45");
- image.setAttribute("height", "70");
- image.setAttribute("href", card_href(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: "PlayCard", card: card}});
}
- this.svg.append(image);
return image;
}
- 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) {
- this.chatroom.take_action(user_action);
+ super.take_action(user_action);
switch (user_action.action.action) {
case "Message":
break;
case "Join":
- this.seats.set(user_action.username, user_action.action.seat);
this.hands.set(user_action.username, []);
- this.num_seats = Math.max(...this.seats.values()) + 1;
- this.my_seat = this.seats.get(this.username) || 0;
this.redraw_players();
break;
case "Leave":
- this.seats.delete(user_action.username);
this.hands.delete(user_action.username);
- this.num_seats = Math.max(...this.seats.values()) + 1;
- this.my_seat = this.seats.get(this.username) || 0;
this.redraw_players();
break;
case "KnockedOut":
this.hands.clear();
this.tricks_won.clear();
this.trumps = null;
- this.active = this.player_after(user_action.username);
+ this.active = this.player_after(user_action.username, _ => true);
this.redraw_trumps();
this.redraw_players();
break;
}
});
this.hands.set(user_action.username, cards);
- this.active = this.player_after(user_action.username);
+ this.active = this.player_after(user_action.username, _ => true);
this.redraw_cards();
this.redraw_players();
break;
this.redraw_players();
break;
case "EndDeal":
- this.active = this.player_after(user_action.username);
+ this.active = this.player_after(user_action.username, _ => true);
this.redraw_players();
break;
case "WinGame":