Type Reference
Complete return types and object shapes for the Modding API
{/ shoutout muffintime for keeping us honest about documentation ā you were right, we should've had this from the start /}
*"return types and values or object properties are just not documented for a good chunk of the functions"* ā a wise and extremely patient modder who will remain unnamed (thanks muffintime)
Every getter in the Modding API returns plain JavaScript objects. This page documents every property so you know exactly what you're working with ā no more guessing.
Station#
Returned by gameState.getStations(), hooks.onStationBuilt(), and hooks.onStationDeleted().
type Station = {
/** Unique station identifier */
id: string;
/** Display name (e.g. "Times Square") */
name: string;
/** [longitude, latitude] */
coords: [number, number];
/** IDs of tracks at this station */
trackIds: string[];
/** Track group this station belongs to */
trackGroupId: string;
/** Station-track node IDs (for routing) */
stNodeIds: string[];
/** 'constructed' | 'blueprint' | 'preview' */
buildType: BuildType;
/** Route IDs serving this station */
routeIds: string[];
/** Train type constraint (default: 'heavy-metro') */
trainType?: string;
/** Station type (default: 'standard', or custom registered type) */
stationType?: string;
/** Nearby stations with walking times */
nearbyStations?: { stationId: string; walkingTime: number }[];
/** Unix timestamp (ms) when station was created */
createdAt?: number;
};Example: Accessing Station Data#
const stations = api.gameState.getStations();
stations.forEach(station => {
// All these properties are guaranteed to exist:
console.log(station.id); // "station-abc123"
console.log(station.name); // "Grand Central"
console.log(station.coords); // [-73.977, 40.752]
console.log(station.buildType); // "constructed"
console.log(station.routeIds); // ["route-1", "route-2"]
// Optional properties ā check before using:
if (station.stationType) {
console.log(station.stationType); // "express" or "standard"
}
if (station.nearbyStations) {
station.nearbyStations.forEach(ns => {
console.log(`${ns.stationId} is ${ns.walkingTime}s walk away`);
});
}
});Route#
Returned by gameState.getRoutes() and hooks.onRouteCreated().
type Route = {
/** Unique route identifier */
id: string;
/** Parent route ID for temporary redirect routes, or null */
tempParentId: string | null;
/** Route bullet/name (e.g. "A", "7", "L") */
bullet: string;
/** Station-track nodes on this route */
stNodes: StationTrackNode[];
/** Route color (hex, e.g. "#0039A6") */
color: string;
/** Text color for the route bullet (hex) */
textColor: string;
/** Ideal number of trains for this route */
idealTrainCount: number;
/** Route shape icon: 'circle' | 'diamond' | 'square' | 'triangle' */
shape?: string;
/** Cars per train on this route */
carsPerTrain?: number;
/** Train type for this route */
trainType?: string;
/** Unix timestamp (ms) */
createdAt?: number;
};Track#
Returned by gameState.getTracks(), hooks.onTrackBuilt(), and hooks.onBlueprintPlaced().
type Track = {
/** Unique track identifier */
id: string;
/** Array of [longitude, latitude] coordinate pairs */
coords: [number, number][];
/** Elevation at start of track (negative = underground, meters) */
startElevation: number;
/** Elevation at end of track (negative = underground, meters) */
endElevation: number;
/** 'constructed' | 'blueprint' | 'preview' */
buildType: string;
/** Display type for rendering */
displayType: 'constructed' | 'blueprint' | 'preview' | 'preview-center' | 'preview-connected';
/** Track feature type */
type: 'station' | 'scissors-crossover' | 'express-station' | null;
/** Whether trains can reverse on this track */
reversable: boolean;
/** Whether this track can be manually deleted/snapped */
interactable: boolean;
/** Track length in meters */
length: number;
/** Track type (determines compatible trains) */
trackType?: string;
/** Percentage of track over water (0-100) */
waterIntersectionPercentage?: number;
/** Unix timestamp (ms) */
createdAt?: number;
/** Curve type for rendering */
curveType?: 'straight' | 'quadratic-start' | 'quadratic-end' | 'cubic';
};Example: Elevation Analysis#
const tracks = api.gameState.getTracks();
const constructed = tracks.filter(t => t.buildType === 'constructed');
// Find deepest tunnel
const deepest = constructed.reduce((min, track) => {
const depth = Math.min(track.startElevation, track.endElevation);
return depth < min ? depth : min;
}, 0);
console.log(`Deepest tunnel: ${deepest}m`);
// Total track length
const totalKm = constructed.reduce((sum, t) => sum + t.length, 0) / 1000;
console.log(`Total track: ${totalKm.toFixed(1)}km`);
// Underwater tracks
const underwater = constructed.filter(t =>
(t.waterIntersectionPercentage ?? 0) > 0
);
console.log(`${underwater.length} tracks cross water`);Train#
Returned by gameState.getTrains() and hooks.onTrainSpawned().
type Train = {
/** Unique train identifier */
id: string;
/** Route this train runs on */
routeId: string;
/** Train length in meters */
length: number;
/** Number of cars */
cars?: number;
/** Train type ID */
trainType?: string;
/** Current position on route */
currentStComboInfo: {
/** Current station-track combo index */
index: number;
/** Gap from head of train to end of route segment */
gapFromHeadToEndOfRoute: number;
/** Game time (ms) train stopped at station, or null */
timeAtStop: number | null;
/** Game time (ms) train will depart station, or null */
timeAtStopEnd: number | null;
};
/** Current motion state */
motion: {
/** Current speed in meters per second */
speed: number;
/** Current acceleration in m/s² */
acceleration: number;
};
/** Train capacity */
specs: {
/** Maximum passenger capacity */
maxCapacity: number;
};
/** Operational time tracking */
operationalTime: {
/** Total seconds in service */
totalSeconds: number;
/** Last time operational costs were charged */
lastChargedAt: number;
};
/** When this train runs (by demand level) */
operatingSchedule?: {
highDemand: boolean;
mediumDemand: boolean;
lowDemand: boolean;
};
};Example: Train Speed Monitor#
const trains = api.gameState.getTrains();
trains.forEach(train => {
const speedKmh = (train.motion.speed * 3.6).toFixed(1);
const isAtStation = train.currentStComboInfo.timeAtStop !== null;
console.log(
`Train ${train.id}: ${speedKmh} km/h` +
(isAtStation ? ' (at station)' : '')
);
});
// Average speed across fleet
const avgSpeed = trains.reduce((s, t) => s + t.motion.speed, 0) / trains.length;
console.log(`Fleet avg: ${(avgSpeed * 3.6).toFixed(1)} km/h`);Bond#
Returned by gameState.getBonds() and actions.getBonds().
type Bond = {
/** Unique bond identifier */
id: string;
/** Reference to bond type (e.g. 'SMALL', 'MEDIUM', 'LARGE') */
bondTypeId: string;
/** Original loan amount in dollars */
principal: number;
/** Amount still owed */
remainingPrincipal: number;
/** Interest rate (e.g. 0.06 = 6%) */
interestRate: number;
/** Hourly principal payment amount */
monthlyPayment: number;
/** Game timestamp when bond was issued */
issuedAt: number;
/** Game timestamp of last interest charge */
lastPaymentAt: number;
};StationGroup#
Returned by gameState.getStationGroups().
type StationGroup = {
/** Unique group identifier */
id: string;
/** Display name (usually the primary station name) */
name: string;
/** Station IDs in this group */
stationIds: string[];
/** Center point [longitude, latitude] */
center: [number, number];
/** Geographic bounds of the group */
bounds: {
minLng: number;
maxLng: number;
minLat: number;
maxLat: number;
};
};DemandData#
Returned by gameState.getDemandData().
type DemandData = {
/** Map of demand point ID ā demand point data */
points: Map<string, DemandPoint>;
/** Map of pop group ID ā pop data */
popsMap: Map<string, Pop>;
};
type DemandPoint = {
/** [longitude, latitude] */
coords: [number, number];
/** Number of jobs at this point */
jobs: number;
/** Number of residents at this point */
residents: number;
};Ridership Stats#
Returned by gameState.getRidershipStats().
type RidershipStats = {
/** Passengers per hour (current window) */
totalRidersPerHour: number;
/** Total passengers all time */
totalRiders: number;
/** Time window used for per-hour calculation (hours) */
timeWindowHours: number;
};Line Metrics#
Returned by gameState.getLineMetrics().
type LineMetric = {
/** Route ID */
routeId: string;
/** Route bullet/name */
routeBullet: string;
/** Route color (hex) */
routeColor: string;
/** Number of active trains */
trainCount: number;
/** Trains per hour, or null if not enough data */
trainsPerHour: number | null;
/** Passengers per hour on this route */
ridersPerHour: number;
/** Revenue per hour from this route */
revenuePerHour: number;
};Mode Choice Stats#
Returned by gameState.getModeChoiceStats().
type ModeChoiceStats = {
/** Number of commuters walking */
walking: number;
/** Number of commuters driving */
driving: number;
/** Number of commuters using transit */
transit: number;
/** Commuters without a determined mode */
unknown: number;
};Station Ridership#
Returned by gameState.getStationRidership().
type StationRidership = {
/** Total riders at this station (or system-wide if no ID given) */
total: number;
/** Number of transfer passengers */
transfers: number;
/** Breakdown by route */
byRoute: Array<{
routeId: string;
popCount: number;
/** Percentage of total (0-100) */
percent: number;
}>;
};Route Ridership#
Returned by gameState.getRouteRidership().
type RouteRidership = {
/** Total riders on this route */
total: number;
/** Breakdown by station */
byStation: Array<{
stationId: string;
popCount: number;
/** Percentage of total (0-100) */
percent: number;
}>;
};Blueprint Cost#
Returned by gameState.calculateBlueprintCost().
type BlueprintCost = {
/** Total cost in dollars */
totalCost: number;
/** Cost breakdown */
breakdown: {
/** Track construction cost */
trackCost: number;
/** Station construction cost */
stationCost: number;
/** Scissors crossover cost */
scissorsCrossoverCost: number;
/** Building demolition cost */
buildingDemolitionCost: number;
};
};Common Enums#
These string values appear across multiple types:
/** Track/station construction state */
type BuildType = 'constructed' | 'blueprint' | 'preview';
/** Game speed settings */
type TimeSpeed = 'slow' | 'normal' | 'fast' | 'ultrafast';
/** Built-in train types */
type TrainTypeId = 'heavy-metro' | 'light-metro' | 'commuter-rail';
/** Station type (extensible via registerStationType) */
type StationTypeId = 'standard' | string;