Stop calling three different things "the ladder"

Oliver: "the talk about ladder and rung is extremely confusing." It was, and it
was my vocabulary, not his reading. "Ladder" had come to mean three unrelated
things in this codebase — the sequence of opponents in zombie tennis, the
sequence of WOUND STAGES on one body, and the name of a stats screen — and
"rung" meant a position in whichever of those you happened to be reading about.

One word per thing now:

  the OPPONENTS   who you play, in order, one game each (config.OPPONENTS,
                  matchFoe(n), zb.foe). Never "rungs".
  WOUND STAGES    healthy -> arm off -> headless, on one body. Unchanged, and
                  now the only thing allowed to be called a progression.
  a FAMILY        the sprite set a body wears (config.ZFAMS).

Renames: LADDER_TRAITS -> OPPONENTS, matchRung -> matchFoe, zb.rung -> zb.foe,
and the stats screen THE LADDER -> OPPONENTS FELLED, which is what it lists.

The 'L0'..'L9' record keys are deliberately NOT renamed: they are written into
saved run records, and renaming them would orphan every match anyone has
played. A comment says so where they are built.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Opus 5 committed 2026-08-12 13:11:55 +02:00
1 parent e2487b784a
commit df454c5d92
6 files changed
+40 -37

No files matched your search

+4 -4
View File
@@ -154,7 +154,7 @@ function doHit(ch, quality, aim) {
// rungs take it back.
// ...and the RUNG's own pace trait rides on top, inside the cap: full pace is still the ceiling,
// a rung just gets there sooner or later than the flat ladder would (see matchRung in config).
s *= Math.min(1, (TUNE.matchOppPace + (G.match.opp - 1) * TUNE.matchOppPacePer) * (ch.rung?.pace || 1));
s *= Math.min(1, (TUNE.matchOppPace + (G.match.opp - 1) * TUNE.matchOppPacePer) * (ch.foe?.pace || 1));
}
if (G.match) {
// THE OPPONENT'S WIDTH IS THE LADDER'S THIRD DIAL. Its returns scatter off aimError x (1 - 0.55
@@ -162,7 +162,7 @@ function doHit(ch, quality, aim) {
// balls". Opponent one keeps its returns inside matchWideBase of the centre line; each rung
// earns matchWidePer more width, so the line-painting is the LATE ladder, not the opening one.
if (ch.side !== 'P') {
const wide = (TUNE.matchWideBase + (G.match.opp - 1) * TUNE.matchWidePer) * (ch.rung?.wide || 1);
const wide = (TUNE.matchWideBase + (G.match.opp - 1) * TUNE.matchWidePer) * (ch.foe?.wide || 1);
tx = Math.max(-wide, Math.min(wide, tx));
}
// THE PLACEMENT, MADE VISIBLE: your aimed rally hit stamps its landing point, and render draws a
@@ -1050,7 +1050,7 @@ function racketAI(rz, dt) {
// 2.8 m/s lumber was tuned for one sudden-death point against a fresh player, and across a
// whole game it read as "very weak and slow": opponent one already moves at matchSpeedBase.
const spd = TUNE.racketSpeed * (G.match ? (TUNE.matchSpeedBase + (G.match.opp - 1) * TUNE.matchSpeedPer)
* (rz.rung?.speed || 1) : 1);
* (rz.foe?.speed || 1) : 1);
const step = Math.min(spd * dt, d);
rz.dist = (rz.dist || 0) + step; // ground covered — his walk cycle steps off this, not the clock
rz.x += dx / d * step; rz.z += dz / d * step; rz.runT += dt;
@@ -1062,7 +1062,7 @@ function racketAI(rz, dt) {
&& ball.y < CONTACT.maxY && ball.y > CONTACT.minY) {
// ...and the match has its OWN flub base: the duel's 0.2 hands a fifth of all returns away,
// which across a game is an opponent that beats itself.
const flub = G.match ? TUNE.matchFlub * Math.pow(TUNE.matchFlubKeep, G.match.opp - 1) * (rz.rung?.flub || 1)
const flub = G.match ? TUNE.matchFlub * Math.pow(TUNE.matchFlubKeep, G.match.opp - 1) * (rz.foe?.flub || 1)
: TUNE.racketFlub;
if (Math.random() < flub) rz.swingCool = 0.5; // whiff — it hesitates (rarer per rung of the ladder)
else { rz.swingT = TUNE.swingWindup + TUNE.swingWindow / 2; trySwing(rz, true, TUNE.racketQMax); rz.swingT = -1; rz.swingCool = 0.3; }