# Content Design — Ships, Modules & Production Tree

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

Which module fits on which hull is controlled purely by geometry — no
explicit allow-lists. Each hull grid is shaped so that it physically cannot
contain the footprint of modules from a larger size class. This keeps the
rules transparent to the player ("it doesn't fit because there is no room")
and makes them trivially moddable through the config files alone.

### Module footprint ladder

| Footprint | Modules | Smallest hull that fits it |
|-----------|---------|----------------------------|
| 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 | railgun_m, drone_bay | cruiser |
| 3x3 | railgun_l | battleship |
| 2x6 | drone_hangar | carrier (only) |

### Hull grids

`O` = buildable cell, `X` = hull structure (not buildable).

**drone (xs, 1 cell)** — exactly one 1x1 module: a small gun, a salvager, or
a repair tool. This is what makes drone roles swappable.

    O

**frigate (s, 5 cells)** — plus shape. Every 1x2 placement crosses the center
cell, so at most ONE 1x2 support fits; alternatively one L-shaped weapon
modifier or one afterburner through the center line. Gun-boat with one or two
support modules, as intended.

    XOX
    OOO
    XOX

**destroyer (s, 8 cells)** — gun deck with three turret bumps. More cells
than the frigate (more small guns), but still no 2x2 area anywhere, so medium
hardware can never be mounted.

    OXOXO
    OOOOO

**cruiser (m, 12 cells)** — notched corners. Fits at most two 2x2 m guns
(stacked through the middle), leaving the side cells for supports. No 3x3
area.

    XOOX
    OOOO
    OOOO
    XOOX

**battlecruiser (m, 16 cells)** — split bow with two gun cheeks, tapered
stern. Fits three 2x2 m guns — one more than the cruiser — with small support
slots left over. The bow split and stern taper prevent any 3x3 area (no l
gun) and any 2x6 area (no drone hangar).

    OOXXOO
    OOOOOO
    XOOOOX
    XXOOXX

**battleship (l, 24 cells)** — broadside hull with notched flanks on every
other row. Fits four 2x2 m guns (two per gun deck) — one more than the
battlecruiser — with bow, stern, and flank cells for supports. All 3x3
placements crowd the center columns, so at most ONE l gun fits: mounted
center it blocks every m gun mount (pure support strips remain), mounted
offset it still allows two m guns. The notched rows are never adjacent-and-
full, so no 2x6 drone hangar fits.

    XOOOOX
    OOOOOO
    XOOOOX
    OOOOOO
    XOOOOX

**dreadnought (xl, 36 cells)** — the main battery deck is split into three
3x3 gun slots by structural spacer columns, so exactly three l guns fit side
by side (or m guns / supports in unused slots), plus bow/stern strips for
supports. The spacers cap every horizontal run at 5 cells, so the 2x6 drone
hangar can never fit — the carrier stays the only hangar hull.

    XXXOOOOOXXX
    OOOXOOOXOOO
    OOOXOOOXOOO
    OOOXOOOXOOO
    XXOOXXXOOXX

**carrier (xl, 37 cells)** — the top flight deck (rows 0–1) is the only
region wide enough for the 2x6 drone hangar, and exactly one fits. The middle
deck row is broken up by elevator shafts (X cells placed so every 3-column
window hits one), which is what prevents any 3x3 l gun from ever fitting.
Lower decks hold supports and 2x2 point-defense m guns.

    XOOOOOOOOX
    OOOOOOOOOO
    OOXOOXOOXO
    XOOOOOOOOX
    XXXOOOOXXX

### Verified gating matrix

Checked programmatically against the configs (all four mask rotations,
all placements) with `tools/verify_layouts.py` — re-run it after editing
layout grids or surface masks:

    python dota_factory/tools/verify_layouts.py

