Combat mechanics: damage, raids, weapons

Purpose: AI-readable reference for Structs combat. Consolidates formulas, requirements, outcomes, and edge cases. If you only need to keep yourself alive — what a raid can take, what keeps shields up, and the minimum defensive posture — read the shorter defense.md card instead.


Combat Actions

Action Message Description
Attack struct-attack Direct struct-to-struct combat
Defend struct-defense-set Set struct to defense mode (blocking)
Raid planet-raid-complete Planet assault; steals unrefined ore

Ambit Targeting

Combat in Structs revolves around four ambits. Each struct operates in one ambit, and each weapon can only target specific ambits. This creates a strategic mesh where fleet composition and positioning determine what you can hit and what can hit you.

Ambit Reach Bit Value
none 1
Water 2
Land 4
Air 8
Space 16
local 32

The four combat ambits are Water/Land/Air/Space; none (1) is a placeholder and local (32) is the Command Ship’s “current ambit” flag (its weapon reaches whatever ambit it currently occupies).

Two ambit numberings — do not conflate. The bit values above are the reach bitmask (bit = 1 << enum) used for StructType.possibleAmbit and weapon-reach fields. Transaction messages (struct-build-initiate, struct-move) and a struct’s stored operatingAmbit use a different enum: none=0, water=1, land=2, air=3, space=4, local=5. When building or moving, pass the enum (the CLI takes the name space|air|land|water), not the bitmask number. See building.md — Ambit Encoding and api/integration-notes.md — Ambit.

Weapon Target Matrix

Which ambits each struct’s primary weapon can hit:

Struct Lives In Targets (Primary) Targets (Secondary)
Command Ship Any (movable) Local only (flag 32 = current ambit)
Battleship Space Land, Water (armour-piercing) Space
Starfighter Space Space Space
Frigate Space Space, Air
Pursuit Fighter Air Air
Stealth Bomber Air Land, Water
High Altitude Interceptor Air Space, Air
Mobile Artillery Land Land, Water
Tank Land Land
SAM Launcher Land Space, Air
Cruiser Water Land, Water Air
Destroyer Water Air, Water
Submersible Water Space, Water

Threatened-By Matrix

Which structs can attack into each ambit:

Target Ambit Threatened By
Space Battleship (secondary), Starfighter, Frigate, High Altitude Interceptor, SAM Launcher, Submersible
Air Frigate, Pursuit Fighter, High Altitude Interceptor, SAM Launcher, Cruiser (secondary), Destroyer
Land Battleship, Stealth Bomber, Mobile Artillery, Tank, Cruiser
Water Battleship, Stealth Bomber, Mobile Artillery, Cruiser, Destroyer, Submersible

The Command Ship can attack into any ambit but must first move there (see below). The Battleship’s armour-piercing primary covers Land and Water; its guided secondary reaches Space.

Targets per attack

struct-attack accepts a comma-separated list of target IDs, but how many of those targets a weapon engages is governed by that weapon’s primaryWeaponTargets / secondaryWeaponTargets value.

Command Ship Ambit Mobility

The Command Ship is the only struct that can change ambits (movable=true). All other structs are fixed in their operating ambit, and the chain rejects struct-move on a non-movable struct — only the Command Ship can be moved.


Health Points

Each struct has a Max HP that determines how much damage it can absorb before destruction.

Struct Max HP
Command Ship 6
Other fleet structs (IDs 2-13) 3
Baseline planetary structs (Ore Extractor, Ore Refinery, Orbital Shield Generator, Jamming Satellite, Ore Bunker, PDC) 6
Field Generator 8
Continental Power Plant 10
World Engine 10

Planetary structs are hardened so a raider cannot casually demolish a planet’s infrastructure; the power generators are the toughest (and carry armour, damage reduction 1), making a power kill a deliberate objective. Armour-piercing weapons (Battleship primary) bypass that reduction.


Damage Formulas

Multi-Shot Damage

