The pack
skyCircuit
function skyCircuit(): Pack<{ skyCircuit: typeof api }>
Install the sky circuit API.
Helpers
CLOUD_CUP_RACERS
const CLOUD_CUP_RACERS: RacerDef[]
Cloud Cup's six racers (original characters).
cloudCupPreset
function cloudCupPreset(o: PresetOptions = {}): SkyCircuitConfig
Cloud Cup, an original configuration of the same rules: one course of three circuits floating 20 to 80 m over a cloud floor, barriers on every edge (nobody falls), six racers, three laps, only a WIN goes on to the next circuit, the player starts last, no catch-up, a floaty low-gravity kart and kickers that send it soaring; prizes for the top three and no extra stage.
cloudKart
function cloudKart(): VehicleSpec
The cloud kart: Power Drift's buggy-kart at well under half its weight in the air, so every crest is a long float.
createSkyRace
function createSkyRace(config: SkyCircuitConfig): SkyRace
Create a sky-circuit race.
DIFFICULTIES
const DIFFICULTIES: readonly Difficulty[]
The three levels in order.
GIRDER_TOWERS
const GIRDER_TOWERS: TowerDef
Red lattice support towers beside the elevated decks (Power Drift's girder pylons).
POWER_DRIFT_RACERS
const POWER_DRIFT_RACERS: RacerDef[]
Power Drift's twelve racers (Sega Retro's list; the looks from the promotional video's portraits). The arcade's
drivers make no difference to the kart (Japanese Wikipedia); skill is each one's pace as a RIVAL here, an
estimate (no source documents the rivals' driving).
powerDriftCourses
function powerDriftCourses(): CourseDef[]
Power Drift's five courses (A to E) of five tracks (names from Wikipedia and Arcade History).
powerDriftExtra
function powerDriftExtra(course: string, vehicle: VehicleSpec): ExtraDef
Power Drift's extra stage for a course: five wins earn it; the kart morphs into After Burner's F-14 on courses A, C and E and into Hang-On's motorbike on B and D (Wikipedia EN/JA, Sega Retro, the frames). The machine is drawn differently; it drives the track like the kart (the arcade's morph is a picture).
powerDriftPreset
function powerDriftPreset(o: PresetOptions = {}): SkyCircuitConfig
Power Drift: twelve karts, four laps, finish third or better to race the next of the course's five tracks (else the game is over); first in all five earns the extra stage. The player starts fifth on the grid of two abreast (every stage in the longplays). No time limit. Score runs with speed (about 900 a second at 244 km/h), plus a PRIZE BONUS for a win and a TIME BONUS at every finish (A1: 42,640 for a win, 28,920 for second; the formula is an estimate fitted to those). Easy and Normal soften the rivals' pace; Arcade is the estimate of the original.
RIBBON_STEP
const RIBBON_STEP: 4
Metres between the ribbon's vertices.
SKY_STEP
const SKY_STEP: number
The fixed simulation step (seconds).
trackOf
function trackOf(id: string, laps?: number): TrackDef
A track from its file: towers where the look has girders.
TRACKS
const TRACKS: Record<string, RoadDocument>
The road files of this folder, by name.
validate
function validate(c: SkyCircuitConfig): void
Throw with a clear message when a configuration cannot run.
Classes
SkyRace
class SkyRace
A sky-circuit race. Create with createSkyRace(config).
readonly config: SkyCircuitConfig— The configuration the race was built from.state: SkyState— Everything the presenter reads; updated by each step.track!: Track— The track being raced; replaced when the next track or the extra stage loads.field!: DepthField— The depth field of the current track: its scenery, towers, karts and shadows; replaced with the track.constructor(config: SkyCircuitConfig)get courseDef(): CourseDef— The course being raced.get karts(): readonly KartState[]— Every kart's rules state (the player's included), in field order (racer order).get player(): KartState— The player's kart.get vehicle(): Vehicle— The player's vehicle (gear, revs, speed for the HUD).get speedDisplay(): number— Speed in the vehicle's display units (km/h).get lapProgress(): number— The player's share of the lap, 0..1 (for the course map).lapProgressOf(id: string): number— Every kart's share of the lap, by racer id (rival dots on the map).autoInput(): SkyInput— The rivals' driver at the wheel of the player's kart: the input it would give now (pace, the line, braking for bends, passing; it shifts the box itself). An attract-mode demo, or a hands-off test drive. Feed it totick.start(): void— Start (from attract): the signals, then the race.tick(dt: number, input: SkyInput = {}): void— Advance bydtseconds of wall time (fixed steps inside, at most 0.25 s per call).get droppedEvents(): number— Events discarded from thedrainqueue because it was full (eventLimit), since the race was created.drain(): SkyEvent[]— Events since the last drain, oldest first; at mosteventLimit(1024 by default): if it was not drained in time the oldest were discarded.scene(): SkyScene— What to draw now (road-view-pseudo3d's ribbon scene, plus the focus its chase camera follows).step(input: SkyInput = {}): void— One fixed step.snapshot(): SkySnapshot— A plain copy of the whole race.restore(s: SkySnapshot): void— Restore a snapshot taken from a race with the same config.
Track
class Track
A circuit ready to race.
readonly def: TrackDef— The definition the track was built from.readonly road: RoadData— The loaded road-data document.readonly path: RoadPath— The lap driven as a path (curved frame), laid lazily as karts drive further.readonly lap: number— Lap length (metres).readonly ribbon: RibbonLeg— The lap laid in world space (closed).readonly index: RibbonIndex— Spatial index of the deck, so scenery stays off every stretch of a track that crosses itself.constructor(def: TrackDef)ensure(z: number): void— Lay the road far enough to sample z (laps are laid lazily as the karts drive them).env(z: number, bounds: number, keepOn = false): RoadEnv— vehicle-handling's environment at course distance z. The deck edges include the shoulders (the kerbs). Over the ground a kart may runboundsmetres off the road; beside a drop nothing holds it (it falls);keepOn(the rivals) holds it on the deck instead.scenery(): ScenePlacement[]— The lap's scenery (road-data pools and landmarks over one lap), with anything that would stand on another stretch of the deck dropped: a track passing over or beside itself must not grow trees through its bridges.towers(): { x: number; y: number; z: number; w: number; h: number; kind: string }[]— The support towers of the lap (def.towers): one object per storey, standing on the ground beside the deck.inLap(z: number): number— Course distance within the lap (0..lap).
Types
ByDifficulty
type ByDifficulty<T> = T | { easy: T; normal: T; arcade: T }
A value, or one value per difficulty.
ContactDef
interface ContactDef
Kart-to-kart contact.
rearLoss: number— Speed lost (fraction) by the kart running into the back of another; the one hit gains half of it.side: number— Lateral knock (m/s) when two karts touch side by side, and how long it holds (s).sideTime: number— Seconds the sideways knock holds.spinAbove?: number | null— A side hit above this closing speed (m/s) spins the kart that was hit (null: never).
CourseDef
interface CourseDef
A course: tracks raced in order.
id: string— Stable id of the course;SkyCircuitConfig.coursenames one.name: string— Name shown on the course select screen.tracks: TrackDef[]— The tracks raced, in order.
Difficulty
type Difficulty = "easy" | "normal" | "arcade"
Difficulty levels: arcade is the original tables; normal (the gallery default) and easy are kinder.
EffectsDef
interface EffectsDef
The objects the race spawns itself. Only the kart shadow is spawned today; any other key is refused by validate.
shadow?: { w?: number; h?: number }— The shadow under each kart (kindshadow): width, and height of the object (metres). Default the kart'swx 1.05 and 0.3.
ExtraDef
interface ExtraDef
The reward stage for winning every race of a course.
when: "all-wins" | "complete"— When it is given:all-wins(first in every race of the course) orcomplete(every race qualified).track: TrackDef— The track raced, and its laps.laps: number— Laps of the extra stage.vehicle: VehicleSpec— The vehicle driven on it (a different machine).kind: string— The player's sprite kind on it.finish: number[]— Points per position at its finish (it does not end the game whatever the result).
FallDef
interface FallDef
Falling off the deck.
time: number— Seconds from leaving the deck until the kart stands on the road again.restartSpeed: number— Speed it is put back at (fraction of its top speed).restart?: "centre" | "lane"— Where it is put back, at the course distance it left:centreof the road, orlane, the side of the deck it left from, one kart width in from the edge (the lateral place it was in if that was already further in). Defaultcentre.
GridDef
interface GridDef
The grid: rows of karts behind the start line.
columns: number— Karts per row.spacing: number— Metres between rows.from: number— Metres from the start line to the first row.player: ByDifficulty<number>— The player's slot (0 = pole,field - 1= last), by difficulty.
KartState
interface KartState
A kart in the race (rules state; its body is field object obj).
id: string— Racer id.obj: string— Field object id.z: number— Course distance (z) and lateral position.x: number— Lateral position, metres right of the road's reference line.y: number— Height above the road (a jump, or below the deck when it has fallen).speed: number— Forward speed, m/s.lap: number— Laps completed.place: number— Place, 1 = leading.player: boolean— The player's kart.finished: boolean— Finished the race (and its finishing place and time).finishPlace: number | null— The place it finished in, 1 = first; null until it has finished.finishTime: number | null— Race time, seconds, at which it finished; null until it has.turn: number— Steering -1..1 (for turn frames).airborne: boolean— True while the kart is in the air (a jump, or a fall off the deck).
PresetOptions
interface PresetOptions
Options for the presets.
course?: string— The course to race, an id from the preset's courses:atoefor Power Drift (defaulta); Cloud Cup has one course and ignores it.difficulty?: Difficulty— The difficulty level. Defaultnormal.seed?: number— The config's seed. Default the preset's own (1988 for Power Drift, 7 for Cloud Cup).player?: string— The player's racer.
RacerDef
interface RacerDef
One racer of the field: who they are, what they drive and how well.
id: string— Stable id (the sprite kind iskart-<id>unlesskindis given).name: string— Name shown on the select screen and the results.kind?: string— Sprite kind of the kart. Defaultkart-<id>.colours?: Record<string, string>— Colours for the presenter's art (body, driver, helmet...): free data.skill: number— How quick this racer is as a rival: 0..1 of the rival speed range (RivalDef.speed). The arcade's rivals differ in pace; a strong rival leads, a weak one tails off.data?: Record<string, string | number | boolean>— Free data (nationality, the kart's look, a catch phrase).
ResultRow
interface ResultRow
One race's result row.
id: string— The racer's id.name: string— The racer's name.place: number— Finishing place, 1 = first.time: number | null— Race time in seconds, or null if the racer had not finished when the table was made.player: boolean— True for the player's row.
RivalDef
interface RivalDef
The rivals' racing.
speed: ByDifficulty<[number, number]>— Cruising speed range as a fraction of the kart's top speed: a racer of skill s cruises at lerp(min, max, s).corner: number— Share of the kart's own corner speed (vehicle-handling'scornerSpeed) the rivals take bends at.look: number— Seconds of road they read ahead for corners and karts in the way.accel?: number— The most acceleration (m/s^2) a rival's driver asks of the throttle: the pedal is eased so the kart's drive, at its current speed and gear, does not exceed it. Omit for the kart's own full drive (the presets do). The kart's engine sets what is possible; this only holds a driver back, which is also how a slow-starting field is made. Not used with athrottle.modeoftarget, where the pedal sets a speed rather than a force.brake?: number— The hardest braking (m/s^2) a rival's driver asks for: the brake pedal is limited so the kart's deceleration does not exceed it. Omit for the kart's own full brake (the presets do). Held back like this a driver overshoots bends unlesscorneris lowered orlookraised.catchUp?: ByDifficulty<{ gap: number; ahead: number; behind: number } | null>— Catch-up: a rival more thangapmetres ahead of the player eases off toaheadof its pace, one more thangapbehind pushes tobehind(the arcade keeps the pack round the player). Null: none.line: number— Fraction of the half-width a rival's racing line may use (1 = kerb to kerb).weave: number— Lateral speed limit of a rival's line change (m/s).gap: number— Rivals keep this far (metres) from a kart ahead in their line before moving out to pass.
ScoringDef
interface ScoringDef
Scoring.
distance: number— Points per metre driven on the road while racing (the arcade's score runs with distance).finish: number[]— PRIZE BONUS for finishing a race, by position (1st first); missing places score 0.time?: { pace: number; points: number } | null— TIME BONUS at the finish:pointsper second the race time is under par, par beingpaceseconds per kilometre of the race (the arcade adds one to every finisher; 0 when slower).pass?: number— Points per rival passed, awarded while racing for every place the player gains beyond the best place held so far this race (the grid place at the start). Places lost and won back score nothing, so only the net gain over a race is paid, never twice. Default 0.
SkyBookkeeping
interface SkyBookkeeping
A race's private bookkeeping, as a snapshot holds it: where in the cup it is, lap and scoring progress, and timers. Read it only to store it; restore puts it back.
course: number— Course index inconfig.courses.track: number— Track index in the course, or -1 on the extra stage.extra: boolean— The race is the course's extra stage.lapStart: number— The player's racing distance at the last lap line.lapsDone: number— Laps the player has finished (for lap events).scored: number— The player's scored distance so far (for distance points).wins: number— Wins on this course (for the extra stage).fallTime: number— Seconds of the current fall.overSaid: boolean— The game-over event has been sent.order: string[]— Finishing order so far (racer ids).heading: number— Integrated road heading under the player.best: number— The best (lowest) place the player has held in this race, from the grid: places beyond it earnscoring.pass.
SkyCircuitConfig
interface SkyCircuitConfig
One whole configuration: courses of tracks and the race rules.
name: string— Shown in a panel.difficulty: Difficulty— The difficulty level: picks the per-difficulty values of the rivals' pace, catch-up and grid slot, and the assist on the player's kart.seed: number— Seed for each rival's lane bias: the same seed gives the same field.courses: CourseDef[]— The courses the player chooses from.course: string— The course raced (an id fromcourses).startTrack?: number— The track of the course to start on (0-based; practice from a later track). Default 0.laps: number— Laps per race (a track's ownlapswins).racers: RacerDef[]— The field, the player's racer included.player: string— The player's racer (an id fromracers).qualify: number— Finish in this position or better to race the next track; otherwise the game is over.vehicle: VehicleSpec— The kart (vehicle-handling spec, metres): the player's and every rival's.assist?: boolean— Apply vehicle-handling's assist for the difficulty to the player's kart. Default true.surfaceSpecs?: Record<string, SurfaceSpec>— Extra vehicle surfaces the tracks name.kart: { w: number; h: number; d: number; hit?: number }— Kart body (metres): drawn widthw, collision widthhit, height and length.bounds: number— How far beyond the deck edges a kart may drive where the road is on the ground (metres).countdown: number— Seconds of start signals (the lights) before GO.signals: number— Lights shown during the countdown.grid: GridDef— Where the karts start.rivals: RivalDef— How the rivals race.contact: ContactDef— What happens when karts touch.fall: FallDef— What happens when the player's kart leaves the deck.scoring: ScoringDef— How points are earned.finishTime: number— Seconds of the finish (the result banner) before the next race or the end.extra?: ExtraDef | null— The reward stage, or null.effects?: EffectsDef— Sizes of the objects the race spawns itself, apart from the scenery and the karts. Default none: the defaults below.eventLimit?: number— Most events the race keeps fordrain(). A game that never drains would otherwise grow the queue forever; when it is full the oldest events are discarded (droppedEventscounts them). Whole number >= 0. Default 1024.
SkyEvent
type SkyEvent = | { type: "start"; track: number; name: string; extra: boolean } | { type: "signal"; n: number; of: number } | { type: "go" } | { type: "lap"; lap: number; time: number } | { type: "final-lap" } | { type: "jump"; id: string } | { type: "land"; id: string; impact: number } | { type: "bump"; a: string; b: string; side: boolean; speed: number } | { type: "spin"; id: string } /** The player's kart hit a solid roadside thing (its kind) at this speed (m/s). */ | { type: "hit"; kind: string; speed: number } | { type: "edge"; side: number } | { type: "fell"; drop: number } | { type: "back-on" } | { type: "place"; place: number; was: number } | { type: "finish"; place: number; time: number; qualified: boolean; prize: number; timeBonus: number } | { type: "results"; rows: ResultRow[] } | { type: "next"; track: number } | { type: "extra" } | { type: "game-over" } | { type: "complete" } | { type: "vehicle"; event: string }
What happened (drain each step).
SkyInput
interface SkyInput
Input for one step.
throttle?: number— Throttle pedal, 0 (off) to 1 (full); values outside are clamped. Default 0.brake?: number— Brake pedal, 0 (off) to 1 (full); values outside are clamped. Default 0.steer?: number— Steering, -1 (full left) to 1 (full right); values outside are clamped. Default 0.gear?: number | "up" | "down"— The shifter: 0 LOW, 1 HIGH (a lever position), or 'up' / 'down'.
SkyPhase
type SkyPhase = "attract" | "countdown" | "racing" | "falling" | "finish" | "results" | "over" | "complete"
Race phases.
SkyScene
interface SkyScene
What scene() returns: the data road-view-pseudo3d's ribbon view draws (structural, no view import).
ribbon: RibbonLeg— The track laid in 3D (road-data's RibbonLeg).field: DepthField— Every object: scenery, karts, shadows (course coordinates).focus: { z: number; x: number; y: number; turn: number; yaw: number }— The kart the camera follows: course z, lateral x, height above the road, steering, yaw on the road.style: string | null— The look of the road under the focus (a region look key), or null.heading: number— Integrated heading of the road under the focus (for backdrop scroll).
SkySnapshot
interface SkySnapshot
A complete saved race: plain data.
state: SkyState— The state the presenter reads.internal: SkyBookkeeping— The race's private bookkeeping: the track and course being raced, lap line, wins, fall timer and the like.karts: { st: KartState; vehicle: VehicleSnapshot; pace: number; bias: number; line: number; pose: number; obj: string; shadow: string }[]— Every kart: its rules state, its vehicle's snapshot and its driver's pace, lane bias, line and pose.field: DepthFieldSnapshot— The depth field holding the scenery, karts and shadows.events: SkyEvent[]— Events raised and not yet drained.acc: number— Seconds of time carried towards the next fixed step.
SkyState
interface SkyState
Everything the presenter reads.
phase: SkyPhase— Where the game is:attractbeforestart, thencountdown,racing,falling(off the deck, waiting to be put back),finish(the result banner),results(the table), and at the endover(did not qualify) orcomplete.phaseTime: number— Seconds spent in the current phase.course: string— The course and the track being raced (1-based within the course), and whether this is the extra stage.track: number— The track being raced, 1-based within the course; one more thantrackson the extra stage.tracks: number— Tracks in the course, not counting the extra stage.extra: boolean— True while the extra stage is being raced.trackName: string— The name of the track being raced.lap: number— Lap being driven (1-based, capped atlaps) and the race length.laps: number— Laps in this race: the track's ownlaps, else the config's (the extra stage's own for the extra stage).lapTime: number— Time on this lap, the last lap and this race.lastLap: number | null— Seconds the last finished lap took; null before the first.raceTime: number— Seconds since the green light, across all laps; counts while racing or falling.place: number— The player's place now (1 = leading).places: number[]— Places of every race finished on this course (1-based).score: number— The player's points: distance driven, rivals passed, and the prize and time bonuses.topScore: number— The highestscorereached so far.signal: number— Lights lit in the countdown (0..signals) and GO.results: ResultRow[] | null— The last race's result table (null before one ends).bonus: { prize: number; time: number } | null— The PRIZE and TIME bonuses of the last finish (null before one).qualified: boolean | null— The player qualified for the next race (set at the finish).falls: number— Times the player has fallen off, and jumps flown.jumps: number— Jumps flown by the player's kart.sinceLap: number— Seconds since the last lap ended (for the LAP TIME banner).
TowerDef
interface TowerDef
Support towers stood beside an elevated deck (Power Drift's red lattice towers): every spacing metres where the
deck is at least min above the ground, one each side, gap outside the deck edge, stacked from the ground in
storeys of storey metres up to above metres over the deck.
kind: string— Field object kind of each tower storey (what the presenter's sprite table is keyed by).spacing: number— Course metres between one pair of towers and the next.storey: number— Height of one storey, metres.w: number— Width and depth of a tower, metres.gap: number— Metres between the outer edge of the deck's shoulder and the tower.min: number— The least height of the deck above the ground, metres, at which towers are stood.above: number— Metres the stack rises above the deck.
TrackDef
interface TrackDef
One track: a closed lap in a road-data file (a close, level section whose successor is itself).
id: string— Stable id of the track (the preset tracks use their road file's id, such asa1).name: string— Name shown for the track in panels and banners (SkyState.trackName).road: RoadDocument— The circuit. Its start section is the lap;defaults.groundsets the ground plane the deck stands over; scenery kinds withon: "ground"stand on it;wallfeatures (upright) are barriers the kart cannot fall past;rampfeatures launch it; the regions'lookkeys pick the presenter's style.laps?: number— Laps (overrides the config's).towers?: TowerDef | null— Support towers beside elevated decks, or none.data?: Record<string, string | number | boolean>— Free data for the presenter (the look, the music).


