The rush picks a lane and commits to it, and stops on a strike line

"They just overrun you without a chance" — two reasons, and the second one only
existed because of the first.

IT FOLLOWED YOU. The rush tracked your x every frame it was running, so the
sidestep was decoration: it simply came across and took you anyway. The lane is
LOCKED when the rush starts now. You get the whole 2.2 s run-up to leave it, and
what it runs down is that line, not you.

AND IT LANDED ON YOU. A rush that stopped at your feet left the boss standing
there afterwards, and nothing gets you out from under that — dodgeCool is 0.5 s
against a 0.28 s i-frame, so the second contact kills you while the first is
still fading. It stops on a STRIKE LINE 0.4 m short instead, reached on the same
frame the rush timer expires, so it turns and withdraws the instant it arrives.

The version of this that ran the rush THROUGH you and out the far side is why
bossStrike is a short stop and not an overrun: a body past the baseline is
deleted and scores a point, so the boss quietly despawned itself mid-fight and
left the fight running with nothing in it.

Measured, 45 s against the seller, the champion's third phase and the physio:
stand still and the first rush kills you (4.1 s); step out of the lane and you
survive all of it — seven or eight rushes, one sidestep each, the boss on court
the whole time and out at its line between them where you can hit it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-09 17:11:21 +02:00
1 parent d57433ed2b
commit 482c7cdce4
2 files changed
+25 -10

No files matched your search

+8
View File
@@ -152,6 +152,14 @@ export const TUNE = {
// from how far away it is (bossAI), so the run-up reads the same every time and // from how far away it is (bossAI), so the run-up reads the same every time and
// is long enough to see coming. Was 1.5 s at a flat 4.2 m/s, which only reached // is long enough to see coming. Was 1.5 s at a flat 4.2 m/s, which only reached
// you at all because the boss crept nearer every cycle until it was on top of you. // you at all because the boss crept nearer every cycle until it was on top of you.
bossStrike: 0.4, // m SHORT of you the rush stops — its STRIKE LINE, reached exactly as the rush
// timer runs out, so the boss turns and withdraws on the same frame it arrives.
// A rush that stopped ON you left it standing there afterwards, and no amount of
// sidestepping gets you out from under that (dodgeCool 0.5 s vs a 0.28 s
// i-frame). It cannot run PAST you either: a body over the baseline is deleted
// and scores a point (see updateZombies), which quietly despawned the boss
// mid-fight the first time this ran through. At 0.4 m the contact test still
// reaches about 0.85 m either side of the lane, and the sidestep covers 1.9.
bossChargeRest: 2.2, // s of held ground between charges — the window you get to hit it bossChargeRest: 2.2, // s of held ground between charges — the window you get to hit it
bossHopT: 0.55, // s per mascot hop (it moves in arcs, not in a slide) bossHopT: 0.55, // s per mascot hop (it moves in arcs, not in a slide)
bossHopX: 2.0, // m of ground one hop covers (the eased arc lands it at about 2/pi of this) bossHopX: 2.0, // m of ground one hop covers (the eased arc lands it at about 2/pi of this)
+17 -10
View File
@@ -174,10 +174,16 @@ function bossAI(zb, dt) {
zb.moveT -= dt; zb.moveT -= dt;
const v = TUNE.bossChargeV * (A.speedMul ?? 1); const v = TUNE.bossChargeV * (A.speedMul ?? 1);
if (zb.charging) { if (zb.charging) {
const dx = P.x - zb.x; // IT COMMITTED TO A LANE AND IT IS RUNNING DOWN THAT LANE, whatever you do next. Tracking your
zb.x += Math.sign(dx) * Math.min(Math.abs(dx), TUNE.bossStrafe * dt); // it tracks your lane SLOWLY: a sidestep beats it // x for the whole rush made the sidestep meaningless — it just followed you across and ran you
zb.z = Math.min(P.z, zb.z + zb.chargeV * dt); // AT you and NO FURTHER: unclamped it ran // over ("they overrun you without a chance"), and stopping the rush ON you left it standing
if (zb.moveT <= 0) { zb.charging = false; zb.moveT = TUNE.bossChargeRest; } // off the back of the court // there afterwards, where the 0.5 s dodge cooldown cannot outrun a 0.28 s i-frame. So: the
// lane is locked when the rush begins, you get the entire run-up to leave it, and the rush
// ends on a STRIKE LINE just in front of you, on the same frame the rush timer expires, so it
// turns and withdraws the instant it arrives. A rush that misses is over.
zb.x += Math.sign(zb.lane - zb.x) * Math.min(Math.abs(zb.lane - zb.x), TUNE.bossStrafe * 2 * dt);
zb.z += zb.chargeV * dt;
if (zb.moveT <= 0) { zb.charging = false; zb.moveT = TUNE.bossChargeRest; } // spent: it turns and walks back
} else { } else {
// AND IT FALLS BACK. A rush that ended where it landed left the boss parked on top of you for // AND IT FALLS BACK. A rush that ended where it landed left the boss parked on top of you for
// the rest of the fight: dodge or die every two seconds, with no window to hit it and nothing // the rest of the fight: dodge or die every two seconds, with no window to hit it and nothing
@@ -187,13 +193,14 @@ function bossAI(zb, dt) {
// PASSES: you dodge it, and the ground it gives up again is your turn to hit it. // PASSES: you dodge it, and the ground it gives up again is your turn to hit it.
const home = TUNE.bossZMax, sp = v * (A.retreat ?? 1.3) * dt; // toward its LINE, from either side: const home = TUNE.bossZMax, sp = v * (A.retreat ?? 1.3) * dt; // toward its LINE, from either side:
zb.z += Math.sign(home - zb.z) * Math.min(Math.abs(home - zb.z), sp); // backward off a rush, forward on the way in zb.z += Math.sign(home - zb.z) * Math.min(Math.abs(home - zb.z), sp); // backward off a rush, forward on the way in
// THE RUSH IS TIMED, NOT PACED. Whatever ground the retreat left between you, it covers it in // THE RUSH IS TIMED, NOT PACED, and it always begins FROM THE LINE — never from wherever a
// bossChargeT — so every charge arrives, and every charge takes the same readable run-up to do // half-finished retreat happened to leave it, which is how a rush could start on your toes
// it. Paced at a flat speed instead, the same fix that stopped it camping on you also stopped // with no run-up to read. It covers the whole distance in bossChargeT, so the approach looks
// it ever reaching you: it started each rush from the band and ran out of clock 8 m short. // the same every time whatever the design's speed.
if (zb.moveT <= 0) { if (zb.moveT <= 0 && Math.abs(zb.z - home) < 0.1) {
zb.charging = true; zb.moveT = TUNE.bossChargeT; zb.charging = true; zb.moveT = TUNE.bossChargeT;
zb.chargeV = Math.max(v, (P.z - zb.z) / TUNE.bossChargeT); zb.lane = P.x; // the lane, locked. You have the run-up to leave it.
zb.chargeV = (P.z - TUNE.bossStrike - zb.z) / TUNE.bossChargeT;
} }
} }
} else if (style === 'shadow') { } else if (style === 'shadow') {