damage = sum(successful_shots) - damageReduction
if damage >= health then health = 0
else health = health - damage
Variable Description
weaponShots Number of shots per attack (primaryWeaponShots / secondaryWeaponShots)
weaponShotSuccessRate Per-shot success (Numerator/Denominator)
weaponGuaranteedShots Minimum number of shots that hit before the success rate roll applies (primaryWeaponGuaranteedShots / secondaryWeaponGuaranteedShots)
weaponDamage Damage per successful shot
damageReduction Defense reduction (target’s attackReduction, e.g. Tank/generator armour = 1); negated when the weapon is armour-piercing
armourPiercing If the weapon is armour-piercing (primaryWeaponArmourPiercing / secondaryWeaponArmourPiercing), the target’s damageReduction is treated as 0
health Target current health

Algorithm: For shot index i in 0..weaponShots, the shot hits if i < weaponGuaranteedShots OR IsSuccessful(weaponShotSuccessRate). The first weaponGuaranteedShots shots are auto-hits; only the remaining shots roll against the success rate. Sum the damage from successful shots, then apply damageReduction (unless the weapon is armour-piercing, in which case reduction is skipped). Minimum damage after reduction is 1. Cap at target health.

Armour-piercing: A weapon flagged armour-piercing negates the target’s damage reduction during volley resolution. The Battleship’s primary is armour-piercing — it deals full damage to Tanks and to power generators (which otherwise reduce incoming damage by 1). Each shot’s piercing is reported on EventAttackShotDetail.armourPiercing.

Why guaranteed shots exist: A weapon with shots=3 and successRate=1/3 has the same expected value as a single guaranteed hit, but its variance is much higher — most attacks would deal zero damage. Setting guaranteedShots=1 floors the damage at one hit per volley while preserving the upside of the other rolls. Guaranteed shots apply only to the Starfighter Attack Run (secondary weapon, secondaryWeaponGuaranteedShots = 1); other weapons leave the field at 0, which means “no guarantee, all shots roll”.

Querying note: primaryWeaponGuaranteedShots / secondaryWeaponGuaranteedShots are omitempty in the chain’s JSON output. Because the value is 0 for every struct type except the Starfighter secondary, the field is omitted entirely from most struct_type query responses. A payload with no guaranteed-shots key means the value is 0, not that the field was removed from the chain. (Verified present in current structsd source.)

Attack results: Attack events include health results (remaining health after attack) in addition to damage amounts.

Evasion

if weaponControl == guided then successRate = guidedDefensiveSuccessRate
else successRate = unguidedDefensiveSuccessRate
canEvade = IsSuccessful(successRate) if successRate.Numerator != 0

Weapon Control vs Defense Type

The interaction between a weapon’s control type (guided/unguided) and the target’s defense type is the core of combat tactics. This matrix determines whether shots can be evaded:

Target Defense vs Guided vs Unguided
Signal Jamming (Battleship, Pursuit Fighter, Cruiser) 66% miss Full hit
Defensive Maneuver (High Alt Interceptor) Full hit 66% miss
Armour (Tank, Field Generator, Continental Power Plant, World Engine) Full hit, -1 damage Full hit, -1 damage
Stealth Mode (Stealth Bomber, Submersible) Same-ambit only Same-ambit only
Indirect Combat Module (Mobile Artillery) Full hit Full hit
None Full hit Full hit

Tactical takeaways: Use unguided weapons against Signal Jamming targets (Battleship, Pursuit Fighter, Cruiser). Use guided weapons against Defensive Maneuver targets (High Alt Interceptor). Armour reduces damage by 1 regardless of weapon control — except against armour-piercing weapons (Battleship primary), which ignore it entirely. The Battleship is the dedicated answer to Tanks and to armoured power generators.

Stealth

Stealthed structs (Stealth Bomber, Submersible) are not invisible – they can still be targeted by structs in the same ambit. Stealth blocks cross-ambit targeting only. A stealthed Submersible (water) can be attacked by other water structs, but air/land/space structs cannot target it.

