Phaser AE

Asteroid field

Free packv3.0.0-32DArcade

The rules of the inertial rock-blasting space shooter: a ship drifting on its own momentum, rocks that split as you shoot them, saucers that hunt you, and a heartbeat that quickens as the wave thins out. The two classic arcade games are presets, and every rule is a definition you can reshape.

This is a Free pack. Every phaser.io account can use it in their games.

Released October 14, 2026 · updated October 20, 2026

Asteroid field: 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

asteroidField

function asteroidField(): Pack<{ asteroidField: AsteroidFieldApi }>

Install the renderer-independent asteroid-field rules (Asteroids, Asteroids Deluxe and configurable variants) as game.asteroidField.

Helpers

ASTEROIDS_FLAME

const ASTEROIDS_FLAME: FieldPoint[]

The ship's thrust flame from ThrustDir0 ($52A2): an open polyline behind the tail.

ASTEROIDS_ROCK_OUTLINES

const ASTEROIDS_ROCK_OUTLINES: FieldPoint[][]

The four Asteroids (1979) rock outlines, decoded from the vector ROM (AstPtrnPtrTbl, $51E6-$524E) and flipped to y down. Units are the ROM's short-vector grid (about +-4); the preset scales them 8, 4 and 2 world units per grid step for large, medium and small rocks.

ASTEROIDS_SAUCER_DETAIL

const ASTEROIDS_SAUCER_DETAIL: FieldPoint[][]

The two interior strokes the ROM draws across the saucer (rim line and dome base).

ASTEROIDS_SAUCER_OUTLINE

const ASTEROIDS_SAUCER_OUTLINE: FieldPoint[]

The flying saucer silhouette from ScrPtrnPtrTbl ($5252), y down: hull, rim and dome.

ASTEROIDS_SHIP_OUTLINE

const ASTEROIDS_SHIP_OUTLINE: FieldPoint[]

The Asteroids ship facing +x, decoded from ShipDir0 ($5290): nose at (7, 0), swept wings and the notched tail whose crossbar the ROM draws between (-3, -2) and (-3, 2).

asteroidsDeluxePreset

function asteroidsDeluxePreset(o: DeluxePresetOptions = {}): AsteroidFieldConfig

Asteroids Deluxe (Atari, 1981): the Asteroids engine with the operator manual's changes. A shield (about ten seconds of charge renewed with each ship; bouncing a rock costs a third and passes its momentum to the ship, ramming a saucer two thirds; no firing while it is up) replaces hyperspace. Waves start with six large rocks (four on the easy setting) rising by one to nine; at most 24 rocks on screen; medium rocks move faster than large and small fastest, and rocks speed up as a wave goes on. The killer satellite (hexagon 50 -> three diamonds 100 -> two wedges each 200) arrives when four rocks remain, from wave 2 on the hard setting, and its pieces home on the ship. Firing repeats while held (four shots). Saucers: the large one aims one shot in four, the small one is the only kind from 10,000 points and gets accurate from 60,000; saucers sometimes shoot at rocks (anti-lurking). Thrust is twice as strong with the same top speed. Extra ship every 10,000; score rolls over at 1,000,000.

asteroidsPreset

function asteroidsPreset(o: AsteroidsPresetOptions = {}): AsteroidFieldConfig

Asteroids (Atari, 1979) as data, from the rev 4 ROM disassembly. 1024 x 768 wrapping field at 62.5 frames per second (250 Hz NMI / 4). Four ROM rock outlines at 8/4/2 px per grid step; large (20) -> 2 medium (50) -> 2 small (100); hit boxes 132/72/42 (+28 for the ship) in the ROM's half-units; at most 27 rocks; waves of 4, 6, 8, 10 then 11, drifting in from the edges with per-axis speeds of 6..31 fine units a frame, a child taking its parent's velocity +-16. Ship: 3/256 turn a frame, thrust and friction (v/128) every other frame, per-axis cap of 63; four shots that live 72 frames at half the thrust table plus the ship's velocity; 3 ships, one more every 10,000, score rolling over at 99,990. Hyperspace with the ROM's failure roll. Saucers: large (200, random fire) and small (1000, aimed, tighter above 35,000), with the timer, quiet-after-a-hit rule and "few rocks left" threshold from UpdateScr. The heartbeat starts at 48 frames between beats each wave and shortens by one every 64 frames to 8.

