Phase 4: bomb balls
Ball boys deliver a bomb ball every TUNE.bombEvery (8) splatter kills, up to TUNE.bombMax (3) in stock (G.bombs / G.killMeter in state). The next serve auto-consumes one (hitServe, splatter-only) — no extra input, keeping one-handed play. A bomb detonates on first zombie contact (regardless of pace) or, if it misses everyone, on its first bounce: explode() splatters everything within TUNE.bombRadius (2.5m), each kill feeding ball.kills so the Phase-2 fanfare escalates naturally, and leaves a crater — big stain, size-3 pile, 40 particles, 0.1s hitstop, boom(). New synth boom() in audio.js. Bomb ball renders black with a flickering orange fuse; the HUD shows stocked bombs as black/orange pips by the YOU line. Both explode sites set phase='over' so the serve loop doesn't stall on the killed ball. Alternative delivery ideas Oliver floated but not built: umpire throws it, or court pickup. Verified via window.T: 8th kill earns a bomb (meter resets); a serve consumes one and renders #111 black + orange fuse; a pow-0 bomb amid 4 clustered zombies kills all 4 with 'MASSACRE ×4', boom, hitstop 0.1, crater stain (r1.4) + size-4 pile; a bomb that misses still craters on first bounce; bombs never leak into tennis (mode guard); 3 HUD pips; no console errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
ad4c8041b9
commit
06025bdccf
6 files changed
+67
-5
No files matched your search
@@ -100,6 +100,22 @@ export function splat(vol = 0.7) { // wet burst — a zombie meets a first serve
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
export function boom(vol = 0.9) { // bomb ball — sub-bass thump under a filtered noise blast
|
||||
try {
|
||||
const ac = audio(), t = ac.currentTime;
|
||||
const o = ac.createOscillator(), og = ac.createGain(); // 80Hz sine diving to 30Hz
|
||||
o.frequency.setValueAtTime(80, t); o.frequency.exponentialRampToValueAtTime(30, t + 0.35);
|
||||
og.gain.setValueAtTime(vol, t); og.gain.exponentialRampToValueAtTime(0.001, t + 0.4);
|
||||
o.connect(og).connect(ac.destination); o.start(t); o.stop(t + 0.42);
|
||||
const src = ac.createBufferSource(); src.buffer = noiseBuf(ac, 0.4); // the blast
|
||||
const lp = ac.createBiquadFilter(); lp.type = 'lowpass';
|
||||
lp.frequency.setValueAtTime(2200, t); lp.frequency.exponentialRampToValueAtTime(200, t + 0.35);
|
||||
const g = ac.createGain();
|
||||
g.gain.setValueAtTime(vol * 0.8, t); g.gain.exponentialRampToValueAtTime(0.001, t + 0.4);
|
||||
src.connect(lp).connect(g).connect(ac.destination); src.start(t);
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
export function moan(vol = 0.1) { // distant undead ambience
|
||||
try {
|
||||
const ac = audio(), t = ac.currentTime, dur = 1.2 + Math.random() * 0.8;
|
||||
|
||||
Reference in new issue
Block a user