Phaser AE

Road pursuit

Pro packv3.0.0-12DArcade

Rules for a police chase on a pseudo-3D road: catch a getaway vehicle against the clock, ram it or shoot it until the damage meter is full, and make the arrest. It sits on top of `road-racer`, which drives your car, the road, the traffic and the crashes, and owns everything about the chase.

This is a Pro pack. Upgrade to Phaser Pro to use it in your games.See Phaser Pro →

Released October 19, 2026 · updated October 20, 2026

Road pursuit: a screenshot of its demo
A note about these graphics

Expansion packs don't come with graphics. This screenshot is one way of drawing what the pack does, made for its demo: none of the sprites, tiles or backgrounds in it are in the pack.

That's by design. A pack is a game's logic, mechanics and effects, and it works with whatever art you give it. Some packs draw effects, backdrops or 3D scenes of their own, but none carries a game's sprites.

So the look is yours. Make the art on phaser.io, bring your own, or let your agent draw it in code: the same pack can look like anything.

The pack

roadPursuit

function roadPursuit(): Pack<{ roadPursuit: typeof api }>

Install the road pursuit API.

Helpers

CHASE_HQ_CARS

const CHASE_HQ_CARS: Readonly<Record<string, TargetCarDef>>

Getaway cars by the ROUND list of Taito's manual (white, yellow, silver, blue, unknown) and the attract specs.

CHASE_HQ_KINDS

const CHASE_HQ_KINDS: Record<string, SceneryKind>

Kinds the race and the pursuit spawn: tyre smoke, dust, sparks, the target's smoke and fire, debris.

CHASE_HQ_MISSIONS

const CHASE_HQ_MISSIONS: readonly MissionDef[]

The five rounds. Briefings follow Nancy's pattern on the arcade's radio monitor (StrategyWiki, the frames).

CHASE_HQ_SCALE

const CHASE_HQ_SCALE: 1.2

Chase H.Q. draws traffic and the getaway cars a little larger than the player's scale. Measured at equal depth on the arcade frames (REFERENCES.md: width / (base row - horizon row)): the Porsche 0.87, a traffic sports car 1.08 (x1.24), the Lotus 0.98 (x1.13). Drawn widths vw are 1.2 x true; collision boxes stay inside them.

CHASE_HQ_TRAFFIC

const CHASE_HQ_TRAFFIC: TrafficKind[]

Civilian traffic seen on the arcade frames: sports cars, saloons, a pickup, a truck and a bus. Speeds are estimates.

chaseHQPreset

function chaseHQPreset(o: ChaseHQOptions = {}): PursuitConfig

Chase H.Q. as a pursuit configuration.

chaseHQRace

function chaseHQRace(o: ChaseHQOptions = {}): RoadRaceConfig

The race underneath Chase H.Q. (road-racer configuration).

createPursuit

function createPursuit(config: PursuitConfig): Pursuit

Create a pursuit.

leadFromRoad

function leadFromRoad(doc: RoadDocument, entry: string, fallback: number): number

The road file's boss marker carries each round's starting DISTANCE (data.lead): the file is the source of truth.

PURSUIT_ROADS

const PURSUIT_ROADS: Record<string, RoadDocument>

The road files of this folder, by name.

SCI_CARS

const SCI_CARS: Readonly<Record<string, TargetCarDef>>

The getaway vehicles (cars 1 to 3 are not identified by any source: shapes from the frames, names generic).

SCI_MISSIONS

const SCI_MISSIONS: readonly MissionDef[]

Six stages: five arrests and the run to the hideout.

SCI_TRAFFIC

const SCI_TRAFFIC: TrafficKind[]

Civilian traffic.

sciPreset

function sciPreset(o: SciOptions = {}): PursuitConfig

S.C.I. as a pursuit configuration: the gun, the bazooka, the biker gang, a flying target and a reach stage.

sciRace

function sciRace(o: SciOptions = {}): RoadRaceConfig

The race underneath S.C.I.

stepTarget

