The pack
track
function track(): Pack<{ track: TrackApi }>
The track pack: a node-and-edge network of curves, with walkers that travel it and branch by weight.
Pure maths, so it needs no backend and runs headless.
Classes
Track
class Track
A network of authored path segments that enemies follow. Build it once, then
spawn() walkers and advance them each frame. Throws when an edge names an unknown node.
const track = new Track({
nodes: { in: [0, 300], fork: [400, 300], top: [800, 120], bot: [800, 480], out: [1200, 300] },
edges: [
{ from: 'in', to: 'fork' },
{ from: 'fork', to: 'top', via: [{ quad: [[600, 120], [800, 120]] }] }, // branch A
{ from: 'fork', to: 'bot', via: [{ quad: [[600, 480], [800, 480]] }] }, // branch B
{ from: 'top', to: 'out' }, { from: 'bot', to: 'out' }, // merge
],
});
const w = track.spawn(140); // 140 units/s
// per frame: w.advance(dt); sprite.x = w.x; sprite.y = w.y; if (w.done) leak();
readonly edges: TrackEdge[]— All baked edges.readonly spawns: string[]— Spawn node names.readonly goals: string[]— Goal node names.constructor(data: TrackData, options: TrackOptions = {})nodeAt(name: string): Vec2 | null— The world position of a named node, ornullif unknown.get length(): number— The total length of the track (sum of all edges).spawn(speed: number, opts: TrackSpawnOptions = {}): TrackWalker— Create a follower. It enters atfrom(or a random spawn node) and picks a branch at each junction withchoose, else by the edge weights drawn fromrng(default the track's stream).speedis world units per second;startDiststaggers a stream by starting it that far along. Throws when the track has no spawn node._pickEdge(node: TrackNode, rng: () => number, walker?: TrackWalker, choose?: TrackChooser): TrackEdge | null_toGoal(name: string): number_node(name: string): TrackNode | undefineddistanceToTrack(x: number, y: number): number— Shortest distance from world point(x, y)to the track centre-line — for validating tower placement ("must be off the path bymargin") and for range-of-track checks. ReturnsInfinityfor an empty track.
TrackWalker
class TrackWalker
A single follower walking a Track. Construct via track.spawn();
call advance(dt) each frame and read x / y / angle / done.
snapshot() and restore() save and rewind it; save its branch stream with it.
x: number— Current world X.y: number— Current world Y.angle: number— Heading in radians (direction of travel).done: boolean—trueonce a goal node is reached (the enemy "leaked").travelled: number— Total distance travelled along the track so far.speed: number— Travel speed in world units per second — mutate live (slows, boosts).constructor(private track: Track, start: TrackNode, speed: number, private rng: () => number, private choose?: TrackChooser)— Enters atstart, choosing its first edge there. Usetrack.spawn()rather than calling this.get edge(): TrackEdge | null— The edge it is on, ornullonce done. Itsfromandtoname the nodes either side.get along(): number— How far along the current edge it is, in world units (0 once done).snapshot(): TrackWalkerState— Its state as plain data, for a save or a rewind. Save the branch stream (rng.snapshot()) with it.restore(state: TrackWalkerState): void— Puts it back where asnapshotof a walker on this track was taken. Throws on an edge index this track does not have.get distanceToGoal(): number— Remaining along-track distance to the nearest goal — SMALLER means FURTHER along (use it to target the "first" enemy: the one closest to leaking).advance(dt: number): boolean— Advance bydtseconds, carrying across junctions and choosing branches. Returnsdone.
Types
TrackApi
type TrackApi = typeof kit
game.track — every function and class this pack carries, as one namespace.
TrackChooser
type TrackChooser = (edges: readonly TrackEdge[], walker: TrackWalker) => TrackEdge | null | undefined
A branch policy: given the edges leaving the junction a walker has reached (more than one), return the one to take, or nothing to fall back to the weighted draw. A switchable junction, a player's choice or "always the shortest route" are each one of these.
TrackData
interface TrackData
The plain-data track notation — JSON-friendly, author it inline.
nodes: Record<string, PathPoint>— Named node positions,name: [x, y].edges: TrackEdgeData[]— The directed edges connecting them.spawns?: string[]— Entry node names (default: every node with no incoming edge).goals?: string[]— Exit node names — reaching one ends the run / "leaks" (default: every node with no outgoing edge).
TrackEdge
interface TrackEdge
A baked edge: its Path, endpoints, length and a sampled polyline. The graph is fixed once built; only weight may be changed.
from: string— Node name this edge leaves.to: string— Node name this edge arrives at.path: Path— The arc-length curve for this edge.length: number— Geometric length in world units.weight: number— Branch weight at thefromnode. Mutable: change it to bias the draw at that junction from now on.points: Vec2[]— A sampled polyline of the edge, for drawing + nearest-point queries.
TrackEdgeData
interface TrackEdgeData
One directed edge of the track in the data notation.
from: string— Name of the node this edge leaves.to: string— Name of the node this edge arrives at.via?: PathSegmentData[]— Optional curve geometry (anyPathSegmentDatachain, continuing fromfrom's point). Omit for a straight line between the two nodes.weight?: number— Relative likelihood of taking this edge at a branching node (default1).
TrackOptions
interface TrackOptions extends PathOptions
Options for a Track: the parse-time transform of PathOptions, plus the default branch stream.
rng?: TrackRng— The stream walkers draw branch choices (and an unnamed spawn) from whenspawnis given none. DefaultcreateRng(0), so a track is deterministic on its own; passgame.rng.fork('track')so the game's seed decides it. Shared by every walker that has no stream of its own.
TrackRng
type TrackRng = Rng | (() => number)
A random source for branch choices: a kernel Rng (such as game.rng.fork('track')), or a function returning 0..1.
TrackSpawnOptions
interface TrackSpawnOptions
Options for Track.spawn.
from?: string— The spawn node to enter at. Default (or when the name is not a spawn): one picked at random fromspawns.rng?: TrackRng— This walker's own branch stream. Default the track's (TrackOptions.rng).startDist?: number— Start this far along the route, in world units, as if it had already travelled it (staggers a stream). Default 0.choose?: TrackChooser— A branch policy consulted at every junction before the weighted draw. Default none: weights only.
TrackWalkerState
interface TrackWalkerState
A walker's saved state, from TrackWalker.snapshot: plain data, JSON-safe.
edge: number— Index of the current edge intrack.edges, or -1 once done.along: number— Distance along the current edge, world units.travelled: number— Total distance travelled, world units.speed: number— Speed, world units per second.done: boolean— Whether it has reached a goal.x: number— World X at the save.y: number— World Y at the save.angle: number— Heading at the save, radians.


