The host names the stakes: classic tennis, or the death match
Three chips under VS A FRIEND — the host's pick rides the room and the start message rebuilds both courts on pairing. In the death match your opponent IS the zombie: their lost points tear its wound stages, their won points draw your blood, and bleeding out crosses the wire as the other phone's win. The same receiver-is-judge verdicts drive the match machinery through resolveInterlude, so the fall, the mop beat and the burst come along unmodified. New: gushProne — the game-winning body bleeds out on the ground, from prone-height anchors, until it bursts. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5e80c4e69d
commit
c0b59eee90
10 files changed
+241
-26
No files matched your search
@@ -89,7 +89,7 @@ const { mirror, netMsg, netTest } = await import('./src/net.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
|
||||
const startDrill = (...a) => { const r = _startDrill(...a); G.modal = null; G.modalQ.length = 0; return r; };
|
||||
const { zombies, spawnZombie, pellets } = await import('./src/zombies.js');
|
||||
const { zombies, spawnZombie, pellets, parts } = await import('./src/zombies.js');
|
||||
const { killPlayer } = await import('./src/zombies.js');
|
||||
const { drillQueue, drillLegal, arrival, drillMult, DRILL_TYPES } = await import('./src/drill.js');
|
||||
const cfg = await import('./src/config.js');
|
||||
@@ -1272,6 +1272,82 @@ test('netplay: a serve fault is the receiver\'s call, and re-arms the server\'s
|
||||
assert.ok(!netTest.sent.some(m => m.t === 'fault'), 'applying a fault does not echo one back');
|
||||
});
|
||||
|
||||
// ---- NETPLAY DEATH MATCH: the host's pick rides the room's start message, and the match
|
||||
// machinery is driven by the same receiver-is-judge verdicts classic uses — routed through
|
||||
// resolveInterlude instead of pointTo, so wounds, blood, the fall and the burst all come along.
|
||||
function freshNetDeath(side) {
|
||||
restartRun();
|
||||
G.classic = true; // the waiting court, exactly as the menu builds it
|
||||
assert.ok(startNetVs(), 'net vs takes a clean court');
|
||||
netTest.sent = [];
|
||||
netMsg({ t: 'start', side, death: true });
|
||||
}
|
||||
|
||||
test('netplay death match: the host\'s pick rebuilds the court on pairing', () => {
|
||||
freshNetDeath('B');
|
||||
assert.ok(G.net && G.net.ready && G.net.death, 'the wire survives the mode swap');
|
||||
assert.equal(G.mode, 'splatter');
|
||||
assert.ok(G.match && G.interlude, 'the match machinery owns the court');
|
||||
assert.ok(zombies.some(z => z.variant === 'r'), 'the body is on court');
|
||||
assert.equal(score.server, 'O', 'the host serves first here too');
|
||||
assert.equal(G.modal, null, 'no popup may eat the first remote serve');
|
||||
});
|
||||
|
||||
test('netplay death match: their verdicts wound the body, mine cost blood — no echo', () => {
|
||||
freshNetDeath('A');
|
||||
const rz = zombies.find(z => z.variant === 'r');
|
||||
const hp0 = rz.hp;
|
||||
netMsg({ t: 'pt', win: 'you' }); // their phone judged: my ball beat them
|
||||
assert.ok(rz.hp < hp0, 'my point tears at the body');
|
||||
assert.ok(!netTest.sent.some(m => m.t === 'pt'), 'applying a verdict does not echo one back');
|
||||
const blood = G.hp;
|
||||
netMsg({ t: 'pt', win: 'me' }); // their phone judged: their ball beat me
|
||||
assert.equal(G.hp, blood - 1, 'their point draws my blood');
|
||||
});
|
||||
|
||||
test('netplay death match: the receiver judges — an untouched winner draws blood and crosses the wire', () => {
|
||||
freshNetDeath('B');
|
||||
netMsg({ t: 'shot', x: 0, y: 1.5, z: 21.77, vx: 0, vy: 3, vz: -16, g: 9.2, pow: 0.6, serve: false });
|
||||
const blood = G.hp;
|
||||
for (let i = 0; i < 900 && ball.active; i++) update(DT);
|
||||
assert.ok(!ball.active, 'the rally resolved locally — this phone was the judge');
|
||||
assert.equal(G.hp, blood - 1, 'their untouched winner draws my blood');
|
||||
const pt = netTest.sent.find(m => m.t === 'pt');
|
||||
assert.ok(pt && pt.win === 'you', 'and the verdict went to their phone');
|
||||
});
|
||||
|
||||
test('netplay death match: bleeding out is said on the wire, and the other phone wins on it', () => {
|
||||
freshNetDeath('B');
|
||||
G.hp = 1;
|
||||
netMsg({ t: 'pt', win: 'me' }); // the point that empties me
|
||||
assert.equal(G.mode, 'eaten', 'my run ends as every match death does');
|
||||
assert.ok(netTest.sent.some(m => m.t === 'dead'), 'the death is said out loud');
|
||||
freshNetDeath('A'); // ...and the winner's phone, receiving it
|
||||
score.games[0] = 3;
|
||||
netMsg({ t: 'dead' });
|
||||
assert.ok(G.done && G.done.won, 'the last one standing gets the win ending');
|
||||
assert.equal(G.match.felled, 3, 'felled = the games that actually burst the body');
|
||||
});
|
||||
|
||||
test('netplay death match: the same friend walks back on — no roster climb', () => {
|
||||
freshNetDeath('A');
|
||||
const opp0 = G.match.opp;
|
||||
for (let i = 0; i < 4; i++) netMsg({ t: 'pt', win: 'you' }); // four points = the game = the burst
|
||||
assert.equal(G.match.opp, opp0, 'the opponent number holds through the game win');
|
||||
for (let i = 0; i < 60 * 25 && !zombies.some(z => z.variant === 'r'); i++) update(DT);
|
||||
assert.ok(zombies.some(z => z.variant === 'r'), 'the body returns after the mop beat');
|
||||
assert.equal(G.match.opp, opp0, 'as the same opponent, not the roster\'s next rung');
|
||||
});
|
||||
|
||||
test('death match: the game-winning body bleeds out on the ground before it bursts', () => {
|
||||
freshNetDeath('A');
|
||||
const rz = zombies.find(z => z.variant === 'r');
|
||||
G.matchFall = { t: 0.3, from: 1, vx: 0, x0: rz.x, kill: true }; // landed, prone, pre-burst
|
||||
const before = parts.length;
|
||||
for (let i = 0; i < 20; i++) update(DT);
|
||||
assert.ok(parts.length > before, 'the prone hold gushes (gushProne), where bleed() is suppressed');
|
||||
});
|
||||
|
||||
// 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