Phaser AE

Platform rounds

Pro packv3.0.0-32DArcade

The game-rule layer for side-view arcade platform games: objectives, bonus timers, pickups, bumps and kicks, spawns, deaths, lives, stage sequences and difficulty. Each stage is data, and the session runs it headless at a fixed step, so a level, a remix or a whole arcade game is a definition rather than new code.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 14, 2026 · updated October 19, 2026

Platform rounds: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

platformRounds

function platformRounds(): Pack<{ platformRounds: typeof api }>

Install the renderer-independent platform rounds API.

Helpers

bumpModule

function bumpModule(): RoundsModule<BumpModuleState>

Floors bumped from below, kicks, POW, Slipice, icicles and fireballs: the Mario Bros family, as a module.

createRounds

function createRounds(def: RoundsDef): Rounds

Create a game session from a definition. Call attract() or start() before ticking.

difficultyPreset

function difficultyPreset(name: "easy" | "normal" | "arcade"): DifficultyScale

The three standard scales. Arcade reproduces the original tables; Normal is the human-playable default.

Constants

BUMP_MODULE

const BUMP_MODULE: "bump"

The id bumpModule runs under: its state is RoundsState.modules.bump.

Types

BumpDef

interface BumpDef

Floors bumped from below (Mario Bros): flips, kills and kicks.

  • radius?: number — Cells either side of the bumped cell that are affected. Default 1.
  • kick?: { scores: number[]; window?: number } — Kicking flipped enemies: the chain of points; a kick within window seconds of the last advances it.
  • afterKick?: { emitter: string; kind: string; delay?: number; skipLast?: boolean } — After each kick, emit this kind from this emitter (Mario Bros coin wafers).

BumpModuleState

interface BumpModuleState

The bump module's state: POW uses, the kick chain, and what it keeps per enemy. Plain data, saved in snapshots.

  • pow: number — POW uses left.
  • powFull: number — POW uses when full; 0 until the first stage with a POW block.
  • kickChain: number — Kicks in the current chain, less one.
  • kickTime: number — When the last kick happened, in seconds of play on the stage.
  • drops: Record<string, number> — By enemy id: seconds after appearing that it lets go and drops (icicles), or -1 once it has.
  • freezes: Record<string, number> — By enemy id: grounded seconds before a Slipice freezes its floor (freeze.at: 'anywhere').
  • grounded: Record<string, number> — By enemy id: seconds a Slipice has walked on a floor.

DifficultyScale

interface DifficultyScale

Difficulty and population scaling shared by every stage (Easy / Normal / Arcade presets, or your own).

  • name: string — Display name.
  • speed: number — Global enemy speed multiplier (platform-enemies speed scale). Arcade 1.
  • interval: number — Multiplier on every emitter interval (larger: fewer enemies). Arcade 1.
  • timer: number — Multiplier on bonus timer step intervals (larger: more time). Arcade 1.
  • aggression: number — 0..1 multiplier the presets apply to aggressive choices (barrel steering, wild barrels, chasers). Arcade 1.
  • population: number — Multiplier on enemy population caps (emitter max, roster sizes where a preset honours it). Arcade 1.
  • grace: number — Multiplier on how long stunned enemies stay flipped and on hammer time (larger: kinder). Arcade 1.

FeetRef

type FeetRef = { x: number; feet: number } | { marker: string; index?: number; dx?: number }

Where a body stands: feet position or a marker's floor.

InterludeDef

interface InterludeDef

