Put a steward on the court

A new ordinary body between the shambler and the brute: a stadium security
steward in a hi-vis vest, 2.15 m, three hp, a touch slower. Deliberately the
mildest thing in the advanced pool — no wound skins, no special rule to learn —
so he can unlock early (game 4) and thicken the ordinary crowd, which a mix of
two shamblers never did. A fifth of horde spawns come out as stewards too, so a
grown wave is not one body dying at one range.

Sheet generated against the existing shambler walk (assets/_sheet_dwalk.png),
sliced to a 4-frame cycle, so he shambles as smoothly as the crowd he walks in.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-07-31 18:57:03 +02:00
1 parent 7803c83564
commit d62478e8b1
9 files changed
+19 -7

No files matched your search

+11 -3
View File
@@ -33,8 +33,9 @@ export function spawnZombie(x, z, variant) {
if (variant === 'f') { zb.v = Math.min(TUNE.runnerVMax, zb.v * TUNE.runnerSpeed); shriek(); } // shriek = the fairness contract; capped so late runners stay reactable
if (variant === 'a' || variant === 'b') zb.hp = TUNE.shamblerHp; // classic shamblers take a wound before they drop — only the runner is one-shot glass
if (variant === 'g') { zb.v *= 0.55; zb.hp = TUNE.bruteHp; } // slow, but soaks hits
if (variant === 'd') { zb.v *= 0.8; zb.hp = TUNE.stewardHp; } // the steward: a head taller and a hit tougher than the crowd, nothing more exotic than that
if (variant === 's') zb.shieldT = 0; // chair up; > 0 = seconds of stagger left
if ('fgs'.includes(variant) && !G.seen['v' + variant]) { G.seen['v' + variant] = true; aside('ump_dress_code', 0.8); }
if ('fgsd'.includes(variant) && !G.seen['v' + variant]) { G.seen['v' + variant] = true; aside('ump_dress_code', 0.8); }
if (variant === 'm') { // the rogue ball machine: furniture, not a body. It does not walk at all.
zb.v = 0; zb.hp = TUNE.machHp; zb.fireT = TUNE.machCycle; zb.windT = 0;
}
@@ -130,6 +131,10 @@ function pickVariant() {
if (g >= 2 && !zombies.some(z => z.variant === 'f')) pool.push('f'); // at most one runner alive
if (g >= 6) pool.push('g');
if (g >= 12) pool.push('s');
// The steward is the mildest of the pool on purpose — bigger and one hit tougher, no special rule
// to learn — so he can arrive EARLY and thicken the ordinary crowd, which the shambler mix alone
// never did. He is also the only advanced body that shows up in a horde (see spawnHordeZombie).
if (g >= 4) pool.push('d');
const nasty = Math.min(TUNE.nastyMax, g * TUNE.nastyPerGame);
if (pool.length && Math.random() < nasty) return pool[Math.floor(Math.random() * pool.length)];
return Math.random() < 0.5 ? 'a' : 'b';
@@ -140,7 +145,7 @@ function pickVariant() {
// A BOSS BODY: takes wound damage from everything instead of being deleted by it. The brute ('g') is
// deliberately NOT one — melee/bomb/fire one-shotting a brute is a settled "power exception".
const isBoss = zb => zb.variant === 'r' || zb.variant === 'B';
const hitR = zb => zb.variant === 'g' ? 0.75 : zb.variant === 'r' ? 0.7
const hitR = zb => zb.variant === 'g' ? 0.75 : zb.variant === 'd' ? 0.64 : zb.variant === 'r' ? 0.7
: zb.variant === 'B' ? 0.55 * (zb.scale || 1) * 1.4 // a chapter boss is a barn door, as it should be
: zb.variant === 'm' ? 0.8 // the machine is a wide low box
: 0.55;
@@ -302,7 +307,10 @@ function splatter(i, fanfare = true) { // the MAIN ball hit a zombie
// ---- horde wave: dense spawns + an auto-firing machine-gun racket (pellets, not the match ball)
function spawnHordeZombie() { // all from the far baseline, across the court — never off-screen at the sides
spawnZombie((Math.random() * 2 - 1) * 5, -1.5 - Math.random(), Math.random() < 0.5 ? 'a' : 'b');
// a few stewards in the mix: the wave needs bodies that survive one pellet, or a grown horde is a
// uniform wall of the same thing dying at the same range
const v = Math.random() < TUNE.hordeSteward && G.runGames >= 4 ? 'd' : Math.random() < 0.5 ? 'a' : 'b';
spawnZombie((Math.random() * 2 - 1) * 5, -1.5 - Math.random(), v);
}
let mgTick = 0;
function firePellet() {