The pack
vehicleHandling
function vehicleHandling(): Pack<{ vehicleHandling: typeof api }>
Install the vehicle handling API.
Helpers
ASSISTS
const ASSISTS: Readonly<Record<"easy" | "normal" | "arcade", Readonly<Assist>>>
The three difficulty levels as data. arcade is the researched preset unchanged.
calibrateCurvePush
function calibrateCurvePush(spec: VehicleSpec, o: { curvature: number; lock: number; speed?: number; skidAt?: number; grip?: number }): VehicleSpec
Road frame: set road.centrifugal so that holding the line on curvature at speed (default the top gear's
top speed) takes lock (0..1) of full steering; set grip.lateral to grip (default 1) times that cornering
demand (below 1 the vehicle slides out there) and grip.skidSlip so the tyres start to squeal at skidAt
(default 0.85) of that speed. Use it to fit a preset to your track's curvature scale.
cornerSpeed
function cornerSpeed(spec: VehicleSpec, curvature: number, frame: "road" | "planar" = "road", surface: SurfaceSpec = SURFACES.tarmac): number
The fastest speed at which a vehicle holds a curve: below the grip limit and within its steering authority (road frame), or below the grip limit (planar). For traffic, drones and rival AI braking points.
createVehicle
function createVehicle(o: VehicleOptions): Vehicle
Create a vehicle in the road or planar frame.
evalCurve
function evalCurve(c: Curve, x: number): number
Evaluate a constant or a piecewise-linear [x, y] table at x, clamped at both ends.
PRESETS
const PRESETS: Readonly<{ outRunCar: () => VehicleSpec; chaseHQCar: () => VehicleSpec; sciCar: () => VehicleSpec; polePositionCar: () => VehicleSpec; nightDriverCar: () => VehicleSpec; ... 11 more ...; arcadeBike: () => VehicleSpec; }> …
Every preset by name.
REFERENCE_CURVATURE
const REFERENCE_CURVATURE: number
The curvature the road presets are fitted to: a hard pseudo-3D bend of 150 m radius. A road whose hardest bends
differ calls calibrateCurvePush(spec, { curvature, lock }) to keep the same feel.
resolveVehicles
function resolveVehicles(a: Vehicle, b: Vehicle, normal: { x: number; y: number }, depth?: number): [ContactResult, ContactResult]
Resolve a contact between two vehicles in the same frame: mass-weighted exchange of the closing velocity with
restitution (knock-back), separation by depth, and a classification for each. normal points from b to a.
routeCurvature
function routeCurvature(curve: number, segmentLength: number): number
Convert the route pack's RoadPiece.curve (a lateral shift per segment squared) into curvature in route
world units: curve / segmentLength^2. Multiply by world units per metre if the vehicle runs in metres.
scaleSpec
function scaleSpec(spec: VehicleSpec, unitsPerMetre: number): VehicleSpec
Express a spec in other length units: unitsPerMetre world units per metre (pixels per metre on a top-down
track, road units per metre on a pseudo-3D road). Every speed, acceleration, length and speed breakpoint is
scaled; aerodynamic drag (per length) is divided. Surface drag, gravity on hills and surface spin speeds follow
through unitsPerMetre. Scaling twice multiplies.
steerToward
function steerToward(state: Readonly<VehicleState>, target: { x: number; y: number } | number, o: { gain?: number } = {}): number
A steering value toward a target, for drones and traffic that drive the same handling as the player.
Planar: target is a point and the result turns the heading toward it. Road: target is a lateral position.
stoppingDistance
function stoppingDistance(spec: VehicleSpec, from: number, to = 0, surface: SurfaceSpec = SURFACES.tarmac): number
Distance to brake from from to to (default 0) at full brake on a surface, with the spec's resistances.
SURFACES
const SURFACES: Readonly<Record<string, SurfaceSpec | undefined>> & { readonly tarmac: SurfaceSpec }
Built-in surfaces. Drag is m/s^2 (scaled by a scaled spec's unitsPerMetre); every multiplier is 1 on tarmac.
The off-road ones (grass, sand, gravel, offroad, verge, median) carry offroad: true; dirt, mud, water,
snow are racing surfaces for off-road games (Badlands, Super Off Road) and are not off the road by default.
upgradeSpec
function upgradeSpec( spec: VehicleSpec, levels: { grip?: number; accel?: number; top?: number }, per: { grip?: number; accel?: number; top?: number } = {}, ): VehicleSpec
Upgrades as multipliers per level: Super Sprint's wrenches buy traction, turbo acceleration and top speed.
levels counts upgrades bought for each; per is the gain per level (default 10% grip, 12% acceleration,
6% top speed). Gear tops, auto shift points and the acceleration curves' breakpoints all follow.
withAssist
function withAssist(spec: VehicleSpec, level: "easy" | "normal" | "arcade" | Assist): VehicleSpec
Apply a difficulty assist ('easy' | 'normal' | 'arcade' or a custom Assist). Grip, curve push, steering and
crash thresholds are applied at run time; off-road penalties are softened; spins from slip, bike falls and the
manual gearbox can be switched off. Surface hazards (oil's spin on entry) and forced spins stay: they are rules.
Functions
curveMax
function curveMax(c: Curve): number
The largest y of a curve.
scaleCurve
function scaleCurve(c: Curve, kx: number, ky: number): Curve
Scale a curve's x and y axes.
Classes
Vehicle
class Vehicle
One vehicle on a fixed step. Deterministic, renderer-free, snapshot-able. Create it with createVehicle.
See docs/design/ARCADE-PACK-7-VEHICLE-HANDLING.md for the step model.
readonly frame: "road" | "planar"— The frame of reference this vehicle moves in.constructor(o: VehicleOptions)get state(): Readonly<VehicleState>— The live state (read it, do not write it).get params(): Readonly<VehicleSpec>— The current spec.get fixedStep(): number— The fixed step in seconds.display(speed = this.s.speed): number— Speed in the spec's speedometer units (km/h for the metre presets).setSpec(spec: VehicleSpec, opts: { surfaces?: Readonly<Record<string, SurfaceSpec>> } = {}): void— Replace the spec (upgrades, difficulty, the car becoming a boat). State is kept; the gear is clamped.opts.surfacesreplaces the vehicle's consumer surface table at the same time.refillBoost(charges?: number): void— Refill the timed boost tocharges(default the spec's): a new round, a continue (Chase H.Q.'s turbos).setSurfaces(surfaces: Readonly<Record<string, SurfaceSpec>>): void— Replace the consumer surface table.surfaceOf(ref: SurfaceRef): Readonly<SurfaceSpec>— Resolve a surface as this vehicle sees it (spec overrides, consumer table, built-ins, assist).place(at: RoadPose | PlanarPose, o: { speed?: number; heading?: number; gear?: number } = {}): void— Respawn or teleport. Clears crashes, spins, slides, jumps and lean. Snap the sprite (no interpolation).crash(): void— Enter the crashed state: inputs ignored, decelerates to a stop (stalled). The consumer animates.rebase(d: { ds?: number; dx?: number; dy?: number }): void— Shift the frame without touching the dynamics: slides, skids, spins, lean, knock and speed are kept. Road:dsalong,dxlateral (a road split rebasing onto the chosen branch's centre). Planar:dx,dy.knock(k: { speed?: number; vx?: number; skid?: number; time?: number }): void— A rules-driven knock that keeps everything else: set the forwardspeed, a lateral knock velocityvx(road: added to the lateral motion and held fortimes before decaying; planar: added to the world velocity across the heading), and force skid intensityskidfortimes. Out Run's traffic rebound (speed to the traffic's speed - 80 km/h, a sideways skid away from the car) is one call.spin(time?: number, dir?: number): void— Start a spin-out now, at any speed (tornado, mine, oil the consumer detects itself).dir+1 clockwise.tip(side: number, time?: number): void— Tip onto two wheels (Buggy Boy's rocks):side-1 drives on the left wheels, 1 on the right, fortimes (defaultspec.tip.time), with steering scaled byspec.tip.steer. Needsspec.tip.launch(upSpeed: number): void— Jump: airborne with this upward speed until it lands, underspec.gravity(orair.gravity), steering byair.steer, landing byair.landing. Ramps in the environment call this themselves.drain(): VehicleEvent[]— Events queued since the last drain (every step's events and contacts).snapshot(): VehicleSnapshot— Plain-JSON snapshot of the state, bookkeeping and queued events (not the spec).restore(snap: VehicleSnapshot): void— Restore a snapshot taken from a vehicle with the same frame.contact(c: Contact): ContactResult— Collision response hook. The consumer detected the contact; this changes the velocity, pushes out bydepth, classifies and reports it. Road frame: the plane is{ x: lateral, y: along s }.step(input: DriveInput = {}, env: RoadEnv | PlanarEnv = {}): VehicleEvent[]— Advance one fixed step. Returns this step's events (also queued fordrain).static resolvePair(a: Vehicle, b: Vehicle, normal: { x: number; y: number }, depth = 0): [ContactResult, ContactResult]— Two vehicles in the same frame, mass-weighted.normalpoints from b toward a.
Types
AirSpec
interface AirSpec
Flight parameters.
gravity?: number— Override ofspec.gravitywhile airborne.steer: number— Fraction of steering kept in the air (0 keeps heading and velocity exactly).crests?: boolean— Road frame: leave the ground over crests when the road falls away faster than gravity. Default true.landing?: LandingSpec— Landing consequences. Default: clean landings.
Assist
interface Assist
Difficulty assists, as multipliers and switches. ASSISTS.arcade is the identity.
grip: number— Multiplier on grip.centrifugal: number— Multiplier on the road curve push.offroad: number— 0..1: how much of an off-road surface's penalty applies (1 = all).spin: boolean— Spin-outs from slip enabled.fall: boolean— Bike falls enabled.autoGears: boolean— Force an automatic gearbox.steer: number— Multiplier on steering authority.crash: number— Multiplier on the crash threshold (higher forgives more).
BoostSpec
interface BoostSpec
A boost: turbo, nitro or Super Hang-On's turbo button.
accel: number— Extra acceleration while boosting.top: number— Extra top speed while boosting.mode: "hold" | "timed"—holdboosts while the input is held;timedfires fortimeseconds per press.time?: number— Seconds per timed boost. Default 1.charges?: number— Boosts available (timed). Omitted = unlimited.cooldown?: number— Seconds after a timed boost before another may start. Default 0.minSpeed?: number— Boosting is only possible at or above this speed (Super Hang-On: 280 km/h). Default 0.steer?: number— Steering multiplier while boosting (harder to steer). Default 1.
Contact
interface Contact
A collision handed to the vehicle by the consumer, which did the detection.
normal: { x: number; y: number }— Unit normal from the obstacle toward this vehicle, in the frame's plane (road: x lateral, y along s).depth?: number— Penetration to push out along the normal. Default 0.other?: { vx: number; vy: number; mass: number } | null— A moving other body; absent or null = immovable.kind?: "wall" | "vehicle" | "object"— What was hit: sets sideswipe (vehicle) versus wall-slide classification. Default vehicle ifother, else wall.restitution?: number— Bounce override.friction?: number— Tangential friction override.
ContactResult
interface ContactResult
How a contact was classified and what it did.
kind: "scrape" | "bump" | "sideswipe" | "wall-slide" | "crash"— The classification; the consumer decides what happens (animation, explosion, nothing).normalSpeed: number— Closing speed along the normal (>= 0).tangentSpeed: number— Relative speed along the surface.impactSpeed: number— Relative speed magnitude.angle: number— 0 grazing .. PI/2 head-on.impulse: { x: number; y: number }— Velocity change applied to this vehicle, in the frame's plane.
Curve
type Curve = number | ReadonlyArray<readonly [x: number, y: number]>
A constant, or a piecewise-linear table of [x, y] points sorted by x (clamped at both ends).
DriveInput
interface DriveInput
One step's controls. Missing fields are 0 / no change.
throttle?: number— Accelerator 0..1.brake?: number— Brake 0..1 (reverse from a standstill when the spec has one).steer?: number— -1 full left .. 1 full right.gear?: number | "up" | "down"— Manual: a gear index (a lever position) or a sequential shift. Ignored by an automatic box.boost?: boolean— Boost button (held, or pressed for a timed boost).
GearboxSpec
interface GearboxSpec
The drivetrain.
mode: "auto" | "manual"—autoshifts by speed;manualfollowsDriveInput.gear(a lever position or up/down presses).gears: ReadonlyArray<GearSpec>— At least one gear, lowest first.start?: number— Starting gear index. Default 0.shiftTime?: number— Seconds without drive during a shift. Default 0.overSpeedDecel?: number— Deceleration while above the current cap (a low gear at high speed, a surface cap, a throttle target).
GearSpec
interface GearSpec
One gear of a gearbox.
name?: string— Name shown by a HUD ("LOW", "HIGH", "3"). Default: the 1-based gear number.top: number— Top speed in this gear.accel: Curve— Drive acceleration at full throttle, by speed (a curve over speed).up?: number— Automatic box: shift up at or above this speed. Default 0.95 x top.down?: number— Automatic box: shift down below this speed. Default 0.7 x the previous gear's top.offroadTop?: number— Speed-cap multiplier on off-road surfaces in this gear, replacing the surface'stopSpeed. Out Run cuts the accelerator to 60% in LOW and 30% in HIGH off the road, so LOW is faster on the grass.
LandingSpec
interface LandingSpec
What landing does, by vertical impact speed and attitude. Every threshold is data.
bounce: number— Fraction of the impact speed bounced back up (0 = none).bounceMin: number— Below this impact speed a landing is clean (no bounce).spinAbove?: number— At or above this impact speed the vehicle spins out on landing. Omitted = never.crashAbove?: number— At or above this impact speed the vehicle crashes. Omitted = never.rollLimit?: number— Landing rolled further than this (rad) crashes (tipped, or on a wall). Omitted = never.yawLimit?: number— Landing with a slip angle (planar) or yaw (road) beyond this (rad) spins. Omitted = never.speedLoss?: number— Fraction of forward speed lost per 10 m/s of impact. Default 0.
PlanarEnv
interface PlanarEnv
Planar environment for one step.
surface?: SurfaceRef— Surface under the vehicle. Defaulttarmac.slope?: { x: number; y: number }— Downhill acceleration vector for hilly tracks.ramp?: { angle: number; heading?: number } | null— A ramp under the vehicle: launch at speed x tan(angle) (alongheadingif given).ceiling?: number— Clearance above the ground (an overpass, a tunnel).tunnel?: boolean— Inside a tunnel or under cover.
PlanarPose
type PlanarPose = { … }
A planar pose.
x: numbery: numberheading?: number
PresetName
type PresetName = keyof typeof PRESETS
A preset name.
RoadEnv
interface RoadEnv
Road-frame environment for one step.
curvature?: number— Signed 1/radius, positive bends right. Default 0.grade?: number— Rise over run, positive uphill. Default 0.halfWidth?: number— Half width whenleft/rightare omitted. Default 6 (metres, scaled with the spec).left?: number— Left road edge (lateral position).right?: number— Right road edge.road?: SurfaceRef— Surface between the edges. Defaulttarmac.off?: SurfaceRef— Surface outside the edges. Defaultgrass.kerb?: number— Kerb width just inside each edge. Default 0.kerbSurface?: SurfaceRef— Kerb surface. Defaultkerb.bands?: ReadonlyArray<SurfaceBand>— Bands checked first, last listed wins (oil patch over a puddle over the road).surface?: SurfaceRef— Force one surface.vertical?: number— Change of grade per unit distance (1/m): negative over a crest. Withair, a crest taken fast launches.bank?: number— Road bank (camber, rad): positive raises the right side; gravity pulls toward the low side.walls?: { left?: RoadWall | null; right?: RoadWall | null }— Walls beside the road, starting at the road edges.ramps?: ReadonlyArray<RoadRamp>— Ramps across the road here.tunnel?: boolean— Inside a tunnel (reported asstate.tunnel, with events).ceiling?: number— Clearance above the road (a tunnel roof): a jump is stopped there.drop?: { left?: number; right?: number }— The road is a deck above the ground (an elevated circuit): the fall beyond the left / right edge to the ground. A vehicle whose centre crosses an edge with a drop leaves the deck and falls (edge); once below the deck it cannot come back over it; reaching the ground is afellevent and a crash. A jump that lands beyond an edge keeps falling. Road-data'senvreports it wherever a road with agroundplane has no barrier.minX?: number— Hard lateral limits this step (Out Run's off-road bounds, a split branch's edges): x is clamped, vx zeroed.maxX?: number— Hard right limit this step.
RoadPose
type RoadPose = { … }
A road-frame pose.
s: numberx: number
RoadRamp
interface RoadRamp
A ramp across part of the road (logs, kickers): crossing it on the ground launches the vehicle.
from: number— One lateral edge.to: number— The other lateral edge.angle: number— Launch angle (rad): upward speed = speed x tan(angle).
RoadWall
interface RoadWall
A ridable (or solid) wall beside the road: Buggy Boy's banked sides.
angle: number— Slope from horizontal (rad). Near PI/2 is a vertical wall.height: number— Height of the wall's top above the road.ridable?: boolean— Can be driven up (otherwise its foot is a solid wall). Default true when angle < 69 degrees (road-data's rule).
SurfaceBand
interface SurfaceBand
A band of surface across the road between two lateral positions.
from: number— One lateral edge.to: number— The other lateral edge.surface: SurfaceRef— The surface between them.
SurfaceRef
type SurfaceRef = string | SurfaceSpec
A surface given by id (looked up in the spec's, the vehicle's, then the built-in table) or inline.
SurfaceSpec
interface SurfaceSpec
How a vehicle reacts to a surface: every multiplier is 1 on dry tarmac.
id: string— Surface id.grip: number— Multiplier on the cornering grip limit.traction: number— Multiplier on drive and braking.drag: number— Extra deceleration (m/s^2, scaled with the spec).topSpeed: number— Multiplier on the speed cap.damping?: number— Multiplier on road-frame lateral damping and on planar re-grip (ice and oil well below 1). Default 1.steer?: number— Multiplier on steering authority. Default 1.rumble?: number— Vibration 0..1, reported asstate.rumblefor shake and sound. Default 0.offroad?: boolean— Counts as off the road (offroadevents,state.offroad, the off-road assist,gear.offroadTop).spinOnEntry?: number— Entering this surface faster than this speed (m/s, scaled) starts a spin, e.g. oil. Omitted = never.slideTime?: number— Seconds this surface's grip loss persists after leaving it (oil on the tyres). Default 0.
VehicleEvent
type VehicleEvent = | { type: "gear"; from: number; to: number } | { type: "shift-end"; gear: number } | { type: "skid-start" } | { type: "skid-end" } | { type: "spin-start"; dir: number } | { type: "spin-end" } | { type: "offroad" } | { type: "onroad" } | { type: "surface"; id: string; prev: string } | { type: "boost-start" } | { type: "boost-end" } | { type: "over-lean"; lean: number; limit: number } | { type: "fall"; lean: number } | { type: "airborne"; vz: number } | { type: "landed"; impact: number; result: "clean" | "bounce" | "spin" | "crash" } | { type: "ramp"; angle: number } | { type: "ceiling" } /** Left the deck over an edge with a drop (`side` -1 left, 1 right). */ | { type: "edge"; side: number } /** Reached the ground below the deck (`drop` metres under the road): a crash. */ | { type: "fell"; drop: number; impact: number } | { type: "tunnel-enter" } | { type: "tunnel-exit" } | { type: "wall-ride"; side: number } | { type: "wall-leave" } | { type: "tip-start"; side: number } | { type: "tip-end" } | { type: "stalled" } | { type: "contact"; result: ContactResult }
Events a vehicle reports.
VehicleOptions
interface VehicleOptions
Options for createVehicle.
frame: "road" | "planar"—road(distance along a road and lateral offset) orplanar(top-down x, y, heading).spec: VehicleSpec— The handling parameters (a preset, scaled and assisted as needed).at?: RoadPose | PlanarPose— Starting pose:{ s, x }on the road,{ x, y, heading }on the plane. Default the origin.speed?: number— Starting speed. Default 0.gear?: number— Starting gear. Defaultgearbox.start.surfaces?: Readonly<Record<string, SurfaceSpec>>— Consumer surfaces by id (puddle, bridge, a custom oil patch), looked up before the built-inSURFACES.unknownSurface?: SurfaceRef— Used for a surface id nobody defines (a road file naming its own strips). Omitted: an unknown id throws a clear error, so typos surface during development.
VehicleSnapshot
interface VehicleSnapshot
A vehicle's complete saved state. Plain JSON.
state: VehicleState— The public state.internal: Internal— Timers and bookkeeping (shift, residue, lean, spin, knock).queue: VehicleEvent[]— Events queued and not yet drained.
VehicleSpec
interface VehicleSpec
The handling parameters of one vehicle. Presets return these; consumers may author their own.
name: string— A display name.kind: "car" | "bike"— Cars steer; bikes lean.dt?: number— Fixed step in seconds. Default 1/60.unitsPerMetre?: number— Units per metre this spec is expressed in (set byscaleSpec). Default 1.length: number— Body length, for consumers' contact geometry.width: number— Body width.mass: number— Relative mass, for knock-back.speedo?: number— Speedometer units per speed unit (3.6 for km/h from m/s). Default 3.6 / unitsPerMetre.throttle?: { mode?: "force" | "target"; curve?: Curve }— Throttle pedal:forcescales the drive;targetsets a target speed as a fraction of the gear's top (Out Run).brake: { decel: number; curve?: Curve }— Brake: full deceleration and a pedal curve (0..1 to 0..1).reverse?: { accel: number; top: number } | null— Reverse gear, or none.gearbox: GearboxSpec— Drivetrain.resist: { rolling: number; aero: number; coast?: number }— Resistances: rolling (m/s^2), aero (decel = aero x v^2), coast (extra decel with the throttle closed).hill?: number— Multiplier on g x grade (0 = hills are only visual). Default 0.boost?: BoostSpec | null— Boost, or none.steer: { /** 1/s: steering position follows the input. */ response: number; /** Planar: yaw rate (rad/s) at full lock, by speed. */ rate: Curve; /** Road: lateral speed at full lock, by speed. */ lateral: Curve; /** Planar: steering flips when reversing. Default true. */ reverseSteer?: boolean; }— Steering.grip: { /** Cornering limit (m/s^2) on traction 1. */ lateral: number; /** Planar: 0..1 blend of the front-grip yaw limit (runs wide at speed). */ understeer: number; /** Planar: yaw allowed beyond the grip limit, as a fraction (the tail steps out into a drift). */ oversteer: number; /** Planar: slip angle (rad) at which a slide in the turn's direction cancels the steering yaw. Default 1.2. */ align?: number; /** 0..1: grip while already sliding, as a fraction of the limit (holds a drift). Default 1. */ slideGrip?: number; /** Planar: slip angle (rad) where skidding starts. Road: fraction of the grip limit where it starts. */ skidSlip: number; /** Speed lost while skidding (m/s^2 at full skid). Default 0. */ skidDrag?: number; /** Skid intensity when skidding starts (0..1). Default 0.3. */ skidFloor?: number; /** How far past the skid threshold (as a ratio) the skid reaches full intensity. Default 1. */ skidRamp?: number; }— Grip.road?: { /** Multiplier on the outward curve push curvature x v^2. */ centrifugal: number; /** 1/s: lateral velocity relaxes toward the steering target at this rate (lower = slidier). */ damping: number; /** Multiplier on the push beyond the grip limit (sliding out). Default 1. */ slide?: number; /** How far beyond either road edge the vehicle may go. Default unlimited. */ bounds?: number; /** * Speed exponent of the curve push: 2 (default) pushes with curvature x v^2; 1 pushes with * curvature x v x vTop (Out Run's set_curve_adjust is linear in speed). Both agree at the top gear's top speed. */ exponent?: number; /** 1/s decay of a lateral knock (contacts, \knock()`). Default 4. / knockDamping?: number; /* Multiplier on gravity's sideways pull on banked road and ridable walls. Default 1. */ bankScale?: number; }` — Road-frame lateral behaviour.spin?: { /** Slip angle (rad, planar) beyond which a slide becomes a spin. */ slip: number; /** No slip spins below this speed. */ minSpeed: number; /** Seconds a spin lasts. */ time: number; /** Yaw rate (rad/s) at the start of a spin. */ rate: number; /** Grip multiplier while spinning. */ grip: number; /** Brake applied while spinning (0..1). Default 0.3. */ brake?: number; /** Fraction of \rate` left at the end of the spin (it eases down linearly). Default 0.35. */ endRate?: number; } | null` — Spin-outs, or none.bike?: { /** Safe lean (rad) by speed. */ maxLean: Curve; /** Lean at full input (rad). Default: the largest maxLean value. */ fullLean?: number; /** rad/s toward the target lean. */ rate: number; /** rad/s back to upright with no input. Default \rate`. / returnRate?: number; /* 0..1 opposite lateral response while a lean builds. / counterSteer: number; /* Falling: past the safe lean by `lean` rad, or past it for `time` s. null = the lean is clamped instead. */ fall?: { lean: number; time: number } | null; } | null` — Bike lean, or none.gravity?: number— Gravity (m/s^2, scaled with the spec). Drives hills (hill), banking and ridable walls, jumps, crests and landings. Default 9.81.air?: AirSpec | null— Jumps and flight. null = never leaves the ground (crests are ignored;launchstill works withgravity).tip?: { angle: number; time: number; steer: number } | null— Tipping onto two wheels (Buggy Boy's rocks), started bytip(). null =tip()does nothing.impact: { scrape: number; crash: number; sideswipeAngle: number; restitution: number; friction: number; crashDecel?: number; }— Impact classification and response.surfaces?: Readonly<Record<string, Partial<SurfaceSpec>>>— How THIS vehicle treats surfaces: overrides by id (a boat makeswaterdrivable and land off-road).assist?: Assist— The assist applied bywithAssist, kept for runtime use.
VehicleState
interface VehicleState
A vehicle's live state. Plain data; frame-specific fields are always present (unused ones stay 0).
frame: "road" | "planar"— Frame of reference.speed: number— Forward speed (planar: along the heading, negative when reversing).gear: number— Current gear index.gearName: string— Current gear name.shifting: boolean— True during a shift (no drive).revs: number— 0..1 through the gear (over 1 when over-speeding a low gear).throttle: number— Applied throttle after its curve.brake: number— Applied brake after its curve.steer: number— Smoothed steering position -1..1.surface: string— Surface id under the vehicle.offroad: boolean— Off the road.rumble: number— Vibration 0..1.shake: number— Deterministic -1..1 jitter x rumble for sprite shake.sliding: boolean— Grip exceeded (or spinning).skid: number— Skid intensity 0..1.spinning: boolean— Spin-out in progress.spinTime: number— Seconds of spin left.spinAngle: number— Visual rotation accumulated by a road-frame spin (planar spins turnheading).boosting: boolean— Boost active.boostTime: number— Seconds of timed boost left.boostCharges: number— Timed boosts left (-1 unlimited).boostCooldown: number— Seconds before another timed boost.crashed: boolean— Set by crash() or a fall; cleared by place().airborne: boolean— In a jump.z: number— Jump height.vz: number— Jump vertical speed.pitch: number— Nose-up pitch for drawing (rad): the flight path while airborne.roll: number— Body roll for drawing (rad, + right side up): bank, a ridden wall or a tip onto two wheels.bank: number— Road bank under the vehicle (rad).wall: number— -1 / 1 while riding up the left / right wall, else 0.wallHeight: number— Height up a ridden wall.tunnel: boolean— In a tunnel.tipped: number— -1 / 1 while on two wheels (tipped onto the left / right side), else 0.tipTime: number— Seconds of tip left.turn: number— -1..1 for sprite frame choice (steering, lean or drift).s: number— Road: distance along.x: number— Road: lateral position. Planar: world x.y: number— Planar: world y.heading: number— Planar: heading.vx: number— Lateral velocity (road) or world x velocity (planar).vy: number— Planar: world y velocity.yaw: number— Road: visual heading relative to the road.angVel: number— Planar yaw rate.slip: number— Planar slip angle.lean: number— Bike lean (rad, + right).leanLimit: number— Current safe lean (rad).fallen: boolean— The rider fell.tick: number— Steps taken.


