Skip to main content

Enemy System

Enemies are managed through the ECS (see ECS System) with spawning orchestrated by EnemyService.

Enemy Types

Hostile Entities

EntityDamageSpeedBehavior
FlowerTrap2010Weeping Angel — can't move while observed
PathMonsterInstant kill36Charges down path, must hide to survive
TreeBranchSnatcher2040Drop attack from above
RootSnare20 + 3s immobilize0Ground trap
GigglingBushKill (50% chance)050/50 lethal/harmless
WatcherInstant kill6Boss — hunts off-path players

Neutral Entities

  • NeutralFlowerTrap — Same appearance as hostile, never attacks
  • NeutralRootSnare — Visible roots, safe to step over
  • LostQuirkymal — Downed character to rescue (30s timer)
  • MimicEntity — Appears as Lost Quirkymal, attacks rescuers

Spawning

Enemy placement is pre-computed for the whole run by planEnemies (pressure-based) and materialized per chunk by EnemySlotGenerator. See Enemy Spawning Pipeline for the full flow: planner → policy-filter → slot generator → dispatch.

Quick summary:

// Once per run (RunDirector.buildRunPlan)
const enemyRunPlan = planEnemies(seed, ENEMY_BALANCE, pacingManager, chaseSections);

// Per chunk
const slots = generateEnemySlots(geometry, plannedPlacements, rng);
for (const slot of slots) dispatchEnemySpawn(slot);

Per-enemy tuning (pressure, minTrail, maxPerRun, cooldownMarkers, compatibility) lives in apps/enemies/server/balance/enemy-balance.ts.

Petal Palette

FlowerTrap colors are generated per session using petal-colors.ts. Both hostile and neutral traps share the same color pool — players can't distinguish by color alone.

Enemy Registry

enemy-registry.ts defines detection ranges and behaviors:

FlowerTrap: { triggerRadius: 16, chaseRadius: 50, attackRange: 5 }
PathMonster: { triggerRadius: 40, speed: 36 }
Watcher: { triggerRadius: 60, speed: 6 }
RootSnare: { triggerRadius: 3, speed: 0 } // trap

PathMonster Sweep

PathMonsters follow the global path node array:

  1. Pick direction (forward or backward)
  2. Sweep through nodes at 36 studs/sec
  3. Kill any unhiding player within 8 studs
  4. Despawn at +2 chunks from player

Watcher Boss

  • Spawns when player is off-path for 30+ seconds
  • Follows path nodes toward player
  • Direct pursuit when close
  • Cannot enter safe zones
  • Debug logs gated behind WatcherLogs flag