Skip to content

Biomass Wave Spawning + Boss/Elite/Minion Taxonomy — Design

Biomass Wave Spawning + Boss/Elite/Minion Taxonomy — Design

Section titled “Biomass Wave Spawning + Boss/Elite/Minion Taxonomy — Design”

Status: draft for review (Phase 1 of the larger feedback batch) Date: 2026-06-28 Scope: Replace the cycle-22b survival ramp/boss-rotation with a wave-based, biomass-driven spawner and a Boss → Waves macro loop. Applies to survival + crystal modes (both use the survival spawner). Story/tutorial unaffected (they use authored encounters, story != null).

Spawning becomes a wave → clear → boss → repeat rhythm with a natural ebb and flow, where the amount of on-screen pressure is a single readable number (“biomass”), and bosses always get a clean arena. This is the canonical fix for the “central cluster + bosses lost in the swarm” feel.

A Sim.spawn_phase enum drives everything (survival/crystal only; guarded by story == null):

  1. WAVES (duration WAVE_PHASE_S = 300s): waves spawn (see below).
  2. At 300s → WIND_DOWN: no new waves; the player clears whatever’s left.
  3. When total_biomass == 0BOSS_PREP (10s): show “BOSS APPROACHING” + a countdown.
  4. After 10s → BOSS: spawn one boss into the now-empty arena.
  5. Boss defeated → REST (10s): show “AREA CLEAR” → back to WAVES (next cycle; endless).

Timers + the current phase are render-readable so the HUD can show wave/boss banners.

  • Every standard enemy and elite carries a biomass score (a new EnemyPool column, swap-removed in lockstep). Bosses and minions carry 0 (excluded from pressure accounting).
  • Sim.total_biomass() = sum of the column over live enemies. Cheap; computed on each wave check.
  • Biomass ≈ how much pressure that enemy adds. Proposed starting values (tunable in bible.json):
    • swarmer 2 · spider 3 · shooter 4 · splitter 4 · scatterer 6 · zapper 6 · orbiter 8 · lancer 8 · bomber 9 · ghost 10 · pyromancer 10 · elite 12 · brute 16 · accumulator 16
    • Elites: Warden 110 · Boss2 100 (high — usually alone in a wave).
  • BIOMASS_TRIGGER = 100 (a wave fires when total drops below this) · BIOMASS_TARGET = 400 (spawn until total ≥ this).
  • Standard enemies — spawn in large waves, low biomass. (The current spawn pool minus zapper/rusher, which stay removed from the normal pool per earlier feedback.)
  • Elites (Warden, Boss2 — demoted from bosses) — spawn in waves at hard-coded times, high biomass, coexist with the wave (do NOT clear the arena). Keep their existing attack patterns. Still pooled BEHAVIOR_BOSS entities, but counted as biomass and spawned by the wave system.
  • Bosses (FunZo, Graviton, Eye now; Giant Zombie added in a follow-up chunk) — spawn only in the BOSS phase, alone, biomass 0. Rotate through the pool per boss cycle.
  • Minions — boss-summoned, downgraded standard enemies, low HP, biomass 0 (a per-enemy minion flag, or biomass set to 0 at summon). Existing boss adds (FunZo jesters, boss-add trickle) become minions.

When a wave fires (total_biomass < 100, in the WAVES phase):

  1. Elites first (non-probabilistic): each elite has a hard-coded spawn time (e.g. Warden @ 90s, Boss2 @ 210s — tunable). If an elite is due this cycle and not yet spawned, it’s added to the wave before anything else.
  2. Probabilistic fill: sample the time-based probability table (below) and spawn standard enemies at off-screen ring positions (existing SPAWN_RING) until total_biomass >= 400, then stop. All in one tick. A “WAVE INCOMING” banner shows.
  • A fixed const SPAWN_TABLE: Array of probability vectors, one entry per 30s of run time. Each vector maps enemy-type → weight, summing to 1. Most weights are 0 (only a few types active at once). Authored to keep the mix varied over time (early: swarmer/spider; mid: + shooter/scatterer/ ghost/orbiter; late: + lancer/bomber/accumulator/brute/pyromancer).
  • For a time t, lerp between the floor/ceil 30s entries.
  • Endless: once past the authored table, loop a designated endless segment (the last N entries) so the mix keeps cycling. (Auto-generation deferred — a fixed looping tail is simpler + balanceable.)
  • Sampling + spawning is deterministic via Sim.rng.

A small centered banner (new lightweight HUD element or reuse RegionTitle’s sweep) shows:

  • “WAVE INCOMING” when a wave fires.
  • “BOSS APPROACHING N” countdown during BOSS_PREP.
  • “AREA CLEAR” during REST. Driven by render-reads of spawn_phase + the phase timer (no sim coupling).

The spawner is rewritten, so the survival determinism baseline WILL change — a one-time, honest re-pin of snapshot_string().hash() + state_checksum() in test_determinism_checksum.gd, noted in CLAUDE.md. The sim stays deterministic (waves/biomass/boss-gate all flow through Sim.rng; run twice → identical). The crystal determinism baseline re-pins in lockstep.

These are the rest of the batch, done after the spawn system ships + is playtested:

  • Giant Zombie boss (promote the Brute) — a new boss + minions. Built right after the spawner so the boss pool is initially FunZo/Graviton/Eye; Giant Zombie joins once built.
  • Dash rework (Spider + Accumulator: slower dash, no pause-between, lower frequency → ~1.5× avg speed, less agile).
  • Spider-web: much longer duration.
  • Passive HP regen (small) + convert stand-still regen into an upgrade.

Open tunables (safe to pick now, tune later)

Section titled “Open tunables (safe to pick now, tune later)”
  • Exact biomass values, BIOMASS_TRIGGER/TARGET, WAVE_PHASE_S, prep/rest 10s, elite spawn times, and the full probability table. I’ll author sensible starting values; all live as constants/data.