Diagnose that player output equals building count, so the growth curve must be shaped by the block and space economy: escalating expansion costs (formula of expansions purchased) as the long-run curve, a designed doubling time for the block feedback loop, and an explicit rule that growth is limited by economy, never by construction waiting. Note the HQ intake ceiling and add the condensed-building-block idea to future work, plus an action item for the expansion cost formula. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN
333 lines
18 KiB
Markdown
333 lines
18 KiB
Markdown
# 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
|
||
|
||
- **Each phase transition adds exactly one new base input chain.** A base
|
||
input is a bottom-level resource entering the factory from outside — a
|
||
mined ore or the scrap-only input. The early game starts with two ores
|
||
as the baseline; the transition to mid adds one (the third ore), the
|
||
transition to late adds one (the scrap-only input). No transition ever
|
||
introduces more than one unfamiliar bottom-level chain, so the factory
|
||
grows in one direction at a time.
|
||
- **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 and
|
||
lower tiers' 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 drop only for known chains.** A shortcut recipe enters the
|
||
drop pool only when both its input items and its output item are
|
||
already unlocked (in addition to the station level check). The player
|
||
is never offered a shortcut for a chain they have not built yet. The
|
||
output-item half of this check already exists in
|
||
REQ-DEF-SCHEMATIC-DROP; the input half is new (see Action items).
|
||
- **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 a *local edit* of the existing production line — adding assemblers
|
||
and belts, or replacing a machine or two in place — never a rebuild of
|
||
the line.
|
||
- **What this buys the player:** foresight pays off in space, not blocks.
|
||
An experienced player leaves a little slack in the middle of a line,
|
||
knowing the next size or tier upgrade means tearing out one assembler
|
||
and a few belts there and inserting the new step — plus maybe swapping
|
||
a recipe or two elsewhere — while the rest of the line keeps running
|
||
untouched.
|
||
- **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 does not exist. 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.
|
||
- **Schematics can require other schematics.** Beyond the station-level
|
||
gate, a schematic (ship, module, or assembler recipe) may list
|
||
prerequisite schematics (`unlock_requires`, REQ-LOCK-PREREQ) that must
|
||
already be unlocked before it enters the drop pool — e.g. the medium
|
||
laser requires the small laser; a future Mk2 requires its base version.
|
||
Station level gates the earliest
|
||
*when*; prerequisites gate the *order*, keeping drop offers coherent
|
||
with what the player already owns.
|
||
- **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 an 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_formula`s, because pushing
|
||
rewards are tuned independently of ship production costs.
|
||
- Consequence: the threat value of scrap is the constant
|
||
`1 / scrap_per_threat` (REQ-THREAT-SCRAP). The former min-`scrap_drop`
|
||
schematic derivation and its potential circularity are gone.
|
||
- **Rule:** the late-game input's income rate meaningfully gates capital
|
||
production — unlocking a capital hull must not mean spamming it; the
|
||
input trickles in slowly enough that every capital ship is a noticeable
|
||
investment. The tuning target is relative, not absolute: assume a
|
||
reference player who destroys and salvages roughly the threat the game
|
||
spawns ("fighting at parity"), and tune `scrap_per_threat`, the
|
||
reprocessing weights, and capital material costs so that this player
|
||
affords roughly N capital ships per boss cycle. An absolute income rate
|
||
would be meaningless (income depends entirely on how much the player
|
||
fights) and would not self-scale; per boss cycle, the target tracks the
|
||
threat rate as it steps up.
|
||
|
||
## 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: the growth curve lives here.** A saturated building produces
|
||
exactly 1 threat/s, so the player's output curve *is* their
|
||
building-count curve — shaping growth over a run means shaping the
|
||
block and space economy, there is nowhere else it can live. Intended
|
||
shape: exponential bootstrap (block-limited) → ramp
|
||
(expansion-limited) → asymptotic squeeze as expansion costs outrun
|
||
income, racing the enemy threat rate throughout.
|
||
- **Rule: escalating expansion costs.** Expansion cost is a formula of
|
||
the number of expansions already purchased, rising steeply enough that
|
||
expansions eventually outrun any block income. The starting asteroid
|
||
is deliberately small — filled within the first boss cycle or two, so
|
||
the early exponential burst is a satisfying ramp, not a balance hole —
|
||
and from then on the output curve is the expansion curve. Blocks keep
|
||
a meaningful sink for the entire run, and "grow vs. army" stays a live
|
||
decision at every moment.
|
||
- **Rule: designed doubling time.** Block production is a positive
|
||
feedback loop (blocks buy assemblers, assemblers make blocks); its
|
||
time constant is a designed quantity, never an accident of quantity
|
||
choice. The block chain's depth and the per-building costs are tuned
|
||
against a stated target of the form: "a factory spending X% of its
|
||
capacity on blocks doubles in ~T minutes."
|
||
- **Rule: growth is limited by economy, never by waiting.** Construction
|
||
times stay short; the serial build queue must not be used as a growth
|
||
brake. Waiting for placed buildings to become operational — especially
|
||
at the start of a run — is frustration, not gameplay. All growth
|
||
limiting comes from block income and expansion pricing.
|
||
- Note: block income has a structural ceiling — blocks enter the stock
|
||
through the HQ's single belt port, so income is capped at belt
|
||
throughput regardless of assembler count. Per-building costs should be
|
||
high enough that this cap can bind late-game (see the condensed-block
|
||
idea under Open tasks).
|
||
|
||
## 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.
|
||
- **Condensed building blocks** — a drop-unlockable shortcut-style
|
||
recipe that packs several blocks' worth of value into one belt item,
|
||
relieving the HQ intake ceiling (see Building block economy) as a
|
||
late-game reward. The ceiling is the puzzle, the drop is the fix —
|
||
same philosophy as shortcut recipes.
|
||
- **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. Completed items are removed from this list (their outcomes live
|
||
in `requirements.md` and the git history). Still open:
|
||
|
||
1. **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.
|
||
2. **Confirm wave scaling carries alone.** With per-ship level scaling
|
||
removed, `threat_rate_formula` is the only time-scaling axis; verify
|
||
the current `world.toml` values still produce the intended difficulty
|
||
curve (likely a review, not a retune, since the config already ran
|
||
with a flat ship level before the removal).
|
||
3. **Gate shortcut-recipe drops on their inputs.** Extend the assembler
|
||
recipe schematic pool eligibility in REQ-DEF-SCHEMATIC-DROP: in
|
||
addition to the existing station-level and output-item checks, all of
|
||
the recipe's input item types must be implicitly unlocked as well.
|
||
4. **Expansion cost formula.** Replace the flat
|
||
`world.toml [expansion].cost_building_blocks` with a formula of the
|
||
number of expansions already purchased (x = expansions bought so
|
||
far), so expansion costs can escalate per the block economy rules.
|
||
Update REQ-EXP-COST and wherever the UI displays the expansion cost.
|