User Tools

Site Tools


manual:subwaysim:map_construction:create_maplua

This is an old revision of the document!


Table of Contents

Map.lua

The Map.lua file defines all runtime logic for a map in SubwaySim 2.

It is responsible for:

  • registering the map in the ContentManager
  • linking the Unreal Engine level to the map
  • defining stations and platforms
  • defining AI timetables and services
  • configuring dispatching and depot logic
  • providing optional career mode data

This page explains the structure and purpose of the Map.lua file and how it interacts with elements placed in the Unreal Editor (tracks, signals, Station Definitions).


Purpose of the Map.lua

The Map.lua acts as the central configuration and logic file for a map.

While the Unreal level defines the physical layout (tracks, stations, signals), the Map.lua defines the operational behavior:

  • where trains may spawn
  • how they route through the map
  • which stations exist and how they are used
  • when and how AI services operate

Without a Map.lua, a map:

  • cannot be selected in the menu
  • cannot spawn AI trains
  • cannot provide station-based logic

Prerequisites

Before working with the Map.lua, make sure that the following steps are completed:

  • The Unreal level (.umap) is finished

Create a Level

  • Tracks and signals are correctly placed

Railtool

  • BP_StationDefinition actors exist for all stations and depots

Prepare a Map for SubwaySim

  • Platform numbers, platform lengths, and platform directions are finalized

(these values are referenced directly by the Map.lua and must not change later)

The Map.lua relies on these elements being set up correctly and consistently in the level.


Map.lua Explained (TestMap Example)

This page explains the structure of a Map.lua file for SubwaySim 2. It follows the file from top to bottom and explains each section in detail.

The Map.lua consists of two major parts:

  • Map Registration (ContentManager DataTable)
  • Runtime Map Logic (Lua class based on BaseMap)

Complete Map.lua File

--
--
-- SubwaySim2
-- Module TestMap.lua
--
-- Map file for SDK TestMap
--
--
-- Author:	SDK User
-- Date:	__/__/____
--
---@class TestMap : TestMap, BaseMap
TestMap = Class("TestMap", TestMap, BaseMap);
 
---@type SSB_Map_DataTable
local TestMap_DataTable = {
	contentType		= "map",
	contentName		= "TestMap",
	class			= TestMap,
	levelName		= "Testmap",
	author			= "$GameDeveloper",
 
	title			= "SDK TestMap",
	subtitle		= "This could be your text",
	description		= "This is a test map that demonstrates how to use the Modding SDK. You can also use it for testing your own vehicles.",
	previewFilename = "/SubwaySim2_Core/UI/MainMenu/Backgrounds/CityBerlin.CityBerlin",
};
g_contentManager:addContent(TestMap_DataTable);
 
--- Creates a new instance of this class
---@return TestMap
function TestMap:new()
	self = TestMap:emptyNew();
 
	self.levelName		= "Testmap";
	self.displayName	= "SDK TestMap";
 
	-- latitude and longitude of Berlin's city center
	self.latitude		= 52.518611;
	self.longitude		= 13.408333;
	-- UTC+1
	self.timezone		= 1;
 
	self:loadStations();
	self:loadTimetables();
	self:loadCareerMode();
 
	EventManager.callModListeners("onMapCreated", self);
 
	return self;
end;
 
--- Event to load any additionally required level instances
function TestMap:loadLevelInstances()
	assert(GameplayStatics.loadLevelInstance("SubwaySim2_Environment", Vector3.zero, Vector3.zero), "Failed to load a part of the level");
end;
 
--- Loads the station definitions for this map
function TestMap:loadStations()
	---@type table<string, Station>
	self.stations = {}
 
	self.stations.TS = Station:new("TS", "TestStation")
		:addSpawnPlatform("1", 115, 2)
		:addSpawnPlatform("2", 115, 2)
 
	self.stations.TSD = Station:new("TSD", "TestStation Depot")
 
	self.stations.TSA = Station:new("TSA", "TestStation Anfang")
		:addSpawnPlatform("1", 110, 1)
		:addSpawnPlatform("2", 110, 1)
 
	self.stations.DP = Station:new("DP", "Depot")
		:addSpawnPlatform("51", 220, 2)
		:addSpawnPlatform("52", 220, 2)
		:addSpawnPlatform("53", 220, 2)
		:addSpawnPlatform("54", 220, 2)
		:addSpawnPlatform("60", 110, 2)
 
