From Oliver's feedback round: - Racket melee: any swish (tap) in splatter cleaves shamblers within TUNE.meleeRange (1.7m) — a close-range survival tool. Works during a horde too (tap = swish only, still no serve). The racket-zombie boss (Phase 6, not built) is immune by design. - Zombies home in on you: on YOUR half (z > netZ) they now drift toward P.x instead of walking a straight lane; far half stays an aimless shamble. - Multi-ball / rapid fire: splatter is now a shooter — every tap/flick fires immediately (dropped the toss ritual there), and serving while a ball still flies stashes the old one into a new extraBalls array so it keeps arcing and killing (cap 6). Refactored the kill into a shared killZombie() with no ball coupling, used by ball hits, pellets, melee, and extra balls. - Machine-gun mode clarity: confirmed free-walk (drag/arrows move you, auto-assist off) and tap-doesn't-serve during a horde; added a code-drawn "rambo" racket held forward with a muzzle flash over the player, and a splatter-specific serve prompt. REVIEW on your phone — two opinionated calls, both easy to revert: 1. Dropped the toss in splatter (tap = instant quick-serve, pow 0.62). Rapid fire, but no apex-timing quality/pierce. Want the toss back as an option? 2. MG posture is a code-drawn forward racket, not a real body sprite — good enough, or worth a proper p_mg sprite later? Also: each rapid serve still auto-consumes a stocked bomb, so bombs deplete fast now — flag if that feels wrong. Verified via window.T: melee kills a 1.0m zombie and spares a 4.5m one; a near-half zombie drifts x 3→1.9 toward you; firing twice leaves 1 stashed ball + a fresh serve (barrage screenshot); extra balls kill and pierce; free-walk during a horde (x 0→2.7); horde-tap melees without serving; tennis toss-serve and Phase-4 bomb consume still work; no console errors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
77 lines
3.9 KiB
JavaScript
77 lines
3.9 KiB
JavaScript
// All tuning and constants. TUNE is live-editable in game via the T panel;
|
|
// the exported consts below are structural values that code shares but the panel doesn't show.
|
|
|
|
export const TUNE = {
|
|
camZ: 36, camY: 9, camF: 280, horizon: 40,
|
|
gravity: 9.2, // slightly floaty vs 9.8 for readability
|
|
spinFactor: 0.9, // extra dive on quality shots (fake topspin)
|
|
bounceY: 0.58, bounceXZ: 0.78,
|
|
playerSpeed: 5.4, aiSpeed: 4.3,
|
|
reach: 1.3, aiReach: 1.6, // tight contact radius: the assist positions you, the tap must be honest
|
|
swingWindup: 0.06, // s before contact window opens
|
|
swingWindow: 0.16, // s contact window length
|
|
baseShotSpeed: 16, qualityShotSpeed: 12, // horizontal m/s = base + q*quality; perfect timing = real pace
|
|
serveSpeed: 21, serveQualitySpeed: 7,
|
|
aimError: 2.4, // m of scatter at quality 0
|
|
perfectMs: 45, // |offset from window center| for PERFECT
|
|
aiQualityMin: 0.45, aiQualityMax: 0.85, aiFlub: 0.09,
|
|
aiReaction: 0.45, // s before the CPU reads your shot — well-aimed corners are winners
|
|
autoAssist: matchMedia('(pointer: coarse)').matches ? 1.15 : 0, // auto-run speed factor (mobile)
|
|
zSpeed: 0.7, // zombie shamble speed m/s
|
|
zSpawnEvery: 2.6, // s between spawns
|
|
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
|
|
zPierce: 0.75, // pace a ball keeps after a splatter — hot balls plow through lined-up pairs
|
|
bombEvery: 8, // splatter kills between bomb-ball rewards
|
|
bombMax: 3, // bomb balls you can stock at once
|
|
bombRadius: 2.5, // m blast radius of a bomb ball
|
|
hordeLen: 12, // s a horde wave lasts
|
|
hordeRate: 2.5, // zombies spawned per second during a horde
|
|
mgRate: 6, // machine-gun pellets fired per second during a horde
|
|
meleeRange: 1.7, // m — a racket swish cleaves shamblers this close to you
|
|
missHud: 1, // show the last miss reason on screen (full log: T.G.missLog)
|
|
};
|
|
|
|
export const COURT = { L: 23.77, W: 8.23, dblW: 10.97, netZ: 11.885, netH: 0.914, netW: 10.06 };
|
|
COURT.svcFar = COURT.netZ - 6.4; // far service line
|
|
COURT.svcNear = COURT.netZ + 6.4; // near service line
|
|
|
|
// world sizes (meters)
|
|
export const PLAYER_H = 1.88;
|
|
export const BOY_H = 1.2;
|
|
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;
|
|
|
|
// recovery positions between shots
|
|
export const HOME = { P: { x: 0, z: 22.8 }, O: { x: 0, z: 2.5 } };
|
|
|
|
// where the near player is allowed to move
|
|
export const P_BOUNDS = { x: 6.5, zMin: COURT.netZ + 1.2, zMax: 26 };
|
|
|
|
// serve toss: release height and upward speed (apex derives from these + gravity)
|
|
export const TOSS = { y0: 1.8, vy: 4.6 };
|
|
|
|
// shot target depth from the receiving baseline, by aim (up = deep, none = mid, down = short)
|
|
export const SHOT_DEPTH = { deep: 3.0, mid: 5.4, short: 8.6 };
|
|
|
|
// how long swing/serve poses hold before returning to idle (s)
|
|
export const POSE_TIME = 0.38;
|
|
|
|
// the server's idle ball bounce: sideways offset to the free hand, apex height, tempo.
|
|
// tempo drives the ball, the hand sprite AND the pock sound — one value, always in sync.
|
|
export const BOUNCE_IDLE = { dx: 0.5, apex: 0.55, tempo: 3.6 };
|
|
|
|
// generated sprite sheets: the idle pose is this many pixels tall; all scales derive from it
|
|
export const SPRITE_H = 112;
|