axisDelta

function axisDelta(a: number, b: number, size: number, wrap: boolean): number

Shortest signed distance from a to b on an axis of length size when it wraps; plain b - a otherwise.

boundedFieldPreset

function boundedFieldPreset(o: BoundedFieldOptions = {}): AsteroidFieldConfig

A materially different configuration of the same rules, proving the pack is not the arcade game: a non-wrapping 960 x 720 arena with bouncing walls; four rock sizes (boulder -> 3 large -> 3 medium -> 2 small) with exact polygon collision and spinning jagged outlines; authored wave compositions placed anywhere clear of the ship; eight short-lived, rapid-fire shots that do not inherit the ship's velocity and die at the walls; a magnitude-capped, snappier ship; no saucers, no hyperspace; the heartbeat follows the rocks left.

createAsteroidField

function createAsteroidField(config: AsteroidFieldConfig): AsteroidField

Build an asteroid-field game from a definition (see the presets). Throws a clear error for an unsupported definition.

DELUXE_ROCK_OUTLINES

const DELUXE_ROCK_OUTLINES: FieldPoint[][]

Three rounder rock outlines for the Asteroids Deluxe preset. Authored for this pack (the Deluxe vector ROM is not disassembled publicly); they keep the Deluxe look of fuller, less spiky rocks in the same +-4 grid.

HUNTER_DIAMOND_OUTLINE

const HUNTER_DIAMOND_OUTLINE: FieldPoint[]

Killer-satellite diamond: two wedges back to back, pointing along +x, half-length 1.

HUNTER_HEX_OUTLINE

const HUNTER_HEX_OUTLINE: FieldPoint[]

Killer-satellite hexagon (Asteroids Deluxe): six wedges around a centre, radius 1.

HUNTER_WEDGE_OUTLINE

const HUNTER_WEDGE_OUTLINE: FieldPoint[]

Killer-satellite wedge ("shark"): a dart pointing along +x, half-length 1.

jaggedOutlines

function jaggedOutlines(count: number, vertices = 11, rough = 0.35, seed = 1): FieldPoint[][]

Deterministic jagged outlines for configurations that want their own rock family: count polygons of vertices points around radius 4 (the same grid as the arcade sets), with radial jitter rough (0..1). The same arguments always give the same shapes, so an authored config stays reproducible.

octagonHit

function octagonHit(dx: number, dy: number, t: number): boolean

The arcade's octagonal hit test (Asteroids HitDetection, $69F0): both axis distances under t and their sum under 1.5 t, which rounds a box toward a circle without a multiply.

outlineRadius

function outlineRadius(local: readonly FieldPoint[], scale = 1): number

Largest distance of an outline point from its origin, after scale.

outlinesOverlap

function outlinesOverlap(a: readonly FieldPoint[], b: readonly FieldPoint[]): boolean

True when two closed polygons overlap (edge crossing or containment); exact for concave outlines.

placeOutline

function placeOutline(local: readonly FieldPoint[], x: number, y: number, angle = 0, scale = 1): FieldPoint[]

Scale, rotate (radians, clockwise on screen in y down) and translate a local outline into the world.

pointInOutline

function pointInOutline(p: FieldPoint, poly: readonly FieldPoint[]): boolean

Even-odd point-in-polygon test.

segmentHitsOutline

function segmentHitsOutline(p0: FieldPoint, p1: FieldPoint, poly: readonly FieldPoint[]): boolean

True when the swept segment p0 -> p1 touches or ends inside the polygon.

