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
@@ -761,6 +761,30 @@ test('a wounding point knocks the body down and it gets up MINUS the limb', () =
|
||||
assert.equal(G.serveNum, 1, 'matchReady ran: the next point is armed');
|
||||
});
|
||||
|
||||
test('the fall beats the ball: a passing winner fells the body while the ball 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." A winner that bounced in beyond the body's
|
||||
// provable reach resolves at bounce ONE: the collapse starts, the ball keeps rolling, and the
|
||||
// decided-latch makes sure the rally can never bank twice.
|
||||
startMatch();
|
||||
G.mode = 'splatter'; // the app's intoSplatter() precedes startMatch()
|
||||
G.interlude.introT = 0;
|
||||
const zb = oppOf();
|
||||
zb.hp = 3; // so THIS point crosses a wound stage (3 -> 2 is the arm)
|
||||
zb.x = -4; zb.z = 2; // parked far from where the winner lands
|
||||
ball.active = true; ball.lastHit = 'P'; ball.bounces = 0; ball.wid = 'ball'; ball.collected = false;
|
||||
ball.x = 3.4; ball.z = 7; ball.y = 0.5; ball.vx = 0.5; ball.vy = -2; ball.vz = -6; ball.g = TUNE.gravity;
|
||||
G.phase = 'rally'; G.wasServe = false;
|
||||
const pts = score.pts[0];
|
||||
for (let i = 0; i < 40 && !G.matchFall; i++) update(DT);
|
||||
assert.ok(G.matchFall, 'the collapse starts at the decided moment, not at the dead ball');
|
||||
assert.ok(ball.active, '...and the ball is still rolling past the falling body');
|
||||
assert.equal(score.pts[0], pts + 1, 'the point banked at the decision');
|
||||
for (let i = 0; i < 5 * 60; i++) update(DT); // the ball dies its ordinary death, the fall plays out
|
||||
assert.equal(score.pts[0], pts + 1, '...and never banked a second time');
|
||||
assert.ok(!G.match.decided, 'the latch cleared for the next rally');
|
||||
});
|
||||
|
||||
test('service alternates by game, and the opponent actually serves', () => {
|
||||
// Until now the corpse only ever returned: score.server was pinned to 'P' because the match calls
|
||||
// advancePoint directly and the flip lives in tennis's pointTo, which the match bypasses. Return
|
||||
|
||||
Reference in new issue
Block a user