Split the monolith into ES modules; hoist scattered magic numbers
game.js (830 lines) -> 9 modules, native ES modules so still zero build step: dom canvas handles (imports nothing; keeps cycles harmless) config TUNE (live-tunable) + structural constants state all mutable game state (G) + setupPoint/sendBoy audio sfx synthesis, recorded umpire voice, reverb, speech fallback input keyboard + touch, exports axes and takeFlickAim() gameplay ballistics, swings, serve, rules, AI, update loop render camera, viewport, sprites, crowd, all drawing panel T tuning UI main boot, loop, intro, window.T test hooks Deduped into named constants: bounce tempo (was in 3 places), contact height band, intercept zone depth, home positions, player bounds, toss params, shot depths, pose hold time, sprite scale, actor heights. Also extracted drawShadow/drawBall/expirePose from repeated code. One intentional import cycle (input <-> gameplay), safe: both sides only call functions at runtime, never during module evaluation. Regression-verified in browser sims: point cycle, CPU returns 11/12, assist contract 8/8, corner-winner gradient intact (0/12 perfect corners returned, 10/12 center balls returned). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f2bf21f252
commit
c506a631c2
12 files changed
+936
-824
No files matched your search
@@ -0,0 +1,57 @@
|
||||
// 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 = 2.7;
|
||||
|
||||
// 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;
|
||||
Reference in new issue
Block a user