The pack
roadCombat
function roadCombat(): Pack<{ roadCombat: typeof api }>
Install the road combat API.
Helpers
COMBAT_ROADS
const COMBAT_ROADS: Record<string, RoadDocument>
The road files of this folder, by name.
createRoadCombat
function createRoadCombat(config: RoadCombatConfig, seed = 1): RoadCombat
Create a session.
desertPatrolPreset
function desertPatrolPreset(o: DesertPatrolOptions = {}): RoadCombatConfig
Desert Patrol: an original road-combat game showing the same API with different rules.
desertPatrolRoad
function desertPatrolRoad()
Desert Patrol's road file (roads/desert-patrol.road.json in this pack).
DIFFICULTY
const DIFFICULTY: Readonly<Record<Difficulty, { population: number; aggression: number }>>
Population, aggression and handling assist per difficulty. Arcade is the original tables.
SPY_AWARD
const SPY_AWARD: Readonly<Record<Difficulty, ReadonlyArray<{ score: number; cars: number }>>>
Spare cars awarded when the opening timer runs out, by score. Arcade: two cars at 18,000 (players' reports); none below a lower threshold, estimated at 9,000 (players report that zero is possible but not where). Normal and Easy are gentler so a human keeps playing past the timer.
spyHunterKinds
function spyHunterKinds(): Record<string, VehicleKindDef>
Spy Hunter's vehicles, sized from the arcade sprites at half resolution (the screen is 240 units across).
spyHunterPreset
function spyHunterPreset(o: SpyHunterOptions = {}): RoadCombatConfig
Spy Hunter (Bally Midway 1983) as a road-combat config.
spyHunterRoad
function spyHunterRoad()
Spy Hunter's road file (roads/spy-hunter.road.json in this pack): territories, forks, bridge out, boathouses.
spyHunterWeapons
function spyHunterWeapons(): Record<string, WeaponDef>
Spy Hunter's weapons: unlimited twin machine guns, and the van's oil slick, smoke screen and missiles.
Functions
validate
function validate(c: RoadCombatConfig): void
Reject configurations the session cannot run, with the reason.
Classes
CombatRoad
class CombatRoad
The road a road-combat session drives.
readonly doc: RoadDocument— The road document the course was loaded from (metres), before the game seed was mixed in.readonly data: RoadData— The loaded road data (road-data), in the document's own units: metres.constructor(doc: RoadDocument, o: CombatRoadOptions)reset(): void— Back to the start of the road.get revision(): number— Bumped whenever ground changes or more road is laid (a road-viewrevision).get entered(): readonly TerritoryVisit[]— Territories entered so far.sample(s: number): RoadSample | null— A sample at a world distance (null beyond the laid road).update(s: number, x: number, ahead: number): RoadUpdate— Advance the car to (s, x): lay road ahead, decide forks by the car's side (never into a dead end), and report territories entered, the fork taken, a boathouse crossed, and banners, vans and checkpoints ahead.territoryAt(s: number): TerritoryVisit— The territory coverings.spansAt(s: number): CombatSpan[]— The cross-section ats(world units, paint order).lookAt(s: number): string | null— The region look ats(the palette a renderer should use).featuresIn(v0: number, v1: number): CombatFeature[]— Features to draw between two distances (world units).markingsIn(v0: number, v1: number): CombatMarking[]— Lane lines to draw (world units).surfaceAt(s: number, x: number): string— The surface id under (s, x).drivable(surface: string, medium: "road" | "river"): boolean— True when a medium may drive on a surface.pathAt(s: number, preferX?: number): PathPoint— The carriageway that continues the road ats, nearestpreferX.respawnAt(s: number, preferX?: number, clear = 160): PathPoint— Where to put a car back down ats: on the continuing carriageway, clear of a median or island opening withinclearahead (the half nearestpreferXis chosen when one opens).laneAt(s: number, x: number, medium: "road" | "river"): { from: number; to: number } | null— The drivable run of the cross-section containing x (for AI steering), or null when off it.snapshot(): CombatRoadState— Plain-data snapshot.restore(s: CombatRoadState): void— Restore a snapshot from a road of the same document and seed.
RoadCombat
class RoadCombat
One game of road combat.
readonly config: RoadCombatConfig— The rule set the game was created with; it is not changed.readonly road: CombatRoad— The road being driven (road-data in world units; also a road-view-topdown source).readonly specs: { car: VehicleSpec; boat: VehicleSpec; top: number }— World-unit handling specs.constructor(config: RoadCombatConfig, seed = 1)get state(): Readonly<RoadCombatState>— The live state (read it; do not write it).get handling()— The player's handling state (gear, revs, skid) from vehicle-handling.drain(): RoadCombatEvent[]— Events since the last drain.get shots(): Shot[]— Shots in flight, in the course frame.get night(): number— Night amount 0..1 at the player's position (0 with no daylight cycle).weaponFor(id: string): WeaponDef | undefined— The weapon a button fires in the player's current medium.reset(): void— Back to the attract state at the start of the road.start(): void— Start a game: the van puts the car down at the start.tick(dt: number, input: RoadCombatInput = {}): void— Advance bydtseconds in fixed 1/60 s steps.step(input: RoadCombatInput = {}): void— One fixed step.spawnKind(kind: string, where?: "ahead" | "behind"): Actor | null— Spawn a kind ahead of or behind the player on its medium. Returns the actor or null when it cannot fit.get timerDisplay(): number | null— The number the timer display shows (Spy Hunter counts 999 down to 0), or null.snapshot(): Snapshot— Plain-data snapshot of everything.restore(snap: Snapshot): void— Restore a snapshot taken from a session with the same config.
Types
Actor
interface Actor
A vehicle in play (enemy, civilian or the van). Plain data.
id: number— Unique id, handed out fromRoadCombatState.nextId.kind: string— Id of the vehicle kind inRoadCombatConfig.kinds; the van isvan.role: "enemy" | "civilian" | "van"— Whether it scores when destroyed (enemy), costs the player (civilian) or is the weapons van (van).medium: Medium— Where it is: on the road, on the river or in the air.s: number— Distance along.x: number— Lateral.speed: number— Speed along.vx: number— Lateral speed.width: number— Body width across the road in world units.length: number— Body length along the road in world units.cruise: number— Cruise target speed (world units/s).lane: number— Lane fraction across its carriageway, -1..1.mode: string— Behaviour state.modeTime: number— Seconds in the mode.timer: number— Next attack timer.ammo: number— Shots or bombs left.hits: number— Bullet hits left.blades: boolean— Blades extended (slasher).blind: number— Seconds left blinded by smoke.credit: number— Seconds left since the player last touched it or hit it with a weapon (credit window).altitude: number— Air height 0..1 (bomber entering and leaving).cargo: string— Van: the weapon it carries.dead: boolean— Dead (exploding) and removed next step.yaw: number— Visual heading relative to the road (radians, + right).spinning: boolean— Spinning (oil).
AiDef
type AiDef = TrafficAi | RammerAi | SlasherAi | ShooterAi | BomberAi | DumperAi | TorpedoAi
Any behaviour.
Armour
interface Armour
Immunities: true means that weapon or attack does nothing to this kind.
bullets?: boolean— Machine guns and cannon shells (Road Lord: bullet-proof).ram?: boolean— Being pushed off the road.oil?: boolean— Oil slicks.smoke?: boolean— Smoke screens.missiles?: boolean— Missiles.flame?: boolean— The boat's flame.mines?: boolean— Mines.
BomberAi
interface BomberAi
A bomber (Mad Bomber): flies ahead of the player and drops bombs that blast and leave craters.
kind: "bomber"— Selects the bomber behaviour inVehicleKindDef.ai.hold: number— Distance ahead of the player it holds, world units.bombEvery: readonly [number, number]— Seconds between bombs (range).bombs: number— Bombs before it leaves.fall: number— Seconds a bomb falls.blast: number— Blast radius, world units (kills the player inside it as the bomb lands).crater: { width: number; length: number } | null— Crater size left on the ground (null: none); driving into it is a crash.track?: number— Lateral tracking rate toward the player, 1/s. Default 1.5.
CombatFeature
interface CombatFeature
A road feature for drawing (world units).
id: string— Stable id: the same feature keeps the same id while it is on screen.kind: string— What to draw, as the road document names it (boathouse, sign, gantry ...).u: number— Centre across the road, world units.v: number— Centre along the road, world units.width: number— Size across the road, world units.length: number— Size along the road, world units.z?: number— Draw order relative to the view's base z; boathouses are 30 and other features 2.
CombatMarking
interface CombatMarking
A painted marking (world units).
id: string— Stable id: dashes are keyed by it.u: number— Centre of the line across the road, world units.from: number— Start along the road, world units.to: number— End along the road, world units.width: number— Line width, world units, at least 1.dash?: number— Dash length, world units; omitted or 0 draws a solid line.gap?: number— Gap between dashes, world units; default the dash length.colour?: string— Line colour; default white.
CombatRoadOptions
interface CombatRoadOptions
Options for a CombatRoad.
unitsPerMetre: number— World units per metre (road files are in metres).seed?: number— Mixed into the document seed, so every game seed generates different territories. Default 0.ahead?: number— Lay road at least this far ahead of the car, world units. Default 600.drivable: { road: readonly string[]; river: readonly string[] }— Surfaces each medium may drive on (anything else is off the road, or aground).
CombatRoadState
interface CombatRoadState
Plain-data state.
path: PathSnapshot— The road-data path's own snapshot: the road laid so far and the car's place on it.visits: TerritoryVisit[]— Every territory entered so far, in order.ahead: number— How far ahead markers have been reported, in road-data units (metres).rev: number— The revision counter, bumped whenever ground changes or road is laid.end: number— The end of the road laid so far, in road-data units (metres).
CombatSpan
interface CombatSpan
A span of surface across the road (world units).
from: number— Left edge across the road, world units.to: number— Right edge across the road, world units.surface: string— Id of the surface in this span, looked up in the road's surfaces.
DaylightDef
interface DaylightDef
Day and night along the road (presentation reads light; the rules can shorten sight).
period: number— One full day-night cycle, world units travelled.night: number— Fraction of the cycle that is night.start?: number— Phase at the start, 0..1 (0 = noon).
DesertPatrolOptions
interface DesertPatrolOptions
Options for desertPatrolPreset.
difficulty?: Difficulty— Default normal.time?: number— Starting time, seconds. Default 75.extend?: number— Seconds added at each checkpoint. Default 40.lives?: number— Spare cars. Default 2.
Difficulty
type Difficulty = "easy" | "normal" | "arcade"
Difficulty: arcade is the original tables, normal the gallery default, easy a gentler game.
DistanceScore
interface DistanceScore
Distance scoring for one medium.
points: number— Points pereveryworld units travelled on a scoring surface.every: number— Distance per award.surfaces?: readonly string[]— Surfaces that score (kinds). Default: the carriageway kinds of that medium.
DumperAi
interface DumperAi
A dumper (Barrel Dumper): drives ahead and drops floating explosives behind it.
kind: "dumper"— Selects the dumper behaviour inVehicleKindDef.ai.dropEvery: readonly [number, number]— Seconds between drops (range).size: number— Barrel size, world units.life?: number— Seconds a barrel lasts. Default 20.weave?: number— Lane changes per second. Default 0.3.
EncounterTable
interface EncounterTable
What appears in a territory.
civilians?: { kinds: Readonly<Record<string, number>>; every: readonly [number, number]; max?: number }— Civilian kinds by weight, and the distance between them (range).enemies?: readonly EnemyEncounter[]— Enemy rows.ramp?: number— Population multiplier added per territory entered (difficulty ramp). Default 0.08.rampCap?: number— Cap on the ramp's multiplier. Default 2.
EnemyEncounter
interface EnemyEncounter
One enemy row of an encounter table.
kind: string— Kind id.every: readonly [number, number]— Distance between appearances (range), world units, at the table's base rate.after?: number— First appears this far into the territory. Default 0.fromVisit?: number— Only from this territory visit of this medium onward (0 = the first). Default 0.fromTerritory?: number— Only from this overall territory index onward. Default 0.max?: number— At most this many of the kind at once. Default 1.
Hazard
interface Hazard
A patch on the ground or water: oil, smoke, a crater, a barrel, a mine, a flame burst.
id: number— Unique id, handed out fromRoadCombatState.nextId.kind: "oil" | "smoke" | "crater" | "barrel" | "mine" | "flame" | "bomb"— What it is: an oil slick, smoke screen, crater, floating barrel, mine, flame burst or a bomb in the air.s: number— Centre distance along the road, world units.x: number— Centre across the road, world units, 0 on the road's centre line.width: number— Size across the road in world units.length: number— Size along the road in world units.life: number— Seconds left.age: number— Seconds since it appeared.owner: "player" | "enemy"— Laid by the player (credit for kills) or an enemy.medium: Medium— Medium it lies on.fall: number— Bomb: seconds until it lands.blast: number— Bomb: blast radius; crater size it leaves.crater: { width: number; length: number } | null— Crater a bomb leaves where it lands (null: none).
LivesDef
interface LivesDef
Lives.
start: number— Spare cars at the start (after a lead-in timer, the award replaces this).extra: readonly number[]— Scores that award an extra car.every?: number— After the last listed score, one more every this many points. Default none.max?: number— At most this many spare cars. Default 9.
LoiterDef
interface LoiterDef
Dawdling: staying slow or on the shoulder brings these kinds after you (Spy Hunter's limo and helicopter).
slow: number— Below this fraction of top speed counts as dawdling.seconds: number— Shoulder or slow for this long dispatches the next kind.kinds: readonly string[]— Kinds dispatched in order (one per trigger).
Medium
type Medium = "road" | "river" | "air"
Where a vehicle lives: on the road, on the river, or in the air (above both).
PathPoint
interface PathPoint
The carriageway that continues the road at a distance.
x: number— Centre across, world units.half: number— Half-width, world units.medium: "road" | "river"— Road or river.
PlayerState
interface PlayerState
The player.
medium: "road" | "river"— Car or boat.phase: "drop" | "drive" | "van" | "wreck"— drop: being put down; drive; van: riding inside; wreck: crashed.phaseTime: number— Seconds in the phase.s: number— Distance along the road, world units.x: number— Position across the road, world units, 0 on the road's centre line.speed: number— Speed along the road in world units per second.vx: number— Lateral speed in world units per second.gear: number— Index of the gear engaged (the lever position of a two-gear shifter).gearName: string— Name of the gear engaged, as the handling spec names it.yaw: number— Visual heading relative to the road.skid: number— Skid intensity 0..1.spinning: boolean— Whether an oil slick has spun the car.weapons: Record<string, number | null>— Supplies by weapon id (null = unlimited).invulnerable: number— Seconds of invulnerability left after a drop.cause: string— Cause of the last crash.surface: string— The surface under the car (road surface id).width: number— Body width across the road in world units.length: number— Body length along the road in world units.
RammerAi
interface RammerAi
A rammer (Road Lord): matches the player's speed alongside, then swerves into it to push it off the road.
kind: "rammer"— Selects the rammer behaviour inVehicleKindDef.ai.ramEvery: readonly [number, number]— Seconds between rams (range).ramTime?: number— Seconds a ram lasts. Default 0.6.gap?: number— Gap kept alongside before ramming, world units. Default 6.
RoadCombatConfig
interface RoadCombatConfig
The whole rule set. Presets build these; anything can be changed.
name: string— A name for the HUD and panel.road: RoadDocument— The road file (road-data): territories as sections, forks, boathouse links, regions, surfaces, van, banner and checkpoint markers. Road files are metres;unitsPerMetreconverts.unitsPerMetre: number— World units per metre for the handling specs (the car's sprite scale).car: VehicleSpec— The player's car and boat handling, in metres (scaled byunitsPerMetre).boat: VehicleSpec— The player's boat handling, in metres (scaled byunitsPerMetre).aiCar: VehicleSpec— A handling spec for AI road vehicles and AI boats, in metres (its gear tops are replaced per kind).aiBoat: VehicleSpec— A handling spec for AI boats, in metres (its gear tops are replaced per kind).surfaces: SurfaceMap— Surface mapping.player: { width: number; length: number; boatWidth?: number; boatLength?: number }— Player body size, world units.window: { ahead: number; behind: number }— The visible window around the player: how far ahead and behind things spawn and despawn.kinds: Readonly<Record<string, VehicleKindDef>>— Vehicle kinds by id.weapons: Readonly<Record<string, WeaponDef>>— Weapons by id.base: readonly string[]— Weapons always fitted (unlimited guns).riverSwap?: Readonly<Record<string, string>>— Weapon substitutions on the river (Spy Hunter: the oil slick button fires a flame from the boat).encounters: Readonly<Record<string, EncounterTable>>— Encounter tables by territory id, falling back todefault.<kind>(default.road,default.river).van: VanDef | null— The van, or null for none.timer: TimerDef | null— Timer, or null.lives: LivesDef— Lives.scoring: ScoringDef— Scoring.loiter: LoiterDef | null— Dawdling rule, or null.daylight: DaylightDef | null— Day and night, or null for always day.crash: { wreck: number; drop: number; invulnerable?: number }— Crash timing: the wreck shows forwrecks, then the car is put back down overdrops.population?: number— Population scale (multiplies encounter rates). Default 1.aggression?: number— Aggression scale (divides attack intervals). Default 1.difficulty: Difficulty— Difficulty passed to the handling assist.shifter?: "toggle" | "sequential"— Gears the shifter offers (indices into the car's gearbox). Default all.
RoadCombatEvent
type RoadCombatEvent = | { kind: "start" } | { kind: "banner"; text: string } | { kind: "territory"; id: string; index: number; medium: "road" | "river" } | { kind: "fork"; branch: "left" | "right" } | { kind: "kill"; actor: number; what: string; by: string; score: number; s: number; x: number; medium: Medium } | { kind: "civilian"; actor: number; what: string; s: number; x: number } | { kind: "crash"; cause: string; s: number; x: number; medium: "road" | "river" } | { kind: "respawn"; medium: "road" | "river" } | { kind: "car-lost"; spares: number } | { kind: "extra-car"; spares: number } | { kind: "timer-end"; awarded: number } | { kind: "time-up" } | { kind: "checkpoint"; bonus: number; added: number } | { kind: "van-parked"; actor: number; weapon: string } | { kind: "van-called"; actor: number; weapon: string } | { kind: "van-enter"; weapon: string } | { kind: "van-exit"; weapon: string } | { kind: "weapon"; weapon: string; supply: number | null } | { kind: "fire"; weapon: string } | { kind: "boathouse"; to: "road" | "river"; score: number } | { kind: "bomb"; s: number; x: number } | { kind: "blast"; s: number; x: number; radius: number } | { kind: "torpedo"; s: number; x: number } | { kind: "no-points"; seconds: number } | { kind: "game-over"; score: number }
Things that happened this step, for sound, effects and banners.
RoadCombatInput
interface RoadCombatInput
The controls for one step.
steer?: number— -1 full left .. 1 full right.throttle?: number— 0..1 pedal.brake?: number— 0..1 brake (only if the car has one worth using; Spy Hunter has none).gear?: number— Gear lever position (Spy Hunter: 0 LOW, 1 HIGH).fire?: Readonly<Record<string, boolean | undefined>>— Held weapon buttons by weapon id (guns,oil,smoke,missiles...).callVan?: boolean— The van button (press).boost?: boolean— Boost (if the car has one).
RoadCombatState
interface RoadCombatState
The whole session state (snapshot).
status: "ready" | "playing" | "over"—readybeforestart,playingduring play,overafter the last car is lost or time runs out.time: number— Seconds of play since the game started.score: number— Points scored.spares: number— Spare cars (not counting the one in play).extrasAwarded: number— Extra-car thresholds already awarded.timer: number | null— Timer seconds left (null: no timer or it has expired).unlimited: boolean— Lead-in timer still running: unlimited cars.noPoints: number— Seconds of NO POINTS left.distanceAcc: number— Distance accumulated toward the next distance award.player: PlayerState— The player's car or boat.actors: Actor[]— Every vehicle in play: enemies, civilians and the van.hazards: Hazard[]— Every patch on the ground or water: slicks, smoke, craters, barrels, mines, flames and bombs.shots: Shot[]— Every projectile in flight.nextId: number— The id the next actor or hazard receives.rng: number[]— The seeded stream's four 32-bit words (a kernelRngsnapshot); drawing advances them in place, so a snapshot carries the stream.road: CombatRoadState— The road's own state: the path, territories entered and how far road is laid.territory: number— The territory index the player is in.vansThisTerritory: number— Vans issued in this territory, and whether the van button may call one.vanCallable: boolean— Whether the van button may call another van: set after leaving a van while the territory has vans left; cleared on entering a territory, on a call, or when a civilian is destroyed (if the van defines that).vanCycle: number— The van's weapon cycle position.vanWeapon: string— Weapon carried by this territory's vans.encounterNext: Record<string, number>— Encounter cursors (next distance per encounter row) for the current territory.loiter: number— Seconds dawdling, and the next loiter kind index.loiterNext: number— Index of the next kind ofLoiterDef.kindsto dispatch; it wraps round.banner: { text: string; time: number } | null— Banner text and seconds left.cooldown: Record<string, number>— Weapon cooldowns (seconds until the next shot) by weapon id.held: Record<string, boolean>— Weapons held last step (for edge-triggered uses).acc: number— Accumulator for the fixed step.visitsByMedium: { road: number; river: number }— Visit counts by medium entered so far.projectiles: { player: unknown; enemy: unknown }— Projectile system snapshots.vehicles: Record<string, unknown>— Vehicle-handling snapshots by actor id ("player" for the player).fired: number— Hazard and shot ids already resolved.
RoadUpdate
interface RoadUpdate
What the road reports as the car advances.
entered: TerritoryVisit[]— Territories entered (in order).forked?: "left" | "right"— The branch taken at a fork.boathouse?: "road" | "river"— A boathouse crossed: the medium the car is now in.banners: string[]— Banner texts whose markers came into view.vans: number[]— Van parking spots that came into view (worlds).checkpoints: number— Checkpoint markers passed.
ScoringDef
interface ScoringDef
Scoring rules.
distance: { road?: DistanceScore; river?: DistanceScore }— Distance points by medium.boathouse?: { river?: number; road?: number }— Points for passing a boathouse into each medium.noPoints?: number— Seconds the score is frozen after a civilian is destroyed (NO POINTS). Default 0.civilianPenalty?: number— Points deducted for a civilian. Default 0.checkpoint?: { points: number; perSecond?: number }— Points for passing a checkpoint marker, plus per second of time left.
ShooterAi
interface ShooterAi
A shooter (Enforcer): runs alongside and fires a spread of pellets at the player.
kind: "shooter"— Selects the shooter behaviour inVehicleKindDef.ai.gap: number— Lateral gap kept alongside, world units.fireEvery: readonly [number, number]— Seconds between volleys (range).pellets: number— Pellets per volley.spread: number— Spread of a volley, radians.shotSpeed: number— Pellet speed relative to the shooter, world units per second.shotLife: number— Pellet life, seconds (its range).alongside?: number— Fire only within this distance along of the player. Default the shooter's length.windup?: number— Seconds the gunman leans out before the volley (the warning; modeaim). Default 0.5.
Shot
interface Shot
A projectile, in the road frame (x lateral, s along).
id: number— Unique id, handed out per projectile system, so a player missile or an enemy shot never shares an id with a bullet.kind: "bullet" | "shell" | "missile" | "pellet" | "torpedo"— What it is: a bullet, cannon shell, missile, shotgun pellet or torpedo.owner: "player" | "enemy"— Who fired it; the player gets the credit for kills.x: number— Position across the road, world units.s: number— Position along the road, world units.rot: number— Heading in the (x, s) plane, radians (PI/2 = straight ahead).
SlasherAi
interface SlasherAi
A slasher (Switch Blade): closes from behind, extends tyre blades alongside and swerves into the player.
kind: "slasher"— Selects the slasher behaviour inVehicleKindDef.ai.reach: number— Blade reach beyond the body, world units: touching the player inside it is a crash.bladesTime: number— Seconds the blades stay out.every: readonly [number, number]— Seconds between blade attacks (range).
SpyHunterOptions
interface SpyHunterOptions
Options for spyHunterPreset.
difficulty?: Difficulty— Easy, Normal (gallery default) or Arcade (the original tables). Default normal.timer?: number— The operator's game timer: 60 or 90 seconds (DIP switch; 90 is the factory setting). Default 90.extra?: readonly number[]— Extra-car scores. Default 30,000, 60,000, 90,000 and 120,000 (the attract screen).vans?: number— Vans per territory. Default 2.noPoints?: number— Seconds of NO POINTS after a civilian. Default 3 (an estimate: "a second or two" to "a short time").
SurfaceMap
interface SurfaceMap
How the session reads the road's surfaces, per medium.
road: Readonly<Record<string, string>>— Road surface id to a vehicle-handling surface id, for road vehicles (*is the fallback).river: Readonly<Record<string, string>>— For boats.drivable: { road: readonly string[]; river: readonly string[] }— Surfaces each medium may drive on; anything else is off the road (a crash) or aground.shoulder?: readonly string[]— Shoulder surfaces: drivable, but they score nothing and count as dawdling.specs?: Readonly<Record<string, SurfaceSpec>>— Extra handling surfaces (puddle, bridge, shoulder ...) by id.
TerritoryVisit
interface TerritoryVisit
A territory (section visit) as entered.
index: number— Order of entry, from 0.id: string— Section id.s0: number— Start along, world units.s1: number— End along (the next territory's start), 1e12 until known (plain JSON).length: number— Length, world units.kind: "road" | "river"— Road or river.ofKind: number— How many territories of this medium were entered before it.via: string | null— The link it was entered through (boathouse), or null.
TimerDef
interface TimerDef
The opening timer (Spy Hunter: unlimited cars while it runs) or a checkpoint timer (time runs out = game over;
every checkpoint marker on the road adds time).
mode: "lead-in" | "checkpoint"—lead-in: unlimited cars until it expires, then cars are awarded by score.checkpoint: the game ends when it reaches 0; eachcheckpointmarker passed addsextendseconds.seconds: number— Seconds.display?: number— The number the display counts down from (Spy Hunter: 999). Default: seconds.award?: ReadonlyArray<{ score: number; cars: number }>— Lead-in: spare cars awarded when it expires, by score reached (the highest row met).extend?: number— Checkpoint: seconds added at each checkpoint marker.cap?: number— Checkpoint: the most time that can be banked.pauseInVan?: boolean— Pause while the car rides in the van. Default true.
TorpedoAi
interface TorpedoAi
A torpedo boat (Doctor Torpedo): from behind it fires forward, from ahead it fires back.
kind: "torpedo"— Selects the torpedo-boat behaviour inVehicleKindDef.ai.fireEvery: readonly [number, number]— Seconds between torpedoes (range).speed: number— Torpedo speed relative to the boat, world units per second.life: number— Torpedo life, seconds.
TrafficAi
interface TrafficAi
Traffic: holds a lane at its cruise speed, changes lane now and then, slows behind slower traffic.
kind: "traffic"— Selects the traffic behaviour inVehicleKindDef.ai.laneChange?: number— Lane changes per second (chance). Default 0.08.
VanDef
interface VanDef
The weapons van (Spy Hunter) or any supply vehicle the player drives into.
weapons: readonly string[]— Weapons it can carry, in order.pick: "cycle" | "random"— Which one each van carries: in order, by the stream, or the same one.perTerritory: number— Vans per territory: the first parks on the shoulder (at the road'svanmarkers), the rest are called with the van button.width: number— Body size.length: number— Body length along the road in world units.speed: number— Speed while overtaking and leading, as a fraction of the player's top speed.wait: number— Seconds it waits ahead with the ramp down before leaving.inside: number— Seconds the car rides inside.tolerance: number— Lateral alignment tolerance for driving in, world units.civilianCancels?: boolean— Killing a civilian cancels the called van (Spy Hunter's lamp goes out). Default true.medium?: "road"— Vans only on these media. Default road.
VehicleKindDef
interface VehicleKindDef
A kind of vehicle the session can spawn.
role: "enemy" | "civilian"— Enemies score when destroyed; civilians cost NO POINTS.medium: Medium— Road, river or air.width: number— Body size, world units.length: number— Body length along the road in world units.mass?: number— Relative mass for ramming. Default 1.score?: number— Points when destroyed. Default 0.speed: readonly [number, number]— Cruise speed range as a fraction of the player's top speed.top?: number— Top speed as a fraction of the player's top speed. Default 1.1 x the cruise maximum.armour?: Armour— Immunities.ai: AiDef— Behaviour.enter?: "ahead" | "behind" | "either"— Where it enters: ahead (overtaken by the player), behind (catches up) or either. Default ahead.hits?: number— Hit points against bullets. Default 1.
WeaponDef
interface WeaponDef
A player weapon.
kind: "gun" | "cannon" | "slick" | "smoke" | "missile" | "flame" | "mine"— What it does: forward guns, a forward cannon, a rear oil slick, rear smoke, anti-air missiles, a rear flame, rear mines.label: string— HUD lamp label.supply: number | null— Supply when issued: shots, uses or seconds of spray (seeunit); null is unlimited.unit: "uses" | "seconds"— Whatsupplycounts.rate?: number— Shots per second while held (guns, cannon, flame ticks). Default 8.speed?: number— Projectile speed relative to the car (guns, cannon, missiles).life?: number— Projectile life, seconds (its range).radius?: number— Projectile radius. Default 1.5.hits?: readonly Medium[]— Targets it can hit. Guns: road and river; missiles: air.pierce?: number— Hits beyond the first (cannon). Default 0.homing?: number— Homing turn rate, rad/s (missiles). Default 0.width?: number— Patch size for slick, smoke, mines and flame (world units).length?: number— Patch length along the road in world units.patchLife?: number— Patch life, seconds.blind?: number— Seconds a vehicle is blinded by smoke. Default 1.5.damage?: number— Damage per hit. Default 1.


