Volleys get wider reach and swing window; serve volleys no longer fault
A ball passing pre-bounce moves at full pace with no bounce cue to time against, so marker-behind volleys needed frame-tight taps. Pre-bounce player contact now widens reach x1.4 and the swing window x1.8 (VOLLEY in config.js). Also clear G.wasServe in doHit so a serve returned out of the air doesn't fault-check the return, and launch.json uses autoPort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e7aab295d4
commit
a5a6f550a3
3 files changed
+19
-9
No files matched your search
+4
-3
@@ -3,9 +3,10 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "splatter-tennis",
|
||||
"runtimeExecutable": "python3",
|
||||
"runtimeArgs": ["-m", "http.server", "8641"],
|
||||
"port": 8641
|
||||
"runtimeExecutable": "sh",
|
||||
"runtimeArgs": ["-c", "python3 -m http.server ${PORT:-8641}"],
|
||||
"port": 8641,
|
||||
"autoPort": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -36,6 +36,10 @@ export const UMP_H = 3.0;
|
||||
// contact rules: outside this height band the ball can't be hit (below = it's past you)
|
||||
export const CONTACT = { minY: 0.24, maxY: 2.3 };
|
||||
|
||||
// volleys (pre-bounce contact) get extra forgiveness: the ball passes at full speed and
|
||||
// there's no bounce cue to time against, so reach and swing window widen by these factors
|
||||
export const VOLLEY = { reach: 1.4, window: 1.8 };
|
||||
|
||||
// how deep from the far/near baseline a player's interception zone extends
|
||||
export const ZONE_DEPTH = 9.5;
|
||||
|
||||
|
||||
+11
-6
@@ -1,6 +1,6 @@
|
||||
// Game logic: ballistics, swings, serves, rules/scoring, AI, and the per-frame update.
|
||||
|
||||
import { TUNE, COURT, CONTACT, ZONE_DEPTH, HOME, P_BOUNDS, TOSS, SHOT_DEPTH, POSE_TIME, BOUNCE_IDLE } from './config.js';
|
||||
import { TUNE, COURT, CONTACT, VOLLEY, ZONE_DEPTH, HOME, P_BOUNDS, TOSS, SHOT_DEPTH, POSE_TIME, BOUNCE_IDLE } from './config.js';
|
||||
import { P, O, ball, score, crowd, G, other, deuceSide, setupPoint, sendBoy } from './state.js';
|
||||
import { audio, pock, swish, applause, voice } from './audio.js';
|
||||
import { axisX, axisZ, takeFlickAim } from './input.js';
|
||||
@@ -76,6 +76,7 @@ function doHit(ch, quality, aim) {
|
||||
ball.g = g;
|
||||
ball.vx = v.vx; ball.vy = v.vy; ball.vz = v.vz;
|
||||
ball.lastHit = ch.side; ball.bounces = 0; ball.firstLand = null;
|
||||
G.wasServe = false; // a serve volleyed back pre-bounce must not fault-check the return
|
||||
ball.pow = quality; // carried pace: zombies splatter only above zSplatQ
|
||||
if (ch.side === 'P') { // shot marker: where this will land — red means it's going out
|
||||
const ok = G.mode !== 'tennis' || (Math.abs(tx) <= COURT.W / 2 + 0.02 && tz >= -0.02 && tz <= COURT.netZ);
|
||||
@@ -267,17 +268,20 @@ function intercept(side) {
|
||||
|
||||
function trySwing(ch, isAI) {
|
||||
// Contact happens if the ball is inside reach while the swing window is open.
|
||||
const reach = isAI ? TUNE.aiReach : TUNE.reach;
|
||||
// Player volleys (pre-bounce) get wider reach + window: full-speed ball, no bounce cue.
|
||||
const volley = !isAI && ball.bounces === 0;
|
||||
const reach = isAI ? TUNE.aiReach : TUNE.reach * (volley ? VOLLEY.reach : 1);
|
||||
const win = TUNE.swingWindow * (volley ? VOLLEY.window : 1);
|
||||
const t = ch.swingT;
|
||||
if (t < TUNE.swingWindup || t > TUNE.swingWindup + TUNE.swingWindow) return;
|
||||
if (t < TUNE.swingWindup || t > TUNE.swingWindup + win) return;
|
||||
if (!ball.active || G.phase !== 'rally') return;
|
||||
if (ball.lastHit === ch.side) return;
|
||||
const dx = ball.x - ch.x, dz = ball.z - ch.z;
|
||||
if (Math.hypot(dx, dz * 0.8) > reach || ball.y > CONTACT.maxY) return;
|
||||
if (ball.y < CONTACT.minY) return; // below the ankles the ball is past you — no grass-scraping saves
|
||||
// quality: how centered in the window the contact lands
|
||||
const center = TUNE.swingWindup + TUNE.swingWindow / 2;
|
||||
const off = Math.abs(t - center) / (TUNE.swingWindow / 2);
|
||||
const center = TUNE.swingWindup + win / 2;
|
||||
const off = Math.abs(t - center) / (win / 2);
|
||||
let q = Math.max(0.05, 1 - off);
|
||||
if (Math.abs(t - center) * 1000 < TUNE.perfectMs) q = 1;
|
||||
ch.facing = dx >= 0 ? 1 : -1; // forehand right (righty), backhand left → mirrored sprite
|
||||
@@ -361,7 +365,8 @@ export function update(dt) {
|
||||
}
|
||||
if (P.swingT >= 0) {
|
||||
P.swingT += dt; trySwing(P, false); // a contact resets swingT to -1 inside trySwing
|
||||
if (P.swingT > TUNE.swingWindup + TUNE.swingWindow + 0.02) { P.swingT = -1; if (G.phase === 'rally') swish(); }
|
||||
const win = TUNE.swingWindow * (ball.active && ball.bounces === 0 ? VOLLEY.window : 1); // match trySwing's volley window
|
||||
if (P.swingT > TUNE.swingWindup + win + 0.02) { P.swingT = -1; if (G.phase === 'rally') swish(); }
|
||||
}
|
||||
expirePose(P, dt);
|
||||
|
||||
|
||||
Reference in new issue
Block a user