Affix death drops

What this is

Six of the eight world-roaming elite affixes register an onDeath hook that calls the prop-spawn adapter to drop one themed prop at the host’s death point. The pattern is a Diablo-style “elite drops loot” beat — narrative continuity from affix identity to corpse-prop, palette-matched per pair (regenerating green → mineral vein emerald, phasing ice-blue → comet ice-blue, gravity well violet → magnetar violet, hardened silver/cyan → drone wreck cyan, volatile magenta → volatile crystal magenta, summoner red → scrap pile rust).

The adapter is wired at engine boot in bridge.ts:

setAffixSpawnPropAt((x, y, typeId) => getPropPool().forceSpawnAt(x, y, typeId));

forceSpawnAt bypasses the prop spawner’s cadence + viewport gate + velocity-cone bias and claims a slot directly at the requested world coordinate. Burning-aura and reflective-burst affixes do not have onDeath death-drop hooks beyond reflective_burst’s volatile crystal drop (burning_aura drops nothing; reflective_burst shares the same drop as volatile).

The drop table

AffixProp droppedCountPlacementDrop telemetry event
hardeneddrone_wreck1host (x, y)affix_proc:hardened_drops_drone
regeneratingmineral_vein1host (x, y)affix_proc:regenerating_drops_mineral
gravity_wellmagnetar_pulse1host (x, y)affix_proc:gravity_well_drops_magnetar
summonerscrap_pile3triangle ring, 40 px radius, even-spaced + 0..0.3 rad jitteraffix_proc:summoner_drops_scrap_cluster
volatilevolatile_crystal1host (x, y)affix_proc:volatile_drops_crystal
reflective_burstvolatile_crystal1host (x, y)affix_proc:reflective_burst_drops_volatile
phasingcomet_fragment1host (x, y)affix_proc:phasing_drops_comet
burning_aura(none)

Drop chance is 100% per affix instance — there is no roll inside onDeath. World-roaming affixes are already gated to rare-tier+ elite-pack leaders by rollEliteAffixes, so rarity gating happens upstream.

Summoner’s drop count is governed by two constants local to the summonerHooks.onDeath body:

ConstantValueRole
SUMMONER_DROP_COUNT3Number of scrap_pile props attempted
SUMMONER_DROP_RADIUS_PX40Ring radius around the host’s death point

Pool-cap fail-silent rule

forceSpawnAt returns a boolean: true when a pool slot was claimed, false when the pool was already at POOL_SIZE capacity. Affix onDeath hooks treat that return value purely as a telemetry signal — they record dropped ? 1 : 0 (or, for summoner, the number of successful spawns out of 3) on the prop-drop telemetry event and exit. No retry, no defer, no error. A pool-full state results in a missed drop and an observable 0 in cloud telemetry, but the affix’s other death-time effects (volatile’s burst + crystal chain, reflective_burst’s preceding bursts) still fire normally.

This is the standard fail-silent pattern for forceSpawnAt consumers — it was first established by the mineral-cascade and supply-pod-cascade chain synergies inside props.ts before affixes claimed the same adapter at affix-system tick 1 (volatile crystal), tick 9 (phasing/summoner/hardened, adding three more consumers), and tick 23 (gravity well).

Crossover breaks

The dropped props each carry their own break-time synergies inside breakProp in engine/world/props.ts. Two of those synergies extend the affix theme back out as a recursive narrative beat — the elite dies, the prop lingers, the prop breaks, and a new affix-palette effect fires:

Prop droppedBreak-synergy chanceEffect on break
volatile_crystal25%burning_aura-palette afterburn — orange fire particles + single proximity damage tick (radius-gated)
mineral_vein30%Chain cascade — forceSpawnAtcomet_fragment at the break point
comet_fragment35%Ship-velocity speed boost — multiplies current velocity, capped at a max-ratio of max speed
drone_wreck30%Ship invuln grant — engages ship.invulnerable + invulnTimer for a fixed duration
scrap_pile15%XP-orb magnet pulse — attracts nearby orbs to ship
magnetar_pulse100%Enemy gravity pull — drags every enemy within MAGNETAR_PULL_RADIUS toward the break point

The volatile-crystal afterburn is the explicit reverse-loop: a volatile elite drops a volatile_crystal, the crystal has a 25% chance on break to fire a burning_aura-palette damage tick — so killing one world-roaming affix produces a chance-gated proc themed on a second world-roaming affix’s palette. The afterburn uses the burning_aura palette via getAffixVfxColor('burning_aura') and routes player damage through the normal damagePlayer chain so it respects invuln, shield, and reduction.