Lifecycle Hooks

React to game events with lifecycle hooks

Hooks let your mod respond to game events. Register a callback function that will be called when the event occurs.

Mod context is preserved automatically. When you register a hook, the modding API captures your mod's identity at registration time. This means api.storage.get() and api.storage.set() work correctly inside hook callbacks, even though your mod's script has already finished executing by the time the hook fires.

Error isolation: If your callback throws an error, it is caught and logged — other mods' callbacks for the same hook will still run. You will not crash the game.

Available Hooks#

onGameInit#

Called when the game initializes. Use this to set up your mod.

javascript
window.SubwayBuilderAPI.hooks.onGameInit(() => {
    console.log('Game initialized!');
});

onDayChange#

Called when the in-game day changes. Receives the new day number (1-indexed, matching the UI clock).

javascript
window.SubwayBuilderAPI.hooks.onDayChange((day) => {
    if (day % 100 === 0) {
        console.log(`Milestone: Day ${day}!`);
    }
});

onCityLoad#

Called when a city is loaded. Receives the city code.

javascript
window.SubwayBuilderAPI.hooks.onCityLoad((cityCode) => {
    console.log(`Loaded city: ${cityCode}`);
    if (cityCode === 'MTL') {
        // Do Montreal-specific initialization
    }
});

onMapReady#

Called when the map is fully loaded. Receives the MapLibre map instance.

javascript
window.SubwayBuilderAPI.hooks.onMapReady((map) => {
    console.log('Map is ready!', map);
    // Access raw MapLibre instance
    map.on('click', (e) => {
        console.log('Clicked:', e.lngLat);
    });
});

onStationBuilt#

Called when a station is constructed from blueprint.

javascript
window.SubwayBuilderAPI.hooks.onStationBuilt((station) => {
    console.log(`Station built: ${station.name}`);
});

onStationDeleted#

Called when a station is deleted.

javascript
window.SubwayBuilderAPI.hooks.onStationDeleted((stationId, stationName) => {
    console.log(`Station "${stationName}" (${stationId}) was deleted`);
});

onRouteCreated#

Called when a new route is created.

javascript
window.SubwayBuilderAPI.hooks.onRouteCreated((route) => {
    console.log(`New route: ${route.bullet} (${route.id})`);
});

onRouteDeleted#

Called when a route is deleted.

javascript
window.SubwayBuilderAPI.hooks.onRouteDeleted((routeId, routeBullet) => {
    console.log(`Route ${routeBullet} (${routeId}) deleted`);
});

onTrackBuilt#

Called when blueprint tracks are constructed.

javascript
window.SubwayBuilderAPI.hooks.onTrackBuilt((tracks) => {
    console.log(`${tracks.length} tracks constructed`);
});

onBlueprintPlaced#

Called when the player places blueprint tracks (before construction).

javascript
window.SubwayBuilderAPI.hooks.onBlueprintPlaced((tracks) => {
    console.log(`${tracks.length} blueprint tracks placed`);
});

onDemandChange#

Called when demand/population data is loaded for a city. If you register this callback after demand data has already loaded, it fires immediately with the current data so your mod doesn't miss the event.

javascript
window.SubwayBuilderAPI.hooks.onDemandChange((popCount) => {
    console.log(`Demand data loaded: ${popCount} commuter groups`);
});

onTrackChange#

Called when tracks are added or removed.

javascript
window.SubwayBuilderAPI.hooks.onTrackChange((changeType, count) => {
    console.log(`Tracks ${changeType}: ${count} tracks`);
    // changeType is 'add' or 'delete'
});

onTrainSpawned#

Called when a new train is generated.

javascript
window.SubwayBuilderAPI.hooks.onTrainSpawned((train) => {
    console.log(`Train spawned on route ${train.routeId}`);
});

onTrainDeleted#

Called when a train is removed from service.

javascript
window.SubwayBuilderAPI.hooks.onTrainDeleted((trainId, routeId) => {
    console.log(`Train ${trainId} deleted from route ${routeId}`);
});

onPauseChanged#

