Skip to content

Ship Systems — Upgrades, Equipment, and Loadouts

How a ship grows from a starter freighter into a specialised platform. The architecture has two distinct extension pointsShip.upgrades (linear stat bumps) and Ship.equipment_slots (modular plug-ins) — that work in parallel.

This doc complements ships.md, which lists ship types and base stats. Here we cover the customisation layer.

Source files: - services/gameserver/src/models/ship.pyShip, ShipSpecification, UpgradeType, InsuranceType, ShipStatus. - services/gameserver/src/services/ship_service.py — creation, destruction, repair, escape pod handling. - services/gameserver/src/services/ship_upgrade_service.pyUPGRADE_DEFINITIONS, EQUIPMENT_DEFINITIONS, purchase_upgrade, install_equipment.


1. The two-track architecture

1.1 Upgrades — linear stat bumps

Ship.upgrades is a JSONB column on the ships table (models/ship.py:97):

upgrades = Column(JSONB, nullable=False, default=[])

It stores {upgrade_type: current_level}. Levels are integer, start at 0, and cap at the ship-spec-defined max (ShipSpecification.max_upgrade_levels, also JSONB). Each upgrade level applies a fixed delta (e.g. +0.5 speed per Engine level).

Costs follow a geometric progression: each next level costs base_cost * cost_multiplier^current_level. So Engine L1 = 5,000 cr, L2 = 10,000 cr, L3 = 20,000 cr, etc.

Upgrades are permanent — once purchased they stay attached to that ship until the ship is destroyed.

1.2 Equipment slots — modular plug-ins

Ship.equipment_slots is also JSONB (models/ship.py:100):

equipment_slots = Column(JSONB, nullable=False, default={})

It stores {equipment_key: {effects: {...}, installed_at: ...}}. Equipment is flat (no levels), ship-type-restricted (each piece lists compatible_ships), and stacks additively across slots — ship_upgrade_service.get_equipment_effects merges all installed equipment into a single effects dict that combat / trading / movement services consult.

Equipment is removable (📐 Design-only — uninstall flow exists in ship_upgrade_service.py:425+ but resale value mechanics are not finalised).

Status: ✅ Shipped — both the upgrades and equipment_slots columns are populated by ship_upgrade_service, which exposes both purchase_upgrade and install_equipment paths. The two systems run in parallel, not as a replacement for one another.


1.5 SHIP-MODS — the unified slot-grid module system

✅ Shipped (SHIP-MODS Phase 1). The slot grid is the unified customization paradigm: each hull gets a finite, size-keyed grid of component slots (some supercharged, some class-locked) into which players bolt modules, with effects baked into the stored stat columns on install. Source: models/ship.py (Ship.modules, ShipSpecification.module_slots), services/ship_upgrade_service.py (MODULE_DEFINITIONS, bake loop), api/routes/ship_upgrades.py, and the SpaceDock slot-grid UI.

The linear upgrades (§2) and equipment plug-ins (§3) are folded into this grid as module classes; the §1 two-track columns coexist while module classes carry their effects, with class-by-class cutover handled as separate Max-gated work orders.

1.5.1 The slot grid

  • ShipSpecification.module_slots (JSONB, nullable) is the per-hull lattice: {"v":1, "cols":int, "rows":int, "slots":[{"i":int, "x":int, "y":int, "super":bool, "class":str|null, "requires":str|null}]}. A null module_slots means a hull that predates the feature — "no grid yet."
  • Ship.modules (JSONB, nullable) is the per-instance installed-module map: {"installed": {slot_index: {"class":str, "tier":int, "super_at_install":bool, "installed_at":isoZ}}}. Only class + tier + super_at_install are stored per module; effect magnitudes resolve from MODULE_DEFINITIONS at bake time, so a later magnitude re-tune does not require a migration.
  • (x, y) coordinates are stored from day one even though same-class adjacency bonuses are a later phase — adjacency becomes a behavior toggle, never a re-migration.

