Pace back to 1.2x, and unweld the racket from over his head

PACE 1.3x -> 1.2x on all four numbers: 19.2 / 14.4 rally, 22.8 / 26.4 serve.

The strength-to-velocity relationship you suspected was already there is: a
splatter serve runs 26 m/s at the minimum pull, 36 at the kill threshold and
49.2 at a full draw. The draw very nearly DOUBLES the pace (1.89x), because
serveQualitySpeed carries more of the pair than serveSpeed does. Nothing to
change unless you want it steeper, and that is the one knob.

THE RACKET-ALWAYS-UP LOOK WAS A LATCH, not the art. hitServe sets pose 'serve'
with poseT 0; that only expires after POSE_TIME (0.38 s); and p_pull, the
wind-up frame, is only ever selected from pose 'idle'. So firing faster than
0.38 s reset the timer before it could expire, he never returned to idle, the
pull frame was never drawn once, and he stayed welded into p_smash — arm
extended overhead with balls appearing out of the strings. Exactly the late
game, because that is where the fast successions are.

serveToss now clears a stale 'serve' pose: a new wind-up ends the last shot's
follow-through. At ~4.6 shots/sec the cycle is now pull x5 frames, smash x8,
pull x5 — a real swing. Slow fire still plays the whole ritual: bounce, pull,
smash, idle, bounce.

Caveat: past roughly 8 shots/sec each pose gets 1-2 frames and will read as a
flicker rather than a swing. There is no fix for that short of drawn
follow-through frames; the poses are stills.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-02 19:14:12 +02:00
1 parent 19c13986c5
commit 7284ca08f8
2 files changed
+12 -5

No files matched your search

+6 -5
View File
@@ -10,13 +10,14 @@ export const TUNE = {
reach: 1.3, aiReach: 1.6, // tight contact radius: the assist positions you, the tap must be honest 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 swingWindup: 0.06, // s before contact window opens
swingWindow: 0.16, // s contact window length swingWindow: 0.16, // s contact window length
// ALL FOUR PACE NUMBERS ARE 1.3x THEIR ORIGINALS (16/12 and 19/22) — the whole ball, tennis and // ALL FOUR PACE NUMBERS ARE 1.2x THEIR ORIGINALS (16/12 and 19/22) — the whole ball, tennis and
// splatter, his and theirs, moved together so nothing about the relative feel shifts. // splatter, his and theirs, moved together so nothing about the relative feel shifts.
baseShotSpeed: 20.8, qualityShotSpeed: 15.6, // horizontal m/s = base + q*quality; perfect timing = real pace baseShotSpeed: 19.2, qualityShotSpeed: 14.4, // horizontal m/s = base + q*quality; perfect timing = real pace
// the slingshot's pace range. Weighted hard toward QUALITY so a full draw is a smash you can // the slingshot's pace range. Weighted hard toward QUALITY so a full draw is a smash you can
// see: 27 m/s floppy at the minimum pull, 53 m/s at full. (baseShotSpeed above is the rally // see: 26 m/s floppy at the minimum pull, 49 m/s at full — the draw nearly DOUBLES the pace,
// volley — shared with tennis mode and the CPU, and scaled with them.) // which is the "harder pull, faster ball" relationship, and it is in serveQualitySpeed's share
serveSpeed: 24.7, serveQualitySpeed: 28.6, // of the pair. (baseShotSpeed is the rally volley — shared with tennis and the CPU.)
serveSpeed: 22.8, serveQualitySpeed: 26.4,
serveSpin: 1.3, // extra gravity a full-power serve carries (was a fixed 0.35). Without it the serveSpin: 1.3, // extra gravity a full-power serve carries (was a fixed 0.35). Without it the
// net-clearance loop ate the whole smash: a flat fast ball can't get over the // net-clearance loop ate the whole smash: a flat fast ball can't get over the
// tape AND come down on a target, so it got throttled back to walking pace. // tape AND come down on a target, so it got throttled back to walking pace.
+6
View File
@@ -861,6 +861,12 @@ function tryGunBurst() {
// slingshot serve, stage one: the pull-back starts the toss animation. The ball rises while // slingshot serve, stage one: the pull-back starts the toss animation. The ball rises while
// you aim (dotted line, render.js); the release (dragServe) fires it. Only from a fresh pretoss. // you aim (dotted line, render.js); the release (dragServe) fires it. Only from a fresh pretoss.
export function serveToss() { export function serveToss() {
// A NEW WIND-UP ENDS THE LAST SHOT'S FOLLOW-THROUGH. Without this he latched: hitServe sets
// pose 'serve' with poseT 0, that only expires after POSE_TIME, and the pull frame is only drawn
// from 'idle' — so firing faster than 0.38 s reset the timer before it could ever expire and he
// was welded into p_smash. Racket permanently up, balls appearing out of it, no wind-up at all,
// which is precisely the late-game rapid-fire look. Clearing it here restores pull/smash/pull.
if (score.server === 'P' && P.pose === 'serve') { P.pose = 'idle'; P.poseT = 0; }
if (score.server === 'P' && G.phase === 'pretoss' && !ball.active) tossBall(P); if (score.server === 'P' && G.phase === 'pretoss' && !ball.active) tossBall(P);
} }