bridge-phase-state.ts

Mutable state bundle shared across bridge subsystem modules. All closure-local state from createMission() is consolidated into a single MissionState object so extracted subsystem modules can read/write without capturing closure variables. Created once per mission, threaded through every subsystem function.

Role in the engine

When createMission() was monolithic, subsystem code lived inside its closure and read free variables. After extraction, those variables had to be hoisted into an explicit container so the new modules could be pure functions over state. This file is that container — no behavior, only typed shape + a zero-value factory.

Exports

SymbolKindPurpose
MissionStateinterfaceTyped shape of the shared mutable bundle.
createMissionState(ctx, canvas, renderer, runDef, callbacks, missionResult, seed, dpr)functionAllocate a fresh MissionState with defaults; called once at the top of createMission().

MissionState fields by group

Canvas / renderer refs

  • ctx: CanvasRenderingContext2D — main 2D context.
  • canvas: HTMLCanvasElement — backing canvas element.
  • renderer: Renderer — engine renderer instance.

Run configuration

  • runDef: RunDefinition — full run config blob (player, timing, etc.).
  • callbacks: BridgeCallbacks — host hooks back into the React/metagame shell.
  • missionResult: MissionResult — accumulator written through the run, returned on exit.
  • seed: number — RNG seed for the mission.

Hot-path config locals (destructured for perf)

  • autoAimRange — from runDef.player.autoAimRange.
  • deadzone — from runDef.player.deadzone.
  • enemyFlashDecay — from runDef.timing.enemyFlashDecay.
  • damageFlashDecay — from runDef.timing.damageFlashDecay.
  • deathTimerDuration — from runDef.timing.deathTimer.

DPR

  • frameDpr: number — device-pixel-ratio captured at frame start.

Death / revive state

  • deathElapsed: number — seconds since death began (0 initially).
  • deathDefianceUses: number — defiance revive charges consumed this mission.
  • revivePromptActive: boolean — true while the revive-or-decline prompt is up.
  • revivePromptTimer: number — countdown for the prompt.
  • deathCinematicPlaying: boolean — gates the death sequence.
  • deathCinematicTimer: number — seconds into the cinematic.
  • postDeclineDeath: boolean — true after the player declined revive.

Mission complete timer

  • completeElapsed: number — seconds since mission-complete fired.

Level-up ring VFX

  • levelupRing: { x, y, l, ml, maxRad } | null — single transient ring; l/ml are life/max-life, maxRad is target radius.

Engine trail buffer

  • trail: Array<{ x, y }> — circular buffer of trail samples.
  • trailIdx: number — write head.

Terrain expansion throttle

  • lastExpandGridX, lastExpandGridY — last grid cell that triggered expansion (init -99999).
  • lastFastExpandTime: number — last wall-clock for fast-expand throttle.

Frame scheduling

  • rAF: number | null — current requestAnimationFrame handle.
  • paused: boolean — frame loop pause flag.
  • frameErrors: number — cumulative frame-error count.

Thermal rest

  • thermalRest: boolean — true while the mission is paused for thermal rest.
  • thermalRestSnapshot: HTMLCanvasElement | null — frozen frame shown during rest.

Saved weapon modes

  • savedWeaponModes: Array<{ fireMode: 'auto' | 'manual' }> — per-slot fire-mode snapshot; defaults to four 'auto' entries.

Timed weapon-chest schedule

  • wcNextSpawnTime: number — next chest spawn time (init -1).
  • wcChecksDone: number — completed spawn checks.
  • wcSpawnedThisLevel: number — chests spawned in current level.
  • wcPendingChest: any — staged chest awaiting spawn slot.

Dynamic artifact drop state

  • artifactNextDropTime: number — next eligible drop time (init -1).
  • artifactLastKillSnapshot: number — kill count at last snapshot.
  • artifactLastSnapshotTime: number — wall-clock of last snapshot.

Illumination / trigger systems

  • illuminationSystem: IlluminationSystem | null — populated after init.
  • triggerSystem: TriggerSystem | null — populated after init.
  • zoneDebugEnabled: boolean — toggle for zone debug overlay.
  • hubSpokeWireframeEnabled: boolean — toggle for hub-spoke wireframe overlay.

Event pool (for level advance)

  • eventPool: any[] — pending events drained on level-advance.

Init planet refs

  • initPlanet: any — initial planet ref captured at boot.
  • initLevelData: LevelData | undefined — initial level-data blob.

createMissionState defaults

All numeric counters start at 0, all booleans at false, all reference fields at null/undefined/[]. The five hot-path config locals are pulled from runDef. lastExpandGridX/lastExpandGridY are seeded to -99999 so any real grid cell triggers expansion on first frame. wcNextSpawnTime and artifactNextDropTime are seeded to -1 (sentinel: “not yet scheduled”). savedWeaponModes is pre-populated with four 'auto' slots.

Imports

  • RunDefinition from ../data/run-config
  • MissionResult from ../data/mission-result
  • BridgeCallbacks from ./bridge-types
  • Renderer from ./rendering/renderer
  • IlluminationSystem from ./world/illumination
  • TriggerSystem from ./world/trigger-system
  • LevelData from ./world/chunk-manager

Lifecycle

  1. createMission() calls createMissionState(...) once at the top.
  2. The returned object is threaded into every extracted subsystem function as a state parameter.
  3. Subsystems mutate fields in place — no copies, no events.
  4. On mission end, the host reads missionResult (which subsystems have populated through the run) and discards the rest.

Anti-patterns

  • Subsystem modules must not capture closure variables from createMission() — everything lives on MissionState.
  • No method-style behavior belongs on MissionState; it is a data bag, not a class.
  • Field types any (wcPendingChest, eventPool, initPlanet) are deliberate escape hatches pending stronger typing of those subsystems.

EXTRACT-CANDIDATE

  • The any-typed fields (wcPendingChest, eventPool[], initPlanet) are extraction targets — each should be replaced with a concrete type owned by its subsystem module (weapon-chest, event-pool, world-init) and re-imported here.
  • The four field groups for weapon-chest scheduling, artifact drop, terrain throttle, and thermal rest read like cohesive sub-bundles; each could be lifted into its own WeaponChestState / ArtifactDropState / TerrainExpandState / ThermalRestState nested object to shrink the flat surface.
  • savedWeaponModes hard-codes 4 slots; if slot count ever becomes data-driven the default initializer needs to derive length from runDef.