1.5.2 Slot counts by hull size

Slot count keys off ShipSpecification.ship_size via a literal table; which slot indices are supercharged or class-locked is hand-authored per hull for distinct identity (never computed from the size formula):

ship_size Slot count
tiny (Escape Pod) 0
small 3
medium 4
large 6
capital 8 (hand-set — CAPITAL has no size_unit, so the budget is hand-keyed, not derived)
null (NPC-only Interdictor hulls) 0 (filtered, ERR_NPC_ONLY_HULL)

The six faction-reward hulls and every future hull inherit this descriptor with zero bespoke code — a premium hull is just a spec row with a richer module_slots layout. (The Citizen Clipper is a small baseline grid plus one extra supercharged, citizen-gated maintenance slot.)

1.5.3 Supercharged slots

A module installed in a supercharged slot has its numeric effects multiplied by a flat supercharge factor, snapshotted as super_at_install at install time so a later lattice re-tune cannot retroactively change a baked module. The supercharge factor is NO-CANON (pending a DECISIONS.md ruling) and co-tunes with the tier cost/effect curve so that breadth-by-count (several Mk I modules) stays a live alternative to depth-in-a-super-slot (one high-tier module supercharged) rather than a strictly-dominant "always tier-up-and-supercharge" choice. Tier is 1-based (Mk I = tier 1).

1.5.4 The bake-on-install model

Effects are baked into the stored stat columns (Ship.combat, Ship.cargo, Ship.maintenance, Ship.current_speed, …) on install, following the baked-delta REPLACE contract: column = current − prev_baked + new_total, storing the new total in Ship.modules['_baked']. The bake loop reads MODULE_DEFINITIONS[(class, tier)] and applies the per-module base → ×supercharge → cap across the summed contribution. The grid is capped (a best-N cap on the summed module effect per class) so a bounded grid cannot re-create the "8 shield modules = god-ship" failure.

1.5.5 The P2W income fence (class-lock)

Module slots may be class-locked via the requires seam: None = open, "citizen" = Galactic-Citizen membership (lapse-safe double-checked live), {faction: tier} = a reputation gate. A citizen-gated slot is barred from the combat and income axes — a paid-membership slot may carry only cosmetic / capped-utility modules whose effects are empty, never passive_income, mining_efficiency, cargo_bonus_percent, credit_bonus, income_bonus, or trade_profit_bonus. This is the monetization firewall: paid buys shape and expression, never power or income, and a CI income-fence test asserts it. Cosmetic citizen choices write to Ship.modules.cosmetics (outside installed), so a skin never consumes a finite slot.

1.5.6 The weapons guardrail

There is no weapon_damage module class. attack_rating is fixed at hull purchase (§2.6) — to get a stronger gun, buy a stronger hull. Modules offer tactical modifiers, never raw firepower.


2. Upgrade categories

Source: services/gameserver/src/services/ship_upgrade_service.py:UPGRADE_DEFINITIONS. All eight upgrade types map to UpgradeType enum values in models/ship.py:35-43.

Upgrade Base cost Multiplier Effect per level UpgradeType enum
Engine 5,000 2.0 speed +0.5 ENGINE
Cargo Hold 3,000 1.8 cargo +30% CARGO_HOLD
Shield 8,000 2.2 max_shields +200 SHIELD
Hull 7,000 2.0 hull_points +300 HULL
Sensor 6,000 2.5 evasion +15% SENSOR
Drone Bay 10,000 2.0 drone capacity +2 DRONE_BAY
Genesis Containment 15,000 3.0 genesis capacity +2 GENESIS_CONTAINMENT
Maintenance System 6,000 2.0 failure_rate_reduction +0.15 MAINTENANCE_SYSTEM

Status: ✅ Shipped — all eight upgrade types are purchasable. MAINTENANCE_SYSTEM now has its UPGRADE_DEFINITIONS entry (base cost, geometric multiplier, failure_rate_reduction per level) plus an _apply_upgrade_effects branch that accumulates the reduction into Ship.maintenance (clamped ≤ 1.0), so it sells through the existing purchase / cost / info flow under the per-hull cap. 🚧 Partial — the cost / effect numbers are a NO-CANON kernel pending a DECISIONS.md ruling, and the effect is latent until the failure-roll consumer (still 📐 Design-only, see §2.9) lands.

2.1 Hull

Source: models/ship.py:ShipSpecification.hull_points, services/gameserver/src/services/combat_service.py.

Each Hull upgrade level adds 300 hull points. Combat damage that exceeds shields hits hull; when hull reaches 0 the ship is destroyed. Hull repair happens at stations via ship_service.repair_ship.

2.2 Engine

Source: models/ship.py:Ship.base_speed, models/ship.py:Ship.current_speed.

Each Engine upgrade adds +0.5 to Ship.current_speed. The column is consumed by Fleet.average_speed (denormalized fleet stat used by the fleet-pacing UI) and reserved for future combat-side wiring. It is not consumed by today's combat-resolver evasion or escape formulas — evasion is driven by Ship.evasion (Sensor-upgrade scaled), and escape uses ship-type / hull / sector-edge / pursuer-class factors, not speed. Speed does not reduce sector turn cost either — every ship pays the same Ship.turn_cost for a direct warp and the same WarpTunnel.turn_cost for a tunnel traversal regardless of speed. See ./movement.md for the canonical movement model and the full breakdown of what current_speed does and does not do.

2.3 Shield

Source: models/ship.py:ShipSpecification.max_shields, shield_recharge_rate.

Each Shield level adds 200 max shields. Shields regenerate per-tick at the spec's shield_recharge_rate. Upgrades do not change the recharge rate — that's set by the ship spec.

2.4 Cargo

Source: models/ship.py:ShipSpecification.max_cargo, Ship.cargo (JSONB).

Each Cargo Hold level adds +30% to base cargo capacity. Cargo holds commodities for trading (see economy/trading.md) — bigger cargo means more profit per haul.

2.5 Sensor

Source: models/ship.py:ShipSpecification.scanner_range, evasion.

Each Sensor level adds +15% evasion and +1 scanner-range sector (✅ Shipped — both the evasion bonus and the scan-range extension apply). Higher evasion = lower hit probability against the ship in combat; longer scanner range reveals more sectors per scan.

2.6 Weapons

Source: models/ship.py:ShipSpecification.attack_rating, models/ship.py:Ship.combat (JSONB).

Weapons are not in UpgradeType — they're driven by attack_rating from the ship spec, which is fixed at ship purchase. To get a stronger gun, buy a stronger ship, or install combat-class equipment (📐 Design-only — no combat equipment in EQUIPMENT_DEFINITIONS yet, only quantum_harvester, mining_laser, planetary_lander, tractor_beam).

Status: 📐 Design-only — the upgrade-vs-equipment split deliberately excludes weapons. The intent is that weapons remain a ship-class signature, with equipment offering tactical modifiers (stealth, ECM, target lock) rather than raw firepower.

2.7 Drone bay

Source: models/ship.py:ShipSpecification.max_drones, models/drone.py, services/drone_service.py.

Each Drone Bay level adds +2 drone capacity. Drones are deployable assets — attack drones, defence drones, and mines (Player.attack_drones, Player.defense_drones, Player.mines).

2.8 Genesis bay

Source: models/ship.py:ShipSpecification.max_genesis_devices, models/ship.py:Ship.genesis_devices.

Each Genesis Containment level adds +2 device slots. Only ships with ShipSpecification.genesis_compatible = true can install this upgrade (typically Carrier and Warp Jumper). See galaxy/genesis-devices.md.

2.9 Maintenance

Source: models/ship.py:Ship.maintenance (JSONB), models/ship.py:Ship.has_automated_maintenance, models/ship.py:FailureType enum.

Ships accumulate wear that produces FailureType events (NONE / MINOR / MAJOR / CATASTROPHIC). The MAINTENANCE_SYSTEM upgrade reduces the failure rate; has_automated_maintenance (boolean flag) makes maintenance run continuously without docking.

Status: 🚧 Partial — maintenance JSONB exists and the MAINTENANCE_SYSTEM upgrade is now ✅ buyable (accumulating failure_rate_reduction into Ship.maintenance, see §2). The failure-roll logic that consumes that reduction is still 📐 Design-only, so the purchased effect is latent.


3. Equipment catalogue

Source: services/gameserver/src/services/ship_upgrade_service.py:EQUIPMENT_DEFINITIONS.

Key Cost Compatible ships Effects
quantum_harvester 50,000 Scout, Fast Courier, Defender, Warp Jumper passive_income: 100 (✅ Shipped — grants 100 cr daily per installed harvester; also gates nebula Shard harvesting per ../galaxy/quantum-resources.md)
mining_laser 35,000 Cargo Hauler, Colony Ship, Defender mining_efficiency: 1.5
planetary_lander 20,000 Colony Ship, Light Freighter, Cargo Hauler landing_bonus: 1.25
tractor_beam 40,000 Cargo Hauler, Defender, Carrier, Warp Jumper tow_capable: true, weapon_mode: tractor (📐 Design-only)