end
 
--- Loads the default timetables for this map
function TestMap:loadTimetables()
 
	---@type Timetable[]
	self.timetables = {};
 
	-- Direction 1 (TS -> TSA)
	self.TestLine_Dir1 = Timetable:new("U1", 0)
		:addTrainComposition("Berlin_HK_1x", 0.5)
		:addTrainComposition("Berlin_HK_2x")
		:addTrainComposition("Berlin_A3L92_1x", 0)
		:addTrainComposition("Berlin_A3L92_2x", 0)
		:addTrainComposition("Berlin_A3L92_3x", 0.5)
		:addTrainComposition("Berlin_A3L92_4x")
		:addStop({
			station = self.stations.TS,
			platform = 2,
			departure = 0,
			speedLimit = 70,
			routeSettingMaxETA = 0.5, -- Fahrstraße stellt sich erst 0.5 Minuten vor Abfahrt
		})
		:addStop({
			station = self.stations.TSD,
			platform = 2,
			departure = 0,
			speedLimit = 70,
		})
		:addStop({
			station = self.stations.TSA,
			platform = 1,
			departure = 0,
			speedLimit = 70,
			altPlatform = { "2",},
		})
 
	-- Direction 2 (TSA -> TS)
	self.TestLine_Dir2 = Timetable:new("U1", 0)
		:addTrainComposition("Berlin_HK_1x", 0.5)
		:addTrainComposition("Berlin_HK_2x")
		:addTrainComposition("Berlin_A3L92_1x", 0)
		:addTrainComposition("Berlin_A3L92_2x", 0)
		:addTrainComposition("Berlin_A3L92_3x", 0.5)
		:addTrainComposition("Berlin_A3L92_4x")
		:addStop({
			station = self.stations.TSA,
			platform = 1,
			departure = 0,
			speedLimit = 70,
			routeSettingMaxETA = 0.5, -- Fahrstraße stellt sich erst 0.5 Minuten vor Abfahrt
		})
		:addStop({
			station = self.stations.TSD,
			platform = 1,
			departure = 0,
			speedLimit = 70,
		})
		:addStop({
			station = self.stations.TS,
			platform = 2,
			departure = 0,
			speedLimit = 70,
			altPlatform = { "1",},
		})
 
	-- List of templates by line, then by direction
	---@type Timetable[][]
	self.templatesByLine = {
		[1] = {
			[1] = self.TestLine_Dir1,
			[2] = self.TestLine_Dir2,
		},
	};
 
	---@type table<1|2, Timetable[]>
	self.templatesByDirection = {
		[1] = {
			self.TestLine_Dir1,
		},
		[2] = {
			self.TestLine_Dir2,
		},
	};
 
	local DM = DayMask;
 
	-- Repeating interval example
	TableUtil.insertList(self.timetables, self.TestLine_Dir1:clone(daytime(04, 30)):repeatUntil(daytime(23, 30), 10));
	TableUtil.insertList(self.timetables, self.TestLine_Dir2:clone(daytime(04, 35)):repeatUntil(daytime(23, 35), 10));
 
	---@type table<string, Depot_DepotSpace[]>
	self.depots = {
	};
 
	---@type table<Station, ControlCenter_DispatchingStrategy[]>
	self.dispatchingStrategies = {
 
		-- Turnaround logic at TS
 		[self.stations.TS] = {
			{
				sourceStation = self.stations.TS,
				targetStation = self.stations.TS,
				sourcePlatforms = { "1", "2" },
				targetPlatforms = { "1", "2" },
				replaceFirstPlatform = true,
				keepLine = false,
				minLayover = 4,
			}, 
		},
 
		-- Turnaround logic at TSA
		[self.stations.TSA] = {
			{
				sourceStation = self.stations.TSA,
				targetStation = self.stations.TSA,
				sourcePlatforms = { "1", "2" },
				targetPlatforms = { "1", "2" },
				replaceFirstPlatform = true,
				keepLine = false,
				minLayover = 4,
			}, 
		},
	};
