The fall beats the ball: a passing winner fells the body while it still rolls
Oliver: 'the zombie falls after the ball is lost although we know this earlier — it could play out as ball lost because of the falling.' The match point now resolves at bounce ONE when the winner is provably beyond the body's reach (closed-form flight vs its own chase dials, full swing reach plus 0.6 m of doubt — borderline balls wait for the ordinary bounce-2 call). startFall keeps the ball rolling for an early-decided point, and the d.decided latch swallows the rally's later resolutions so it can never bank twice. Verified in-pane: collapse starts ~0.3 s after the bounce, ball rolls past the prone body, score banks exactly once. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c6218a6284
commit
53df079db6
4 files changed
+64
-2
No files matched your search
+8
-1
@@ -357,6 +357,7 @@ export function startMatch() {
|
||||
function matchReady() {
|
||||
ball.active = false;
|
||||
G.serveNum = 1;
|
||||
if (G.match) G.match.decided = false; // the next rally may be decided early again
|
||||
setupPoint();
|
||||
const rz = zombies.find(z => z.variant === 'r');
|
||||
// THE OPPONENT STANDS WHERE THE POINT PUTS IT: on its receiving mark, or behind its own baseline
|
||||
@@ -404,7 +405,10 @@ export const FALL_LEN = fallLen(); // the load-time value test.js imports as a
|
||||
function startFall(from) {
|
||||
const fz = zombies.find(z => z.variant === 'r');
|
||||
G.matchFall = { t: 0, from, vx: (fz && fz.vx) || 0, x0: (fz && fz.x) || 0 };
|
||||
ball.active = false;
|
||||
// an EARLY-decided point keeps its ball: the whole reason the fall starts now is so the ball
|
||||
// visibly rolls past the collapsing body (gameplay's matchOutOfReach call). The rally cannot
|
||||
// resolve twice — the d.decided latch swallows every later resolution of it.
|
||||
if (!G.matchEarly) ball.active = false;
|
||||
G.phase = 'over'; G.overT = fallLen() + 0.6; // outlives the fall on purpose — see above
|
||||
}
|
||||
|
||||
@@ -642,6 +646,9 @@ export function resolveInterlude(side) {
|
||||
// interlude is permanent while a match runs (it is what keeps the serving tennis-like and the
|
||||
// spawn director quiet). Regicide arrives here too, as a 'P' with the body already gone.
|
||||
if (G.match) {
|
||||
// a rally resolved at its DECIDED moment (the early fall) is spent: the ball that kept rolling
|
||||
// for the picture still reaches its ordinary bounce-2 / rolled-out calls, and they land here
|
||||
if (G.match.decided) { ball.active = false; return; }
|
||||
if (!zombies.some(z => z.variant === 'r') && !G.match.ending) return matchGameWon(null);
|
||||
return matchPoint(side);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user