The leagues, phase 2: five bands, pace tiers, locked courts, the members' ground, the promotion ceremony

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
wuwiandClaude Fable 5 committed 2026-08-29 12:43:23 +02:00
1 parent 9133c0d351
commit 16bf5b9fed
12 files changed
+406 -71

No files matched your search

+51 -5
View File
@@ -552,6 +552,10 @@ export const TUNE = {
matchPlayerHp: 8, // your pool for the MATCH — richer than the gore mode's 4, because a real game
// has rallies you deserve to lose without the run ending ("the player is too
// weak in this mode"). Their point still costs 1; matchHeal is how you last
leaguePace: 0.06, // THE LEAGUES' pace tier: solo match modes run at 1 + (league-1)*this —
// ball pace on BOTH rackets and the opponent's legs together, so league 1
// IS the shipped game (factor 1.0, the calibration rule) and league 5
// plays ~24% faster. The one knob for "das Spiel wird schneller".
matchSpeedBase: 1.25, // racketAI speed multiplier for opponent ONE. 1.4 was calibrated against
// the broken recovery (the player never walked back to centre, so every
// wide return looked like opponent strength) — with real footwork restored
@@ -769,6 +773,20 @@ export const COURTS = [
out: '#0f1a15', lawn: '#16261e', stripe: '#16261e' },
splatter: { stands: '#06050b', tierA: '#0b0813', tierB: '#08060f', board: '#2c0e0e',
out: '#0a120d', lawn: '#101c15', stripe: '#101c15' } },
// THE MEMBERS' GROUND (2026-08-29, THE LEAGUES): the Dead Club's decayed place of honour and the
// top league's home — Oliver picked it over the abattoir and the dawn court. BRIGHTLY decayed,
// the deliberate opposite of gloom's dark: a dead grey-green lawn gone to seed, pale weathered
// marble stands, faded gold on the boards, cracked aged-cream lines. Borrows clay's warm crowd
// and ads sheets (the same move gloom made with hard's) with a light `dim` to push the stone
// back; the surface is the FASTEST in the game — a dead dry lawn skids — which lets the court
// table itself finish the pace ladder the leagues ride (clay slowest ... members fastest).
{ id: 'members', name: "THE MEMBERS' GROUND", sub: 'DEAD LAWN · FASTEST, LOWEST SKID', tile: 'court_members',
ads: 'x_ads_c', crowd: 'x_crowd_c', stripes: true, line: '#e8e2d0', border: '#d8d2c0', dim: 0.15,
bounceY: 0.47, bounceXZ: 0.87,
tennis: { stands: '#4a463c', tierA: '#565144', tierB: '#504b40', board: '#6e5a20',
out: '#5c6b4c', lawn: '#93a273', stripe: '#8a996a' },
splatter: { stands: '#28251d', tierA: '#302c22', tierB: '#2b2820', board: '#3a2e10',
out: '#3a4630', lawn: '#5c6a46', stripe: '#556340' } },
];
// The tour, picked on the same screen as the court. Only the two TENNIS PLAYERS and the turned CPU
@@ -850,12 +868,40 @@ export const DIFFS = [
// bottom third opens, the middle third is the quarter, everything up to management is the semi,
// and the final is the honours board — but every round now draws from a deeper hat, which is
// exactly what the bracket screen's shuffle is showing you.
export const CUP_ROUNDS = [
{ name: 'ROUND ONE', band: [1, 2, 3, 4, 5], need: 1 },
{ name: 'QUARTER-FINAL', band: [6, 7, 8, 9, 10], need: 2 },
{ name: 'SEMI-FINAL', band: [11, 12, 13, 14, 15, 16, 17], need: 2 },
{ name: 'THE FINAL', band: [18], need: 2 },
// THE LEAGUES (2026-08-29, the plan of record in PROJECT.md). Five bands over the 18-rung ladder,
// each with its home court; the club reads bottom-up, so the bands keep the roster's own
// hierarchy (ground staff -> club staff -> officials -> coaching staff -> the honours board).
// The tournament (below) draws from the CURRENT league's band, and winning it is promotion.
// `band` is [lo, hi] inclusive rungs. Court order deliberately rides the bounce table: clay is
// the slowest surface for the slowest league, grass the first prestige, and the top of the club
// plays on THE MEMBERS' GROUND. The pace tier is TUNE.leaguePace (gameplay.leaguePace()).
export const LEAGUES = [
{ id: 1, name: 'THE GRAVEYARD SHIFT', court: 'clay', band: [1, 4] },
{ id: 2, name: 'THE CLUB STAFF', court: 'hard', band: [5, 8] },
{ id: 3, name: 'THE OFFICIALS', court: 'grass', band: [9, 12] },
{ id: 4, name: 'THE COACHING STAFF', court: 'gloom', band: [13, 15] },
{ id: 5, name: 'THE HONOURS BOARD', court: 'members', band: [16, 18] },
];
export const leagueOf = lg => LEAGUES[Math.min(LEAGUES.length, Math.max(1, lg | 0 || 1)) - 1];
// The tournament, PER LEAGUE. Same shape CUP_ROUNDS always had (name/band/need) — round one is a
// single game so a run opens fast, and the drawn body's pool still scales with `need`
// (matchOpponent). The rounds ride ON the cup object (beats.startMatch stamps them), so every
// consumer reads cup.rounds and the full-ladder constant is gone.
// A league band is 3-4 rungs, so the draw is WITHOUT REPLACEMENT: every non-final round carries
// the whole sub-band and the run deals each body at most once (cup.played / buildBracket's used
// set), the final is always the band's top rung — "only the finale is settled", still. A 4-rung
// league is four rounds (a shuffle of three challengers), a 3-rung league is three.
export const cupRoundsFor = lg => {
const [lo, hi] = leagueOf(lg).band;
const open = [];
for (let r = lo; r < hi; r++) open.push(r);
const names = open.length >= 3 ? ['ROUND ONE', 'QUARTER-FINAL', 'SEMI-FINAL']
: ['ROUND ONE', 'SEMI-FINAL'];
return open.map((_, i) => ({ name: names[i] || 'ROUND ' + (i + 1), band: open.slice(),
need: i ? 2 : 1 }))
.concat([{ name: 'THE FINAL', band: [hi], need: 2 }]);
};
export const TOURS = [
// `kit` — HOW this tour's art takes a colour, because the two sets are dressed differently and the