// 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) }; 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 }; // 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;