The finishing shot is spent, and the wound skins come back

"one-shot is bad - way too easy - also we're losing all the nice gore
animation sprites" and "I noticed the last kill of a game can carry to the
spawn of a new zombie of the next round".

drillOneShot is off. The second half of that note is the part worth
remembering: a 1 hp body never passes through a wound stage, so every skin in
ZWOUND — arm off, ribs out, still walking — is skipped and the roster's best
art never renders at all. Kept as a switch, never a default.

The carry-over was a real unfairness, not a curiosity. A ball keeps pace
through a kill and travels UP-COURT, which is exactly where the next batch is
born — so the shot that finished your round flew on into bodies that did not
exist when you took it, and faulted you on a round you had not been shown
yet. The shot that ends a round is spent now, and drillCaught does the same
since it also refills the court up-court. Pierce within a batch is untouched:
that is honest occlusion you could have read, this was not.

Verified with real aimed serves through a whole round: round banks, ball goes
inactive, the next batch walks on with 0 faults and a clean sequence, and the
gang carries roster HP again (brute 5, ball boy 3).

33 tests, 15 seeds clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-11 20:43:26 +02:00
1 parent 0f524a08c9
commit 57e96d629e
5 files changed
+71 -22

No files matched your search

+7 -6
View File
@@ -374,12 +374,13 @@ export const TUNE = {
// brings it back. Past 3 the puzzle stops being readable.
drillEaterSpeed: 1.15,// they walk a touch faster than the batch: the batch is a list, this is a threat
drillRampT: 210, // s to go from the bottom of every curve above to the top
// TESTING KNOB, currently ON. Every body in the drill dies to one splat-quality hit, whatever the
// roster says it should soak. Flip to 0 for the real thing — and note that this is not purely a
// convenience: the roster's HP is what makes the called ORDER matter (a roller is seven serves, so
// being told to take it third means starting on it first), and one-shot bodies delete that whole
// layer. Decide it on feel rather than leaving it on by drift.
drillOneShot: 1, // >= 0.5: batch bodies AND eaters all drop to 1 hp
// TESTING KNOB, and it is OFF again after one playtest: "one-shot is bad - way too easy - also
// we're losing all the nice gore animation sprites". Both halves matter and the second is the one
// that is easy to miss — a 1 hp body never passes through any of its WOUND STAGES, so every skin
// in ZWOUND (arm off, ribs out, still walking) is skipped entirely and the roster's best art never
// renders. The HP is also what makes the called ORDER matter: a roller is seven serves, so being
// told to take it third means starting on it first. Left here as a switch, not a default.
drillOneShot: 0, // >= 0.5: batch bodies AND eaters all drop to 1 hp
drillCaughtBack: 45, // s of ramp handed back when one reaches you. A real setback, not a reset
fps: 60, // frame cap — 30 halves battery/heat but Oliver felt it; smoothness wins (0 = uncapped)
lowPower: 1, // >= 0.5 caps RES at 3 (−44% of pixels painted per frame) — the battery saver,
+8 -1
View File
@@ -19,7 +19,7 @@
// render.js reads DRILL_COLS and draws.
import { TUNE } from './config.js';
import { G } from './state.js';
import { G, ball } from './state.js';
import { zombies, spawnZombie } from './zombies.js';
import { voice } from './audio.js';
@@ -176,6 +176,12 @@ export function drillKill(zb) {
if (d.step >= d.seq.length) { // round complete — and the next one is already here
d.rounds++;
d.msg = { text: 'GOOD', t: 0.9 };
// THE SHOT THAT ENDS A ROUND IS SPENT. A ball keeps pace through a kill (TUNE.zPierce) and it
// is travelling UP-COURT — which is exactly where the next batch is about to be born. So the
// ball that finished your round flew on into bodies that did not exist when you took the shot
// and faulted you on a round you had not been shown yet. Pierce WITHIN a batch stays: that is
// honest occlusion you could have read. This is not.
ball.active = false;
newRound(d);
}
return;
@@ -206,6 +212,7 @@ export function drillCaught(text = 'CAUGHT') {
voice('game_opponent', 0.1);
d.msg = { text, t: 1.8, bad: true };
zombies.length = 0;
ball.active = false; // same reason as the round-complete case: the court refills up-court
d.seq = []; d.step = 0; d.faults = 0; d.spawnT = 0;
d.t = Math.max(0, d.t - TUNE.drillCaughtBack); // ...and the ramp backs off a little
}