The pack
landerMissions
function landerMissions(): Pack<{ landerMissions: typeof api }>
Install the lander missions API.
Helpers
clock
function clock(seconds: number): string
Format seconds as m:ss.
createTaxi
function createTaxi(def: TaxiDef): Taxi
Build a taxi game.
createTouchdown
function createTouchdown(def: TouchdownDef): Touchdown
Build a touchdown game.
lunarCraft
function lunarCraft(): TouchdownCraft
The lunar module's flight and landing figures.
lunarLanderPreset
function lunarLanderPreset(opts: LunarLanderOptions = {}): TouchdownDef
Lunar Lander (Atari 1979).
lunarMissions
function lunarMissions(): TouchdownMission[]
The four missions, from the cabinet cards and the ROM's mission tables.
lunarModuleHull
function lunarModuleHull(): Vec2[]
The lunar module: a cabin on a descent stage with two splayed legs (feet are hull points 3 and 4).
lunarSites
function lunarSites(): TouchdownSite[]
The fifteen landing sites.
lunarTerrain
function lunarTerrain(): Vec2[]
The Lunar Lander landscape as a world-space heightfield.
spaceTaxiHull
function spaceTaxiHull(): Vec2[]
The taxi cab: 24 x 10 with its gear legs at the bottom corners.
spaceTaxiPreset
function spaceTaxiPreset(opts: SpaceTaxiOptions = {}): TaxiDef
Space Taxi (Muse 1984).
spaceTaxiScreens
function spaceTaxiScreens(): TaxiScreen[]
Every authored screen, in shift order. 7:00 Teleports, 8:00 Puzzler (gates toggled by touching dots), 11:00 Magnets and the screens after 12:00 are not reproduced.
Constants
LUNAR_FUEL
const LUNAR_FUEL: Record<LanderDifficulty, number>
Fuel per coin for each difficulty: all three are the cabinet's own DIP settings.
LUNAR_HEIGHT
const LUNAR_HEIGHT: number
World and far-view height.
LUNAR_LEVER
const LUNAR_LEVER: number[]
The ROM's thrust lever table (16 positions) as fractions of full thrust.
LUNAR_WIDTH
const LUNAR_WIDTH: number
World width (one wide view; the landscape wraps).
TAXI_H
const TAXI_H: 180
Playfield height (C64 pixels inside the border).
TAXI_SHIFTS
const TAXI_SHIFTS: Record<string, number[] | { random: number }>
Shifts: the morning's screens, a later set with hazards, everything in order, or ten at random.
TAXI_W
const TAXI_W: 320
Playfield size (C64 pixels inside the border).
Types
LanderDifficulty
type LanderDifficulty = "easy" | "normal" | "arcade"
Easy / Normal / Arcade. Arcade is the original's tables; each rules block says what the other two change.
LunarLanderOptions
interface LunarLanderOptions
Options for the Lunar Lander preset.
difficulty?: LanderDifficulty— Easy (1550 fuel a coin), Normal (1100) or Arcade (750, the factory setting). Default 'normal'.fuelPerCoin?: number— Override fuel per coin (the operator settings are 450, 600, 750, 900, 1100, 1300, 1550, 1800).mission?: number— Starting mission: 0 Training, 1 Cadet, 2 Prime, 3 Command. Default 0.lit?: [number, number]— Lit sites per round as [2x sites, 3x-5x sites]. Default [2, 2].startSpeed?: number— Starting horizontal speed (an estimate). Default 30.seed?: number— Seed for lit sites and messages. Default 1979.
SpaceTaxiOptions
interface SpaceTaxiOptions
Options for the Space Taxi preset.
difficulty?: LanderDifficulty— Easy, Normal or Arcade (the estimated original): landing speed bands, fuel use and hazard speed. Default 'normal'.shift?: "morning" | "day" | "standard" | "random" | number[]— Which shift. Default 'morning'.fleet?: number— Cabs in the fleet. Default 6.gravity?: number— Gravity. Default 30.seed?: number— Seed. Default 1984.
Taxi
interface Taxi
A running taxi game.
readonly def: TaxiDef— The definition the game was built from.readonly state: TaxiState— The session state (plain data;snapshotsaves it).readonly world: ThrustWorld— The world of the current screen.readonly craftId: string— The taxi's craft id inworld.screen(): TaxiScreen— The current screen.start(): void— Start a shift.attract(): void— Back to attract.step(input?: TaxiInput): void— Advance one fixed step.drain(): TaxiEvent[]— Take queued events.pad(id: string): TaxiPad | undefined— A pad on the current screen.hourLabel(): string— The current hour label ("1:00").snapshot(): unknown— Capture.restore(s: unknown): void— Restore.
TaxiCraft
interface TaxiCraft
The taxi. Speeds in units per second.
hull: Vec2[]— Local hull (nose +x; use thrust-craft'sfromUpright).feet: number[]— Hull indices of the landing gear feet.thrust: { up: number; down: number; left: number; right: number }— Thruster accelerations.maxSpeed: number— Speed cap.fuel: number— Fuel tank.idleBurn: number— Fuel per second while flying.thrustBurn: number— Extra fuel per second at full thrust.gearTime: number— Seconds for the gear to come down.safe: { vertical: number; horizontal: number }— Safe landing: vertical and horizontal speed.rough: { vertical: number; horizontal: number }— Rough but survivable landing.
TaxiDef
interface TaxiDef
Everything a taxi game is built from.
screens: TaxiScreen[]— The screens.shift: number[] | { random: number }— The shift: screen indices in play order, or 'random' over all of them.gravity: number— Default gravity.taxi: TaxiCraft— The taxi.fleet: number— Taxis in the fleet.fares: TaxiFares— Money.timing: { intro: number; beam: number; walkSpeed: number; nextFare: number; crash: number; lowFuel: number }— Timings, seconds.messages: { hail: string; pad: string; up: string; thanks: string; hey: string }— Messages.step?: number— Fixed step. Default 1/60.gearLocksSideways?: boolean— The gear disables the side thrusters while it is out. Default true.firstPad?: string— The first passenger on each screen hails from the pad with this label (Space Taxi: "1"). Default any.hazardSpeed?: number— Hazard speed multiplier (difficulty scaling). Default 1.seed?: number— Seed.difficulty?: LanderDifficulty— The difficulty this was built for (informational).
TaxiEvent
type TaxiEvent = | { type: "screen"; screen: number; name: string; hour: number } | { type: "hail"; pad: string } | { type: "board"; to: string } | { type: "deliver"; pad: string; fare: number; tip: number } | { type: "hey"; penalty: number } | { type: "gate"; open: boolean } | { type: "crash"; at: Vec2; cause: string } | { type: "landed"; pad: string | null; rough: boolean } | { type: "refuel"; units: number; cost: number } | { type: "extra-taxi" } | { type: "low-fuel" } | { type: "over"; money: number } | { type: "shift-done"; money: number }
Events for a presenter.
TaxiFares
interface TaxiFares
Money rules (dollars).
local: number— Fare for a ride within a screen.between: number— Fare for a ride from the previous screen.tipMin: number— Tip at the hail: a random amount between these.tipMax: number— Highest hail tip in dollars; the tip is a random amount fromtipMinto this.tipDecay: number— Tip lost per second from the hail until delivery.roughForfeits?: boolean— A rough landing during the ride forfeits the tip. Default true.landOnPassenger: number— Landing on the waiting passenger.hey: number— Sitting on a new passenger's spot: charged everyheyEveryseconds until the taxi moves.heyEvery: number— Seconds between "HEY!"s.fuelPrice: number— Price of a unit of fuel.refuelRate: number— Fuel units a second while refuelling.extraTaxi: number[]— Earnings at which an extra taxi is given.
TaxiHazard
interface TaxiHazard
Something on a screen that destroys the taxi on contact.
id: string— Hazard id.x: number— Start position.y: number— Start position.radius: number— Collision radius.motion?: TaxiMotion— Motion. Default static.phase?: number— Time offset, seconds (phases several movers apart).blink?: { on: number; off: number }— Switch on and off: seconds on, seconds off (a laser, a force field).look?: string— Presentation hint ("ball", "star", "laser", "mine", "bird"...).box?: { w: number; h: number }— Half extents for a box-shaped hazard (a laser beam); the radius is ignored when given.
TaxiInput
interface TaxiInput
Player input.
x?: number— Thrust -1..1 each axis (x right, y down).y?: number— Thrust on the y axis, -1 (up) to 1 (down). Default 0.gear?: boolean— The fire button: gear down (or up while flying).
TaxiMotion
type TaxiMotion = /** Stays put. */ | { kind: "static" } /** Along waypoints at a speed, looping or back and forth. */ | { kind: "path"; points: Vec2[]; speed: number; pingPong?: boolean } /** Bounces inside a box like a ball. */ | { kind: "bounce"; vx: number; vy: number; box: { x0: number; y0: number; x1: number; y1: number } } /** Falls (a shooting star) from a start band to an end, then starts again after `gap` seconds. */ | { kind: "fall"; from: Vec2; to: Vec2; speed: number; gap?: number; spread?: number }
How a hazard moves.
TaxiPad
interface TaxiPad
A landing pad on a screen.
id: string— Pad id.label: string— What the passenger calls it ("1".."9"). Fuel pads are "F".x: number— Centre x of the landing surface.y: number— y of the landing surface (y down).width: number— Width of the landing surface.fuel?: boolean— A fuel pad: refuelling, never a fare.
TaxiPassenger
interface TaxiPassenger
A passenger.
phase: "beam" | "wait" | "walk-in" | "ride" | "walk-out"— wait (hailing on a pad), walk-in, ride, walk-out.from: string— Pickup pad.to: string— Destination pad id, or 'up'.x: number— Position (on a pad, or the taxi's).y: number— Vertical position, world units: on the pad while waiting, the taxi's while riding.tip: number— Tip meter.rough: boolean— A rough landing happened during the ride.between: boolean— Came from the previous screen (pays the between-screens fare).t: number— Seconds in this phase.
TaxiScreen
interface TaxiScreen
One authored screen.
id: string— Screen id.name: string— The name shown before it starts.width: number— Playfield width.height: number— Playfield height.gravity?: number— Gravity (down). Default the definition's.wells?: WellDef[]— Point wells (magnets, a black hole).walls: TerrainDef[]— Solid walls: any touch destroys the taxi.pads: TaxiPad[]— Landing pads.gate: { x: number; width: number; y?: number }— The force field in the top wall: an opening that opens for an "up" passenger.start: Vec2— Where the taxi enters (from the gate) and restarts after a crash.hazards?: TaxiHazard[]— Hazards.fares?: number— Deliveries this screen needs before a rider asks to go up. Default: once every fare pad has been served (picked up from or delivered to).data?: Record<string, unknown>— Free data for a presenter (colours, decoration).
TaxiState
interface TaxiState
The session state (plain data).
phase: "attract" | "intro" | "play" | "crash" | "over" | "done"—attractbefore a shift,introwhile the screen's name shows,play,crashwhile a new taxi is awaited,overwhen the fleet is gone,donewhen the shift is finished.hour: number— Index into the shift.screen: number— Screen index intodef.screens.money: number— Earnings, dollars.taxis: number— Taxis left (including the one flying).extrasGiven: number— Extra taxis awarded so far, one for each threshold offares.extraTaxireached.passenger: TaxiPassenger | null— The passenger on this screen, or null when none is hailing or riding.served: string[]— Pads served this screen.rides: number— Fares completed this screen.gateOpen: boolean— Whether the force field in the top wall is open (while an 'up' passenger rides).message: string— The current spoken line.messageT: number— Seconds the current message has been showing.nextT: number— Seconds to the next passenger.t: number— Seconds in the current phase.speedLight: "blue" | "yellow" | "red"— The taxi's dashboard.takeoffLight: "yellow" | "red"— Dashboard light:redwhile a passenger walks to or from the taxi (take-off is held), elseyellow.lowFuel: boolean— Whether fuel is belowtiming.lowFuelof the tank.heyT: number— Seconds until the next "HEY!" charge while the taxi sits on a new passenger's spot.hazards: Array<{ id: string; x: number; y: number; vx: number; vy: number; on: boolean; t: number; seg: number; dir: number }>— Run-time state of each hazard of the screen, in its order: position, velocity, whether it is on, its clock and its path segment and direction.order: number[]— Screen indices in play order for this shift (the definition'sshift, or random picks).rng: number[]— The seeded random stream's state: four words, from the kernel'srng.snapshot(). It advances in place and is saved with the state.
Touchdown
interface Touchdown
A running touchdown game.
readonly def: TouchdownDef— The definition the game was built from.readonly world: ThrustWorld— The thrust-craft world that flies the lander.readonly state: TouchdownState— The session state (plain data;snapshotsaves it).readonly craftId: string— The lander's craft id inworld.insertCoin(): void— Add one coin's fuel (any time; capped).start(freePlay?: boolean): void— Start a game (free play adds a coin when there is no fuel).select(): void— The SELECT button: next mission (any time).setMission(index: number): void— Choose a mission.step(input?: TouchdownInput): void— Advance one fixed step.drain(): TouchdownEvent[]— Take queued events.readout(): TouchdownReadout— The HUD values.site(id: string): TouchdownSite | undefined— A site by id.groundAt(x: number): number— The ground y at x.attract(): void— Back to attract.snapshot(): unknown— Capture the whole game.restore(s: unknown): void— Restore a snapshot.
TouchdownAbort
interface TouchdownAbort
The abort button: right the craft, kill horizontal speed, then burn hard upward.
turnRate: number— Righting rate, rad/s.accel: number— Abort engine acceleration (once upright).burn: number— Abort fuel per second.brake: number— Horizontal braking, units/s^2.minTime: number— Shortest burn, seconds.maxTime: number— Longest burn, seconds.climb: number— Stop early once climbing at least this fast (afterminTime).upright?: number— Counts as upright within this tilt. Default one heading step.
TouchdownCraft
interface TouchdownCraft
The lander craft. Speeds are world units per second, angles radians.
hull: Vec2[]— Local hull, nose along +x (use thrust-craft'sfromUpright).feet: number[]— Hull indices of the feet.thrust: number— Full-throttle engine acceleration.levels: number[]— Throttle lever levels as fractions of full (the input snaps to them).burn: number— Fuel per second at full throttle.turnRate: number— Turn rate, rad/s (controlled rotation; momentum's spin cap isspinCap).steps: number— Headings (32 on the arcade).turnBurn?: number— Fuel per second while a rotate button is held. Default 0.spinCap?: number— Momentum rotation: spin cap, rad/s. Default 4 x turnRate.spinAccel?: number— Momentum rotation: spin acceleration while held, rad/s^2. Default 2 x turnRate.spinDeadband?: number— Momentum rotation: a released spin slower than this stops, rad/s. Default 0.1.good: { vertical: number; horizontal: number; angle: number }— Good landing: highest vertical and horizontal speed and tilt.hard: { vertical: number; horizontal: number; angle: number }— Hard landing: highest vertical and horizontal speed and tilt.footTolerance?: number— Both feet must be within this of the ground (flat ground only). Default 2.flat?: number— Steepest ground counted as flat, radians. Default 0.03.
TouchdownDef
interface TouchdownDef
Everything a touchdown game is built from.
width: number— World width (wraps horizontally).view: TouchdownView— The view and zoom rule;view.heightis the world height.terrain: Vec2[]— Terrain heightfield across the world, y down, first and last heights equal so it wraps.sites: TouchdownSite[]— Flat landing sites.lit: Array<{ multipliers: number[]; count: number }>— Which sites light each round: draws from groups of multipliers.craft: TouchdownCraft— The lander.missions: TouchdownMission[]— Missions;missionpicks the starting one.mission?: number— Starting mission index. Default 0.fuel: { perCoin: number; max: number; lowAt: number; overDelay: number }— Fuel economy.scoring: TouchdownScoring— Scoring.start: { x: number; y: number; vx: number; vy: number; angle: number }— Where each round starts.abort?: TouchdownAbort | null— The abort button. Null: none.messages: TouchdownMessages— Messages.resultTime: number— Seconds a result shows before the next round.step?: number— Fixed step. Default 1/60.seed?: number— Seed for lit sites and messages.difficulty?: LanderDifficulty— The difficulty this definition was built for (informational).
TouchdownEvent
type TouchdownEvent = | { type: "round"; lit: string[] } | { type: "landed"; result: TouchdownResult } | { type: "crashed"; result: TouchdownResult } | { type: "abort" } | { type: "coin"; fuel: number } | { type: "mission"; mission: number } | { type: "zoom"; zoom: number } | { type: "low-fuel" } | { type: "out-of-fuel" } | { type: "over"; score: number } | { type: "start" }
Events for a presenter.
TouchdownInput
interface TouchdownInput
Player input for one step.
turn?: number— Rotate: -1 left (anticlockwise), 1 right.lever?: number— Thrust lever 0..1 (snaps to the lever levels).abort?: boolean— The abort button (held or pressed).
TouchdownMessages
interface TouchdownMessages
The messages shown after a landing or crash. The second line is picked at random.
good: string— Good landing headline.goodLines: string[]— Good landing second lines.hard: string— Hard landing headline.hardLines: string[]— Hard landing second lines.crashLines: string[]— Crash lines (no headline).auxTanks: string[]— Fuel-penalty lines;{n}is the units lost.points: string— Points line;{n}is the points.lowFuel: string— Low fuel warning.outOfFuel: string— Out of fuel.perCoin: string— Attract line;{n}is fuel per coin.
TouchdownMission
interface TouchdownMission
One mission (Lunar Lander's TRAINING, CADET, PRIME, COMMAND).
id: string— Mission id.name: string— Display name.card?: string[]— The mission card lines.gravity: number— Gravity, units/s^2 (down).drag?: number— Atmospheric friction: linear drag per second. Default 0.thrustScale?: number— Engine acceleration multiplier. Default 1.burnScale?: number— Engine fuel-burn multiplier (not the abort). Default 1.rotation?: "controlled" | "momentum"— Rotation:controlledturns at the fixed rate;momentumbuilds and keeps spin. Default 'controlled'.tiltLimit?: number— Largest tilt from upright, radians (Training: PI/2, never upside down). Default unlimited.
TouchdownReadout
interface TouchdownReadout
The HUD values, rounded as the arcade shows them.
score: number— Score so far.time: number— Seconds flown in the current round.fuel: number— Fuel left, whole units.altitude: number— Height above the ground below, world units, rounded; 0 when there is none.horizontal: number— Horizontal speed, units per second, rounded.horizontalDir: number— -1 left, 1 right, 0 none.vertical: number— Vertical speed, units per second, rounded.verticalDir: number— -1 up, 1 down, 0 none.
TouchdownResult
interface TouchdownResult
What the last landing or crash produced.
kind: "good" | "hard" | "crash"— How the landing was graded:good,hardorcrash.site: string | null— Id of the lit site landed on, or null when the craft came down elsewhere.multiplier: number— Score multiplier applied: the lit site's, orTouchdownScoring.unlit(default 1) off a lit site.points: number— Points awarded: the grade's base points timesmultiplier.fuelLost: number— Fuel taken as the crash penalty (units); 0 unless crashed.fuelGained: number— Fuel given for a good landing (units); 0 otherwise.lines: string[]— Message lines to show: headline, second line and the points line, with{n}filled in.at: Vec2— Where it happened, world units.
TouchdownScoring
interface TouchdownScoring
Points and fuel rules.
good: number— Good landing points (times the multiplier).hard: number— Hard landing points.crash: number— Crash points.goodFuel: number— Fuel added after a good landing.crashAllowance: number— Crash penalty: fuel allowed per second of flight; any allowance not used is lost. 0 = none.unlit?: number— Multiplier on unlit flat ground. Default 1.
TouchdownSite
interface TouchdownSite
A flat landing site cut into the terrain.
id: string— Site id.x: number— Centre x.width: number— Width of the flat.multiplier: number— Score multiplier while the site is lit.
TouchdownState
interface TouchdownState
The session state (plain data).
phase: "attract" | "flying" | "result" | "over"—attractbefore a game,flying,resultwhile a landing result shows,overwhen the game has ended.mission: number— Index intoTouchdownDef.missionsof the mission in force.score: number— Points scored this game.coins: number— Coins inserted since the lastattract.roundTime: number— Seconds flown in the current round.roundFuel: number— Fuel the craft had when the round began, in fuel units (used for the crash penalty).rounds: number— Rounds started this game.lit: string[]— Ids of the sites lit this round.lever: number— Thrust lever position now, 0..1.aborting: { t: number } | null— The abort burn in progress and the seconds it has run, or null.result: TouchdownResult | null— The last landing or crash, or null while flying.resultT: number— Seconds the result has been showing.outOfFuelT: number | null— Seconds since the fuel ran out, or null while fuel remains.lowFuel: boolean— Whether fuel is above 0 but belowfuel.lowAt.view: { zoom: number; x: number; y: number; w: number; h: number }— The camera rectangle now:zoomfactor (1 is the far view), centrexandyand sizewbyh, in world units.rng: number[]— The seeded random stream's state: four words, from the kernel'srng.snapshot(). It advances in place and is saved with the state.
TouchdownView
interface TouchdownView
The camera rule: the far view scrolls at its edges and zooms in near the ground.
width: number— Far view width, world units.height: number— Far view height (also the world height).edge?: number— Edge zone that scrolls the view, fraction of the view width or height. Default 1/8.zoom?: { factor: number; inBelow: number; outAbove: number } | null— Zoom: factor, zoom in belowinBelowaltitude, out at or aboveoutAbovenear the top. Null: never zoom.


