Three modes, picked up front — and CLASSIC TENNIS is one of them
The setup screen picks WHAT before WHO and WHERE. Three rows out of a table, in the court row's own markup: CLASSIC TENNIS is not the opener with the turn switched off and left there — it is the mode the other two are built on, so it gets real match scoring: six games with a two-game lead is a set, best of TUNE.classicSets is the match, the board shows the sets column (hidden until now outside splatter) and the bar under it says which set this is. The CPU stops rubber-banding and flubs less: aiFlubLoss exists to get a new player PAST the opener, and a mode whose whole promise is a real match cannot be won by an opponent that falls apart the moment he goes ahead. No tiebreak — 6-6 plays on to 8-6. Marked ponytail. That needed a second ENDING, the one with no corpse in it: G.done, the same summary and gated tap as the death screen, its own words (GAME, SET, MATCH. / BEATEN.) and the man still standing. The three endings now share one block rather than three copies of it. Caught by the first classic test: stats banked only the FIRST classic run of a session. `banked` was cleared by the next KILL, and a classic match has no kills in it at all — every later match was swallowed by the double-bank guard. beginRun() is the honest reset and restartRun calls it. ZOMBIE TENNIS from the menu is one line; the back room keeps its door. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66fdad3d74
commit
c89d4a3ff9
31 files changed
+472
-123
No files matched your search
@@ -82,7 +82,7 @@ globalThis.getComputedStyle = () => ({ paddingTop: '0' });
|
||||
|
||||
// ---- game modules (real code) ----------------------------------------------
|
||||
const { G, P, O, score, ball, setupPoint } = await import('./src/state.js');
|
||||
const { update, forceZombies, splatPoint, restartRun, flickTarget, keyPressed } = await import('./src/gameplay.js');
|
||||
const { update, forceZombies, splatPoint, restartRun, flickTarget, keyPressed, classicGame } = await import('./src/gameplay.js');
|
||||
const { startDuel, resolveInterlude, startBossFight, endBossFight, startDrill: _startDrill, endDrill, startMatch: _startMatch, endMatch } = await import('./src/beats.js');
|
||||
// the drill's first-sight modal freezes the world (that is what modals do) — dismiss it the way a
|
||||
// player would, so the tests step a live court
|
||||
@@ -776,6 +776,44 @@ test('the match measures OPPONENTS FELLED, banked as its own kind of run', () =>
|
||||
G.mode = 'splatter'; endMatch();
|
||||
});
|
||||
|
||||
// ---- classic tennis: the mode where nothing turns ---------------------------------------------
|
||||
|
||||
test('classic tennis: a game is not a set, and six with a two-game lead is', () => {
|
||||
G.mode = 'tennis'; G.classic = true; G.everTransitioned = false;
|
||||
score.games = [5, 4];
|
||||
assert.equal(classicGame('P'), false, '5-4 is not a set — advancePoint has not banked the sixth yet');
|
||||
score.games = [6, 5];
|
||||
assert.equal(classicGame('P'), false, 'nor is 6-5: the two-game lead is the rule');
|
||||
assert.deepEqual(G.sets, [0, 0]);
|
||||
score.games = [7, 5];
|
||||
assert.equal(classicGame('P'), false, '7-5 IS a set, but not the match');
|
||||
assert.deepEqual(G.sets, [1, 0], 'the set is banked');
|
||||
assert.deepEqual(score.games, [0, 0], 'and the games reset under it');
|
||||
assert.equal(G.done, null, 'one set is not a match');
|
||||
});
|
||||
|
||||
test('classic tennis: the second set ends the run, with no corpse', () => {
|
||||
G.mode = 'tennis'; G.classic = true;
|
||||
G.sets = [1, 0]; score.games = [6, 0]; G.runGames = 14;
|
||||
assert.equal(classicGame('P'), true, 'the match is over');
|
||||
assert.ok(G.done && G.done.won, 'and you won it');
|
||||
assert.notEqual(G.mode, 'eaten', 'nobody died — this is the other ending');
|
||||
const runs = JSON.parse(localStorage.getItem('splatterRuns'));
|
||||
const r = runs[runs.length - 1];
|
||||
assert.equal(r.m, 'c', 'banked as a classic run');
|
||||
assert.equal(r.dead, 0, 'a match WON is not a death — that is how wins are counted');
|
||||
assert.equal(r.s, 2, 'sets won ride in the second headline slot');
|
||||
});
|
||||
|
||||
test('classic tennis: losing the match ends it the same way, and counts as a loss', () => {
|
||||
G.mode = 'tennis'; G.classic = true;
|
||||
G.sets = [0, 1]; score.games = [0, 6]; G.runGames = 12;
|
||||
assert.equal(classicGame('O'), true);
|
||||
assert.ok(G.done && !G.done.won, 'beaten');
|
||||
const runs = JSON.parse(localStorage.getItem('splatterRuns'));
|
||||
assert.equal(runs[runs.length - 1].dead, 1, 'a match lost IS the run ending badly');
|
||||
});
|
||||
|
||||
// stats.js ships its own fixture-based check — the period filter, the mode split and the name
|
||||
// normalisation that has to agree with server.py. It was exported and never called by anything.
|
||||
test("stats.js's own check", () => { assert.equal(statsCheck(), 'ok'); });
|
||||
|
||||
Reference in new issue
Block a user