segmentsCross

function segmentsCross(a: FieldPoint, b: FieldPoint, c: FieldPoint, d: FieldPoint): boolean

True when segments ab and cd cross (touching counts).

wrapGhosts

function wrapGhosts(x: number, y: number, r: number, width: number, height: number): FieldPoint[]

Positions at which something centred at (x, y) with reach r must be drawn so it shows on both sides of a wrapping seam: the point itself plus mirrors at +-width / +-height when it overlaps an edge.

Classes

AsteroidField

class AsteroidField

Owns an inertial asteroid-field game: waves, splits, ship handling, shots, saucers, hunters, defence, lives and score.

  • readonly config: AsteroidFieldConfig — The validated definition (a private copy).
  • constructor(config: AsteroidFieldConfig)
  • reset(o: { wave?: number; attract?: boolean } = {}) — Start again: a game from wave (default 1), or the attract-mode field with no ship.
  • get state(): AsteroidFieldState — A read-only copy of the whole state.
  • drain(): AsteroidFieldEvent[] — Take the events since the last drain.
  • snapshot(): AsteroidFieldSnapshot — Serializable snapshot of the whole game.
  • restore(snap: AsteroidFieldSnapshot) — Restore a snapshot taken from an owner built with the same definition.
  • get liveRocks(): number — Rocks that are whole (not exploding).
  • size(id: string): RockSize — A rock size by id.
  • tier(id: string): HunterTier — A hunter tier by id.
  • saucerKind(id: string): SaucerKind — A saucer kind by id.
  • waveComposition(wave: number): WaveGroup[] — The groups wave wave (1-based) starts with.
  • rockOutline(rock: Pick<RockState, "size" | "shape" | "angle">): FieldPoint[] — A rock's outline in world units about its centre, scaled and rotated (add its x, y to place it).
  • shipOutline(angle = this.s.ship.angle): FieldPoint[] — The ship's outline about its centre at heading angle.
  • flameOutline(angle = this.s.ship.angle): FieldPoint[] — The ship's flame polyline about its centre at heading angle.
  • saucerOutline(kind: string): FieldPoint[] — A saucer's silhouette about its centre.
  • saucerDetail(kind: string): FieldPoint[][] — A saucer's interior strokes about its centre.
  • hunterOutline(h: Pick<HunterState, "tier" | "angle">): FieldPoint[] — A hunter piece's outline about its centre.
  • ghosts(x: number, y: number, r: number): FieldPoint[] — Where something at (x, y) with reach r must be drawn so it shows across a wrapping seam (just itself in a bounded world).
  • tick(seconds: number, input: AsteroidFieldInput = {}) — Advance by real seconds (whole fixed frames inside); returns the live state.
  • step(input: AsteroidFieldInput = {}) — Advance exactly one simulation frame.
  • spawnClear(): boolean — True when nothing is near the ship's spawn point and no saucer is flying (the arcade's respawn rule).

Types

AsteroidFieldApi

type AsteroidFieldApi = typeof api

Everything the pack installs as game.asteroidField: the rules owner, presets and outline geometry.

AsteroidFieldConfig

interface AsteroidFieldConfig

A complete asteroid-field definition. Every arcade value lives in a preset.

  • hz: number — Simulation frames per second.
  • seed: number — Seed for every random choice.
  • world: { width: number; height: number; wrap: boolean; restitution: number } — World size and edges: wrapping (arcade) or bounded with bouncing walls.
  • collision: "box" | "polygon" — box: the arcade's octagonal hit boxes. polygon: exact outline tests (shots swept as segments).
  • rocks: RockRules — Rock family.
  • waves: WaveRules — Wave composition and pacing.
  • ship: ShipRules — The ship.
  • shots: PlayerShotRules — Player shots.
  • defence: DefenceRules — Hyperspace, shield or nothing.
  • saucers: SaucerRules | null — Flying saucers, or null.
  • hunters: HunterRules | null — Homing hunters, or null.
  • heartbeat: HeartbeatRules | null — Heartbeat tempo, or null.
  • rockSpeedUp: SpeedUpRules | null — Rock speed-up within a wave, or null.
  • lives: number — Ships at the start, including the one in play.
  • extraLife: { every: number | null; at: number[] } — Extra ships: at each listed score, then every every points after the last (or from 0).
  • scoreLimit: number | null — The displayed score rolls over here (arcade: 100000), or null.

