Files
dota_factory/docs/progression_design.md
Malte Langkabel 437ac97355 Rewrite progression_design.md as rules/principles document
Turn the rough draft into structured rules for the production tree,
progression pacing, and balancing: resource phases, ratio curve,
shortcut recipes, refactorability, cost archetypes, threat model,
drop pacing, scrap/block economies, and numeric guardrails.

Includes agreed decisions: ship scrap derived from threat via a
scrap_per_threat key, no duplicate schematic drops (Mk2 upgrade
recipes deferred to future work), removal of ship levels, and an
action-item list for the follow-up requirements/code changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN
2026-07-03 08:36:33 +02:00

15 KiB
Raw Blame History

Progression & Balancing Design

Rules and principles that govern the production tree, progression pacing, and balancing. This document contains rules only — concrete content (item lists, recipes, unlock levels, stat numbers) lives in the config files and content_design.md; those numbers must follow the rules stated here.

Player-experience goals

What each phase of a run should feel like:

  • Early: learning belts and ratios with forgiving chains. The building block economy is the main constraint; the player bootstraps a self-sustaining factory from the starting stock.
  • Mid: deeper chains, the first real ratio puzzles, and the first meaningful drop decisions (which schematic, when to push).
  • Late: combat feeds the factory — capital production requires salvage. Progress means extending and refactoring the existing factory, not rebuilding it. Strange ratios are deliberate optimization puzzles.

Overarching: an experienced player gains efficiency through knowledge — layout foresight, understanding chains, exploiting shortcut recipes — never through hidden mechanics. An inexperienced setup should not cost much more than an experienced one; experience pays off in how easily the factory adapts later (see Refactorability).

Resource phases

  • A run has exactly four base inputs:
    1. Two mined ores available from the start.
    2. A third mined ore unlocked mid-game (slower to mine than the starting ores).
    3. A fourth input unlocked late-game, obtainable only from reprocessing salvaged scrap.
  • The fourth input is the core loop hook: capital ship production requires fighting (salvaging and reprocessing), not just mining.
  • There is no direct "resource unlock" mechanism. Miner recipes unlock implicitly (REQ-LOCK-IMPLICIT) when some unlocked schematic's material chain reaches that ore. Resource pacing is therefore controlled entirely through the unlock_at_station_level values of ships, modules, and assembler recipe schematics — and the content must guarantee that the chains actually connect (a mid-game schematic must require an item whose chain reaches the third ore, or the ore never unlocks).

Production tree rules

Structure

  • One new base input chain per phase. Factory complexity ramps alongside ship size; no phase adds more than one new bottom-level chain.
  • Intermediates are generic shared parts. Keep the item count low — modules and hulls of a tier draw from a shared pool of that tier's intermediates rather than each having bespoke inputs.
  • Thematic naming over thematic items. Inputs should be plausible for what the recipe produces (crystals for lasers, heat sinks for bigger lasers). Achieve this through naming and chain membership, not by adding item types: rename a generic part, don't add a parallel one.

Ratios

  • Ratio "niceness" degrades with tier. The producer:consumer ratios needed for 100% throughput follow a curve:
    • Tier 1 (ore → basic material): trivially nice (e.g. 1:1 or 1:2 miner:smelter).
    • Tier 2: slightly complex but still clean (e.g. 2:3).
    • Higher tiers: increasingly strange ratios, as deliberate optimization puzzles.
  • Exceptions in both directions are allowed when there is a reason — a clean late chain as a breather, an odd early chain as a teaser — but the curve is the default.

Shortcut recipes

  • Some strange chains get a shortcut recipe: an explicitly unlockable assembler recipe schematic (unlock_at_station_level ≥ 0, drop-only per REQ-LOCK-EXPLICIT) that skips a step (e.g. t1 → t3 directly) and yields nice ratios for a chain whose base path is strange.
  • Not every strange chain gets a shortcut. Some strangeness is permanent; the absence of a fix is a valid design choice.
  • Shortcuts are pure rewards, never balance factors. An item's threat value is the maximum across its producing recipes (REQ-THREAT-ITEM), so unlocking a cheaper recipe does not lower the item's threat accounting — the player gains real factory efficiency without their ships being valued cheaper and without enemy wave budgets shifting. Consequently: balance every chain around its base (expensive) path; the shortcut's savings define the size of the reward.

