Phase 1: tighten the splatter shooting loop
Input during the 'over' beat is no longer swallowed: a tap/flick while G.phase==='over' (splatter only) resets inline and falls through to the serve logic, so the finger is the only cadence limit. Spent balls (lastHit 'X' after a stagger bounce) and hopeless balls (out of the z/x bounds) are retired immediately instead of lingering until they roll out. New TUNE.splatterOverT (0.1) replaces the hardcoded 0.25 over-timer. The out-of-bounds z-bounds gate on travel direction so a quick-serve born behind the baseline (P.z up to 26, heading vz<0 to the far court) isn't killed on frame one. Verified via window.T: flick/tap during 'over' serves same frame; spent ball dies the frame it bounces; serve->serve latency 0 frames with immediate flicks; tennis 'over' unchanged (2.2s pause, tap ignored); tennis CPU still returns 11/12 serves; no console errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
fe5532b558
commit
795ddeb425
2 files changed
+14
-1
No files matched your search
+13
-1
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user