Three more chapters, and at game 100 the champion returns everything

The roster ran out at chapter 6 and then repeated itself; game 70 got the
groundskeeper again, bigger. bossArt() now walks nine designs, one per chapter,
and hands out THE CHAMPION on every tenth — the floor(ch/10) term takes his
chapters back out of the count so chapter 11 opens the second lap on the
groundskeeper rather than skipping him.

  70 THE STRINGER — leather apron, bandoliers of gut string, racket frames on
     his back. He takes your lane and puts two dead-straight ones on it.
  80 THE TOURNAMENT DIRECTOR — blazer, lanyard, radio. `adds` is new: he calls
     stewards onto the court while you fight him, which is the one thing a boss
     fight could not do before, since the stage is cleared for it.
  90 THE STRAWBERRY SELLER — tray of cream, apron soaked red, ladle. A tank
     that sprays four at once and then comes for you.
 100 THE CHAMPION — torn whites, a laurel of strapping tape, the trophy still
     in one fist. `rally`: anything under splatter quality comes straight back
     at your head, so he is the one body you have to out-HIT rather than
     out-shoot. `phases` lays three rows over his own as his health falls — he
     holds the baseline and rallies, then runs the line drilling volleys, then
     drops to a charge — and each turn is an event: cry, shake, the stand up.

Thirty-six frames through assets/_regen.py, the mascot's three-batch recipe
(2 stride, 3 wounds, 4 throws). The trap that cost the re-rolls: a throw batch
references its own wound frames, so a wounds batch that fails leaves the throw
sheet drawn against the SEED — two of the four came back half groundskeeper.
Wounds first, verify, then throws.

And the physio, who Oliver reported "does basically nothing but move towards
the net": he kept 4.2 of every 6.3 m he charged, so the fight was always over
before he arrived. `withdraw` is his own number now (0.2) and he charges half
again as fast — measured, he is on top of you in 11 s instead of never — and he
drills three balls a cycle on the way in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-09 16:08:57 +02:00
1 parent f4c6f408e7
commit 04ee0174ce
42 files changed
+353 -72

No files matched your search

