The level is you, in the league: quicker legs, a wider window, and a curve that pays on the way up
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
7dc72009eb
commit
7e2649c888
5 files changed
+82
-16
No files matched your search
+14
-4
@@ -1315,7 +1315,16 @@ function onBounce() {
|
||||
// them, so the same swing whiffed centimetres short there. Gore's opener keeps the old feel: its
|
||||
// tennis is a prologue, not the mode.
|
||||
export const rallyFeel = () => !!(G.match || G.classic);
|
||||
export const assistK = () => rallyFeel() ? Math.max(TUNE.autoAssist, TUNE.matchAssist) : TUNE.autoAssist;
|
||||
// THE LEVEL IS YOU, in the league (2026-08-29): the player's legs and contact window grow subtly
|
||||
// with the level — LEAGUE modes only (the climb, the cup, the training court). Classic keeps its
|
||||
// own difficulty picker, netplay is a fair human match, gore is untouched (its trio lives in
|
||||
// profile.bonus too, read elsewhere). Level 1 is factor 1: the shipped baseline is the floor.
|
||||
export const legLevel = () => G.match && !G.net ? bonus().run : 1;
|
||||
export const winLevel = () => G.match && !G.net ? bonus().window : 1;
|
||||
// legLevel rides INSIDE assistK, so the mover's legs and intercept's reachability can never
|
||||
// disagree about what those legs can get to (the rzChaseSpeed rule, player-side).
|
||||
export const assistK = () => (rallyFeel() ? Math.max(TUNE.autoAssist, TUNE.matchAssist) : TUNE.autoAssist)
|
||||
* legLevel();
|
||||
|
||||
function intercept(side) {
|
||||
const ch = side === 'O' ? O : P;
|
||||
@@ -1409,7 +1418,7 @@ function trySwing(ch, isAI, qCap = TUNE.aiQualityMax) {
|
||||
// dies on contact feeling fair, and the gore modes are untouched.
|
||||
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 win = TUNE.swingWindow * (volley ? VOLLEY.window : 1) * (isAI ? 1 : winLevel());
|
||||
const t = ch.swingT;
|
||||
if (t < TUNE.swingWindup || t > TUNE.swingWindup + win) return;
|
||||
if (!ball.active || G.phase !== 'rally') return;
|
||||
@@ -1986,12 +1995,13 @@ export function update(dt) {
|
||||
const volley = ball.bounces === 0;
|
||||
(G.swingSnaps ||= []).push({ t: P.swingT, d: Math.hypot(ball.x - P.x, (ball.z - P.z) * 0.8), y: ball.y,
|
||||
bz: ball.z, bounces: ball.bounces,
|
||||
r: TUNE.reach * (volley ? VOLLEY.reach : 1), w: TUNE.swingWindow * (volley ? VOLLEY.window : 1) });
|
||||
r: TUNE.reach * (volley ? VOLLEY.reach : 1), w: TUNE.swingWindow * (volley ? VOLLEY.window : 1) * winLevel() });
|
||||
}
|
||||
trySwing(P, false); // a contact resets swingT to -1 inside trySwing
|
||||
// volley-length window while a hostile shot is in the air too, or the swing clock expires at
|
||||
// 0.24 s and closes TUNE.parryWindow (0.34 s) before it has had its chance
|
||||
const win = TUNE.swingWindow * ((shots.length || (ball.active && ball.bounces === 0)) ? VOLLEY.window : 1);
|
||||
const win = TUNE.swingWindow * ((shots.length || (ball.active && ball.bounces === 0)) ? VOLLEY.window : 1)
|
||||
* winLevel(); // the clock must outlive the widened window or the level's gift is dead time
|
||||
if (P.swingT > TUNE.swingWindup + win + 0.02) { P.swingT = -1; if (G.phase === 'rally') swish(); logWhiff(); } // log even if the point ended mid-swing
|
||||
}
|
||||
expirePose(P, dt);
|
||||
|
||||
+15
-7
@@ -89,14 +89,22 @@ export function award(kind, n = 1) {
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- the level is you ----
|
||||
// Phase 4's defensive buffs, through existing dials only: level 1 is the shipped game (factor 1)
|
||||
// and the cap stays honest — a maxed profile survives volleys better, it does not stop dying.
|
||||
// Read at the dial sites (meleeCooldown's floor, the parry's reach/arm, hurtPlayer's grace).
|
||||
// The level's buffs, through existing dials only: level 1 is the shipped game (factor 1, the
|
||||
// untouchable baseline) and the caps stay honest. FRONT-LOADED (sqrt of the level fraction,
|
||||
// 2026-08-29): Oliver plays at level ~20 and the buffs must be felt on the way up, not only at
|
||||
// the cap — the xp curve itself is fast-early for the same reason. At level 20 the fraction is
|
||||
// ~0.62 of the cap value; linear gave 0.39.
|
||||
// Read at the dial sites: meleeCooldown's floor, the parry's reach/arm, hurtPlayer's grace (the
|
||||
// gore trio), and — THE LEAGUE PAIR (his league-4 report: "autorun schlicht zu langsam, timing
|
||||
// zu tight") — gameplay.assistK's legs and trySwing's contact window, league modes only.
|
||||
export function bonus() {
|
||||
const f = (levelAt(prof.xp) - 1) / (LEVEL_CAP - 1);
|
||||
return { melee: 1 - 0.25 * f, // the swish floor drops to 75% — panic scales with mastery
|
||||
parry: 1 + 0.25 * f, // the parry reaches a quarter further
|
||||
grace: 1 + 0.35 * f }; // a volley needs a third longer to land twice
|
||||
const f = Math.sqrt((levelAt(prof.xp) - 1) / (LEVEL_CAP - 1));
|
||||
return { melee: 1 - 0.25 * f, // the swish floor drops to 75% — panic scales with mastery
|
||||
parry: 1 + 0.25 * f, // the parry reaches a quarter further
|
||||
grace: 1 + 0.35 * f, // a volley needs a third longer to land twice
|
||||
run: 1 + 0.15 * f, // the league autorun's legs, up to +15%
|
||||
window: 1 + 0.20 * f }; // the league contact window, up to +20% — cleaner timing too:
|
||||
// quality is centre-normalised, so the same ms error scores higher
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- the promotion ----
|
||||
|
||||
Reference in new issue
Block a user