diff --git a/config.js b/config.js index 5e63fd3..c71b01a 100644 --- a/config.js +++ b/config.js @@ -22,6 +22,7 @@ export const TUNE = { zMax: 6, // shamblers on court at once zSplatQ: 0.5, // min shot quality to splatter; below = stagger pushback zPushback: 1.4, // m a staggered zombie is knocked back + splatterOverT: 0.1, // s the spent-ball 'over' beat lasts before the next serve is armed missHud: 1, // show the last miss reason on screen (full log: T.G.missLog) }; diff --git a/gameplay.js b/gameplay.js index 89917cb..948798d 100644 --- a/gameplay.js +++ b/gameplay.js @@ -354,6 +354,9 @@ function trySwing(ch, isAI) { export function keyPressed(code) { if (code !== 'Space') return; audio(); // unlock on first gesture + // splatter: a tap/flick during the brief 'over' beat arms the next serve immediately — + // reset inline, then fall through to pretoss so the finger is the only cadence limit + if (G.mode === 'splatter' && G.phase === 'over') { ball.active = false; setupPoint(); } if (G.phase === 'pretoss' && score.server === 'P') { if (G.mode === 'splatter') { const flick = takeFlickAim(); @@ -505,11 +508,20 @@ export function update(dt) { if (ball.active && Math.abs(ball.vy) < 0.8) { // rolled out — dead ball ball.active = false; if (G.mode === 'splatter') { // hand the server a fresh one, fast — they're coming - G.phase = 'over'; G.overT = 0.25; sendBoy(); + G.phase = 'over'; G.overT = TUNE.splatterOverT; sendBoy(); if (Math.random() < 0.12) voice('new_balls', 0.3); } else if (ball.bounces === 1) pointTo(ball.lastHit, null); } } + // splatter: retire spent or hopeless balls fast so the next serve comes right up. + // z-bounds gate on travel direction — a serve born behind the baseline (P.z up to 26) + // heads to the far court (vz<0), so it must not read as "out the back" on frame one. + if (G.mode === 'splatter' && ball.active && + ((ball.lastHit === 'X' && ball.bounces >= 1) + || (ball.z < -2 && ball.vz < 0) || (ball.z > 25.5 && ball.vz > 0) + || Math.abs(ball.x) > 7.5)) { + ball.active = false; G.phase = 'over'; G.overT = TUNE.splatterOverT; + } if (G.phase === 'toss' && ball.y <= 1.4 && ball.vy < 0) { // uncaught toss → retoss ball.active = false; G.phase = 'pretoss'; if (score.server === 'O') G.aiServeT = 0.8;