Merge pull request 'Put bets on the felt as chips instead of numbers in a box' (#29) from feature/casino-holdem-bet-chips into feature/casino-holdem-sidebar-polish
Reviewed-on: #29
This commit was merged in pull request #29.
This commit is contained in:
@@ -31,6 +31,7 @@
|
|||||||
<span id="potBadgeAmount">$0</span>
|
<span id="potBadgeAmount">$0</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="betsLayer" aria-hidden="true"></div>
|
||||||
<div id="seatsLayer"></div>
|
<div id="seatsLayer"></div>
|
||||||
</div>
|
</div>
|
||||||
<p id="tableHint" class="tableHint"></p>
|
<p id="tableHint" class="tableHint"></p>
|
||||||
|
|||||||
@@ -223,6 +223,68 @@ body {
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---------- Bets on the felt ---------- */
|
||||||
|
|
||||||
|
/* Shares the seat ring's box exactly, so a bet always lands on the spoke between its
|
||||||
|
player and the pot. When the seat ring gets inset on a phone, this moves with it —
|
||||||
|
measured the alternative, and a bets ring on its own geometry ends up either behind
|
||||||
|
the pucks or on top of the pot badge. */
|
||||||
|
#betsLayer {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.betChip {
|
||||||
|
position: absolute;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.28rem;
|
||||||
|
padding: 0.1rem 0.42rem 0.1rem 0.2rem;
|
||||||
|
background: rgba(0, 0, 0, 0.55);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
border-radius: 999px;
|
||||||
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.5);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reads as a chip seen from above: a lighter top-left, a rim, and the notches a
|
||||||
|
casino chip has around its edge. */
|
||||||
|
.betChipDisc {
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.45);
|
||||||
|
box-shadow:
|
||||||
|
inset 0 1px 1px rgba(255, 255, 255, 0.45),
|
||||||
|
inset 0 -1px 2px rgba(0, 0, 0, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
.betChipAmount {
|
||||||
|
font-size: 0.62rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #f0f0f0;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clinging to a puck rather than sitting on its own ring — the phone layout. */
|
||||||
|
.betChipAnchored {
|
||||||
|
left: 50%;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.betChipAbove {
|
||||||
|
top: -11px;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.betChipBelow {
|
||||||
|
top: auto;
|
||||||
|
bottom: -11px;
|
||||||
|
transform: translate(-50%, 50%);
|
||||||
|
}
|
||||||
|
|
||||||
/* ---------- Seats (compact pucks on the table) ---------- */
|
/* ---------- Seats (compact pucks on the table) ---------- */
|
||||||
|
|
||||||
.seat {
|
.seat {
|
||||||
@@ -1734,7 +1796,8 @@ body.hasActionBar #toastHost {
|
|||||||
|
|
||||||
/* Keeps every puck inside the viewport: the ring is drawn within this box,
|
/* Keeps every puck inside the viewport: the ring is drawn within this box,
|
||||||
so half a puck of padding is exactly enough to stop the overhang. */
|
so half a puck of padding is exactly enough to stop the overhang. */
|
||||||
#seatsLayer {
|
#seatsLayer,
|
||||||
|
#betsLayer {
|
||||||
inset: 26px 44px;
|
inset: 26px 44px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,13 +69,85 @@ const Render = {
|
|||||||
// ---- Seats around the table (compact pucks) ----
|
// ---- Seats around the table (compact pucks) ----
|
||||||
|
|
||||||
seatPosition(index, count) {
|
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 angle = -90 + (360 / count) * index;
|
||||||
const radians = (angle * Math.PI) / 180;
|
const radians = (angle * Math.PI) / 180;
|
||||||
const rx = 44;
|
return {
|
||||||
const ry = 42;
|
left: 50 + rx * Math.cos(radians),
|
||||||
const left = 50 + rx * Math.cos(radians);
|
top: 50 + ry * Math.sin(radians),
|
||||||
const top = 50 + ry * Math.sin(radians);
|
};
|
||||||
return { left, top };
|
},
|
||||||
|
|
||||||
|
// 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) {
|
puckLabel(player, seatIndex) {
|
||||||
@@ -118,6 +190,8 @@ const Render = {
|
|||||||
|
|
||||||
layer.appendChild(node);
|
layer.appendChild(node);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.renderBets();
|
||||||
},
|
},
|
||||||
|
|
||||||
isRedSuit(card) {
|
isRedSuit(card) {
|
||||||
@@ -168,11 +242,13 @@ const Render = {
|
|||||||
span.textContent = this.formatCard(card);
|
span.textContent = this.formatCard(card);
|
||||||
cardsEl.appendChild(span);
|
cardsEl.appendChild(span);
|
||||||
});
|
});
|
||||||
// What's in front of them this street reads as the live number during betting;
|
// Always this player's total for the hand. What they've just pushed out on this
|
||||||
// between hands it falls back to their total for the hand.
|
// street is shown as a chip on the felt instead — the puck used to flip between
|
||||||
const streetBet = Betting.committed(player);
|
// the two meanings depending on the moment, which is a bad thing for a number
|
||||||
const total = streetBet > 0
|
// sitting next to a stack size to do.
|
||||||
? streetBet
|
const handTotal = Betting.handTotal(player);
|
||||||
|
const total = handTotal > 0
|
||||||
|
? handTotal
|
||||||
: HoldemState.computeContribution(player, this.state.chipLegend, this.state.basePotAmount);
|
: HoldemState.computeContribution(player, this.state.chipLegend, this.state.basePotAmount);
|
||||||
node.querySelector(".seatPuckTotal").textContent = total > 0 ? Money.format(total) : "";
|
node.querySelector(".seatPuckTotal").textContent = total > 0 ? Money.format(total) : "";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user