Recoil Damage

Attacker takes damage after firing: health = health - weaponRecoilDamage. Recoil only applies if the attacker survives the entire shot sequence (including counter-attacks).

Post-Destruction Damage

If health == 0 and postDestructionDamage > 0, damage applies to surrounding structs.

Assigning Defenders (struct-defense-set)

Only structs whose type has canDefend: true can be assigned. That is every fleet type (Command Ship through Submersible, IDs 1–13). Every planetary type (Ore Extractor through World Engine, IDs 14–22) has canDefend: false and is rejected (StructCannotDefend) — an Ore Bunker cannot be used as a block.

Assignment then checks that the defender is co-located with the protected struct (same planet or fleet) and is built and online — an offline or still-building struct is rejected. Ambit does not gate assignment; it gates what the defender can do once an attack lands:

So assigning a water Submersible to defend a land struct is valid and useful: it will counter air/space-reachable attackers even though it can never block for the land struct. Spread defenders across ambits to widen counter coverage, and keep same-ambit defenders where you need actual damage interception. There is no per-ambit cap on how many structs can defend (see Edge Cases).

Blocking

if !evaded and defender exists and defender.operatingAmbit == target.operatingAmbit then
  if weapon.blockable and defender.ReadinessCheck() then
    canBlock = IsSuccessful(defender.blockingSuccessRate)

Requirements (all must be true):

  1. The shot was NOT evaded – block does not fire on evaded shots
  2. Weapon must be blockable (GetWeaponBlockable returns true)
  3. Defender must pass ReadinessCheck – struct online AND owner online
  4. Defender must be in the same ambit as the target being defended (not the attacker)

A struct cannot block for a friendly in a different ambit. Blocking is strictly same-ambit defense. Unlike counter-attacks, block is attempted on every shot (not limited to once per attack).

Counter-Attack

Each struct can counter-attack at most once per struct-attack invocation. Counter-spent state is tracked per struct per attack command (not per target, not per shot). For a 3-shot Attack Run: the defender counters on the first shot only, but can attempt to block all 3 shots. The target counters once after all shots resolve.

Counter-attacks are ambit-independent from the defended target. A space-based defender can counter-attack a space-based attacker even while defending a land-based struct. Defenders do not take counter-attack damage — only the original attacker and target can be damaged by counters.

Range rule: A struct on a fleet that is away from the home planet cannot defend planetary structs at that planet. Only on-station fleet structs (canDefend: true) can defend their home planet. Planetary structs cannot be assigned as defenders.

Scenario Damage
Same ambit as attacker the defender type’s counterAttackSameAmbit value
Different ambit from attacker the defender type’s counterAttack value

Counter damage comes from two per-type fields, not a flat number: counterAttackSameAmbit (when the counter-attacker shares the attacker’s ambit) and counterAttack (when it does not). Most hulls are 1 / 1; the Command Ship is 2 / 2; the Destroyer is 1 / 2 (cross / same). See struct-types.md — Defensive Properties for the per-type values.

Counters are a backstop, not a damage plan. The values are small (typically 1), and an attacker striking from an ambit your structs can’t reach takes no counter at all. Real damage comes from active struct-attack volleys — build offense around attacking from a safe ambit, not around baiting counters.

Requirements (all must be true):

  1. Weapon must be counterable (GetWeaponCounterable returns true)
  2. Neither counter-attacker nor attacker is destroyed
  3. Defender’s weapons must be able to target the attacker’s ambit (via CanCounterTargetAmbit)
  4. Location reachability to the attacker:

Two types of counter-attack:

Both the defender and the target can counter-attack – an attacker may take damage from two sources per target.

Planetary Defense Cannon

damage = planetaryShieldBase + sum(defenseCannon.damage for each cannon on planet)

Other Planetary Defense Structs

Two planetary defenses are wired to structs and affect combat: the Planetary Defense Cannon and the low-orbit ballistic interceptor network (provided by the Jamming Satellite struct).

