Serve reads, the mop beat, the head-icon card, and Daniel benched

Oliver's batch, the code half:
- The tennis serve lost its ghost fingers (idle tap demo + apex rider deleted
  with their counters; the rally-return hint survives).
- The toss rises over the tossing HAND, measured off the toss frames' raised
  fingertips (TOSS.dx/oDx, women's tour override) — not over the head.
- An uncaught toss returns to the bounce stance instead of flashing the smash
  frame the player never earned.
- Between match games the court belongs to the mop boy: 5 s (TUNE.matchMopT),
  full job allowance, a hurry speed between stains, and the far-court reach
  limit lifted for the match (his stains are exactly where opponents die).
  The next opponent walks on only after — d.ending guards the empty court.
- The walk-on card wears the opponent's own head: config.HEAD_CROP, straight
  crops measured off the family sheets, no new art.
- The match intro is DEATH MATCH, counting the real blood pool.
- Daniel is benched: every ump_* personality line routes through aside()'s
  early return; officiating calls keep speaking. Personality pass later.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-13 17:10:45 +02:00
1 parent 0e01894f4d
commit c084d53497
8 files changed
+141 -70

No files matched your search

+17 -2
View File
@@ -505,6 +505,9 @@ test('endDrill hands the court back', () => {
const startMatch = () => { const r = _startMatch(); G.modal = null; G.modalQ.length = 0; return r; };
const oppOf = () => zombies.find(z => z.variant === 'r');
// a won game now opens the MOP BEAT (TUNE.matchMopT) before the next opponent walks on —
// tests that play across a game boundary step through it here
const passMopBeat = () => { for (let i = 0; i < (TUNE.matchMopT + 1) * 60; i++) update(DT); };
test('a match owns the court like every other mode', () => {
assert.ok(startMatch(), 'a match starts on a clean court');
@@ -532,8 +535,14 @@ 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 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');
assert.ok(G.match.mopT > 0, 'the beat is armed');
assert.ok(G.interlude && G.interlude.introT > 0, 'and serving is barred for its whole length');
for (let i = 0; i < (TUNE.matchMopT + 1) * 10; i++) update(0.1);
const next = oppOf();
assert.ok(next && next !== zb, 'a fresh opponent shambled on');
assert.ok(next && next !== zb, 'a fresh opponent shambled on after the mop beat');
assert.equal(next.hpMax, TUNE.matchOppHp, 'same flat pool — the ladder escalates in speed/flub, not hp');
assert.equal(G.runGames, 1, 'the ladder banked the game');
assert.deepEqual(score.pts, [0, 0], 'and the points reset');
@@ -753,6 +762,7 @@ test('service alternates by game, and the opponent actually serves', () => {
for (let i = 0; i < 2 * 60 && G.matchFall; i++) update(DT); // let each collapse finish
}
assert.equal(score.server, 'O', 'their serve next game');
for (let i = 0; i < (TUNE.matchMopT + 1) * 60; i++) update(DT); // the mop beat holds the court first
// the opponent stands behind ITS baseline to serve, not on the receiving mark
const rz = zombies.find(z => z.variant === 'r');
assert.ok(rz.z < 1.0, 'it steps back to serve: z=' + rz.z.toFixed(2));
@@ -879,13 +889,17 @@ test('regicide: killing the body mid-game wins the game outright', () => {
resolveInterlude('P'); // killZombie's exit lands here with the court empty
assert.equal(score.games[0], games + 1, 'the game banks');
assert.deepEqual(score.pts, [0, 0], 'the part-played points reset');
passMopBeat();
assert.ok(oppOf(), 'and the next opponent is on');
});
test('the match measures OPPONENTS FELLED, banked as its own kind of run', () => {
localStorage.removeItem('splatterRuns');
startMatch();
for (let k = 0; k < 8; k++) resolveInterlude('P'); // two games = two bodies down
for (let g = 0; g < 2; g++) { // two games = two bodies down
for (let k = 0; k < 4; k++) resolveInterlude('P');
passMopBeat(); // each corpse gets its mop beat
}
assert.equal(G.match.opp, 3, 'on the third rung');
G.hp = 1; resolveInterlude('O');
assert.equal(G.mode, 'eaten', 'bled out');
@@ -894,6 +908,7 @@ test('the match measures OPPONENTS FELLED, banked as its own kind of run', () =>
// a shorter second run must NOT overwrite the mark, and must know what it is chasing
G.mode = 'splatter'; endMatch(); startMatch();
for (let k = 0; k < 4; k++) resolveInterlude('P');
passMopBeat(); // no points settle while the boy owns the court
G.hp = 1; resolveInterlude('O');
assert.equal(G.match.felled, 1);
assert.equal(G.match.best, 2, 'the mark to beat is shown');