miaoda-game-wave-core
v0.2.0
Published
Engine-agnostic enemy-spawn director. Covers both models games actually use: discrete waves (tower defense, boss rush, clear-the-room — ordered waves of groups that advance on cleared/spawned/timer) and continuous pressure (survivors-likes, arenas, AI dir
Maintainers
Readme
miaoda-game-wave-core
Use this engine-independent package to decide what enemies spawn, when they spawn, and which named spawn point they use. It emits plain SpawnInfo records; your game instantiates prefabs, places entities, and reports deaths.
Install
pnpm add miaoda-game-wave-coreChoose a pacing model
| Model | Best for | Completion |
| --- | --- | --- |
| WaveRunner | Tower defense, rooms, boss rushes | Ordered waves with cleared/spawned/timer rules |
| Director | Survivors-like arenas and continuous pressure | Runs until stop() |
Both use seconds, accept an optional seed, and advance only when you call update(dt).
Discrete waves
import { WaveRunner } from 'miaoda-game-wave-core';
const runner = new WaveRunner({
seed: 42,
waves: [
{ groups: [{ type: 'goblin', count: 5, interval: 0.5, spawnPointId: 'left' }], restDelay: 3 },
{ name: 'boss', groups: [{ type: 'ogre' }], completeOn: 'cleared' },
],
});
runner.onSpawn(({ type, spawnPointId }) => spawnEnemy(type, spawnPointId));
runner.onWaveComplete((index) => showWaveComplete(index));
runner.onComplete(() => finishLevel());
runner.start();
// Fixed or frame update, in seconds:
runner.update(dt);
// When an enemy owned by the active cleared wave dies:
runner.reportKilled();Groups run on their own delay/interval clocks and may use a fixed type or weighted table. completeOn defaults to cleared; use spawned when survival of old enemies should not block the next wave, or timer with duration for time-boxed pressure.
Continuous director
import { Director } from 'miaoda-game-wave-core';
const director = new Director({
seed: 42,
rate: [{ at: 0, value: 0.5 }, { at: 180, value: 6 }], // spawns per second
table: (elapsed) => elapsed > 120 ? { bat: 3, skeleton: 2 } : { bat: 5 },
maxAlive: 100,
spawnPoints: ['north', 'south'],
});
director.onSpawn((info) => spawnEnemy(info.type, info.spawnPointId));
director.start();Call director.reportKilled() for every tracked death. Otherwise maxAlive never frees capacity and spawning stops at the cap. stop() halts the run; a later start() begins a fresh run with reset time, alive count, spawn credit, and seeded RNG sequence.
Determinism and state
A fixed seed reproduces weighted types and spawn-point choices when update and death inputs are identical. WaveRunner.snapshot and Director.snapshot are observation/telemetry views only; neither class provides restore. Save authoritative spawned entities and scheduling state in your own game protocol if mid-wave restoration is required.
Public API
WaveRunner: ordered wave scheduling, phase/status events, and kill accounting.Director: rate curves, evolving weighted tables, warmup, and alive caps.sampleCurve,sampleOverTime: resolve time-varying numeric values.Rng,SpawnInfo,SpawnTable,Wave,SpawnGroup, and config types.
The core never loads assets, chooses coordinates, or detects deaths.