end;
 
--- Initializes data for career mode
function TestMap:loadCareerMode()
end;
 
--- Registers all valid timetables to the given `controlCenter` instance
---@param controlCenter ControlCenter
function TestMap:registerTimetables(controlCenter)
	controlCenter:setStationList(self.stations);
	controlCenter:setTimetableList(self.timetables, self.dispatchingStrategies, self.depots);
end;

1) Map Registration (ContentManager DataTable)

The DataTable registers the map in the ContentManager. Without it the map will not appear in the map selection menu and cannot be loaded.

DataTable Fields

Field Description
contentType Defines the type of content. Must be `“map”`.
contentName Unique internal identifier for this map across all mods.
class Reference to the Lua map class that provides runtime logic.
levelName Unreal level (.umap) name that will be loaded. Must match exactly.
author Author metadata (UI / debugging).
title Map title shown in the selection menu.
subtitle Optional subtitle below the title.
description Longer description shown in UI.
previewFilename Path to the preview image used in the main menu.

Registering the DataTable

After the table is defined, it must be registered:

g_contentManager:addContent(TestMap_DataTable);

If this call is missing, the map is not registered and will never load.


2) Runtime Map Class (BaseMap)

The runtime class is responsible for everything that happens when the map is loaded:

  • defining stations and their platforms
  • defining timetables and AI services
  • configuring dispatching / turnaround rules
  • optional career mode setup

Class Definition

TestMap = Class("TestMap", TestMap, BaseMap);

This creates a new map class inheriting from `BaseMap`.


3) Constructor (new)

The constructor creates the map instance and prepares the runtime data.

Core Properties

Property Description
self.levelName The Unreal level to load (must match DataTable `levelName`).
self.displayName Internal display name used at runtime.
self.latitude Used for sun position and environment lighting.
self.longitude Used for sun position and environment lighting.
self.timezone Timezone offset for day/time simulation (UTC+1 = 1).

Loading Runtime Data

The order matters:

  • `loadStations()` must run first (timetables reference stations)
  • `loadTimetables()` uses station references
  • `loadCareerMode()` is optional

Mod Event Hook

EventManager.callModListeners("onMapCreated", self);

This allows other mods or systems to react when the map instance is created.


4) loadLevelInstances()

GameplayStatics.loadLevelInstance("SubwaySim2_Environment", Vector3.zero, Vector3.zero)

This call loads the shared environment level used by SubwaySim 2.

It provides:

  • weather effects (e.g. rain)
  • global lighting
  • time-of-day behavior

By loading this prepared level, mod maps can use the full weather and lighting system without accessing license-protected core code.

The environment level is loaded in addition to the map level and runs alongside it.

⚠️ Current limitation: Loading multiple levels that contain Railtool Blueprints can cause issues. At the moment, only one loaded level should contain Railtool infrastructure. This limitation will be resolved in future updates.


5) Stations (Derived from BP_StationDefinition)

Stations defined in `loadStations()` must match BP_StationDefinition actors placed in the Unreal Editor.

5.1 How Lua Stations Connect to BP_StationDefinition

The connection is made via the station short name:

  • In Unreal: BP_StationDefinition → Name Short
  • In Lua: `Station:new(“TS”, “TestStation”)`

If the short name does not match exactly:

  • stations may not register correctly
  • AI routing can fail
  • timetable stops may not resolve

5.2 Station Table

Stations are stored as a keyed table:

---@type table<string, Station>
self.stations = {}

The key is usually identical to the short name:

  • `self.stations.TS`
  • `self.stations.TSA`
  • `self.stations.DP`

5.3 Station:new()

Creating a station:

Station:new("TS", "TestStation")
Parameter Description
`“TS”` Short name (station code). Must match BP_StationDefinition Name Short.
`“TestStation”` Display name shown in UI.