function stepTarget(t: TargetState, d: TargetDrive, w: TargetWorld): void

Move the target one step.

Classes

Pursuit

class Pursuit

A pursuit. Create with createPursuit(config).

  • readonly config: PursuitConfig — The configuration the pursuit was created with.
  • race: RoadRace — The road-racer race under the pursuit: the car, road, traffic and field. It is replaced by a new race at the start of each mission.
  • state: PursuitState — The pursuit state the presenter reads; replaced on a restore, so read it afresh each frame.
  • riders: RiderState[] — The gang riders on the road now.
  • constructor(config: PursuitConfig)
  • get mission(): MissionDef — The mission being played.
  • start(): void — Start from attract: the first mission's briefing.
  • tick(dt: number, input: PursuitInput = {}): void — Advance by dt seconds of wall time (fixed steps inside, at most 0.25 s per call). Presses are latched.
  • drain(): PursuitEvent[] — Events since the last drain.
  • get speedDisplay(): number — Speed in the vehicle's display units.
  • scene(base?: { back: number; fov: number }): PursuitScene — What to draw: the race's scene, plus the turbo camera over the view's camera (base: the view's back and fov; road-view-pseudo3d's RoadScene.camera).
  • step(input: PursuitInput = {}): void — One fixed step.
  • snapshot(): PursuitSnapshot — A plain copy of the whole pursuit.
  • restore(s: PursuitSnapshot): void — Restore a snapshot taken from a pursuit with the same config.

Types

ChaseHQOptions

interface ChaseHQOptions

Options for chaseHQPreset.

  • difficulty?: Difficulty — "easy", "normal" or "arcade". Default "normal".
  • seed?: number — Seed for the pursuit's random choices. Default 1988.
  • road?: RoadDocument — Another road file with the same entries (stage-1 .. stage-5).
  • timer?: number — The timer DIP: 55, 60 (factory), 65 or 70 s. Default by difficulty (easy 70, normal 65, arcade 60).
  • turbos?: number — The turbo DIP: 3 (factory) or 5. Default by difficulty (easy 5, normal and arcade 3).
  • startStage?: number — Start at this round (1-based).

ContinueDef

interface ContinueDef

The continue: after the clock runs out, a count during which a credit carries on from the same spot.

  • countdown: number — Seconds of the CONTINUE count.
  • clearDamage: boolean — Damage on the target is cleared on a continue (the factory DIP) or carried over.
  • max: number — Continues per game (-1 unlimited).

ForkWay

type ForkWay = "left" | "right" | "random"

Which branch at a mission's fork is the way the target went.

GangDef

interface GangDef

