Story after Oliver's first test: metal, a straight prologue, 2x3 chapters

- The gore bed is "Dystopian Survival", made loopable: the last ten
  seconds cross-faded into the first ten, 203.5 s, as MP3 at 128k (the
  decoder strips the encoder's delay, so the seam has no gap). High-pass
  at 40 Hz instead of 800, level matched to the menu theme.
- The prologue no longer drops into gore for a moment: after the turn,
  act I's card and cup take over directly.
- RESET STORY under the build stamp (two taps; test only). It resets
  the story, league, cups and title, keeps the level, and pushes at once
  so the mirror cannot hand the old act back.
- The story screens have sound: a whoosh and a boom for the act card,
  applause and the rack's own click as each weapon lands, applause and
  a pock per arcade door on the act's end.
- Chapters are six games: mid-boss after three, the horde after four,
  the act boss after six.

v384, staging only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5.5 committed 2026-10-15 15:12:52 +02:00
1 parent 9e6d566c08
commit 16d0532f97
9 files changed
+119 -30

No files matched your search

+18 -9
View File
@@ -1020,7 +1020,7 @@ export const cupFixture = (lg, champion = 0) => {
// ---- THE STORY (2026-10-15, PROJECT.md "THE STORY"): five acts, one per league and court. An act is
// the league cup (zombie tennis), the transformation, and one ten-game gore chapter with a MID-BOSS
// at game 5 (the checkpoint) and the act's BOSS at game 10. Pure data; beats.js runs it.
// (STORY.midAt, the checkpoint) and the act's BOSS at its last game. Pure data; beats.js runs it.
// rounds the cup's rungs, in order. The FINAL is always the boss's own body (it grows into the
// boss), so acts II and IV play their band out of ladder order — cupRoundsFor is untouched.
// mid BOSS_ART index of the game-5 boss (the five designs one chapter per act leaves over)
@@ -1028,7 +1028,7 @@ export const cupFixture = (lg, champion = 0) => {
// pool weapons this act ADDS to the rack; the stock is every pool up to and including the act
// double the act's stock and restock pay two tins of each (league 5's double tins)
// arcade the arcade doors clearing this act opens (MODES ids, plus 'vs' for hosting a friend)
// The chapter plays the arcade's games base+1..base+10 (base = (act-1)*20), so every director curve
// The chapter plays the arcade's games base+1..base+length (base = (act-1)*20), so every director curve
// that reads G.runGames — spawn ramps, barrel gaps, machines, racket hp — keeps its tuned slope.
export const STORY = {
acts: [
@@ -1038,8 +1038,12 @@ export const STORY = {
{ lg: 4, rounds: [13, 15, 14], mid: 4, boss: 3, pool: ['flamer', 'rocket'], arcade: ['vs'] },
{ lg: 5, rounds: [16, 17, 18], mid: 7, boss: 9, pool: [], double: true, arcade: [] },
],
midAt: 5, // the mid-boss walks on after this many chapter games (and is the checkpoint)
length: 10, // games in a chapter; the act's boss walks on after the last
// Oliver on v383: "Gore scheint mir etwas zu lang — statt 2x5 lieber 2x3 Spiele". Six games, the
// mid-boss after the third, the act's boss after the sixth — and the horde after the fourth, between
// them (a chapter's 'set' is hordeAt games: at the arcade's six it would land on the boss and vanish).
midAt: 3, // the mid-boss walks on after this many chapter games (and is the checkpoint)
length: 6, // games in a chapter; the act's boss walks on after the last
hordeAt: 4, // the chapter's set: the horde comes after this many games
base: act => (act - 1) * 20,
};
export const ROMAN = ['', 'I', 'II', 'III', 'IV', 'V'];
@@ -1049,13 +1053,18 @@ export const ROMAN = ['', 'I', 'II', 'III', 'IV', 'V'];
export const STORY_HELP = { offerAt: 2, max: 2, run: 0.08, window: 0.12, heart: 1, slow: 0.25 };
// 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 };
export const STORE = { freeTier: false, arcadePremium: false, testReset: true }; // testReset: the splash's RESET STORY button
// 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 };
// sent "Dystopian Survival" (2026-10-15). metal-loop.mp3 is that track made loopable: 0.5-214 s with
// its last ten seconds cross-faded (equal power) into its first ten, so the seam is inside the fade
// and the loop is 203.5 s exactly. MP3 at Oliver's call; the encoder's delay and padding are carried
// in the LAME tag and the decoder strips them (measured in Chromium: 8974350 frames, no silence at
// either end), and `loopEnd` pins the source length anyway. `vol` sets it near the menu theme's level
// (it is mastered at -14 LUFS, the old bed at -8); `lowcut` 40 Hz keeps the guitars' body, where the
// old bed's 800 Hz high-pass would have thinned metal to a radio. The old bed: zombie-mode.m4a,
// 20.47077 s, 0.2, 800.
export const GORE_BED = { file: 'metal-loop.mp3', loopEnd: 203.5, vol: 0.5, lowcut: 40 };
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 => {