Two different things are called “jamming” — don’t conflate them. Both defeat only guided ordnance (unguided passes through either), but they operate at different scopes:

  Unit signalJamming Low-orbit ballistic interceptor network
Provided by Being a Battleship, Pursuit Fighter, or Cruiser (per-unit trait) The Jamming Satellite struct (type 17) on the planet
Scope Protects only the struct that has it Protects all planetary structs on that struct’s owner’s planet
Effect 66% miss on incoming guided fire Per-shot evade chance; stacks with each extra interceptor
Ambit N/A (self) Source and target ambit irrelevant
Order Checked first Checked second, only if the unit field didn’t already evade
Flag on evade normal weapon-control miss evadedByPlanetaryDefenses: true, cause lowOrbitBallisticInterceptorNetwork

Attack Resolution Sequence

When struct-attack is executed, the following steps occur in order per target:

  1. Validation – Verify weapon ambits can reach target ambit. Stealthed targets are only targetable from the same ambit. Verify target struct exists.
  2. Stealth break – If the attacker has stealth active, it is instantly deactivated (attacking reveals position).
  3. Evasion check (per-target, not per-shot) – Evaluate weapon control (guided/unguided) vs target defense type. If evaded, ALL shots against this target miss but counters still fire.
  4. Defender resolution (ResolveDefenders) – all defenders are handled in a single pass, counters first:
  5. Attack damage (ResolveAttackDamage) – the attacker’s volley lands only if it was not blocked and the attacker is still alive. A destroyed attacker deals no damage — its entire volley is voided (a Bomber killed by a counter does not still bomb). If it fires, per-projectile success rates and armour reduction apply (minimum 1 damage per hit), unless the target evaded in step 3, in which case all shots miss.
  6. Target counter-attack (once per struct-attack invocation) – fires after the volley resolves. Destroyed targets cannot counter.
  7. Early termination – If the attacker is destroyed mid-sequence, remaining targets do not process.

After all targets are resolved:

  1. Recoil damage – Applied to attacker if it survived all shots
  2. Planetary Defense Cannon auto-fire – If any target was a planetary struct, PDCs fire against the attacker

Per-Projectile Events

Each projectile gets its own EventAttackShotDetail row. For a 3-shot Attack Run, the attack event contains 3 separate shot detail entries with per-projectile hit/miss breakdowns. targetPlayerId is on EventAttackShotDetail (not EventAttackDetail).

In the live struct_attack event detail, the attacker context is flat at the top and the per-shot rows are a nested array eventAttackShotDetail[]. See api/integration-notes.md — struct_attack event detail schema for the exact field layout integrators receive.

Key implications:


Requirements

Requirement Attack Raid
Operating struct online, and its owner online
Raider player online (sufficient power)
Sufficient charge
Target struct built (a struct cannot be attacked until it finishes building)
Raider fleet away (at the target)
Raider fleet first in the target’s queue (LocationListForward == "")
Defender’s shields vulnerable (defender’s fleet off-station, or their Command Ship offline / destroyed / non-existent)
Raid clock started (blockStartRaid != 0)
Proof-of-work

An attack action depends only on the attacking struct (and its owner) being online — the attacker’s Command Ship does not need to be online, and the same holds for defensive changes (struct-defense-set) and stealth changes. The target must be a built struct; struct-attack against a struct that is still building is rejected (unbuilt), and a destroyed struct is rejected (destroyed). The target’s online status is irrelevant.

Visiting-fleet queue

A planet holds at most 1 + locationListExtra visiting fleets. locationListExtra defaults to 0, so capacity is one raider. The owner’s home fleet does not consume a slot. A fleet-move onto a foreign planet that is already at capacity is rejected (queue_full). The owner’s mine and refine proofs are rejected (under_raid) for as long as locationListStart is set.

