bridge-death-debris.ts
3D death debris module — tumbling shard sprites ejected on enemy kill. Sprites pre-baked in atlas-builder.ts; this file owns spawn logic, archetype-driven tinting, and the active fragment pool.
What
Module exports a single spawn function (spawnDeathDebris) and an active deathDebris pool. Called from enemy-death handlers when an enemy is killed. Pushes randomized shard records into the pool capped at _MAX_DEBRIS = 60. Each shard is rendered as one batched sprite picking one of SHARD_VARIANT_COUNT = 6 pre-baked atlas regions.
Why
Kill burst is the primary feedback channel — it has to read at a glance even mid-swarm. The archetype-keyed tint map (Tick 44) and the archetype-keyed count multiplier (Tick 56) together encode mass + character: chargers throw aggressive orange-red, wisps throw a few magenta sparks, brutes throw heavy grey-brown chunks. The 50/50 bright/dark per-shard jitter preserves the pre-tick-44 red-bright/red-dark vibration look.
Constants
| Constant | Value | Role |
|---|---|---|
DEBRIS_MIN_SPEED | 220 | Ejection speed floor (px/s) |
DEBRIS_SPEED_RANGE | 180 | Ejection speed jitter band |
DEBRIS_MIN_LIFE | 0.18 | Lifetime floor (s) |
DEBRIS_LIFE_RANGE | 0.14 | Lifetime jitter band |
_MAX_DEBRIS | 60 | Global pool cap |
SHARD_VARIANT_COUNT | 6 | Atlas variants (must match atlas-builder.ts) |
_DEFAULT_DEBRIS_TINT | rL/gL/bL = 1.00/0.16/0.16, rD/gD/bD = 0.78/0.10/0.10 | Pre-tick-44 red fallback |
DebrisTint type
type DebrisTint = { rL: number; gL: number; bL: number; rD: number; gD: number; bD: number };Two-tone tint: L = bright, D = dark. Per-shard 50/50 weight at spawn.
ARCHETYPE_DEBRIS_TINT map
Tick-44 dispatch-site override-map (T18 dogfood, sibling to tick-42 audio and tick-43 VFX-burst).
| Archetype | Bright (rL,gL,bL) | Dark (rD,gD,bD) | Character |
|---|---|---|---|
| orb | 1.00, 0.16, 0.16 | 0.78, 0.10, 0.10 | generic red (= default) |
| charger | 1.00, 0.40, 0.10 | 0.78, 0.25, 0.06 | aggressive orange-red |
| racer | 1.00, 0.90, 0.20 | 0.78, 0.62, 0.10 | neon yellow-electric |
| shooter | 0.30, 1.00, 0.30 | 0.18, 0.78, 0.18 | toxic green |
| gunner | 0.60, 1.00, 0.20 | 0.40, 0.78, 0.10 | green-yellow |
| mortar | 1.00, 0.55, 0.10 | 0.78, 0.35, 0.06 | explosive orange |
| sniper | 0.75, 0.30, 1.00 | 0.50, 0.18, 0.78 | beam purple |
| field | 0.30, 0.90, 1.00 | 0.18, 0.62, 0.78 | field cyan |
| brute (T25/26) | 0.60, 0.45, 0.32 | 0.40, 0.28, 0.20 | dark grey-brown chunks |
| wisp (T25/26) | 1.00, 0.50, 1.00 | 0.78, 0.32, 0.78 | ethereal magenta |
| lurker (T25/26) | 0.55, 0.20, 0.80 | 0.35, 0.12, 0.55 | deep dark-purple |
| bombardier (T25/26) | 1.00, 0.75, 0.20 | 0.78, 0.50, 0.10 | bright explosive amber |
| sprinter (T48) | 0.78, 1.00, 1.00 | 0.50, 0.78, 0.78 | supersonic cyan-electric |
| spitter (T50) | 0.70, 1.00, 0.20 | 0.50, 0.78, 0.10 | acid yellow-green |
| burner (T51) | 1.00, 0.50, 0.15 | 0.78, 0.32, 0.08 | fire/lava red-orange |
| suppressor (T52) | 0.55, 0.70, 0.30 | 0.35, 0.50, 0.18 | industrial olive-drab |
Missing archetypes fall back to _DEFAULT_DEBRIS_TINT.
ARCHETYPE_DEBRIS_COUNT_MULT map
Tick-56 override-map (6th T19 instance). Multiplies the caller-supplied count before flooring at 1.
| Archetype | Mult | Rationale |
|---|---|---|
| gunner | 0.8 | small turret |
| mortar | 1.3 | (orb/charger/racer/shooter/sniper default 1.0) |
| field | 1.4 | big emitter |
| brute | 1.5 | heavy chassis |
| wisp | 0.4 | tiny ethereal — almost no shards |
| lurker | 1.1 | slightly heavier than sniper |
| bombardier | 1.5 | big explosive payload |
| sprinter | 0.5 | fragile + supersonic |
| spitter | 1.2 | tanky standoff mass |
| burner | 1.4 | big static emitter (matches field) |
| suppressor | 1.3 | tanky turret |
All others default to 1.0.
DeathDebris interface
interface DeathDebris {
x: number; y: number;
vx: number; vy: number;
rot: number; rotSpd: number;
size: number;
life: number;
maxLife: number;
variant: number; // 0..SHARD_VARIANT_COUNT-1, picks atlas region
r: number; g: number; b: number; // per-shard vertex tint
}Pool exported as export const deathDebris: DeathDebris[].
spawnDeathDebris
export function spawnDeathDebris(
wx: number, wy: number,
radius: number,
count: number,
behavior?: string
): voidPer call:
- Look up
tintfromARCHETYPE_DEBRIS_TINT[behavior](default red on miss). - Look up
countMultfromARCHETYPE_DEBRIS_COUNT_MULT[behavior](1.0 on miss). scaledCount = Math.max(1, Math.round(count * countMult))— floors at 1 so tiny enemies still emit at least one shard.- Loop up to
scaledCount, short-circuit if pool already at_MAX_DEBRIS. - Per shard: random angle,
spd ∈ [220, 400),life ∈ [0.18, 0.32), position offset±0.2*radiuson each axis, rotation jitter±11 rad/s(rotSpd = (rand-0.5)*22),size = 4..8 + 0.10*radius,variant = floor(rand * 6),bright = rand < 0.5picks bright vs dark tint channel.
Spawn position scatter uses radius * 0.4 width ((rand-0.5) * radius * 0.4).
Render / lifetime
This module owns spawn + storage only. Update (velocity, rotation, life decrement) and render (one batched sprite per shard, atlas lookup by variant) live elsewhere — search for consumers of deathDebris and SHARD_VARIANT_COUNT. Atlas-side variants are baked at startup in atlas-builder.ts; SHARD_VARIANT_COUNT must stay in sync with that file.
Touchpoints
- Atlas:
src/starship-survivors/engine/atlas-builder.tsbakes the 6 red shard sprite variants; consumers tint them per-shard via ther/g/bfields. - Callers: enemy death handlers pass
behaviorfrom the killed enemy’s archetype string. - Sibling override-maps: tick-42 audio + tick-43 VFX-burst follow the same dispatch-site override-map pattern; this is the third (tint) and sixth (count) T18/T19 instance respectively.
EXTRACT-CANDIDATE
- Archetype maps belong in data, not code. Both
ARCHETYPE_DEBRIS_TINTandARCHETYPE_DEBRIS_COUNT_MULTare content tables hard-coded in a TS file with tick-comments. Per CLAUDE.md (“Content in data tables, behavior in code”), these should move to a per-archetype data row (e.g.data/archetypes.jsonordata/archetypes.ts) alongside the tick-42 audio + tick-43 VFX-burst maps. One archetype row, N override columns; the spawn function reads the row. - Tick-comment noise. The “Tick 44 — Strict T18 dogfood of the dispatch-site override-map pattern (tick-42 audio, tick-43 VFX-burst)” prose is process metadata, not code. Move to an ADR; leave a one-line pointer.
- Magic 0.4 / 0.10 / 22 / 0.5 scatter constants in
spawnDeathDebrisare unnamed. Per CLAUDE.md (“Every number traces to a named constant”), promote:DEBRIS_POS_SCATTER = 0.4,DEBRIS_SIZE_RADIUS_COEF = 0.10,DEBRIS_ROT_SPD_RANGE = 22,DEBRIS_BRIGHT_PROB = 0.5. SHARD_VARIANT_COUNTduplicated across files. Source of truth comment says “must matchatlas-builder.ts” — that’s a foot-gun. Either import from the atlas module or move both to a shared constants module.- Top comment says “3D death debris — tiny tumbling cubes” but the inline comment on line 6 says “2D red polygon shards.” Code is 2D shards. Fix the top comment.
_MAX_DEBRIScap silently drops shards when the pool is full (the for-loop short-circuits). Either documented behavior (current state) or recycle oldest — pick one and note it.