The blood you have left, the bodies behind you, and a face for staging

Three things, all in service of the match reading as its own mode:

BLOOD, COUNTED. The vignette says "worse" and never says "how many". One pip
per hit left, top-right beside the board, in the panel language the game/sets
bar already uses; the last two breathe. Pips rather than a bar on purpose —
the pool is 4 or 8, and a smooth readout of a small integer is the exact
ambiguity being fixed.

OPPONENTS FELLED IS THE MEASURE (handoff slice 1). Blood-only scoring means a
lost game costs nothing but the blood already spent, so games survived is not
the achievement. The in-play bar prints OPPONENT n · m FELLED instead of the
game/sets line, the death screen says BLED OUT / FELLED n OPPONENTS · m GAMES,
and it keeps its own matchBest — drillBest's pattern. The kills-to-next-point
pips are hidden in a match: nothing there is killed for points, and they were
colliding with the new line anyway.

STAGING WEARS A DIFFERENT ICON. Both builds live on the same phone and two
identical bloody tennis balls is a coin toss. Driven by HOSTNAME, not by the
branch — the block is identical on staging and main, so it never conflicts on
a merge and never has to be remembered.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-12 08:53:02 +02:00
1 parent a54cfa5f4f
commit 8011186cda
10 files changed
+113 -9

No files matched your search

+21
View File
@@ -750,5 +750,26 @@ test('regicide: killing the body mid-game wins the game outright', () => {
assert.ok(oppOf(), 'and the next opponent is on');
});
test('the match measures OPPONENTS FELLED, with its own best', () => {
localStorage.removeItem('matchBest');
startMatch();
for (let k = 0; k < 8; k++) resolveInterlude('P'); // two games = two bodies down
assert.equal(G.match.opp, 3, 'on the third rung');
G.hp = 1; resolveInterlude('O');
assert.equal(G.mode, 'eaten', 'bled out');
assert.equal(G.match.felled, 2, 'the summary counts bodies, not games');
assert.ok(G.match.newBest, 'first run of the day is a record');
assert.equal(localStorage.getItem('matchBest'), '2', '...and it is written down');
// 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');
G.hp = 1; resolveInterlude('O');
assert.equal(G.match.felled, 1);
assert.equal(G.match.best, 2, 'the mark to beat is shown');
assert.ok(!G.match.newBest, 'and one body does not beat two');
assert.equal(localStorage.getItem('matchBest'), '2', 'the record survives a worse run');
G.mode = 'splatter'; endMatch();
});
console.log(`\n${passed} tests passed`);
process.exit(0); // render.js schedules viewport timers at load; don't let them outlive the report