AsteroidFieldEvent

type AsteroidFieldEvent = | { kind: "game-start"; status: FieldStatus } | { kind: "wave-start"; wave: number; rocks: number } | { kind: "wave-clear"; wave: number } | { kind: "fire"; owner: "ship" | "saucer"; x: number; y: number } | { kind: "rock-destroyed"; id: number; size: string; x: number; y: number; by: FieldCause; score: number; pieces: number } | { kind: "ship-destroyed"; x: number; y: number; by: FieldCause | "rock" } | { kind: "ship-spawn"; x: number; y: number } | { kind: "hyperspace"; fromX: number; fromY: number; toX: number; toY: number } | { kind: "hyperspace-exit"; x: number; y: number } | { kind: "shield-hit"; x: number; y: number; energy: number; on: "rock" | "hunter" | "saucer" | "shot" } | { kind: "saucer-spawn"; saucer: string; x: number; y: number } | { kind: "saucer-destroyed"; saucer: string; x: number; y: number; by: FieldCause | "rock"; score: number } | { kind: "saucer-gone"; saucer: string } | { kind: "hunter-spawn"; tier: string; x: number; y: number } | { kind: "hunter-destroyed"; tier: string; x: number; y: number; by: FieldCause; score: number; pieces: number } | { kind: "extra-life"; lives: number } | { kind: "beat"; tone: number; interval: number } | { kind: "game-over"; score: number }

Ordered notifications for presentation and audio: drain() them each frame.

AsteroidFieldInput

interface AsteroidFieldInput

One frame of player input.

  • turn?: number — -1 turns anticlockwise on screen (left), +1 clockwise (right).
  • thrust?: boolean — Thrust held.
  • fire?: boolean — Fire held (edge or auto rules apply).
  • defend?: boolean — Hyperspace (on press) or shield (while held).

AsteroidFieldSnapshot

interface AsteroidFieldSnapshot

Serializable snapshot of a whole game.

  • state: AsteroidFieldState — The state.
  • carry: number — Fractional frame carried by tick.

AsteroidFieldState

interface AsteroidFieldState

Serializable game state; read it through state.

  • frame: number — Frames since reset.
  • waveFrame: number — Frames since the current wave began.
  • status: FieldStatus — Phase.
  • wave: number — Current wave, 1-based (0 before the first).
  • score: number — Displayed score (rolls over at scoreLimit).
  • total: number — Total points ever scored (drives extra ships).
  • lives: number — Ships left including the one in play.
  • extras: number — Extra ships awarded.
  • ship: ShipState — The ship.
  • rocks: RockState[] — Rocks, including exploding ones.
  • shots: ShotState[] — Shots in flight.
  • saucer: SaucerState | null — The saucer, or null.
  • hunters: HunterState[] — Hunter pieces.
  • waveTimer: number — Frames until the next wave once the field is clear.
  • saucerTimer: number — Frames until a saucer may appear.
  • saucerReload: number — Current saucer interval.
  • quiet: number — Frames since-a-rock-broke quiet period left.
  • crowd: number — Rocks-left threshold under which saucers ignore the quiet period.
  • speedFactor: number — Rock speed multiplier this wave.
  • huntersThisWave: number — Hunter appearances this wave.
  • waveRocks: number — Rocks the current wave started with.
  • heartbeat: HeartbeatState — Heartbeat.
  • fireHeld: boolean — Fire was held last frame.
  • defendHeld: boolean — Defend was held last frame.
  • autoTimer: number — Frames to the next automatic shot.
  • nextId: number — Next entity id.
  • random: number[] — The kernel generator's state (four words from createRng), advanced in place, so a snapshot carries the stream.