Query locationListStart, locationListLast, locationListCount, and locationListExtra on the planet. First-in-queue (LocationListForward == "" on the raiding fleet) is still required to complete a raid — with default extra=0, the only visitor is first.

Note: planet-raid-complete does not consume charge (it is a proof-of-work message, not a charge message). Direct struct-attack does consume the player’s charge.


Raid Phases and SHIELDS_VULNERABLE

A raid cannot eliminate a player — the only prize is stored ore. No matter how one-sided the outcome, a successful raid seizes the defender’s unrefined stored ore and nothing more: it does not destroy the player, take their planet, capture structs, or touch refined Alpha Matter. Refined Alpha is untouchable; refine promptly and a raid can win you nothing. (This is the single most common combat misconception — treat raids as ore theft, not conquest.)

A raid is only winnable while the defending planet’s shields are vulnerable. Shields are vulnerable whenever the defender’s fleet is off-station (the Command Ship only defends the home planet while the fleet is on station), or the defender’s Command Ship is offline, destroyed, or non-existent. While the defender’s fleet is on station with a built, online Command Ship, the planet’s shields are up and planet-raid-complete is rejected (shields_active) no matter how much work the raider does. The single most effective raid defense is therefore keeping your fleet on station with the Command Ship online — and note that sending your own fleet away to raid someone else leaves your planet’s shields vulnerable until it returns.

This predicate is the chain function IsDefenderCommandStructVulnerable() (verified in structsd source, keeper/planet_cache.go): it returns vulnerable when the owner has no fleet, the defender’s fleet is off-station, there is no Command Ship, or the Command Ship is destroyed or offline. (Struct/player online is pure power math — load vs capacity — not recent activity; see power.md.)

Vulnerability is a state you can create — two ways to raid

The single biggest raid misconception is treating “shields vulnerable” as a fixed property to hunt for. It is a state, and you can put a defender into it. There are two modes:

stateDiagram-v2
    [*] --> shielded: CMD ship online + fleet on station
    shielded --> vulnerable: defender fleet leaves / CMD ship offline / CMD ship destroyed
    vulnerable --> shielded: CMD ship back online with fleet on station (clock clears)
    vulnerable --> raidSuccessful: raider completes PoW while clock runs
    note right of shielded
        planet-raid-compute is rejected here
        ("no active raid window"); blockStartRaid = 0
    end note
    note right of vulnerable
        blockStartRaid running; PoW age accrues
    end note

Idle is not vulnerable

A dormant owner (no transactions for days) is not the same as a vulnerable one. A player who set up correctly and walked away keeps their structs powered, so their Command Ship stays online and their fleet stays on station — IsDefenderCommandStructVulnerable() returns false and the planet is unraidable by the opportunistic path, no matter how long they’ve been idle. Do not infer raidability from an inactivity signal (or from a UI “vulnerable”/”inactive” badge). Gate on the live predicate: Command Ship online + fleet on station.

The flip side is the key insight for offense: a dormant owner is the ideal siege target. Because they are not watching, they will not rebuild a destroyed Command Ship or restore a power-starved one — so once you force the window open it stays open. An active defender, by contrast, may rebuild the Command Ship and slam the window shut.

The trap (a strictly-negative move). Moving your fleet to a not-yet-vulnerable target and expecting to raid is worse than doing nothing: your fleet is now off-station, so your own shields drop and your stored ore is exposed — all for a raid that can never complete until you also destroy their Command Ship. If you commit your fleet, commit to the siege (destroy the Command Ship), not just the fleet-move.

The blockStartRaid attribute is the vulnerability clock, and raid PoW age is measured from it:

So a raider must have their fleet at the target, catch (or make) the defender vulnerable, and let the clock age before the puzzle becomes solvable. If the defender restores their shields mid-raid (Command Ship back online with the fleet on station), the clock resets and the raid status flips to ongoing until the planet becomes vulnerable again.

The two rejection messages (don’t confuse them)

The tooling surfaces the vulnerability gate in two different places with two different strings — verified in structsd source:

