Skip to content

Planetary Defense

A multi-layered system protecting colonies. Five engagement layers, each with its own buildings, costs, and effective targets.

Engagement order

When a hostile fleet attacks a planet:

  1. Long-range scanner detects approach (1–2 sectors out).
  2. Orbital defense platforms open fire from 2 sectors away.
  3. Fixed rail gun batteries engage in planetary orbit (1 sector).
  4. Defense drones swarm.
  5. Shield generators absorb damage to citadel.
  6. Citadel structure — final layer; if destroyed the planet is permanently lost (population dies, production gone, safe contents preserved for the owner).

Per-unit defense pricing ✅ Shipped

Turrets, shield units, and fighter/drones are bought by the count. Each unit's price scales with citadel level (stronger defenses cost more) and planet type (harder-to-hold worlds cost more to fortify):

per_unit_price = base × citadelMultiplier × planetTypeModifier   // rounded to nearest 10 cr

Base price per unit — the citadel-L1, baseline-type anchor:

Unit Base price
Turret 500
Shield unit 1,000
Fighter / drone 2,000

Citadel multiplier — citadel level gates both the max count and the unit strength, so price rises with the tier you buy into; back-loaded so fully fortifying an L5 citadel is a serious credit sink:

Citadel level Multiplier
1 ×1.0
2 ×1.25
3 ×1.6
4 ×2.2
5 ×3.0

Planet-type modifier — cheaper to fortify a naturally defensible world; a 0.75–1.5 swing so planet type meaningfully shapes the bill:

Planet type Modifier
Terran / Oceanic ×0.75
Mountainous / Arctic ×1.0
Desert / Volcanic ×1.25
Gas / Barren ×1.5

Worked examples: an L1 turret on a Terran world costs 500 × 1.0 × 0.75 = 375, rounded to 380 cr; an L5 turret on a Gas world costs 500 × 3.0 × 1.5 = 2,250 cr; an L5 fighter/drone on a Gas world costs 2,000 × 3.0 × 1.5 = 9,000 cr/unit.

Citadel level is what unlocks higher tiers; this table prices the units you then install. Genesis pre-installs remain free (see genesis-devices.md). The server computes price from this table at purchase time and the affordability gate is checked against it.

Defense drones

Citadel level Max drones
1 10
2 25
3 50
4 100
5 200
  • Cost: per the per-unit defense pricing table — base 2,000 cr/drone × citadel × planet-type modifiers, bought at military stations.
  • Planet-deployed drones get +5% effectiveness vs ship-mounted.
  • Drone health 0–100; 100 cr to repair, 1,000 cr to replace.
  • Drones engage all ship sizes equally.

Drone deployment lifecycle

Drones exist in two complementary representations:

  • Scalar counters — fast aggregate totals used for defense-rating math and UI summaries. Player.attack_drones and Player.defense_drones (integers) hold a player's owned drone counts; Planet.defense_fighters (integer) is the planet's drone-equivalent garrison (the Planet model has no defense_drones column, so fighters fill that role). These are what the defense overview panel and the planet defense API surface.
  • Per-unit ORM model — each individual drone is also a Drone row (models/drone.py), owned by a player (and optionally a team), with its own health, level, combat stats, current sector_id, and a status field. A separate DroneDeployment row records each deploy/recall as deployment history with is_active, deployed_at, and recalled_at.

Each per-unit drone moves through a DroneStatus lifecycle (models/drone.py):

Status Meaning Entered when
IDLE Created, not deployed anywhere (default on creation) create_drone
DEPLOYED Assigned to a sector and on station deploy_drone (also restored after a non-fatal combat)
COMBAT Currently engaged with another drone initiate_combat
RETURNING Recalled, leaving its sector (sector_id cleared) recall_drone
DAMAGED Health dropped below 30% of max take_damage threshold
DESTROYED Health reached 0 (terminal; destroyed_at set) take_damage to 0

Transitions:

  • Default on creation is IDLE.
  • deploy_drone sets DEPLOYED, stamps sector_id and deployed_at, and opens a DroneDeployment (any prior active deployment is recalled first). A destroyed drone cannot be deployed.
  • recall_drone closes the active deployment (is_active = False, recalled_at set), clears sector_id, and sets the drone to RETURNING (unless it is DESTROYED).
  • Combat sets both participants to COMBAT; survivors return to DEPLOYED, the loser becomes DESTROYED.
  • repair lifts a DAMAGED drone back to DEPLOYED once health rises above the 30% threshold; a DESTROYED drone cannot be repaired or deployed.