| Footprint | drone | frigate | destroyer | cruiser | battlecruiser | battleship | dreadnought | carrier |
|-----------|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 1x1 | x | x | x | x | x | x | x | x |
| 1x2 | | x | x | x | x | x | x | x |
| 1x3 | | x | x | x | x | x | x | x |
| L-shape | | x | x | x | x | x | x | x |
| 2x2 | | | | x | x | x | x | x |
| 3x3 | | | | | | x | x | |
| 2x6 | | | | | | | | x |

Maximum simultaneous (disjoint) placements: m guns — cruiser 2,
battlecruiser 3, battleship 4; l guns — battleship 1, dreadnought 3;
drone hangar — carrier 1.

## Production tree

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

- **iron_ore, copper_ore** — from the start, minable on every asteroid
  tile. Fiction: the asteroid is an M-type (metal) body — its bulk rock
  *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
  `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 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), 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
  sci-fi anyway), volatiles/ice (materials are build costs only — no
  consumption mechanic to justify fuel).

### Weapons

- 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

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).

**Smelted (smelter — exactly one recipe per input item):**

| output | input | ratio class |
|---|---|---|
| iron_ingot | iron_ore | nice (1:1) |
| copper_ingot | copper_ore | nice |
| silicon | quartz | mid entry |
| iron_ingot | scrap | the safe, boring scrap sink |

**Reprocessing pool (scrap):** `iron_ingot`, `copper_ingot`, `silicon`,
`voidsteel` — the only source of voidsteel. Weights authored for the
fully unlocked pool state.

**Tier 2 — early intermediates (clean ratios, ~2:3):**

| item | inputs | role |
|---|---|---|
| steel_plate | iron_ingot | structure backbone, highest volume |
| copper_wire | copper_ingot | conductors |
| copper_coil | copper_wire | electromagnets: railguns, thrusters |
| building_block | steel_plate | depth-3 chain = the doubling-time knob |

**Tier 3 — mid intermediates (strange ratios begin, need quartz):**

| item | inputs | role |
|---|---|---|
| control_chip | silicon + copper_wire | electronics gate for m+ hulls |
| capacitor_bank | copper_coil + silicon | power for railgun m/l |
| hardened_steel | steel_plate (long cycle) | quality gate for m+ hulls; time-heavy |
| ceramic_plate | quartz | heat shielding: drives, l guns, capitals |
| drive_unit | steel_plate + copper_coil + control_chip | propulsion for m+ hulls |

**Tier 4 — late intermediates (need voidsteel):**

| item | inputs | role |
|---|---|---|
| voidsteel_plate | voidsteel + hardened_steel | capital structure |
| 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 **both**
hardened_steel (quality steel, the time-heavy step) *and* control_chip
(electronics):

| hull | inputs |
|---|---|
| drone_hull | iron_ingot |
| frigate_hull | steel_plate + copper_wire |
| destroyer_hull | steel_plate + copper_coil |
| cruiser_hull | hardened_steel + control_chip |
| battlecruiser_hull | hardened_steel + control_chip + drive_unit |
| battleship_hull | voidsteel_plate + drive_unit + control_chip |
| dreadnought_hull | voidsteel_plate + capital_core + drive_unit |
| carrier_hull | voidsteel_plate + capital_core + drive_unit |

**Module items** (`<module>_module`, assembler-made prefabs — kept as
items so shipyard belt inputs stay simple and module production can be
stockpiled):

| module | inputs | archetype |
|---|---|---|
| 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 |
| maneuvering_thrusters | steel_plate + copper_coil | balanced |
| sensor_booster | copper_wire + copper_coil | lean (an antenna, no chip) |
| afterburner | copper_coil + steel_plate | balanced |
| weapon_stabilizer | steel_plate + copper_coil | balanced |
| weapon_primer | capacitor_bank + copper_coil | mid; time-heavy |
| weapon_upgrade | control_chip + copper_coil | mid; time-heavy |
| railgun_m | capacitor_bank + steel_plate + copper_coil | mid |
| drone_bay | control_chip + steel_plate + copper_coil | mid |
| 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: 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 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).

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 voidsteel).
