The toss is the server's ball — the real death loop

"still when I lost a single point and try to serve again every further tap
does hurt me more until I am dead". Reproduced tap-for-tap this time, and the
serveNum fix was a red herring — a real bug, but not this one.

tossBall never stamped ball.lastHit. After any rally the zombie touched last,
the freshly tossed ball still said lastHit 'O' — and the previous pass moved
the server behind the baseline, past the passing-shot line. So the moment the
toss went up, the game saw an opponent ball beyond your baseline: "its
passing shot ends the duel", point to them, blood. Every tap = a toss = an
instant lost point, faster the more he panicked. Two fixes composed into a
death loop; the stale field was harmless while serving happened inside the
court.

One stamp in tossBall — ball.lastHit = sv.side — fixes every caller. The duel
and the gore mode carried the same latent bug for anyone tossing deep behind
the line after a racket-zombie touch.

Verified with the exact field shape: lose a rally the zombie touched last,
then eight badly-timed panic taps — one honestly lost point, zero
toss-bleeds. Before the fix every single toss bled.

41 tests, 15 seeds clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-11 22:54:13 +02:00
1 parent f67411e330
commit 24be613c61
4 files changed
+52 -2

No files matched your search

+9
View File
@@ -192,6 +192,15 @@ function tossBall(sv) {
// keeps the reach he was drawn with.
ball.x = sv.x + (sv.side === 'P' ? 0.12 : -0.35); ball.z = sv.z; ball.y = TOSS.y0;
ball.vx = 0; ball.vz = 0; ball.vy = TOSS.vy; ball.g = TUNE.gravity;
// THE TOSS IS THE SERVER'S BALL. lastHit was left stale from the previous rally, and if the
// OPPONENT had touched it last, the freshly tossed ball read as *their* ball in flight — which
// armed every rule that judges an opponent ball, most fatally the passing-shot test: the match
// serves from BEHIND the baseline (z 24.3 > the test's 24.07), so the moment you tossed after any
// rally they had touched, "its passing shot ends the duel" fired, the point went to them and drew
// blood. That was Oliver's death loop: every tap = a toss = an instant lost point, faster the
// more he panicked. One stamp here fixes every caller — the duel and the gore mode carried the
// same latent bug for anyone tossing from deep behind the line.
ball.lastHit = sv.side;
G.phase = 'toss';
}
export function serveApexY() { return TOSS.y0 + TOSS.vy * TOSS.vy / (2 * TUNE.gravity); } // ~2.95