5.4 addSpawnPlatform()

Spawn platforms define where trains may spawn for player spawning (depending on map setup)

Example:

:addSpawnPlatform("1", 115, 2)
Parameter Description
`“1”` Platform number as defined in BP_StationDefinition platform array.
`115` Max allowed train length in meters for spawning at this platform.
`2` Spawn direction on the track (orientation). Must match your platform Begin/End marker direction logic.

⚠️ Important Platform numbers are not “free”. They must match exactly the platform configuration inside BP_StationDefinition.

5.5 Example: Depot Platforms

The depot station `DP` uses multiple platform numbers:

  • 51–54
  • 60

This is a common pattern to represent multiple depot tracks.


6) Timetables

Timetables define:

  • which trains spawn (compositions)
  • what route they drive (stops)
  • on which platforms they stop
  • how often services repeat

6.1 Timetable:new()

A timetable template is created with:

Timetable:new("U1", 0)
Parameter Description
`“U1”` Line name used for UI and routing logic.
`0` Variant / index value (map-specific usage).

Templates are usually created per direction:

  • Direction 1: TS → TSA
  • Direction 2: TSA → TS

6.2 Train Compositions

Train compositions define which vehicle sets may be used when this timetable spawns AI trains.

Each composition is referenced by its `contentName`, exactly as it is registered in the ContentManager (e.g. in `Vehicle.lua` or `Composition.lua`).

Example:

contentName = "Berlin_HK_2x"

The name must match exactly. If a composition is not registered or the name is incorrect, no train will spawn for it.

Adding Compositions to a Timetable

Compositions are added directly to the timetable template:

:addTrainComposition("Berlin_HK_2x")

A weight can optionally be provided:

:addTrainComposition("Berlin_HK_1x", 0.5)

If no weight is specified, a default weight of `1.0` is assumed.

Composition Weights

Weights control how likely a composition is selected relative to other compositions within the same timetable.

They only affect AI spawning behavior for this timetable.

Weight Meaning
1.0 Standard usage. The composition is commonly selected.
0.5 Reduced probability. The composition is used less frequently.
0.0 The composition is excluded from AI spawning for this timetable.

Important Behavior of Weight = 0.0

A weight of `0.0` prevents the composition from being selected when AI trains are spawned by this timetable.

The composition itself remains a valid registered vehicle and may still:

  • exist in the content system
  • appear in the vehicle selection menu

This behavior is intentional and allows a composition to be:

  • available to the player
  • but excluded from AI traffic on a specific line or timetable

6.3 addStop() (Route Definition)

Stops define the actual route of a timetable. Each stop is a table passed into `addStop({ … })`.

Example:

:addStop({
	station = self.stations.TS,
	platform = 2,
	departure = 0,
	speedLimit = 70,
	routeSettingMaxETA = 0.5,
})
Field Type Description
station Station Reference to a station defined in `loadStations()`.
platform number Platform number the train uses at this station. Must exist in BP_StationDefinition.
departure number Minutes after service start/spawn when the train departs this stop.
speedLimit number Speed limit applied after departing this stop (signal logic dependent).
routeSettingMaxETA number (optional) How many minutes before departure the route (Fahrstraße) should be requested/set.
altPlatform table<string> (optional) Alternative platforms that may be used if the primary platform is unavailable.

6.4 altPlatform (Alternative Platforms)

Example:

altPlatform = { "2", }

⚠️ Use string values (`“1”`, `“2”`) because platform identifiers are typically handled as strings in routing/dispatch contexts.

This allows AI to select another platform if:

  • the preferred platform is blocked
  • dispatching assigns an alternative

6.5 routeSettingMaxETA (Route Pre-Setting)

Example:

routeSettingMaxETA = 0.5

Meaning:

  • the route will be requested/updated roughly 0.5 minutes before departure

This can help avoid early route locking and improves traffic handling at busy stations.

6.6 clone() + DayMask (Creating Services)

A timetable template does not spawn trains by itself. It must be cloned into real timetable entries and inserted into `self.timetables`.