+52 -4
View File
@@ -500,15 +500,62 @@ export const BOSS_ART = [
{ name: 'THE PHYSIO', walk: ['z_physio1', 'z_physio2'],
wounds: ['z_physiow1', 'z_physiow2', 'z_physiow3'],
throws: ['z_physiot0', 'z_physiot1', 'z_physiot2', 'z_physiot3'],
move: 'charge', hpMul: 0.8, // fast and fragile: the BODY is the weapon, and contact
volley: 1, cycle: 3.4, wind: 0.35, shot: 21, spread: 0.15 }, // is still instant death
// THE BODY IS THE WEAPON — which it never once was. One ball every 3.4 s and a withdrawal that
// gave back 4.2 of every 6.3 m it charged meant the fight was over long before it arrived:
// Oliver, "the physio does basically nothing but move towards the net, too easy". It now keeps
// three quarters of every rush (withdraw) and covers the ground half again as fast (speedMul),
// so it is on top of you inside two charges, and it drills you on the way in.
move: 'charge', hpMul: 0.9, speedMul: 1.45, withdraw: 0.2,
volley: 3, gap: 0.26, cycle: 2.4, wind: 0.3, shot: 22, spread: 0.5 },
{ name: 'THE SPONSOR MASCOT', walk: ['z_mascot1', 'z_mascot2'],
wounds: ['z_mascotw1', 'z_mascotw2', 'z_mascotw3'],
throws: ['z_mascott0', 'z_mascott1', 'z_mascott2', 'z_mascott3'],
move: 'bounce', hpMul: 1.3, speedMul: 0.6, // soaks, and answers with a WALL: five at once, so
volley: 5, gap: 0, spread: 1.6, cycle: 3.2, wind: 0.65, shot: 14 }, // there is nothing to parry
{ name: 'THE STRINGER', walk: ['z_string1', 'z_string2'],
wounds: ['z_stringw1', 'z_stringw2', 'z_stringw3'],
throws: ['z_stringt0', 'z_stringt1', 'z_stringt2', 'z_stringt3'],
move: 'shadow', paceMul: 1.6, hpMul: 1.1, // he never mis-strings: he takes your lane and puts
volley: 2, gap: 0.28, spread: 0.1, cycle: 2.1, wind: 0.34, shot: 23 }, // two dead-straight ones on it
{ name: 'THE TOURNAMENT DIRECTOR', walk: ['z_dir1', 'z_dir2'],
wounds: ['z_dirw1', 'z_dirw2', 'z_dirw3'],
throws: ['z_dirt0', 'z_dirt1', 'z_dirt2', 'z_dirt3'],
move: 'surge', hpMul: 1.2, // the one fight you cannot stand still in: he has a
volley: 2, gap: 0.4, spread: 0.7, cycle: 3.0, wind: 0.5, // radio, and he calls security with it
line: 'HE IS CALLING SECURITY.', adds: { every: 6, variant: 'd', cap: 5 } },
{ name: 'THE STRAWBERRY SELLER', walk: ['z_berry1', 'z_berry2'],
wounds: ['z_berryw1', 'z_berryw2', 'z_berryw3'],
throws: ['z_berryt0', 'z_berryt1', 'z_berryt2', 'z_berryt3'],
move: 'charge', hpMul: 1.4, speedMul: 1.15, withdraw: 0.3, // a tray of cream and a ladle: he
volley: 4, gap: 0.12, spread: 1.4, cycle: 3.0, wind: 0.55, shot: 15 }, // sprays, then comes for you
// THE FINAL BOSS, and the only row that is not in the chapter rotation: bossArt() hands him out
// every TENTH chapter (game 100, then 200). Two things make him the wall the run ends on.
// `rally` he RETURNS anything that is not a splatter-quality kill shot — see the boss branch in
// zombies.js. Every other body in the game can be worn down with soft balls; he is the
// one you have to out-HIT, and a lazy serve comes straight back at your head.
// `phases` three rows that override this one as his health falls (bossPlan), each with its own
// cry. He holds the baseline and rallies, then runs the line drilling volleys, then
// drops the trophy and comes for you. Same body, three fights.
{ name: 'THE CHAMPION', walk: ['z_champ1', 'z_champ2'],
wounds: ['z_champw1', 'z_champw2', 'z_champw3'],
throws: ['z_champt0', 'z_champt1', 'z_champt2', 'z_champt3'],
rally: true, hpMul: 1.6, line: 'HE RETURNS EVERYTHING.',
phases: [
{ move: 'pace', paceMul: 0.9, volley: 1, cycle: 3.2, wind: 0.6, shot: 17, spread: 0.3 },
{ move: 'shadow', paceMul: 1.8, volley: 4, gap: 0.22, cycle: 2.2, wind: 0.4, shot: 20, spread: 0.5,
cry: 'SECOND SET.' },
{ move: 'charge', speedMul: 1.5, withdraw: 0.15, volley: 3, gap: 0.1, cycle: 1.9, wind: 0.3,
shot: 24, spread: 1.1, cry: 'MATCH POINT.' },
] },
];
// WHICH design a chapter gets. The roster is walked one design per chapter, and every TENTH chapter
// is THE CHAMPION — last in the array and never part of the rotation. The floor(ch/10) term takes
// his chapters back out of the count, so chapter 11 opens the second lap on the groundskeeper
// instead of skipping him.
export const bossArt = ch => ch % 10 === 0 ? BOSS_ART[BOSS_ART.length - 1]
: BOSS_ART[(ch - 1 - Math.floor(ch / 10)) % (BOSS_ART.length - 1)];
// The roster, as the BODY COUNT screen reads it. Until now a variant's NAME lived only in the
// comment above its hp value — fine while nothing ever had to print one. The key is the `variant`
// char the zombie already carries, except that 'a' and 'b' fold together (they are one shambler in
@@ -533,8 +580,9 @@ export const ENEMIES = {
r: { name: 'THE CLUB PRO', spr: 'z_c1', sub: 'STILL HAS A BACKHAND' },
m: { name: 'THE BALL MACHINE', spr: 'z_mach1', sub: 'FIRES BACK · FURNITURE' },
};
for (let i = 0; i < BOSS_ART.length; i++) // the six chapter bosses, named where they already are
ENEMIES['B' + i] = { name: BOSS_ART[i].name, spr: BOSS_ART[i].walk[0], sub: 'CHAPTER BOSS', boss: true };
for (let i = 0; i < BOSS_ART.length; i++) // the chapter bosses, named where they already are
ENEMIES['B' + i] = { name: BOSS_ART[i].name, spr: BOSS_ART[i].walk[0], boss: true,
sub: i === BOSS_ART.length - 1 ? 'THE FINAL · EVERY TENTH CHAPTER' : 'CHAPTER BOSS' };
// Which wound stage a body is in: 0 = unhurt, 1..n = the skins, spread evenly over its full health.
// This is the rule the chapter boss already used, and it turns out to reproduce the brute's old