Skip to main content

Enemy Spawn Factory

enemy-spawn.ts contains factory functions that create fully-composed ECS entities for each enemy type.

Spawn Functions

FunctionEntityKey Components
spawnFlowerTrap()Hostile flowerPosition, EnemyState, IsHostile, PetalColors, ChunkBounds
spawnNeutralFlowerTrap()Passive flowerSame minus IsHostile
spawnPathMonster()Path sweeperPatrolNodes, SweepDirection, DespawnAtNodeIndex
spawnTreeBranchSnatcher()Drop attackerSpawnPosition (returns to perch), BoundingRadius
spawnRootSnare()Ground trapIsLethal, BoundingRadius (3 studs)
spawnNeutralRootSnare()Visible trapSame minus IsLethal
spawnGigglingBush()50/50 gambleIsLethal (random), BoundingRadius
spawnWatcher()BossTargetPlayer, PatrolNodes
spawnLostQuirkymal()Rescue targetDownedUntil (30s timer), OwnerPlayer
spawnMimic()Fake quirkymalIsMimicEntity tag, appears as LostQuirkymal

Entity Assembly Pattern

Each spawn function follows the same pattern:

export function spawnFlowerTrap(
position: CFrame,
chunkBounds: { min: Vector3; max: Vector3 },
petalColors: Color3[],
): Entity {
const entity = world.entity();

// Core components
world.set(entity, Position, position);
world.set(entity, SpawnPosition, position);
world.set(entity, EnemyType, "FlowerTrap");
world.set(entity, EnemyState, "Idle");
world.set(entity, StateSince, os.clock());

// Type-specific components
world.set(entity, IsHostile, true);
world.set(entity, PetalColors, petalColors);
world.set(entity, ChunkBounds, chunkBounds);

// Network sync
world.set(entity, NetworkId, HttpService.GenerateGUID());
world.add(entity, NeedsSync);
world.add(entity, IsFlowerTrap);
world.add(entity, IsEnemy);

// Clone and attach model
const model = cloneEnemyModel("FlowerTrap");
world.set(entity, ModelRef, model);

return entity;
}

NetworkId Generation

Each entity gets a unique HttpService.GenerateGUID() used for:

  • Client ↔ server entity identification
  • Rescue requests (Lost Quirkymal)
  • Despawn targeting

Model Cloning

Models are cloned from ReplicatedStorage.Assets.Enemies.[TypeName] and positioned at the entity's spawn CFrame. The ModelRef component holds the reference for behavior systems to animate.

Despawn

export function despawnEnemy(entity: Entity) {
const model = world.get(entity, ModelRef);
if (model) model.Destroy();
world.delete(entity);
}