For day-based schedules, use `DayMask`:

local DM = DayMask;
DayMask Meaning
DM.Weekdays Monday to Friday
DM.Weekends Saturday and Sunday
DM.Sat Saturday only
DM.Sun Sunday only
DM.Always Every day

The typical workflow is:

  • Create a timetable template (e.g. `self.TestLine_Dir1`)
  • Clone it for a start time (optionally with a day mask)
  • Either insert a single trip, or generate a repeating service

6.6.1 Repeating Services (Interval Based)

This creates a repeating service between two times:

TableUtil.insertList(
	self.timetables,
	self.TestLine_Dir1:clone(daytime(04, 30), DM.Weekdays):repeatUntil(daytime(23, 30), 10)
);
 
TableUtil.insertList(
	self.timetables,
	self.TestLine_Dir2:clone(daytime(04, 35), DM.Weekdays):repeatUntil(daytime(23, 35), 10)
);
Call Description
clone(daytime(HH, MM), DayMask) Creates the first entry at a given time (filtered by the day mask).
repeatUntil(daytime(HH, MM), interval) Repeats every X minutes until the end time.
TableUtil.insertList(list, result) Inserts all generated entries into `self.timetables`.

Result:

  • a full service pattern is generated automatically

6.6.2 Single Timetable Entries (Manual Trips)

If you want to schedule individual trips (first/last train, gaps, special runs), insert a single cloned entry:

table.insert(self.timetables, self.TestLine_Dir1:clone(daytime(12, 07), DM.Weekdays));
table.insert(self.timetables, self.TestLine_Dir2:clone(daytime(12, 12), DM.Weekdays));

This creates exactly one departure at the given time.

Use this approach when you need full control over:

  • exact departure times
  • exceptions or gaps
  • different patterns on different days

6.7 Useful Variations (Based on the TestMap)

The following patterns are commonly used when building more advanced schedules. They are shown here using the TestMap stations (`TS`, `TSD`, `TSA`, `DP`).

6.7.1 Short Runs (Start or Terminate Early)

Sometimes a service should start later or terminate earlier than the full route. This is useful for:

  • depot in/out runs
  • special services
  • partial line operations

Example: terminate at `TSD` (short turn / depot related movement):

local TS_to_TSD = self.TestLine_Dir1:clone(0, nil, true):terminateAtStation("TSD", true);
 
table.insert(self.timetables, TS_to_TSD:clone(daytime(05, 10), DM.Weekdays));

Example: start at `TSD` (depot insertion into service):

local TSD_to_TSA = self.TestLine_Dir1:clone(0, nil, true):startAtStation("TSD", true);
 
table.insert(self.timetables, TSD_to_TSA:clone(daytime(05, 20), DM.Weekdays));

6.7.2 Platform Overrides (TestMap Example)

If you want to use the same template but spawn on a different platform, you can override platform numbers after cloning.

Example: force first stop to use platform 1 instead of 2 at TS:

local TS_Platform1 = self.TestLine_Dir1:clone(0, nil, true);
TS_Platform1:getFirstStop().platform = 1;
 
table.insert(self.timetables, TS_Platform1:clone(daytime(06, 00), DM.Weekdays));

This is useful if:

  • multiple platforms exist
  • you want different patterns at different times of day
  • you temporarily reroute services during testing

6.7.3 Service Runs (Non-Passenger Moves)

A service run is a trip that should not be treated as a normal passenger service.

Example: a depot-related move to TS marked as service run:

local DP_to_TS_SR = self.TestLine_Dir2:clone(0, nil, true)
	:startAtStation("DP", true)
	:terminateAtStation("TS", true)
	:setIsServiceRun(true);
 
table.insert(self.timetables, DP_to_TS_SR:clone(daytime(04, 10), DM.Weekdays));

6.7.4 Force Unique Stop Lists (Safe Editing)

If you modify stop properties (platforms, PIS text, flags), it can be helpful to ensure the stop list is unique:

local Variant = self.TestLine_Dir1:clone(0, nil, true);
Variant:forceUniqueStopList();