AsteroidsPresetOptions

interface AsteroidsPresetOptions

Knobs on the classic presets. Anything else: edit the returned definition.

  • lives?: number — Ships at the start (arcade DIP switch: 3 or 4).
  • seed?: number — Seed for every random choice.
  • waveStart?: number — Rocks in wave 1.
  • waveStep?: number — Extra rocks per wave.
  • waveMax?: number — Cap on a wave's starting rocks.
  • splitCount?: number — Pieces a large or medium rock breaks into.
  • maxShots?: number — Most player shots in flight.
  • shotLife?: number — Player shot life in frames.
  • wrap?: boolean — Wrap the field (arcade) or bounce off walls.
  • saucers?: boolean — Flying saucers on or off.
  • defence?: "hyperspace" | "shield" | "none" — Override the emergency button.
  • extraEvery?: number | null — Points per extra ship (null for none).
  • thrustScale?: number — Ship thrust multiplier over the preset.

BoundedFieldOptions

interface BoundedFieldOptions

Knobs on the bounded-arena variant.

  • seed?: number — Seed.
  • lives?: number — Ships at the start.
  • splitCount?: number — Pieces each split makes.
  • maxShots?: number — Most shots in flight.

DefenceRules

type DefenceRules = | { kind: "none" } | { kind: "hyperspace"; /** Frames the ship is gone. */ frames: number; /** Re-entry area as fractions of the world. */ area: { x0: number; x1: number; y0: number; y1: number }; /** Chance the jump enters the danger roll. */ failChance: number; /** The arcade danger roll: fail when base + step * random(levels) >= rocks on screen; null fails outright. */ crowdEase: { base: number; step: number; levels: number } | null; } | { kind: "shield"; /** Full charge, in frames of use. */ energy: number; /** Energy spent per shielded frame. */ drain: number; /** Charge lost bouncing a rock. */ rockCost: number; /** Charge lost bouncing a hunter. */ hunterCost: number; /** Charge needed to survive ramming a saucer (it is destroyed). */ saucerCost: number; /** Charge lost absorbing a shot. */ shotCost: number; /** Share of a rock's closing speed the ship takes when it bounces. */ transfer: number; /** The ship cannot fire while shielded. */ blocksFire: boolean; /** Shield radius: the ship's hit and reach while it is up. */ radius: number; }

The emergency button: nothing, hyperspace or a shield.

DeluxePresetOptions

interface DeluxePresetOptions extends AsteroidsPresetOptions

Knobs on the Deluxe preset.

  • difficulty?: "easy" | "hard" — The operator's easy setting (4, 5, 6 ... 9 slower rocks; slow diamonds) or the manual's hard game (6 ... 9).
  • hunters?: boolean — Killer satellites on or off.
  • extraEvery?: number | null — Points per extra ship (operator choice 10,000, 12,000 or 15,000; null for none).

FieldCause

type FieldCause = "ship-shot" | "ship" | "saucer" | "saucer-shot" | "hunter" | "hyperspace"

What broke something: the player's shot, the ship ramming, the saucer, its shot, or a failed hyperspace jump.

FieldPoint

interface FieldPoint

A point or vector in world units (y down).

  • x: number — Horizontal coordinate.
  • y: number — Vertical coordinate, y down.

FieldStatus

type FieldStatus = "attract" | "playing" | "game-over"

Game phase: attract (no ship), playing, or over (the field keeps drifting).

HeartbeatRules

interface HeartbeatRules

