This is an old revision of the document!
Table of Contents
Create Timetables
Timetables define the AI service pattern on a map:
- which trains spawn (compositions)
- which route they drive (stops)
- on which platforms they stop
- when and how often services repeat
- how depot and dispatching logic supports the timetable traffic
Timetables are usually created in your map's Map.lua inside:
- `function <MapClass>:loadTimetables()`
This page explains the full timetable workflow using the TestMap example.
Related:
Prerequisites
Before creating timetables, ensure:
- stations are fully defined in `loadStations()`
- platform numbers / lengths / directions are final
- the map loads correctly without timetables first (recommended)
Timetables reference station objects, so stations must exist before you build schedules.
Timetables in Map.lua (Where they “live”)
A typical `loadTimetables()` builds:
- `self.timetables` — list of actual service entries (the ones that spawn trains)
- templates per line/direction (used for cloning)
- optional lookup tables (`self.templatesByLine`, `self.templatesByDirection`)
- depot spaces (`self.depots`)
- dispatching strategies (`self.dispatchingStrategies`)
At the end, the map registers them via:
controlCenter:setTimetableList(self.timetables, self.dispatchingStrategies, self.depots);
Step 1 — Create Templates (Per Line & Direction)
Templates describe the route and the allowed compositions, but do not spawn trains by themselves.
Example:
-- 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")
| Parameter | Description |
|---|---|
| `“U1”` | Line name used for UI and routing logic. |
| `0` | Variant / index value (map-specific usage). |
Step 2 — Add Stops (Route Definition)
Stops define the route as a sequence of station/platform/time entries.
: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. |
Step 3 — Alternative Platforms (altPlatform)
Example:
altPlatform = { "2", }
Use strings (`“1”`, `“2”`) because platform identifiers are commonly handled as strings in dispatch/routing.
Step 4 — Composition Weights
Weights control probability of selecting a composition for AI spawning.
| Weight | Meaning |
|---|---|
| 1.0 | Standard usage |
| 0.5 | Reduced probability |
| 0.0 | Excluded from AI spawning in this timetable |
A composition with weight `0.0` can still exist in the content system and be selectable for the player.
Step 5 — Create Real Services (clone + DayMask)
Templates must be cloned into real timetable entries.
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 |
5.1 Repeating Interval Services
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));
| Call | Description |
|---|---|
| clone(daytime(HH, MM), DayMask) | Creates the first entry at a given time (filtered by day mask if provided). |
| repeatUntil(daytime(HH, MM), interval) | Repeats every X minutes until the end time. |
| TableUtil.insertList(list, result) | Inserts all generated entries into `self.timetables`. |
5.2 Single Manual Trips
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));
Use this when you need exact control (first/last train, gaps, specials).
Step 6 — Useful Variations
6.1 Short Runs (Start/Terminate Early)
Terminate at `TSD`:
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));
Start at `TSD`:
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.2 Platform Overrides
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));
6.3 Service Runs (Non-Passenger Moves)
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.4 Force Unique Stop Lists
local Variant = self.TestLine_Dir1:clone(0, nil, true); Variant:forceUniqueStopList();
Step 7 — Depots and Dispatching
Depots and dispatching strategies support the timetable traffic and keep trains moving.
7.1 Depots (Depot Spaces)
`self.depots` groups depot tracks into named blocks.
| 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 is generated |
Example:
---@type table<string, Depot_DepotSpace[]> self.depots = { ["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 }, }, ["DP_60"] = { { station = self.stations.DP, platform = "60", direction = 2, noParkingTimetable = true }, }, };
7.2 Dispatching Strategies (Turnarounds / Depot Moves)
Dispatching strategies define how trains are handled outside normal passenger services:
- turnarounds at terminals
- spawning trains from depots
- sending trains back to depots
- resolving conflicts / keeping traffic stable
Basic structure:
---@type table<Station, ControlCenter_DispatchingStrategy[]> self.dispatchingStrategies = { [self.stations.TS] = { -- strategies for TS }, }
Strategies are evaluated top to bottom (order matters).
7.2.1 Pattern A — Simple Turnaround
{ sourceStation = self.stations.TS, targetStation = self.stations.TS, sourcePlatforms = { "1", "2" }, targetPlatforms = { "1", "2" }, replaceFirstPlatform = true, keepLine = false, minLayover = 4, }
7.2.2 Pattern B — Turnaround with Internal Movement
Hidden timetables are used for internal shunting and must be service runs:
{ 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, }), }
7.2.3 Pattern C — Spawning Trains from a Depot
{ 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, }), }
`sourceStation = nil` means the train comes from a depot.
7.2.4 Pattern D — Sending Trains to a Depot
{ 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, }), }
`targetStation = nil` means the train leaves traffic into a depot.
7.2.5 Hidden Timetables
Hidden timetables:
- are not shown to the player
- should always use `setIsServiceRun(true)`
- are used only for internal movements
Common Pitfalls
- station short names not matching BP_StationDefinition
- platform ids mismatching the BP_StationDefinition platform array
- compositions not registered or misspelled
- wrong dispatching strategy order
- forgetting to insert cloned services into `self.timetables`