This prevents accidental shared stop references when creating multiple variants.


7) Depots and Dispatching

This section defines:

  • depot storage spaces (optional)
  • turnaround / dispatch behavior at stations

7.1 Depots (Parking Tracks / Depot Spaces)

Depots define where AI trains are allowed to park when they are not in service. They are also used by the ControlCenter for dispatching and (later) career mode.

In the TestMap, the depot station is:

  • `DP` (Depot)

And it provides these depot tracks:

  • 51, 52, 53, 54 (long depot tracks)
  • 60 (short depot / test track)

7.1.1 Depot Table Structure

`self.depots` is a table that groups depot tracks into named blocks (groups). Each group contains a list of `Depot_DepotSpace` entries.

Field Meaning
station Station reference (must exist in `self.stations`)
platform Track / platform ID as defined in the BP_StationDefinition
direction Which direction trains should park/spawn facing (1 or 2)
noParkingTimetable If true: no dedicated parking timetable should be generated for this track (useful to keep a track free)

7.1.2 TestMap Example (DP 51–54 + 60)

---@type table<string, Depot_DepotSpace[]>
self.depots = {
 
	-- Main depot area (long tracks)
	["DP_51_54"] = {
		{ station = self.stations.DP, platform = "51", direction = 2, noParkingTimetable = false },
		{ station = self.stations.DP, platform = "52", direction = 2, noParkingTimetable = false },
		{ station = self.stations.DP, platform = "53", direction = 2, noParkingTimetable = false },
		{ station = self.stations.DP, platform = "54", direction = 2, noParkingTimetable = false },
	},
 
	-- Short depot / test track (useful to keep free or for special moves)
	["DP_60"] = {
		{ station = self.stations.DP, platform = "60", direction = 2, noParkingTimetable = true },
	},
};

Notes:

  • The group keys (`“DP_51_54”`, `“DP_60”`) are just identifiers for readability.
  • `platform` must match the platform numbers inside your BP_StationDefinition for DP.
  • Use `noParkingTimetable = true` if you want to keep a track free (e.g. for turnarounds or testing).

7.2 Dispatching Strategies

Dispatching Strategies define how trains are handled outside of normal passenger service. They are used to:

  • turn trains around at terminal stations,
  • spawn trains from depots,
  • send trains back to depots,
  • resolve platform conflicts,
  • and keep traffic flowing when timetables alone are not sufficient.

Dispatching is handled by the ControlCenter and works in addition to normal timetables.

7.2.1 Basic Structure

Dispatching strategies are defined as a table, grouped by station:

---@type table<Station, ControlCenter_DispatchingStrategy[]>
self.dispatchingStrategies = {
    [self.stations.WA] = {
        -- strategies for this station
    },
}

Each station can have multiple strategies. They are evaluated top to bottom, so order matters.

7.2.2 When Dispatching Is Used

Dispatching strategies are evaluated when:

  • a timetable ends at a station,
  • a train needs to turn around,
  • no suitable train is available for a departure,
  • or a train must be moved to or from a depot.

If no strategy matches, the train will remain idle.

7.2.3 Strategy Fields

Each dispatching strategy can define the following fields:

Field Description
sourceStation Station where the train currently is. Use `nil` for depot spawns.
targetStation Station the train should serve next. Use `nil` for depot despawn.
sourcePlatforms Allowed platforms the train may come from.
targetPlatforms Allowed platforms the train may go to.
depotName Name of the depot (as defined in the depots table).
minLayover Minimum minutes the train must wait before reuse.
keepLine Try to keep the train on the same line.
replaceFirstPlatform Replace the first stop platform if needed.
replaceLastPlatform Replace the last stop platform if needed.
overrideFirstPlatform Force a specific first platform.
overrideLastPlatform Force a specific last platform.
timetable Optional hidden timetable used for movements.

Not all fields are required for every strategy.

7.2.4 Pattern A — Simple Turnaround

This is the most common case: A train arrives at a station and turns around to serve the opposite direction.

Example (Test Map):

