Difficulty moves the dials a player can feel: reaction, reach, floor, serve
"using the difficulty in classic tennis I don't feel any difference" — and he was right, verified live: G.diff arrives intact, the dials were just the wrong ones. flub/cap/speed barely register: the quality cap trims a distribution whose 0.45 floor never moved (EASY and HARD averaged 0.59 vs 0.70), and ±12% on aiSpeed never binds because the CPU pre-plans its intercept. What a player reads as SKILL is how fast the CPU reacts to the shot (aiReaction — the config's own comment says corners are winners because of it), how long its racket is (aiReach), the FLOOR of its shot quality (a raised qMin means every ball comes back with pace), and the serve — none of which the first cut touched, and the serve was hardcoded 0.6-0.95 besides. DIFFS carries seven dials now with real spread: EASY whiffs one ball in three, reads late, swings short and serves soft; HARD reads in 0.28s, swings 1.75m, floors quality at 0.62 with a cap of 1.0, and serves 10% hotter. MEDIUM is pinned dial-for-dial to the shipped TUNE values by test, so the middle chip remains exactly yesterday's classic. All G.diff-guarded, so the racket zombie and gore's opener keep their own numbers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
6b795f77cc
commit
77bc68043d
4 files changed
+33
-14
No files matched your search
+10
-4
@@ -1154,7 +1154,8 @@ function trySwing(ch, isAI, qCap = TUNE.aiQualityMax) {
|
||||
// sampled 1.3-1.6 m — centimetres outside the 1.3 m post-bounce reach, point after point. The
|
||||
// assist can deliver you NEAR the ball but not INSIDE a phone-tuned reach; a rally mode lives or
|
||||
// dies on contact feeling fair, and the gore modes are untouched.
|
||||
const reach = isAI ? TUNE.aiReach : TUNE.reach * (volley ? VOLLEY.reach : 1) * (rallyFeel() ? TUNE.matchReach : 1);
|
||||
const reach = isAI ? (G.diff ? G.diff.reach : TUNE.aiReach)
|
||||
: TUNE.reach * (volley ? VOLLEY.reach : 1) * (rallyFeel() ? TUNE.matchReach : 1);
|
||||
const win = TUNE.swingWindow * (volley ? VOLLEY.window : 1);
|
||||
const t = ch.swingT;
|
||||
if (t < TUNE.swingWindup || t > TUNE.swingWindup + win) return;
|
||||
@@ -1176,7 +1177,11 @@ function trySwing(ch, isAI, qCap = TUNE.aiQualityMax) {
|
||||
const aim = isAI
|
||||
? { ax: [ -1, 0, 1 ][Math.floor(Math.random() * 3)], az: Math.random() < 0.25 ? -1 : 0 }
|
||||
: flick || { ax: axisX(), az: axisZ() * (ch.side === 'P' ? 1 : -1) };
|
||||
if (isAI) q = Math.min(q, TUNE.aiQualityMin + Math.random() * (qCap - TUNE.aiQualityMin));
|
||||
// G.diff (classic difficulty) moves the FLOOR too: a raised qMin is what makes HARD's every
|
||||
// ball arrive with pace, where a cap alone just trims the best ones. Null outside classic, so
|
||||
// the racket zombie and the gore opener keep the TUNE floor.
|
||||
const qMin = G.diff ? G.diff.qMin : TUNE.aiQualityMin;
|
||||
if (isAI) q = Math.min(q, qMin + Math.random() * (Math.max(qCap, qMin) - qMin));
|
||||
if (!isAI && G.mode === 'tennis') {
|
||||
G.swingSnaps = null; G.rallyNear = null;
|
||||
logSwing({ result: 'HIT', reason: `contact q=${Math.round(q * 100) / 100}`, d: Math.round(Math.hypot(dx, dz * 0.8) * 100) / 100, y: Math.round(ball.y * 100) / 100 });
|
||||
@@ -1711,14 +1716,15 @@ export function update(dt) {
|
||||
if (G.aiServeT <= 0) tossBall(O);
|
||||
} else if (oServes && G.phase === 'toss') {
|
||||
if (ball.vy < 0 && ball.y < serveApexY() - 0.1) {
|
||||
G.wasServe = true; hitServe(O, 0.6 + Math.random() * 0.35);
|
||||
G.wasServe = true; hitServe(O, Math.min(1, (0.6 + Math.random() * 0.35) * (G.diff ? G.diff.serve : 1)));
|
||||
// ...and the body that just served holds the strike, so the swing reads as its own (beats)
|
||||
if (G.match) { const rz = zombies.find(z => z.variant === 'r'); if (rz) rz.serveHold = 0.4; }
|
||||
}
|
||||
}
|
||||
if (G.mode === 'tennis' && G.phase === 'rally') {
|
||||
G.aiReactT += dt;
|
||||
const incoming = ball.active && ball.lastHit === 'P' && G.aiReactT >= TUNE.aiReaction;
|
||||
const incoming = ball.active && ball.lastHit === 'P'
|
||||
&& G.aiReactT >= (G.diff ? G.diff.reaction : TUNE.aiReaction);
|
||||
const tgt = incoming ? intercept('O') : HOME.O;
|
||||
const spd = TUNE.aiSpeed * (G.diff ? G.diff.speed : 1) * (incoming ? 1 : 0.5); // slow recovery: wrong-footing wins points
|
||||
const dx = tgt.x - O.x, dz = tgt.z - O.z, d = Math.hypot(dx, dz);
|
||||
|
||||
Reference in new issue
Block a user