Nothing arrives unannounced: the horde gets its breather, the boss gets a name
THEY KEEP COMING is a 2.4s 'over' beat again (warnHorde in beats.js) instead of a flash raised on the same frame the wave lands, and the chapter boss walks on behind an overlay — his name in red over ENTERED THE COURT — held for 3s by the new self-dismissing modal clock (m.hold) rather than announced on a routine-set card. startHorde returns a bool: if the court has been taken when the warning expires the pendingHorde latch is re-armed rather than the set losing the horde it paid for. The warning ticks on its own clock, not on the 'over' beat, so a beat that moves the phase underneath it can't strand it (the mgT lock, again). Three tests, 15 total. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1f636530b9
commit
5ea84f8511
8 files changed
+132
-16
No files matched your search
+27
-6
@@ -94,8 +94,14 @@ export function startBossFight(chapter) {
|
||||
zombieDuck(true); // the music drops out for it
|
||||
voice('ump_boss_arrives', 0, 0.9); voice('sudden_death', 2.6, 0.85);
|
||||
const art = bossArt(chapter);
|
||||
G.card = { title: art.name, color: UI.bad, t: TUNE.setCardT };
|
||||
G.flash = null;
|
||||
// THE WALK-ON IS AN OVERLAY, not a band card. A card announced the biggest fight in the game in
|
||||
// the same slot and size as a routine set, and it read as the boss simply being there. This is on
|
||||
// the sanctioned modal list (a boss stage is guaranteed calm — the two lines above just cleared
|
||||
// it), and it HOLDS itself for bossIntroT rather than waiting for a tap: the point is to prepare
|
||||
// the player, and demanding a tap to start a fight he didn't ask for is the control steal the
|
||||
// popup policy exists to prevent. A tap still skips it.
|
||||
G.card = null; G.flash = null;
|
||||
showModal({ title: art.name, color: UI.bad, hold: TUNE.bossIntroT, body: ['ENTERED THE COURT'] });
|
||||
// (the four-line machine instruction modal is gone — people figure it out)
|
||||
}
|
||||
|
||||
@@ -163,7 +169,7 @@ export function watchdog(dt) {
|
||||
if ((stuckT += dt) < TUNE.stuckT) return;
|
||||
logSwing({ result: 'UNSTUCK', reason: `nothing moved for ${TUNE.stuckT}s — released a dead latch`,
|
||||
phase: G.phase, sig, aim: !!G.aim });
|
||||
G.horde = null; G.mgT = 0; G.changeover = false; G.interlude = false;
|
||||
G.horde = null; G.mgT = 0; G.changeover = false; G.interlude = false; G.hordeWarn = 0;
|
||||
G.aim = null; endBurst(); setupPoint();
|
||||
stuckT = 0; stuckSig = '';
|
||||
}
|
||||
@@ -200,9 +206,23 @@ export function resolveInterlude(side) {
|
||||
}
|
||||
}
|
||||
|
||||
// the swarm keeps coming: no serving, the racket auto-fires, the thumb drag steers you
|
||||
// THE BREATHER BEFORE THE WAVE. The horde used to begin on the same frame as its own announcement,
|
||||
// so the flash was already gone by the time the crowd was on you — "the horde is not announced
|
||||
// anymore". It is an ordinary 'over' beat (no serving, the world keeps walking, the RESUMING clock
|
||||
// counts it down under the flash), and startHorde still owns the transition itself.
|
||||
export function warnHorde() {
|
||||
if (G.horde || G.interlude || G.bossFight) return false; // same court-ownership contract as the rest
|
||||
G.hordeWarn = TUNE.hordeWarnT;
|
||||
G.flash = { text: 'THEY KEEP COMING', t: TUNE.hordeWarnT };
|
||||
ball.active = false; G.phase = 'over'; G.overT = TUNE.hordeWarnT;
|
||||
return true;
|
||||
}
|
||||
|
||||
// the swarm keeps coming: no serving, the racket auto-fires, the thumb drag steers you.
|
||||
// Returns false when another mode owns the court — the caller re-arms the latch rather than
|
||||
// silently eating the wave the set paid for.
|
||||
export function startHorde() {
|
||||
if (G.horde || G.interlude || G.bossFight) return; // same contract as startDuel: one mode owns the court
|
||||
if (G.horde || G.interlude || G.bossFight) return false; // same contract as startDuel: one mode owns the court
|
||||
G.changeover = false; // a cheat-forced horde mid-changeover must not leave spawns locked
|
||||
const grow = 1 + (G.sets[0] + G.sets[1]) * TUNE.hordeGrow; // each set banked makes the next wave worse
|
||||
// the gun grows WITH the wave: spawn rate and length both scale by grow, so a flat gun fell
|
||||
@@ -213,5 +233,6 @@ export function startHorde() {
|
||||
P.z = HOME.P.z; // brace at the baseline — the horde is a hold-the-line stand (finger steers x only)
|
||||
aside('ump_spectators', 0.4); // a horde IS the crowd on the court. (The near-identical 'remain seated'
|
||||
// that used to play right before it belongs to the transition, not here.)
|
||||
G.flash = { text: 'THEY KEEP COMING', t: 1.2 };
|
||||
G.flash = { text: 'THEY KEEP COMING', t: 1.2 }; // re-raised so the warning's line carries into the wave
|
||||
return true;
|
||||
}
|
||||
Reference in new issue
Block a user