The pack
wavePlan
function wavePlan(): Pack<{ wavePlan: WavePlanApi }>
The wave-plan pack: wave plans, difficulty curves, goals and catalogues. Pure state, so it needs no backend and runs headless. No options.
Helpers
createCatalogue
function createCatalogue(wanted: readonly string[]): Catalogue
Creates a Catalogue of the names to capture.
createCurve
function createCurve<K extends string>( ranges: Record<K, CurveRange>, opts: CurveOptions<string> = {}, ): DifficultyCurve<K>
Creates a DifficultyCurve from named ranges.
createWaves
function createWaves(opts: WavePlanOptions): WavePlan
Creates a WavePlan.
goals
const goals: { clear(left: () => number, label?: string): Objective; count(need: number, got: () => number, label?: string): Objective; survive(seconds: number, label?: string): Objective; ... 7 more ...; any(list: Objective[], label?: string): Objective; } …
The objective templates. Each returns a plain Objective.
Classes
Catalogue
class Catalogue
One of each — Web Wars' trophy room. Not a counter: WHICH ones you have is the game, and the missing list is what the HUD shows.
readonly wanted: readonly string[]— The full list of names to capture.constructor(wanted: readonly string[])capture(name: string): boolean— True if this one was new.has(name: string): boolean— True ifnamehas been captured.get have(): string[]— The wanted names already captured, in wanted order.get missing(): string[]— The wanted names not yet captured.get complete(): boolean— True when every wanted name is held.clear(): void— Removes every captured name.objective(label = 'CAPTURE'): Objective— As anObjective, so it binds to a HUD like any other.
DifficultyCurve
class DifficultyCurve<K extends string>
Difficulty as data. Every number a wave, an enemy or a drain rate needs is
declared once as a range and read as curve.at(level).speed — so raising the
ramp is one edit, not a hunt through the game for magic numbers.
level: number— The current level. Set it as the game advances.constructor(ranges: Record<K, CurveRange>, opts: CurveOptions<string> = {})at(level = this.level): Record<K, number>— Every value atlevel(default: the current one).value(name: K, level = this.level): number— One value, without building the object.unlocked(name: string, level = this.level): boolean— Has this enemy type been introduced yet?available(level = this.level): string[]— Everything introduced bylevel— the pool a wave draws from.
WavePlan
class WavePlan
A declarative spawn schedule. loop counts completed passes through the
list, which is the number every per-loop mutator is a function of.
index: number— Index intowaves.loop: number— Completed passes through the whole list.time: number— Seconds into the current wave.readonly waves: WaveDef[]— The wave list, as given tocreateWaves.finished: boolean— A non-looping plan that has run out.constructor(opts: WavePlanOptions)get wave(): WaveDef | undefined— The current wave, or undefined when there is none.get spent(): boolean— Every entry has fired and (if given)clear()reads true.start(index = 0): void— Starts the wave atindexwith its clock at zero. Default 0.update(dt: number): void— Fires the spawn entries that are due and advances a spent wave. Call once a frame.next(): void— Force the next wave — a superzapper, or a debug skip.reset(): void— Back to wave 0 withloopat 0 — a new game.
Types
CurveOptions
interface CurveOptions<K extends string>
Configures createCurve.
over?: number— Level at which every range reaches itsto. Default 10.ease?: 'linear' | 'ease' | 'step'— Shape of the ramp.easestarts gently;stepsnaps per level.unlock?: Partial<Record<K, number>>— Minimum level at which a named thing appears at all.
CurveRange
type CurveRange = [number, number]
[from, to] — the value at level 0 and at the level the curve tops out.
Objective
interface Objective
A win/lose rule a HUD can draw. Get one from goals or Catalogue.objective.
readonly label: string— The name the HUD shows.readonly progress: number— 0..1. A HUD can draw any objective without knowing which kind it is.readonly done: boolean— True once the objective is met.readonly failed: boolean— True once the objective is lost.update(dt: number): void— Steps the rule. Call once a frame.reset(): void— Back to the start: not done, not failed, progress 0.
SpawnEntry
interface SpawnEntry
One scheduled spawn within a wave.
type: string— Passed straight back toonSpawn; the game decides what it means.count?: number— How many. Default 1.at?: number— Seconds after the wave starts. Default 0.every?: number— Seconds between them whencount> 1. Default 0 — all at once.data?: unknown— Anything else the spawner needs: a lane, a depth, a formation.
WaveDef
interface WaveDef
One wave: what spawns, and what holds it open.
name?: string— A label for the HUD or the wave card.spawn: SpawnEntry[]— The spawn entries this wave fires.clear?(): boolean— Hold the wave open until this reads true, even once everything has spawned. Omit and the wave ends when its last entry has fired.
WavePlanApi
type WavePlanApi = typeof kit
game.wavePlan: every builder and type this pack carries, as one namespace.
WavePlanOptions
interface WavePlanOptions
Configures createWaves.
waves: WaveDef[]— The waves, played in order.loop?: boolean— Start again at wave 0 withloopincremented. Default true.onSpawn(entry: SpawnEntry, index: number, plan: WavePlan): void— Called once for each spawn.indexcounts the fires of this entry, from 0.onWave?(wave: WaveDef, index: number, plan: WavePlan): void— Fired as each wave starts.onLoop?(loop: number, plan: WavePlan): void— Fired when the last wave of a loop clears.


