Restructure balancing docs into docs/balancing/
Split the cluttered progression_design.md and content_design.md into
separated documents by role:
- docs/balancing/rules.md - design rules and principles (moved from
progression_design.md, which is removed)
- docs/balancing/targets.md - the chosen base numbers: run shape,
factory curve, threat ladder (achieved
values adopted), combat anchors, pacing
anchors
- docs/balancing/derived.md - current tuned state of all derived
numbers, mirroring the configs
- docs/balancing/process.md - pass order, tuning discipline learned in
arena rounds 1-5, tools, next-round
checklist
- docs/balancing/history.md - chronological record: decisions, numbers
pass, calculator bugs, arena rounds,
pacing pass
- docs/balancing/README.md - index, status, open action items (moved
from progression_design.md)
content_design.md slims back down to actual content: footprint gating,
hull grids, gating matrix (module names updated to railguns), and the
production tree structure with its fiction - numbers, anchors, arena
logs, and pacing all moved to docs/balancing/. Stale references in the
config comments updated to the new locations.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DyCu8vwChKMbLJQ3xosYEN
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
# Content Design — Ships & Modules
|
||||
# Content Design — Ships, Modules & Production Tree
|
||||
|
||||
First real-content iterations (June 2026). Pass 1 defined ship hull grids and
|
||||
module surface masks; pass 2 defined the production tree (recipes). Stats and
|
||||
threat costs in the config files are still placeholders for the balancing
|
||||
pass.
|
||||
The designed game content: hull layout grids, module footprints and the
|
||||
gating between them, and the production tree (items, chains, fiction).
|
||||
All numbers — quantities, durations, threat values, stats, unlock levels
|
||||
— live in the config files and are documented with their derivations in
|
||||
`docs/balancing/` (see `balancing/README.md` for the index).
|
||||
|
||||
## Design principle: footprint gating
|
||||
|
||||
@@ -17,12 +18,12 @@ and makes them trivially moddable through the config files alone.
|
||||
|
||||
| Footprint | Modules | Smallest hull that fits it |
|
||||
|-----------|---------|----------------------------|
|
||||
| 1x1 | laser_cannon_s, salvager, repair_tool | drone |
|
||||
| 1x1 | railgun_s, salvager, repair_tool | drone |
|
||||
| 1x2 | maneuvering_thrusters, sensor_booster, armor_plates | frigate |
|
||||
| 1x3 | afterburner | frigate (eats most of it) |
|
||||
| L-shape (3 cells) | weapon_stabilizer, weapon_primer, weapon_upgrade | frigate |
|
||||
| 2x2 | laser_cannon_m, drone_bay | cruiser |
|
||||
| 3x3 | laser_cannon_l | battleship |
|
||||
| 2x2 | railgun_m, drone_bay | cruiser |
|
||||
| 3x3 | railgun_l | battleship |
|
||||
| 2x6 | drone_hangar | carrier (only) |
|
||||
|
||||
### Hull grids
|
||||
@@ -129,58 +130,11 @@ Maximum simultaneous (disjoint) placements: m guns — cruiser 2,
|
||||
battlecruiser 3, battleship 4; l guns — battleship 1, dreadnought 3;
|
||||
drone hangar — carrier 1.
|
||||
|
||||
## Production tree (first pass — superseded)
|
||||
## Production tree
|
||||
|
||||
**Superseded (July 2026):** this first-pass tree predates the rules in
|
||||
`docs/progression_design.md` and will be replaced. The decisions for the
|
||||
redesign are recorded in "Production tree v2 — decisions" below; the text
|
||||
of this section is kept for reference until the new tree lands.
|
||||
|
||||
Design principle: each game phase adds exactly one new base input chain, so
|
||||
factory complexity ramps alongside ship size.
|
||||
|
||||
| Phase | New input | How acquired | Unlocks |
|
||||
|-------|-----------|--------------|---------|
|
||||
| early | iron_ore, copper_ore | mined | drone, frigate, destroyer; small guns and basic supports |
|
||||
| mid | titanium_ore | mined (3x slower than iron) | cruiser, battlecruiser; m guns, drone bay, weapon modifiers |
|
||||
| late | advanced_alloy | ONLY from reprocessing salvaged scrap | battleship, dreadnought, carrier; l guns, drone hangar |
|
||||
|
||||
The advanced_alloy gate is the core loop hook: capital ship production
|
||||
requires fighting (salvaging scrap from kills and reprocessing it), not just
|
||||
mining. The reprocessing plant turns 5 scrap into iron/copper/titanium ingots
|
||||
or advanced_alloy probabilistically.
|
||||
|
||||
Intermediate components, by tier:
|
||||
|
||||
- **Tier 2 (early):** copper_wire (copper), steel_plate (iron), circuit_board
|
||||
(iron + wire), building_block (iron).
|
||||
- **Tier 3 (mid):** mechanical_parts (steel + iron), targeting_unit (circuits
|
||||
+ wire), drive_unit (steel + mechanical_parts + circuit), titanium_frame
|
||||
(titanium + steel).
|
||||
- **Tier 4 (late):** reinforced_plating (steel + advanced_alloy),
|
||||
capital_core (targeting_unit + drive_unit + 2 advanced_alloy).
|
||||
|
||||
Hulls and modules consume intermediates of their tier: early items are built
|
||||
from tier-2 parts, midgame items require tier-3 parts (deeper chains, more
|
||||
assemblers), capital items require tier-4 parts (and therefore combat). Hull
|
||||
items are named `<ship>_hull`; module items `<module>_module`. Every item has
|
||||
an `[items.*]` entry in visuals.toml; hull item outlines match the ship's
|
||||
fleet color from `[ships.*]`.
|
||||
|
||||
Consistency is checked by `tools/verify_recipes.py` — re-run it after editing
|
||||
recipes, ship/module materials, or visuals:
|
||||
|
||||
python dota_factory/tools/verify_recipes.py
|
||||
|
||||
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).
|
||||
|
||||
## Production tree v2 — decisions (July 2026)
|
||||
|
||||
The old tree is discarded; the new one is designed against the rules in
|
||||
`docs/progression_design.md` (ratio curve, cost ladder, cost archetypes,
|
||||
refactorability). Decisions fixed so far:
|
||||
Designed against the rules in `docs/balancing/rules.md` (ratio curve,
|
||||
cost ladder, cost archetypes, refactorability). Quantities, durations,
|
||||
and threat values live in `docs/balancing/derived.md`.
|
||||
|
||||
### Base inputs (4) and fiction
|
||||
|
||||
@@ -189,28 +143,27 @@ refactorability). Decisions fixed so far:
|
||||
*is* ore, which is why the shipyard operation was built here at all.
|
||||
- **quartz** — mid-game, minable only on geode deposit patches in
|
||||
expansion territory (see the Resource deposits rules in
|
||||
`progression_design.md`). Fiction: ordinary silicate dust is everywhere
|
||||
and worthless; chips and optics need rare, pocket-bound optical-grade
|
||||
crystal.
|
||||
`docs/balancing/rules.md`; the deposit mechanic itself is an open
|
||||
action item — until it lands, quartz mines anywhere). Fiction:
|
||||
ordinary silicate dust is everywhere and worthless; chips and optics
|
||||
need rare, pocket-bound optical-grade crystal.
|
||||
- **voidsteel** — late-game, obtained only by reprocessing scrap.
|
||||
Fiction: battle-forged — formed when weapon plasma anneals hull metal
|
||||
in the violence of ship destruction. Any wreck yields it, including the
|
||||
player's own; no foundry can replicate it.
|
||||
- **titanium is dropped.** Its hull-gating role moves to quartz-era
|
||||
control systems ("you can smelt all the steel you want, but you cannot
|
||||
steer a battlecruiser without electronics") and possibly a *quality*
|
||||
steel step — e.g. a long-running hardened-steel recipe (time-heavy
|
||||
archetype). Open question for the tree draft: quality step vs.
|
||||
electronics-only gating; explicitly **not** sheer steel quantity alone.
|
||||
- **titanium was dropped** (v1 tree). Its hull-gating role moved to
|
||||
quartz-era control systems ("you can smelt all the steel you want, but
|
||||
you cannot steer a battlecruiser without electronics") plus the
|
||||
hardened-steel quality step (a deliberately long-running, time-heavy
|
||||
recipe) — explicitly not sheer steel quantity alone.
|
||||
|
||||
### Material palette (fingerprints per family)
|
||||
|
||||
- **iron/steel** — structure.
|
||||
- **copper** — conduction and heat: wiring, coils, heat sinks.
|
||||
- **silicon family** (all derived from quartz): silicon (logic,
|
||||
sensors), glass/optics (lenses, focusing crystals), ceramics (heat
|
||||
shielding, insulators). Carries the non-metal variety without extra
|
||||
base inputs.
|
||||
sensors), ceramics (heat shielding, insulators); glass/optics are cut
|
||||
from v1 — their only consumers would be lasers, which are deferred.
|
||||
- **voidsteel** — capital-tier structure and exotics.
|
||||
- Deliberately skipped: carbon (mostly redundant with copper/ceramics),
|
||||
plastics (drags in Factorio-style chemical chains; ceramics read more
|
||||
@@ -219,23 +172,25 @@ refactorability). Decisions fixed so far:
|
||||
|
||||
### Weapons
|
||||
|
||||
- All current lasers are renamed to **railguns** (`laser_cannon_s/m/l` →
|
||||
`railgun_s/m/l`); footprints and the gating matrix are unchanged.
|
||||
Implementation stays as-is (instant damage application, no projectile,
|
||||
no ammunition) — the beam visual reads as a tracer round. Materials:
|
||||
iron slugs, copper coils, steel rails — the starting-metal fingerprint.
|
||||
- All v1 weapons are **railguns** (`railgun_s/m/l`, renamed from the
|
||||
laser placeholders; footprints and the gating matrix unchanged).
|
||||
Implementation is instant damage application with no projectile and no
|
||||
ammunition — the beam visual reads as a tracer round. Materials: iron
|
||||
slugs, copper coils, steel rails — the starting-metal fingerprint.
|
||||
- **Lasers are reserved for later** as a genuinely distinct weapon type
|
||||
(e.g. once projectile/ammunition mechanics exist for other families),
|
||||
arriving with quartz optics. More weapon types are planned; railguns
|
||||
are simply the baseline tech that ships with v1.
|
||||
- `drone_bay` and `drone_hangar` are footprint-only placeholders: the
|
||||
drone-launching capability does not exist in the simulation yet, so
|
||||
they define no capability section. The carrier is deliberately weak
|
||||
until that capability lands (see the accepted imbalances in
|
||||
`docs/balancing/targets.md`).
|
||||
|
||||
### Tree structure — draft
|
||||
### Tree structure
|
||||
|
||||
Recipes are sketched as input lists only; quantities and durations come
|
||||
in the numbers pass, tuned so every chain sums to its threat-ladder
|
||||
value and follows the ratio curve (t1 nice → t4 strange). Glass/optics
|
||||
are cut from v1 — their only consumers would be lasers, which are
|
||||
deferred; the silicon family ships as silicon + ceramics.
|
||||
Input lists only — quantities, durations, and per-item threat values are
|
||||
in `docs/balancing/derived.md` and the configs.
|
||||
|
||||
**Mined (miner):** `iron_ore`, `copper_ore` (every tile), `quartz`
|
||||
(geode deposits in expansion territory).
|
||||
@@ -244,7 +199,7 @@ deferred; the silicon family ships as silicon + ceramics.
|
||||
|
||||
| output | input | ratio class |
|
||||
|---|---|---|
|
||||
| iron_ingot | iron_ore | nice (1:1 or 1:2) |
|
||||
| iron_ingot | iron_ore | nice (1:1) |
|
||||
| copper_ingot | copper_ore | nice |
|
||||
| silicon | quartz | mid entry |
|
||||
| iron_ingot | scrap | the safe, boring scrap sink |
|
||||
@@ -280,14 +235,13 @@ fully unlocked pool state.
|
||||
| capital_core | voidsteel + capacitor_bank + control_chip | capital heart |
|
||||
|
||||
**Hull items** (`<ship>_hull`, assembler-made; the shipyard consumes the
|
||||
hull item plus module materials). The m+ hull gate is resolved as
|
||||
**both** of the open-question options: hardened_steel (quality steel, a
|
||||
deliberately long-running recipe — the time-heavy step) *and*
|
||||
control_chip (electronics):
|
||||
hull item plus module materials). The m+ hull gate is **both**
|
||||
hardened_steel (quality steel, the time-heavy step) *and* control_chip
|
||||
(electronics):
|
||||
|
||||
| hull | inputs |
|
||||
|---|---|
|
||||
| drone_hull | steel_plate |
|
||||
| drone_hull | iron_ingot |
|
||||
| frigate_hull | steel_plate + copper_wire |
|
||||
| destroyer_hull | steel_plate + copper_coil |
|
||||
| cruiser_hull | hardened_steel + control_chip |
|
||||
@@ -302,7 +256,7 @@ stockpiled):
|
||||
|
||||
| module | inputs | archetype |
|
||||
|---|---|---|
|
||||
| railgun_s | steel_plate + copper_coil | balanced |
|
||||
| railgun_s | copper_coil | lean |
|
||||
| salvager | steel_plate + copper_wire | balanced |
|
||||
| repair_tool | steel_plate + copper_wire | balanced |
|
||||
| armor_plates | steel_plate (many) | material-heavy, fast |
|
||||
@@ -317,395 +271,24 @@ stockpiled):
|
||||
| railgun_l | capacitor_bank + hardened_steel + ceramic_plate | late |
|
||||
| drone_hangar | voidsteel_plate + control_chip + drive_unit | late (carrier only) |
|
||||
|
||||
Refactorability check (the default technique holds): railgun_s → m
|
||||
introduces capacitor_bank, built from a subset of the small gun's
|
||||
inputs (copper_coil) plus the new base resource (silicon); the m gun
|
||||
otherwise reuses the small gun's inputs. Hulls likewise: cruiser adds
|
||||
**Refactorability check** (the default technique holds): railgun_s → m
|
||||
introduces capacitor_bank, built from a subset of the small gun's inputs
|
||||
(copper_coil) plus the new base resource (silicon); the m gun otherwise
|
||||
reuses the small gun's inputs. Hulls likewise: the cruiser adds
|
||||
hardening (fed by the existing steel line) and chips (fed by the new
|
||||
quartz territory) without touching the iron/copper core.
|
||||
|
||||
**Shortcut recipe candidates** (drop-only assembler schematics; not
|
||||
every strange chain gets one):
|
||||
**Shortcut recipes** (drop-only assembler schematics; not every strange
|
||||
chain gets one): `iron_ore → steel_plate` (skips the ingot step on the
|
||||
highest-volume chain), `quartz → control_chip` (skips silicon),
|
||||
`iron_ingot → hardened_steel` (a nicer-ratio route past the deliberately
|
||||
awkward hardening step).
|
||||
|
||||
- `iron_ore → steel_plate` — skips the ingot step on the highest-volume
|
||||
chain in the game.
|
||||
- `quartz → control_chip` — skips silicon on the electronics chain.
|
||||
- `iron_ingot → hardened_steel` — a nicer-ratio route past the
|
||||
deliberately awkward hardening step.
|
||||
Consistency is checked by `tools/verify_recipes.py` — re-run it after
|
||||
editing recipes, ship/module materials, or visuals:
|
||||
|
||||
Item count: 3 mined + scrap + 3 smelted + 4 t2 + 5 t3 + 3 t4 (incl.
|
||||
voidsteel) + 8 hulls + 14 modules ≈ 41 item types — same scale as the
|
||||
first-pass tree. `verify_recipes.py` must be re-run once this lands in
|
||||
recipes.toml; the numbers pass should add a threat-report tool that
|
||||
prints per-item threat values and producer:consumer ratio tables.
|
||||
python dota_factory/tools/verify_recipes.py
|
||||
|
||||
### Numbers — first pass
|
||||
|
||||
These numbers are live in the config files and verified by
|
||||
`tools/threat_report.py`; quantities and durations are tuned so fitted
|
||||
ships land on the threat-cost ladder and the ratio curve is realized.
|
||||
The typical loadouts are geometry-validated against the hull grids and
|
||||
configured as each ship's `default_modules`: the L-shaped weapon
|
||||
modifiers turned out not to fit alongside the full gun complement on
|
||||
the battlecruiser and dreadnought, so their loadouts use armor and
|
||||
small-railgun fillers instead — which moved all three heavy ships
|
||||
closer to their ladder targets.
|
||||
|
||||
**Economy constants:** `scrap_per_threat = 0.25` (1 scrap per 4 threat
|
||||
destroyed — a cruiser kill drops ~59 scrap). Reprocessing: 4 scrap per
|
||||
cycle, 4 s, full-pool weights iron_ingot 30 / copper_ingot 30 /
|
||||
silicon 20 / voidsteel 20 → threat(voidsteel) = (4·4 + 4)/0.2 = 100.
|
||||
Scrap smelting: 1 scrap → 1 iron_ingot, 1 s — deliberately
|
||||
value-losing (4 threat of scrap becomes a 2-threat ingot); reprocessing
|
||||
is the value-preserving path.
|
||||
|
||||
**Recipes** (dur in seconds; threat is per output item):
|
||||
|
||||
| item | recipe | dur | out | threat |
|
||||
|---|---|---|---|---|
|
||||
| iron_ore / copper_ore | miner | 1 | 1 | 1 |
|
||||
| quartz | miner (deposit) | 2 | 1 | 2 |
|
||||
| iron_ingot | 1 iron_ore | 1 | 1 | 2 |
|
||||
| copper_ingot | 1 copper_ore | 1 | 1 | 2 |
|
||||
| silicon | 1 quartz | 2 | 1 | 4 |
|
||||
| steel_plate | 2 iron_ingot | 3 | 1 | 7 |
|
||||
| copper_wire | 1 copper_ingot | 1 | 2 | 1.5 |
|
||||
| copper_coil | 2 copper_wire | 1.5 | 1 | 4.5 |
|
||||
| building_block | 2 steel_plate | 2 | 4 | 4 |
|
||||
| control_chip | 1 silicon + 2 copper_wire | 5 | 1 | 12 |
|
||||
| capacitor_bank | 2 copper_coil + 1 silicon | 5 | 1 | 18 |
|
||||
| hardened_steel | 3 steel_plate | 12 | 1 | 33 |
|
||||
| ceramic_plate | 2 quartz | 4 | 1 | 8 |
|
||||
| drive_unit | 2 steel_plate + 2 copper_coil + 1 control_chip | 8 | 1 | 43 |
|
||||
| voidsteel_plate | 1 voidsteel + 1 hardened_steel | 8 | 1 | 141 |
|
||||
| capital_core | 2 voidsteel + 1 capacitor_bank + 1 control_chip | 10 | 1 | 240 |
|
||||
|
||||
**Module prefabs** (contribution = item threat + module production
|
||||
time):
|
||||
|
||||
| module | recipe | dur | mod. time | contribution |
|
||||
|---|---|---|---|---|
|
||||
| railgun_s | 1 copper_coil | 1 | 1 | 6.5 |
|
||||
| salvager | 1 steel_plate + 2 copper_wire | 2 | 1 | 13 |
|
||||
| repair_tool | 1 steel_plate + 2 copper_wire | 2 | 1 | 13 |
|
||||
| armor_plates | 4 steel_plate | 3 | 1 | 32 |
|
||||
| maneuvering_thrusters | 1 steel_plate + 1 copper_coil | 2 | 1 | 14.5 |
|
||||
| sensor_booster | 2 copper_wire + 1 copper_coil | 2 | 1 | 10.5 |
|
||||
| afterburner | 2 copper_coil + 1 steel_plate | 3 | 1 | 20 |
|
||||
| weapon_stabilizer | 1 steel_plate + 1 copper_coil | 2 | 1 | 14.5 |
|
||||
| weapon_primer | 1 capacitor_bank + 1 copper_coil | 4 | 2 | 28.5 |
|
||||
| weapon_upgrade | 1 control_chip + 1 copper_coil | 4 | 2 | 22.5 |
|
||||
| railgun_m | 1 capacitor_bank + 2 steel_plate + 1 copper_coil | 4 | 3 | 43.5 |
|
||||
| drone_bay | 1 control_chip + 2 steel_plate + 1 copper_coil | 4 | 3 | 37.5 |
|
||||
| railgun_l | 1 capacitor_bank + 2 hardened_steel + 1 ceramic_plate | 6 | 4 | 102 |
|
||||
| drone_hangar | 1 voidsteel_plate + 2 control_chip + 1 drive_unit | 10 | 6 | 224 |
|
||||
|
||||
**Ships** (fitted = hull item + ship base time + typical loadout; the
|
||||
typical loadouts double as the `default_modules` for enemy waves):
|
||||
|
||||
| ship | hull recipe | dur | base | typical loadout | fitted (target) |
|
||||
|---|---|---|---|---|---|
|
||||
| drone | 1 iron_ingot | 1 | 1 | railgun_s | 10 (10) |
|
||||
| frigate | 2 steel_plate + 1 copper_wire | 2 | 2 | 2× railgun_s, maneuvering_thrusters | 47 (40) |
|
||||
| destroyer | 3 steel_plate + 2 copper_coil | 4 | 3 | 3× railgun_s, armor_plates, sensor_booster | 99 (80) |
|
||||
| cruiser | 2 hardened_steel + 2 control_chip | 6 | 4 | 2× railgun_m, armor_plates, maneuvering_thrusters | 234 (200) |
|
||||
| battlecruiser | 3 hardened_steel + 2 control_chip + 1 drive_unit | 8 | 5 | 3× railgun_m, armor_plates, 2× railgun_s | 355 (350) |
|
||||
| battleship | 3 voidsteel_plate + 1 drive_unit + 2 control_chip | 10 | 6 | railgun_l, 2× railgun_m, weapon_stabilizer, 2× railgun_s | 723 (700) |
|
||||
| dreadnought | 5 voidsteel_plate + 1 capital_core + 2 drive_unit | 12 | 8 | 3× railgun_l, 4× armor_plates, railgun_s | 1492 (1500) |
|
||||
| carrier | 5 voidsteel_plate + 1 capital_core + 2 drive_unit | 12 | 8 | drone_hangar, 2× railgun_m, 2× armor_plates, sensor_booster | 1436 (1500) |
|
||||
|
||||
**Checks:**
|
||||
|
||||
- *Ratio curve realized:* t1 all 1:1 (miner:smelter); t2 clean 2:3
|
||||
(ingot→plate, wire→coil); t3 strange — 2:5 (silicon→chip), 3:5
|
||||
(coil→capacitor), 3:4 (plate→hardened, plate→drive); t4 inverted 3:2
|
||||
(hardened→voidsteel_plate).
|
||||
- *Belt feasibility:* worst input demand is 1.33 items/s (wire→coil,
|
||||
plate→armor) — under the ~2/s single-belt cap everywhere; no
|
||||
accidental multi-belt recipes.
|
||||
- *Block economy:* building_block = 4 threat ⇒ for the 4-minute
|
||||
doubling at 30% capacity, the average building must cost ≈ 18 blocks
|
||||
(0.3 × 240 / 4). buildings.toml costs should be set around that mean
|
||||
(belts cheap, producers ~20–30).
|
||||
- *Small-end deviation:* frigate–cruiser land 6–24% hot because the
|
||||
fixed chain overhead dominates small hulls. Recommendation: accept
|
||||
and adjust the ladder targets to the achieved values (the ~×2-per-
|
||||
class curve shape is preserved) rather than thinning the early
|
||||
chains below readability.
|
||||
- *Rule bug discovered:* the scrap→iron_ingot smelter recipe combined
|
||||
with REQ-THREAT-ITEM's max-across-recipes rule would set
|
||||
threat(iron_ingot) to the scrap path (1 + 4 = 5, or more at other
|
||||
scrap values) instead of 2, inflating every downstream item. Fix:
|
||||
scrap-consuming recipes count toward an item's threat only when no
|
||||
scrap-free recipe produces that item, mirroring the reprocessing-path
|
||||
rule (see progression_design.md action items).
|
||||
|
||||
### Combat stat anchors — first pass
|
||||
|
||||
All combat stats derive from a few anchors so that power-per-threat holds
|
||||
by construction; the arena suite in `bin/balancing/data/balancing.toml`
|
||||
verifies it empirically. Change the anchors first, re-derive stats second.
|
||||
|
||||
- **Weapon DPS per threat pays a concentration tax that grows with gun
|
||||
size**: railgun_s 2 dmg × 2.0 Hz = 4.0 DPS at 6.5 threat (0.62),
|
||||
range 50; railgun_m 14 × 1.5 = 21 at 43.5 (0.48), range 70;
|
||||
railgun_l 52 × 0.8 = 41.6 at 102 (0.41), range 100. Arena round 1
|
||||
(see below) showed that at equal threat, concentrated fleets win
|
||||
flawlessly with a flat-ish DPS curve — concentration itself (focus
|
||||
survivability, range, no losses-to-attrition of own DPS) is worth
|
||||
paying for, so bigger guns get less raw DPS per threat.
|
||||
- **Hull HP = 15 per threat of hull contribution** (incl. ship base
|
||||
time) as the *prior*; per-hull HP is the empirical trim knob for
|
||||
arena results (guns are shared across many hulls, so gun changes move
|
||||
many matchups at once — hull HP moves exactly one). Current values
|
||||
after round-5 trims: drone 60, frigate 300, destroyer 550,
|
||||
cruiser 1500, battlecruiser 2400, battleship 6300,
|
||||
dreadnought/carrier 24000. (The capital hulls sit well above the
|
||||
15/threat prior while their guns sit below the DPS prior — the arena
|
||||
consistently prices big ships as tanks with taxed guns.)
|
||||
- **Armor HP ≈ 37 per threat** — a strong premium over hull HP (15)
|
||||
because armor is pure HP with no capability, and fights snowball:
|
||||
killing enemies removes their DPS, while surviving merely delays —
|
||||
so a point of HP must be cheaper than a point of DPS.
|
||||
armor_plates adds 1200 HP at 32 threat (raised from 640 → 1000 →
|
||||
1200: the all-guns destroyer beat the armored one in three
|
||||
consecutive arena rounds).
|
||||
- **TTK check:** a mirror cruiser duel (51 DPS vs 2140 EHP) lasts ~42 s —
|
||||
inside the 30–60 s parity-fight band implied by the 25-ship fleet
|
||||
target. Capital mirrors run longer (~90 s), deliberately.
|
||||
- **Repair ≈ 0.7 HP/s per threat:** repair_tool heals 9 HP × 1 Hz at 13
|
||||
threat (nerfed 25 → 12 → 9: in-combat sustain effectively removes
|
||||
enemy DPS and must be priced like DPS, not like HP — the escorted
|
||||
team held a persistent +24% at 12). Salvager: collection range 60 (was
|
||||
a nonsensical 500, beyond any sensor), cargo 20, 0.5 collections/s.
|
||||
`scrap_despawn_seconds` raised 30 → 120: a capital kill drops
|
||||
hundreds of scrap collected one per cycle, so 30 s despawned nearly
|
||||
everything.
|
||||
- **Mobility ladder is monotone in size** — small = fast and nimble
|
||||
(drone 45 m/s, 60 accel) down to capitals (10 m/s, 8 accel); the old
|
||||
placeholder had the drone as the least agile ship. Sensor ranges keep
|
||||
the existing 150→350 ladder; all weapon ranges stay below sensor
|
||||
ranges.
|
||||
- **Weapon modifiers are capital economy:** ×1.2 damage at 22.5 threat
|
||||
beats adding a gun once a ship carries more than ~68 threat of weapons
|
||||
— so modifiers pay off on gun-heavy big hulls and are a waste on small
|
||||
ones. Damage/rate kept at ×1.2; the stabilizer's range multiplier was
|
||||
cut ×1.5 → ×1.3 after round 2 — range is the strongest stat in the
|
||||
orbit-AI's hands (free approach fire), and the stabilized battleship
|
||||
was the one loadout still overperforming.
|
||||
- **Stations:** a fresh player station (3000 HP, 25 dmg × 1 Hz,
|
||||
range 120) holds one early parity wave unaided; the enemy station at
|
||||
level 0 matches it exactly and scales per push level
|
||||
(+1500 HP, +12 dmg, +0.1 Hz per level). HQ 5000 HP. Range reduced
|
||||
from 200 in round 1: at 200 (4× a small gun's range) two stations
|
||||
annihilated a 3× threat swarm through approach fire alone — range,
|
||||
not HP, is the station dominance lever.
|
||||
- **Known imbalance, accepted:** the carrier loses its equal-threat
|
||||
arena until the drone-launching capability exists — the hangar is 224
|
||||
threat of dead weight. Do not compensate with stats; fix by
|
||||
implementing drones.
|
||||
|
||||
**Arena round 1 results (2026-07-04):** mirrors snowball as expected
|
||||
(40–50% of the winner survives) but team 1 won all three mirrors —
|
||||
possible sim-side bias, under investigation (progression_design.md
|
||||
action items 6–7). All four equal-threat cross-tier matchups were
|
||||
flawless wins for the concentrated side; glass destroyers beat armored;
|
||||
repair escort beat raw numbers flawlessly; two stations shrugged off a
|
||||
3× threat swarm. Round-2 knob changes: concentration tax on m/l gun
|
||||
DPS (17→14, 70→52 damage), armor 640→1000, repair 25→12, station range
|
||||
200→120. Gun ranges deliberately untouched — if concentrated fleets
|
||||
still win flawlessly in round 2, the range ladder (kiting) is the next
|
||||
suspect, ahead of further DPS cuts.
|
||||
|
||||
**Arena round 5 results (2026-07-04, with fight durations) — combat
|
||||
pass converged:** durations validate the TTK anchor: drone mirror
|
||||
22.7 s, cruiser mirror 71.2 s, battleship mirror 94.6 s, most fights
|
||||
in the 22–72 s band; the destroyers-vs-dreadnought slugfest is the
|
||||
outlier at 214 s (extreme tank vs. chip damage — acceptable for the
|
||||
extreme case). Converged: mirrors 5–10%, swarm vs cruisers +15%,
|
||||
frigates vs battleship dropped to +13% on its own (confirming the
|
||||
noise-floor read — now inside band anyway), dreadnought at +3% knife
|
||||
edge, glass vs armored +10%, two-to-one 78%, station assault stable at
|
||||
44%. Two two-round signals actioned in round 6: mixed beat
|
||||
battlecruisers again (+20%, +18%) → battlecruiser hull 2200→2400;
|
||||
repair escort held +24% twice with all frigates surviving →
|
||||
repair_amount 12→9. Everything else is frozen; further refinement
|
||||
moves to real-game playtests.
|
||||
|
||||
**Arena round 4 results (2026-07-04) and convergence policy:** mirrors
|
||||
at 0–3%; swarm vs cruisers +7%; glass vs armored resolved at +3% for
|
||||
armored (armor 1200 confirmed after four rounds of signal). Noise
|
||||
floor established: the battleship held +23% straight through a −10%
|
||||
EHP cut, and repair drifted 14→24% with no repair changes — single-run
|
||||
margins move ~±10% on a re-roll. Policy from here: only act on signals
|
||||
that persist two rounds; treat anything inside ±20% as converged for
|
||||
v1 (real-game playtests refine further). Armor-coupling overshoots
|
||||
corrected in round 5: battlecruiser 2000→2200, dreadnought/carrier
|
||||
22500→24000 (two-round persistent deficit vs destroyers). **Accepted,
|
||||
not chased:** the stabilized battleship beats a pure frigate fleet by
|
||||
~23% — the edge survived a stat cut, so it is mechanical (range), and
|
||||
"the extreme smallest-ship fleet modestly loses to a range-fitted
|
||||
capital" is desirable doctrine texture; the fair anti-capital answer
|
||||
is the mixed fleet, whose arena is balanced.
|
||||
|
||||
**Arena round 3 results (2026-07-04, narrow lanes):** arenas were
|
||||
narrowed (height 10) so fleets can no longer pass each other — arena
|
||||
geometry is part of the fixture from here on; margins are only
|
||||
comparable within the same geometry. Results: mirrors healthy (1–7%);
|
||||
dreadnought closed to −11%; battleship improved to +22% (still over);
|
||||
drone swarm now beats cruisers by 14% (full engagement in the narrow
|
||||
lane favors numbers); mixed vs battlecruisers +18%; glass beat armored
|
||||
for the third consecutive round (+12%); repair 14%, two-to-one 78%,
|
||||
station set cracked at 51% by the 3× swarm (geometry changed under the
|
||||
stations — watch before touching stats). Round-4 knob changes:
|
||||
armor_plates 1000→1200 (the persistent glass signal; also lifts the
|
||||
armor-carrying cruiser/BC/destroyer/DN loadouts), battlecruiser hull
|
||||
2500→2000 (net −300 EHP after its armor gain), battleship 7000→6300
|
||||
(no armor in its loadout, clean −10%), dreadnought/carrier
|
||||
19000→22500 (the opposing destroyers gain armor too, so the DN needs
|
||||
the larger step).
|
||||
|
||||
**Arena round 2 results (2026-07-04, with EHP-margin logging):** the
|
||||
mirror "bias" was noise — repeated runs split randomly, and winners
|
||||
finish at 5–7% EHP (near-mutual annihilation, healthy). Drones vs
|
||||
cruisers 10% and mixed vs battlecruisers 14% — near parity. The two
|
||||
capital fights diverged: battleship beat frigates at +33% while the
|
||||
dreadnought lost to destroyers at −37% — same guns, so the difference
|
||||
is the destroyers carrying the buffed armor plus the battleship's
|
||||
stabilizer putting all its guns at 75–150 m against 50 m opponents.
|
||||
Glass vs armored narrowed to 11% (watch; if it persists, armor
|
||||
1000→1200). Repair escort 11% — fair. Two-to-one decisive at 80%;
|
||||
station assault cracked by the 3× swarm at 13% (stations strong but
|
||||
breakable). Round-3 knob changes: stabilizer range ×1.5→×1.3, and
|
||||
per-hull HP trims — battlecruiser 2700→2500, battleship 7500→7000,
|
||||
dreadnought/carrier 15500→19000.
|
||||
|
||||
### Pacing pass — first values
|
||||
|
||||
The mapping between station levels and boss cycles: at the intended
|
||||
push cadence (first set around cycle 2–3, roughly one per cycle from
|
||||
mid on), the destroyed set's level ℓ is reached around cycle ℓ+2. So
|
||||
level 2 ≈ the mid boundary, level 6 ≈ mid-late, levels 8–9 ≈ capitals
|
||||
with enough cycles left to ramp voidsteel production before late.
|
||||
|
||||
**Starting set** (`unlock_at_station_level = -1`), per the starting-set
|
||||
rule (exactly enough to reach the first push): drone, frigate,
|
||||
railgun_s, salvager. Note: the building_block recipe must carry an
|
||||
explicit `-1` — building blocks appear in no schematic's materials, so
|
||||
implicit unlocking can never reach it (fixed in this pass; it was
|
||||
silently locked).
|
||||
|
||||
**Unlock ladder** (level → unlocks; ← marks `unlock_requires`):
|
||||
|
||||
| level | ships | modules | recipes |
|
||||
|---|---|---|---|
|
||||
| 0 | destroyer | repair_tool, armor_plates | |
|
||||
| 1 | | maneuvering_thrusters, sensor_booster | shortcut_steel_plate |
|
||||
| 2 | cruiser | railgun_m, afterburner | shortcut_control_chip, shortcut_hardened_steel |
|
||||
| 3 | | weapon_stabilizer | |
|
||||
| 4 | battlecruiser ← cruiser | weapon_primer, weapon_upgrade | |
|
||||
| 5 | | drone_bay | |
|
||||
| 6 | battleship ← battlecruiser | railgun_l ← railgun_m | |
|
||||
| 8 | dreadnought ← battleship | | |
|
||||
| 9 | carrier ← battleship | drone_hangar | |
|
||||
|
||||
Level 0's pool has exactly three entries, so the first push offers a
|
||||
full dialog with no artifacts competing. Level 2 is the quartz gate:
|
||||
cruiser and railgun_m are the first schematics whose chains reach
|
||||
quartz (the shortcut recipes' outputs only become implicitly unlocked
|
||||
alongside them, so they cannot drop early).
|
||||
|
||||
**Threat rate** `2*x + 0.15*x*x`, tuned against the factory-size curve
|
||||
with roughly half the player's output assumed military:
|
||||
|
||||
| cycle x | rate (threat/s) | player military (≈ curve/2) |
|
||||
|---|---|---|
|
||||
| 2 | 4.6 | ~12 |
|
||||
| 6 | 17.4 | ~30 |
|
||||
| 15 | 63.8 | ~60 |
|
||||
| 20 | 100 | ~75 |
|
||||
| 24 | 134 | — |
|
||||
|
||||
Below the player early, crossing at the late boundary, overwhelming by
|
||||
~24 — the endless-race shape from the progression rules. Typical wave
|
||||
at x=6 (30 s gap): ~520 threat ≈ two cruisers plus escorts; boss at
|
||||
x=15: ~3800 threat ≈ two and a half dreadnoughts.
|
||||
|
||||
**Economy**: `starting_building_blocks` 1000 → 200 (minimal block loop
|
||||
+ first shipyard ≈ 150, plus beginner slack); expansion cost 200 → 400
|
||||
flat (placeholder until the escalating formula, action item 4);
|
||||
`artifact_win_count` 3 → 5 with chance `0.05*x`: ~15–18 pushes in a
|
||||
winning run offer ~7 artifact options cumulatively, so winning takes
|
||||
choosing the artifact over a schematic five times — a real decision
|
||||
each time, and the win lands in the cycle 20–24 target window.
|
||||
|
||||
## 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 (quartz, medium hulls), late = cycles 15+
|
||||
(voidsteel, 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
|
||||
them yet (WaveSystem treats any ship with positive threat cost as wave-
|
||||
eligible, regardless of unlock level). The balancing pass should set real
|
||||
threat costs together with `default_modules` loadouts so waves spawn them
|
||||
armed.
|
||||
- All new hulls and all assembler recipes are `unlock_at_station_level = -1`
|
||||
(available from the start) to make testing easy; the balancing pass should
|
||||
stagger these so mid/lategame recipes drop as schematics from enemy defence
|
||||
stations.
|
||||
- Recipe quantities and durations are a first guess, deliberately roughly
|
||||
tiered (capital hulls ~60 s, drones 4 s); the balancing pass tunes them.
|
||||
- `drone_bay` and `drone_hangar` are footprint-only placeholders: the drone
|
||||
launching capability does not exist in the simulation yet, so they define
|
||||
no capability section.
|
||||
- Renames in this pass: `laser_cannon_xs` → `laser_cannon_s` (the old 2x2
|
||||
`laser_cannon_s` became `laser_cannon_m`), `armor_plate` → `armor_plates`,
|
||||
`manuvering_thrusters` → `maneuvering_thrusters` (typo fix). Test data
|
||||
under `bin/test/data/config` intentionally still uses the old ids — it is
|
||||
an independent fixture set.
|
||||
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 voidsteel).
|
||||
|
||||
Reference in New Issue
Block a user