Time a tap from touch-down, and give the opponents' legs a knee
Oliver after step 1: much harder, stuck at the Steward, "trotz gutem Timing nicht mehr bekommen". A tap only proves it is a tap when the finger lifts, so the swing started 100-150 ms after he meant it; the old volley window hid that, step 1's groundstrokes did not. A tap made on the gold ring fell from q 0.82 to 0.09 (and a third were missed at 150 ms), murky balls landed short and the opponent came in. The swing clock now starts at the touch's age (rally modes only); flicks keep their crossing. The opponents' legs had no ceiling: 7.3 m/s at the Runner, 15.7 at the Champion against the player's ~6.2. Past legKnee (6 m/s) only 15% of the growth survives, monotone, so the ladder order holds; champion ~7.5. v362. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4e7c816a25
commit
f9b0dd2efb
6 files changed
+82
-7
No files matched your search
@@ -920,7 +920,8 @@ test('league 5 has no speed dip below league 4, including serve and wrong-footed
|
||||
const fourth = cfg.leagueOf(4).band, fifth = cfg.leagueOf(5).band;
|
||||
let floor = 0;
|
||||
for (let n = fourth[0]; n <= fourth[1]; n++) floor = Math.max(floor, measure(n, serve, surprised));
|
||||
assert.ok(floor > 7, 'the rig measures a real full-speed chase');
|
||||
// since the legs have a knee (gameplay.knee) the late ladder runs at ~6-7.5 m/s, not 7-15
|
||||
assert.ok(floor > TUNE.legKnee * 0.8, 'the rig measures a real full-speed chase: ' + floor.toFixed(2));
|
||||
for (let n = fifth[0]; n <= fifth[1]; n++) {
|
||||
const speed = measure(n, serve, surprised);
|
||||
assert.ok(speed >= floor, `${cfg.matchFoe(n).short}: ${speed} must clear ${floor} (serve ${serve}, surprised ${surprised})`);
|
||||
@@ -1136,6 +1137,38 @@ test('a SHORT ball is an approach, a deep one keeps the baseliner back, and nobo
|
||||
} finally { TUNE.matchFlub = flub; }
|
||||
});
|
||||
|
||||
test('a TAP is timed from touch-down: a normal 120 ms tap on the gold ring still returns cleanly', () => {
|
||||
// Oliver, 2026-10-14: "manchmal scheine ich den Ball trotz gutem Timing nicht mehr zu bekommen".
|
||||
// A tap only proves it is a tap when the finger LIFTS, so it swung ~100-150 ms late — inside the
|
||||
// groundstroke window that took a tap made exactly on the gold ring from q 0.82 to 0.09, and at
|
||||
// 150 ms a third of the balls were simply missed. keyPressed's `lead` starts the swing clock at
|
||||
// touch-down; this is the ring-tapping player, with a real finger's 120 ms of tap on top.
|
||||
const assist = TUNE.autoAssist; TUNE.autoAssist = 1.15;
|
||||
try {
|
||||
startMatch(); G.interlude.introT = 0; G.match.opp = 6;
|
||||
for (let i = 0; i < 2.2 * 60; i++) update(DT);
|
||||
const LAG = 7; // frames: ~120 ms between the finger landing and it lifting
|
||||
let fire = -1, armed = false, incoming = 0, returned = 0, sumQ = 0;
|
||||
for (let i = 0; i < 90 * 60; i++) {
|
||||
G.modal = null; G.modalQ.length = 0; G.hp = G.hpMax; G.match.opp = 6;
|
||||
if (G.phase === 'pretoss' && !(G.interlude && G.interlude.introT > 0)) { if (i % 40 === 0) keyPressed('Space'); }
|
||||
else if (G.phase === 'toss') { if (ball.vy < 0.4 && ball.y > 2.4) keyPressed('Space'); }
|
||||
else if (G.phase === 'rally' && ball.active && ball.lastHit === 'O' && ball.vz > 0 && !armed) {
|
||||
const d = Math.hypot(ball.x - P.x, (ball.z - P.z) * 0.8);
|
||||
if (Math.abs(d - TUNE.matchRingSweet) < TUNE.matchRingBand) { armed = true; incoming++; fire = i + LAG; }
|
||||
}
|
||||
if (i === fire) keyPressed('Space', LAG / 60); // the lift: the touch's age rides along
|
||||
const before = ball.lastHit;
|
||||
update(DT);
|
||||
if (armed && before === 'O' && ball.lastHit === 'P') { returned++; sumQ += ball.pow; armed = false; }
|
||||
else if (armed && (!ball.active || G.phase !== 'rally')) armed = false;
|
||||
}
|
||||
assert.ok(incoming >= 30, 'a real sample: ' + incoming);
|
||||
assert.ok(returned / incoming >= 0.9, `a gold-ring tap returns the ball: ${returned}/${incoming}`);
|
||||
assert.ok(sumQ / returned >= 0.6, 'and returns it cleanly: mean q ' + (sumQ / returned).toFixed(2));
|
||||
} finally { TUNE.autoAssist = assist; }
|
||||
});
|
||||
|
||||
test('rally depth: deep by default, mistiming lands it SHORTER, never longer', () => {
|
||||
// the only out a rally ball may find is wide — depth must never put one past the baseline
|
||||
startMatch(); G.interlude.introT = 0;
|
||||
|
||||
Reference in new issue
Block a user