The "heartbeat" pulse: off-time between beats, shortened over time or as rocks dwindle.

  • start: number — Frames between beats at a wave's start.
  • min: number — Fastest.
  • mode: "time" | "rocks" — time: one frame shorter every every frames (arcade). rocks: interpolated by rocks left versus the wave's peak.
  • every: number — Frames per tempo step in time mode.
  • on: number — Frames each beat sounds.

HeartbeatState

interface HeartbeatState

Heartbeat state for audio.

  • interval: number — Current frames between beats.
  • on: boolean — A beat is sounding.
  • tone: number — Alternating tone, 0 or 1.
  • onTimer: number — Frames left of the sounding beat.
  • offTimer: number — Frames to the next beat.
  • beats: number — Beats so far.
  • peak: number — Most rocks seen this wave (for rocks mode).

HunterRules

interface HunterRules

Homing hunters that appear late in a wave.

  • tiers: HunterTier[] — Tiers; split targets must exist.
  • root: string — Tier that appears.
  • fromWave: number — First wave (1-based) they appear in.
  • atOrBelowRocks: number — They appear once this few rocks (not exploding) remain, and fewer than the wave started with.
  • perWave: number — Appearances per wave.
  • dyingFrames: number — Frames a destroyed piece's explosion lasts.

HunterState

interface HunterState

A hunter piece.

  • id: number — Stable id.
  • tier: string — Tier id.
  • x: number — Centre x.
  • y: number — Centre y.
  • vx: number — Velocity x.
  • vy: number — Velocity y.
  • angle: number — Heading or spin angle.
  • dying: number — Frames of explosion left; 0 while whole.

HunterTier

interface HunterTier

One stage of a homing hunter (the Deluxe killer satellite: hexagon, diamonds, wedges).

  • id: string — Tier id.
  • outline: FieldPoint[] — Closed outline facing +x.
  • scale: number — World units per outline unit.
  • hit: number — Half-extent when struck.
  • reach: number — Added to the ship's hit box when it strikes the ship.
  • score: number — Points to the player.
  • speed: number — Cruise speed, world units per frame.
  • steer: number — Steering per frame toward the ship, 0..1 (0 drifts).
  • spin: number — Spin per frame for non-steering tiers.
  • split: { into: string; count: number; offset: number } | null — The tier it breaks into, how many, and how far apart they start.

PlayerShotRules

interface PlayerShotRules extends ShotRules

Player shot rules.

  • fire: "edge" | "auto" — edge: one shot per press (arcade). auto: a shot every autoEvery frames while held.
  • autoEvery: number — Frames between automatic shots.
  • hitsShip: boolean — A shot that wraps round can destroy its own ship (arcade).
  • bounce: boolean — In a bounded world shots bounce off walls instead of dying there.

RockRules

interface RockRules

The rock family: outlines, sizes, speed rule and the field cap.

  • outlines: FieldPoint[][] — Closed outlines (any count >= 1); each rock takes one at random.
  • sizes: RockSize[] — Sizes, largest first by convention; split targets must exist.
  • velocity: RockVelocity — Default velocity rule.
  • max: number — Most rocks (including ones still exploding) the field holds; a split makes only as many pieces as fit.
  • dyingFrames: number — Frames a broken rock's explosion occupies its slot.
  • splitOffset: number — Children start up to this far from the parent's centre.
  • spin: number — Largest spin in radians per frame (each rock gets a random spin in +-spin); 0 for the arcade's non-rotating rocks.

RockSize

interface RockSize

One rock size: its scale, hit box, score and what it breaks into.

  • id: string — Size id used by waves and splits.
  • scale: number — World units per outline unit.
  • hit: number — Half-extent of the octagonal hit box when something strikes it (box collision).
  • score: number — Points to the player for breaking it with a shot or by ramming it.
  • split: { into: string; count: number } | null — The size it breaks into and how many pieces, or null to vanish.
  • velocity?: RockVelocity — Velocity rule for rocks of this size (falls back to rocks.velocity).

RockState

interface RockState