Where Message Meaning
CLI pre-check, planet-raid-compute (client/cli/tx_planet_raid_compute.go) planet (X) shields are not vulnerable: no active raid window (defending Command Ship may still be online) blockStartRaid == 0 — no live raid window; the compute would be wasted work. This is the message a raider usually hits first.
Chain handler, planet-raid-complete (keeper/msg_server_planet_raid_complete.go) planet (X) cannot raid_complete while shields_active / ...while raid_clock_unset The completion transaction itself is rejected: shields_active = defender not vulnerable at completion; raid_clock_unset = clock is 0.

Both point at the same fix: the defender is not vulnerable. Either wait/watch for them to slip (opportunistic), or open the window yourself by destroying their Command Ship (siege). Neither means “grind harder.”

One message, two independent gates. blockStartRaid is set only by RefreshRaidVulnerability(), which returns early unless GetLocationListStart() != "" (a raider is present). So blockStartRaid == 0 — and the single no active raid window string — collapses two independent conditions: (1) a raider is present at the target, and (2) the defender is vulnerable. If you have not moved your fleet in yet, the clock is 0 for reason (1), not (2); moving in against a shielded defender then flips the reason to (2) but the clock still stays 0. Completion (planet-raid-complete) adds two more gates the compute message doesn’t mention: your raiding fleet must be first in the queue (LocationListForward == "") and your player online. Read “no active raid window” as “one of {raider-present-and-first-in-queue, defender-vulnerable} is not yet true,” not as a hashing problem.

Raid statuses

Status Meaning
initiated Raider fleet has arrived at the planet
shieldsVulnerable Defender’s shields are down (fleet off-station, or Command Ship offline/destroyed) — the raid is now winnable and the clock is running
ongoing Defender restored shields mid-raid (Command Ship back online with the fleet on station) — completion blocked
raidSuccessful Raider won and seized all of the defender’s stored ore
attackerDefeated Raider’s own Command Ship was destroyed while away (trigger_raid_defeat_by_destruction) — the raiding fleet is defeated and sent home
attackerRetreated Raider withdrew before completion
demilitarized Planet has no defenders to resolve against (fleet.PeaceDeal())

Status values are the RaidStatus_* enum emitted on EventRaid. The most a defender loses to a raid is all of their stored ore (raidSuccessful).

Raid status also includes seizedOre – a convenience figure for the ore stolen, surfaced on the planet_raid record. For an authoritative count, derive it from ledger rows with action = 'seized' (which also record 0-gram seizures). See schemas/entities/planet.md for the planet_raid table schema.

What a raid does

A successful planet-raid-complete seizes all of the defender’s storedOre, sends the raider’s fleet home, and emits raidSuccessful. Ore is the only thing a raid takes — a raid does not destroy the defending player or their structs.

Destroying the defender’s Command Ship (or catching their fleet off-station) makes the planet’s shields vulnerable (shieldsVulnerable), which is the condition that lets a raid complete. If the defender restores their shields before completion — Command Ship back online with the fleet on station — the shields return and planet-raid-complete is rejected with shields_active. So the planet is either vulnerable at completion — and the raider takes all the ore — or shielded, and the raid is rejected.

trigger_raid_defeat_by_destruction is a property of the Command Ship. When a Command Ship is destroyed while away from home (its planet’s owner differs from its own owner), its fleet is defeated: the raid ends with attackerDefeated and the fleet is sent home. This defeats an attacking fleet whose Command Ship dies during a raid.

Raid attack doctrine

