Put bets on the felt as chips instead of numbers in a box
The seat puck showed a green figure that meant "what this player just bet" during a street and "what they've put in this hand" between streets — two different numbers wearing the same badge, sitting directly above their stack size. Easy to misread, and nothing on the table looked like money. Bets now sit on the felt between their player and the pot, as a chip coloured by the largest denomination in the chip legend that fits the amount: a $30 bet shows green against a $25 chip, a $2 bet shows white. When a street closes the chips come off the felt and the pot badge keeps the money, the way they'd be swept in. The puck's figure now always means the same thing — this player's total for the hand. On a phone the bet clings to its own puck instead of sitting on its own ring. That isn't a preference: sweeping every radius between the seat ring and the pot badge at 390px showed each one collides with either the pucks or the pot, because the felt has no room for a third ring at that size. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_015bnp7xKUPFFSpwkYNFaPgb
This commit is contained in:
@@ -69,13 +69,85 @@ const Render = {
|
||||
// ---- Seats around the table (compact pucks) ----
|
||||
|
||||
seatPosition(index, count) {
|
||||
return this.positionOnRing(index, count, 44, 42);
|
||||
},
|
||||
|
||||
// Bets sit on the felt between their player and the pot, the way chips actually
|
||||
// do — closer in than the seats, on the same spoke. The bets layer is inset to
|
||||
// match the seat ring (see the CSS), so this one radius holds at every size.
|
||||
betPosition(index, count) {
|
||||
return this.positionOnRing(index, count, 27, 25);
|
||||
},
|
||||
|
||||
positionOnRing(index, count, rx, ry) {
|
||||
const angle = -90 + (360 / count) * index;
|
||||
const radians = (angle * Math.PI) / 180;
|
||||
const rx = 44;
|
||||
const ry = 42;
|
||||
const left = 50 + rx * Math.cos(radians);
|
||||
const top = 50 + ry * Math.sin(radians);
|
||||
return { left, top };
|
||||
return {
|
||||
left: 50 + rx * Math.cos(radians),
|
||||
top: 50 + ry * Math.sin(radians),
|
||||
};
|
||||
},
|
||||
|
||||
// Colour a bet by the biggest chip that fits it, so a table with a chip legend
|
||||
// gets bets that look like the chips actually being pushed out.
|
||||
chipColorFor(amount) {
|
||||
const legend = [...(this.state.chipLegend || [])]
|
||||
.filter(chip => Number(chip.value) > 0)
|
||||
.sort((a, b) => Number(b.value) - Number(a.value));
|
||||
const match = legend.find(chip => amount >= Number(chip.value));
|
||||
return (match || legend[legend.length - 1] || {}).color || "#8a8a8a";
|
||||
},
|
||||
|
||||
// ---- Bets on the felt ----
|
||||
|
||||
makeBetChip(bet) {
|
||||
const chip = document.createElement("div");
|
||||
chip.className = "betChip";
|
||||
|
||||
const disc = document.createElement("span");
|
||||
disc.className = "betChipDisc";
|
||||
disc.style.background = this.chipColorFor(bet);
|
||||
|
||||
const amount = document.createElement("span");
|
||||
amount.className = "betChipAmount";
|
||||
amount.textContent = Money.format(bet);
|
||||
|
||||
chip.appendChild(disc);
|
||||
chip.appendChild(amount);
|
||||
return chip;
|
||||
},
|
||||
|
||||
// A phone's felt has no room for a third ring: measured every radius between the
|
||||
// seats and the pot badge and each one collides with one or the other. So on a
|
||||
// narrow screen the bet clings to its own puck instead — same chip, no ring —
|
||||
// sitting on whichever side faces the middle of the table.
|
||||
renderBets() {
|
||||
const layer = document.getElementById("betsLayer");
|
||||
if (!layer) return;
|
||||
layer.innerHTML = "";
|
||||
|
||||
const anchorToSeat = window.matchMedia("(max-width: 700px)").matches;
|
||||
|
||||
this.state.seats.forEach((player, index) => {
|
||||
const bet = Betting.committed(player);
|
||||
if (bet <= 0) return;
|
||||
|
||||
const chip = this.makeBetChip(bet);
|
||||
const pos = this.betPosition(index, this.state.seatCount);
|
||||
|
||||
if (anchorToSeat) {
|
||||
const seat = document.querySelector(`.seat[data-seat-index="${index}"]`);
|
||||
if (!seat) return;
|
||||
const seatPos = this.seatPosition(index, this.state.seatCount);
|
||||
chip.classList.add("betChipAnchored", seatPos.top > 50 ? "betChipAbove" : "betChipBelow");
|
||||
seat.appendChild(chip);
|
||||
return;
|
||||
}
|
||||
|
||||
chip.style.left = `${pos.left}%`;
|
||||
chip.style.top = `${pos.top}%`;
|
||||
layer.appendChild(chip);
|
||||
});
|
||||
},
|
||||
|
||||
puckLabel(player, seatIndex) {
|
||||
@@ -118,6 +190,8 @@ const Render = {
|
||||
|
||||
layer.appendChild(node);
|
||||
});
|
||||
|
||||
this.renderBets();
|
||||
},
|
||||
|
||||
isRedSuit(card) {
|
||||
@@ -168,11 +242,13 @@ const Render = {
|
||||
span.textContent = this.formatCard(card);
|
||||
cardsEl.appendChild(span);
|
||||
});
|
||||
// What's in front of them this street reads as the live number during betting;
|
||||
// between hands it falls back to their total for the hand.
|
||||
const streetBet = Betting.committed(player);
|
||||
const total = streetBet > 0
|
||||
? streetBet
|
||||
// Always this player's total for the hand. What they've just pushed out on this
|
||||
// street is shown as a chip on the felt instead — the puck used to flip between
|
||||
// the two meanings depending on the moment, which is a bad thing for a number
|
||||
// sitting next to a stack size to do.
|
||||
const handTotal = Betting.handTotal(player);
|
||||
const total = handTotal > 0
|
||||
? handTotal
|
||||
: HoldemState.computeContribution(player, this.state.chipLegend, this.state.basePotAmount);
|
||||
node.querySelector(".seatPuckTotal").textContent = total > 0 ? Money.format(total) : "";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user