games: the money moves

The table dealt cards but settled money by editing a number. So the felt got
the two things it was missing: a bet spot in front of you, and the house's rack
beside the shoe. Every chip is now always travelling between one of those and
the other.

You build a bet by throwing chips onto the spot — the chip you clicked is the
chip that flies. The stake sits there through the hand. The house pays out of
its rack into the spot, and the pile is then swept back to your stack. A loss
goes to the rack and does not come back.

Two rules hold it together. The number under the pile is a readout of the pile,
never the other way round: the bet starts at nothing rather than at a default
nobody put down, and a settled hand leaves your stake back up on the spot,
because otherwise the panel prints "your bet: 300" over an empty circle. And
the chip bar does not move until the chips that justify it have landed — a
counter that pays you before the dealer turns over is a counter that has told
you the ending.

casino-fx.js is the engine underneath: chips fly on an arc, out of a fixed
overlay so no container clips one crossing from a button to the felt. It knows
nothing about blackjack.

Also: cards land with weight and a degree or two of tilt, so a hand looks dealt
rather than typeset; the dealer takes a beat before drawing out; and a natural
gets confetti, which is the only thing in the room that does.

Driven in a real browser, which is the only way to review an animation — and
which is what caught the verdict pill rendering white on white in a dark room,
a chip rack sitting on top of the dealer, and Hit being offered over a table
that was still being paid out. devcasino_test.go is that harness, kept.
This commit is contained in:
prosolis
2026-07-14 00:33:49 -07:00
parent b00da21a47
commit 6961f90634
8 changed files with 845 additions and 64 deletions

View File

@@ -18,9 +18,18 @@
{{template "_chipbar" .}}
<!-- The felt. -->
<!-- The felt. The two things on it that aren't cards are the shoe the cards
come out of and the rack the money comes out of — a chip on this table is
always travelling between one of those and the spot in front of you. -->
<section class="pete-felt relative overflow-hidden rounded-3xl p-6 sm:p-10 shadow-pete-lg border-2 border-[color:var(--ink)]/10">
<div class="pete-rack" data-house aria-hidden="true">
<span data-chip="500" style="--stack: 5"></span>
<span data-chip="100" style="--stack: 7"></span>
<span data-chip="25" style="--stack: 4"></span>
<span data-chip="5" style="--stack: 6"></span>
</div>
<!-- The shoe every card flies out of. -->
<div class="pete-shoe" aria-hidden="true"></div>
@@ -29,7 +38,7 @@
<!-- Dealer -->
<div>
<div class="mb-2 flex items-center gap-2">
<span class="text-xs font-bold uppercase tracking-wider text-white/60">Dealer</span>
<span data-dealer-label class="text-xs font-bold uppercase tracking-wider text-white/60">Dealer</span>
<span data-dealer-total class="hidden rounded-full bg-black/25 px-2.5 py-0.5 text-xs font-bold tabular-nums text-white"></span>
</div>
<div data-dealer class="pete-hand" aria-live="polite"></div>
@@ -37,17 +46,25 @@
<!-- What just happened. -->
<div class="flex min-h-[2.75rem] items-center justify-center">
<p data-verdict class="hidden rounded-full bg-white/95 px-5 py-2 font-display text-lg font-bold shadow-pete"></p>
<!-- The pill is white, so its text is ink — not var(--ink), which is the
room's cream and would be white on white. -->
<p data-verdict class="hidden rounded-full bg-white/95 px-5 py-2 font-display text-lg font-bold text-[#2b2118] shadow-pete"></p>
</div>
<!-- Player -->
<!-- Player: the bet sits in front of the cards it's riding on. -->
<div>
<div class="mb-2 flex items-center gap-2">
<span class="text-xs font-bold uppercase tracking-wider text-white/60">You</span>
<span data-player-total class="hidden rounded-full bg-black/25 px-2.5 py-0.5 text-xs font-bold tabular-nums text-white"></span>
<span data-bet class="hidden ml-auto rounded-full bg-[color:var(--accent)] px-3 py-0.5 text-xs font-bold tabular-nums text-white"></span>
</div>
<div data-player class="pete-hand" aria-live="polite"></div>
<div class="flex items-center gap-5">
<div class="pete-spot" data-spot>
<span class="pete-spot-label">Bet</span>
<div class="pete-stack" data-stack></div>
<span data-spot-total class="pete-spot-total hidden"></span>
</div>
<div data-player class="pete-hand flex-1" aria-live="polite"></div>
</div>
</div>
</div>
@@ -58,14 +75,15 @@
<div class="flex flex-wrap items-center gap-x-6 gap-y-4">
<div>
<div class="text-xs font-semibold uppercase tracking-wider text-[color:var(--ink)]/50">Your bet</div>
<div class="font-display text-3xl font-bold tabular-nums"><span data-bet-amount>25</span></div>
<div class="font-display text-3xl font-bold tabular-nums"><span data-bet-amount>0</span></div>
</div>
<div class="flex flex-wrap items-center gap-2">
{{range .Denominations}}
<button type="button" data-chip="{{.}}"
class="pete-chip h-12 w-12 rounded-full font-display text-sm font-bold text-white shadow-pete
hover:-translate-y-0.5 active:translate-y-0 transition">{{.}}</button>
<button type="button" data-chip="{{.}}" aria-label="Bet {{.}} more"
class="pete-chip pete-disc grid h-12 w-12 place-items-center font-display text-sm font-bold text-white">
<span>{{.}}</span>
</button>
{{end}}
<button type="button" data-bet-clear
class="rounded-full px-3 py-2 text-sm font-semibold text-[color:var(--ink)]/50 hover:text-[color:var(--ink)] transition">Clear</button>
@@ -108,6 +126,7 @@
{{end}}
{{define "scripts"}}
<script src="/static/js/casino-fx.js" defer></script>
<script src="/static/js/games.js" defer></script>
<script src="/static/js/blackjack.js" defer></script>
{{end}}