One file owns the court: beats.js, PROJECT.md (né HANDOFF), and tests gate the build

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-11 13:23:27 +02:00
1 parent 05930f0877
commit c122745390
11 files changed
+362 -231

No files matched your search

+5 -4
View File
@@ -6,7 +6,8 @@
import { TUNE, UI, COURT, CONTACT, BOSS_ART, bossArt, WEAPONS, BARREL_WEAPONS, woundStage, zombieH, EATEN_BURST, EATEN_SUMMARY, EATEN_FEED_R } from './config.js';
import { G, P, ball, sendBoy, setupPoint } from './state.js';
import { splat, moan, pock, thud, zap, voice, boom, swish, shriek, aww, sfx , whoosh, flame } from './audio.js';
import { splatPoint, cheer, spentBall, resolveInterlude, endBossFight, aside, grantWeapon, endBurst, sliceCarom } from './gameplay.js';
import { splatPoint, cheer, spentBall, aside, grantWeapon, endBurst, sliceCarom } from './gameplay.js';
import { resolveInterlude, endBossFight } from './beats.js'; // runtime-only cycle, like the gameplay one
import { tally, multiKill, endRun } from './stats.js';
export const zombies = []; // {x, z, v, walkT, variant: 'a'|'b'|'c'}
@@ -121,7 +122,7 @@ export function spawnMachine() {
// ---- the chapter boss. One every TUNE.bossEvery games, bigger and tougher each time. It advances
// slowly and volleys at you, so it threatens BOTH channels: its shots chip your hp, and letting it
// reach you is the ordinary instant death. Reuses the whole existing damage model — it is just a
// body with a lot of hp (see the wound() invariant in HANDOFF), so no weapon needs special-casing.
// body with a lot of hp (see the wound() invariant in PROJECT.md), so no weapon needs special-casing.
export function spawnChapterBoss(chapter) {
const art = bossArt(chapter); // which of the boss designs this chapter is (every tenth = THE CHAMPION)
// Every behaviour field on the design is OPTIONAL and falls back to the shared TUNE number, so a
@@ -332,7 +333,7 @@ function pickVariant() {
// The late roster, spread WIDE on purpose. They were at 3/8/15 and Oliver had met the whole cast
// by about game 10 — "let's introduce them more gradually". One new body roughly every seven
// games now, and the ORIGINAL four keep their gates exactly (the curve through ~game 12 was
// already tuned; see the calibration rule in HANDOFF).
// already tuned; see the calibration rule in PROJECT.md).
if (g >= 5) pool.push('e');
if (g >= 12) pool.push('h');
if (g >= 20) pool.push('k');
@@ -1125,7 +1126,7 @@ export function updateZombies(dt) {
ball.vx *= 0.2; ball.vz *= 0.2; ball.lastHit = 'X';
}
continue; // soft balls pass through ON PURPOSE, for racketAI to return — that rally IS the
// feedback that it wasn't hard enough, so no clank here; see HANDOFF
// feedback that it wasn't hard enough, so no clank here; see PROJECT.md
}
if (zb.variant === 's' && ball.vz < 0 && !(zb.shieldT > 0)) { // chair up, ball at its face: BLOCKED
zb.shieldT = TUNE.shieldDown; // ...but the block staggers it — the chair drops, kill window opens