A rock in the field.

  • id: number — Stable id.
  • size: string — Size id.
  • shape: number — Index into rocks.outlines.
  • x: number — Centre x.
  • y: number — Centre y.
  • vx: number — Velocity x, world units per frame (before the wave speed factor).
  • vy: number — Velocity y.
  • angle: number — Rotation, radians.
  • spin: number — Spin, radians per frame.
  • dying: number — Frames of explosion left; 0 while the rock is whole.

RockVelocity

type RockVelocity = /** The arcade rule: each axis is base + uniform(-jitter, jitter), then its magnitude is clamped to [min, max]. */ | { kind: "axis"; jitter: number; min: number; max: number } /** A random heading at a speed in [min, max], plus `inherit` times the base velocity. */ | { kind: "polar"; min: number; max: number; inherit: number }

How a rock picks its velocity when a wave spawns it (base 0) or a parent splits (base = the parent).

SaucerAim

interface SaucerAim

How a saucer aims.

  • aimedChance: number — Chance a shot is aimed at the ship (0 = always random, 1 = always aimed).
  • spread: { belowScore?: number; radians: number }[] — Aim error in radians by score; the first entry whose belowScore exceeds the score applies, the last otherwise.
  • lead: number — Frames of the saucer's own velocity subtracted from the aim (compensates inherited shot velocity).
  • rockChance: number — Chance a shot aims at the nearest rock or hunter instead (anti-lurking, Deluxe).

SaucerKind

interface SaucerKind

One saucer type.

  • id: string — Kind id.
  • scale: number — World units per outline unit.
  • hit: number — Half-extent when struck.
  • reach: number — Added to a target's hit box when the saucer strikes it.
  • score: number — Points to the player.
  • aim: SaucerAim — Aim policy.

SaucerRules

interface SaucerRules

Flying saucers: timing, size choice, motion and fire.

  • kinds: SaucerKind[] — Saucer types.
  • outline: FieldPoint[] — Closed silhouette (outline units).
  • detail: FieldPoint[][] — Extra interior strokes for presentation.
  • tick: number — Timers advance in ticks of this many frames (arcade: 4).
  • waveStart: number — Spawn timer at each wave start, frames.
  • reloadStart: number — Initial spawn interval, frames.
  • reloadStep: number — Each spawn shortens the interval by this much...
  • reloadMin: number — ...down to this.
  • quietAfterHit: number — After a rock breaks, saucers wait this long unless fewer than crowd rocks remain.
  • crowd: { first: number; perWave: number; max: number } — The "few rocks left" threshold: first in wave 1, +perWave, capped at max.
  • choice: { early: string; late: string; earlyWhileReloadAtLeast: number; lateFromScore: number; earlyChance: "reload" | number; } — Which kind appears: early while the interval is long, late from a score, else a chance.
  • speed: number — Horizontal speed, world units per frame.
  • verticalSpeeds: number[] — Vertical speeds picked at random every turnEvery frames.
  • turnEvery: number — Frames between vertical course changes.
  • fireEvery: number — Frames between shots.
  • firstShot: number — Frames before the first shot.
  • shots: ShotRules — Saucer shot rules.
  • dyingFrames: number — Frames a destroyed saucer's explosion lasts.
  • removeOnShipDeath: boolean — A saucer still flying when the player's wreck finishes exploding is removed (arcade).

SaucerState

interface SaucerState

The saucer on screen.

  • id: number — Stable id.
  • kind: string — Kind id.
  • x: number — Centre x.
  • y: number — Centre y.
  • vx: number — Velocity x.
  • vy: number — Velocity y.
  • dying: number — Frames of explosion left; 0 while flying.
  • fireTimer: number — Frames to its next shot.

ShipRules

interface ShipRules

