enemy-status.ts
PURPOSE
Lightweight per-enemy status effect system. Manages three status types — stunned, shredded, burning — with stack counts, duration timers, and tick-driven decay. Storage is lazily allocated on each enemy as e._statuses (a plain Record, not a Map, for mobile performance).
OWNS
applyStatus(e, type, duration, value, maxStacks)— applies or refreshes a status entry on an enemy, increments stacks up to cap, refreshes timer to the max of existing and incoming duration.hasStatus(e, type)— boolean check for whether an enemy has an active entry of a given type with timer > 0.getStacks(e, type)— returns active stack count, or 0 if no entry or timer expired.getShredMult(e)— returns the damage multiplier fromshreddedstacks;1 + stacks × value, or1.0if no shred.tickStatuses(enemies, dt, game, ship, world)— per-frame decay loop across all enemies; decrements timers, applies burning DPS, clears expired entries.clearStatuses(e)— wipese._statuses(used on enemy death/despawn).- Lazy allocation of the
e._statusesrecord on firstapplyStatuscall. - Maintenance of the legacy
e._stunTimerfield for backward compatibility with existing behavior handlers.
READS FROM
EnemyStatusEntrytype from./types.GameState,ShipState,WorldStatetypes from../core/types.- Each enemy’s
e._statusesrecord,e.hp, ande._stunTimer. game.stats.damageDealtfor burn damage accounting.
PUSHES TO
e._statuses[type]— writes new entries, mutatesstacks,timer,value.e._stunTimer— sets legacy stun field when applying or expiring astunnedstatus.e.hp— subtracts burning damage per tick (value × stacks × dt).game.stats.damageDealt— accumulates burn damage for run stats.
DOES NOT
- Apply stun-related movement or attack gating directly — behavior handlers read
hasStatus('stunned')ore._stunTimerthemselves. - Apply shred damage scaling directly —
damageEnemy()indamage.tscallsgetShredMult()at hit-time. - Render status icons, particle effects, or any VFX.
- Trigger sounds or telemetry events on apply/expire.
- Spawn new statuses from triggers — only
actions.tscallsapplyStatus. - Defend against missing enemies, negative durations, or unknown status types (assumes valid inputs from callers).
- Despawn enemies — burning damage that drops
hpto 0 or below is detected by the standard damage pipeline elsewhere.
Signals
- No external event bus or pub/sub. All consumers poll via
hasStatus,getStacks, orgetShredMultat the point of use. - Burn damage accumulates into
game.stats.damageDealtas a passive side effect oftickStatuses.
Entry points
actions.ts—apply_enemy_statusandapply_status_aoeeffect actions callapplyStatus().damage.ts—damageEnemy()readsgetShredMult()to scale incoming damage.behaviors.ts— enemy AI readshasStatus('stunned')to skip movement/attack ticks.bridge.ts— callstickStatuses()once per frame in the enemy update loop.
Pattern notes
- Status entries follow a uniform
{ stacks, timer, value }shape (EnemyStatusEntry), so the same storage handles all three types. - Re-apply semantics: timer refreshes to
Math.max(existing, incoming)so longer-duration applications never shorten an existing effect; stacks increment up tomaxStacks(stun uses defaultmaxStacks = 1, so it cannot stack). - Burning DPS is computed as
value × stacks × dtper frame, applied directly toe.hp. No tick rate — it’s continuous per-frame. - Shred is read-only at damage-time via
getShredMultrather than precomputed; this keeps the multiplier always-fresh and avoids cache invalidation. - Stun maintains a parallel
e._stunTimerfield purely for legacy behavior code that hasn’t been migrated tohasStatus. New code should prefer the unified API. - Storage uses a plain object (
Record) rather thanMapas an explicit mobile-performance choice noted in the file header. - The tick loop short-circuits on
!e._statusesande.hp <= 0to avoid touching dead or status-free enemies. - Expired entries are reset in-place (
stacks = 0,timer = 0) rather than deleted, leaving the record key in place for cheap reuse. clearStatusessetse._statuses = undefinedrather than emptying it — full reset on death/despawn.