Refactorability

  • Rule (the property): unlocking the next tier or size of a thing must be an extension of the existing production line — adding assemblers and belts to what is already built — never a teardown and rebuild.
  • Default technique: the bigger version introduces one new intermediate that is produced from a subset of the smaller version's inputs (possibly plus one additional low-tier material), and otherwise reuses the smaller version's inputs. Existing lines keep running and feed the new intermediate's assemblers.
  • The property is the rule; the technique is only the default. It may be broken where it fights thematic plausibility, as long as the property still holds.

Cost archetypes

Every item has two cost knobs: material quantity and cycle time. Both feed the threat value identically (threat = recursive production-seconds, REQ-MOD-THREAT), so the split between them does not change what an item is worth — it changes what kind of factory pressure it creates:

  • Material-heavy, fast (e.g. armor plates): simple items; stress belt throughput, splitter logistics, and miner/smelter counts.
  • Time-heavy, lean (e.g. shield modules): technically complex items; few inputs — possibly higher-tier ones — but long cycles; stress assembler counts and parallelization.

Rule: each module family commits to a clear archetype, so factories supporting different fleet doctrines feel structurally different to build.

Threat model (balancing backbone)

  • Threat cost = total recursive production-seconds (REQ-MOD-THREAT). One factory-second equals one threat; player output and enemy wave budgets are denominated in the same currency.
  • Rule: combat power per threat is roughly constant across all ships, modules, and tiers. Higher tiers are better per ship and per module slot, not per invested factory-second — their advantage is concentration (fewer, bigger things; slot geometry per content_design.md) and qualitative capabilities, not a better exchange rate. Deviations from this rule are deliberate and documented.
  • Difficulty race: the enemy threat rate (threat_rate_formula) is tuned against the factory output (threat/s) achievable by a competent player — slightly below it early, crossing above it eventually. The game is endless; enemy scaling must ultimately outpace any factory, and player skill shifts when, not whether.
  • All time scaling lives in the threat rate — waves get bigger, ships of a given schematic never get individually stronger. There is no ship level dimension: stat formulas are plain values, and per-ship level scaling (ship_level_formula) does not exist (see Action items). Push scaling on enemy defence stations is the separate, player-triggered difficulty axis and keeps its level formulas.

Unlock & drop pacing

  • Starting set rule: the schematics unlocked at game start (unlock_at_station_level = -1) must be exactly enough to reach the first push unaided — a functioning block loop, small hulls, a basic weapon, and the salvage loop. Nothing more.
  • The unlock_at_station_level ladder mirrors the resource phases: mid-tier hulls/modules/recipes at low station levels, capital content at higher levels. A schematic must not become available before the chains its materials need can be unlocked alongside it.
  • No duplicate drops. Ship and module schematics leave the drop pool once owned, exactly as assembler recipe schematics already do. There are no schematic level-ups; player power grows through unlock breadth and factory scale only, which keeps power-per-threat exact on both sides. The pool therefore shrinks over a run and late pushes increasingly offer artifacts — intended: the late game is a race for the win condition. Per-item progression may return later as Mk2 upgrade recipes (see Open tasks), never as free level-ups.
  • Artifacts trade power for progress. Artifact options compete with schematic picks in the same choice dialog; the artifact chance must be tuned so that taking one is a real decision (giving up a level-up or unlock), not automatic in either direction.

Scrap & reprocessing economy

  • Scrap is the bridge from combat back into the factory, with two sinks: smelting (same basic materials as ore — the safe, boring option) and reprocessing (probabilistic higher intermediates, including the late-game input — the gamble that eventually becomes mandatory).
  • The reprocessing output pool renormalizes over implicitly unlocked items (REQ-LOCK-REPROCESSING-POOL), so its output quality improves automatically as the run progresses. Rule: weights are authored for the fully unlocked pool state; early-game behavior falls out of renormalization for free and needs no separate staging.
  • Rule: ship scrap drops are derived, never authored. A destroyed ship drops threat cost × scrap_per_threat (a world.toml key), with the threat cost computed from its actual hull plus installed modules (REQ-MOD-THREAT) — a kitted-out ship drops more scrap than a bare hull automatically. ships.toml carries no scrap value. Defence stations are the exception: they keep authored scrap_drop_formulas, because pushing rewards are tuned independently of ship production costs.
  • Consequence: the threat value of scrap is the constant 1 / scrap_per_threat. The min-scrap_drop schematic derivation (REQ-THREAT-SCRAP) and its potential circularity disappear.
  • Rule: the late-game input's income rate meaningfully gates capital production. The tuning target is expressed as "capital ships per boss cycle for a player who fights at parity", not as an absolute rate.