Per-unit endpoints (api/routes/drones.py, prefix /drones):

  • POST /drones/{drone_id}/deploy — deploy one drone to a sector (body: sector_id, optional deployment_type, target_id); returns the DroneDeployment.
  • POST /drones/{drone_id}/recall — recall one drone by drone id.
  • Companion routes: POST /drones/ (create), POST /drones/{id}/repair, POST /drones/{id}/upgrade, POST /drones/combat/initiate, and the bulk client-contract routes POST /drones/deploy, GET /drones/deployed, DELETE /drones/{deploymentId}/recall.

Shield generators

10 levels, each with strength and regen.

Level Shield strength
0 0
1 1,000
2 2,500
3 5,000
4 10,000
5 15,000
6 20,000
7 30,000
8 40,000
9 50,000
10 75,000
  • Regen: 10%/hour at peace, 5%/hour during combat.
  • Upgrade costs scale steeply (10k cr → 1.5M cr per step); cumulative L0→L10 ≈ 5.235M cr + 523.5k equipment.

Per-step shield upgrade ladder (📐 Design-only — shield_generator levels 4+ not yet in citadel_service.DEFENSE_BUILDINGS):

From → To Credit cost Equipment Citadel prereq Build hours
L0 → L1 10,000 1,000 L1 6
L1 → L2 25,000 2,500 L2 12
L2 → L3 50,000 5,000 L2 18
L3 → L4 100,000 10,000 L3 24
L4 → L5 200,000 20,000 L3 30
L5 → L6 400,000 40,000 L4 36
L6 → L7 700,000 70,000 L4 42
L7 → L8 1,000,000 100,000 L5 48
L8 → L9 1,250,000 125,000 L5 54
L9 → L10 1,500,000 150,000 L5 60

Orbital defense platforms

  • Citadel level 4+ required.
  • Capacity: 1 platform at L4, 3 platforms at L5 (max).
  • Cost: 500,000 cr + 50,000 ore + 25,000 equipment per platform.
  • Build time: 168 hours (7 days).
  • Range: 2 sectors out — engages before attackers reach the planet.
  • Targeting priority: largest ships first (Carriers, Colony Ships).
  • Fire rate: 1 burst / 30s; damage 500.
  • Platform health: 5,000 hull + 2,500 shields (regen 10%/hr).

Upgrades: - Enhanced Targeting (+25% accuracy) — 200,000 cr. - Increased Power (+50% damage) — 300,000 cr. - Advanced Shields (+2,500 shields) — 250,000 cr. - Rapid Fire (30s → 20s) — 400,000 cr.

Fixed rail gun batteries

Status: 🚧 Partial — rail_gun_battery is in citadel_service.DEFENSE_BUILDINGS (catalogued + buildable, with the per-ship-size multiplier table carried as metadata). The combat path that injects its raw-burst damage through the multiplier table is not yet wired — the resolver's 1–5 scale defers raw-burst injection (see combat-resolver integration).

  • Citadel level 4+ required.
  • Capacity: 4 batteries at L4, 10 at L5 (max).
  • Cost: 150,000 cr + 20,000 ore + 10,000 equipment per battery.
  • Build time: 72 hours (3 days) per battery.
  • Range: planetary orbit only (1 sector).
  • Fire rate: 1 shot / 60s; damage 1,000–3,000 before size multipliers.

Damage by ship size:

Ship class Damage %
Carrier 200%
Colony Ship 200%
Cargo Hauler 150%
Defender 120%
Light Freighter 50%
Warp Jumper 25%
Fast Courier 15%
Scout Ship 10%

This makes rail guns a specialized anti-capital weapon. Small/fast ships shrug them off; large ships take heavy damage.

Upgrades: - Predictive Targeting (+20% vs large) — 100k cr per battery. - Armor-Piercing Rounds (ignore 50% shields) — 150k cr per battery. - Rapid Reload (60s → 45s) — 200k cr per battery.

Combat-resolver integration

Planet-defense weapons compose with the canonical 5-step damage stack from ../../SYSTEMS/combat-resolver.md. Each round, defense weapons fire before attacker ship weapons:

✅ Shipped — defense-building combat contribution (WO-CT1 c73307f, proven live 2026-06-20). Built turret networks and orbital platforms reduce attacker effectiveness in planet combat through _calculate_planetary_defense_reduction: each turret −3% damage (cap −18%) plus per-round anti-drone point defense (1–3 kills/turret, capped at 18); each orbital platform −6% (cap −18%) plus 250 armour HP. The combined reduction is ADR-0087-scaled and hard-capped at 0.90; a planet with no defense_buildings contributes nothing. The full ship-size-multiplier raw-damage model below (orbital 500/burst, rail-gun 1500×table) stays 📐 Design-only — the resolver's 1–5 scale would instakill, so raw-burst injection is a scoped resolver redesign.

  1. Phase 1 (orbital approach, 2-sector range): orbital defense platforms fire if attacker is within 2 sectors.
  2. Phase 3 (orbital engagement, 1-sector range): rail guns fire if attacker is within 1 sector. Orbital platforms continue to fire.
  3. Attacker weapon resolution: player ship weapons fire next, applying the standard damage stack against the defender (orbital platform → rail gun → citadel hull, in that target priority).