{
    sourceStation = self.stations.Kbo,
    targetStation = self.stations.Kbo,
    sourcePlatforms = { "1" },
    targetPlatforms = { "2" },
    minLayover = 3,
}

What happens:

  • a train arriving on platform 1
  • waits at least 3 minutes
  • and departs again from platform 2

No depot is involved.

7.2.5 Pattern B — Turnaround with Internal Movement

Some stations require a shunting move to turn a train.

In this case, a hidden timetable is attached:

{
    sourceStation = self.stations.Go,
    targetStation = self.stations.Go,
    sourcePlatforms = { "1" },
    targetPlatforms = { "2" },
    minLayover = 3,
    timetable = Timetable:new("", 0)
        :setIsServiceRun(true)
        :addStop({
            station = self.stations.Go,
            platform = "6",
            departure = 2,
            turnAround = true,
        })
        :addStop({
            station = self.stations.Go,
            platform = "2",
            departure = 3,
        }),
}

This allows:

  • temporary use of siding or crossover tracks
  • clean turnarounds without blocking passenger platforms

7.2.6 Pattern C — Spawning Trains from a Depot

When no train is available, dispatching can fetch a train from a depot.

Example:

{
    sourceStation = nil,
    targetStation = self.stations.WA,
    targetPlatforms = { "1", "2" },
    depotName = "WA_06_09",
    overrideFirstPlatform = "3",
    timetable = Timetable:new("", 0)
        :setIsServiceRun(true)
        :addStop({
            station = self.stations.WA,
            platform = "7",
            departure = -5,
        })
        :addStop({
            station = self.stations.WA,
            platform = "3",
            departure = -3,
        }),
}

Key points:

  • `sourceStation = nil` means the train comes from a depot
  • the depot name must match the depots table
  • negative departure times happen before the actual service

7.2.7 Pattern D — Sending Trains to a Depot

After service ends, trains can be removed from traffic.

Example:

{
    sourceStation = self.stations.WA,
    sourcePlatforms = { "1", "2" },
    targetStation = nil,
    depotName = "WA_11_18",
    timetable = Timetable:new("", 0)
        :setIsServiceRun(true)
        :addStop({
            station = self.stations.WA,
            platform = "3",
            departure = 2,
        })
        :addStop({
            station = self.stations.WA,
            platform = "11",
            departure = 6,
        }),
}

Here:

  • the train leaves passenger service
  • moves into the depot
  • and is no longer available for dispatching

7.2.8 Hidden Timetables

Timetables inside dispatching strategies:

  • are not shown to the player
  • are always marked as `setIsServiceRun(true)`
  • are used only for internal movements

They allow precise control over:

  • routing
  • speed limits
  • platform usage
  • turnarounds

7.2.9 Strategy Order

Dispatching strategies are evaluated in order.

Recommended structure per station:

1. normal turnarounds
2. depot spawn strategies
3. depot despawn strategies
4. fallback strategies

This avoids unnecessary depot movements and keeps traffic stable.

7.2.10 Common Pitfalls

  • depot names not matching the depots table
  • missing `setIsServiceRun(true)` on hidden timetables
  • conflicting platform definitions
  • wrong strategy order

If dispatching behaves unexpectedly, always check the order first.


8) Career Mode (Optional)

`loadCareerMode()` is optional and currently empty.

This is where career mode related data can be initialized later.


9) Registering Stations and Timetables

The final step is registering runtime data with the ControlCenter:

controlCenter:setStationList(self.stations);
controlCenter:setTimetableList(self.timetables, self.dispatchingStrategies, self.depots);
Call Description
setStationList Registers all stations for routing, UI and spawning logic.
setTimetableList Registers AI services plus dispatching and depot logic.

If this function is missing or incomplete:

  • AI traffic will not work
  • stations may not be recognized for routing

© 2025 Simuverse Interactive · SubwaySim 2 Modding Wiki

All trademarks and registered trademarks are the property of their respective owners. This Wiki is provided for documentation and modding purposes only.

2025/12/12 12:06 · dcs
manual/subwaysim/map_construction/create_maplua.1768396185.txt.gz · Last modified: by dcs

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki