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:
- Long-range scanner detects approach (1–2 sectors out).
- Orbital defense platforms open fire from 2 sectors away.
- Fixed rail gun batteries engage in planetary orbit (1 sector).
- Defense drones swarm.
- Shield generators absorb damage to citadel.
- 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_dronesandPlayer.defense_drones(integers) hold a player's owned drone counts;Planet.defense_fighters(integer) is the planet's drone-equivalent garrison (thePlanetmodel has nodefense_dronescolumn, 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
Dronerow (models/drone.py), owned by a player (and optionally a team), with its own health, level, combat stats, currentsector_id, and astatusfield. A separateDroneDeploymentrow records each deploy/recall as deployment history withis_active,deployed_at, andrecalled_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_dronesetsDEPLOYED, stampssector_idanddeployed_at, and opens aDroneDeployment(any prior active deployment is recalled first). A destroyed drone cannot be deployed.recall_dronecloses the active deployment (is_active = False,recalled_atset), clearssector_id, and sets the drone toRETURNING(unless it isDESTROYED).- Combat sets both participants to
COMBAT; survivors return toDEPLOYED, the loser becomesDESTROYED. repairlifts aDAMAGEDdrone back toDEPLOYEDonce health rises above the 30% threshold; aDESTROYEDdrone 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, optionaldeployment_type,target_id); returns theDroneDeployment.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 routesPOST /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_batteryis incitadel_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 nodefense_buildingscontributes 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.
- Phase 1 (orbital approach, 2-sector range): orbital defense platforms fire if attacker is within 2 sectors.
- Phase 3 (orbital engagement, 1-sector range): rail guns fire if attacker is within 1 sector. Orbital platforms continue to fire.
- 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_gridis incitadel_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.minesJSONB 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 intoPlanet.active_events["defense_buildings"]["turret_network"]at L2 (alongside the flatdefense_turretscolumn), so they count incitadel_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.25inplanetary_service.py). Applied inside_calculate_production_rates. - Colonist growth halted: colonist rate forced to 0 each tick.
- Morale loss:
SIEGE_MORALE_LOSS_PER_TURN = 5percentage points per turn, mitigated by 0.05 ×defense_level(each defense level reduces the per-turn morale loss). - Defense damage reduction: each
defense_leveladdsDEFENSE_DAMAGE_REDUCTION_PER_LEVEL = 0.05(5%) damage reduction to citadel structure during siege resolution. At the maxdefense_levelof 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_levelupgrades, 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).