Damage path per defense weapon:

base_damage = weapon.attack_rating × ship_class_multiplier[attacker.ship.type]
   // rail gun: 1500 × multiplier from table above
   // orbital platform: 500 (uniform across ship classes)

final = damage_stack(
  base_damage,
  defender_rank_bonus = planet.commanding_player.combat_rank_bonus,
  shield_hit, armor_modifier, drone_modifier,    // standard 5-step stack
  critical_roll, hull_update
)

The defending planet's commanding player rank-bonus is applied (not the attacker's). Subsequent steps (shield hit, armor + drone modifier, critical, hull update) follow the canonical resolver.

Platform availability states:

State Cause Round behavior
Operational Hull > 0, not suppressed Fires fully
Suppressed Attacker EMP damage ≥ platform.current_shields this round Skips turn (re-evaluates next round when shields regen or suppression lifts)
Destroyed Hull ≤ 0 Permanently offline this engagement

Orbital platforms and rail guns are independent — destroying one does not affect the others. The 5-round engagement cap from combat-resolver.md Phase 3 still applies; if all attackers are destroyed or escape, defense weapons cease fire.

Shield generators do not fire; they absorb damage to the citadel hull. Once all orbital weapons are destroyed, the citadel structure becomes the final target.

Auxiliary defensive buildings

Defense grid

Status: 🚧 Partial — planetary_defense_grid is in citadel_service.DEFENSE_BUILDINGS (catalogued + buildable, carrying the drone-damage modifier as metadata). The combat path that applies the +15% drone-damage modifier is not yet wired.

  • Citadel level 3+, 200,000 cr + 15,000 equipment, 96h.
  • +15% drone damage and accuracy. Upgradable to L2 (+25% total) for 300k cr.

Scanner array

  • Citadel level 2+, 75,000 cr + 10,000 equipment, 48h.
  • Capacity: 1 at L2, 1 at L3, 2 at L4–L5.
  • Effect: 1-sector early warning; advanced upgrade extends to 2 sectors.
  • Upgrade: Advanced Scanner — 200,000 cr.

Mine fields

Status: 📐 Design-only — distinct from ship-deployable mines (Sector.defenses.mines JSONB on the sector layer). Planet-deployed minefields ring the orbital approach and are tied to the planet, not the sector.

  • Citadel level 3+, 100,000 cr + 10,000 equipment per minefield, 48h build.
  • Capacity: 1 minefield at L3, 2 at L4, 3 at L5.
  • Each minefield carries 20 mines (2,000 cr each), replenished automatically while the planet has equipment in stockpile (rate: 1 mine restored per hour).
  • Mines target incoming hostiles only (not landed friendlies, not orbiting team-mates); deal 500–1,500 hull damage per mine impact, ignore shields.
  • Destroyed when the minefield is destroyed (5,000 hull / 0 shields per minefield).

Automated turret network ✅ Shipped

  • Citadel level 3+ for player-built turrets.
  • Cost: 150,000 cr + 8,000 equipment; 72h build.
  • Capacity: 2 at L3, 4 at L4, 6 at L5.
  • Each turret destroys 1–3 attacking drones per round.

Genesis pre-install exception (✅ Shipped). Advanced Genesis seed colonies bypass the L3 prereq for the 4 turrets they ship with — these are operator-grade pre-installs baked into the seeded defense_buildings, not player-constructed buildings, and they exist on the L2 starting citadel by design. Player-driven turret construction at the same colony still requires citadel L3 once the colony grows past Settlement.

✅ Fixed (Phase 1, 2026-06-16, dev da8139c). The advanced-tier genesis formation path now writes the initial 4 turrets into Planet.active_events["defense_buildings"]["turret_network"] at L2 (alongside the flat defense_turrets column), so they count in citadel_service's defense system independent of the L3 prereq — proven live (defense_buildings {} → {turret_network: 4}).

Siege

A siege is the persistent state imposed on an owned planet when hostile forces hold the surrounding sector for SIEGE_TURNS_THRESHOLD = 3 consecutive turns.