The player's ship: outline, collision sizes, handling and the death/respawn cycle.

  • outline: FieldPoint[] — Closed outline facing +x.
  • flame: FieldPoint[] — Thrust flame polyline (presentation data, same units).
  • scale: number — World units per outline unit.
  • hit: number — Half-extent when struck (by shots or the saucer).
  • reach: number — Added to a rock's or hunter's hit box when the ship strikes it.
  • start: { x: number; y: number; angle: number } — Spawn point and heading.
  • respawnAngle: "keep" | "reset" — After a death the ship returns with its old heading (keep, arcade) or start.angle.
  • turn: number — Radians turned per frame while a turn is held.
  • thrust: number — Velocity added per physics tick while thrusting (world units per frame).
  • drag: number — Fraction of velocity removed per physics tick while coasting.
  • physicsEvery: number — Thrust and drag apply every Nth frame (arcade: 2).
  • maxSpeed: number — Speed cap in world units per frame.
  • clamp: "axis" | "magnitude" — axis clamps each velocity component (arcade); magnitude clamps the speed.
  • explodeFrames: number — Frames the wreck explodes before the next ship is considered.
  • respawnFrames: number — Frames after the explosion before the safe-spawn check starts.
  • startFrames: number — Frames before the first ship of a game appears ("PLAYER 1").
  • safeRadius: number — Respawn waits until nothing is within this box half-size of the spawn point.

ShipState

interface ShipState

The player's ship.

  • x: number — Centre x.
  • y: number — Centre y.
  • vx: number — Velocity x.
  • vy: number — Velocity y.
  • angle: number — Heading, radians.
  • status: ShipStatus — Phase.
  • timer: number — Frames left in the phase (0 while respawning means waiting for a clear centre).
  • thrusting: boolean — Thrust held this frame.
  • hyperFail: boolean — The current hyperspace jump will fail on re-entry.
  • shield: boolean — Shield up this frame.
  • energy: number — Shield charge left (frames).

ShipStatus

type ShipStatus = "alive" | "exploding" | "respawning" | "hyperspace" | "absent"

Ship phase: flying, exploding, waiting to respawn, in hyperspace, or not in play.

ShotRules

interface ShotRules

Shot rules for the player (and, with a subset, for saucers).

  • max: number — Most shots in flight.
  • life: number — Frames a shot lives.
  • speed: number — Muzzle speed, world units per frame.
  • inherit: number — Fraction of the shooter's velocity added to the shot.
  • clamp: number | null — Per-axis velocity clamp, or null.
  • muzzle: number — The shot starts this far ahead of the shooter's centre.

ShotState

interface ShotState

A shot in flight.

  • id: number — Stable id.
  • owner: "ship" | "saucer" — Who fired it.
  • x: number — Position x.
  • y: number — Position y.
  • vx: number — Velocity x.
  • vy: number — Velocity y.
  • life: number — Frames left.

SpeedUpRules

interface SpeedUpRules

Rocks speeding up during a wave (Deluxe).

  • every: number — Frames between speed-ups.
  • factor: number — Multiplier per speed-up.
  • max: number — Cap on the multiplier.

WaveGroup

interface WaveGroup

One authored group in a wave: count rocks of size.

  • size: string — Rock size id.
  • count: number — How many.

WaveRules

interface WaveRules

How waves are composed, placed and paced.

  • start: number — Rocks in wave 1 when no table is given.
  • step: number — Extra rocks per later wave.
  • max: number — Most rocks a wave starts with.
  • size: string — Size the counted waves use.
  • table?: WaveGroup[][] — Authored compositions per wave (index 0 = wave 1); the last repeats. Overrides start/step/max.
  • delay: number — Frames between the last rock vanishing and the next wave.
  • firstDelay: number — Frames before the first wave of a game.
  • waitForSaucer: boolean — The next wave waits while a saucer is on screen (arcade).
  • waitForHunters: boolean — The wave is not clear while hunters remain.
  • place: "edges" | "anywhere" — edges: rocks drift in from the left or top edge (arcade). anywhere: random, kept clearance from the ship.
  • clearance: number — Minimum distance from the ship's spawn point for anywhere placement.