An intermission played before or after a stage (presentation reads kind and phaseTime).

  • kind: string — Free name read by presenters: 'how-high', 'carry', 'fall', 'phase' ...
  • seconds: number — Seconds.
  • everyAttempt?: boolean — Play it before every attempt, not only the first (Donkey Kong's "How high can you get?"). Default false.

JumpOverDef

interface JumpOverDef

Jump-over scoring: points for clearing enemies in one jump (Donkey Kong: 100, 300, 800).

  • scores: number[] — Points by count: index 0 for one enemy, the last entry for that many or more.
  • below?: number — How far below the feet an enemy may pass and still count, world units. Default 24.
  • falls?: boolean — Also score when the player merely falls over enemies (not only in a jump). Default false.
  • shown?: number[] — Values to display instead of scores (Donkey Kong shows 800 for three barrels but pays 500). Default scores.

KindRules

interface KindRules

What happens when the player touches an enemy kind, bumps it, smashes it or jumps over it.

  • contact?: "kill" | "harmless" | "collect" — Touching it while active: the player dies ('kill', default), nothing ('harmless') or it is collected ('collect').
  • stunnedContact?: "kick" | "kill" | "harmless" — Touching it while flipped: kick it away ('kick', default for kinds with stun rules), 'kill' or 'harmless'.
  • collect?: number — Points for collecting it (contact: 'collect').
  • smash?: number | number[] — Hammer smash points; an array picks one at random per smash. Omit: a hammer cannot destroy it.
  • jump?: boolean — Counts toward jump-over scoring when the player jumps over it.
  • bump?: "flip" | "kill" | "none" — A floor bumped from below under it: 'flip' (platform-enemies hit rules), 'kill' (destroyed), 'none'. Default 'flip'.
  • bumpScore?: number — Points for a bump: per flip ('flip') or per kill ('kill'). Default 0.
  • pow?: "flip" | "kill" | "none" — The POW block destroys it while it touches a floor (or is airborne, with powAir). Default: flips it like a bump.
  • powAir?: boolean — POW also reaches it in the air. Default false.
  • powScore?: number — Points when a POW destroys it. Default bumpScore.
  • counts?: boolean — Counts toward a 'clear' objective. Default true.
  • dropAfter?: number | [number, number] — Seconds after spawning before a hanging body (flight profile) starts to fall (icicles).
  • shatterOnLand?: boolean — Removed when it lands after falling ('shatter'). Default false.
  • expire?: number — Removed this many seconds after spawning (fireballs that burn out).
  • freeze?: { at?: "centre" | "anywhere"; after?: number | [number, number]; material?: string; tag?: string } — Freezes the floor run it walks on into material, then is removed (Slipice): when it reaches the run's centre (at: 'centre', default) or after after grounded seconds (at: 'anywhere'). With tag, only runs whose cells carry that tag freeze. A run already of that material is passed over.
  • removeOutside?: boolean — Removed when it leaves the map through an open side. Default true.
  • edgeExit?: boolean — Removed when it reaches the side of the map it is heading for (fireballs crossing a wrapping screen). Default false.
  • young?: { seconds: number; contact: "destroy" | "harmless" } — While younger than seconds, touching it 'destroy's it (no points) or is 'harmless' (forming icicles).

ObjectiveDef

type ObjectiveDef = /** Reach a zone (Donkey Kong: Pauline's girder). */ | { type: "reach"; zone: ZoneRef; grounded?: boolean } /** Defeat `count` enemies of `kinds` (default: every counted kind), none left alive (Mario Bros phases). */ | { type: "clear"; kinds?: string[]; count?: number } /** Remove every map cell tagged `tag` by walking or jumping over it (Donkey Kong rivets). */ | { type: "cells"; tag: string; score?: number; replace?: CellSpec; over?: number } /** Take every item pickup (or those named `names`): gems, stars, keys. */ | { type: "pickups"; names?: string[] } /** Collect `count` enemies of `kinds` (contact 'collect' or bump 'kill'); `perfect` points when all are taken. */ | { type: "collect"; kinds: string[]; count?: number; perfect?: number | number[] }

A stage objective. Stages end when ALL their objectives are met.

PickupDef

type PickupDef = /** A hammer (Donkey Kong): smash enemies whose rules allow it; no jumping or climbing while held. */ | { type: "hammer"; id?: string; at: PointRef; w?: number; h?: number; duration: number; warn?: number; reach?: number; above?: number; profile?: WalkerProfileInput; pause?: number } /** A bonus item worth points (Pauline's umbrella, hat and purse). */ | { type: "item"; id?: string; name?: string; at: PointRef; w?: number; h?: number; score: number }

A pickup.

PickupState

interface PickupState

A pickup's state.

  • id: string — Id.
  • type: "hammer" | "item" — 'hammer' or 'item'.
  • name: string — Item name (render).
  • x: number — Centre x.
  • y: number — Centre y.
  • w: number — Width.
  • h: number — Height.
  • taken: boolean — Taken this attempt.

PointRef

type PointRef = { x: number; y: number } | { marker: string; index?: number; dx?: number; dy?: number }

A point given directly (x centre, y centre) or a map marker's cell centre (plus an offset).

PowDef

interface PowDef

A POW block: map cells tagged tag; bumping one from below hits every grounded enemy.

  • tag?: string — Cell tag. Default 'pow'.
  • uses?: number — Uses when full. Default 3.
  • refill?: boolean — Refill to full when this stage starts. Default false (uses carry over between stages).
  • score?: number — Points per POW. Default 0.

Rect

interface Rect

A world rectangle by its top-left corner and size.

  • x: number — Left, world units.
  • y: number — Top, world units (y down).
  • w: number — Width.
  • h: number — Height.

RespawnDef

interface RespawnDef

Where and how the player comes back after a death that does not restart the stage (Mario Bros).

  • at: FeetRef — Respawn point (feet).
  • platform?: { width: number; seconds: number; kind?: string } — A temporary platform under the respawn point: width and seconds before it vanishes (or the player walks off).

Rounds

interface Rounds

A running game.

  • readonly def: RoundsDef — The definition.
  • readonly state: RoundsState — A copy of the presenter-facing state.
  • readonly stage: StageDef — The current stage's resolved definition.
  • readonly map: PlatformMap — The current level model.
  • readonly movers: Movers — The current movers.
  • readonly world: WalkerWorld — The current walker world.
  • readonly enemies: EnemyDirector — The current enemy director.
  • readonly player: string — The player body id ('player').
  • readonly difficulty: DifficultyScale — The difficulty in force: the definition's, or the last setDifficulty. A copy.
  • levelStages(level: number): string[] — Stage ids of a level.
  • start(o?: { level?: number; index?: number; skipIntro?: boolean }): void — Start a new game (optionally at a level and stage index).
  • attract(o?: { level?: number; index?: number }): void — Show a stage without playing (attract mode: the player is hidden, enemies run).
  • tick(seconds: number, input: RoundsInput): number — Run whole fixed steps for elapsed seconds; returns the number run.
  • step(input: RoundsInput): void — One fixed step.
  • setDifficulty(d: DifficultyScale): void — Change the difficulty (applies to stages built from now on, and enemy speed at once).
  • setPlayerProfile(profile: WalkerProfile | WalkerProfileInput): void — Replace the player's movement profile now and for the rest of the game (respawns and hammers restore to it).
  • drain(): RoundsEvent[] — Events since the last drain.
  • snapshot(): RoundsSnapshot — Serialisable state.
  • restore(s: RoundsSnapshot): void — Restore a snapshot taken from a session with the same definition.

RoundsDef

interface RoundsDef

The whole game: stages, their order, the player, lives and timing.

  • stages: Record<string, StageSource> — Stages by id.
  • sequence: SequenceDef — Order.
  • player: { profile: WalkerProfile | WalkerProfileInput; tags?: string[] } — The player's body: its walker profile (a full profile or the fields that differ from the default) and tags.
  • lives?: number — Lives at the start (including the one in play). Default 3.
  • maxLives?: number — Most lives held. Default 9.
  • extraLives?: number[] — Scores that award an extra life (each once). Default none.
  • extraEvery?: number — After the last threshold, another life every this many points (0: none). Default 0.
  • timings?: { intro?: number; ready?: number; death?: number; clear?: number; over?: number } — Phase durations, seconds.
  • difficulty?: DifficultyScale — Difficulty scale. Default Arcade.
  • seed?: number — Seed. Default 1.
  • step?: number — Fixed step, seconds. Default 1/60.
  • hiScore?: number — Initial high score. Default 0.
  • maxSteps?: number — Cap on a tick's steps. Default 8.
  • modules?: RoundsModule[] — Rules modules, called in this order at fixed points of each step (see RoundsModule). Default [bumpModule()]: floors bumped from below, kicks, POW, Slipice, icicles and fireballs. Add a game's own after it, or leave it out where a game has none of those (its kind and stage fields then do nothing).

RoundsEvent

type RoundsEvent = | { kind: "game-start" } | { kind: "stage-start"; level: number; index: number; stage: string; attempt: number } | { kind: "phase"; phase: RoundsPhase } | { kind: "score"; points: number; reason: string; x: number; y: number; total: number } | { kind: "extra-life"; lives: number } | { kind: "death"; cause: string; x: number; y: number } | { kind: "life-lost"; lives: number } | { kind: "jump-over"; count: number; points: number; shown: number; x: number; y: number } | { kind: "spawned"; enemy: string; enemyKind: string; emitter: string; x: number; y: number } | { kind: "pickup"; id: string; type: "hammer" | "item"; name: string; x: number; y: number } | { kind: "hammer-end"; id: string } | { kind: "smash"; enemy: string; enemyKind: string; points: number; x: number; y: number } | { kind: "cell"; col: number; row: number; tag: string; points: number } | { kind: "bump"; col: number; row: number; x: number; y: number } | { kind: "flip"; enemy: string; enemyKind: string; result: "flipped" | "angered" | "unflipped" } | { kind: "kick"; enemy: string; enemyKind: string; points: number; chain: number; x: number; y: number } | { kind: "collect"; enemy: string; enemyKind: string; points: number; x: number; y: number } | { kind: "destroy"; enemy: string; enemyKind: string; cause: string; points: number; x: number; y: number } | { kind: "pow"; uses: number; col: number; row: number } | { kind: "sink"; enemy: string; enemyKind: string; spawned: string; x: number; y: number } | { kind: "freeze"; enemy: string; row: number; cols: number[] } | { kind: "toggle"; id: string; on: boolean } | { kind: "timer"; value: number } | { kind: "timer-zero" } | { kind: "stage-clear"; stage: string; bonus: number; result: string } | { kind: "level-clear"; level: number } | { kind: "game-over"; score: number }

Events, in order, from drain().

RoundsInput

interface RoundsInput

Player input for one step.

  • x: number — -1 left .. 1 right.
  • y: number — -1 up .. 1 down.
  • jump: boolean — Jump held.

RoundsModule

interface RoundsModule<S = unknown>

A rules module: game rules the session calls at fixed points of each step, with a state of its own (plain data, carried by snapshots, shown in RoundsState.modules). bumpModule() is one; a game passes its own in RoundsDef.modules. Every hook is optional.

  • id: string — Unique among the modules: names its state in RoundsState.modules and in snapshots.
  • init?(): S — Its state for a new game: plain data. Default {}.
  • build?(api: RoundsModuleApi, state: S): void — A stage has been built: at its start, and again when a death restarts it.
  • beforeMove?(api: RoundsModuleApi, state: S): void — Each step of play, before the bodies move (after enemy ages have advanced).
  • bump?(api: RoundsModuleApi, state: S, at: { col: number; row: number; x: number; y: number }): void — The player bumped a map cell from below; x, y is where. The session has already reported a bump event.
  • contact?(api: RoundsModuleApi, state: S, e: Readonly<EnemyState>, b: Readonly<WalkerBody>): boolean | void — The player touches an enemy whose body is enabled. Return true when the module has dealt with it: the session's own contact rule then leaves it alone.
  • enemyEvent?(api: RoundsModuleApi, state: S, ev: WalkerEvent, e: Readonly<EnemyState>): void — A world event about an enemy's body (landed, out-of-bounds ...), after the session's own handling (which removes an enemy that leaves the map).
  • enemies?(api: RoundsModuleApi, state: S): void — Each step of play, and of attract mode, after contacts and sinks: rules about the enemies alone.
  • removed?(api: RoundsModuleApi, state: S, id: string): void — An enemy has been removed, for any reason: forget what the module kept about it.

RoundsModuleApi

interface RoundsModuleApi

What a rules module reaches while the session runs. Bodies and enemies come live and read-only: read them within the call, and change things through these calls and the parts' own APIs.

  • readonly stage: StageDef — The current stage's resolved definition.
  • readonly map: PlatformMap — The level.
  • readonly world: WalkerWorld — The walker world.
  • readonly enemies: EnemyDirector — The enemy director.
  • readonly player: string — The player's body id.
  • readonly time: number — Seconds of play on this stage attempt.
  • readonly dt: number — The fixed step, seconds.
  • rules(kind: string): KindRules — A kind's rules on this stage; empty when it has none.
  • stuns(kind: string): boolean — Whether a kind can be flipped: it has stun rules.
  • body(e: Readonly<EnemyState>): Readonly<WalkerBody> | null — An enemy's body, live and read-only, or null when it has none.
  • age(id: string): number — Seconds since an enemy appeared; 0 for one the session does not know.
  • random(): number — The session's seeded stream: a number in [0, 1).
  • award(points: number, reason: string, x?: number, y?: number): void — Add points, with where to show them.
  • emit(e: RoundsEvent): void — Report an event.
  • remove(e: Readonly<EnemyState>, reason: string): void — Remove an enemy. It counts toward a 'clear' objective unless reason is 'sink', 'gone', 'expired' or 'frozen', or its kind has counts: false.
  • destroy(e: Readonly<EnemyState>, b: Readonly<WalkerBody>, cause: string, points: number): void — Destroy an enemy for points (cause says why), or collect it when its kind's contact is 'collect'.
  • die(cause: string): void — Kill the player.
  • spawnLater(o: { kind: string; emitter?: string; at?: { x: number; feet: number }; facing?: -1 | 1; delay?: number }): void — Spawn one body of kind later: from emitter, or at at (feet) facing facing, delay seconds from now (default 0).
  • lastDefeated(): boolean — Whether a 'clear' objective has all it needs and nothing it counts is left: the last enemy is down.

RoundsPendingSpawn

interface RoundsPendingSpawn

A spawn still to come: from the stage's spawns[src], or a one-off (src -1: kicks, sinks, spawnLater).

  • src: number — Index into the stage's spawns, or -1 for a one-off.
  • kind: string — Enemy kind.
  • emitter?: string — Emitter to spawn from, if any.
  • at?: { x: number; feet: number } — Where (feet), if not from an emitter or the spawn's points.
  • facing?: -1 | 1 — Facing.
  • due: number — When it is due, seconds of play.
  • left: number — Spawns left: -1 unlimited.
  • cyc: number — Which of the spawn's points comes next (place: 'cycle').

RoundsPhase

type RoundsPhase = "attract" | "intro" | "before" | "ready" | "play" | "dying" | "clear" | "after" | "over"

Session phase.

RoundsSession

interface RoundsSession

The session's own state, as a snapshot holds it: plain data. Read it only to store it; restore puts it back.

  • phase: RoundsPhase — The phase.
  • phaseTime: number — Seconds in the phase.
  • interlude: string — The interlude playing in a 'before' or 'after' phase, or ''.
  • level: number — Level number, from 1.
  • index: number — Stage index within the level, from 0.
  • loop: number — Sequence loops completed.
  • attempt: number — Attempt on this stage, from 1.
  • score: number — Score.
  • hi: number — High score.
  • lives: number — Lives, the one in play included.
  • extras: number — Extra lives awarded so far.
  • time: number — Seconds of play on this stage attempt.
  • timer: number — The bonus timer's value.
  • timerStart: number — The bonus timer's value at the stage's start.
  • timerAcc: number — Seconds towards the timer's next tick.
  • timerOn: boolean — Whether the timer is running.
  • hammer: string — The id of the hammer held, or ''.
  • hammerLeft: number — Seconds the hammer has left.
  • pickups: PickupState[] — The stage's pickups and whether each has been taken.
  • removedCells: [number, number][] — Cells a 'cells' objective has removed, [column, row], kept across deaths.
  • keptPickups: string[] — Item pickups taken, kept across restarts with keepPickups.
  • touchedCells: [number, number][] — 'cells' objective cells the player is over now, [column, row]: removed once left behind.
  • defeated: number — Enemies defeated toward a 'clear' objective.
  • collected: number — Enemies collected toward a 'collect' objective.
  • pending: RoundsPendingSpawn[] — Spawns still to come.
  • pause: number — Seconds the action stays frozen (a hammer smash).
  • zeroAt: number — When the timer reached zero, seconds of play, or -1: its onZero waits zeroDelay from here.
  • jumpSet: string[] — Enemies cleared in the jump under way, for jump-over scoring.
  • ages: Record<string, number> — By enemy id: seconds since it appeared.
  • modules: Record<string, unknown> — By module id: each rules module's state.
  • toggles: { id: string; on: boolean; t: number }[] — Toggles: on or off, and seconds into the cycle.
  • respawnLeft: number — Seconds the respawn platform has left.
  • respawnMoved: boolean — Whether the player has moved since respawning (the platform then goes when left).
  • deathCause: string — Why the player died last, or ''.
  • result: string — How the stage ended ('objective', 'time'), or '' while it runs.
  • ramp: number — The enemy speed ramp in force (the stage's ramp), 1 without one.
  • rng: number[] — The seeded random stream's state: four words, from the kernel's rng.snapshot(). It advances in place and is saved with the state.
  • events: RoundsEvent[] — Events not yet drained.

RoundsSnapshot

interface RoundsSnapshot

Serialisable session state (sub-packs snapshot themselves inside).

  • s: RoundsSession — Session fields.
  • map: PlatformMapSnapshot — Map.
  • movers: MoversSnapshot — Movers.
  • world: WalkerWorldSnapshot — World.
  • enemies: EnemiesSnapshot — Enemies.
  • difficulty: DifficultyScale — The difficulty in force, which setDifficulty may have changed since the definition.
  • player: WalkerProfile | null — The player profile setPlayerProfile set, or null for the definition's.

RoundsState

interface RoundsState

Everything a presenter reads.

  • phase: RoundsPhase — Phase.
  • phaseTime: number — Seconds in the phase.
  • interlude: string — Interlude being played ('before'/'after' phases) or ''.
  • level: number — Level number.
  • stageIndex: number — Stage index within the level.
  • stage: string — Stage id.
  • stageName: string — Stage display name.
  • theme: string — Stage theme.
  • loop: number — Sequence loops completed.
  • attempt: number — Attempt on this stage.
  • score: number — Score.
  • hiScore: number — High score.
  • lives: number — Lives including the one in play.
  • extras: number — Extra lives awarded so far.
  • timer: { value: number; start: number; warning: boolean; active: boolean } — Bonus timer value (0 without a timer), its start and whether it is in its warning band.
  • hammer: { active: boolean; left: number; warning: boolean; id: string } — Hammer held: seconds left, and the warning flag.
  • pow: { uses: number; full: number } — POW uses left and full, from bumpModule (0 and 0 without it).
  • modules: Record<string, unknown> — Copies of each rules module's state, by module id.
  • pickups: PickupState[] — Pickups.
  • cellsLeft: number — Cell objective: cells left.
  • defeated: number — Clear objective: defeated so far and needed.
  • needed: number — Enemies needed for a 'clear' objective (0 without one).
  • collected: number — Collect objective: collected so far and needed.
  • toCollect: number — Items needed for a 'collect' objective.
  • toggles: { id: string; on: boolean; progress: number }[] — Toggles: on, seconds into the current state and render progress 0..1 (1 = fully on).
  • respawning: boolean — Respawn platform present (Mario Bros).
  • deathCause: string — Why the player died last ('' none).
  • result: string — How the last stage ended ('' while running): 'objective', 'time'.
  • time: number — Seconds of play on this stage attempt.
  • speed: number — Current enemy speed multiplier (difficulty x ramp).
  • difficulty: string — Difficulty name.

SequenceDef

interface SequenceDef

The order of stages: one list per level; after the last level the list at loopFrom onward repeats.

  • levels: string[][] — levels[i] lists the stage ids of level i + 1.
  • loopFrom?: number — Level index (0-based) to repeat from after the last. Default the last level (it repeats forever).
  • maxLevel?: number — Highest level (the game ends after it). Default unlimited.

SinkDef

interface SinkDef

A zone that swallows enemies of some kinds and may turn them into another (an oil drum lighting fireballs).

  • zone: ZoneRef — Zone.
  • kinds: string[] — Kinds swallowed.
  • spawn?: { kind: string; max?: number; facing?: -1 | 1; delay?: number; at?: FeetRef } — Spawn this kind in their place (at at, default the zone's centre bottom), up to max alive, after delay seconds.
  • score?: number — Points awarded when swallowed. Default 0.

SpawnDef

interface SpawnDef

A body spawned when a stage starts, or later, once or repeatedly.

  • kind: string — Enemy kind.
  • at?: FeetRef — Where (feet).
  • points?: FeetRef[] | ((rt: StageRuntime) => FeetRef[]) — Candidate points (feet), chosen by place; or computed when due.
  • place?: "cycle" | "random" | "opposite-player" | "opposite-edge" — How to choose among points: 'cycle' (default), 'random', or 'opposite-player' (random among points on the half of the map away from the player); 'opposite-edge' ignores points and appears at the map side away from the player at the player's feet height, facing them (Mario Bros green fireball).
  • emitter?: string — Emit from this emitter instead (its points and timing rules apply to the body).
  • facing?: -1 | 1 — Facing. Default 1 (toward the player for 'opposite-edge').
  • delay?: number — Seconds after play starts. Default 0.
  • every?: number | [number, number] | ((rt: StageRuntime) => number) — Repeat every this many seconds (a random interval in a range, or computed now) after the first. Default: once.
  • max?: number | ((rt: StageRuntime) => number) — Most alive of this kind for a spawn to happen (a skipped spawn waits for the next interval). Default unlimited.
  • count?: number — Most spawns in total from this entry. Default unlimited when repeating, else 1.
  • when?(rt: StageRuntime): boolean — A condition checked at each due time (a false result skips that spawn).

StageContext

interface StageContext

Context passed to stage builders: build a stage for a level, attempt and difficulty.

  • level: number — Level number, from 1.
  • index: number — Stage index within the level, from 0.
  • stage: string — Stage id.
  • loop: number — Times the sequence has looped (0 on the first pass).
  • difficulty: DifficultyScale — Difficulty scale.
  • attempt: number — Attempt number on this stage (1 on the first).

StageDef

interface StageDef

One stage: a level plus its rules.

  • name?: string — Display name ('25m', 'PHASE 1').
  • theme?: string — Free label for presenters ('barrels', 'conveyors', 'bonus' ...).
  • map: PlatformMapDef — The level (platform-map definition).
  • movers?: MoverDef[] — Moving surfaces (platform-movers definitions).
  • kinds?: Record<string, EnemyKind> — Enemy kinds (platform-enemies).
  • behaviours?: Record<string, EnemyBehaviour> — Custom enemy behaviours (platform-enemies behaviours).
  • emitters?: EmitterDef[] — Emitters (intervals are scaled by the difficulty).
  • exits?: ExitDef[] — Exits (pipes).
  • spawns?: SpawnDef[] — Initial and scheduled spawns.
  • start?: FeetRef — Player start (feet). Default the 'player' marker.
  • facing?: -1 | 1 — Player facing at the start. Default 1.
  • profile?: WalkerProfileInput — Player profile override for this stage.
  • objective: ObjectiveDef | ObjectiveDef[] — Objectives (all must be met).
  • timer?: TimerDef — Bonus timer.
  • rules?: Record<string, KindRules> — Per-kind interaction rules.
  • pickups?: PickupDef[] — Pickups.
  • jumpOver?: JumpOverDef — Jump-over scoring.
  • bump?: BumpDef — Floors bumped from below.
  • pow?: PowDef — POW block.
  • sinks?: SinkDef[] — Sinks.
  • toggles?: ToggleDef[] — Timed cell toggles.
  • ramp?: { every: number; step: number; max: number } — Enemy speed ramp within the stage: +step every every seconds, up to max (multiplier).
  • onDeath?: "restart" | "respawn" — After a death: rebuild the stage ('restart', Donkey Kong) or continue it with a respawn ('respawn', Mario Bros). Default 'restart'.
  • respawn?: RespawnDef — Respawn rules for onDeath: 'respawn'.
  • keepCells?: boolean — Keep cells removed by a 'cells' objective across deaths. Default true.
  • before?: InterludeDef — Interlude before the stage starts.
  • after?: InterludeDef — Interlude after the stage is cleared.
  • deaths?: { falls?: boolean; hazards?: boolean; outside?: boolean; crush?: boolean } — Deaths from fatal landings, hazards, leaving through the bottom, and crushes (walker embeds caused by a moving surface or a map edit; plain overlaps never kill). Default all true.
  • keepPickups?: boolean — Item pickups taken stay taken after a death that restarts the stage (collected gems stay collected). Default false.
  • clearBonus?: number — Points added when the stage's objectives are met (a level-clear bonus). Default 0.
  • speed?: number — Enemy speed multiplier for this stage, on top of the difficulty (a per-level speed-up). Default 1.
  • inset?: number — Contact inset (world units) for player/enemy touches. Default 2.
  • hazards?: ZoneRef[] — Zones that kill the player on contact (a giant ape, a burning oil drum, a fatal bottom girder).
  • step?(rt: StageRuntime, dt: number): void — A policy hook called after the built-in rules every fixed step of play.

StageRuntime

interface StageRuntime

What the presenter and hooks can reach while a stage runs.

  • readonly map: PlatformMap — The level.
  • readonly movers: Movers — Movers (always present, possibly empty).
  • readonly world: WalkerWorld — Walker world.
  • readonly enemies: EnemyDirector — Enemy director.
  • readonly player: string — Player body id.
  • readonly time: number — Seconds since play started on this stage attempt.
  • award(points: number, reason: string, x?: number, y?: number): void — Add points (with a popup position).
  • kill(cause: string): void — Kill the player now.
  • clear(reason?: string): void — End the stage as cleared now.
  • random(): number — Seeded random number in [0, 1).

StageSource

type StageSource = StageDef | ((ctx: StageContext) => StageDef)

A stage given as data, or built per level and difficulty.

TimerDef

interface TimerDef

A countdown that pays out at the end of a stage, kills at zero, or ends the stage at zero.

  • start: number — Starting value (Donkey Kong: 5000 at level 1).
  • step: number — Amount removed per tick.
  • every: number — Seconds per tick (scaled by the difficulty's timer).
  • onZero?: "die" | "end" | "none" — At zero: the player dies ('die', Donkey Kong), the stage ends ('end', bonus phases) or nothing ('none'). Default 'die'.
  • award?: boolean — Add the remaining value to the score when the stage is cleared. Default true.
  • warn?: number — Value at or below which warning is reported. Default 1000.
  • emitter?: string — Tick once per spawn from this emitter instead of every every seconds (Donkey Kong 25 m: one tick per barrel).
  • zeroDelay?: number — Seconds between reaching zero and onZero taking effect (Donkey Kong: about 256 frames). Default 0.
  • stopEmitters?: string[] — Emitters switched off when the timer reaches zero (Kong stops throwing).

ToggleDef

interface ToggleDef

Map cells that switch between two specs on a timetable (Donkey Kong 50 m retracting ladders).

  • id: string — Id (render state, events).
  • cells: { col: number; row: number }[] — Cells switched.
  • on: CellSpec — Spec while on (extended).
  • off: CellSpec — Spec while off (retracted).
  • period: [number, number] — Seconds on, then seconds off.
  • phase?: number — Seconds into the cycle at the start. Default 0.
  • travel?: number — Seconds the change takes (render progress only; cells switch at the start of the change). Default 0.5.
  • hold?: boolean — Do not switch off while the player is on one of the cells (the switch waits). Default false.

ZoneRef

type ZoneRef = Rect | { marker: string; index?: number; w?: number; h?: number; dx?: number; dy?: number }

A rectangle given directly, or centred horizontally on a map marker (the index-th of that name, default the first) with its bottom on the marker's floor line.