← Design Design

Battle-station designs — dedicated defensive station identities

Captured 2026-07-19. The "[Structures] Battle station designs" card. Stations that fight (pirate pickets, militia defense platforms) were borrowing ship capital-hull art (`Station.…

Captured 2026-07-19. The “[Structures] Battle station designs” card. Stations that fight (pirate pickets, militia defense platforms) were borrowing ship capital-hull art (Station.HullSpriteByFaction — civilian Nautolan Dreadnought, militia Nairan Battlecruiser, pirate Kla’ed Battlecruiser). A fortified position that a raider pack anchors on, or that brackets a contested belt, should read like a structure worth assaulting, not a big parked ship. This card gives the fighting stations their own silhouette language and feeds the follow-up conflict-ecology card (a pirate base spawns hunters — so the Kessler Raider Bastion is the priority archetype).

The player-owned station work is a separate track (docs/design-stations.md); this card is the NPC/enemy side — the emplacements the player flies at.

What already exists (build on it, don’t reinvent)

The whole danger-interaction machinery already ships (see docs/design-stations.md “What already exists” for the fuller audit). The two levers this card actually needs were both already present:

  • StationDef.Mounts (features/universe/StarSystemDef.cs) — an optional list of explicit [x, y] turret positions in local station space. When present, Station.ArmTurrets places one turret per listed coordinate and skips the procedural even-ring entirely (ArmTurretsAtMounts). Authored via the HardpointEditor tool, or hand-written to its export shape (a JSON array of [x, y] pairs). This is the layout knob.
  • Station hull pipeline — a single Sprite2D whose texture is padded (SpritePadding.Load), alpha-traced for a collision polygon (HullTracer.Trace), cel-outlined (OutlineStyle), and gently multiplied toward the owning faction color (0.2 lerp, Station.TintFor). Everything downstream — dock zone radius, map intel, the disable-on-shield-down behavior — derives from that one traced sprite.

The one gap: art selection was faction-only

Station chose its sprite purely from HullSpriteByFaction[factionId], so every pirate station was the Kla’ed Battlecruiser and every militia station the Nairan Battlecruiser — ship hulls. There was no way for a specific station to declare “I am a bastion, here is my structure art.”

The mechanism this card adds is deliberately tiny: two optional StationDef fields, Sprite (a res:// path that overrides the faction hull map) and SpriteScale (0 = keep the faction 2× default). Station._Ready picks Def.Sprite over the faction hull when set, and uses Def.SpriteScale for both the sprite scale and the collision trace. Nothing else in the hull pipeline changes — a dedicated-art station traces, outlines, tints, docks, and disables exactly like a capital-hull one. That keeps collision/outline/dock math untouched and puts the whole archetype in content (JSON + a baked PNG).

The art problem, and the answer

No dedicated station/structure art exists in any vendored pack (audited docs/assets.md + the asset tree): every Foozle/Kenney/Tiny-Ships PNG is a ship hull, a projectile, or UI. The card anticipated this (“procedural composition of existing structure parts is acceptable if it reads well”).

The owned structural (non-ship) art that DOES exist:

  • assets/sprites/vindicator/stargate_parts.png (owned, 384×384, 3×3 @ 128) — ring segments (chevron-marked C-rings, cells 0,0 / 2,0), a heavy bilateral connector block (cell 1,0 — an industrial reactor/dock spine, no thrust axis), and a compact reactor pod module (cell 1,2). These are the strongest “reads STRUCTURE not ship” parts in the repo: symmetry, mass, mechanical bristle, and crucially no engine flare / no pointed prow.
  • assets/backgrounds/foozle_void_env/Asteroids/PNGs/Asteroid 01 - Base.png (CC0, 96×96) — an irregular rock: pure mass, no thrust, no bilateral “front.”

Decision: bake dedicated station sprites by compositing these owned parts (ImageMagick, scripts/ one-off) into standalone PNGs under assets/sprites/vindicator/stations/, so the single-sprite Station pipeline consumes them unchanged. This is “dedicated station art” (owned, derived from owned parts) rather than “ship-hull reuse” — exactly the card’s intent — and it sidesteps runtime multi-sprite composition, which would have fractured the single-file collision trace + outline the station relies on. See docs/decisions.md for the alternatives (runtime child-sprite composition; single raw connector cell; raw asteroid) and why they were rejected.

Silhouette language — what makes each read STRUCTURE

The shared rules across all three: bilateral or radial symmetry (a ship has a prow and a stern; a structure is symmetric about a point or an axis), no thrust axis (no engine flare, so nothing implies it wants to move), visible mass (chunky mechanical or rock body), and guns that ring the body rather than face forward. The self-rotation Station already applies reads as a slow station spin on a symmetric body, where on a ship hull it looked like a drifting derelict.

The three archetypes

Archetype Faction Type Sprite (baked) Scale Shield / Regen / Hull Guns (mounts) Where it appears
Kessler Raider Bastion pirates Defense raider_bastion.png (208²) 1.5 400 / 22 / 900 5, bristled Karda belt (karda.thornpicket), Marches belt (marches.gibbet)
Militia Bulwark Platform militia Defense militia_bulwark.png (224²) 1.5 500 / 26 / 1100 6, ring-even Eos (eos.relay), Marches inner planet (marches.warden)
Independent Claim-Guard independent Defense claim_guard.png (148²) 1.5 140 / 14 / 360 2, flank Karda belt (karda.claimguard, new)

Stats are calibrated against the existing station defaults (Shield 250 / Regen 18 / Hull 600) and against ship hulls (a Kla’ed Battlecruiser ship is Hull 320 / Shield 60 — stations already dwarf ships, which is correct: a station tanks). Shield is the disable threshold (beat it flat → turrets go dark until it regenerates — Station.Disabled), not a kill bar, so the tiers read as “how long can it hold its guns up under fire.”

1. Kessler Raider Bastion (PRIORITY)

  • Role. A pirate strongpoint dug into a belt’s edge — the frontier’s teeth on an ore-haul route, and the anchor the conflict-ecology card will spawn a raider pack from.
  • Silhouette. A hollowed asteroid (irregular rock mass) welded with a scavenged connector-block reactor spine across its face. Asymmetric rock
  • bilateral metal core = unmistakably an improvised fort, nothing like a hull. Amber pirate tint (#B45309).
  • Layout philosophy. Five guns bristled around the rock, not evenly — two on the spine flanks, two low, one high ([[-150,-20],[150,-20],[-95,95], [95,95],[0,-150]]). Bristle-over-symmetry sells “scavenged and up-gunned.”
  • Stats. Tough: Shield 400, Regen 22, Hull 900 — you have to work to punch a window in it. Above a stock picket, below the militia bulwark.
  • Reward. Salvage/loot is deferred to the conflict-ecology card (a disabled bastion is where the pirate-hunter loop and any base-loot hook will live); today it is a hard, hostile emplacement.

2. Militia Bulwark Platform

  • Role. A purpose-built, disciplined system-defense emplacement. Where the bastion is improvised, the bulwark is engineered.
  • Silhouette. A radial double-ring (two chevron-marked ring segments closed into a full armored torus) around a central connector hub. Full radial symmetry, guns on the ring — the cleanest “battle platform” read. Green militia tint (#00843D).
  • Layout philosophy. Six guns evenly on the ring ([[150,0],[75,-130],[-75,-130],[-150,0],[-75,130],[75,130]]) — even spacing reads as designed doctrine, the opposite of the bastion’s bristle.
  • Stats. Toughest: Shield 500, Regen 26, Hull 1100 — the militia holds ground.

3. Independent Claim-Guard

  • Role. A cheap, improvised picket the unaligned Independents throw up to hold a belt claim against raiders. The soft end of the ladder.
  • Silhouette. A compact reactor pod on a short connector spine — a small cross/anchor of mechanical mass, two gun stubs. Reads as a minimal emplacement, not a warship. Gold independent tint (#D4A017); neutral to the player.
  • Layout philosophy. Two flank guns only ([[-95,0],[95,0]]) — token defense, matching a faction that “isn’t built to fight” (codex: independents).
  • Stats. Soft: Shield 140, Regen 14, Hull 360 — below the stock default; a claim-guard is a speed bump, not a wall.

Placement (verification + fiction)

Existing fighting stations were retrofitted in place to their archetype (sprite + scale + mounts + stats), keeping every spawn intact — this directly retires the ship-hull reuse the card exists to end, with zero new-content balance risk:

  • karda.thornpicketThorn Bastion (pirate, Frontier Karda — one hop from Eos, reachable for verification).
  • marches.gibbetThe Gibbet (pirate, contested Marches).
  • eos.relayEos Relay bulwark (militia, Safe Eos).
  • marches.wardenWarden Bulwark (militia, contested Marches).

One genuinely new station places the third archetype: karda.claimguard (Belt Claim-Guard, independent) free-floating past the Karda belt, opposite the pirate bastion — Independents holding their frontier claim against the raiders, thematically. Karda is not asserted by any test’s station count (Eos’ count-of-3 test is untouched), and a neutral soft picket does not perturb the Eos economy demo.

Capture / verification

A narrow -Station <id> capture flag was added (scripts/capture.ps1 + CaptureRig, following the existing -Flag pattern): it centers the camera on a station by id, hides the camera-anchor ship, and drops that anchor out of the target pool (so a hostile bastion’s turrets don’t bury the structure under point-blank beams). Proofs in capture-out/battle-station-{bastion,bulwark, guard}.png.

Alternatives considered + rejected

  • Keep reusing capital ship hulls. Rejected — it is the exact problem the card names; a battlecruiser hull parked in an orbit reads as a ship, not a fort.
  • Runtime multi-sprite composition (compose the body from child sprites at load, like StarlaneGate does). Rejected: it fractures the single-file collision trace, outline material, and tint the whole Station pipeline assumes, for no visual gain over a pre-baked composite. Baking keeps the archetype entirely in content.
  • A single raw stargate connector cell as the body. Rejected: reads too much like a piece of a gate. The asteroid + spine composite is distinct and unmistakably a base.
  • A bare scaled-up asteroid. Rejected: plain, and “big rock” doesn’t say “fortified emplacement with guns.” The embedded reactor spine is what makes it a bastion.
  • New per-archetype hulls as first-class ship-style templates. Rejected as over-built: a station is a StationDef + a sprite, not a ship template with engine/shield/weapon sheets. Two optional fields on StationDef carry the whole feature.
  • New bespoke turret art per faction. Out of scope: turrets already skin per faction (Turret.BarrelSheetByFaction); the card is about station bodies.

Follow-ups (not this card)

  • Conflict ecology (its own card): the Kessler Raider Bastion spawns a raider/hunter pack anchored on it; base-loot/salvage on disable lives there.
  • Player-station art: docs/design-stations.md step 2 notes a dedicated faction.player station silhouette is a nice-to-have; the Sprite field this card adds is exactly the hook it would use.