A siege turn is one canonical day: SIEGE_TURN_HOURS = 24 canonical hours, advanced lazily on read (the same incremental-tick model the terraforming and warp-gate clocks use — siege stages escalate when the planet is next observed, not on a wall-clock scheduler).

State on Planet: under_siege (bool), siege_started_at (datetime), siege_attacker_id (uuid), siege_turns (int).

While under_siege is true:

  • Production penalty: all production rates ×0.75 (SIEGE_PRODUCTION_PENALTY = 0.25 in planetary_service.py). Applied inside _calculate_production_rates.
  • Colonist growth halted: colonist rate forced to 0 each tick.
  • Morale loss: SIEGE_MORALE_LOSS_PER_TURN = 5 percentage points per turn, mitigated by 0.05 × defense_level (each defense level reduces the per-turn morale loss).
  • Defense damage reduction: each defense_level adds DEFENSE_DAMAGE_REDUCTION_PER_LEVEL = 0.05 (5%) damage reduction to citadel structure during siege resolution. At the max defense_level of 10 this caps at 50% reduction, so a citadel is never fully invulnerable.
  • Vulnerability penalty: the besieged planet's effective defenses are multiplied down for as long as a qualifying besieger holds the sector. Vulnerability is a presence-gated multiplier, not a binary capture flag — it rewards maintaining presence rather than a one-shot timer the attacker can game. The penalty expires the moment the siege is lifted (the besieger leaves, is destroyed, or withdraws).
  • Resource theft: a fraction of generated commodities should transfer to the besieger. 📐 Design-only — penalty is applied but no transfer.

A siege lifts when no hostile ships remain in the sector for the detection window (lift_siege in planetary_service.py); lifting clears the vulnerability penalty and stops siege-turn advancement.

Defensive strength examples

All examples assume the design naming progression (Outpost / Settlement / Colony / Major Colony / Planetary Capital).

L2 Settlement (traditional): 25 drones (50k cr) + Level 1 shields (10k cr) ≈ 60k investment → defense rating ~15/100.

L2 Settlement (Advanced Genesis start): 25 drones + L1 shields + 4 turrets included → ~25/100.

L4 Major Colony, fortified: 100 drones (200k) + L6 shields (~785k) + 1 orbital platform (500k) + 4 rail guns (600k) + L2 defense grid (500k) + advanced scanner (200k) + 4 turrets (600k) ≈ 3.385M cr → ~70/100.

L5 Planetary Capital, fortress: 200 drones (400k) + L10 shields (~5.235M) + 3 platforms (1.5M) + 10 rail guns (1.5M) + L2 defense grid (500k) + advanced scanner (200k) + 6 turrets (900k) ≈ 10.235M cr → ~95/100.

Citadel destruction

If the citadel falls: - Population dies. - Production facilities destroyed. - Planet reverts to uncolonized. - Safe contents are preserved — owner can recover what was in the safe vault. See citadels.md.

Player-facing affordances

  • Defense overview panel: rating (0–100), drone count, shield strength, platform/rail-gun counts, recent attack log.
  • Build queue UI showing upgrade timers.
  • Real-time alerts when scanner detects incoming hostiles.
  • Auto-repair toggle for drones (uses planet credits when below threshold).

Source map

Topic Path
Citadel/defense service services/gameserver/src/services/citadel_service.py
Siege detection, defense-level upgrades, shield mechanics services/gameserver/src/services/planetary_service.py
Planet model defense fields services/gameserver/src/models/planet.py
Combat resolution against planets services/gameserver/src/services/combat_service.py:_resolve_planet_combat
Planet API routes services/gameserver/src/api/routes/planets.py
Drone service services/gameserver/src/services/drone_service.py
Per-unit drone model (Drone, DroneDeployment, DroneStatus) services/gameserver/src/models/drone.py
Drone deploy/recall API routes services/gameserver/src/api/routes/drones.py
Scalar drone counters services/gameserver/src/models/player.py (attack_drones, defense_drones), services/gameserver/src/models/planet.py (defense_fighters)

Status: 🚧 Partial — defense_level upgrades, shield mechanics, siege detection (SIEGE_TURNS_THRESHOLD, SIEGE_PRODUCTION_PENALTY, SIEGE_MORALE_LOSS_PER_TURN), and the citadel-bound defense-building queue (orbital platform, scanner array, turret network) are implemented. Turret + orbital defense-building combat contribution (damage-reduction + anti-drone + armour) is ✅ Shipped (WO-CT1, proven live). Outstanding gaps tracked inline above: siege resource theft transfer (📐 Design-only), and the full ship-size-multiplier raw-damage table for rail gun + orbital platform (📐 Design-only — the resolver's 1–5 scale defers raw-burst injection).