PURPOSE

Centralized data table for gameplay-side tunable constants. Holds every numeric balance dial that governs movement, heat, warp, damage, camera, world geometry, entity pool ceilings, pickups, spawner cadence, collision, mission objectives, boss flow, vision, revive, events, portals, and mission timers. The whole file is one frozen-shape CFG literal exported by name so the rest of the engine reads but does not mutate it.

OWNS

  • The CFG object literal — single source of truth for gameplay constants.
  • Movement constants: DRAG_BASE, SPEED_LERP.
  • Heat system constants: HEAT_DANGER, HEAT_STALL_DMG, HEAT_THR_RS, HEAT_THR_RA, HEAT_ZOOM_S, HEAT_ZOOM_A, HEAT_SMOKE_S, HEAT_DIL_S, HEAT_DIL_A, HEAT_COOL_RAMP, HEAT_COOL_MAX.
  • Warp drive constants: WARP_DURATION, WARP_SPEED, WARP_EXIT_ENEMY_RANGE, WARP_SPAWN_INTERVAL, WARP_SPAWN_DIST_MIN, WARP_SPAWN_DIST_MAX, WARP_MAX_ON_MAP.
  • Damage balance: PLAYER_DAMAGE_MULT, ENEMY_DAMAGE_CAP, INVULN.
  • Camera: CAMERA_LERP, BASE_ZOOM (computed once at module load from window.innerWidth with a 0.85× multiplier under 900px and a further linear pinch under 480px).
  • World geometry: CHUNK, INNER_BELT_RADIUS, INNER_BELT_WIDTH, OUTER_BELT_RADIUS, OUTER_BELT_WIDTH.
  • Entity pool ceilings: MAX_ENEMIES, MAX_ENEMY_BULLETS, MAX_HORDE_ENEMIES, MAX_STRUCTURES, MAX_JUNK, MAX_PARTICLES, MAX_XP_ORBS, MAX_WARNINGS, MAX_SFX_PER_RAF.
  • Pickup system: MAGNET_SPEED, MAGNET_SNAP_MULT, MAGNET_COLLECT_DIST, XP_COLLECT_RADIUS, ORB_LIFETIME.
  • Spawner tuning: PROXIMITY_INTERVAL, PROXIMITY_COUNT, PROXIMITY_DISTANCE, PROXIMITY_WARN_TIME, PATROL_BASE_TIMER, WAVE_MIN_PROGRESS, WAVE_INTERVAL.
  • Collision grid: GRID_CELL_SIZE.
  • Mission objectives: BEACON_CHARGE_TIME, BEACON_RADIUS.
  • Boss legacy + room + intro sequence: BOSS_INTRO_TIME, BOSS_ROOM_MARGIN, BOSS_ROOM_SHRINK_DURATION, BOSS_ROOM_INITIAL_SCALE, BOSS_ROOM_WALL_THICKNESS, BOSS_ROOM_SIZE_MULT, BOSS_INTRO_DURATION, BOSS_INTRO_FADE_DURATION, BOSS_INTRO_PAN_DURATION, BOSS_INTRO_HP_FILL_DURATION, BOSS_INTRO_SQUASH_DURATION, BOSS_INTRO_PAN_BACK_DURATION, BOSS_DEATH_SEQUENCE_DURATION, BOSS_ENRAGE_TIME, BOSS_ENRAGE_FIRE_MULT, BOSS_ENRAGE_SPEED_MULT, BOSS_ZOOM, BOSS_HP_SCALE_PER_LEVEL, BOSS_MAX_ENEMY_BULLETS, BOSS_LOOT_XP_COUNT, BOSS_LOOT_WEAPON_CHESTS, BOSS_LOOT_ARTIFACTS_A.
  • Vision defaults: VISION_DEFAULT (base_radius, dim_width, dark_opacity, fog_density, ambient_light).
  • Death-defiance / revive: REVIVE_HP_PCT, REVIVE_INVULN_TIME, REVIVE_DISMISS_TIME.
  • Event system: EVENT_TYPES, EVENT_CHARGE_TIME, EVENT_RADIUS.
  • Portal room: PORTAL_ROOM_RADIUS, PORTAL_BASE_RADIUS, PORTAL_VFX_START_RADIUS, PORTAL_VFX_GROWTH_RATE, PORTAL_VORTEX_RAMP, PORTAL_VORTEX_RADIAL, PORTAL_VORTEX_TANGENTIAL.
  • Mission base timers map: MISSION_BASE_TIMERS keyed by beacons, extraction, wanted, chase, scavenge, gauntlet, holdout, infiltrate, convoy, exterminate.
  • The named re-export _IS_MOBILE for downstream perf-flag consumers.