Building block economy

  • Building blocks are the only global currency and the early game's central constraint. The early game is a bootstrap problem: convert the starting stock into a self-sustaining block loop before the first waves bite.
  • Rule: the starting stock suffices for a minimal block loop plus the first shipyard — with a little slack for beginner mistakes, but not enough to skip the loop entirely.
  • Rule: asteroid expansions compete with buildings for the same currency. Expansion pricing makes "more space" a real alternative to "more stuff", not an afterthought.

Numeric guardrails

Constraints that every recipe must respect, independent of tuning:

  • Belt throughput: belt speed and per-tile capacity cap how fast a single belt can feed an input. A recipe whose per-cycle inputs cannot be sustained by one belt per input at 100% duty cycle is a deliberate design (forcing parallel belts/splitters as part of a high-tier puzzle) — never an accident of quantity choice.
  • Buffer burstiness: input buffers hold 2× the per-cycle amount (REQ-MAT-INPUT-BUFFER), so large per-cycle quantities create bursty belt demand. Low tiers prefer small quantities with short cycles; big-batch recipes are reserved for high tiers where burstiness is part of the puzzle.
  • Cycle times scale with tier monotonically — a higher-tier item never has a shorter total chain time than a lower-tier item of the same role.

Open tasks / future work

  • Rework recipes.toml once the rules in this document are fixed: the current tree feels too close to Factorio; apply the thematic-naming rule and the ratio curve to it (renames and quantity changes, not new items).
  • Balancing pass (see placeholders in content_design.md): set the unlock_at_station_level ladder, real threat costs and default_modules, reprocessing weights, and the threat-rate formulas according to the rules above.
  • Mk2 upgrade recipes — the deferred design for per-item progression, to revisit once the config has stabilized. A duplicate-style drop unlocks a distinct *_mk2 item whose recipe consumes the Mk1 item plus higher-tier parts. This preserves power-per-threat (the extra power is paid in real production-seconds, since threat is recursive), satisfies the refactorability rule (the Mk1 line keeps running and feeds one new assembler), and keeps balancing one-dimensional (no level variable anywhere). Enemy-side progression happens via default_modules variants per era instead of a level formula.

Action items — changes beyond this document

Agreed changes that require edits to requirements.md, the code, and the configs:

  1. Derive ship scrap from threat. Add scrap_per_threat to world.toml; remove [ship.loot].scrap_drop from ships.toml; update REQ-RES-SCRAP-DROP; replace REQ-THREAT-SCRAP with the constant 1 / scrap_per_threat. Station scrap_drop_formulas stay.
  2. Remove schematic upgrades. Owned ship/module schematics leave the drop pool (REQ-DEF-SCHEMATIC-DROP); drop the new-unlock/level-up annotation from the choice dialog; remove player_production_level from ships.toml, modules.toml, and the code paths that increment and evaluate it (REQ-BLD-SHIPYARD, REQ-MOD-CONFIG).
  3. Remove ship levels entirely. Delete ship_level_formula (REQ-WAV-SHIP-LEVEL); replace all stat formulas in ships.toml and modules.toml with plain values (REQ-SHP-STATS, REQ-MOD-CONFIG, REQ-MOD-STAT-CALC evaluation rules). Enemy defence stations keep their station-level formulas — push scaling is unaffected.
  4. Balancing tool follow-up. Remove the per-ship-entry level from balancing.toml and the L<level> display format (REQ-BAL-TEAM, REQ-BAL-CONFIG-GAME, REQ-BAL-UI-WIDGET, REQ-BAL-UI-INSPECT-WINDOW); station entries keep their level. Ship stats panels no longer evaluate at a level (REQ-MOD-UI-STATS-PANEL, REQ-UI-SHIP-STATS-PANEL).
  5. Fill unfillable schematic slots with artifacts. With duplicates removed, the schematic drop pool can run dry — previously unreachable. Decision: every slot in the choice dialog that cannot be filled with a schematic because the eligible pool is exhausted is filled with an artifact option instead (in addition to any artifact option granted by the regular artifact roll). A push therefore always awards a full dialog. Update REQ-DEF-SCHEMATIC-DROP.
  6. Confirm wave scaling carries alone. With per-ship level scaling gone, threat_rate_formula is the only time-scaling axis; verify the current world.toml values still produce the intended difficulty curve (the config reportedly already runs with a flat ship level, so this is likely a review, not a retune).