games: rotate through foley takes on every play, not just by v

Take-selection was keyed only on the caller's v, but callers pass small
constants (chip is always v:0), so only the first take of each sound ever
played. Add a per-name cursor that advances every call: consecutive plays
now walk the takes, v just offsets simultaneous sounds and drives pitch.
This commit is contained in:
prosolis
2026-07-14 23:44:24 -07:00
parent 30b0e8debb
commit 1ca794ea1a

View File

@@ -147,6 +147,13 @@
var buffers = {};
var warmed = false;
// A rotating cursor per sound, so that even a run of identical calls walks
// through the takes rather than replaying the first one. Callers pass `v` to
// separate simultaneous sounds (the cards of one deal), but many pass the same
// constant every time — the cursor is what keeps a stack of chips from being the
// same click over and over.
var turn = {};
// decode fetches a file and turns it into a buffer, exactly once per filename.
// decodeAudioData exists in a modern promise flavour and an old callback one;
// this handles both so the foley works on older Safari too.
@@ -179,14 +186,17 @@
function sample(name, t0, v) {
var cfg = SAMPLES[name];
if (!cfg) return false;
var i = Math.abs(Math.round(v));
var buf = buffers[cfg.files[i % cfg.files.length]];
// Advance the cursor, then offset it by v. The cursor guarantees consecutive
// calls move to the next take; v spreads apart sounds fired together (one
// deal's cards) so they don't all land on the same take at once.
var k = (turn[name] = (turn[name] || 0) + 1) + Math.abs(Math.round(v));
var buf = buffers[cfg.files[k % cfg.files.length]];
if (!buf) return false; // not decoded yet, or failed: let synth cover it
var src = ctx.createBufferSource();
src.buffer = buf;
// A few percent off pitch, spread across a small range keyed to v, so a run of
// the same sound never reads as a loop.
if (cfg.vary) src.playbackRate.value = 1 + (((i % 5) - 2) * cfg.vary);
// A few percent off pitch, walking with the same cursor, so even a single-take
// sound like the shuffle isn't pitch-identical twice in a row.
if (cfg.vary) src.playbackRate.value = 1 + (((k % 5) - 2) * cfg.vary);
var g = ctx.createGain();
g.gain.value = cfg.gain == null ? 0.6 : cfg.gain;
src.connect(g).connect(master);