engine/effects/types.ts

Type definitions for the unified effect system. The effect engine provides a signal → condition → action pipeline used by artifacts, ship passives, and ship mod abilities. Every triggered gameplay effect flows through these types.

Consumed by effect-engine.ts (runtime registry, tick, signal dispatch), conditions.ts (condition evaluators), actions.ts (action executors), and run-effects.ts (wires artifacts/passives/mods at run start).

Imports

  • SignalContext from ../core/signals
  • GameState, ShipState, WorldState from ../core/types

DamageTag

type DamageTag = 'bullet' | 'energy' | 'bomb' | 'fire' | 'true' | 'boss'

Every damage event carries exactly one tag. Weapon-produced events use one of the four exclusive weapon tags (bullet, energy, bomb, fire). true and boss are reserved for engine-internal events.

TriggerType

type TriggerType = 'signal' | 'run_start' | 'aura' | 'timer' | 'counter'

How an effect activates:

TypeBehavior
signalFires when a named Sig signal is emitted.
run_startFires once after all effects are registered at run start.
auraContinuously checks conditions; applies/removes modifiers.
timerFires after N seconds (optionally repeating, optionally reset by a signal).
counterFires after counting N occurrences of a signal.

TriggerDef

Discriminated by type: TriggerType. Fields are conditionally meaningful per trigger type.

FieldTypeUsed byNotes
typeTriggerTypeallrequired
signalstring?signalsignal name (e.g. 'enemy_kill', 'shield_break')
auraIntervalnumber?auraseconds between checks; default 0.5
timerDurationnumber?timerseconds until fire
timerRepeatboolean?timerrestart after firing; default false
timerResetSignalstring?timersignal that resets countdown to 0
counterSignalstring?countersignal whose occurrences are counted
counterThresholdnumber?counterfires after this many; supports $var
counterResetsboolean?counterresets to 0 after firing; default true

ConditionDef

interface ConditionDef {
  type: string
  params: Record<string, number | string>
}

A single condition check. All conditions in an effect must pass (AND logic). Evaluated cheapest-first: random/str1 checks before HP/stat queries.

Built-in types: random, signal_str1_eq, signal_str1_neq, signal_num1_gte, hp_below, hp_above, shield_active, shield_broken, shield_empty, has_artifact, has_upgrade, has_buff, kill_streak_above, elapsed_above, elapsed_below, tier_at_least, heat_above, speed_above, damage_tag_eq.

ActionDef

interface ActionDef {
  type: string
  params: Record<string, number | string | boolean>
}

A single action to execute. Actions run in order on each trigger. Params starting with $ are resolved from EffectInstance.values.

Built-in types: modify_stat, modify_stat_scaled, modify_upgrade_count, heal, heal_shield, grant_invuln, set_heat, damage_aoe, damage_single, apply_knockback, spawn_projectile, spawn_beam, spawn_zone, apply_enemy_status, apply_status_aoe, empower_weapon, restore_speed, remove_modifiers, flash_artifact, vfx, emit_signal, custom, custom_tick.

EffectDef

Data-driven, immutable per content. One per artifact/passive/mod definition.

FieldTypeNotes
idstringunique (e.g. 'soul_leech_ghosts', 'reactive_plating_stack')
triggerTriggerDefwhat activates the effect
conditionsConditionDef[]all must pass; empty array = always passes
actionsActionDef[]executed in order when trigger + conditions pass
cooldownnumber?seconds between activations; 0 = no cooldown
chargesnumber?max activations per run; -1 = unlimited; default -1
prioritynumber?signal listener priority; default 50 (effects band)
showBannerboolean?push “[Artifact] activated” banner on fire; throttled to 1 per artifact per 0.5s in the banner module; dev-curated, leave off for per-hit procs / auras / run_start

EffectInstance

Runtime state, one per registered effect.

FieldTypeNotes
defEffectDefimmutable definition
ownerstringowner identifier ('artifact:soul_leech', 'passive:afterburner', 'mod:static_discharge')
valuesRecord<string, number>value dict for $var param resolution (e.g. tier values)
cooldownRemainingnumberseconds until next allowed fire
chargesUsednumberhow many times this effect has fired
counterAccumnumberaccumulated count for counter trigger
timerAccumnumberaccumulated time for timer trigger
auraActivebooleanfor aura trigger: are modifiers currently applied
enabledbooleankill switch — disabled effects skip all evaluation

SignalSnapshot

interface SignalSnapshot {
  name: string
  uid1: number
  uid2: number
  num1: number
  num2: number
  str1: string
}

Snapshot of SignalContext fields captured before action dispatch. Passed to custom action handlers so they read consistent values even if the live signal context mutates mid-pipeline.

CustomActionFn

type CustomActionFn = (
  snap: SignalSnapshot | null,
  params: Record<string, number>,
  game: GameState,
  ship: ShipState,
  world: WorldState,
) => void

Custom action handler — called for type: 'custom' actions. snap is null for triggers that have no signal (e.g. run_start, aura, timer).

CustomTickFn

type CustomTickFn = (
  dt: number,
  params: Record<string, number>,
  game: GameState,
  ship: ShipState,
  world: WorldState,
) => void

Custom tick handler — called every frame for type: 'custom_tick' actions. Used for per-frame custom logic that doesn’t map cleanly to a one-shot action.

EnemyStatusType

type EnemyStatusType = 'stunned' | 'shredded' | 'burning'

Lightweight status effects on enemies.

EnemyStatusEntry

interface EnemyStatusEntry {
  stacks: number
  timer: number
  value: number
}
FieldMeaning
stackscurrent stack count (shredded stacks, burning stacks)
timerseconds remaining before status expires
valueeffect magnitude per stack (DPS for burning, +%dmg for shredded)

Cross-references

EXTRACT-CANDIDATE

  • Damage tag taxonomybullet | energy | bomb | fire are the four exclusive weapon tags; true and boss are engine-internal-only. Belongs on a combat/damage-tags concept page if one exists, or a Styx roll-up.
  • Built-in condition catalog (18 types) — extract to effects/conditions page as a canonical reference table.
  • Built-in action catalog (~24 types) — extract to effects/actions page as a canonical reference table.
  • Banner throttle rule — “1 banner per artifact per 0.5s” lives in the banner module; cross-reference from a UI/banner page.
  • Priority bands50 is the “effects band”; full band layout belongs on the signals page.
  • Trigger-type semantics table (signal/run_start/aura/timer/counter) — candidate for promotion to the effects/index overview if not already there.
  • Enemy status stack semanticsburning value = DPS-per-stack, shredded value = +%dmg-per-stack; promote to a combat/enemy-status concept page.