From 1e601ca33d591e5e242ebaf6e7be8c936d3dd718 Mon Sep 17 00:00:00 2001 From: Oliver Rieger Date: Thu, 13 Aug 2026 21:58:32 +0200 Subject: [PATCH] The fall drops fast, lies long, and never rises MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Oliver, three refinements in one breath: the frame PROGRESSION quicker (drop 0.06+0.25 -> 0.05+0.15 — against a live rolling ball the slower drop read as mush), the prone hold KEEPS its bench-tuned 2 s ('the lying itself can stay that long'), and the rise row is cut ('the opponent respawns anyway') — done() re-marks the body in its wound skin regardless, so rising first was a respawn with extra steps. The rise frames stay in the sheets for the bench. Wound-beat test updated to the new contract. Co-Authored-By: Claude Fable 5 --- index.html | 2 +- src/beats.js | 9 ++++++++- test.js | 12 ++++++------ 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index 42a76a5..5d6d89f 100644 --- a/index.html +++ b/index.html @@ -431,6 +431,6 @@ if (location.hostname !== 'splatter-tennis.wuwi.one') { - + diff --git a/src/beats.js b/src/beats.js index 8bc1430..7d89d45 100644 --- a/src/beats.js +++ b/src/beats.js @@ -396,7 +396,14 @@ function matchReady() { // the collapse reads as one continuous movement decelerating into the ground; the 0.07 s fall2 it // replaced gave the two drawn frames ~4 display frames each and read as a cut, and a 0.40 s one split // them 0.80 against 0.56 m/s, which is a visible hitch between two poses of the same drop. -export const FALL = [['fall1', 0.06], ['fall2', 0.25], ['fall3', 2.0], ['rise', 0.45]]; +// 2026-08-13, after the early-fall retiming: "the progression of frames in the fall must be +// quicker... we must remove the rise frame, the opponent respawns anyway... the lying itself can +// stay that long." So: the DROP tightened (0.06+0.25 -> 0.05+0.15 — the collapse now plays +// against a live rolling ball, and the slower drop tuned for a dead court read as mush next to +// it), the 2 s prone hold KEEPS its bench-tuned length, and the rise row is CUT: done() puts the +// body on its mark in its wound skin anyway, and rising just to be teleported read as a respawn +// with extra steps. The z_*rise frames stay in the sheets/preload — the bench can still show them. +export const FALL = [['fall1', 0.05], ['fall2', 0.15], ['fall3', 2.0]]; // Read LIVE by startFall, because a snapshot goes stale the moment the table is tuned and the match's // 'over' beat would then expire mid-collapse — the exact bug the comment above is about. export const fallLen = () => FALL.reduce((s, f) => s + f[1], 0); diff --git a/test.js b/test.js index c6fe086..a9208ef 100644 --- a/test.js +++ b/test.js @@ -746,17 +746,17 @@ test('a wounding point knocks the body down and it gets up MINUS the limb', () = update(DT); assert.match(zb.showFrame, /fall1$/, 'first frame is the buckle'); assert.ok(!/w\d/.test(zb.showFrame), 'and it is the UNWOUNDED buckle'); - // ...and it must LIE THERE a moment before it rises: a corpse that touches the ground and is - // instantly upright reads as a stumble, not as something the point did to it. + // ...and it must LIE THERE a moment: a corpse that touches the ground and is instantly upright + // reads as a stumble, not as something the point did to it. THE RISE ROW IS GONE (2026-08-13, + // Oliver: "remove the rise frame. the opponent respawns anyway") — the beat ends prone and the + // body takes its mark in the wound skin. let downFrames = 0; - for (let i = 0; i < FALL_LEN * 60 && G.matchFall && !/rise$/.test(zb.showFrame || ''); i++) { + for (let i = 0; i < (FALL_LEN + 1) * 60 && G.matchFall; i++) { update(DT); if (/fall3$/.test(zb.showFrame || '')) downFrames++; } assert.ok(downFrames > 0.3 * 60, 'it stays down for a beat: ' + (downFrames / 60).toFixed(2) + 's'); - assert.match(zb.showFrame, /w1rise$/, 'it drags itself up as the one-armed body'); - // ...and the point comes back only once it is standing - for (let i = 0; i < FALL_LEN * 60 && G.matchFall; i++) update(DT); assert.equal(G.matchFall, null, 'the fall ends on its own clock'); + assert.equal(racketStage(zb), 1, '...and it comes back as the one-armed body'); assert.equal(zb.showFrame, null, 'and hands the sprite back to drawZombie'); assert.equal(G.serveNum, 1, 'matchReady ran: the next point is armed'); });