games: burial send-offs, money rain on wins, and honest stack targeting

Three front-end changes to the casino games:

- uno: a draw-card stack now names the seat it's actually pointed at
  ("+N on Beep") instead of always reading "+N on you". The bill reads
  v.turn, which the engine advances to the target when a draw card lands.

- uno: the No Mercy rule buries a seat with some ceremony now. bury()
  rolls one of four send-offs over the seat (or your hand, if it's you) —
  a rockslide that piles up, a tombstone, a coffin, or the Mega Man death
  burst — each with its own synth sound, and a static headstone under
  reduced motion. The "Buried on N" badge still rides alongside.

- all games: winning makes it rain. FX.moneyRain drops a curtain of bills
  and coins with a jackpot coin-cascade sound, gated by significance so it
  reads as a win and not as noise: hold'em keeps its light per-pot confetti
  and only rains on a real haul (>= 20bb), while the confetti stays the
  rare cherry (natural 21, full board clear, phrase solved outright).
This commit is contained in:
prosolis
2026-07-15 18:37:19 -07:00
parent e0d90ff7cc
commit 8504c4f47a
10 changed files with 326 additions and 25 deletions

View File

@@ -319,6 +319,65 @@
return Promise.all(done);
}
// moneyRain: it rains money. The big finish on a win — bills and coins tumbling
// the whole height of the window, swaying and spinning as they fall. Louder than
// confetti, and it carries its own sound (a coin cascade) so it can stand in for
// the plain win fanfare wherever a table decides a win is worth the theatre.
//
// Like burst, the sound comes before the reduced-motion bail: no rain still
// deserves to be heard.
function moneyRain(opts) {
opts = opts || {};
if (opts.sound !== false) sfx(opts.sound || "jackpot");
if (reduced) return Promise.resolve();
var n = opts.count || 22;
var glyphs = opts.glyphs || ["💶", "💰", "🪙", "💵", "💴", "🤑"];
var vw = window.innerWidth || document.documentElement.clientWidth;
var vh = window.innerHeight || document.documentElement.clientHeight;
var done = [];
for (var i = 0; i < n; i++) {
var bit = document.createElement("div");
bit.className = "pete-money";
bit.textContent = glyphs[i % glyphs.length];
stage().appendChild(bit);
// Spread across the width in even lanes, each nudged off its lane so the
// curtain doesn't read as a grid. It sways sideways and spins on the way down.
var x = ((i + 0.5) / n) * vw + jitter(i, vw / n / 2);
var drift = jitter(i + 5, 80);
var startY = -50 - Math.abs(jitter(i + 2, 180));
var spin = jitter(i, 300);
var scale = 0.85 + Math.abs(jitter(i + 3, 0.55));
done.push(
bit
.animate(
[
{ transform: t(x, startY, scale, 0), opacity: 0, offset: 0 },
{ transform: t(x + drift, startY + 40, scale, spin * 0.2), opacity: 1, offset: 0.1 },
{ transform: t(x - drift, vh * 0.55, scale, spin * 0.6), opacity: 1, offset: 0.6 },
{ transform: t(x + drift * 0.5, vh + 60, scale, spin), opacity: 1, offset: 1 },
],
{
duration: 1600 + Math.abs(jitter(i + 9, 1)) * 900,
easing: "cubic-bezier(0.35, 0.15, 0.55, 1)",
fill: "both",
delay: Math.abs(jitter(i, 320)),
}
)
.finished.catch(function () {})
.then(
(function (b) {
return function () { b.remove(); };
})(bit)
)
);
}
return Promise.all(done);
}
// count rolls a number to a new value instead of swapping it. A chip count that
// jumps is a variable; one that climbs is a payout.
function count(el, to, opts) {
@@ -359,6 +418,7 @@
flyMany: flyMany,
spot: spot,
burst: burst,
moneyRain: moneyRain,
count: count,
centre: centre,
};