Types
Data types returned by gameState getters and carried by hook payloads.
Station#
A stop derived from a station track group; a group whose platforms are all yard berths forms none. The store's setStations action fills nearbyStations and backfills maxCars.
Used by api.hooks.onStationBuilt, api.hooks.onTrainArrivedAtStation, api.hooks.onStationSelected, api.gameState.getStations.
type Station = {
/** Station id; equals the owning track group's id. */
id: string;
/** Competitive multiplayer company id inherited from the track group. */
companyId?: string;
/** Display name: `'Turnaround'` for a pocket station. */
name: string;
/** [lng, lat] midpoint of the track group's centerline. */
coords: [number, number];
/** Ids of the track group's tracks. */
trackIds: string[];
/** Owning track group id; same value as `id`. */
trackGroupId: string;
/** Ids of the group's station track nodes (platform lines), yard berths excluded. */
stNodeIds: string[];
/** `'constructed'` | `'blueprint'` | `'preview'`. */
buildType: BuildType;
/** Ids of routes whose stNodes include one of this station's. */
routeIds: string[];
/** Declared TrainTypeId; the station builder sets no value for it. */
trainType?: TrainTypeId;
/** StationTypeId; kept from the previous derivation, else `'standard'`. */
stationType?: StationTypeId;
/** Stations reachable on foot with `walkingTime` in seconds … */
nearbyStations?: { stationId: string; walkingTime: number }[];
/** Unix time in ms (`Date.now()`) of the first derivation, kept across rebuilds. */
createdAt?: number;
/** Cars the platform fits, rounded down to whole car sets … */
maxCars?: number;
/** True for a pocket (zero-platform) station where routes reverse and nobody boards. */
noBoarding?: boolean;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | Station id; equals the owning track group's id. |
companyId | string | none | Competitive multiplayer company id inherited from the track group; undefined otherwise. |
name | string | required | Display name: 'Turnaround' for a pocket station; else kept from the previous derivation; else generated from nearby road names, 'Station' when no road name qualifies; a rename through the store's updateStationName overwrites it. |
coords | [number, number] | required | [lng, lat] midpoint of the track group's centerline. |
trackIds | string[] | required | Ids of the track group's tracks. |
trackGroupId | string | required | Owning track group id; same value as id. |
stNodeIds | string[] | required | Ids of the group's station track nodes (platform lines), yard berths excluded. |
buildType | BuildType | required | 'constructed' or 'blueprint' or 'preview'; taken from the first station track node. |
routeIds | string[] | required | Ids of routes whose stNodes include one of this station's; getStations({ includeTempRoutes: false }) drops temp routes from it. |
trainType | TrainTypeId | none | Declared TrainTypeId; the station builder sets no value for it. |
stationType | StationTypeId | none | StationTypeId; kept from the previous derivation, else 'standard'; the station details panel's type picker sets it through the store's updateStationType. |
nearbyStations | { stationId: string; walkingTime: number }[] | none | Stations reachable on foot with walkingTime in seconds (walk-graph or straight-line time divided by the target's walk speed multiplier), within the target's transfer radius (the station type's … See Station.nearbyStations below. |
createdAt | number | none | Unix time in ms (Date.now()) of the first derivation, kept across rebuilds. |
maxCars | number | none | Cars the platform fits, rounded down to whole car sets, at least one car set and at most the train type's maxCars; from platform length and the group's track type; absent when the centerline measures 0 m. |
noBoarding | boolean | none | True for a pocket (zero-platform) station where routes reverse and nobody boards; absent otherwise. |
Station.nearbyStations#
Stations reachable on foot with walkingTime in seconds (walk-graph or straight-line time divided by the target's walk speed multiplier), within the target's transfer radius (the station type's transferRadiusOverride, else PATHFINDING_RULES.MAX_TRANSFER_WALKING_TIME 600 s times its transferRadiusMultiplier) and a 6000 m search box. Sorted ascending.
Route#
A transit line: ordered station track nodes, the paths between them, timings, schedule and bullet styling.
Used by api.hooks.onRouteCreated, api.gameState.getRoutes.
type Route = {
/** UUID assigned at creation. */
id: string;
/** Competitive multiplayer company id; absent otherwise. */
companyId?: string;
/** Null on player routes; the parent route id on a temporary route created for one train … */
tempParentId: string | null;
/** On yard temp routes: `'pull-out'` (berth to route) or `'put-in'` (route to berth). */
yardDeadhead?: 'pull-out' | 'put-in';
/** Put-in temp routes: berth slot the train parks on, 0 = deepest. */
parkSlot?: number;
/** Short label shown in the route bullet. */
bullet: string;
/** Optional long name. */
fullName?: string;
/** Station track nodes in stop order; empty from route creation (a duplicate copies its source's). */
stNodes: StationTrackNode[];
/** Consecutive stNode pairs with the track path between them. */
stCombos: RouteStCombo[];
/** Player-placed via points the path must run through. */
waypoints?: RouteWaypoint[];
/** Route color; CSS color string. */
color: string;
/** Bullet text color; CSS color string. */
textColor: string;
/** True for a two-tone bullet; undefined when off. */
bordered?: boolean;
/** Per stNode visit: `arrivalTime` and `departureTime` in seconds from route start. */
stComboTimings: RouteStComboTiming[];
/** A flat train count; 0 at creation. */
idealTrainCount: number;
/** Trains per demand period (`highDemand`, `mediumDemand`, `lowDemand`, ...). */
trainSchedule?: RouteTrainSchedule;
/** Timetable scheduling: `mode` (`'frequency'` | `'timetable'`) and `periods`. */
timetableSchedule?: RouteTimetableSchedule;
/** Bullet shape: `'circle'` | `'diamond'` | `'square'` | `'rounded-square'` | `'triangle'`. */
shape?: RouteShape;
/** Bullet font style: `'mono'` | `'bolder'` | `'lighter'` | `'italic'`. */
font?: RouteBulletFont;
/** Bullet label size: `'small'` shrinks the glyphs within the badge. */
textSize?: RouteBulletTextSize;
/** Diagram label style: `'bullet'` | `'text'`. */
catStyle?: RouteCatStyle;
/** Alternate-platform terminus entries: `arrivalStComboIndex`, `departureStComboIndex`, `stationId`, `platforms`. */
terminusPlatformAlternates?: TerminusPlatformAlternates[];
/** Player allowlist of platform stNode ids per terminus station id. */
allowedTerminusPlatforms?: { [stationId: string]: string[] } | null;
/** Cars per train; the train type's `defaultCars` (falling back to `minCars`) at creation. */
carsPerTrain?: number;
/** TrainTypeId the route runs; DEFAULT_TRAIN_TYPE_ID when none was chosen. */
trainType?: TrainTypeId;
/** Single yard id from route creation … */
yardId?: string;
/** Supplying yards, each `{ yardId, share }` with `share` the fraction (0..1] of that yard's berths. */
yardAssignments?: RouteYardAssignment[];
/** Unix time in ms (`Date.now()`) at creation or duplication. */
createdAt?: number;
/** Set while a track edit, or a save load with missing tracks, left the route without a path. */
disruption?: RouteDisruption | null;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | UUID assigned at creation. |
companyId | string | none | Competitive multiplayer company id; absent otherwise. |
tempParentId | string or null | required | Null on player routes; the parent route id on a temporary route created for one train (a reroute after a route edit, or a yard pull-out or put-in). |
yardDeadhead | 'pull-out' or 'put-in' | none | On yard temp routes: 'pull-out' (berth to route) or 'put-in' (route to berth); absent otherwise. |
parkSlot | number | none | Put-in temp routes: berth slot the train parks on, 0 = deepest; when absent, berth claims and the parking check use slot 0 and the arriving train stops centered on the berth. |
bullet | string | required | Short label shown in the route bullet. |
fullName | string | none | Optional long name. A route duplicated from another gets <fullName or bullet> 2. |
stNodes | StationTrackNode[] | required | Station track nodes in stop order; empty from route creation (a duplicate copies its source's); set to the confirmed stop list when a new or edited route is confirmed; route regeneration drops stops whose station no longer exists. |
stCombos | RouteStCombo[] | required | Consecutive stNode pairs with the track path between them; empty from route creation (a duplicate copies its source's); rebuilt whenever the route's path is regenerated; emptied while the route is disrupted. |
waypoints | RouteWaypoint[] | none | Player-placed via points the path must run through; dropped when their track or stop pair goes away. |
color | string | required | Route color; CSS color string. |
textColor | string | required | Bullet text color; CSS color string. |
bordered | boolean | none | True for a two-tone bullet; undefined when off. |
stComboTimings | RouteStComboTiming[] | required | Per stNode visit: arrivalTime and departureTime in seconds from route start; estimated by route regeneration from a tick simulation and reused while path and speeds are unchanged. |
idealTrainCount | number | required | A flat train count; 0 at creation. Train spawning ignores it. With no trainSchedule, the train scheduler's yard cap falls back to it, and so do yard fleet sizing and track-usage estimates outside timetable mode. |
trainSchedule | RouteTrainSchedule | none | Trains per demand period (highDemand, mediumDemand, lowDemand, ...); absent (outside timetable mode) means a spawn target of 0 at every hour. |
timetableSchedule | RouteTimetableSchedule | none | Timetable scheduling: mode ('frequency' or 'timetable') and periods; absent until the route is first switched to timetable mode; switching back keeps it with mode: 'frequency'. |
shape | RouteShape | none | Bullet shape: 'circle' or 'diamond' or 'square' or 'rounded-square' or 'triangle'; route creation writes 'circle' when none is chosen, and an absent value draws as a circle. |
font | RouteBulletFont | none | Bullet font style: 'mono' or 'bolder' or 'lighter' or 'italic'; undefined for the default bold face. |
textSize | RouteBulletTextSize | none | Bullet label size: 'small' shrinks the glyphs within the badge; undefined draws at the default size. |
catStyle | RouteCatStyle | none | Diagram label style: 'bullet' or 'text'; absent means automatic by name length. |
terminusPlatformAlternates | TerminusPlatformAlternates[] | none | Alternate-platform terminus entries: arrivalStComboIndex, departureStComboIndex, stationId, platforms; one per terminus the route visits once and reverses at, when another constructed platform there is reachable within RULES.MAX_TERMINUS_ALTERNATE_DETOUR of the canonical paths. |
allowedTerminusPlatforms | { [stationId: string]: string[] } or null | none | Player allowlist of platform stNode ids per terminus station id; a missing key, an empty list, or a list matching none of the terminus's alternates allows every platform. |
carsPerTrain | number | none | Cars per train; the train type's defaultCars (falling back to minCars) at creation. |
trainType | TrainTypeId | none | TrainTypeId the route runs; DEFAULT_TRAIN_TYPE_ID when none was chosen. |
yardId | string | none | Single yard id from route creation; read as one assignment with share 1 only when yardAssignments is absent; the store's yard-assignment actions clear it when they write yardAssignments. |
yardAssignments | RouteYardAssignment[] | none | Supplying yards, each { yardId, share } with share the fraction (0..1] of that yard's berths; absent or empty on yardless routes. |
createdAt | number | none | Unix time in ms (Date.now()) at creation or duplication. |
disruption | RouteDisruption or null | none | Set while a track edit, or a save load with missing tracks, left the route without a path: reason text and sinceSeconds game time; null or absent otherwise. |
Route Schedules#
RouteTrainSchedule#
type RouteTrainSchedule = {
/** Number of trains for high demand periods */
highDemand: number;
/** Number of trains for medium demand periods */
mediumDemand: number;
/** Number of trains for low demand periods */
lowDemand: number;
/** Number of trains for very low demand periods. */
veryLowDemand?: number;
};| Field | Type | Default | Description |
|---|---|---|---|
highDemand | number | required | Number of trains for high demand periods |
mediumDemand | number | required | Number of trains for medium demand periods |
lowDemand | number | required | Number of trains for low demand periods |
veryLowDemand | number | none | Number of trains for very low demand periods. Optional for backwards compatibility with old saves; falls back to lowDemand when absent. |
RouteTimetableSchedule#
type RouteTimetableSchedule = {
/** `'frequency'` (train counts per demand tier from `trainSchedule`) or `'timetable'` (headways per period). */
mode: SchedulingMode;
/** Time periods with their target headways. */
periods: ScheduleTimePeriod[];
};| Field | Type | Default | Description |
|---|---|---|---|
mode | SchedulingMode | required | 'frequency' (train counts per demand tier from trainSchedule) or 'timetable' (headways per period). |
periods | ScheduleTimePeriod[] | required | Time periods with their target headways. |
ScheduleTimePeriod#
type ScheduleTimePeriod = {
/** Period id. */
id: string;
/** Display name. */
name: string;
/** First hour of the period, inclusive. */
startHour: number;
/** Hour the period ends, exclusive. */
endHour: number;
/** Target headway in seconds. */
headwaySeconds: number;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | Period id. |
name | string | required | Display name. |
startHour | number | required | First hour of the period, inclusive. |
endHour | number | required | Hour the period ends, exclusive. |
headwaySeconds | number | required | Target headway in seconds. 0 = no service. |
RouteScheduleSnapshot#
Copy of a route's schedule fields passed to onScheduleChange.
Built by pickRouteSchedule: trainSchedule is a shallow copy and timetableSchedule.periods is an array of copied period objects, so editing the snapshot does not touch the store.
Used by api.hooks.onScheduleChange.
type RouteScheduleSnapshot = {
/** Number of trains the route is set to run. */
idealTrainCount: number;
/** Copy of the demand-tier train counts (`highDemand`, `mediumDemand`, `lowDemand`, `veryLowDemand`). */
trainSchedule?: RouteTrainSchedule;
/** Copy of the timetable `mode` and `periods` (each period copied). */
timetableSchedule?: RouteTimetableSchedule;
};| Field | Type | Default | Description |
|---|---|---|---|
idealTrainCount | number | required | Number of trains the route is set to run; trains. |
trainSchedule | RouteTrainSchedule | none | Copy of the demand-tier train counts (highDemand, mediumDemand, lowDemand, veryLowDemand); undefined when the route has none. |
timetableSchedule | RouteTimetableSchedule | none | Copy of the timetable mode and periods (each period copied); undefined when the route has none. |
Route Parts#
StationTrackNode#
A node where two station tracks connect
type StationTrackNode = {
/** Station track node id; routes list these as stops (`Route.stNodes`). */
id: string;
/** [lng, lat] of the node. */
center: [number, number];
/** The track IDs that connect to this node */
trackIds: [string, string];
/** Build state of the node. */
buildType: BuildType;
/** True when this node joins two yard tracks, or two station tracks the player designated as a yard berth … */
isYard?: boolean;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | Station track node id; routes list these as stops (Route.stNodes). |
center | [number, number] | required | [lng, lat] of the node. |
trackIds | [string, string] | required | The track IDs that connect to this node |
buildType | BuildType | required | Build state of the node. |
isYard | boolean | none | True when this node joins two yard tracks, or two station tracks the player designated as a yard berth (Track.yardBerth). See StationTrackNode.isYard below. |
StationTrackNode.isYard#
True when this node joins two yard tracks, or two station tracks the player designated as a yard berth (Track.yardBerth). Yard nodes exist only as deadhead endpoints (trains pulling out of or returning to a yard). They are never route stops, never count as station platforms, and the route editor renders them only as disabled "yard" markers.
RouteStCombo#
type RouteStCombo = {
/** The ID of the starting station track node */
startStNodeId: string;
/** The ID of the ending station track node */
endStNodeId: string;
/** The path of the route between the two station track nodes */
path: TrackRoutePath[];
/** Summed `length` of the path; m. */
distance: number;
};| Field | Type | Default | Description |
|---|---|---|---|
startStNodeId | string | required | The ID of the starting station track node |
endStNodeId | string | required | The ID of the ending station track node |
path | TrackRoutePath[] | required | The path of the route between the two station track nodes |
distance | number | required | Summed length of the path; m. |
TrackRoutePath#
type TrackRoutePath = {
/** Track id. */
trackId: string;
/** If trains on this route travel in the opposite direction of the track */
reversed: boolean;
/** Track length; m. */
length: number;
/** The IDs of the signals on part or all of this path */
signals: TrackRoutePathSignal[];
};| Field | Type | Default | Description |
|---|---|---|---|
trackId | string | required | Track id. |
reversed | boolean | required | If trains on this route travel in the opposite direction of the track |
length | number | required | Track length; m. |
signals | TrackRoutePathSignal[] | required | The IDs of the signals on part or all of this path |
TrackRoutePathSignal#
type TrackRoutePathSignal = {
/** Signal id. */
signalId: string;
/** Part of the track the signal covers: `{ start, end }` progress in m, or `'all'`. */
areaCovered: SignalTrackCoverage;
};| Field | Type | Default | Description |
|---|---|---|---|
signalId | string | required | Signal id. |
areaCovered | SignalTrackCoverage | required | Part of the track the signal covers: { start, end } progress in m, or 'all'. |
RouteStComboTiming#
To track the time it takes to reach a station from when the route started
type RouteStComboTiming = {
/** Station track node of this stop. */
stNodeId: string;
/** The order the station track node is in the route so if a train visits an stNode more than once they don't … */
stNodeIndex: number;
/** Seconds from route start to arrival at this stop. */
arrivalTime: number;
/** Seconds from route start to departure from this stop. */
departureTime: number;
};| Field | Type | Default | Description |
|---|---|---|---|
stNodeId | string | required | Station track node of this stop. |
stNodeIndex | number | required | The order the station track node is in the route so if a train visits an stNode more than once they don't get mxed up |
arrivalTime | number | required | Seconds from route start to arrival at this stop. |
departureTime | number | required | Seconds from route start to departure from this stop. |
RouteWaypoint#
A player-placed via point on a route leg: pathfinding between fromStNodeId
and toStNodeId (an ordered pair of adjacent stops on the route cycle) must traverse trackId. Waypoints on the same leg are visited in the order they were added. A waypoint whose track is demolished, or whose two stops are no longer adjacent after a stop edit, is dropped on the next route regeneration.
type RouteWaypoint = {
/** Waypoint id. */
id: string;
/** Snapped position on the track, where the waypoint marker renders. */
coords: [number, number];
/** Track the leg must run over. */
trackId: string;
/** Stop the leg starts at. */
fromStNodeId: string;
/** Stop the leg ends at. */
toStNodeId: string;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | Waypoint id. |
coords | [number, number] | required | Snapped position on the track, where the waypoint marker renders. |
trackId | string | required | Track the leg must run over. |
fromStNodeId | string | required | Stop the leg starts at. |
toStNodeId | string | required | Stop the leg ends at. |
TerminusPlatformAlternates#
Paired alternate paths for a multi-platform terminus station. Each platform
choice substitutes BOTH the stCombo arriving at the terminus AND the next stCombo departing from it, keeping the chain stCombos[i].endStNodeId === stCombos[i+1].startStNodeId intact.
For the loop terminus the arrival is the last stCombo and departure is stCombos[0] (wraps around).
type TerminusPlatformAlternates = {
/** Index in route.stCombos of the stCombo arriving at the terminus */
arrivalStComboIndex: number;
/** Index in route.stCombos of the stCombo departing from the terminus */
departureStComboIndex: number;
/** ID of the terminus station */
stationId: string;
/** Each entry is one platform the train can use at this terminus. */
platforms: Array<{…}>;
};| Field | Type | Default | Description |
|---|---|---|---|
arrivalStComboIndex | number | required | Index in route.stCombos of the stCombo arriving at the terminus |
departureStComboIndex | number | required | Index in route.stCombos of the stCombo departing from the terminus |
stationId | string | required | ID of the terminus station |
platforms | Array<{ platformStNodeId: string; arrival: { startStNodeId: string; endStNodeId: string; path: TrackRoutePath[]; distance: number }; departure: { startStNodeId: string; endStNodeId: string; path: TrackRoutePath[]; distance: number }; }> | required | Each entry is one platform the train can use at this terminus. The first entry is always the canonical platform (matches the actual stCombos[i] and stCombos[i+1] content). Trailing entries are the alternates. |
RouteYardAssignment#
One supplying-yard assignment on a route: which yard, and how much of it.
type RouteYardAssignment = {
/** Supplying yard id. */
yardId: string;
/** Fraction (0..1] of the yard's berths this route may claim. */
share: number;
};| Field | Type | Default | Description |
|---|---|---|---|
yardId | string | required | Supplying yard id. |
share | number | required | Fraction (0..1] of the yard's berths this route may claim; 1 = whole yard. |
RouteDisruption#
Set when a route's path could not be rebuilt after a track edit (a track it
ran on was deleted or flipped with no alternative path). A disrupted route keeps its stops, schedule and styling but has no stCombos, spawns no trains, and is automatically re-pathed on every subsequent track change until a valid path exists again (see regenerateRoutes / setTracks).
type RouteDisruption = {
/** Human-readable cause, e.g. */
reason: string;
/** Game time (elapsedSeconds) when the disruption began */
sinceSeconds: number;
};| Field | Type | Default | Description |
|---|---|---|---|
reason | string | required | Human-readable cause, e.g. "No path found between Foo and Bar" |
sinceSeconds | number | required | Game time (elapsedSeconds) when the disruption began |
FareGroup#
A set of routes priced by one fare system and one transfer policy. Reconciliation keeps every route whose tempParentId is null in exactly one group.
Used by api.hooks.onFareGroupsChanged, api.actions.addFareGroup, api.actions.updateFareGroup, api.gameState.getFareGroups.
type FareGroup = {
/** Fare group id. */
id: string;
/** Player-editable display name; empty string shows the localized default. */
name: string;
/** `'flat'` | `'route'` | `'distance'` | `'zone'`. */
fareSystem: FareSystem;
/** Fare when `fareSystem` is `'flat'`: charged once per journey in the group … */
flatFare: number;
/** Per-route price keyed by route id when `fareSystem` is `'route'`. */
routeFares: Record<string, number>;
/** Ordered route ids in the group. */
routeIds: string[];
/** `'all-paid'` | `'count-within-group'` | `'count-all-groups'` | `'free-within-group'` (legacy … */
transferPolicy: FareTransferPolicy;
/** Distance system: flag-fall charge, paid once per trip in the group … */
boardingCharge: number;
/** Distance system: price per km ridden; $/km. */
perKmRate: number;
/** Distance system: `'straight-line'` or `'track'`. */
distanceMode: FareDistanceMode;
/** Distance and zone systems: maximum fare per trip. */
fareCap: number;
/** Zone system: ordered fare zones; index + 1 is the displayed zone number and the ring order. */
zones: FareZone[];
/** Zone system: `'count'` (pay per distinct zone) or `'combination'` (pay the contiguous span … */
zonePricingMode: FareZonePricingMode;
/** Zone system: fare for the first charged zone. */
zoneBaseFare: number;
/** Zone system: amount added per additional charged zone. */
zonePerZoneFare: number;
/** Deprecated early-save flag superseded by `transferPolicy`. */
chargeOnInterGroupTransfer?: boolean;
/** Off-peak discount. */
offPeakFareMultiplier?: number;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | Fare group id. |
name | string | required | Player-editable display name; empty string shows the localized default. |
fareSystem | FareSystem | required | 'flat' or 'route' or 'distance' or 'zone'. |
flatFare | number | required | Fare when fareSystem is 'flat': charged once per journey in the group (waived on a credited 'count-all-groups' entry), or on every boarding under 'all-paid'; $. |
routeFares | Record<string, number> | required | Per-route price keyed by route id when fareSystem is 'route'; $; a route without an entry uses flatFare. |
routeIds | string[] | required | Ordered route ids in the group. |
transferPolicy | FareTransferPolicy | required | 'all-paid' or 'count-within-group' or 'count-all-groups' or 'free-within-group' (legacy, read as 'count-within-group'). |
boardingCharge | number | required | Distance system: flag-fall charge, paid once per trip in the group, waived when the trip enters the group on a 'count-all-groups' transfer credit, and paid on every boarding under 'all-paid'; $. |
perKmRate | number | required | Distance system: price per km ridden; $/km. |
distanceMode | FareDistanceMode | required | Distance system: 'straight-line' or 'track'. |
fareCap | number | required | Distance and zone systems: maximum fare per trip; $; 0 means no cap. |
zones | FareZone[] | required | Zone system: ordered fare zones; index + 1 is the displayed zone number and the ring order. |
zonePricingMode | FareZonePricingMode | required | Zone system: 'count' (pay per distinct zone) or 'combination' (pay the contiguous span, minimum two zones when the group has two or more). |
zoneBaseFare | number | required | Zone system: fare for the first charged zone; a trip touching no zone pays it; $. |
zonePerZoneFare | number | required | Zone system: amount added per additional charged zone; $. |
chargeOnInterGroupTransfer | boolean | none | Deprecated early-save flag superseded by transferPolicy; read only when transferPolicy is absent (true reads as 'count-within-group', false as 'count-all-groups'). |
offPeakFareMultiplier | number | none | Off-peak discount. See FareGroup.offPeakFareMultiplier below. |
FareGroup.offPeakFareMultiplier#
Off-peak discount. Every dollar amount the group charges (flat fare, route fares, boarding charge, per-km rate, fare cap, zone fares) is multiplied by this in hours whose demand level isn't 'high' (see getHourDemandLevel). It applies when journeys are priced (buildJourneyFareIndex), so the fare panel's base-rate editor still shows peak prices. Absent or 1 means no discount, which matches existing groups and saves.
FareZone#
One polygon zone of a zone-based fare group, ordered by zone number. The zone tools and addFareZone/updateFareZone clip a zone against the group's other zones; where zones still overlap, a station takes the first zone in order.
Used by api.actions.addFareZone.
type FareZone = {
/** Zone id. */
id: string;
/** MultiPolygon coordinates ([lng, lat]): polygons … */
polygons: [number, number][][][];
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | Zone id. |
polygons | [number, number][][][] | required | MultiPolygon coordinates ([lng, lat]): polygons, each a list of rings whose first ring is the outer boundary and later rings are holes. |
Track#
One rail segment: geometry, elevation, build state and speed-model metadata.
Used by api.hooks.onTrackBuilt, api.hooks.onBlueprintPlaced, api.gameState.getTracks, api.gameState.calculateBlueprintCost, api.gameState.getBlueprintTrackLength.
type Track = {
/** Track id. A drag preview uses `'preview-centerline'` plus `'preview-track-<n>'` lanes. */
id: string | 'preview-centerline' | 'preview-track-1' | 'preview-track-2';
/** [lng, lat] polyline in travel order. */
coords: [number, number][];
/** Elevation at `coords[0]`; m; negative below grade. */
startElevation: number;
/** Elevation at the last coordinate; m; same scale as `startElevation`. */
endElevation: number;
/** `'constructed'` | `'blueprint'` | `'preview'`. */
buildType: BuildType;
/** Render state: `'constructed'` | `'blueprint'` | `'preview'` | `'preview-center'` | `'preview-connected'`. */
displayType: 'constructed' | 'blueprint' | 'preview' | 'preview-center' | 'preview-connected';
/** `'station'` | `'scissors-crossover'` | `'express-station'` | `'yard'` | null. */
type: 'station' | 'scissors-crossover' | 'express-station' | 'yard' | null;
/** True on both half-tracks of a station platform line the player converted to a yard berth. */
yardBerth?: boolean;
/** True on a `'station'`-type track whose platform has screen doors. */
platformScreenDoors?: boolean;
/** True where trains may reverse. */
reversable: boolean;
/** False on scissors-crossover diagonals, true on tracks from regular construction. */
interactable: boolean;
/** Polyline length of `coords`; m. */
length: number;
/** TrackTypeId: decides which train types may run it, and its train type's stats set the track's speed limits … */
trackType?: TrackTypeId;
/** Share of `length` over water, 0 to 100 … */
waterIntersectionPercentage?: number;
/** Unix time in ms (`Date.now()`) when the generator created the track. */
createdAt?: number;
/** CurveType the generator used; `'straight'` unless a curve was fitted. */
curveType?: CurveType;
/** Analytic centerline curvature: `startCurvature`, `endCurvature` (1/m), `length` (m) … */
curveGeometry?: {
/** Curvature at the start; 1/m, signed. */
startCurvature: number;
/** Curvature at the end; 1/m, signed. */
endCurvature: number;
/** Centerline arc length (m) the curvatures are parameterized over. */
length: number;
/** Optional piecewise-linear curvature profile. */
profile?: { position: number; curvature: number }[];
};
/** True on tracks from track construction, yard generation and `api.build`. */
cantEnabled?: boolean;
/** Meters at each end (`start`, `end`) covered by a lane-spacing transition … */
offsetTransitionZones?: {
/** Transition length at the start of the track. */
start?: number;
/** Transition length at the end of the track. */
end?: number;
};
/** Street-running data: `roadSegmentIds`, `speedLimitMps` (m/s), optional `roadName`. */
streetRunning?: {
/** Ids of the road segments the track runs along. */
roadSegmentIds: string[];
/** Speed limit of the road; m/s. */
speedLimitMps: number;
/** Name of the road, when it has one. */
roadName?: string;
/** Declared on the type; the game writes and reads no value. */
isExclusiveROW?: boolean;
};
};| Field | Type | Default | Description |
|---|---|---|---|
id | string or 'preview-centerline' or 'preview-track-1' or 'preview-track-2' | required | Track id. A drag preview uses 'preview-centerline' plus 'preview-track-<n>' lanes; a scissors-crossover preview uses 'preview-scissors-1' and 'preview-scissors-2'. |
coords | [number, number][] | required | [lng, lat] polyline in travel order. |
startElevation | number | required | Elevation at coords[0]; m; negative below grade. See Track.startElevation below. |
endElevation | number | required | Elevation at the last coordinate; m; same scale as startElevation. |
buildType | BuildType | required | 'constructed' or 'blueprint' or 'preview'. |
displayType | 'constructed' or 'blueprint' or 'preview' or 'preview-center' or 'preview-connected' | required | Render state: 'constructed' or 'blueprint' or 'preview' or 'preview-center' or 'preview-connected'. gameState.getBlueprintTrackLength and gameState.calculateBlueprintCost count only tracks with 'blueprint'. |
type | 'station' or 'scissors-crossover' or 'express-station' or 'yard' or null | required | 'station' or 'scissors-crossover' or 'express-station' or 'yard' or null; in a scissors group only the crossing diagonals carry 'scissors-crossover'. |
yardBerth | boolean | none | True on both half-tracks of a station platform line the player converted to a yard berth; the track keeps its 'station' or 'express-station' type; toggled by the store. |
platformScreenDoors | boolean | none | True on a 'station'-type track whose platform has screen doors: the local-station speed cap (getMaxSpeedLocalStation) is skipped and the track runs at the train type's full maxSpeed instead, same as 'express-station' track. Toggled per station by the store; absent = doors off, cap applies. |
reversable | boolean | required | True where trains may reverse. Track construction sets it on station and yard tracks; scissors-crossover building sets it on its diagonals and on the rails it joins; generated yard layouts set it on their tracks; a save-load heal recomputes it on rails without a type. |
interactable | boolean | required | False on scissors-crossover diagonals, true on tracks from regular construction; snapping skips tracks where it is false. |
length | number | required | Polyline length of coords; m. |
trackType | TrackTypeId | none | TrackTypeId: decides which train types may run it, and its train type's stats set the track's speed limits, TPH limit and grade-crossing TPH limits. |
waterIntersectionPercentage | number | none | Share of length over water, 0 to 100; computed by the store from the ocean-depth index for blueprint tracks written without one; writes that skip undo history, and cities without an ocean-depth index, leave it unset. |
createdAt | number | none | Unix time in ms (Date.now()) when the generator created the track; building a blueprint keeps it. |
curveType | CurveType | none | CurveType the generator used; 'straight' unless a curve was fitted; absent on scissors-crossover diagonals and api.build tracks. |
curveGeometry | object | none | Analytic centerline curvature: startCurvature, endCurvature (1/m), length (m), optional profile of { position 0..1, curvature }; absent on tracks built without one, older saves among them. See Track.curveGeometry below. |
cantEnabled | boolean | none | True on tracks from track construction, yard generation and api.build; absent on older saves, which keep the legacy curve-speed formula, and on scissors-crossover diagonals. |
offsetTransitionZones | object | none | Meters at each end (start, end) covered by a lane-spacing transition, where the speed model multiplies the curve radius by RULES.OFFSET_TRANSITION_RADIUS_MULTIPLIER. See Track.offsetTransitionZones below. |
streetRunning | object | none | Street-running data: roadSegmentIds, speedLimitMps (m/s), optional roadName. Full text below the table. |
streetRunning: Street-running data: roadSegmentIds, speedLimitMps (m/s), optional roadName. Set by track construction when street running is on and both track ends sit at 0 m elevation, from the road matched along the track or, failing that, the road under the snapped cursor (then roadSegmentIds is empty). See Track.streetRunning below.
Track.startElevation#
Elevation at coords[0]; m; negative below grade. The cost model classifies the mean of startElevation and endElevation of a group's first priced track against RULES.CONSTRUCTION_COSTS.ELEVATION_THRESHOLDS: deep bore at or below -24, standard tunnel at or below -10, cut and cover at or below -4, trenched at or below -1, at grade at or below 0, ramp below 5, elevated at or above 5.
Track.curveGeometry#
| Field | Type | Default | Description |
|---|---|---|---|
startCurvature | number | required | Curvature at the start; 1/m, signed. |
endCurvature | number | required | Curvature at the end; 1/m, signed. |
length | number | required | Centerline arc length (m) the curvatures are parameterized over. |
profile | { position: number; curvature: number }[] | none | Optional piecewise-linear curvature profile: signed curvature κ at normalized positions 0..1. Present for compound curves such as spiral-arc-spiral connectors; absent means κ interpolates linearly from start to end. |
Track.offsetTransitionZones#
| Field | Type | Default | Description |
|---|---|---|---|
start | number | none | Transition length at the start of the track; m. |
end | number | none | Transition length at the end of the track; m. |
Track.streetRunning#
| Field | Type | Default | Description |
|---|---|---|---|
roadSegmentIds | string[] | required | Ids of the road segments the track runs along; empty when only the road under the cursor matched. |
speedLimitMps | number | required | Speed limit of the road; m/s. |
roadName | string | none | Name of the road, when it has one. |
isExclusiveROW | boolean | none | Declared on the type; the game writes and reads no value. |
Train#
A running or parked train: position along its route, motion, windows, timings and capacity.
Used by api.hooks.onTrainSpawned, api.hooks.onTrainArrivedAtStation, api.hooks.onTrainSelected, api.gameState.getTrains.
type Train = {
/** UUID assigned when the train is generated, unless the generator was given an id. */
id: string;
/** Competitive multiplayer company id; absent otherwise. */
companyId?: string;
/** Id of the route the train runs, temp routes included. */
routeId: string;
/** `cars` times the train type's `carLength`. */
length: number;
/** Car count: the route's `carsPerTrain`, else the train type's `carsPerCarSet`. */
cars?: number;
/** TrainTypeId of the generated train. */
trainType?: TrainTypeId;
/** Progress through the route: `index` of the current stCombo, `gapFromHeadToEndOfRoute` (m) … */
currentStComboInfo: {
/** The index of the current station track route combo */
index: number;
/** The train should stop when the head of the train is this distance from the end of the current station … */
gapFromHeadToEndOfRoute: number;
/** The game time when the train arrived the first station track node of the current station track route combo */
timeAtStop: number | null;
/** The game time when the train departed the first station track node of the current station track route combo */
timeAtStopEnd: number | null;
/** The ID of the station track node that this train is headed to */
endStNodeId: string | null;
/** True when the train has to change direction to depart the stop it's currently dwelling at … */
reversesAtStop?: boolean;
/** The dynamic dwell computed for the in-progress stop (see DynamicDwellSnapshot). */
dynamicDwell?: DynamicDwellSnapshot;
};
/** `speed` in m/s and `acceleration` in m/s^2. */
motion: {
/** Current speed; m/s. */
speed: number;
/** Current acceleration; m/s^2. */
acceleration: number;
};
/** Per window type: occupied tracks, head and tail progress along the stCombo, coords and signal ids. */
windows: AllTrainWindowCollections;
/** Per stNode visit: actual and expected arrival and departure times in game seconds, null until known. */
timings: TrainStNodeTiming[];
/** `maxCapacity` in passengers: `cars` times the train type's `capacityPerCar`. */
specs: {
/** Passenger capacity: `cars` times the train type's `capacityPerCar`. */
maxCapacity: number;
};
/** `totalSeconds` in service and `lastChargedAt` (game seconds) for operating-cost billing. */
operationalTime: {
/** Total seconds this train has been in service */
totalSeconds: number;
/** Last time the operational cost was charged */
lastChargedAt: number;
};
/** Per-train substitute stCombos keyed by stCombo index, applied over the route for alternate-platform termini. */
stComboOverrides?: { [index: number]: RouteStCombo } | null;
/** Which demand periods the train runs: `highDemand`, `mediumDemand`, `lowDemand`, `veryLowDemand` booleans. */
operatingSchedule?: {
/** Runs in high-demand hours. */
highDemand: boolean;
/** Runs in medium-demand hours. */
mediumDemand: boolean;
/** Runs in low-demand hours. */
lowDemand: boolean;
/** Runs in very-low-demand hours. */
veryLowDemand: boolean;
};
/** Declared on the type; the game writes no value, so it is always absent. */
assignedSlot?: number;
/** `lastMovementTime` (game seconds) and `lastStComboIndex` of the last detected movement. */
stuckDetection?: {
/** Last time this train moved (changed stComboIndex or had speed > 0) */
lastMovementTime: number;
/** Last stComboIndex when movement was detected */
lastStComboIndex: number;
};
/** Set while stabled at a yard: `yardId`, `berthStNodeId`, optional `slot` (0 = deepest). */
parked?: { yardId: string; berthStNodeId: string; slot?: number; } | null;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | UUID assigned when the train is generated, unless the generator was given an id. |
companyId | string | none | Competitive multiplayer company id; absent otherwise. |
routeId | string | required | Id of the route the train runs, temp routes included. |
length | number | required | cars times the train type's carLength; m. |
cars | number | none | Car count: the route's carsPerTrain, else the train type's carsPerCarSet. |
trainType | TrainTypeId | none | TrainTypeId of the generated train. |
currentStComboInfo | object | required | Progress through the route: index of the current stCombo, gapFromHeadToEndOfRoute (m), timeAtStop and timeAtStopEnd (game seconds or null), endStNodeId, optional reversesAtStop and dynamicDwell. See Train.currentStComboInfo below. |
motion | object | required | speed in m/s and acceleration in m/s^2; both 0 at generation. See Train.motion below. |
windows | AllTrainWindowCollections | required | Per window type: occupied tracks, head and tail progress along the stCombo, coords and signal ids. |
timings | TrainStNodeTiming[] | required | Per stNode visit: actual and expected arrival and departure times in game seconds, null until known. |
specs | object | required | maxCapacity in passengers: cars times the train type's capacityPerCar. See Train.specs below. |
operationalTime | object | required | totalSeconds in service and lastChargedAt (game seconds) for operating-cost billing; 0 and the generation time at creation. See Train.operationalTime below. |
stComboOverrides | { [index: number]: RouteStCombo } or null | none | Per-train substitute stCombos keyed by stCombo index, applied over the route for alternate-platform termini; null or absent otherwise. |
operatingSchedule | object | none | Which demand periods the train runs: highDemand, mediumDemand, lowDemand, veryLowDemand booleans; absent when unset. See Train.operatingSchedule below. |
assignedSlot | number | none | Declared on the type; the game writes no value, so it is always absent. |
stuckDetection | object | none | lastMovementTime (game seconds) and lastStComboIndex of the last detected movement; absent when unset. See Train.stuckDetection below. |
parked | { yardId: string; berthStNodeId: string; slot?: number; } or null | none | Set while stabled at a yard: yardId, berthStNodeId, optional slot (0 = deepest); the movement tick returns a parked train unchanged; null or absent while running. |
Train.currentStComboInfo#
| Field | Type | Default | Description |
|---|---|---|---|
index | number | required | The index of the current station track route combo |
gapFromHeadToEndOfRoute | number | required | The train should stop when the head of the train is this distance from the end of the current station track route combo This is dependent on the train's length and the length of the ending station tracks |
timeAtStop | number or null | required | The game time when the train arrived the first station track node of the current station track route combo |
timeAtStopEnd | number or null | required | The game time when the train departed the first station track node of the current station track route combo |
endStNodeId | string or null | required | The ID of the station track node that this train is headed to |
reversesAtStop | boolean | none | True when the train has to change direction to depart the stop it's currently dwelling at (a terminus or mid-route turnback). Full text below the table. |
dynamicDwell | DynamicDwellSnapshot | none | The dynamic dwell computed for the in-progress stop (see DynamicDwellSnapshot). Full text below the table. |
reversesAtStop: True when the train has to change direction to depart the stop it's currently dwelling at (a terminus or mid-route turnback); adds the train type's turnaroundTimeSeconds to the dwell. Computed at arrival in generateTrain; absent on trains from older saves (treated as false for the in-progress dwell only).
dynamicDwell: The dynamic dwell computed for the in-progress stop (see DynamicDwellSnapshot). Stamped by updatePopMovements on the first pop pass after arrival; undefined until then, on older saves' in-progress dwells, and whenever RULES.DWELL.DYNAMIC_DWELL_ENABLED is off; all of these fall back to the flat scheduled dwell. The dwell used is max(scheduled, dynamicDwell.seconds): dynamic dwell only ever extends a stop.
Train.motion#
| Field | Type | Default | Description |
|---|---|---|---|
speed | number | required | Current speed; m/s. |
acceleration | number | required | Current acceleration; m/s^2. |
Train.specs#
| Field | Type | Default | Description |
|---|---|---|---|
maxCapacity | number | required | Passenger capacity: cars times the train type's capacityPerCar. |
Train.operationalTime#
| Field | Type | Default | Description |
|---|---|---|---|
totalSeconds | number | required | Total seconds this train has been in service |
lastChargedAt | number | required | Last time the operational cost was charged |
Train.operatingSchedule#
| Field | Type | Default | Description |
|---|---|---|---|
highDemand | boolean | required | Runs in high-demand hours. |
mediumDemand | boolean | required | Runs in medium-demand hours. |
lowDemand | boolean | required | Runs in low-demand hours. |
veryLowDemand | boolean | required | Runs in very-low-demand hours. |
Train.stuckDetection#
| Field | Type | Default | Description |
|---|---|---|---|
lastMovementTime | number | required | Last time this train moved (changed stComboIndex or had speed > 0) |
lastStComboIndex | number | required | Last stComboIndex when movement was detected |
Train Parts#
TrainStNodeTiming#
The in-game time when the train arrived and departed from a station track node
type TrainStNodeTiming = {
/** Station track node this timing is for. */
stNodeId: string;
/** The order the station track node is in the route so if a train visits an stNode more than once they don't … */
stNodeIndex: number;
/** Game seconds (`elapsedSeconds`) the train arrived. */
arrivalTime: number | null;
/** Game seconds the train departed; null until it has. */
departureTime: number | null;
/** Next scheduled arrival in game seconds, from the route's timings. */
expectedArrivalTime: number | null;
/** Next scheduled departure in game seconds. */
expectedDepartureTime: number | null;
/** Adjusted expected arrival time accounting for delays from previous stations */
adjustedExpectedArrivalTime: number | null;
/** Adjusted expected departure time accounting for delays from previous stations */
adjustedExpectedDepartureTime: number | null;
/** Expected arrival times for future cycles of the route */
futureCycleArrivalTimes?: number[];
/** Expected departure times for future cycles of the route */
futureCycleDepartureTimes?: number[];
};| Field | Type | Default | Description |
|---|---|---|---|
stNodeId | string | required | Station track node this timing is for. |
stNodeIndex | number | required | The order the station track node is in the route so if a train visits an stNode more than once they don't get mixed up |
arrivalTime | number or null | required | Game seconds (elapsedSeconds) the train arrived; null until it has. |
departureTime | number or null | required | Game seconds the train departed; null until it has. |
expectedArrivalTime | number or null | required | Next scheduled arrival in game seconds, from the route's timings; null when there is none. |
expectedDepartureTime | number or null | required | Next scheduled departure in game seconds; null when there is none. |
adjustedExpectedArrivalTime | number or null | required | Adjusted expected arrival time accounting for delays from previous stations |
adjustedExpectedDepartureTime | number or null | required | Adjusted expected departure time accounting for delays from previous stations |
futureCycleArrivalTimes | number[] | none | Expected arrival times for future cycles of the route |
futureCycleDepartureTimes | number[] | none | Expected departure times for future cycles of the route |
DynamicDwellSnapshot#
One stop's computed dynamic dwell (RULES.DWELL, see dwellModel.ts): the
passenger-exchange seconds plus the per-term breakdown the train details panel renders. Stamped onto currentStComboInfo by updatePopMovements on the first pop pass of a dwell; cleared implicitly when generateTrain mints the next stop's currentStComboInfo.
type DynamicDwellSnapshot = {
/** Total passenger-exchange dwell in seconds (function time + flow terms) … */
seconds: number;
/** True when the raw model output exceeded MAX_DWELL_SECONDS (running far outside the calibrated envelope). */
clampedAtMax: boolean;
/** Head-count that alighted here. */
alighting: number;
/** Head-count boarding here (after capacity limiting). */
boarding: number;
/** Standing through-riders (through load beyond the train's seats). */
standees: number;
/** Fixed mechanical door-cycle + dispatch seconds. */
functionTime: number;
/** Alighting-flow seconds. */
alightTime: number;
/** Boarding-flow seconds. */
boardTime: number;
/** Opposing-stream friction seconds (0 on both-side platforms, where the streams split). */
frictionTime: number;
/** Standee-crowding seconds. */
crowdTime: number;
/** 2 when the platform track had slabs on both sides (Spanish solution), else 1. */
platformSides: 1 | 2;
};| Field | Type | Default | Description |
|---|---|---|---|
seconds | number | required | Total passenger-exchange dwell in seconds (function time + flow terms), capped at RULES.DWELL.MAX_DWELL_SECONDS. |
clampedAtMax | boolean | required | True when the raw model output exceeded MAX_DWELL_SECONDS (running far outside the calibrated envelope). |
alighting | number | required | Head-count that alighted here. |
boarding | number | required | Head-count boarding here (after capacity limiting). |
standees | number | required | Standing through-riders (through load beyond the train's seats). |
functionTime | number | required | Fixed mechanical door-cycle + dispatch seconds. |
alightTime | number | required | Alighting-flow seconds. |
boardTime | number | required | Boarding-flow seconds. |
frictionTime | number | required | Opposing-stream friction seconds (0 on both-side platforms, where the streams split). |
crowdTime | number | required | Standee-crowding seconds. |
platformSides | 1 or 2 | required | 2 when the platform track had slabs on both sides (Spanish solution), else 1. |
AllTrainWindowCollections#
One window per TrainWindowType: train is the track span the train occupies; warning and warningExtra are the wider spans train movement also tracks, drawn by the train warning debug layers.
type AllTrainWindowCollections = { [key in TrainWindowType]: TrainWindowCollection; }TrainWindowCollection#
type TrainWindowCollection = {
/** Tracks the window covers, each with head and tail progress along it and the signals it triggered. */
tracks: TrainWindowTrack[];
/** The progress of the train's head along the current station track route combo */
headStComboProgress: number;
/** The progress of the train's tail along the current station track route combo */
tailStComboProgress: number;
/** [lng, lat] line of the window for the map layers. */
coords: [number, number][] | null;
/** Ids of the signals inside the window. */
signalIds: string[];
};| Field | Type | Default | Description |
|---|---|---|---|
tracks | TrainWindowTrack[] | required | Tracks the window covers, each with head and tail progress along it and the signals it triggered. |
headStComboProgress | number | required | The progress of the train's head along the current station track route combo |
tailStComboProgress | number | required | The progress of the train's tail along the current station track route combo |
coords | [number, number][] or null | required | [lng, lat] line of the window for the map layers; null when it is not drawn. |
signalIds | string[] | required | Ids of the signals inside the window. |
TrainWindowTrack#
type TrainWindowTrack = {
/** Track id. */
trackId: string;
/** Progress of the window's head along the track. */
headProgress: number;
/** Progress of the window's tail along the track. */
tailProgress: number;
/** Signals on this track the window has triggered. */
triggeredSignalIds: string[];
};| Field | Type | Default | Description |
|---|---|---|---|
trackId | string | required | Track id. |
headProgress | number | required | Progress of the window's head along the track; m. |
tailProgress | number | required | Progress of the window's tail along the track; m. |
triggeredSignalIds | string[] | required | Signals on this track the window has triggered. |
Bond#
A loan the player took: principal, remaining balance and the figures used to service it every game hour.
Used by api.gameState.getBonds.
type Bond = {
/** UUID assigned at issue. */
id: string;
/** `id` of the BOND_TYPES entry: `'small'`, `'medium'`, `'large'`, or `'custom'` for a slider-issued bond. */
bondTypeId: string;
/** Amount borrowed; $. */
principal: number;
/** Principal still owed; $; reduced by `monthlyPayment` each game hour and by manual payments. */
remainingPrincipal: number;
/** Daily interest fraction on `remainingPrincipal` (SMALL default 0.06 = 6% per game day). */
interestRate: number;
/** Principal repaid every game hour; $; `principal` times the type's `principalPaymentRate`. */
monthlyPayment: number;
/** Game time at issue; game seconds (`elapsedSeconds`). */
issuedAt: number;
/** Game time of the last hourly charge, the issue time before the first. */
lastPaymentAt: number;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | UUID assigned at issue. |
bondTypeId | string | required | id of the BOND_TYPES entry: 'small', 'medium', 'large', or 'custom' for a slider-issued bond. |
principal | number | required | Amount borrowed; $. |
remainingPrincipal | number | required | Principal still owed; $; reduced by monthlyPayment each game hour and by manual payments; the bond is removed at 0. |
interestRate | number | required | Daily interest fraction on remainingPrincipal (SMALL default 0.06 = 6% per game day); charged every game hour at 1/24. |
monthlyPayment | number | required | Principal repaid every game hour; $; principal times the type's principalPaymentRate. |
issuedAt | number | required | Game time at issue; game seconds (elapsedSeconds). |
lastPaymentAt | number | required | Game time of the last hourly charge, the issue time before the first; game seconds. |
StationGroup#
A derived cluster of stations chained within 150 m of each other, with the player's manual edits applied; a lone station forms a one-station group, and every rebuild leaves out pocket (no-boarding) stations. Rebuilt by every setStations call.
Used by api.hooks.onStationGroupSelected, api.gameState.getStationGroups, api.gameState.getStationGroupForStation.
type StationGroup = {
/** Group id; kept from the matching previous group, else a fresh UUID. */
id: string;
/** `customName` when set; else a name two or more members share … */
name: string;
/** Member station ids. */
stationIds: string[];
/** [lng, lat]: `manualCenter` when set; else the mean of member centers, or … */
center: [number, number];
/** Bounding box of member centers: `minLng`, `maxLng`, `minLat`, `maxLat`. */
bounds: {
/** Westmost member center longitude; degrees. */
minLng: number;
/** Eastmost member center longitude; degrees. */
maxLng: number;
/** Southmost member center latitude; degrees. */
minLat: number;
/** Northmost member center latitude; degrees. */
maxLat: number;
};
/** Station ids the player added by hand; carried over from the matched previous group. */
manualAdditions?: string[];
/** Station ids the player removed by hand … */
manualRemovals?: string[];
/** Player-picked marker position [lng, lat]. */
manualCenter?: [number, number];
/** Player-given name; cleared on a one-station group. */
customName?: string;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | Group id; kept from the matching previous group, else a fresh UUID. |
name | string | required | customName when set; else a name two or more members share, else the name of the member with the most routes; a one-station group uses its station's name. |
stationIds | string[] | required | Member station ids. |
center | [number, number] | required | [lng, lat]: manualCenter when set; else the mean of member centers, or, for a two-station group whose platform centerlines cross, their intersection. |
bounds | object | required | Bounding box of member centers: minLng, maxLng, minLat, maxLat; degrees. See StationGroup.bounds below. |
manualAdditions | string[] | none | Station ids the player added by hand; carried over from the matched previous group. |
manualRemovals | string[] | none | Station ids the player removed by hand; a removed station does not auto-merge with that group's members again until the group is reset. |
manualCenter | [number, number] | none | Player-picked marker position [lng, lat]; overrides the computed center. |
customName | string | none | Player-given name; cleared on a one-station group. |
StationGroup.bounds#
| Field | Type | Default | Description |
|---|---|---|---|
minLng | number | required | Westmost member center longitude; degrees. |
maxLng | number | required | Eastmost member center longitude; degrees. |
minLat | number | required | Southmost member center latitude; degrees. |
maxLat | number | required | Northmost member center latitude; degrees. |
DemandData#
The city's demand model: demand points and the pops commuting between them, both as Maps.
Used by api.gameState.getDemandData.
type DemandData = {
/** Map of point id to demand point: `location` [lng, lat], `jobs`, `residents`, `popIds` … */
points: ExtendedDemandPointMap;
/** Map of pop id to pop: `size` people, `residenceId`, `jobId`, driving data … */
popsMap: ExtendedPopMap;
};| Field | Type | Default | Description |
|---|---|---|---|
points | ExtendedDemandPointMap | required | Map of point id to demand point: location [lng, lat], jobs, residents, popIds, plus derived mode shares and walk-graph snap fields (walkNode, walkSnapM). |
popsMap | ExtendedPopMap | required | Map of pop id to pop: size people, residenceId, jobId, driving data, departure times in seconds from midnight and commute summaries. |
Demand Parts#
ExtendedDemandPointMap#
type ExtendedDemandPointMap = Map<string, ExtendedDemandPoint>;A Map keyed by id; values are ExtendedDemandPoint.
ExtendedDemandPoint#
type ExtendedDemandPoint = {
/** Demand point id; pops name it as `residenceId` or `jobId`. */
id: string;
/** [lng, lat] of the point. */
location: [number, number];
/** People who work here. */
jobs: number;
/** People who live here. */
residents: number;
/** Ids of the pops that live or work here. */
popIds: string[];
/** Mode split of the people living here (home-to-work leg). */
residentModeShare?: ModeChoice;
/** Mode split of the people working here (home-to-work leg). */
workerModeShare?: ModeChoice;
/** Nearest walk-graph node id. */
walkNode?: number;
/** Distance from the point to `walkNode`; m. */
walkSnapM?: number;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | Demand point id; pops name it as residenceId or jobId. |
location | [number, number] | required | [lng, lat] of the point. |
jobs | number | required | People who work here. |
residents | number | required | People who live here. |
popIds | string[] | required | Ids of the pops that live or work here. |
residentModeShare | ModeChoice | none | Mode split of the people living here (home-to-work leg). |
workerModeShare | ModeChoice | none | Mode split of the people working here (home-to-work leg). |
walkNode | number | none | Nearest walk-graph node id. Derived at load by walkAccess.snapDemandPoints and never persisted. Absent when the city has no walk graph or the point is off-network (straight-line fallback). |
walkSnapM | number | none | Distance from the point to walkNode; m. |
ExtendedPopMap#
type ExtendedPopMap = Map<string, ExtendedPop>;A Map keyed by id; values are ExtendedPop.
ExtendedPop#
type ExtendedPop = {
/** Pop id. */
id: string;
/** People this pop stands for; a pop commutes as one group. */
size: number;
/** Demand point id of the home end. */
residenceId: string;
/** Demand point id of the work end; `AIR_` and `UNI_` prefixes mark airport and university jobs … */
jobId: string;
/** Driving time between home and work; s. */
drivingSeconds: number;
/** Driving distance between home and work; m. */
drivingDistance: number;
/** Optional: GeoJSON LineString coordinates for driving route [lng, lat] pairs */
drivingPath?: [number, number][];
/** Time in seconds from midnight when the pop leaves for work */
homeDepartureTime: number;
/** Time in seconds from midnight when the pop leaves for home */
workDepartureTime: number;
/** The most recent commute the pop made or is on currently */
lastCommute?: {
/** Candidate transit journeys fed to mode choice (same order as `modeChoice.transitByPath`). */
transitPaths: RaptorPath[];
/** The walk-only option: `distance` m, `time` s. */
walking: { distance: number; time: number; };
/** Mode split of this commute; people. */
modeChoice: ModeChoice;
};
/** Both directions' mode splits, each overwritten only by its own direction. */
commutes?: {
homeToWork?: CommuteSummary;
workToHome?: CommuteSummary;
};
/** Sim-host slim mirror only (see helpers/popMovements/slimPops.ts). */
legacyCommute?: CommuteSummary;
};| Field | Type | Default | Description |
|---|---|---|---|
id | string | required | Pop id. |
size | number | required | People this pop stands for; a pop commutes as one group. |
residenceId | string | required | Demand point id of the home end. |
jobId | string | required | Demand point id of the work end; AIR_ and UNI_ prefixes mark airport and university jobs, which get flatter departure curves (popTiming dampening). |
drivingSeconds | number | required | Driving time between home and work; s. |
drivingDistance | number | required | Driving distance between home and work; m. |
drivingPath | [number, number][] | none | Optional: GeoJSON LineString coordinates for driving route [lng, lat] pairs |
homeDepartureTime | number | required | Time in seconds from midnight when the pop leaves for work |
workDepartureTime | number | required | Time in seconds from midnight when the pop leaves for home |
lastCommute | object | none | The most recent commute the pop made or is on currently See ExtendedPop.lastCommute below. |
commutes | { homeToWork?: CommuteSummary; workToHome?: CommuteSummary; } | none | Both directions' mode splits, each overwritten only by its own direction. lastCommute is a single slot that the evening commute overwrites the morning one in, which made mode-share displays swing between the peaks; these don't. Read them via getDisplayCommute rather than directly. |
legacyCommute | CommuteSummary | none | Sim-host slim mirror only (see helpers/popMovements/slimPops.ts). See ExtendedPop.legacyCommute below. |
ExtendedPop.lastCommute#
| Field | Type | Default | Description |
|---|---|---|---|
transitPaths | RaptorPath[] | required | Candidate transit journeys fed to mode choice (same order as modeChoice.transitByPath); saves drop them for pops not travelling. |
walking | { distance: number; time: number; } | required | The walk-only option: distance m, time s. |
modeChoice | ModeChoice | required | Mode split of this commute; people. |
ExtendedPop.legacyCommute#
Sim-host slim mirror only (see helpers/popMovements/slimPops.ts): the undirected legacy summary getDisplayCommute would have reconstructed from lastCommute.transitPaths, captured at slim time because the slim copy drops those paths. Absent on full pops and never persisted: saves are written from the host's full popsMap.
CommuteSummary#
A mode split plus the inputs that produced it, for one direction of travel.
lastCommute keeps only the split, so anything wanting to re-derive it (the income distribution chart) had to guess at the inputs and read them off transitPaths, which saves drop for idle pops. Keeping the handful of numbers that fed getModeChoice means displays reproduce the split exactly, with no dependence on paths surviving the save.
type CommuteSummary = {
/** Mode split of this leg; people. */
modeChoice: ModeChoice;
/** Perceived transit time fed to getModeChoice. */
transitTime?: number;
/** Fare charged for the chosen journey. */
transitCost?: number;
/** The candidate journeys fed to getModeChoice with how many commuters chose each … */
transitOptions?: Array<{ riders: number; perceivedTime: number; fareCost: number }>;
/** Traffic multiplier for this direction's departure hour. */
drivingTimeMultiplier: number;
};| Field | Type | Default | Description |
|---|---|---|---|
modeChoice | ModeChoice | required | Mode split of this leg; people. |
transitTime | number | none | Perceived transit time fed to getModeChoice. Omitted when the pop had no transit option. |
transitCost | number | none | Fare charged for the chosen journey. Omitted only for pre-summary saves. |
transitOptions | Array<{ riders: number; perceivedTime: number; fareCost: number }> | none | The candidate journeys fed to getModeChoice with how many commuters chose each (same order as modeChoice.transitByPath). Present only when there was more than one candidate; with a single journey, transitTime/transitCost above already reproduce the split. |
drivingTimeMultiplier | number | required | Traffic multiplier for this direction's departure hour. |
ModeChoice#
type ModeChoice = {
/** People who walk. */
walking: number;
/** People who drive. */
driving: number;
/** People who ride transit. */
transit: number;
/** People with no mode decided. */
unknown: number;
/** How `transit` splits across the candidate journeys fed to getModeChoice … */
transitByPath?: number[];
};| Field | Type | Default | Description |
|---|---|---|---|
walking | number | required | People who walk. |
driving | number | required | People who drive. |
transit | number | required | People who ride transit. |
unknown | number | required | People with no mode decided. |
transitByPath | number[] | none | How transit splits across the candidate journeys fed to getModeChoice (same order as lastCommute.transitPaths). See ModeChoice.transitByPath below. |
ModeChoice.transitByPath#
How transit splits across the candidate journeys fed to getModeChoice (same order as lastCommute.transitPaths): lower-income commuters may pick a cheaper, slower journey while higher-income ones pay for a faster one. Present only when the commute had more than one candidate journey. Aggregated ModeChoice objects (demand points, city totals) drop it.
RaptorPath#
type RaptorPath = {
/** Legs in travel order: train rides, walks and driving access legs. */
segments: RaptorPathSegment[];
/** Journey span in seconds: first segment departure to final arrival. */
totalTime: number;
/** Transfers between trains. */
transfers: number;
/** Generalized ("perceived") time in seconds. */
perceivedTime?: number;
/** Walk weight applied to this path's perceivedTime when it differs from the baseline WALK_MULTIPLIER. */
perceivedWalkMultiplier?: number;
/** Unweighted real-time components behind perceivedTime, for UI display */
timeBreakdown?: RaptorTimeBreakdown;
/** Fare in dollars for this journey, computed AFTER pathfinding from the fare groups of the routes it boards … */
fareCost?: number;
};| Field | Type | Default | Description |
|---|---|---|---|
segments | RaptorPathSegment[] | required | Legs in travel order: train rides, walks and driving access legs. |
totalTime | number | required | Journey span in seconds: first segment departure to final arrival. Includes riding, walking, and platform waits at transfers; excludes the origin wait before the journey starts (pops time their departure to the schedule, so that wait happens at home and is free). |
transfers | number | required | Transfers between trains. |
perceivedTime | number | none | Generalized ("perceived") time in seconds. See RaptorPath.perceivedTime below. |
perceivedWalkMultiplier | number | none | Walk weight applied to this path's perceivedTime when it differs from the baseline WALK_MULTIPLIER: AIRPORT_WALK_MULTIPLIER (Table 8 S8) for airport pops so UI breakdowns sum to the total. |
timeBreakdown | RaptorTimeBreakdown | none | Unweighted real-time components behind perceivedTime, for UI display |
fareCost | number | none | Fare in dollars for this journey, computed AFTER pathfinding from the fare groups of the routes it boards (routing is fare-blind). See RaptorPath.fareCost below. |
RaptorPath.perceivedTime#
Generalized ("perceived") time in seconds: each real-time component weighted by how commuters experience it (PATHFINDING_RULES.PERCEIVED_TIME). Journeys are ranked by this, and it's what mode choice prices against driving/walking. Optional because paths in old save files predate it; fall back to totalTime.
RaptorPath.fareCost#
Fare in dollars for this journey, computed AFTER pathfinding from the fare groups of the routes it boards (routing is fare-blind). Set by the commute worker; optional because journey-planner paths and paths in old saves predate it. The UI falls back to computing it live, the sim to the legacy flat transitCost.
RaptorPathSegment#
type RaptorPathSegment = {
/** Route ridden on this leg; `'walking'` for a walk leg. */
routeId: string | 'walking';
/** Station id where the leg starts, or `'origin'` for the first walk. */
fromStopId: RaptorStationId;
/** Station id where the leg ends, or `'destination'` for the last walk. */
toStopId: RaptorStationId;
/** [lng, lat] where the leg starts. */
fromStopCoords: [number, number];
/** [lng, lat] where the leg ends. */
toStopCoords: [number, number];
/** In game-time seconds */
departureTime: number;
/** In game-time seconds */
arrivalTime: number;
/** Whether this segment is walking (not a train) */
isWalking?: boolean;
/** Whether this segment is driving (access leg) */
isDriving?: boolean;
/** Walk leg priced as a straight line: the place had no walk-graph node (off-network, or an airport terminal … */
directWalk?: boolean;
};| Field | Type | Default | Description |
|---|---|---|---|
routeId | string or 'walking' | required | Route ridden on this leg; 'walking' for a walk leg. |
fromStopId | RaptorStationId | required | Station id where the leg starts, or 'origin' for the first walk. |
toStopId | RaptorStationId | required | Station id where the leg ends, or 'destination' for the last walk. |
fromStopCoords | [number, number] | required | [lng, lat] where the leg starts. |
toStopCoords | [number, number] | required | [lng, lat] where the leg ends. |
departureTime | number | required | In game-time seconds |
arrivalTime | number | required | In game-time seconds |
isWalking | boolean | none | Whether this segment is walking (not a train) |
isDriving | boolean | none | Whether this segment is driving (access leg) |
directWalk | boolean | none | Walk leg priced as a straight line: the place had no walk-graph node (off-network, or an airport terminal, which opts out because its road network doesn't model how pedestrians actually cross it). Renderers draw these … See RaptorPathSegment.directWalk below. |
RaptorPathSegment.directWalk#
Walk leg priced as a straight line: the place had no walk-graph node (off-network, or an airport terminal, which opts out because its road network doesn't model how pedestrians actually cross it). Renderers draw these straight instead of resolving a road polyline, keeping the drawn path consistent with the priced time on every thread.
RaptorTimeBreakdown#
All in real (clock) seconds; weights are applied when computing perceivedTime
type RaptorTimeBreakdown = {
/** Time on board trains */
inVehicle: number;
/** Access/egress/transfer walking */
walk: number;
/** Park-and-ride driving access */
drive: number;
/** Platform waits between segments (includes the ARRIVAL_GAP buffer) */
wait: number;
/** How much later the journey departs than the pop requested */
departureShift: number;
};| Field | Type | Default | Description |
|---|---|---|---|
inVehicle | number | required | Time on board trains |
walk | number | required | Access/egress/transfer walking |
drive | number | required | Park-and-ride driving access |
wait | number | required | Platform waits between segments (includes the ARRIVAL_GAP buffer) |
departureShift | number | required | How much later the journey departs than the pop requested |
Ridership Stats#
Returned by api.gameState.getRidershipStats(). The code declares this shape inline; RidershipStats is its name on this page.
type RidershipStats = {
/** `totalRiders` divided by 0.25; people per game hour. */
totalRidersPerHour: number;
/** People (summed commute `size`) who finished a transit journey in the last 900 game seconds. */
totalRiders: number;
/** Window length; game hours; always 0.25. */
timeWindowHours: number;
};| Field | Type | Description |
|---|---|---|
totalRidersPerHour | number | totalRiders divided by 0.25; people per game hour. |
totalRiders | number | People (summed commute size) who finished a transit journey in the last 900 game seconds. |
timeWindowHours | number | Window length; game hours; always 0.25. |
Line Metrics#
Returned by api.gameState.getLineMetrics() as an array. The code declares this shape inline; LineMetric is its name on this page.
type LineMetric = {
/** Route id. */
routeId: string;
/** Route bullet text. */
routeBullet: string;
/** Route color; CSS color string. */
routeColor: string;
/** Trains whose `routeId` is the route. */
trainCount: number;
/** 3600 / (last `stComboTimings` departureTime / `trainCount`). */
trainsPerHour: number | null;
/** People per game hour who finished a commute on the route in the last 900 game seconds. */
ridersPerHour: number;
/** `ridersPerHour` times the global ticket price times RULES.FARE_MULTIPLIER (365). */
revenuePerHour: number;
};| Field | Type | Description |
|---|---|---|
routeId | string | Route id. |
routeBullet | string | Route bullet text. |
routeColor | string | Route color; CSS color string. |
trainCount | number | Trains whose routeId is the route. |
trainsPerHour | number or null | 3600 / (last stComboTimings departureTime / trainCount); null with no trains, no timings or a zero final departureTime. |
ridersPerHour | number | People per game hour who finished a commute on the route in the last 900 game seconds. |
revenuePerHour | number | ridersPerHour times the global ticket price times RULES.FARE_MULTIPLIER (365); fare groups are ignored; $. |
Route Financials#
Returned by api.gameState.getRouteFinancials() as an array. The code declares this shape inline; RouteFinancials is its name on this page.
type RouteFinancials = {
/** Route id. */
routeId: string;
/** Route bullet text. */
routeBullet: string;
/** Route color; CSS color string. */
routeColor: string;
/** Completed game hours, oldest first, at most 336 (14 game days). */
hourly: Array<{ timestamp: number; revenue: number; expenses: number }>;
/** Amounts of the hour in progress; they move to `hourly` when the hour completes. */
currentHour: { revenue: number; expenses: number };
/** Sums of the last 24 entries of `hourly` plus `currentHour` (when nonzero). */
last24Hours: { revenue: number; expenses: number; profit: number };
};| Field | Type | Description |
|---|---|---|
routeId | string | Route id. |
routeBullet | string | Route bullet text. |
routeColor | string | Route color; CSS color string. |
hourly | Array<{ timestamp: number; revenue: number; expenses: number }> | Completed game hours, oldest first, at most 336 (14 game days); timestamp is the hour's start in game seconds; hours with no revenue and no expenses are skipped; $. |
currentHour | { revenue: number; expenses: number } | Amounts of the hour in progress; they move to hourly when the hour completes; $. |
last24Hours | { revenue: number; expenses: number; profit: number } | Sums of the last 24 entries of hourly plus currentHour (when nonzero); profit is revenue minus expenses; $. |
Mode Choice Stats#
Returned by api.gameState.getModeChoiceStats(). The code declares this shape inline; ModeChoiceStats is its name on this page.
type ModeChoiceStats = {
/** People whose displayed commute walks; summed over pops. */
walking: number;
/** People whose displayed commute drives; summed over pops. */
driving: number;
/** People whose displayed commute rides transit. */
transit: number;
/** People with no displayed commute (whole pop `size`) plus the unknown share of simulated pops. */
unknown: number;
};| Field | Type | Description |
|---|---|---|
walking | number | People whose displayed commute walks; summed over pops. |
driving | number | People whose displayed commute drives; summed over pops. |
transit | number | People whose displayed commute rides transit; summed over pops. |
unknown | number | People with no displayed commute (whole pop size) plus the unknown share of simulated pops. |
Station Ridership#
Returned by api.gameState.getStationRidership(). The code declares this shape inline; StationRidership is its name on this page.
type StationRidership = {
/** People (summed `size`) in snapshot commutes touching the station. */
total: number;
/** People in those commutes for which the station appears in more than one leg … */
transfers: number;
/** Per route: `popCount` people (each route once per commute … */
byRoute: Array<{ routeId: string; popCount: number; percent: number }>;
};| Field | Type | Description |
|---|---|---|
total | number | People (summed size) in snapshot commutes touching the station. |
transfers | number | People in those commutes for which the station appears in more than one leg, boards a leg after the first or alights a leg before the last; 0 when stationId is null. |
byRoute | Array<{ routeId: string; popCount: number; percent: number }> | Per route: popCount people (each route once per commute, legs touching the station only when stationId is set), percent of total; sorted by popCount descending. |
Route Ridership#
Returned by api.gameState.getRouteRidership(). The code declares this shape inline; RouteRidership is its name on this page.
type RouteRidership = {
/** People (summed `size`) in snapshot commutes with a leg on the route. */
total: number;
/** Per station: `popCount` people (boarded or alighted there on the route, once per commute) … */
byStation: Array<{ stationId: string; popCount: number; percent: number }>;
};| Field | Type | Description |
|---|---|---|
total | number | People (summed size) in snapshot commutes with a leg on the route. |
byStation | Array<{ stationId: string; popCount: number; percent: number }> | Per station: popCount people (boarded or alighted there on the route, once per commute), percent of total (0 when total is 0); sorted by popCount descending. |
Blueprint Cost#
Returned by api.gameState.calculateBlueprintCost(). The code declares this shape inline; BlueprintCost is its name on this page.
type BlueprintCost = {
/** Full construction cost; $; includes cost lines the breakdown omits. */
totalCost: number;
/** Partial cost lines; $: `trackCost` (track plus yard trackage), `stationCost`, `buildingDemolitionCost`. */
breakdown: { trackCost: number; stationCost: number; buildingDemolitionCost: number; };
};| Field | Type | Description |
|---|---|---|
totalCost | number | Full construction cost; $; includes cost lines the breakdown omits. |
breakdown | { trackCost: number; stationCost: number; buildingDemolitionCost: number; } | Partial cost lines; $: trackCost (track plus yard trackage), stationCost, buildingDemolitionCost. |
Common Enums#
String values used across the types above. Id types are plain strings that registries extend; the values listed are the built-in ones.
/** Build state of a track, station or route part. */
type BuildType = 'constructed' | 'blueprint' | 'preview';
/** Track geometry. */
type CurveType = 'hobby' | 'straight' | 'quadratic-start' | 'quadratic-end' | 'cubic' | 'euler-quadratic-start' | 'euler-quadratic-end' | 'euler-cubic';
/** A key of the train type registry (`TRAIN_TYPES`), so any string a mod registers with `trains.registerTrainType`. */
type TrainTypeId = string; // built in: 'heavy-metro', 'light-metro', 'light-rail', 'commuter-rail', 'commuter-rail-diesel'
/** A track type named in some train type's `compatibleTrackTypes`. */
type TrackTypeId = string;
/** A key of the station type registry (`STATION_TYPES`), so any string a mod registers with `stations.registerStationType`. */
type StationTypeId = string; // built in: 'standard'
/** Route bullet shape; absent draws a circle. */
type RouteShape = 'circle' | 'diamond' | 'square' | 'rounded-square' | 'triangle';
/** Bullet text styling. */
type RouteBulletFont = 'default' | 'mono' | 'bolder' | 'lighter' | 'italic';
/** How the schematic diagram labels a route along its ribbon. */
type RouteCatStyle = 'bullet' | 'text';
/** How fares are priced within a fare group. */
type FareSystem = 'flat' | 'route' | 'distance' | 'zone';
/** How transfers are priced for a fare group (the policy of the group being boarded). */
type FareTransferPolicy = 'all-paid' | 'count-within-group' | 'count-all-groups' | 'free-within-group';
/** How a distance-based fare group measures the distance a journey leg is charged for. */
type FareDistanceMode = 'straight-line' | 'track';
/** How a zone-based fare group counts zones for pricing. */
type FareZonePricingMode = 'count' | 'combination';
/** Keys of `AllTrainWindowCollections`: the occupied-track windows each train holds. */
type TrainWindowType = 'warningExtra' | 'warning' | 'train';
/** Mode of a route's timetable schedule: `frequency` runs the demand-tier counts in `trainSchedule`, `timetable` the headways per period. */
type SchedulingMode = 'frequency' | 'timetable';
/** Part of a track a signal covers: progress `start` to `end` in m, or the whole track. */
type SignalTrackCoverage = { start: number; end: number; } | 'all';
/** Station id of a journey leg end, or `origin` and `destination` for the walks at either end. */
type RaptorStationId = string | 'origin' | 'destination';
/** How challenging a city is to build a successful network in, shown as a badge on the city-select screen. */
type CityDifficulty = 'very-easy' | 'easy' | 'medium' | 'hard' | 'very-hard';
/** Country code of a city; the city picker filters and shows flags by it. */
type Country = 'US' | 'UK' | 'CA' | 'IE' | 'FR' | 'JP' | 'NZ' | 'CH' | 'AU';BuildType#
Values: 'constructed', 'blueprint', 'preview'. Build state of a track, station or route part.
CurveType#
Values: 'hobby', 'straight', 'quadratic-start', 'quadratic-end', 'cubic', 'euler-quadratic-start', 'euler-quadratic-end', 'euler-cubic'. Track geometry. New track is hobby or straight; the other values appear only in saves from older versions.
TrainTypeId#
string; built in: 'heavy-metro', 'light-metro', 'light-rail', 'commuter-rail', 'commuter-rail-diesel'. A key of the train type registry (TRAIN_TYPES), so any string a mod registers with trains.registerTrainType.
TrackTypeId#
Type: string. A track type named in some train type's compatibleTrackTypes. Each built-in train type runs on the track type of the same id; commuter-rail-diesel also runs on commuter-rail track.
StationTypeId#
string; built in: 'standard'. A key of the station type registry (STATION_TYPES), so any string a mod registers with stations.registerStationType.
RouteShape#
Values: 'circle', 'diamond', 'square', 'rounded-square', 'triangle'. Route bullet shape; absent draws a circle.
RouteBulletFont#
Values: 'default', 'mono', 'bolder', 'lighter', 'italic'. Bullet text styling.
RouteCatStyle#
Values: 'bullet', 'text'. How the schematic diagram labels a route along its ribbon.
FareSystem#
Values: 'flat', 'route', 'distance', 'zone'. How fares are priced within a fare group.
FareTransferPolicy#
Values: 'all-paid', 'count-within-group', 'count-all-groups', 'free-within-group'. How transfers are priced for a fare group (the policy of the group being boarded).
FareDistanceMode#
Values: 'straight-line', 'track'. How a distance-based fare group measures the distance a journey leg is charged for.
FareZonePricingMode#
Values: 'count', 'combination'. How a zone-based fare group counts zones for pricing.
TrainWindowType#
Values: 'warningExtra', 'warning', 'train'. Keys of AllTrainWindowCollections: the occupied-track windows each train holds.
SchedulingMode#
Values: 'frequency', 'timetable'. Mode of a route's timetable schedule: frequency runs the demand-tier counts in trainSchedule, timetable the headways per period.
SignalTrackCoverage#
Type: { start: number; end: number; } or 'all'. Part of a track a signal covers: progress start to end in m, or the whole track.
RaptorStationId#
Type: string or 'origin' or 'destination'. Station id of a journey leg end, or origin and destination for the walks at either end.
CityDifficulty#
Values: 'very-easy', 'easy', 'medium', 'hard', 'very-hard'. How challenging a city is to build a successful network in, shown as a badge on the city-select screen.
Country#
Values: 'US', 'UK', 'CA', 'IE', 'FR', 'JP', 'NZ', 'CH', 'AU'. Country code of a city; the city picker filters and shows flags by it.