READS FROM

  • ../device-capabilities — imports isMobile() to populate the module-local _IS_MOBILE boolean. That boolean is computed once at module load.
  • window.innerWidth (browser global) — read once inside the BASE_ZOOM IIFE to compute a screen-width-conditioned zoom. Falls back to 0.675 when window is undefined (SSR / Node test environments).

PUSHES TO

Nothing. The module performs no writes, dispatches no events, and triggers no side effects beyond evaluating its constant table at import time. Consumers import CFG (or _IS_MOBILE) by name and read fields directly.

DOES NOT

  • Does not freeze or seal the CFG object — runtime mutation is possible but not the intended contract.
  • Does not register listeners on resize, orientation change, or any other event — BASE_ZOOM is a one-shot snapshot at module load, not a reactive value.
  • Does not vary any pool ceiling by device tier inside this file — _IS_MOBILE is exported but only the BASE_ZOOM IIFE inspects viewport width here. Mobile-tier budget cuts live elsewhere (perf-flags layer).
  • Does not own boss intro orchestration timing — the constants here must stay summed to match boss-fight.ts updateIntro sub-phase totals, but the orchestration itself is not in this file.
  • Does not own per-mission spawn data, enemy stats, weapon stats, artifact stats, passive stats, or progression curves — those live in data tables under data/ and balance code elsewhere.

Signals

None. No emitter, no observer, no callback registration.

Entry points

  • import { CFG } from '...config/_gameplay' — direct field reads by every gameplay system that needs a tunable.
  • import { _IS_MOBILE } from '...config/_gameplay' — perf-flag layer reads this to gate mobile-specific budgets and effects.
  • CFG.BASE_ZOOM — computed at module evaluation; treated as a constant by the camera system.
  • CFG.MISSION_BASE_TIMERS[<mission-type>] — keyed lookup used by mission setup code to seed countdown clocks.
  • CFG.VISION_DEFAULT — read as a starting object by the vision system before any per-biome overrides.

Pattern notes

  • Pure data module — content in a data table, no behavior. Aligns with the project rule that stats, rates, and curves live in data while logic lives in functions.
  • Single declaration export const CFG = { ... } keeps all gameplay-balance dials co-located so a balance pass touches one file.
  • BASE_ZOOM is the only computed field; it uses a self-invoked function expression to fold viewport-dependent logic into the literal. The SSR guard (typeof window === 'undefined') keeps the module safe to import from test or build contexts.
  • Comment-block headers group fields by subsystem (MOVEMENT / HEAT SYSTEM / WARP DRIVE / DAMAGE BALANCE / CAMERA / WORLD GEOMETRY / ENTITY POOL LIMITS / PICKUP SYSTEM / SPAWNER TUNING / COLLISION / MISSION OBJECTIVES / BOSS / BOSS ROOM / VISION SYSTEM / DEATH DEFIANCE / REVIVE / EVENT SYSTEM / PORTAL ROOM / MISSION TYPE CONFIGS). The grouping is convention only — the runtime shape is one flat object.
  • BOSS_INTRO_DURATION is a derived total that must equal the sum of the five sub-phase durations (BOSS_INTRO_FADE_DURATION + BOSS_INTRO_PAN_DURATION + BOSS_INTRO_HP_FILL_DURATION + BOSS_INTRO_SQUASH_DURATION + BOSS_INTRO_PAN_BACK_DURATION). Edits to any sub-phase require updating the total in lockstep.
  • Tuple-typed range fields (PROXIMITY_INTERVAL, PROXIMITY_COUNT, PROXIMITY_DISTANCE, WAVE_INTERVAL) are written as two-element arrays for [min, max] ranges sampled by spawner code.
  • Pool ceilings carry an inline comment marking the deliberate 10× headroom raise so the kill-cap curve, bullet pool, and orb storage saturate at peak difficulty.
  • EVENT_TYPES is a string array used as a discrete enum domain; new event kinds added elsewhere must be registered here for the spawner to pick them.
  • _IS_MOBILE is the only export besides CFG and is re-exported explicitly so downstream perf-flag code can avoid re-importing device-capabilities directly.
  • No backwards-compatibility shims, no aliases — every consumer reads canonical field names.