Called when the game is paused or resumed.

javascript
window.SubwayBuilderAPI.hooks.onPauseChanged((isPaused) => {
    console.log(`Game ${isPaused ? 'paused' : 'resumed'}`);
});

onSpeedChanged#

Called when game speed changes.

javascript
window.SubwayBuilderAPI.hooks.onSpeedChanged((newSpeed) => {
    console.log(`Game speed changed to: ${newSpeed}`);
    // newSpeed is 'slow', 'normal', 'fast', or 'ultrafast'
});

onMoneyChanged#

Called when the player's money changes (from any source — fares, construction, mods calling setMoney, etc.).

javascript
window.SubwayBuilderAPI.hooks.onMoneyChanged((newBalance, change, type, category) => {
    console.log(`${type}: $${Math.abs(change)} (${category || 'general'})`);
    console.log(`New balance: $${newBalance}`);
    // type is 'revenue' or 'expense'
    // category examples: 'construction', 'trainOperational', 'mod-setMoney'
});

onGameSaved#

Called after the game is saved (manual or autosave).

javascript
window.SubwayBuilderAPI.hooks.onGameSaved((saveName) => {
    console.log(`Game saved: ${saveName}`);
});

onGameLoaded#

Called after a save is loaded. Fires every time a save is loaded, including when returning to the main menu and loading a different save. If registered after a save has already been loaded, fires immediately with the current save name so your mod doesn't miss the event.

javascript
window.SubwayBuilderAPI.hooks.onGameLoaded((saveName) => {
    console.log(`Game loaded: ${saveName}`);
});

onWarning#

Called when the game issues a warning (e.g., insufficient funds, track collision).

javascript
window.SubwayBuilderAPI.hooks.onWarning((warning) => {
    console.log(`Warning [${warning.type}]: ${warning.message}`);
    // warning.id - unique warning identifier
    // warning.type - warning category
    // warning.message - human-readable message
});

onError#

Called when a recoverable error occurs. Useful for monitoring and debugging.

javascript
window.SubwayBuilderAPI.hooks.onError((error) => {
    console.log(`Error [${error.type}]: ${error.message} (recoverable: ${error.recoverable})`);
    // error.type - error category
    // error.message - human-readable error description
    // error.recoverable - whether the game can continue
});

onGameEnd#

Called when exiting the game (returning to menu or closing). Use this to clean up resources.

javascript
window.SubwayBuilderAPI.hooks.onGameEnd(() => {
    console.log('Game ending, cleaning up...');
    // Clean up intervals, listeners, etc.
});

Hook Summary#

HookParametersDescription
onGameInitnoneGame initialization
onDayChangeday: numberDay changed
onCityLoadcityCode: stringCity loaded
onMapReadymap: MapLibreMap ready
onStationBuiltstation: StationStation constructed
onStationDeletedstationId: string, stationName: stringStation deleted
onRouteCreatedroute: RouteRoute created
onRouteDeletedrouteId: string, routeBullet: stringRoute deleted
onTrackBuilttracks: Track[]Tracks constructed
onBlueprintPlacedtracks: Track[]Blueprints placed
onDemandChangepopCount: numberDemand data loaded (late-fire)
onTrackChange`type: 'add' \'delete', count: number`Tracks added/removed
onTrainSpawnedtrain: TrainTrain created
onTrainDeletedtrainId: string, routeId: stringTrain removed
onPauseChangedisPaused: booleanPause state changed
onSpeedChanged`speed: 'slow' \'normal' \'fast' \'ultrafast'`Speed changed
onMoneyChanged`balance: number, change: number, type: 'revenue' \'expense', category?: string`Money changed
onGameSavedsaveName: stringGame saved
onGameLoadedsaveName: stringGame loaded (late-fire)
onWarningwarning: { id: string, type: string, message: string }Game warning
onErrorerror: { type: string, message: string, recoverable: boolean }Error occurred
onGameEndnoneExiting the game

Late-fire hooks: onDemandChange and onGameLoaded fire immediately if registered after the event has already occurred, so mods that load late still get the data they need.