The felled body earns its fall: the burst waits for the collapse, the card waits for the burst

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-14 16:35:46 +02:00
1 parent 97b076c574
commit 108ed695eb
4 files changed
+117 -53

No files matched your search

+28 -1
View File
@@ -510,6 +510,8 @@ const oppOf = () => zombies.find(z => z.variant === 'r');
// freezes the world until tapped. Tests that play across a game boundary step through all of it,
// dismissing popups the way a player would.
const passMopBeat = () => { // ...stopping at the walk-on: past it, the next opponent PLAYS
// the KILL FALL plays first — the felled body is still on court, so oppOf() must not count it
for (let i = 0; i < 5 * 60 && G.matchFall; i++) { G.modal = null; G.modalQ.length = 0; update(DT); }
for (let i = 0; i < (TUNE.matchMopT + 8) * 60 && !oppOf(); i++) { G.modal = null; G.modalQ.length = 0; update(DT); }
};
@@ -538,7 +540,16 @@ test('a point won tears a wound stage off the body; only the game point fells it
}
assert.equal(zb.hp, 1, 'three points in: one stage left');
resolveInterlude('P'); // the game point
assert.ok(!zombies.includes(zb), 'the game-winning point IS the kill');
// THE KILL FALL: the game point fells the body but no longer bursts it on the same frame —
// the collapse plays, the prone hold holds, and only then the explosion (Oliver: "it would be
// better if the explosion would happen after the fall").
assert.ok(zombies.includes(zb), 'the game point drops the body — the burst waits for the fall');
assert.ok(G.matchFall && G.matchFall.kill, 'the kill fall is armed');
assert.ok(G.interlude && G.interlude.introT > 0, 'serving is barred while it plays');
for (let i = 0; i < (0.3 + TUNE.matchBoomHold) * 60 + 5 && zombies.includes(zb); i++) update(DT);
assert.ok(!zombies.includes(zb), 'the fallen body bursts after the prone hold');
assert.ok(!(G.match.mopT > 0), '...and the finish still waits for the smoke to be seen');
for (let i = 0; i < (TUNE.matchBoomCard + 0.5) * 60 && !(G.match.mopT > 0); i++) update(DT);
// THE MOP BEAT: the corpse's gore holds the court first — no fresh body until the boy has had
// his time (TUNE.matchMopT, skipped only on a clean court; the explode() above guarantees gore).
assert.ok(!oppOf(), 'no fresh opponent on the kill frame — the mop beat owns the court');
@@ -555,6 +566,18 @@ test('a point won tears a wound stage off the body; only the game point fells it
assert.deepEqual(score.pts, [0, 0], 'and the points reset');
});
test('the WOUNDS TREATED card lands after the explosion, never on top of it', () => {
startMatch();
const zb = oppOf();
G.hp = G.hpMax - 2; // something for the trainer to treat
for (let k = 0; k < 4; k++) resolveInterlude('P'); // a straight game
assert.ok(!G.modal, 'no card while the body is still falling');
for (let i = 0; i < (0.3 + TUNE.matchBoomHold) * 60 + 5 && zombies.includes(zb); i++) update(DT);
assert.ok(!zombies.includes(zb) && !G.modal, 'no card on the burst frame either');
for (let i = 0; i < (TUNE.matchBoomCard + 0.5) * 60 && !G.modal; i++) update(DT);
assert.ok(G.modal && G.modal.title === 'WOUNDS TREATED', 'the trainer card follows the burst');
});
test('their points draw blood from the MATCH pool, and running dry is the run', () => {
startMatch();
assert.equal(G.hp, TUNE.matchPlayerHp, 'the match brings its own, deeper pool');
@@ -829,6 +852,9 @@ test('THE TOURNAMENT: bands draw, bodies deepen, rounds hold their body, the cha
if (g < R.need)
assert.ok(zombies.includes(zb), R.name + ' game ' + g + ': the same body plays on');
}
// the round-winning game arms the KILL FALL: the body drops first, bursts after the hold
assert.ok(G.matchFall && G.matchFall.kill, R.name + ': the kill fall is armed');
for (let i = 0; i < 4 * 60 && G.matchFall; i++) { G.modal = null; G.modalQ.length = 0; update(DT); }
assert.ok(!zombies.includes(zb), R.name + ': the round-winning game is the kill');
if (round < CUP_ROUNDS.length - 1) passMopBeat(); // the boy, then the next draw walks on
}
@@ -1001,6 +1027,7 @@ test('a game won heals, so a clean player can go the distance', () => {
startMatch();
G.hp = 1;
for (let k = 0; k < 4; k++) resolveInterlude('P');
for (let i = 0; i < 5 * 60 && G.matchFall; i++) update(DT); // the heal waits for the burst now
assert.equal(G.hp, Math.min(G.hpMax, 1 + TUNE.matchHeal), 'the game paid its heal');
});