This is the siege path — how to force the window open when the defender is shielded. It is executable at the source level: an away raiding fleet that is first in the target’s raid queue can attack any struct on that planet, including the on-station defender’s Command Ship (isReachable, verified in keeper/struct_cache.go), subject to ambit reach and same-ambit blockers.

  1. Move your fleet to the target (raid initiated). A foreign planet holds one visiting fleet by default — if someone is already parked, fleet-move is queue_full. While the defender is still shielded, blockStartRaid stays 0 and planet-raid-compute is rejected — this is expected; do not grind yet.
  2. Strip same-ambit blockers so your attacks reach the Command Ship (cross-ambit defenders counter but cannot block — see Blocking).
  3. Destroy the defender’s Command Ship to open the shieldsVulnerable window. The destruction path fires CommandStructRaidStatusHook()RefreshRaidVulnerability(), which starts the clock and emits shieldsVulnerable because your raider is already present.
  4. Run planet-raid-compute now that the clock is running, and complete while it is down to seize all stored ore.
  5. Expect an online defender to rebuild the Command Ship — a destroyed Command Ship can be rebuilt (see Struct Destruction), which shuts the window. Sieges are most reliable against an offline or dormant defender who cannot/will not restore it.

Cost of a siege. It is a real commitment, not a free option: you must win the fleet engagement to kill the Command Ship (6 HP, usually defended), and while your fleet is away your own planet’s shields are down. Weigh the defender’s storedOre (all of which you seize) against that exposure and the risk your raiding Command Ship dies while away (attackerDefeated, trigger_raid_defeat_by_destruction). Small ore + a defended Command Ship often means “watch and wait” beats “siege.” But a dormant target holding meaningful ore, whose Command Ship you can reach, is exactly what a siege is for.

What the outcome data says

Everything above is derivable from the source. It is also confirmed by outcomes: roughly 300 raid episodes reconstructed from planet_activity (92k events, 2026-03 → 2026-07), used to tune the Desktop auto_raid loop. Four findings are worth internalizing before you raid anyone.

Vulnerability is not an edge, it is the entire game. Since the current mechanic landed on 2026-06-15, raids that reached shieldsVulnerable went 69 successful vs 7 retreats. Raids that never saw it went 0 successful vs 50 retreats — not “unlikely,” zero. This is why the shield-vulnerability doctrine is stated as a hard gate rather than a preference, and why moving a fleet onto a healthy planet is strictly self-harm: it drops your own shields for a raid that cannot complete.

Shield strength does not predict the outcome. Successful raids faced an average shield of 127.9; retreats faced 111.5. Both span 25–325. Shield is a timer, not armor — it sets the raid proof’s difficulty range. Score a target’s shield as speed, never as strength, and never skip a rich target because its shield number looks big.

Loot is a lottery with a fat tail. 123 successful raids yielded 1,336 ore in total, but the median haul was 1 and 52 of them seized nothing at all. Nine raids carried 74% of all ore ever stolen. Since a raid risks a Command Ship that costs far more than one ore, a minimum-ore threshold is not optional — below it you are taking an unpriced risk for a rounding error.

The attacker’s own risk is badly underrated. Across 22 raids by one well-instrumented operator: 2 wins, 11 retreats, and 9 attackerDefeated — a 41% loss rate against an ecosystem baseline near 5%, every one of them their primary’s Command Ship dying in the field. Raiding with the account you cannot afford to lose is the single most expensive habit in the data. If you run multiple players, raid with an expendable one.


Edge Cases


Struct Destruction

When a struct reaches 0 HP, it is destroyed and removed from the game. The destroyed instance is gone forever and cannot be repaired. However, you can build a new struct of the same type as a replacement — full build PoW required.

Consequence Detail
Destroyed struct Instance gone forever; build a replacement (full PoW)
Lost defenders Each destroyed defender must be individually rebuilt
Rebuild cost Full PoW + power draw, same as original build

FAQ: Can I rebuild a destroyed Command Ship?

YES. A destroyed Command Ship cannot be repaired, but you can build a brand new Command Ship (type 1) to replace it. The new Command Ship gets a new struct ID and requires full build PoW (~17 min at D=3). You choose the starting ambit at build time.

Until the replacement is online, the fleet cannot move, raid, or build in space. This downtime is the real cost of losing a Command Ship. Always assign defenders to protect it via struct-defense-set before engaging in offensive operations.


See Also