Give the menus a theme

background-music.wav loops under the title screen and the court picker, ducks
out when a court is picked, and fades back in at the death summary — the end
screen is a menu too, so the zombie bed ducks out as this one comes in.

It starts at BOOT, not on the first tap, so the title screen already has music.
That needed loopBed to call audio() before its already-started early return: the
theme plays into a context the autoplay policy holds suspended, and the call
that wakes it is a second menuMusic() from the first tap, which used to return
at the beds.has guard without reaching AC.resume().

fadeBed also now records a duck for a bed that is still loading, and loopBed
starts that bed silent. An 8 MB file ducked mid-fetch would otherwise come up at
full level, seconds later, over the match. Same latent bug applied to zombieDuck.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-01 10:42:10 +02:00
1 parent fa30fb94c4
commit 60a74f6a99
6 files changed
+51 -8

No files matched your search

+8 -2
View File
@@ -7,7 +7,7 @@ import { TUNE, COURT, COURTS, UMP_H, UMP_X, EATEN_RESTART } from './config.js';
import { P, O, ball, score, G, setupPoint, selectCourt } from './state.js';
import { update, keyPressed, solveShot, inServeBox, inCourt, forceZombies, splatPoint, deliverTin, selectWeapon, showModal, dismissModal, startDuel, resolveInterlude, flickTarget, startBossFight, endBossFight } from './gameplay.js';
import { fitViewport, render, proj, RES } from './render.js';
import { voice, ambient } from './audio.js';
import { voice, ambient, menuMusic, menuDuck } from './audio.js';
import { zombies, spawnZombie, spawnRacketZombie, spawnMachine, spawnChapterBoss, piles, gore, parts, pellets, extras, shots, explode, hurtPlayer, killPlayer } from './zombies.js';
import { cv } from './dom.js';
import './input.js';
@@ -52,10 +52,16 @@ for (const c of COURTS) {
b.addEventListener('pointerdown', e => { e.preventDefault(); e.stopPropagation(); start(c.id); });
courts.appendChild(b);
}
function openPicker() { if (intro.parentNode) intro.classList.add('picking'); }
menuMusic(); // the title screen has a theme. Silent until the first tap on a phone — see loopBed.
function openPicker() {
if (!intro.parentNode) return;
intro.classList.add('picking');
menuMusic(); // ...and this call is the one that resumes it: the tap IS the gesture
}
function start(id) {
if (!intro.parentNode) return;
selectCourt(id);
menuDuck(true); // the theme belongs to the menus — hand the court over to the ambience
intro.remove(); ambient(); voice('quiet_please'); // low atmosphere bed starts with the first gesture
}
// a tap anywhere on the splash opens the picker; on the picker itself only the buttons act, so a