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 fromwave(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 wavewave(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 headingangle.flameOutline(angle = this.s.ship.angle): FieldPoint[]— The ship's flame polyline about its centre at headingangle.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 reachrmust 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 everyeverypoints 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 bytick.
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 atscoreLimit).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 fromcreateRng), 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 everyeveryframes (arcade).rocks: interpolated by rocks left versus the wave's peak.every: number— Frames per tempo step intimemode.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 (forrocksmode).
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 everyautoEveryframes 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 torocks.velocity).
RockState
interface RockState
A rock in the field.
id: number— Stable id.size: string— Size id.shape: number— Index intorocks.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 whosebelowScoreexceeds 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 thancrowdrocks remain.crowd: { first: number; perWave: number; max: number }— The "few rocks left" threshold:firstin wave 1, +perWave, capped atmax.choice: { early: string; late: string; earlyWhileReloadAtLeast: number; lateFromScore: number; earlyChance: "reload" | number; }— Which kind appears:earlywhile the interval is long,latefrom a score, else a chance.speed: number— Horizontal speed, world units per frame.verticalSpeeds: number[]— Vertical speeds picked at random everyturnEveryframes.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) orstart.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"—axisclamps each velocity component (arcade);magnitudeclamps 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 notableis 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, keptclearancefrom the ship.clearance: number— Minimum distance from the ship's spawn point foranywhereplacement.


