Restructure balancing docs into docs/balancing/
This commit is contained in:
333
docs/balancing/rules.md
Normal file
333
docs/balancing/rules.md
Normal file
@@ -0,0 +1,333 @@
|
||||
# Balancing & Progression Rules
|
||||
|
||||
Rules and principles that govern the production tree, progression pacing,
|
||||
and balancing. This document contains **rules only** — the chosen base
|
||||
numbers live in `targets.md`, everything derived from them in
|
||||
`derived.md`, and the concrete content in the config files and
|
||||
`../content_design.md`. All of those 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 resources available from the start, minable on **every**
|
||||
asteroid tile.
|
||||
2. A third mined resource unlocked mid-game, minable **only on deposit
|
||||
patches** found in expansion territory (see Resource deposits).
|
||||
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.
|
||||
- Every gating has a fictional reason (concrete fiction in
|
||||
`../content_design.md`): the asteroid is a metal-rich body, so its bulk
|
||||
rock is minable anywhere; the mid resource sits in rare pockets; the
|
||||
late input is battle-forged — created only in the violence of ship
|
||||
destruction, which is why any wreck (including the player's own)
|
||||
yields it and no foundry can make it.
|
||||
- The mid resource is **dual-gated**: schematics (knowledge, via drops)
|
||||
and territory (deposits, via expansions). Tuning must guarantee the
|
||||
deposit-bearing expansion is comfortably affordable by the time the
|
||||
first mid-tier schematics drop, or those drops are dead picks.
|
||||
- There is no direct "resource unlock" mechanism. Miner recipes unlock
|
||||
**implicitly** (REQ-LOCK-IMPLICIT) when some unlocked schematic's material
|
||||
chain reaches that resource. Resource pacing is therefore controlled
|
||||
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 mid resource, or it never unlocks).
|
||||
|
||||
### Resource deposits
|
||||
|
||||
- **Rule: freedom first, geography later.** The starting resources are
|
||||
minable everywhere, so the player has full layout freedom while
|
||||
learning. Later mined resources are bound to deposit patches — fixed
|
||||
geography as a layout puzzle, introduced once the player is competent.
|
||||
- **Rule: deposits exist only in expansion territory.** Expansions buy
|
||||
space *and* access to resource tiers — the second leg of the growth
|
||||
curve (see Building block economy).
|
||||
- **Rule: patch area is the throughput cap.** Deposits never deplete but
|
||||
are finite in area; the number of deposit tiles caps how many miners
|
||||
the chain supports. Buying deeper expansions raises the throughput
|
||||
ceiling of high-tier chains.
|
||||
- **Rule: no empty expansions.** Deposit content per expansion is
|
||||
deterministic and config-defined; only the placement within the new
|
||||
columns is randomized. Buying an expansion never rolls "nothing".
|
||||
- **Rule: mining is binary.** A miner whose footprint overlaps at least
|
||||
one deposit tile of a resource can select that resource's recipe; no
|
||||
partial-coverage rate scaling.
|
||||
- Deposits arrive at the periphery (expansions add columns on the left),
|
||||
so each new chain starts in fresh space — supporting the
|
||||
refactorability property — and high-tier chains have the longest belt
|
||||
runs to the shipyards, escalating the logistics puzzle with tier.
|
||||
|
||||
## 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 resource or the scrap-only input. The early game starts with two
|
||||
ores as the baseline; the transition to mid adds one (the deposit-bound
|
||||
mid resource), 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 an open action item (see
|
||||
`README.md`).
|
||||
- **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
|
||||
gun requires the small gun; 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 Future
|
||||
work), 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 Future work).
|
||||
|
||||
## 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.
|
||||
|
||||
## Future work
|
||||
|
||||
- **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.
|
||||
Reference in New Issue
Block a user