Status: ✅ Shipped — first three equipment types installable. 📐 Design-only — tractor_beam (dual-use weapon + ship-tow rig — see ./ships.md#tractor-beam-tow-operations for the tow side and ./combat.md#weapons for the weapon side). The Warp Jumper's own tractor_beam is the exclusive Tractor that operates through Quantum Jump (medium-max towed ship, one per jump, +5 turns flat); other compatible ships' Tractors do not transit QJ. Additional equipment (combat ECM, stealth modules, scientific scanners, fleet command link) is also planned but not yet defined.

3.1 How equipment effects apply

ship_upgrade_service.get_equipment_effects(ship) returns a merged dict. Other services consume:

  • passive_income → granted by the idle-income job, which credits 100 cr per day for each installed quantum_harvester (✅ Shipped — daily credit-grant scheduler).
  • mining_efficiency → multiplier on raw resource yields when mining (📐 Design-only — mining gameplay loop).
  • landing_bonus → multiplier on planet-side production / colonist throughput (📐 Design-only — ties into planets/colonization.md).
  • tow_capable → enables the ship-towing operation per ./ships.md#tractor-beam-tow-operations. Movement service consults this flag plus Ship.tow_state (📐 Design-only JSONB on the hauler — see ../../DATA_MODELS/ships.md#ship-tow-state) to look up the towed ship's size and apply the per-move turn surcharge.
  • weapon_mode: tractor → registers the tractor weapon profile with the combat resolver per ./combat.md#weapons (📐 Design-only — speed-debuff + flee-suppression weapon variant).

The pipe (read merged effects → apply at use site) is in place; the use sites that consume these specific effects are mostly 📐 Design-only.


4. Insurance

Source: models/ship.py:InsuranceType (NONE / BASIC / STANDARD / PREMIUM), models/ship.py:Ship.insurance (JSONB), services/ship_service.py:_calculate_insurance_payout.

Insurance is a one-time purchase at ship commissioning time. The premium is a flat upfront fee proportional to Ship.purchase_value; coverage stays attached to the ship for its lifetime. On destruction the payout is the tier's coverage fraction of purchase_value, minus the tier's deductible. See ./ships.md#insurance for the canonical tier table and pricing.


5. Ship destruction & escape pod

Source: services/ship_service.py:destroy_ship, _ensure_escape_pod, _transfer_emergency_cargo.

When a ship is destroyed: 1. Ship.is_destroyed = true, Ship.status = ShipStatus.DESTROYED. 2. An ESCAPE_POD ship is created for the player if they don't already have one. The escape pod is a special ShipType.ESCAPE_POD — minimal cargo, no weapons, indestructible to non-PvP. 3. A small fraction of the destroyed ship's cargo (emergency cargo) transfers to the escape pod. 4. If insurance was active, the payout credits are added to Player.credits.

Status: ✅ Shipped — destruction → escape pod transition works end-to-end.


6. Ship types and loadouts

The ship types live in models/ship.py:ShipType:

ESCAPE_POD, LIGHT_FREIGHTER, CARGO_HAULER, FAST_COURIER, SCOUT,
COLONY, DEFENDER, CARRIER, WARP_JUMPER

Each has a ShipSpecification row with base stats, max_upgrade_levels (which upgrades it accepts and how high), and acquisition_methods. See ships.md for base stats; here we cover example loadouts — what a player should build toward depending on play-style.

6.1 Scout build (exploration)

Base ship: SCOUT — high speed, high evasion, low cargo, no genesis bay.

Recommended upgrades (in order): 1. Sensor L1–L3 (max evasion: +45%) 2. Engine L1–L2 (max speed boost) 3. Shield L1 (survival buffer) 4. Cargo Hold L1 (small)

Recommended equipment: quantum_harvester (passive income while exploring deep space).

Total investment to fully kit: ~155,000 cr.

6.2 Cargo Hauler build (trader)

Base ship: CARGO_HAULER — large cargo, slow, modest defence.

Recommended upgrades: 1. Cargo Hold L1–L4 (max cargo: +120%) 2. Engine L1–L2 (offset baseline slowness) 3. Shield L1–L2 (defend valuable cargo) 4. Hull L1

Recommended equipment: mining_laser (extra resource yield from sectors), planetary_lander (improved colonist trade with own planets).

Total investment to fully kit: ~450,000 cr.

6.3 Defender build (combat / escort)

Base ship: DEFENDER — balanced offense/defence, high attack rating.

Recommended upgrades: 1. Hull L1–L4 (max hull: +1,200) 2. Shield L1–L4 (max shields: +800) 3. Drone Bay L1–L3 (max drones: +6) 4. Sensor L1–L2 (improved combat evasion)

Recommended equipment: quantum_harvester (passive income while patrolling), or 📐-Design-only future combat equipment (ECM, stealth).

Total investment to fully kit: ~700,000 cr.

6.4 Colony Ship build (colonist hauler)

Base ship: COLONY — high max_colonists, modest cargo.

Recommended upgrades: 1. Cargo Hold L1–L3 2. Hull L1–L2 (lots of valuable cargo) 3. Shield L1 4. Engine L1

Recommended equipment: planetary_lander (mandatory), mining_laser.

6.5 Carrier build (fleet flagship)

Base ship: CARRIER — many drones, genesis-compatible.

Recommended upgrades: 1. Drone Bay L1–L5 (massive drone swarm) 2. Genesis Containment L1–L3 (carry multiple genesis devices) 3. Hull / Shield L1–L4 4. Cargo Hold L1–L2

Equipment slots: large; intended for end-game tactical equipment (📐 Design-only).

6.6 Warp Jumper build (cross-region)

Base ship: WARP_JUMPERquantum_jump_capable, warp_creation_capable.

Recommended upgrades: 1. Engine L1–L3 (jump cooldown reduction — 📐 Design-only effect) 2. Shield L1–L3 3. Genesis Containment L1–L2 4. Sensor L1–L2

Equipment: quantum_harvester. End-game logistics ship — fewest in number, most strategic value.

6.7 Fast Courier build (high-value cargo)

Base ship: FAST_COURIER — fast, small cargo, highly evasive.

Recommended upgrades: 1. Engine L1–L4 (max speed) 2. Sensor L1–L3 (max evasion) 3. Shield L1 4. Cargo Hold L1

Use case: hauling exotic_technology and luxury_goods on long routes where speed matters more than volume.


7. Cross-references