Rail signalling
Rail signalling is how a game's signals decide when a train may enter the track ahead, from one train per block to signals that look at the way out.
Rail signalling decides when a train may enter the track ahead. In the games read here, the simplest signal lets one train into each block1. Others hold a train until the block beyond has a way out, read from any exit2 or, as Friday Facts #81 (2015) describes Factorio’s chain signal, from the exit on the train’s own path3, or let a train through once it has reserved its own track4.
- BlockTransport Tycoon (1994). In Deluxe, signals split the line into sections and let one train into each; one-way signals suit double track.1,5,6
- Any exitOpenTTD (2004). Entry, exit and combo pre-signals: an entry signal turns red when the block ahead has exit signals and none of them is green.7,2,8TTDPatch. Pre-signals, a signal type the patch adds to Transport Tycoon Deluxe: one shows green only if an exit signal of the block past it is green.9,10,11
- Route exitFactorio (2016). Signals cut the track into blocks that hold one train each; chain signals, as Friday Facts #81 (2015) describes them, keep a train out of a block unless the exit on its own path is free.12,3,13,14
- Path reservationOpenTTD (2004). Path signals, since 2009: a train reserves track to the next safe place to stop, and the signal turns green for it when the reservation succeeds.4,15,16
#How far a signal looks
| Signal | Turns red when | Games |
|---|---|---|
| Block | another train is in the block past it | Transport Tycoon Deluxe1, OpenTTD17, Factorio12 |
| Pre-signal (any exit) | the block past it has exit signals and none of them is green | TTDPatch9, OpenTTD’s entry signal2 |
| Chain signal (route exit) | the block past it is occupied, or the exit on the train’s path isn’t free | Factorio, as Friday Facts #81 (2015) describes it3 |
| Path signal | a train is in its block, or the block holds a junction or several ways in; it turns green for a train that reserves track to the next safe place to stop | OpenTTD4 |
All three games keep the block rule. Transport Tycoon Deluxe’s signals, OpenTTD’s block signal and Factorio’s ordinary rail signal each keep a train out of a block that another train is in18,19. The manual of Transport Tycoon Deluxe describes only that signal and a single-direction one10,5. Its advice for single track is passing loops with signals at both ends, and for three trains or more, two tracks with one-way signals facing opposite ways6.
The look-ahead signals differ in which exit they read. A TTDPatch pre-signal, or an OpenTTD entry signal, clears when any exit signal of the block ahead is green, even if the train will leave by another exit. Factorio’s chain signal, as Friday Facts #81 (2015) describes it, looks only at the exit on the train’s path11,8. TTDPatch’s manual names station entrances as a place where pre-signals are particularly useful9. Friday Facts #81 shows the chain signal keeping a train out of a shared single-track section it can’t leave3.
OpenTTD’s path signal works differently from both. With no reservation behind it, it is red while a train is in its block, and also when the block holds a junction or more than one signal leads into it. A train at the signal reserves track to the next safe place to stop, and the signal turns green for that train when the reservation succeeds4. OpenTTD 15.3 has all three kinds: block, three pre-signals (entry, exit and combo), and two path signals (two-way and one-way)7.
#Signals and routes
In Factorio, a train already knows its route, and signals mainly keep trains from colliding or blocking each other12. A train that would have to stop at a red signal looks for a new path14. OpenTTD’s pathfinder prices the signals ahead: a red block or pre-signal among the first ten adds a cost that falls with distance, and a red path signal adds none20. Both games count a busy block or red signal for less the farther ahead it lies21. Pathfinding compares the route costs.
#Quirks
- In OpenTTD, a train inside a depot doesn’t count as occupying a block17.
- An OpenTTD combo signal placed both ways doesn’t count its own back face as a way out of the block. When an exit or combo signal changes color, the block behind it is worked out again, so a chain of combo signals passes the state back block by block2.
- OpenTTD’s pathfinder treats a signal facing only against the train as a dead end. The exception is a two-way path signal, which a train may pass from behind at a cost of 15 tiles of track15.
- Factorio’s chain signal has a fourth state, blue, beside the rail signal’s red, yellow and green. Each state can be sent to the circuit network13.
- In Transport Tycoon Deluxe, the player makes a single-direction signal by clicking a second or third time on the same spot with the signal tool5.
#History
Transport Tycoon Deluxe came with improved signals for complex train systems, Retro Gamer wrote in 2015, without saying what changed22. TTDPatch’s manual calls the game’s own signals “block signals” and presents pre-signals as a new type that its switches add10.
OpenTTD’s changelog lists preliminary pre-signal support in March 2004, and pre-signals set by Ctrl-clicking a signal in April 200423. Path based signalling is listed in the 0.7.0-beta1 changelog (February 2009)16. OpenTTD’s About page says many of its features were inspired by TTDPatch, but it doesn’t name them24. No source read here says OpenTTD’s pre-signals came from TTDPatch’s.
Friday Facts #81 (2015) says Factorio’s chain signals have some similarities with “TTD pre signals” but work differently, so they got a different name25. That is a comparison, not a statement of influence.
Games
| Game | Year | How | Note |
|---|---|---|---|
| Transport Tycoon | 1994 | Block | in Deluxe, signals split the line into sections and let one train into each; one-way signals suit double track |
| OpenTTD | 2004 | Any exit | entry, exit and combo pre-signals: an entry signal turns red when the block ahead has exit signals and none of them is green |
| TTDPatch | Any exit | pre-signals, a signal type the patch adds to Transport Tycoon Deluxe: one shows green only if an exit signal of the block past it is green | |
| Factorio | 2016 | Route exit | signals cut the track into blocks that hold one train each; chain signals, as Friday Facts #81 (2015) describes them, keep a train out of a block unless the exit on its own path is free |
| OpenTTD | 2004 | Path reservation | path signals, since 2009: a train reserves track to the next safe place to stop, and the signal turns green for it when the reservation succeeds |
Every claim about this mechanic, by game (19)
References
- ^ abcIn Transport Tycoon Deluxe, signals divide the railway into sections and let only one train into a section at a time: each signal shows green if the section ahead is clear and red if it is occupied, and a train always stops and waits at a red signal.ManualMicroProse, Transport Tycoon Deluxe manual
How we know
As in the real thing, the principle of the railway signalling system is to prevent trains from colliding by preventing more than one train from entering any one section of railway track. Each signal tells the train if the section of track ahead is clear (green signal), or occupied (red signal). The train will always stop and wait at a red signal; preventing it from colliding with a train in the next section.
p. 94, Using Railway Signals, The Basics · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abcdWith no train in the block, an entry signal turns red when the block ahead has at least one exit or combo signal leading out of it and none of them is green, whichever way the waiting train would go. A combo signal acts as an entry and an exit at once; one placed both ways doesn't count its own back face as a way out. When an exit or combo signal changes color, the block behind it is worked out again, so a chain of combo signals passes the state back block by block.CodeOpenTTD 15.3 source
How we know
432 } else { 433 /* is it a bidir combo? - then do not count its other signal direction as exit */ 434 if (sig == SIGTYPE_COMBO && HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) { 435 /* at least one more exit */ 436 if (flags.Test(SigFlag::MultiExit) && 437 /* no green exit */ 438 (!flags.Test(SigFlag::Green) || 439 /* only one green exit, and it is this one - so all other exits are red */ 440 (!flags.Test(SigFlag::MultiGreen) && GetSignalStateByTrackdir(tile, ReverseTrackdir(trackdir)) == SIGNAL_STATE_GREEN))) { 441 newstate = SIGNAL_STATE_RED; ⋮ 443 } else { // entry, at least one exit, no green exit 444 if (IsPresignalEntry(tile, TrackdirToTrack(trackdir)) && flags.Test(SigFlag::Exit) && !flags.Test(SigFlag::Green)) newstate = SIGNAL_STATE_RED;
src/signal.cpp lines 432–446 at 14ec60f · upstream ↗ 330 /* if it is a presignal EXIT in OUR direction and we haven't found 2 green exits yes, do special check */ 331 if (!flags.Test(SigFlag::MultiGreen) && IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit 332 if (flags.Test(SigFlag::Exit)) flags.Set(SigFlag::MultiExit); // found two (or more) exits 333 flags.Set(SigFlag::Exit); // found at least one exit - allow for compiler optimizations 334 if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit 335 if (flags.Test(SigFlag::Green)) flags.Set(SigFlag::MultiGreen); 336 flags.Set(SigFlag::Green);
src/signal.cpp lines 329–338 at 14ec60f · upstream ↗ 448 /* only when the state changes */ 449 if (newstate != GetSignalStateByTrackdir(tile, trackdir)) { 450 if (IsPresignalExit(tile, TrackdirToTrack(trackdir))) { 451 /* for pre-signal exits, add block to the global set */ 452 DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir)); 453 _globset.Add(tile, exitdir); // do not check for full global set, first update all signals
src/signal.cpp lines 448–454 at 14ec60f · upstream ↗ 307inline bool IsPresignalEntry(Tile t, Track track) ⋮ 309 return GetSignalType(t, track) == SIGTYPE_ENTRY || GetSignalType(t, track) == SIGTYPE_COMBO; ⋮ 312inline bool IsPresignalExit(Tile t, Track track) ⋮ 314 return GetSignalType(t, track) == SIGTYPE_EXIT || GetSignalType(t, track) == SIGTYPE_COMBO;
src/rail_map.h lines 307–315 at 14ec60f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abcdA chain signal holds a train back not only when the block beyond it is occupied but also when the exit the train will take out of that block isn't free, so a train doesn't enter a junction or a shared single-track section unless it can leave it.Dev blogFriday Facts #81revised
How we know
While normal signal prevents train from entering the occupied block, chain signal prevents train from entering the block also when the exit isn't free. When more exits exist, the one relevant to the train path is taken into account.
The definition · archived copy ↗ With chain signals, this problem can be totally avoided by preventing the train from going to the shared section unless it can exit it.
Deadlock prevention · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abcdA path signal with no reservation behind it is red when a train is in its block, and also when the block holds a junction or has more than one signal leading into it; otherwise it is green. A train at a path signal reserves track to the next safe place to stop, and the signal turns green for it when the reservation succeeds. A safe place is a depot, a signal that isn't a path signal, the tile before a path signal on a single line ahead, or, where the search allows it, a dead end; a one-way path signal facing the other way counts only as such a dead end.CodeOpenTTD 15.3 source
How we know
422 /* Signal state of reserved path signals is handled by the reserve/unreserve process. */ 423 if (IsPbsSignal(sig) && (GetRailReservationTrackBits(tile) & TrackToTrackBits(track)) != TRACK_BIT_NONE) continue; ⋮ 425 /* determine whether the new state is red */ 426 if (flags.Test(SigFlag::Train)) { 427 /* train in the segment */ 428 newstate = SIGNAL_STATE_RED; 429 } else if (IsPbsSignal(sig) && flags.Any({SigFlag::Split, SigFlag::MultiEnter})) { 430 /* Turn path signals red if the segment has a junction or more than one way in. */ 431 newstate = SIGNAL_STATE_RED;
src/signal.cpp lines 422–431 at 14ec60f · upstream ↗ 381bool IsSafeWaitingPosition(const Train *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg) ⋮ 383 if (IsRailDepotTile(tile)) return true; ⋮ 385 if (IsTileType(tile, MP_RAILWAY)) { 386 /* For non-pbs signals, stop on the signal tile. */ 387 if (HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true; ⋮ 390 /* Check next tile. For performance reasons, we check for 90 degree turns ourself. */ 391 CFollowTrackRail ft(v, GetAllCompatibleRailTypes(v->railtypes)); ⋮ 393 /* End of track? */ 394 if (!ft.Follow(tile, trackdir)) { 395 /* Last tile of a terminus station is a safe position. */ 396 if (include_line_end) return true; ⋮ 399 /* Check for reachable tracks. */ ⋮
src/pbs.cpp lines 381–417 at 14ec60f · upstream ↗ 2747 if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir))) { 2748 do_track_reservation = true; 2749 changed_signal = true; 2750 SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir), SIGNAL_STATE_GREEN);
src/train_cmd.cpp lines 2747–2750 at 14ec60f · upstream ↗ 2759 if (do_track_reservation) { 2760 res_dest = ExtendTrainReservation(v, &tracks, &dest_enterdir); 2761 if (res_dest.tile == INVALID_TILE) { 2762 /* Reservation failed? */ 2763 if (mark_stuck) MarkTrainAsStuck(v); 2764 if (changed_signal) SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SIGNAL_STATE_RED);
src/train_cmd.cpp lines 2759–2764 at 14ec60f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abcIn Transport Tycoon Deluxe, a single-direction signal lets trains pass in only one direction. The player makes one by clicking a second or third time with the signal tool on the same spot; only one signal then shows on the track.ManualMicroProse, Transport Tycoon Deluxe manual
How we know
A single-direction signal will allow trains to pass in only one direction, enabling much more efficient double-track railways to be constructed. To build single-direction signals, click with the signal pointer a 2nd or 3rd time on the position where the signal is required.
p. 80, Signals · archived copy ↗ You will know that you have successfully planted a single-direction signal because only 1 signal will appear on the track.
p. 94 · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abTo run several trains on a single-track line, the Deluxe manual has the player build double-track passing loops at intervals, with signals on both tracks at both ends and room between them for the longest train. For three or more trains it advises against passing loops and recommends two parallel tracks with single-direction signals about every ten squares, facing opposite ways on each track.ManualMicroProse, Transport Tycoon Deluxe manual
How we know
Each passing loop should have signals on both tracks AND at both ends, with enough length between the signals on the double-track for your longest possible train to fit.
p. 94, Passing Loops · archived copy ↗ Running more than two trains on a single track railway with passing loops is not advisable. For three or more trains, build parallel tracks between the stations and add junctions and single-direction signals about every ten squares. On one track the signals should face in one direction and on the other track they should face in the opposite direction.
p. 94, Parallel Tracks · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abOpenTTD 15.3 has six kinds of rail signal: the block signal; three pre-signals (entry, exit and combo); the path signal; and the one-way path signal. Each comes as a light signal or a semaphore, which look different and behave alike.CodeOpenTTD 15.3 source
How we know
15/** Variant of the signal, i.e. how does the signal look? */ 16enum SignalVariant : uint8_t { 17 SIG_ELECTRIC = 0, ///< Light signal 18 SIG_SEMAPHORE = 1, ///< Old-fashioned semaphore signal
src/signal_type.h lines 15–19 at 14ec60f · upstream ↗ 22/** Type of signal, i.e. how does the signal behave? */ 23enum SignalType : uint8_t { 24 SIGTYPE_BLOCK = 0, ///< block signal 25 SIGTYPE_ENTRY = 1, ///< presignal block entry 26 SIGTYPE_EXIT = 2, ///< presignal block exit 27 SIGTYPE_COMBO = 3, ///< presignal inter-block 28 SIGTYPE_PBS = 4, ///< normal pbs signal 29 SIGTYPE_PBS_ONEWAY = 5, ///< no-entry signal
src/signal_type.h lines 22–29 at 14ec60f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abOpenTTD's entry pre-signal and Factorio's chain signal both look past the next block, but differently: the entry signal clears when any exit signal of the block ahead is green, whichever exit the train will take, while the chain signal looks only at the exit on the train's path. Friday Facts #81 (2015) says the chain signal resembles the "TTD pre signals" but works differently.Dev blogOpenTTD 15.3 source · Friday Facts #81
How we know
443 } else { // entry, at least one exit, no green exit 444 if (IsPresignalEntry(tile, TrackdirToTrack(trackdir)) && flags.Test(SigFlag::Exit) && !flags.Test(SigFlag::Green)) newstate = SIGNAL_STATE_RED;
src/signal.cpp lines 443–444 at 14ec60f · upstream ↗ chain signal prevents train from entering the block also when the exit isn't free. When more exits exist, the one relevant to the train path is taken into account.
The definition · archived copy ↗ The chain signals have some similarities with the TTD pre signals, but as they actually work differently, we also decided to call them differently to avoid confusion.
The chain signal in detail · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abcIn TTDPatch a pre-signal shows green if, and only if, one of the exit signals from the block behind it shows green, so trains enter the block only when there is a green exit and leave it at once. The manual names station entrances as a place where they are particularly useful.ManualTTDPatch manual, Pre-signalsrevised
How we know
The idea of pre-signals is quite simple: a pre-signal shows a green light if, and only if, one of the exit signals from the block behind it shows a green light. This means that trains will only enter the pre-signal block if there is a green exit. As a result, the block will only be entered for short times and trains will leave it immediately, allowing for a very efficient layout. This is particularly useful for station entrances
3.12 Pre-signals, the idea of pre-signals · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abcThe Transport Tycoon Deluxe manual describes only signals that protect the section ahead and single-direction ones. The manual of TTDPatch, a patch to the game, presents the pre-signal as a new type of signal its switches introduce, and calls the game's own signals "block signals".ManualMicroProse, Transport Tycoon Deluxe manual · TTDPatch manual, Pre-signals · TTDPatch manual, Pre-signals (detailed)revised
How we know
Each signal tells the train if the section of track ahead is clear (green signal), or occupied (red signal).
p. 94, Using Railway Signals · archived copy ↗ These switches introduce a new type of signal to TTD, the "pre-signal."
3.12 Pre-signals · archived copy ↗ The regular TTD signals are called "block signals", because they always indicate whether the block protected by them is occupied, no matter where the train actually wants to go.
4.2 Pre-signals (detailed) · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abTTDPatch's pre-signal and Factorio's chain signal both hold a train back until it can leave the block ahead, but they read different exits: a pre-signal turns green when any exit signal from the block is green, and a chain signal looks at the exit on the train's own path. Factorio's developers say the two "work differently", which is why the chain signal got its own name.ManualTTDPatch manual, Pre-signals · Friday Facts #81revised
How we know
a pre-signal shows a green light if, and only if, one of the exit signals from the block behind it shows a green light.
3.12 Pre-signals, the idea of pre-signals · archived copy ↗ While normal signal prevents train from entering the occupied block, chain signal prevents train from entering the block also when the exit isn't free. When more exits exist, the one relevant to the train path is taken into account.
The definition · archived copy ↗ The chain signals have some similarities with the TTD pre signals, but as they actually work differently, we also decided to call them differently to avoid confusion.
The chain signal in detail · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^ abcRail signals cut the track into blocks, and a train may enter a block only when no other train is in it; signals keep trains from colliding or blocking each other, while the train already knows its route.Dev blogFriday Facts #81
How we know
In Factorio, trains know exactly where to go, and signals are mainly just preventing them from crashing to each other or blocking each other.
The chain signal in detail · archived copy ↗ While normal signal prevents train from entering the occupied block
The definition · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abcA rail signal shows red, yellow or green and can send each as a circuit signal; a chain signal adds a fourth state, blue.Codefactorio-data 2.1.23
How we know
1667 default_red_output_signal = {type = "virtual", name = "signal-red"}, 1668 default_orange_output_signal = {type = "virtual", name = "signal-yellow"}, 1669 default_green_output_signal = {type = "virtual", name = "signal-green"}
base/prototypes/entity/trains.lua lines 1667–1669 at 39dbd0f · upstream ↗ 1691 default_red_output_signal = {type = "virtual", name = "signal-red"}, 1692 default_orange_output_signal = {type = "virtual", name = "signal-yellow"}, 1693 default_green_output_signal = {type = "virtual", name = "signal-green"}, 1694 default_blue_output_signal = {type = "virtual", name = "signal-blue"}
base/prototypes/entity/trains.lua lines 1691–1694 at 39dbd0f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abA train that would have to stop at a red signal tries to find a new path; that is the only part signals play in pathfinding beyond the penalties below.Dev blogFriday Facts #81
How we know
The only role the signals have in the path finding is, that whenever would the train have to stop because of the red signal, it tries to recalculate its path.
The chain signal in detail · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abThe pathfinder treats a signal that faces only against the train as a dead end, unless it is a two-way path signal: that one a train may pass from behind, at a cost of 15 tiles of track while it is within the first ten signals of the path.CodeOpenTTD 15.3 source
How we know
176 int cost = 0; 177 /* if there is one-way signal in the opposite direction, then it is not our way */ 178 if (IsTileType(tile, MP_RAILWAY)) { 179 bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir)); 180 bool has_signal_along = HasSignalOnTrackdir(tile, trackdir); 181 if (has_signal_against && !has_signal_along && IsOnewaySignal(tile, TrackdirToTrack(trackdir))) { 182 /* one-way signal in opposite direction */ 183 n.segment->end_segment_reason.Set(EndSegmentReason::DeadEnd);
src/pathfinder/yapf/yapf_costrail.hpp lines 176–183 at 14ec60f · upstream ↗ 237 if (has_signal_against && IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) { 238 cost += n.num_signals_passed < Yapf().PfGetSettings().rail_look_ahead_max_signals ? Yapf().PfGetSettings().rail_pbs_signal_back_penalty : 0;
src/pathfinder/yapf/yapf_costrail.hpp lines 237–239 at 14ec60f · upstream ↗ 318inline bool IsOnewaySignal(Tile t, Track track) ⋮ 320 return GetSignalType(t, track) != SIGTYPE_PBS;
src/rail_map.h lines 318–321 at 14ec60f · upstream ↗ 261var = pf.yapf.rail_pbs_signal_back_penalty ⋮ 264def = 15 * YAPF_TILE_LENGTH
src/table/settings/pathfinding_settings.ini lines 261–264 at 14ec60f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abOpenTTD's changelog lists "path based signalling" in 0.7.0-beta1 (16 February 2009); 0.7.0 came out on 1 April 2009.DeveloperOpenTTD 15.3 source
How we know
5411### 0.7.0-beta1 (2009-02-16)changelog.md line 5411 at 14ec60f · upstream ↗ 5455- Feature: Path based signalling (r13926-13967)changelog.md line 5455 at 14ec60f · upstream ↗ 5296### 0.7.0 (2009-04-01)changelog.md line 5296 at 14ec60f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^ abAny signal that isn't a path signal holding a reservation turns red while a train is anywhere in the block it leads into; a train inside a depot doesn't count. A block runs from signal to signal through junctions, stations, level crossings, tunnels and bridges, over the owning company's track only.CodeOpenTTD 15.3 sourcerevised
How we know
422 /* Signal state of reserved path signals is handled by the reserve/unreserve process. */ 423 if (IsPbsSignal(sig) && (GetRailReservationTrackBits(tile) & TrackToTrackBits(track)) != TRACK_BIT_NONE) continue; ⋮ 425 /* determine whether the new state is red */ 426 if (flags.Test(SigFlag::Train)) { 427 /* train in the segment */ 428 newstate = SIGNAL_STATE_RED;
src/signal.cpp lines 422–428 at 14ec60f · upstream ↗ 194static bool IsTrainAndNotInDepot(const Vehicle *v) ⋮ 196 return v->type == VEH_TRAIN && Train::From(v)->track != TRACK_BIT_DEPOT;
src/signal.cpp lines 194–197 at 14ec60f · upstream ↗ 279 if (GetTileOwner(tile) != owner) continue; // do not propagate signals on others' tiles (remove for tracksharing)src/signal.cpp line 279 at 14ec60f · upstream ↗ 302 /* If no train detected yet, and there is not no train -> there is a train -> set the flag */ 303 if (!flags.Test(SigFlag::Train) && EnsureNoTrainOnTrackBits(tile, tracks).Failed()) flags. Set(SigFlag::Train); 304 } else { 305 if (tracks_masked == TRACK_BIT_NONE) continue; // no incidating track 306 if (!flags.Test(SigFlag::Train) && HasVehicleOnTile(tile, IsTrainAndNotInDepot)) flags.Set(SigFlag::Train);
src/signal.cpp lines 302–306 at 14ec60f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^Transport Tycoon Deluxe's signals and Factorio's ordinary rail signals keep the same rule: a signal lets a train into the section beyond only when no other train is in it.ManualMicroProse, Transport Tycoon Deluxe manual · Friday Facts #81revised
How we know
the principle of the railway signalling system is to prevent trains from colliding by preventing more than one train from entering any one section of railway track.
p. 94, Using Railway Signals, The Basics · archived copy ↗ While normal signal prevents train from entering the occupied block
The definition · archived copy ↗ In Factorio, trains know exactly where to go, and signals are mainly just preventing them from crashing to each other or blocking each other.
The chain signal in detail · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^OpenTTD's block signal does what Factorio's rail signal does: it keeps a train out of a block another train is in.Dev blogOpenTTD 15.3 source · Friday Facts #81
How we know
426 if (flags.Test(SigFlag::Train)) { 427 /* train in the segment */ 428 newstate = SIGNAL_STATE_RED;
src/signal.cpp lines 426–428 at 14ec60f · upstream ↗ While normal signal prevents train from entering the occupied block
The definition · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^For each of the first ten signals along a route, a red block or pre-signal adds a cost that falls with distance: 500 for the first, then 405, 320, 245, 180, 125, 80, 45, 20 and 5; green signals and red path signals add none. If the first signal is red, a block or entry signal adds 1000 more and an exit or combo signal 10000. With the rail_firstred_twoway_eol setting on, as it is by default, a red first block or pre-signal that is two-way, met after the route has had a choice of track, ends that branch of the search as a dead end.CodeOpenTTD 15.3 sourcerevised
How we know
62 int p0 = Yapf().PfGetSettings().rail_look_ahead_signal_p0; 63 int p1 = Yapf().PfGetSettings().rail_look_ahead_signal_p1; 64 int p2 = Yapf().PfGetSettings().rail_look_ahead_signal_p2; 65 this->sig_look_ahead_costs.clear(); 66 this->sig_look_ahead_costs.reserve(Yapf().PfGetSettings().rail_look_ahead_max_signals); 67 for (uint i = 0; i < Yapf().PfGetSettings().rail_look_ahead_max_signals; i++) { 68 this->sig_look_ahead_costs.push_back(p0 + i * (p1 + i * p2));
src/pathfinder/yapf/yapf_costrail.hpp lines 62–69 at 14ec60f · upstream ↗ 192 int look_ahead_cost = (n.num_signals_passed < this->sig_look_ahead_costs.size()) ? this->sig_look_ahead_costs[n.num_signals_passed] : 0; 193 if (sig_state != SIGNAL_STATE_RED) { 194 /* green signal */ 195 n.flags_u.flags_s.last_signal_was_red = false; 196 /* negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal */ 197 if (look_ahead_cost < 0) { 198 /* add its negation to the cost */ 199 cost -= look_ahead_cost; ⋮ 201 } else { 202 /* we have a red signal in our direction 203 * was it first signal which is two-way? */ 204 if (!IsPbsSignal(sig_type) && Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.choice_seen && has_signal_against && n.num_signals_passed == 0) { ⋮
src/pathfinder/yapf/yapf_costrail.hpp lines 192–229 at 14ec60f · upstream ↗ 111var = pf.yapf.rail_firstred_twoway_eol ⋮ 113def = true ⋮ 117var = pf.yapf.rail_firstred_penalty ⋮ 120def = 10 * YAPF_TILE_LENGTH ⋮ 126var = pf.yapf.rail_firstred_exit_penalty ⋮ 129def = 100 * YAPF_TILE_LENGTH
src/table/settings/pathfinding_settings.ini lines 111–129 at 14ec60f · upstream ↗ 207var = pf.yapf.rail_look_ahead_max_signals ⋮ 210def = 10 ⋮ 216var = pf.yapf.rail_look_ahead_signal_p0 ⋮ 219def = 500 ⋮ 225var = pf.yapf.rail_look_ahead_signal_p1 ⋮ 228def = -100 ⋮ 234var = pf.yapf.rail_look_ahead_signal_p2 ⋮ 237def = 5
src/table/settings/pathfinding_settings.ini lines 207–237 at 14ec60f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^Both games' train pathfinders count a red signal or a busy block for less the farther ahead it lies: Factorio divides the penalty for an occupied block by how many blocks it is from the start, and OpenTTD's default penalty for a red signal that isn't a path signal falls from 500 at the first signal to 5 at the tenth and stops.WikiFactorio Wiki: Train path finder · OpenTTD 15.3 sourcerevised
How we know
When the rail block is occupied by a train -> Add a penalty of 2 * length of the block divided by the number of blocks from the start, so the far away occupied paths don't matter much.
Path finding penalties · archived copy ↗ 214 /* look-ahead signal penalty */ 215 if (!IsPbsSignal(sig_type) && look_ahead_cost > 0) { 216 /* add the look ahead penalty only if it is positive */ 217 cost += look_ahead_cost;
src/pathfinder/yapf/yapf_costrail.hpp lines 214–218 at 14ec60f · upstream ↗ 67 for (uint i = 0; i < Yapf().PfGetSettings().rail_look_ahead_max_signals; i++) { 68 this->sig_look_ahead_costs.push_back(p0 + i * (p1 + i * p2));
src/pathfinder/yapf/yapf_costrail.hpp lines 67–68 at 14ec60f · upstream ↗ 207var = pf.yapf.rail_look_ahead_max_signals ⋮ 210def = 10 ⋮ 216var = pf.yapf.rail_look_ahead_signal_p0 ⋮ 219def = 500 ⋮ 225var = pf.yapf.rail_look_ahead_signal_p1 ⋮ 228def = -100 ⋮ 234var = pf.yapf.rail_look_ahead_signal_p2 ⋮ 237def = 5
src/table/settings/pathfinding_settings.ini lines 207–237 at 14ec60f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^Retro Gamer's 2015 sidebar on Transport Tycoon Deluxe says the complete version came with improved signals for complex train systems, and a world editor.InterviewRetro Gamer 138, Chris Sawyer interview
How we know
The complete version of TTD came with improved signals - for complex train systems - as well as a world editor to build and make your own scenarios.
p. 97, Transport Tycoon Deluxe 1995 sidebar · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^OpenTTD's changelog lists "preliminary presignal support" in 0.1.1 (14 March 2004) and pre-signals, set by Ctrl-clicking an existing signal, in 0.2 (3 April 2004).DeveloperOpenTTD 15.3 source
How we know
7825### 0.1.1 (2004-03-14) ⋮ 7827- Feature: Preliminary presignal support
changelog.md lines 7825–7827 at 14ec60f · upstream ↗ 7693### 0.2 (2004-04-03) ⋮ 7695- Feature: Autoscroll (only works to left/right) 7696- Feature: Train checkpoints, instead of TTDPatch's nonstop handling 7697- Feature: TTDPatch compatible nonstop handling 7698- Feature: Refresh rate configuration setting 7699- Feature: Town directory sorting options 7700- Feature: Pre-signals (ctrl-click on existing signals to change signal type)
changelog.md lines 7693–7700 at 14ec60f · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
- ^OpenTTD's About page says many of its features were inspired by TTDPatch, and others are original. It doesn't name which.PublisherOpenTTD, "About"revised
How we know
Many features were inspired by TTDPatch while others are original.
Features · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Revised after a check by a second AI model, 2026-10-26.
- ^Friday Facts #81 (2015) says Factorio's chain signals have some similarities with "TTD pre signals" but work differently, so they were given a different name. The TTDPatch manual describes pre-signals as a new type of signal that its switches introduce to Transport Tycoon Deluxe; who had them first isn't shown here.Dev blogFriday Facts #81 · TTDPatch manual, Pre-signals
How we know
The chain signals have some similarities with the TTD pre signals, but as they actually work differently, we also decided to call them differently to avoid confusion.
The chain signal in detail · archived copy ↗ These switches introduce a new type of signal to TTD, the "pre-signal."
3.12 Pre-signals · archived copy ↗ Checked against the evidence by its author, an AI model, 2026-10-26.
Checked by a second AI model, 2026-10-26.
Sources
Code
- OpenTTD contributors. 2026. “OpenTTD source code, release 15.3.” Commit 14ec60f. OpenTTD/OpenTTD @ 14ec60f · licence, all uses
- Software, Wube. 2026. “factorio-data: Factorio's Lua prototype definitions, version 2.1.23.” Commit 39dbd0f. wube/factorio-data @ 39dbd0f · licence, all uses
Primary
- Haslam, Rik. 1995. “Transport Tycoon Deluxe instruction manual (MicroProse, 1995).” MicroProse Ltd. original · archived · licence, all uses
- Software, Wube. 2015. “Friday Facts #81 - Chain signals.” Factorio blog. Wube Software. original · archived · licence, all uses
- TTDPatch team. n.d. “TTDPatch Manual: Pre-signals.” ttdpatch.net. original · archived · licence, all uses
- TTDPatch team. n.d. “TTDPatch Manual: Pre-signals (detailed).” ttdpatch.net. original · archived · licence, all uses
- Barnes, Adam. 2015. “In the chair with Chris Sawyer — Retro Gamer 138.” Retro Gamer, issue 138, February 2015. Imagine Publishing. original · archived · licence, all uses
- OpenTTD contributors. n.d. “OpenTTD — About.” OpenTTD. original · archived · licence, all uses
Secondary
- Official Factorio Wiki contributors. n.d. “Train path finder.” Official Factorio Wiki. original · archived · licence, all uses