From a6451f2bdf5f8eb02b29bab35120aa8e5b435cfd Mon Sep 17 00:00:00 2001 From: Malte Langkabel Date: Fri, 3 Jul 2026 11:30:43 +0200 Subject: [PATCH] Add balancing targets section to content_design.md Record the six root numbers for the balancing pass: run length (win within ~2 h game time, cycle 20-24), phase boundaries, factory size curve, fitted threat-cost ladder, swarm fleet size (~25 ships), and block economy roots (bootstrap, doubling time, expansion cadence). Derived values are always re-derived from these roots, never patched directly. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN --- docs/content_design.md | 49 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/docs/content_design.md b/docs/content_design.md index ed4e49a..4d4d8e1 100644 --- a/docs/content_design.md +++ b/docs/content_design.md @@ -171,6 +171,55 @@ It verifies every consumed item has a producer, every item has a visuals entry, flags orphaned items, and prints which items are reprocessing-only (currently exactly advanced_alloy). +## Balancing targets (first pass, July 2026) + +The six root numbers for the balancing pass. Every derived value (threat +rate, recipe quantities, block costs, scrap rates, unlock ladder) is tuned +to hit these; when rebalancing later, change these first and re-derive, +never the other way around. The rules they follow live in +`docs/progression_design.md`. + +All targets are in **game time**. The player can pause and accelerate, so +real session length differs; playtests should measure both. The time unit +is the boss cycle (`world.toml boss_countdown_seconds`, 300 s). Destroying +a station set advances the boss countdown by `boss_advance_seconds` +(60 s), so cycles run shorter than nominal when pushing actively — these +targets deliberately ignore that; playtesting will show real run lengths. + +1. **Run length** — a winning run takes up to 2 hours of game time: win + around boss cycle 20–24. Losing runs end earlier. +2. **Phase boundaries** — early = cycles 1–5 (iron/copper, small hulls), + mid = cycles 6–14 (titanium, medium hulls), late = cycles 15+ + (advanced alloy, capitals). Push cadence roughly one station set per + cycle from mid onward. This fixes the `unlock_at_station_level` + ladder and, with the artifact win count, the `artifact_chance_formula` + pacing. +3. **Factory size curve** — producing buildings over time; when + saturated, output threat/s equals this count, so this curve IS the + player power curve. Targets: ~25 when the starting asteroid is full + (end of cycle 2), ~60 at the start of mid (cycle 6), ~120 at the + start of late (cycle 15), ~150 near the win. `threat_rate_formula` + must remain a fraction of this curve, and buildings plus belts must + physically fit the asteroid plus affordable expansions. +4. **Threat-cost ladder** — total production-seconds per *fitted* hull + (including a typical module loadout): drone ~10, frigate ~40, + destroyer ~80, cruiser ~200, battlecruiser ~350, battleship ~700, + dreadnought/carrier ~1500. Every production chain must sum to its + ladder value. Cross-check against (3): fitted-ship cadence = + ladder value / (factory threat/s devoted to military) — e.g. a mid + factory spending half of 60 threat/s on ships fields a fitted + cruiser roughly every 7 s. +5. **Fleet size** — swarm-leaning: ~25 player combat ships as the + standing mid-game fleet. Standing fleet = build cadence (4) × average + ship lifetime, so this target drives time-to-kill and therefore the + combat stat magnitudes tuned in the arena pass. +6. **Block economy roots** — bootstrap complete (starting asteroid full) + by the end of cycle 2; a factory spending ~30% of its capacity on + blocks doubles in ~4 minutes early game; one expansion affordable per + cycle at ~1/3 of block income, with escalating costs that eventually + outrun any income (see the growth curve rules in + `progression_design.md`). + ## Deliberate placeholders / open questions for later passes - All new hulls have `threat.cost_formula = "0"` so enemy waves do not spawn