A gang that rides out to stop you before the target (S.C.I.'s motorcyclists): bump them off the road.

  • kind: string — Sprite kind and collision box.
  • w: number — Collision box width (metres).
  • h: number — Collision box height (metres).
  • d: number — Collision box depth along the road (metres).
  • count: ByDifficulty<number> — Riders per mission (by difficulty).
  • at: number — They appear when the DISTANCE counter is below this (metres).
  • match: number — Their speed relative to yours (m/s, they hang around you), lateral speed (m/s) and how often they lunge (s).
  • lateral: number — Lateral speed (m/s) when a rider moves across the road.
  • lunge: [number, number] — A rider lunges at you every so many seconds, picked between these two (min, max).
  • knock: number — A rider hitting you knocks your speed down by this (m/s).
  • points: number — Points for each rider bumped off (times the stage number).

GunDef

interface GunDef

The partner's gun (S.C.I.: Gibson fires from the roof).

  • rounds: number — Rounds per credit (-1 unlimited: the S.C.I. pistol).
  • every: number — Seconds between rounds while the button is held.
  • speed: number — Round speed relative to your car (m/s).
  • range: number — Range (metres).
  • damage: ByDifficulty<number> — Damage of a round on the target.
  • lock: { range: number; window: number } — The gunsight locks onto the target within this range (metres) and this lateral window (metres): rounds fly at it.
  • points: number — Points for a round on the target (times the stage number).

HeavyDef

interface HeavyDef

A heavy weapon dropped by the helicopter (S.C.I.'s bazooka): limited rounds, far stronger than the gun.

  • rounds: number — Rounds in each drop.
  • damage: ByDifficulty<number> — Damage per round (by difficulty).
  • every: number — Seconds between rounds.
  • dropAt: number — The drop comes once you are within this distance of the target, landing ahead metres in front of you.
  • ahead: number — Metres in front of you where the drop lands.

MissionDef

interface MissionDef

One mission: a criminal, a getaway vehicle, a road, a fork.

  • id: string — Unique name of the mission (such as "courier"); error messages quote it.
  • stage?: number — Stage number shown and used for scoring (1-based). Default its place in missions.
  • kind?: "arrest" | "reach" — arrest (catch and immobilise the target) or reach (S.C.I.'s last stage: reach the end against the clock).
  • criminal: string — The criminal ("RALPH, THE IDAHO SLASHER").
  • charge: string — "OK! You're under arrest on suspicion of ...".
  • briefing: string[] — The dispatcher's radio briefing, line by line.
  • frequency?: number — The radio frequency on the briefing monitor (MHz).
  • car?: TargetCarDef — The getaway vehicle (arrest missions).
  • entry: string — A road-data entry of the road file to start from.
  • lead: number — DISTANCE at the start (metres).
  • approach: ByDifficulty<number> — How far ahead of you the target notionally travels while out of sight (m/s): DISTANCE closes at your speed minus this.
  • fork?: { way: ForkWay; detour: number; helicopter?: boolean } | null — The fork: which way the target went (the arrow), the extra DISTANCE for the other way, and a helicopter showing the way.
  • time?: ByDifficulty<number> — Override the chase clock (S.C.I.'s last stage: 30 s).
  • turbos?: number — Override the turbos for this mission (S.C.I.'s last stage: 1).
  • noContinue?: boolean — No continue in this mission.
  • drive?: Partial<TargetDrive> — Override the drive.
  • gang?: boolean — A gang rides out in this mission.
  • heavy?: boolean — A heavy weapon is dropped in this mission.

PursuitClock

interface PursuitClock

The clocks.

  • chase: ByDifficulty<number> — Seconds to catch up (DIP: 55 / 60 / 65 / 70; 60 factory).
  • extend: ByDifficulty<number> — Seconds added (or set) when the target is sighted (the DIP value again).
  • extendMode: "add" | "reset" — add the extension to the time left (the US manual) or reset the clock to it.
  • sight: number — The target counts as sighted within this distance (metres).
  • warning: number — The dispatcher warns that time is running out at this many seconds.
  • max?: number — Cap on the clock.

PursuitConfig

interface PursuitConfig

A whole pursuit game.

  • name: string — Display name of the game ("Chase H.Q.").
  • difficulty: Difficulty — "easy", "normal" or "arcade": picks the value of every by-difficulty setting.
  • seed: number — Seed for the pursuit's own random choices (the target's weave, the gang's lunges, a random fork); the same seed and inputs replay the same pursuit.
  • race: RoadRaceConfig — The race underneath (road-racer): the road file (one entry per mission), the vehicle (its turbo is a timed boost with charges), traffic, crashes and the race's distance score. The pursuit replaces its clock start and entry per mission, and scores overtakes itself.
  • missions: MissionDef[] — The missions in order; at least one. An arrest mission needs a car, and every mission a lead above 0.
  • clock: PursuitClock — The chase clock.
  • drive: TargetDrive — How the getaway vehicle drives (a mission may override parts).
  • ram: RamDef — Ramming and the arrest.
  • turbos: ByDifficulty<number> — Turbos per mission and after a continue (the vehicle's boost.charges is replaced).
  • gun?: GunDef | null — The partner's gun. null or left out: no gun.
  • heavy?: HeavyDef | null — The heavy weapon a helicopter drops (in missions that set heavy). null or left out: none.
  • gang?: GangDef | null — The gang of riders (in missions that set gang). null or left out: none.
  • scoring: PursuitScoring — Points for overtaking, hits, clears and bonuses.
  • continue: ContinueDef — The continue after the clock runs out.
  • smash?: { slow: number; points: number } | null — Field kinds that smash out of the way when driven through (barriers, garbage cans), slowing you by slow (0..1).
  • scrape?: number — Tunnel walls: scraping one costs this fraction of your speed per second and throws sparks.
  • briefingTime: number — Seconds of the radio briefing (any button skips it). The arrest scene lasts at most ram.arrestTime.
  • clearTime: number — Seconds the clear tally is shown after an arrest, before the next mission starts (or the all-clear bonus is awarded).
  • turboCamera?: { back: number; fov: number; ease: number } | null — Turbo camera: while a turbo burns the camera drops back back metres and widens by fov degrees, easing in and out over ease s.

PursuitEvent

type PursuitEvent = | { type: "briefing"; mission: number; criminal: string } | { type: "mission"; mission: number; stage: number } | { type: "go" } | { type: "sighted"; time: number } | { type: "warning" } | { type: "turbo"; left: number } | { type: "turbo-end" } | { type: "hit"; damage: number; side: boolean; points: number; spin: boolean } | { type: "shot"; damage: number; points: number; heavy: boolean } | { type: "fire"; heavy: boolean } | { type: "fork"; right: boolean; detour: number } | { type: "smash"; kind: string } | { type: "scrape" } | { type: "overtake"; points: number } | { type: "rider-down"; points: number } | { type: "drop" } | { type: "pickup"; rounds: number } | { type: "arrest"; criminal: string; charge: string } | { type: "clear"; time: number; timeBonus: number; clear: number } | { type: "time-up" } | { type: "continue"; countdown: number } | { type: "continued" } | { type: "escaped" } | { type: "game-over" } | { type: "complete"; bonus: number } | { type: "race"; event: RaceEvent }

Events for the presenter and sound.

PursuitInput

interface PursuitInput extends RaceInput

What the player does this step.

  • turbo?: boolean — Turbo button (a press fires one turbo).
  • fire?: boolean — The partner's gun (held).
  • start?: boolean — Start / continue / skip the briefing (a press).

PursuitPhase

type PursuitPhase = "attract" | "briefing" | "chase" | "arrest" | "clear" | "continue" | "over" | "complete"

Phases of a pursuit.

PursuitScene

type PursuitScene = RaceScene & { camera?: { back?: number; fov?: number } }

What scene() adds to the race's scene: the turbo camera over the view's camera.

PursuitScoring

interface PursuitScoring

Scoring of a pursuit (the race scores distance; this is the rest). Points marked "x stage" multiply by the stage.

  • overtake: { start: number; step: number; max: number } — Overtaking traffic: start points, step more each car passed without a crash, up to max (Chase H.Q.: 200, 200, 8,000).
  • hit: number — Each hit on the target (x stage).
  • clear: number — Clear bonus at the arrest (x stage).
  • perSecond: number — Time bonus per whole second left at the arrest.
  • allClear: number — Bonus for clearing every mission.
  • continued: number — Points scored in a stage after a continue are divided by this (Chase H.Q.: 10).

PursuitSnapshot

interface PursuitSnapshot

A saved pursuit: plain data.

  • state: PursuitState — The pursuit state.
  • internal: Internal — Counters and flags the pursuit keeps besides the state: timers, spawned-object ids and what has happened this mission.
  • riders: RiderState[] — The gang riders on the road.
  • race: RaceSnapshot — The race underneath, as road-racer saves it.
  • events: PursuitEvent[] — Events not yet drained.
  • acc: number — Seconds of tick time not yet spent in fixed steps; less than one step (1/60 s).

PursuitState

interface PursuitState

The pursuit state the presenter reads.

  • phase: PursuitPhase — Where the pursuit is: briefing, chase, arrest and so on.
  • phaseTime: number — Seconds spent in the current phase.
  • mission: number — Mission index (0-based) and stage number (1-based).
  • stage: number — Stage number shown and used for scoring (1-based).
  • time: number — Seconds on the clock.
  • score: number — Points scored so far: the race's distance score plus the pursuit's awards, carried across missions.
  • topScore: number — The best score reached so far.
  • distance: number — DISTANCE to the target (metres; never below 1 once sighted) and the mission's starting lead.
  • lead: number — The mission's starting DISTANCE (metres).
  • sighted: boolean — The target has been sighted this mission (dome light on, siren, damage meter).
  • damage: number — Damage on the target 0..1.
  • turbos: number — Turbos left; seconds left of the turbo burning; 0..1 of the turbo camera.
  • turbo: number — Seconds left of the turbo burning; 0 when none is.
  • zoom: number — How far the turbo camera has pulled back, 0..1, easing in and out over turboCamera.ease.
  • rounds: number — Rounds left (-1 unlimited, 0 none) and heavy rounds left.
  • heavy: number — Heavy-weapon rounds left; 0 until one is picked up.
  • locked: boolean — The gunsight is on the target.
  • continues: number — Continues used, and whether this stage was continued (its points are divided).
  • continued: boolean — Whether this stage was continued, so its points are divided by scoring.continued.
  • countdown: number — Seconds left of the CONTINUE count.
  • chain: number — The overtake chain's next award.
  • award: number — The last award (shown under the score) and seconds since.
  • sinceAward: number — Seconds since the last award was made; Infinity before the first.
  • typed: number — The briefing: characters of the text shown so far.
  • arrow: number — The fork arrow being shown (-1 left, 1 right, 0 none) and the way the target went (-1, 1).
  • way: number — The way the target went at this mission's fork: -1 left, 1 right, 0 when there is no fork.
  • detoured: boolean — Took the wrong branch at this mission's fork.
  • bonus: { time: number; timeBonus: number; clear: number } | null — Bonuses of the last clear (for the tally).
  • target: TargetState | null — The getaway vehicle once it has been sighted this mission; null before that, and in a mission with no car.

RamDef

interface RamDef

Ramming and the arrest. Damage is a fraction of the meter (1 = immobilised).

  • damage: ByDifficulty<{ rear: number; side: number }> — Damage of a hit to the rear and to the side (Chase H.Q.'s manual: side hits do more), by difficulty.
  • sideOverlap: number — A hit counts as a side hit when the cars overlap sideways by less than this fraction of their mean width.
  • closingBonus: number — Extra damage per m/s of closing speed above minClosing, as a fraction of the base damage.
  • minClosing: number — Closing speed (m/s) below which a touch does no damage (a nudge).
  • cooldown: number — Seconds after a hit before another counts.
  • rebound: { drop: number; push: number; time: number } — Your car after a hit: speed to the target's speed minus drop (m/s), pushed push m/s away, skidding time s.
  • shove: { x: number; z: number } — The target is shoved sideways (m/s) and forward (m/s) by a hit.
  • outsideSpin: { kappa: number; time: number } | null — Hitting it from the outside of a bend spins you out (Chase H.Q.'s manual): on a bend of at least this curvature (1/m), when you are on the outside of the target. null = never.
  • arrestTime: number — Seconds of the arrest: the target pulls over and stops, then the clear.
  • segments: number — Segments of the damage meter (Chase H.Q.: 20), for the presenter.

RiderState

interface RiderState

A gang rider.

  • id: string — The id of the field object that stands for the rider in the race's scene.
  • z: number — Course position (metres along the course, the race's z).
  • x: number — Lateral position across the road (metres, positive right).
  • speed: number — Forward speed (m/s).
  • hold: number — Its offset from you it holds (metres ahead) and its lateral line.
  • line: number — The lateral position (metres, positive right) it is moving to.
  • lungeIn: number — Seconds until it next lunges at you.
  • down: boolean — Knocked off: tumbling away.

SciOptions

interface SciOptions

Options for sciPreset.

  • difficulty?: Difficulty — "easy", "normal" or "arcade". Default "normal".
  • seed?: number — Seed for the pursuit's random choices. Default 1989.
  • road?: RoadDocument — Another road file with the same entries (stage-1 .. stage-6).
  • timer?: number — The timer DIP: 55, 60 (factory), 65 or 70 s. Default by difficulty.
  • turbos?: number — The turbo DIP: 3 (factory) or 5.
  • startStage?: number — Start at this stage (1-based).

TargetCarDef

interface TargetCarDef

The getaway vehicle: what it is.

  • kind: string — Sprite kind in the field (the presenter's art key).
  • name: string — Display name ("WHITE SPORTS CAR").
  • w: number — Collision box (metres).
  • h: number — Collision box height (metres).
  • d: number — Collision box depth along the road (metres).
  • vw?: number — Drawn width (metres) when the art is drawn larger than the box. Default w.
  • top: number — Top speed (m/s): it sprints up to this when you are on its tail (Chase H.Q.'s attract specs).
  • cruise: number — Speed while it cruises ahead of you (m/s).
  • flying?: number — It flies (S.C.I.'s stage 5 helicopter): it cannot be rammed, only shot, and it ignores traffic. Height (m).
  • data?: Record<string, number | string | boolean> — Plain values handed to the presenter (colour, art hints).

TargetDrive

interface TargetDrive

How a getaway vehicle drives once it is on the road with you.

  • pace: ByDifficulty<number> — Multiplier on its cruise and top speeds by difficulty (arcade 1).
  • sprintRange: number — Within this distance behind it (metres) it puts its foot down to its top speed.
  • accel: number — Acceleration and braking (m/s^2).
  • brake: number — Braking deceleration (m/s^2), a positive number.
  • lateral: number — Lateral speed (m/s) when it swerves, weaves and blocks.
  • weave: [number, number] — It picks a new line across the road every weave seconds (min, max).
  • blockRange: number — Within this distance behind it it moves across in front of you to block. 0 = never.
  • block: number — 0..1 of the way toward your line it moves when it blocks.
  • lookAhead: number — It swerves around traffic this far ahead (metres).
  • shaken: number — Seconds it swerves away and sprints after a hit.
  • match: number | null — Level with you or behind you it goes this much faster than you (m/s), whatever its top speed: Chase H.Q.'s target cannot simply be overtaken (Japanese Wikipedia). null = it keeps its own top speed.

TargetState

interface TargetState

The getaway vehicle's live state.

  • id: string — The id of the field object that stands for the vehicle in the race's scene.
  • z: number — Course z and lateral x (the race's frame).
  • x: number — Lateral position across the road (metres, positive right).
  • speed: number — Its own forward speed (m/s), before the shove of a hit is added.
  • line: number — Lateral line it is moving to.
  • weaveIn: number — Seconds until it picks a new line.
  • shaken: number — Seconds left of a hit's swerve and sprint.
  • vx: number — Lateral and forward shove from the last hit (decaying).
  • vz: number — Forward shove from the last hit (m/s), decaying.
  • stopping: boolean — Pulling over for the arrest.

TargetWorld

interface TargetWorld

What the target sees this step.

  • dt: number — Seconds the step advances.
  • playerZ: number — Your car: course z, lateral x, speed.
  • playerX: number — Your car's lateral position across the road (metres, positive right).
  • playerSpeed: number — Your car's speed (m/s).
  • road(z: number): { left: number; right: number } — Tarmac edges of the road it is on (the strips the race drives).
  • traffic: readonly { z: number; x: number; speed: number }[] — Traffic (course z, lateral x, speed). A flying target passes over it.
  • rng: () => number — Seeded uniform [0, 1).
  • cruise: number — Cruise and top speeds for the difficulty (m/s).
  • top: number — Top speed for the difficulty (m/s).
  • flying?: boolean — It flies (no traffic to avoid).