The pack
runnerCore
function runnerCore(): Pack<{ runner: RunnerCoreApi }>
The runner-core pack: game.runner, the endless run a runner's motor stands on. No options.
Helpers
createRunner
function createRunner<I = unknown>(options: RunnerOptions<I>): Runner<I>
Start a run. Throws, with every fault, on options validateRunner refuses.
runnerDefaults
const runnerDefaults: { seed: number; ahead: number; behind: number; gap: number; noRepeat: number; hits: "stumble"; grace: number; speed: { start: number; max: number; accel: number; by: "time"; }; stumble: { window: number; limit: number; slow: number; slowFor: number; }; score: { perUnit: number; perCoin: number; multiplier: number; }; }
The defaults a run's options fall back on.
validateRunner
function validateRunner<I>(options: RunnerOptions<I>): string[]
Check a run's options. Empty when it can be run; otherwise one line per fault.
Types
Runner
interface Runner<I = unknown>
A run.
readonly state: RunnerState— Where it stands. Read it; change it through the methods.readonly options: Readonly<Required<Omit<RunnerOptions<I>, 'milestone'>>> & { milestone: number | undefined }— The options it was made with, defaults filled in.readonly chunks: ReadonlyArray<RunnerPlaced<I>>— The chunks laid frombehindthe runner toaheadof it, in order.step(dt: number): void— Advance the run bydtseconds: the speed, the distance, the timers and the chunks ahead.hit(kind: 'stumble' | 'crash', cause?: string): RunnerHitResult— A hit:'stumble'for a scrape,'crash'for a head-on. Says what it did.end(cause: string): void— End the run now, whatever is running: a fall off the world that no shield or grace can stop.coin(count?: number): number— Coins picked up. Returns the total.power(name: string): boolean— Start a power-up by name. Returns false whenstack: 'ignore'refused it.remaining(name: string): number— Seconds left of a power-up, 0 when it is not running.has(name: string): boolean— True while a power-up runs.powerUp(name: string): RunnerPowerUp | undefined— The definition of a power-up.endPower(name: string): void— End a power-up now.setMultiplier(n: number): void— The run's own multiplier, before power-ups (Subway Surfers raises it with missions).revive(): void— Carry on after the end ("save me"), with the grace time and a clear chaser.protect(seconds: number): void— Make the runner unhittable for at leastsecondsmore: a landing after a jetpack, a respawn.chunkAt(distance: number): RunnerPlaced<I> | null— The chunk under a distance along the run, or null in a gap or beyond what is laid.drain(): RunnerEvent<I>[]— The events since the last call. At most 512 are kept.snapshot(): RunnerSave— The run as plain JSON.restore(save: RunnerSave): void— Pick up a saved run. Throws on a save that does not fit these options.
RunnerChunk
interface RunnerChunk<I = unknown>
One stretch of authored content: an obstacle pattern, a coin run, a breather. The core strings chunks end to end as the runner goes, chosen by seed, weight and the rules below; what is in a chunk is the motor's business.
id: string— Its name, unique among the chunks.length: number— How long it is along the run, in the motor's units (metres, say).items?: I[]— The motor's content, placed relative to the chunk's start. The core never reads it.weight?: number— How likely it is against the other candidates. Default 1; 0 keeps it foropeningonly.from?: number— The first distance it may start at. Default 0.until?: number— The last distance it may start at. Default no limit.tags?: string[]— Names the rules below can use for a group of chunks ('trains', 'breather').follows?: string[]— May come only straight after a chunk with one of these ids or tags.never?: string[]— Never comes straight after a chunk with one of these ids or tags.
RunnerCoreApi
interface RunnerCoreApi
What the runner-core pack adds to a game: game.runner.
create: typeof createRunner— Start a run.validate: typeof validateRunner— Check a run's options; empty when it can be run.defaults: typeof runnerDefaults— The defaults a run's options fall back on.
RunnerEvent
type RunnerEvent<I = unknown> = | { kind: 'chunk'; placed: RunnerPlaced<I> } | { kind: 'drop'; placed: RunnerPlaced<I> } | { kind: 'coin'; count: number; total: number } | { kind: 'stumble'; count: number; cause: string } | { kind: 'shielded'; power: string; cause: string } | { kind: 'over'; cause: string } | { kind: 'power'; power: string; seconds: number } | { kind: 'power-end'; power: string } | { kind: 'milestone'; distance: number } | { kind: 'revive' }
Something that happened, for sound, effects and the HUD. Read them with drain().
RunnerHitResult
type RunnerHitResult = 'ignored' | 'shielded' | 'stumbled' | 'over'
What a hit did.
RunnerOptions
interface RunnerOptions<I = unknown>
Everything a run is made of.
chunks: RunnerChunk<I>[]— The chunks to string together. At least one must fit at every distance.opening?: string[]— Chunk ids played first, in order, before the seeded choice: a safe runway, a tutorial.seed?: number— The seed for the choice of chunks. Default 1.ahead?: number— How far ahead chunks are laid. Default 150.behind?: number— How far behind a chunk is kept before it is dropped. Default 30.gap?: number— Empty run between chunks. Default 0.noRepeat?: number— A chunk is not chosen again within this many chunks. Default 1: never twice running.speed?: RunnerSpeed— How the speed rises. Default from 10 to 30 units a second, gaining 0.15 a second.hits?: 'stumble' | 'kill'—'stumble': a soft hit stumbles and a crash ends the run (Subway Surfers, Temple Run).'kill': every hit ends it (Jetpack Joyride). Default'stumble'.stumble?: RunnerStumble— What a stumble does.grace?: number— Seconds a runner cannot be hit after a shield takes a hit, or after a revive. Default 1.5.powerUps?: Record<string, RunnerPowerUp>— The power-ups a pickup can start, by name.score?: RunnerScore— How score is counted.milestone?: number— Raise an event every this many units run. Default none.
RunnerPlaced
interface RunnerPlaced<I = unknown>
A chunk laid on the run.
readonly index: number— Its place in the run: 0 for the first chunk laid.readonly chunk: RunnerChunk<I>— The chunk.readonly start: number— Where it starts along the run.readonly end: number— Where it ends.
RunnerPowerUp
interface RunnerPowerUp
A timed power-up a pickup starts.
duration: number— Seconds it lasts. 0 lasts until it is used: a shield that waits for its hit.stack?: 'refresh' | 'extend' | 'ignore'— A second pickup while it runs: start the time again ('refresh', the default), add to it, or do nothing.shield?: boolean— Takes one hit of any kind, then ends, and the runner has its grace time.invulnerable?: boolean— Hits do nothing while it runs.multiplier?: number— Score multiplier while it runs (they multiply together).speed?: number— Speed factor while it runs.data?: unknown— Anything else the motor reads: a magnet's reach, a jetpack's height.
RunnerSave
interface RunnerSave
A saved run, as plain JSON: restore on a run made with the same options picks it up exactly.
state: RunnerState— The state. Infinity is saved as -1.placed: [number, string, number][]— The chunks laid that are still kept, as[index, id, start].next: [number, number]— The next chunk's index and start.recent: string[]— The ids of the last chunks chosen, fornoRepeat.sinceStumble: number— Seconds since the last stumble.rng: RngState— The chunk choice's random stream.milestone: number— The next milestone.
RunnerScore
interface RunnerScore
How score is counted. The score is kept as it goes, so a multiplier counts only while it is on.
perUnit?: number— Points per unit run. Default 1.perCoin?: number— Points per coin. Default 0: coins are their own count.multiplier?: number— The multiplier a run starts with. Default 1.
RunnerSpeed
interface RunnerSpeed
How the speed rises.
start: number— The speed a run starts at, in units per second.max: number— The most it reaches.accel: number— What it gains: units per second, each second (by: 'time') or each unit run (by: 'distance').by?: 'time' | 'distance'— What the speed rises with. Default'time'.
RunnerState
interface RunnerState
Where a run stands.
time: number— Seconds run.distance: number— Units run: the runner's place along the run.speed: number— The speed now, with stumbles and power-ups applied.rampSpeed: number— The speed the ramp has reached, before them.score: number— The score.coins: number— Coins collected.multiplier: number— The multiplier now, with power-ups applied.baseMultiplier: number— The multiplier before power-ups: the run's own, whichsetMultipliersets.alive: boolean— False once the run is over.cause: string | null— Why it ended, or null.stumbles: number— Stumbles in the current window.chaser: number— How close the chaser is, 1 on the runner's heels to 0 gone.grace: number— Seconds left of the grace time.powers: Record<string, number>— The power-ups running, by name, with the seconds left (a waiting shield hasInfinity).revives: number— Revives used.
RunnerStumble
interface RunnerStumble
What a stumble does, in a run that has them.
window?: number— Seconds the chaser stays on the runner's heels after a stumble. Default 4.limit?: number— Stumbles inside one window that end the run: the chaser catches up. Default 2.slow?: number— The speed, as a fraction, just after a stumble. Default 0.6.slowFor?: number— Seconds the speed takes to come back. Default 0.8.


