diff --git a/PROJECT.md b/PROJECT.md index 563537d..88e8813 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -711,8 +711,12 @@ silently dropped, so `delay(k, more)` merges them. the free tier on, act I's card points at the full game. - The non-story summaries' fixture is the story now (door 'next' → `start('story')`). +**Phase 5, prepared:** the gore bed is one switch, `config.GORE_BED` = {file, loopEnd, vol}. It plays +in gore and in every story chapter from the transformation's run-off on, and is ducked everywhere else. +Metal is: drop the track into `assets/audio` and change those three values. + **Not done / open:** -- Phase 5 (the metal bed when Oliver has a track; Members' Night). +- Phase 5: Oliver's metal track, and Members' Night (the boss marathon, option C). - The tutorial and the ghost hand for the new double tap. - Stats: story kills are not banked into the career totals (no `endRun`). - The old cup mode is unreachable but still in MODES. diff --git a/index.html b/index.html index fef0482..25c4f5b 100644 --- a/index.html +++ b/index.html @@ -1195,6 +1195,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one' && !window.Capacitor?.isNat - + diff --git a/src/audio.js b/src/audio.js index bc84bcf..904358d 100644 --- a/src/audio.js +++ b/src/audio.js @@ -1,6 +1,7 @@ // All sound: WebAudio context, synthesized SFX, recorded umpire lines, speech fallback. import { crowd } from './state.js'; // the aww carries an applause trail, and the stand has to match it +import { GORE_BED } from './config.js'; // which track the gore bed is (metal, when Oliver has one) let AC = null; export const audio = () => { @@ -210,7 +211,7 @@ export const ambient = () => { // ...and the splatter bed, same treatment: 5.9 MB of 24-bit WAV down to 287 KB. Its master is // 982597 frames at 48 kHz, which is the un-round 20.47077 s below — an AAC loop needs the SOURCE // length as loopEnd, not the decoder's padded one. See the note on menuMusic. -export const zombieMusic = () => { loopBed('zombie-mode.m4a', 800, 0.2, 20.47077); zombieDuck(false); }; +export const zombieMusic = () => { loopBed(GORE_BED.file, 800, GORE_BED.vol, GORE_BED.loopEnd); zombieDuck(false); }; // Menu theme: every menu screen and the death summary. Barely highpassed — this one is MUSIC, not // an atmosphere bed, so the low end is the point. Started by the FIRST TAP (main.js `go`), never at // boot: a title screen that starts playing at you before you have touched it is rude, and on a phone @@ -244,7 +245,7 @@ function fadeBed(file, out, secs = 0.7) { b.g.gain.setValueAtTime(b.g.gain.value, t); // anchor at current level so a mid-fade toggle stays smooth b.g.gain.linearRampToValueAtTime(out ? 0 : b.vol, t + secs); } -export const zombieDuck = out => fadeBed('zombie-mode.m4a', out); // key must match loopBed's, filename and all +export const zombieDuck = out => fadeBed(GORE_BED.file, out); // key must match loopBed's, filename and all // The menu theme is never stopped, only faded — the buffer is already decoded, so fading it back up // on the death screen costs nothing and can't miss its cue waiting on a re-fetch. export const menuDuck = out => fadeBed('background-music.m4a', out); diff --git a/src/config.js b/src/config.js index 9fdef24..303e0df 100644 --- a/src/config.js +++ b/src/config.js @@ -1050,6 +1050,12 @@ export const STORY_HELP = { offerAt: 2, max: 2, run: 0.08, window: 0.12, heart: // THE STORE's switches (PROJECT.md "LATER — shipping it"). Both OFF until the unlock IAP exists: the // free tier ends on act I's card, and the arcade may become a premium door. premium() is the stub. export const STORE = { freeTier: false, arcadePremium: false }; +// THE GORE BED — the one track that plays while the dead are being killed (gore, every story chapter, +// from the transformation's run-off on), ducked everywhere else. Oliver wants METAL here (2026-10-15) +// and is finding the track: drop it into assets/audio and change these three values. `loopEnd` is the +// SOURCE length in seconds for an AAC file (its padded last packet clicks at the seam otherwise), 0 +// for a WAV; `vol` is the bed's level under the umpire. +export const GORE_BED = { file: 'zombie-mode.m4a', loopEnd: 20.47077, vol: 0.2 }; export const storyAct = act => STORY.acts[Math.max(1, Math.min(5, act)) - 1]; // the story cup: the league's own round names and needs, on the act's rung order export const storyRounds = act => {