diff --git a/docs/landscape.html b/docs/landscape.html
index 9493f35..979c3ce 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -376,30 +376,40 @@
          </tr>
         </table>
         <ul>
-         <li>m2 bit 6: Signal 0 and 1: set = semaphore signals, clear = light signals</li>
-         <li>m2 bit 2: Signal 2 and 3: set = semaphore signals, clear = light signals</li>
-         <li>m2 bits 5..4: type of signal 0 and 1 (same values as m2 bits 1..0)</li>
-         <li>m2 bits 1..0: type of signal 2 and 3
+         <li>m2 bit 7: Signal 0 and 1: set = semaphore signals, clear = light signals</li>
+         <li>m2 bit 3: Signal 2 and 3: set = semaphore signals, clear = light signals</li>
+         <li>m2 bits 6..4: type of signal 0 and 1 (same values as m2 bits 2..0)</li>
+         <li>m2 bits 2..0: type of signal 2 and 3
           <table>
            <tr>
-            <td nowrap="nowrap" valign="top"><tt>00</tt>: </td>
+            <td nowrap="nowrap" valign="top"><tt>000</tt>: </td>
             <td align="left">normal signals</td>
            </tr>
 
            <tr>
-            <td nowrap="nowrap" valign="top"><tt>01</tt>: </td>
+            <td nowrap="nowrap" valign="top"><tt>001</tt>: </td>
             <td align="left">pre-signals</td>
            </tr>
 
            <tr>
-            <td nowrap="nowrap" valign="top"><tt>10</tt>: </td>
+            <td nowrap="nowrap" valign="top"><tt>010</tt>: </td>
             <td align="left">exit-signals</td>
            </tr>
 
            <tr>
-            <td nowrap="nowrap" valign="top"><tt>11</tt>: </td>
+            <td nowrap="nowrap" valign="top"><tt>011</tt>: </td>
             <td align="left">combo-signals</td>
            </tr>
+
+           <tr>
+            <td nowrap="nowrap" valign="top"><tt>100</tt>: </td>
+            <td align="left">pbs signals</td>
+           </tr>
+
+           <tr>
+            <td nowrap="nowrap" valign="top"><tt>101</tt>: </td>
+            <td align="left">no-entry signals</td>
+           </tr>
           </table>
          </li>
 
@@ -407,6 +417,39 @@
          <li>m4 bits 7..4: bit clear = signal 3..0 shows red</li>
         </ul>
        </li>
+       <li>m2 bits 8..10: track reserved for pbs
+         <table>
+          <tr>
+            <td><tt>0</tt>&nbsp; </td>
+            <td>not reserved</td>
+          </tr>
+          <tr>
+            <td><tt>1</tt>&nbsp; </td>
+            <td>X direction</td>
+          </tr>
+          <tr>
+            <td><tt>2</tt>&nbsp; </td>
+            <td>Y direction</td>
+          </tr>
+          <tr>
+            <td><tt>3</tt>&nbsp; </td>
+            <td>north corner (W-E)</td>
+          </tr>
+          <tr>
+            <td><tt>4</tt>&nbsp; </td>
+            <td>south corner (W-E)</td>
+          </tr>
+          <tr>
+            <td><tt>5</tt>&nbsp; </td>
+            <td>west corner (N-S)</td>
+          </tr>
+          <tr>
+            <td><tt>6</tt>&nbsp; </td>
+            <td>east corner (N-S)</td>
+          </tr>
+         </table>
+       </li>
+       <li>m2 bit 11: opposite track is reserved, too</li>
       </ul>
      </li>
      <li>m5 bit 7 set, bit 6 clear: checkpoint
@@ -425,6 +468,7 @@
          </tr>
         </table>
        </li>
+       <li>m5 bit 4: pbs reservation state</li>
       </ul>
      </li>
      <li>m5 bit 7 set, bit 6 set: railway depot
@@ -452,6 +496,7 @@
          </tr>
         </table>
        </li>
+       <li>m5 bit 4: pbs reservation state</li>
       </ul>
      </li>
      <li>m6 bits 7..6 : Possibility of a bridge above, in the <a href="#bridge_direction">direction specified</a></li>
@@ -582,6 +627,7 @@
          </li>
           <li>m4 bit 5: set if crossing lights are on</li>
          <li>m4 bits 4..0: <a href="#OwnershipInfo">owner</a> of the road type 0 (normal road)</li>
+         <li>m5 bit 4: pbs reservation state</li>
         </ul>
        </li>
       </ul>
@@ -898,6 +944,7 @@
      </li>
      <li>m6 bits 5..3: the station type (rail, airport, truck, bus, oilrig, dock, buoy)</li>
      <li>m6 bit 2: 1 when a drive through road stop is built over a town owned road, otherwise 0</li>
+     <li>m6 bit 2: pbs reservation state for railway stations</li>
      <li>m6 bits 1..0 : <a href="#tropic_zone">Tropic zone definition</a></li>
      <li>m7: animation frame</li>
     </ul>
@@ -1369,6 +1416,7 @@
      <li>m3 bits 3..0: <a href="#TrackType">track type</a> for railway</li>
      <li>m3 bits 2..0: present road types for road</li>
      <li>m4 bit 7 set = on snow or desert</li>
+     <li>m5 bit 4: pbs reservation state for railway</li>
      <li>m5 bits 7 clear: tunnel entrance/exit</li>
      <li>m5 bit 7 set: bridge ramp
       <ul>
diff --git a/docs/landscape_grid.html b/docs/landscape_grid.html
index f47b7f4..2b2cda2 100644
--- a/docs/landscape_grid.html
+++ b/docs/landscape_grid.html
@@ -90,7 +90,7 @@ the array so you can quickly see what is used and what is not.
       <td class="caption">rail</td>
       <td class="bits">XXXX XXXX</td>
       <td class="bits"><span class="option">~~~</span>X XXXX</td>
-      <td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td>
+      <td class="bits"><span class="free">OOOO</span> XXXX <span class="free">OOOO OOOO</span></td>
       <td class="bits"><span class="free">OOOO</span> <span class="option">~~</span>XX</td>
       <td class="bits"><span class="free">OOOO</span> XXXX</td>
       <td class="bits">XXXX XXXX</td>
@@ -101,7 +101,7 @@ the array so you can quickly see what is used and what is not.
       <td class="caption">rail with signals</td>
       <td class="bits">-inherit-</td>
       <td class="bits">-inherit-</td>
-      <td class="bits"><span class="free">OOOO OOOO O</span>XXX <span class="free">O</span>XXX</td>
+      <td class="bits"><span class="free">OOOO</span> XXXX XXXX XXXX</td>
       <td class="bits">XXXX <span class="option">~~</span>XX</td>
       <td class="bits">XXXX XXXX</td>
       <td class="bits">-inherit-</td>
@@ -115,7 +115,7 @@ the array so you can quickly see what is used and what is not.
       <td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td>
       <td class="bits"><span class="free">OOOO</span> <span class="option">~~</span>XX</td>
       <td class="bits"><span class="free">OOOO</span> XXXX</td>
-      <td class="bits">XX<span class="free">OO OO</span>XX</td>
+      <td class="bits">XX<span class="free">O</span>X <span class="free">O</span>XXX</td>
       <td class="bits">XX<span class="free">OO OO</span>XX</td>
       <td class="bits"><span class="free">OOOO OOOO</span></td>
     </tr>
@@ -126,7 +126,7 @@ the array so you can quickly see what is used and what is not.
       <td class="bits">XXXX XXXX XXXX XXXX</td>
       <td class="bits"><span class="free">OOOO</span> <span class="option">~~</span>XX</td>
       <td class="bits"><span class="free">OOOO</span> XXXX</td>
-      <td class="bits">XX<span class="free">OO OOO</span>X</td>
+      <td class="bits">XX<span class="free">O</span>X <span class="free">OOO</span>X</td>
       <td class="bits">XX<span class="free">OO OO</span>XX</td>
       <td class="bits"><span class="free">OOOO OOOO</span></td>
     </tr>
@@ -149,7 +149,7 @@ the array so you can quickly see what is used and what is not.
       <td class="bits">-inherit-</td>
       <td class="bits">XXXX <span class="option">~~</span>XX</td>
       <td class="bits"><span class="free">O</span>XXX XXXX</td>
-      <td class="bits">XX<span class="free">OO</span> XXXX</td>
+      <td class="bits">XX<span class="free">O</span>X XXXX</td>
       <td class="bits">XX<span class="free">OO OO</span>XX</td>
       <td class="bits">-inherit-</td>
     </tr>
@@ -197,7 +197,7 @@ the array so you can quickly see what is used and what is not.
       <td class="bits">XXXX <span class="option">~~</span>XX</td>
       <td class="bits">XXXX XXXX</td>
       <td class="bits">XXXX XXXX</td>
-      <td class="bits"><span class="free">OO</span>XX X<span class="free">O</span>XX</td>
+      <td class="bits"><span class="free">OO</span>XX XXXX</td>
       <td class="bits">XXXX XXXX</td>
     </tr>
     <tr>
@@ -310,7 +310,7 @@ the array so you can quickly see what is used and what is not.
       <td class="bits"><span class="free">OOOO OOOO OOOO OOOO</span></td>
       <td class="bits"><span class="free">OOOO</span> <span class="option">~</span><span class="abuse">XXX</span></td>
       <td class="bits">X<span class="free">OOO OOOO</span></td>
-      <td class="bits">X<span class="free">OOO</span> <span class="option">~</span>XXX</td>
+      <td class="bits">X<span class="free">OO</span>X <span class="option">~</span>XXX</td>
       <td class="bits">XX<span class="free">OO OO</span>XX</td>
       <td class="bits"><span class="free">OOOO OOOO</span></td>
     </tr>
@@ -321,7 +321,7 @@ the array so you can quickly see what is used and what is not.
       <td class="bits"><span class="free">OOOO OOOO</span> XXXX <span class="free">OOOO</span></td>
       <td class="bits"><span class="free">OOOO</span> <span class="option">~</span><span class="abuse">XXX</span></td>
       <td class="bits">X<span class="free">OOO OOOO</span></td>
-      <td class="bits">X<span class="free">OOO</span> <span class="option">~</span>XXX</td>
+      <td class="bits">X<span class="free">OO</span>X <span class="option">~</span>XXX</td>
       <td class="bits">XX<span class="free">OO OO</span>XX</td>
       <td class="bits"><span class="free">OOOO OOOO</span></td>
     </tr>
diff --git a/projects/openttd_vs80.vcproj b/projects/openttd_vs80.vcproj
index ef5e2f9..6b1b543 100644
--- a/projects/openttd_vs80.vcproj
+++ b/projects/openttd_vs80.vcproj
@@ -672,6 +672,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\pbs.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\players.cpp"
 				>
 			</File>
@@ -1308,6 +1312,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\pbs.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\player_base.h"
 				>
 			</File>
diff --git a/projects/openttd_vs90.vcproj b/projects/openttd_vs90.vcproj
index 2e6224d..85ad78a 100644
--- a/projects/openttd_vs90.vcproj
+++ b/projects/openttd_vs90.vcproj
@@ -669,6 +669,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\pbs.cpp"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\players.cpp"
 				>
 			</File>
@@ -1305,6 +1309,10 @@
 				>
 			</File>
 			<File
+				RelativePath=".\..\src\pbs.h"
+				>
+			</File>
+			<File
 				RelativePath=".\..\src\player_base.h"
 				>
 			</File>
diff --git a/source.list b/source.list
index 4e873c2..275318b 100644
--- a/source.list
+++ b/source.list
@@ -59,6 +59,7 @@ os_timer.cpp
 	ottdres.rc
 #end
 pathfind.cpp
+pbs.cpp
 players.cpp
 queue.cpp
 rail.cpp
@@ -252,6 +253,7 @@ order_func.h
 order_type.h
 core/overflowsafe_type.hpp
 pathfind.h
+pbs.h
 player_base.h
 player_face.h
 player_func.h
diff --git a/src/debug.cpp b/src/debug.cpp
index fb1e28a..c4970aa 100644
--- a/src/debug.cpp
+++ b/src/debug.cpp
@@ -31,6 +31,7 @@ int _debug_freetype_level;
 int _debug_sl_level;
 int _debug_station_level;
 int _debug_gamelog_level;
+int _debug_pbs_level;
 
 
 struct DebugLevel {
@@ -56,6 +57,7 @@ struct DebugLevel {
 	DEBUG_LEVEL(sl),
 	DEBUG_LEVEL(station),
 	DEBUG_LEVEL(gamelog),
+	DEBUG_LEVEL(pbs),
 	};
 #undef DEBUG_LEVEL
 
diff --git a/src/debug.h b/src/debug.h
index 4048c16..127021d 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -48,6 +48,7 @@
 	extern int _debug_sl_level;
 	extern int _debug_station_level;
 	extern int _debug_gamelog_level;
+	extern int _debug_pbs_level;
 
 	void CDECL debug(const char *dbg, ...);
 	void CDECL debug_print(const char *dbg, const char *buf);
diff --git a/src/genworld_gui.cpp b/src/genworld_gui.cpp
index e080973..7296346 100644
--- a/src/genworld_gui.cpp
+++ b/src/genworld_gui.cpp
@@ -477,7 +477,7 @@ struct GenerateLandscapeWindow : public QueryStringBaseWindow {
 			* (use random seed) it should not be possible to be
 			* entered into the input field; the generate seed
 			* button can be used instead. */
-		_settings_newgame.game_creation.generation_seed = minu(strtoul(this->edit_str_buf, NULL, 10), MAX_UVALUE(uint32) - 1);
+		_settings_newgame.game_creation.generation_seed = minu(strtoul(this->edit_str_buf, NULL, sizeof(this->edit_str_buf) - 1), MAX_UVALUE(uint32) - 1);
 		return state;
 	}
 
diff --git a/src/gfx.cpp b/src/gfx.cpp
index 676e68f..ac9e32f 100644
--- a/src/gfx.cpp
+++ b/src/gfx.cpp
@@ -689,7 +689,7 @@ void DrawSprite(SpriteID img, SpriteID pal, int x, int y, const SubSprite *sub)
 	}
 }
 
-static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub)
+static inline void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode, const SubSprite *sub)
 {
 	const DrawPixelInfo *dpi = _cur_dpi;
 	Blitter::BlitterParams bp;
@@ -715,8 +715,8 @@ static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode,
 	bp.height = UnScaleByZoom(sprite->height - clip_top - clip_bottom, dpi->zoom);
 	bp.top = 0;
 	bp.left = 0;
-	bp.skip_left = UnScaleByZoomLower(clip_left, dpi->zoom);
-	bp.skip_top = UnScaleByZoomLower(clip_top, dpi->zoom);
+	bp.skip_left = UnScaleByZoom(clip_left, dpi->zoom);
+	bp.skip_top = UnScaleByZoom(clip_top, dpi->zoom);
 
 	x += ScaleByZoom(bp.skip_left, dpi->zoom);
 	y += ScaleByZoom(bp.skip_top, dpi->zoom);
@@ -767,9 +767,6 @@ static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode,
 		if (bp.width <= 0) return;
 	}
 
-	assert(bp.skip_left + bp.width <= UnScaleByZoom(sprite->width, dpi->zoom));
-	assert(bp.skip_top + bp.height <= UnScaleByZoom(sprite->height, dpi->zoom));
-
 	BlitterFactoryBase::GetCurrentBlitter()->Draw(&bp, mode, dpi->zoom);
 }
 
diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp
index 1338fe9..ae2322f 100644
--- a/src/industry_cmd.cpp
+++ b/src/industry_cmd.cpp
@@ -337,7 +337,8 @@ static Foundation GetFoundation_Industry(TileIndex tile, Slope tileh)
 	IndustryGfx gfx = GetIndustryGfx(tile);
 
 	/* For NewGRF industry tiles we might not be drawing a foundation. We need to
-	 * account for this, as other structures should
+	 * account for this, otherwise we might be applying a FOUNDATION_LEVELED
+	 * on a steep slope which is not allowed. Furthermore other structures should
 	 * draw the wall of the foundation in this case.
 	 */
 	if (gfx >= NEW_INDUSTRYTILEOFFSET) {
diff --git a/src/landscape.cpp b/src/landscape.cpp
index 982f02a..2216a38 100644
--- a/src/landscape.cpp
+++ b/src/landscape.cpp
@@ -79,9 +79,8 @@ uint ApplyFoundationToSlope(Foundation f, Slope *s)
 	if (!IsFoundation(f)) return 0;
 
 	if (IsLeveledFoundation(f)) {
-		uint dz = TILE_HEIGHT + (IsSteepSlope(*s) ? TILE_HEIGHT : 0);
 		*s = SLOPE_FLAT;
-		return dz;
+		return TILE_HEIGHT;
 	}
 
 	if (f != FOUNDATION_STEEP_BOTH && IsNonContinuousFoundation(f)) {
@@ -403,9 +402,6 @@ void DrawFoundation(TileInfo *ti, Foundation f)
 
 			AddSortableSpriteToDraw(inclined_base + inclined, PAL_NONE, ti->x, ti->y, 16, 16, 1, ti->z);
 			OffsetGroundSprite(31, 9);
-		} else if (IsLeveledFoundation(f)) {
-			AddSortableSpriteToDraw(leveled_base + SlopeWithOneCornerRaised(highest_corner), PAL_NONE, ti->x, ti->y, 16, 16, 7, ti->z - TILE_HEIGHT);
-			OffsetGroundSprite(31, 1);
 		} else if (f == FOUNDATION_STEEP_LOWER) {
 			/* one corner raised */
 			OffsetGroundSprite(31, 1);
diff --git a/src/lang/english.txt b/src/lang/english.txt
index e01c4dc..9dbd07c 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -986,6 +986,7 @@ STR_CANT_SHARE_ORDER_LIST                                       :{WHITE}Can't sh
 STR_CANT_COPY_ORDER_LIST                                        :{WHITE}Can't copy order list...
 STR_END_OF_SHARED_ORDERS                                        :{SETX 10}- - End of Shared Orders - -
 
+STR_TRAIN_IS_STUCK                                              :{WHITE}Train {COMMA} can't find a path to continue.
 STR_TRAIN_IS_LOST                                               :{WHITE}Train {COMMA} is lost.
 STR_TRAIN_IS_UNPROFITABLE                                       :{WHITE}Train {COMMA}'s profit last year was {CURRENCY}
 STR_EURO_INTRODUCE                                              :{BLACK}{BIGFONT}European Monetary Union!{}{}The Euro is introduced as the sole currency for everyday transactions in your country!
@@ -1134,6 +1135,7 @@ STR_CONFIG_PATCHES_DEFAULT_RAIL_TYPE_MAGLEV                     :Maglev
 STR_CONFIG_PATCHES_DEFAULT_RAIL_TYPE_FIRST                      :First available
 STR_CONFIG_PATCHES_DEFAULT_RAIL_TYPE_LAST                       :Last available
 STR_CONFIG_PATCHES_DEFAULT_RAIL_TYPE_MOST_USED                  :Most used
+STR_CONFIG_PATCHES_SHOW_TRACK_RESERVATION                       :{LTBLUE}Show reserved tracks: {ORANGE}{STRING1}
 
 STR_CONFIG_PATCHES_ALWAYS_BUILD_INFRASTRUCTURE                  :{LTBLUE}Show building tools when no suitable vehicles are available: {ORANGE}{STRING1}
 STR_CONFIG_PATCHES_MAX_TRAINS                                   :{LTBLUE}Max trains per player: {ORANGE}{STRING1}
@@ -1169,6 +1171,14 @@ STR_CONFIG_PATCHES_ALLOW_SHARES                                 :{LTBLUE}Allow b
 STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY                         :{LTBLUE}When dragging, place signals every: {ORANGE}{STRING1} tile(s)
 STR_CONFIG_PATCHES_SEMAPHORE_BUILD_BEFORE_DATE                  :{LTBLUE}Automatically build semaphores before: {ORANGE}{STRING1}
 STR_CONFIG_PATCHES_ENABLE_SIGNAL_GUI                            :{LTBLUE}Enable the signal GUI: {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_DEFAULT_SIGNAL_TYPE                          :{LTBLUE}Signal type to build by default: {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_DEFAULT_SIGNAL_NORMAL                        :Normal
+STR_CONFIG_PATCHES_DEFAULT_SIGNAL_PBS                           :Advanced
+STR_CONFIG_PATCHES_DEFAULT_SIGNAL_PBSOWAY                       :One-way advanced
+STR_CONFIG_PATCHES_CYCLE_SIGNAL_TYPES                           :{LTBLUE}Cycle through signal types: {ORANGE}{STRING1}
+STR_CONFIG_PATCHES_CYCLE_SIGNAL_NORMAL                          :Normal only
+STR_CONFIG_PATCHES_CYCLE_SIGNAL_PBS                             :Advanced only
+STR_CONFIG_PATCHES_CYCLE_SIGNAL_ALL                             :All
 
 STR_CONFIG_PATCHES_TOWN_LAYOUT_INVALID                          :{WHITE}The town layout "no more roads" isn't valid in the scenario editor
 STR_CONFIG_PATCHES_TOWN_LAYOUT                                  :{LTBLUE}Select town-road layout: {ORANGE}{STRING1}
@@ -1291,9 +1301,6 @@ STR_CANT_BUILD_LOCKS                                            :{WHITE}Can't bu
 STR_BUILD_LOCKS_TIP                                             :{BLACK}Build locks
 STR_LANDINFO_LOCK                                               :Lock
 
-STR_CANT_PLACE_RIVERS                                           :{WHITE}Can't place rivers here...
-STR_LANDINFO_RIVER                                              :River
-
 STR_BUOY_IS_IN_USE                                              :{WHITE}...buoy is in use!
 
 STR_LANDINFO_COORDS                                             :{BLACK}Coordinates: {LTBLUE}{NUM}x{NUM}x{NUM} ({STRING})
@@ -1665,12 +1672,23 @@ STR_RAILROAD_TRACK_WITH_NORMAL_SIGNALS                          :Railway track w
 STR_RAILROAD_TRACK_WITH_PRESIGNALS                              :Railway track with pre-signals
 STR_RAILROAD_TRACK_WITH_EXITSIGNALS                             :Railway track with exit-signals
 STR_RAILROAD_TRACK_WITH_COMBOSIGNALS                            :Railway track with combo-signals
+STR_RAILROAD_TRACK_WITH_PBSSIGNALS                              :Railway track with advanced signals
+STR_RAILROAD_TRACK_WITH_NOENTRYSIGNALS                          :Railway track with one-way advanced signals
 STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS                       :Railway track with normal and pre-signals
 STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS                      :Railway track with normal and exit-signals
 STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS                     :Railway track with normal and combo-signals
+STR_RAILROAD_TRACK_WITH_NORMAL_PBSSIGNALS                       :Railway track with normal and advanced signals
+STR_RAILROAD_TRACK_WITH_NORMAL_NOENTRYSIGNALS                   :Railway track with normal and one-way advanced signals
 STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS                         :Railway track with pre- and exit-signals
 STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS                        :Railway track with pre- and combo-signals
+STR_RAILROAD_TRACK_WITH_PRE_PBSSIGNALS                          :Railway track with pre- and advanced signals
+STR_RAILROAD_TRACK_WITH_PRE_NOENTRYSIGNALS                      :Railway track with pre- and one-way advanced signals
 STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS                       :Railway track with exit- and combo-signals
+STR_RAILROAD_TRACK_WITH_EXIT_PBSSIGNALS                         :Railway track with exit- and advanced signals
+STR_RAILROAD_TRACK_WITH_EXIT_NOENTRYSIGNALS                     :Railway track with exit- and one-way advanced signals
+STR_RAILROAD_TRACK_WITH_COMBO_PBSSIGNALS                        :Railway track with combo- and advanced signals
+STR_RAILROAD_TRACK_WITH_COMBO_NOENTRYSIGNALS                    :Railway track with combo- and one-way advanced signals
+STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS                      :Railway track with advanced and one-way advanced signals
 STR_MUST_REMOVE_RAILWAY_STATION_FIRST                           :{WHITE}Must remove railway station first
 
 
@@ -2870,6 +2888,7 @@ STR_TRAIN_STOPPING_VEL                                          :{RED}Stopping,
 STR_INCOMPATIBLE_RAIL_TYPES                                     :Incompatible rail types
 STR_TRAIN_NO_POWER                                              :{RED}No power
 STR_TRAIN_START_NO_CATENARY                                     :This track lacks catenary, so the train can't start
+STR_TRAIN_STUCK                                                 :{ORANGE}Waiting for free path
 
 STR_NEW_VEHICLE_NOW_AVAILABLE                                   :{BLACK}{BIGFONT}New {STRING} now available!
 STR_NEW_VEHICLE_TYPE                                            :{BLACK}{BIGFONT}{ENGINE}
@@ -2890,6 +2909,8 @@ STR_TIMETABLE_TOTAL_TIME_INCOMPLETE                             :This timetable
 STR_TIMETABLE_AUTOFILL                                          :{BLACK}Autofill
 STR_TIMETABLE_AUTOFILL_TOOLTIP                                  :{BLACK}Fill the timetable automatically with the values from the first journey
 
+STR_TRAIN_IS_APPROACHING                                        :{WHITE}Train is approaching
+
 ##id 0x9000
 STR_9000_ROAD_VEHICLE_IN_THE_WAY                                :{WHITE}Road vehicle in the way
 STR_9001_ROAD_VEHICLES                                          :{WHITE}{COMPANY} - {COMMA} Road Vehicle{P "" s}
@@ -3548,10 +3569,14 @@ STR_BUILD_SIGNAL_SEMAPHORE_NORM_TIP                             :{BLACK}Standard
 STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TIP                            :{BLACK}Entry-Signal (semaphore){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red.
 STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TIP                             :{BLACK}Exit-Signal (semaphore){}Behaves in the same way as a normal signal but is necessary to trigger the correct colour on entry & combo pre-signals.
 STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TIP                            :{BLACK}Combo-Signal (semaphore){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of presignals.
+STR_BUILD_SIGNAL_SEMAPHORE_PBS_TIP                              :{BLACK}Advanced Signal (semaphore){}An advanced signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. Advanced signals can be passed from the backside.
+STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TIP                         :{BLACK}One-way Advanced Signal (semaphore){}An advanced signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. One-way signals cannot be passed from the backside.
 STR_BUILD_SIGNAL_ELECTRIC_NORM_TIP                              :{BLACK}Standard Signal (electric){}Signals are necessary to keep trains from crashing on railway networks with more than one train.
 STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TIP                             :{BLACK}Entry-Signal (electric){}Green as long as there is one or more green exit-signal from the following section of track. Otherwise it shows red.
 STR_BUILD_SIGNAL_ELECTRIC_EXIT_TIP                              :{BLACK}Exit-Signal (electric){}Behaves in the same way as a normal signal but is necessary to trigger the correct colour on entry & combo pre-signals.
 STR_BUILD_SIGNAL_ELECTRIC_COMBO_TIP                             :{BLACK}Combo-Signal (electric){}The combo signal simply acts as both an entry and exit signal. This allows you to build large "trees" of presignals.
+STR_BUILD_SIGNAL_ELECTRIC_PBS_TIP                               :{BLACK}Advanced Signal (electric){}An advanced signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. Advanced signals can be passed from the backside.
+STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TIP                          :{BLACK}One-way Advanced Signal (electric){}An advanced signal allows more than one train to enter a signal block at the same time, if the train can reserve a path to a safe stopping point. One-way signals cannot be passed from the backside.
 STR_SIGNAL_CONVERT_TIP                                          :{BLACK}Signal Convert{}When selected, clicking an existing signal will convert it to the selected signal type and variant, CTRL-click will toggle the existing variant.
 STR_DRAG_SIGNALS_DENSITY_TIP                                    :{BLACK}Dragging signal density
 STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP                           :{BLACK}Decrease dragging signal density
diff --git a/src/map_func.h b/src/map_func.h
index a4b3991..41845ad 100644
--- a/src/map_func.h
+++ b/src/map_func.h
@@ -268,6 +268,23 @@ static inline TileIndexDiffC TileIndexDiffCByDir(Direction dir)
 	return _tileoffs_by_dir[dir];
 }
 
+/*
+ * Returns the DiagDirection from a TileIndexDiffC.
+ *
+ * @param diff The tile offset
+ * @return The diagonal direction
+ */
+static inline DiagDirection DiagDirByTileIndexDiff(const TileIndexDiffC& diff)
+{
+	assert(diff.x == 0 || diff.y == 0);
+
+	if (diff.x == 0) {
+		return diff.y > 0 ? DIAGDIR_SE : DIAGDIR_NW;
+	} else {
+		return diff.x > 0 ? DIAGDIR_SW : DIAGDIR_NE;
+	}
+}
+
 /**
  * Add a TileIndexDiffC to a TileIndex and returns the new one.
  *
diff --git a/src/misc/dbg_helpers.cpp b/src/misc/dbg_helpers.cpp
index 6db02e8..1c34e25 100644
--- a/src/misc/dbg_helpers.cpp
+++ b/src/misc/dbg_helpers.cpp
@@ -49,7 +49,7 @@ CStrA ValueStr(DiagDirection dd)
 
 /** SignalType short names. */
 static const char* signal_type_names[] = {
-	"NORMAL", "ENTRY", "EXIT", "COMBO",
+	"NORMAL", "ENTRY", "EXIT", "COMBO", "PBS", "NOENTRY",
 };
 
 /** Return name of given SignalType. */
diff --git a/src/misc_gui.cpp b/src/misc_gui.cpp
index a87e0bb..85d9851 100644
--- a/src/misc_gui.cpp
+++ b/src/misc_gui.cpp
@@ -58,13 +58,13 @@ static bool _savegame_sort_dirty;
 
 static const Widget _land_info_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,    14,     0,    10,     0,    13, STR_00C5,                       STR_018B_CLOSE_WINDOW},
-{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   279,     0,    13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
-{      WWT_PANEL,   RESIZE_NONE,    14,     0,   279,    14,    92, 0x0,                            STR_NULL},
+{    WWT_CAPTION,   RESIZE_NONE,    14,    11,   299,     0,    13, STR_01A3_LAND_AREA_INFORMATION, STR_018C_WINDOW_TITLE_DRAG_THIS},
+{      WWT_PANEL,   RESIZE_NONE,    14,     0,   299,    14,    92, 0x0,                            STR_NULL},
 {    WIDGETS_END},
 };
 
 static const WindowDesc _land_info_desc = {
-	WDP_AUTO, WDP_AUTO, 280, 93, 280, 93,
+	WDP_AUTO, WDP_AUTO, 300, 93, 300, 93,
 	WC_LAND_INFO, WC_NONE,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET,
 	_land_info_widgets,
@@ -90,13 +90,13 @@ public:
 		for (uint i = 0; i < LAND_INFO_CENTERED_LINES; i++) {
 			if (StrEmpty(this->landinfo_data[i])) continue;
 
-			DoDrawStringCentered(140, y, this->landinfo_data[i], i == 0 ? TC_LIGHT_BLUE : TC_FROMSTRING);
+			DoDrawStringCentered(150, y, this->landinfo_data[i], i == 0 ? TC_LIGHT_BLUE : TC_FROMSTRING);
 			y += 11;
 		}
 
 		y += 5;
 
-		if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) DrawStringMultiCenter(140, y, BindCString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]), this->width - 4);
+		if (!StrEmpty(this->landinfo_data[LAND_INFO_MULTICENTER_LINE])) DrawStringMultiCenter(150, y, BindCString(this->landinfo_data[LAND_INFO_MULTICENTER_LINE]), this->width - 4);
 	}
 
 	LandInfoWindow(TileIndex tile) : Window(&_land_info_desc) {
diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp
index 4f57699..5414e11 100644
--- a/src/newgrf_station.cpp
+++ b/src/newgrf_station.cpp
@@ -25,6 +25,7 @@
 #include "player_func.h"
 #include "animated_tile_func.h"
 #include "functions.h"
+#include "rail_map.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -397,7 +398,12 @@ static uint32 StationGetVariable(const ResolverObject *object, byte variable, by
 
 		case 0x42: return GetTerrainType(tile) | (GetRailType(tile) << 8);
 		case 0x43: return st->owner; // Station owner
-		case 0x44: return 2;         // PBS status
+		case 0x44:
+			if (IsTileType(tile, MP_RAILWAY) && IsRailWaypoint(tile)) {
+				return GetWaypointReservation(tile) ? 7 : 4;
+			} else {
+				return GetRailwayStationReservation(tile) ? 7 : 4; // PBS status
+			}
 		case 0x45:
 			if (!HasBit(_svc.valid, 2)) { _svc.v45 = GetRailContinuationInfo(tile); SetBit(_svc.valid, 2); }
 			return _svc.v45;
diff --git a/src/npf.cpp b/src/npf.cpp
index e4498bb..d5f431b 100644
--- a/src/npf.cpp
+++ b/src/npf.cpp
@@ -24,6 +24,7 @@
 #include "vehicle_base.h"
 #include "settings_type.h"
 #include "tunnelbridge.h"
+#include "pbs.h"
 
 static AyStar _npf_aystar;
 
@@ -221,6 +222,23 @@ static uint NPFSlopeCost(AyStarNode* current)
 	 * there is only one level of steepness... */
 }
 
+static uint NPFReservedTrackCost(AyStarNode *current)
+{
+	TileIndex tile = current->tile;
+	TrackBits track = TrackToTrackBits(TrackdirToTrack((Trackdir)current->direction));
+	TrackBits res = GetReservedTrackbits(tile);
+
+	if (NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL) || ((res & track) == TRACK_BIT_NONE && !TracksOverlap(res | track))) return 0;
+
+	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+		DiagDirection exitdir = TrackdirToExitdir((Trackdir)current->direction);
+		if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(exitdir)) {
+			return  _settings_game.pf.npf.npf_rail_pbs_cross_penalty * (GetTunnelBridgeLength(tile, GetOtherTunnelBridgeEnd(tile)) + 1);
+		}
+	}
+	return  _settings_game.pf.npf.npf_rail_pbs_cross_penalty;
+}
+
 /**
  * Mark tiles by mowing the grass when npf debug level >= 1.
  * Will not work for multiplayer games, since it can (will) cause desyncs.
@@ -354,30 +372,46 @@ static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare
 	/* Determine extra costs */
 
 	/* Check for signals */
-	if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir)) {
-		/* Ordinary track with signals */
-		if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_RED) {
-			/* Signal facing us is red */
-			if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
-				/* Penalize the first signal we
-				 * encounter, if it is red */
-
-				/* Is this a presignal exit or combo? */
-				SignalType sigtype = GetSignalType(tile, TrackdirToTrack(trackdir));
-				if (sigtype == SIGTYPE_EXIT || sigtype == SIGTYPE_COMBO) {
-					/* Penalise exit and combo signals differently (heavier) */
-					cost += _settings_game.pf.npf.npf_rail_firstred_exit_penalty;
+	if (IsTileType(tile, MP_RAILWAY)) {
+		if (HasSignalOnTrackdir(tile, trackdir)) {
+			/* Ordinary track with signals */
+			if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_RED) {
+				/* Signal facing us is red */
+				if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
+					/* Penalize the first signal we
+					 * encounter, if it is red */
+
+					/* Is this a presignal exit or combo? */
+					SignalType sigtype = GetSignalType(tile, TrackdirToTrack(trackdir));
+					if (!IsPbsSignal(sigtype)) {
+						if (sigtype == SIGTYPE_EXIT || sigtype == SIGTYPE_COMBO) {
+							/* Penalise exit and combo signals differently (heavier) */
+							cost += _settings_game.pf.npf.npf_rail_firstred_exit_penalty;
+						} else {
+							cost += _settings_game.pf.npf.npf_rail_firstred_penalty;
+						}
+					}
+				}
+				/* Record the state of this signal */
+				NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true);
+			} else {
+				/* Record the state of this signal */
+				NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
+			}
+			if (NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
+				if (NPFGetFlag(current, NPF_FLAG_2ND_SIGNAL)) {
+					NPFSetFlag(current, NPF_FLAG_3RD_SIGNAL, true);
 				} else {
-					cost += _settings_game.pf.npf.npf_rail_firstred_penalty;
+					NPFSetFlag(current, NPF_FLAG_2ND_SIGNAL, true);
 				}
+			} else {
+				NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
 			}
-			/* Record the state of this signal */
-			NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true);
-		} else {
-			/* Record the state of this signal */
-			NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
 		}
-		NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
+
+		if (HasPbsSignalOnTrackdir(tile, ReverseTrackdir(trackdir)) && !NPFGetFlag(current, NPF_FLAG_3RD_SIGNAL)) {
+			cost += _settings_game.pf.npf.npf_rail_pbs_signal_back_penalty;
+		}
 	}
 
 	/* Penalise the tile if it is a target tile and the last signal was
@@ -406,7 +440,7 @@ static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare
 	}
 
 	/* Check for occupied track */
-	//TODO
+	cost += NPFReservedTrackCost(current);
 
 	NPFMarkTile(tile);
 	DEBUG(npf, 4, "Calculating G for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), cost);
@@ -422,6 +456,17 @@ static int32 NPFFindDepot(AyStar* as, OpenListNode *current)
 		AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
 }
 
+/** Find any safe and free tile. */
+static int32 NPFFindSafeTile(AyStar *as, OpenListNode *current)
+{
+	const Vehicle *v = ((NPFFindStationOrTileData*)as->user_target)->v;
+
+	return
+		IsSafeWaitingPosition(v, current->path.node.tile, (Trackdir)current->path.node.direction, true, _settings_game.pf.forbid_90_deg) &&
+		IsWaitingPositionFree(v, current->path.node.tile, (Trackdir)current->path.node.direction, _settings_game.pf.forbid_90_deg) ?
+			AYSTAR_FOUND_END_NODE : AYSTAR_DONE;
+}
+
 /* Will find a station identified using the NPFFindStationOrTileData */
 static int32 NPFFindStationOrTile(AyStar* as, OpenListNode *current)
 {
@@ -441,9 +486,48 @@ static int32 NPFFindStationOrTile(AyStar* as, OpenListNode *current)
 	}
 }
 
-/* To be called when current contains the (shortest route to) the target node.
+/**
+ * Find the node containing the first signal on the path.
+ *
+ * If the first signal is on the very first two tiles of the path,
+ * the second signal is returnd. If no suitable signal is present, the
+ * last node of the path is returned.
+ */
+static const PathNode* FindSafePosition(PathNode *path, const Vehicle *v)
+{
+	/* If there is no signal, reserve the whole path. */
+	PathNode *sig = path;
+
+	for(; path->parent != NULL; path = path->parent) {
+		if (IsSafeWaitingPosition(v, path->node.tile, (Trackdir)path->node.direction, true, _settings_game.pf.forbid_90_deg)) {
+			sig = path;
+		}
+	}
+
+	return sig;
+}
+
+/**
+ * Lift the reservation of the tiles from @p start till @p end, excluding @p end itself.
+ */
+static void ClearPathReservation(const PathNode *start, const PathNode *end)
+{
+	bool first_run = true;
+	for (; start != end; start = start->parent) {
+		if (IsRailwayStationTile(start->node.tile) && first_run) {
+			SetRailwayStationPlatformReservation(start->node.tile, TrackdirToExitdir((Trackdir)start->node.direction), false);
+		} else {
+			UnreserveRailTrack(start->node.tile, TrackdirToTrack((Trackdir)start->node.direction));
+		}
+		first_run = false;
+	}
+}
+
+/**
+ * To be called when @p current contains the (shortest route to) the target node.
  * Will fill the contents of the NPFFoundTargetData using
- * AyStarNode[NPF_TRACKDIR_CHOICE].
+ * AyStarNode[NPF_TRACKDIR_CHOICE]. If requested, path reservation
+ * is done here.
  */
 static void NPFSaveTargetData(AyStar* as, OpenListNode* current)
 {
@@ -452,6 +536,40 @@ static void NPFSaveTargetData(AyStar* as, OpenListNode* current)
 	ftd->best_path_dist = current->g;
 	ftd->best_bird_dist = 0;
 	ftd->node = current->path.node;
+	ftd->res_okay = false;
+
+	if (as->user_target != NULL && ((NPFFindStationOrTileData*)as->user_target)->reserve_path && as->user_data[NPF_TYPE] == TRANSPORT_RAIL) {
+		/* Path reservation is requested. */
+		const Vehicle  *v = ((NPFFindStationOrTileData*)as->user_target)->v;
+
+		const PathNode *target = FindSafePosition(&current->path, v);
+		ftd->node = target->node;
+
+		/* If the target is a station skip to platform end. */
+		if (IsRailwayStationTile(target->node.tile)) {
+			DiagDirection dir = TrackdirToExitdir((Trackdir)target->node.direction);
+			uint len = GetStationByTile(target->node.tile)->GetPlatformLength(target->node.tile, dir);
+			TileIndex end_tile = TILE_ADD(target->node.tile, (len - 1) * TileOffsByDiagDir(dir));
+
+			/* Update only end tile, trackdir of a station stays the same. */
+			ftd->node.tile = end_tile;
+			if (!IsWaitingPositionFree(v, end_tile, (Trackdir)target->node.direction, _settings_game.pf.forbid_90_deg)) return;
+			SetRailwayStationPlatformReservation(target->node.tile, dir, true);
+			SetRailwayStationReservation(target->node.tile, false);
+		} else {
+			if (!IsWaitingPositionFree(v, target->node.tile, (Trackdir)target->node.direction, _settings_game.pf.forbid_90_deg)) return;
+		}
+
+		for (const PathNode *cur = target; cur->parent != NULL; cur = cur->parent) {
+			if (!TryReserveRailTrack(cur->node.tile, TrackdirToTrack((Trackdir)cur->node.direction))) {
+				/* Reservation failed, undo. */
+				ClearPathReservation(target, cur);
+				return;
+			}
+		}
+
+		ftd->res_okay = true;
+	}
 }
 
 /**
@@ -713,16 +831,28 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current)
 		}
 	}
 
+	if (NPFGetFlag(&current->path.node, NPF_FLAG_IGNORE_RESERVED)) {
+		/* Mask out any reserved tracks. */
+		TrackBits reserved = GetReservedTrackbits(dst_tile);
+		trackdirbits &= ~TrackBitsToTrackdirBits(reserved);
+
+		uint bits = TrackdirBitsToTrackBits(trackdirbits);
+		int i;
+		FOR_EACH_SET_BIT(i, bits) {
+			if (TracksOverlap(reserved | TrackToTrackBits((Track)i))) trackdirbits &= ~TrackToTrackdirBits((Track)i);
+		}
+	}
+
 	/* Enumerate possible track */
 	uint i = 0;
 	while (trackdirbits != 0) {
 		Trackdir dst_trackdir = RemoveFirstTrackdir(&trackdirbits);
 		DEBUG(npf, 5, "Expanded into trackdir: %d, remaining trackdirs: 0x%X", dst_trackdir, trackdirbits);
 
-		/* Check for oneway signal against us */
+		/* Tile with signals? */
 		if (IsTileType(dst_tile, MP_RAILWAY) && GetRailTileType(dst_tile) == RAIL_TILE_SIGNALS) {
-			if (HasSignalOnTrackdir(dst_tile, ReverseTrackdir(dst_trackdir)) && !HasSignalOnTrackdir(dst_tile, dst_trackdir))
-				/* if one way signal not pointing towards us, stop going in this direction. */
+			if (HasSignalOnTrackdir(dst_tile, ReverseTrackdir(dst_trackdir)) && !HasSignalOnTrackdir(dst_tile, dst_trackdir) && IsOnewaySignal(dst_tile, TrackdirToTrack(dst_trackdir)))
+				/* If there's a one-way signal not pointing towards us, stop going in this direction. */
 				break;
 		}
 		{
@@ -782,7 +912,9 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, bool ignore_start
 	/* Initialize result */
 	result.best_bird_dist = (uint)-1;
 	result.best_path_dist = (uint)-1;
-	result.best_trackdir = INVALID_TRACKDIR;
+	result.best_trackdir  = INVALID_TRACKDIR;
+	result.node.tile      = INVALID_TILE;
+	result.res_okay       = false;
 	_npf_aystar.user_path = &result;
 
 	/* Initialize target */
@@ -868,7 +1000,7 @@ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir,
 	 */
 	Queue depots;
 	int r;
-	NPFFoundTargetData best_result = {(uint)-1, (uint)-1, INVALID_TRACKDIR, {INVALID_TILE, 0, {0, 0}}};
+	NPFFoundTargetData best_result = {(uint)-1, (uint)-1, INVALID_TRACKDIR, {INVALID_TILE, 0, {0, 0}}, false};
 	NPFFoundTargetData result;
 	NPFFindStationOrTileData target;
 	AyStarNode start;
@@ -956,6 +1088,30 @@ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir,
 	return best_result;
 }
 
+NPFFoundTargetData NPFRouteToSafeTile(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool override_railtype)
+{
+	assert(v->type == VEH_TRAIN);
+
+	NPFFindStationOrTileData fstd;
+	fstd.v = v;
+	fstd.reserve_path = true;
+
+	AyStarNode start1;
+	start1.tile = tile;
+	/* We set this in case the target is also the start tile, we will just
+	 * return a not found then */
+	start1.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
+	start1.direction = trackdir;
+	NPFSetFlag(&start1, NPF_FLAG_IGNORE_RESERVED, true);
+
+	RailTypes railtypes = v->u.rail.compatible_railtypes;
+	if (override_railtype) railtypes |= GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes;
+
+	/* perform a breadth first search. Target is NULL,
+	 * since we are just looking for any safe tile...*/
+	return NPFRouteInternal(&start1, true, NULL, false, &fstd, NPFFindSafeTile, NPFCalcZero, TRANSPORT_RAIL, 0, v->owner, railtypes, 0);
+}
+
 void InitializeNPF()
 {
 	static bool first_init = true;
@@ -973,7 +1129,7 @@ void InitializeNPF()
 	_npf_aystar.max_search_nodes = _settings_game.pf.npf.npf_max_search_nodes;
 }
 
-void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v)
+void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, Vehicle *v, bool reserve_path)
 {
 	/* Ships don't really reach their stations, but the tile in front. So don't
 	 * save the station id for ships. For roadvehs we don't store it either,
@@ -989,4 +1145,6 @@ void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v)
 		fstd->dest_coords = v->dest_tile;
 		fstd->station_index = INVALID_STATION;
 	}
+	fstd->reserve_path = reserve_path;
+	fstd->v = v;
 }
diff --git a/src/npf.h b/src/npf.h
index 5a70b6b..bf19b62 100644
--- a/src/npf.h
+++ b/src/npf.h
@@ -45,6 +45,8 @@ enum {
 struct NPFFindStationOrTileData {
 	TileIndex dest_coords;   ///< An indication of where the station is, for heuristic purposes, or the target tile
 	StationID station_index; ///< station index we're heading for, or INVALID_STATION when we're heading for a tile
+	bool      reserve_path;  ///< Indicates whether the found path should be reserved
+	const Vehicle* v;        ///< The vehicle we are pathfinding for
 };
 
 /* Indices into AyStar.userdata[] */
@@ -64,9 +66,13 @@ enum {
 /* Flags for AyStarNode.userdata[NPF_NODE_FLAGS]. Use NPFGetBit() and NPFGetBit() to use them. */
 enum NPFNodeFlag {
 	NPF_FLAG_SEEN_SIGNAL,       ///< Used to mark that a signal was seen on the way, for rail only
+	NPF_FLAG_2ND_SIGNAL,        ///< Used to mark that two signals were seen, rail only
+	NPF_FLAG_3RD_SIGNAL,        ///< Used to mark that three signals were seen, rail only
 	NPF_FLAG_REVERSE,           ///< Used to mark that this node was reached from the second start node, if applicable
 	NPF_FLAG_LAST_SIGNAL_RED,   ///< Used to mark that the last signal on this path was red
 	NPF_FLAG_IGNORE_START_TILE, ///< Used to mark that the start tile is invalid, and searching should start from the second tile on
+	NPF_FLAG_TARGET_RESERVED,   ///< Used to mark that the possible reservation target is already reserved
+	NPF_FLAG_IGNORE_RESERVED,   ///< Used to mark that reserved tiles should be considered impassable
 };
 
 /* Meant to be stored in AyStar.userpath */
@@ -75,6 +81,7 @@ struct NPFFoundTargetData {
 	uint best_path_dist;    ///< The shortest path. Is (uint)-1 if no path is found
 	Trackdir best_trackdir; ///< The trackdir that leads to the shortest path/closest birds dist
 	AyStarNode node;        ///< The node within the target the search led us to
+	bool res_okay;          ///< True if a path reservation could be made
 };
 
 /* These functions below are _not_ re-entrant, in favor of speed! */
@@ -105,7 +112,13 @@ NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir t
  * of choices and accurate heuristics, such as water. */
 NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir, bool ignore_start_tile, TransportType type, uint sub_type, Owner owner, RailTypes railtypes);
 
-void NPFFillWithOrderData(NPFFindStationOrTileData* fstd, Vehicle* v);
+/**
+ * Search for any safe tile using a breadth first search and try to reserve a path.
+ */
+NPFFoundTargetData NPFRouteToSafeTile(const Vehicle *v, TileIndex tile, Trackdir trackdir,bool override_railtype);
+
+
+void NPFFillWithOrderData(NPFFindStationOrTileData *fstd, Vehicle *v, bool reserve_path = false);
 
 
 /*
diff --git a/src/openttd.cpp b/src/openttd.cpp
index 1861d3c..b5a8199 100644
--- a/src/openttd.cpp
+++ b/src/openttd.cpp
@@ -2449,6 +2449,46 @@ bool AfterLoadGame()
 		}
 	}
 
+	/* Move the signal variant back up one bit for PBS. We don't convert the old PBS
+	 * format here, as an old layout wouldn't work properly anyway. To be safe, we
+	 * clear any possible PBS reservations as well. */
+	if (CheckSavegameVersion(99)) {
+		for (TileIndex t = 0; t < map_size; t++) {
+			switch (GetTileType(t)) {
+				case MP_RAILWAY:
+					if (HasSignals(t)) {
+						/* move the signal variant */
+						SetSignalVariant(t, TRACK_UPPER, HasBit(_m[t].m2, 2) ? SIG_SEMAPHORE : SIG_ELECTRIC);
+						SetSignalVariant(t, TRACK_LOWER, HasBit(_m[t].m2, 6) ? SIG_SEMAPHORE : SIG_ELECTRIC);
+						ClrBit(_m[t].m2, 2);
+						ClrBit(_m[t].m2, 6);
+					}
+
+					/* Clear PBS reservation on track */
+					if (IsRailDepot(t) ||IsRailWaypoint(t)) {
+						SetWaypointReservation(t, false);
+					} else {
+						SetTrackReservation(t, TRACK_BIT_NONE);
+					}
+					break;
+
+				case MP_ROAD: /* Clear PBS reservation on crossing */
+					if (IsLevelCrossing(t)) SetCrossingReservation(t, false);
+					break;
+
+				case MP_STATION: /* Clear PBS reservation on station */
+					if (IsRailwayStation(t)) SetRailwayStationReservation(t, false);
+					break;
+
+				case MP_TUNNELBRIDGE: /* Clear PBS reservation on tunnels/birdges */
+					if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) SetTunnelBridgeReservation(t, false);
+					break;
+
+				default: break;
+			}
+		}
+	}
+
 	if (_debug_gamelog_level > 0) GamelogPrintDebug();
 
 	return InitializeWindowsAndCaches();
diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp
index 01e0fec..f56def4 100644
--- a/src/order_cmd.cpp
+++ b/src/order_cmd.cpp
@@ -1618,7 +1618,7 @@ static bool OrderConditionCompare(OrderConditionComparator occ, int variable, in
  * @param v the vehicle to update
  * @return index of next order to jump to, or INVALID_VEH_ORDER_ID to use the next order
  */
-static VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
+VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
 {
 	if (order->GetType() != OT_CONDITIONAL) return INVALID_VEH_ORDER_ID;
 
@@ -1644,7 +1644,7 @@ static VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle
  * @param order the order the vehicle currently has
  * @param v the vehicle to update
  */
-static bool UpdateOrderDest(Vehicle *v, const Order *order)
+bool UpdateOrderDest(Vehicle *v, const Order *order)
 {
 	switch (order->GetType()) {
 		case OT_GOTO_STATION:
diff --git a/src/order_func.h b/src/order_func.h
index 8d145da..d32a05d 100644
--- a/src/order_func.h
+++ b/src/order_func.h
@@ -37,6 +37,8 @@ bool VehicleHasDepotOrders(const Vehicle *v);
 void CheckOrders(const Vehicle*);
 void DeleteVehicleOrders(Vehicle *v);
 bool ProcessOrders(Vehicle *v);
+bool UpdateOrderDest(Vehicle *v, const Order *order);
+VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v);
 
 void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int y, bool selected, bool timetable);
 
diff --git a/src/pathfind.cpp b/src/pathfind.cpp
index 9757b09..e6a27df 100644
--- a/src/pathfind.cpp
+++ b/src/pathfind.cpp
@@ -674,7 +674,7 @@ start_at:
 			if (HasSignals(tile)) {
 				if (!HasSignalOnTrackdir(tile, track)) {
 					/* if one way signal not pointing towards us, stop going in this direction => End of rail segment. */
-					if (HasSignalOnTrackdir(tile, ReverseTrackdir(track))) {
+					if (HasSignalOnTrackdir(tile, ReverseTrackdir(track)) && IsOnewaySignal(tile, TrackdirToTrack(track))) {
 						bits = TRACK_BIT_NONE;
 						break;
 					}
diff --git a/src/pbs.cpp b/src/pbs.cpp
new file mode 100644
index 0000000..ccfbbfd
--- /dev/null
+++ b/src/pbs.cpp
@@ -0,0 +1,329 @@
+/* $Id$ */
+
+/** @file pbs.cpp */
+#include "stdafx.h"
+#include "openttd.h"
+#include "pbs.h"
+#include "rail_map.h"
+#include "road_map.h"
+#include "station_map.h"
+#include "tunnelbridge_map.h"
+#include "functions.h"
+#include "debug.h"
+#include "direction_func.h"
+#include "settings_type.h"
+#include "road_func.h"
+#include "vehicle_base.h"
+#include "yapf/follow_track.hpp"
+
+/**
+ * Get the reserved trackbits for any tile, regardless of type.
+ * @param t the tile
+ * @return the reserved trackbits. TRACK_BIT_NONE on nothing reserved or
+ *     a tile without rail.
+ */
+TrackBits GetReservedTrackbits(TileIndex t)
+{
+	switch (GetTileType(t)) {
+		case MP_RAILWAY:
+			if (IsRailWaypoint(t) || IsRailDepot(t)) return GetRailWaypointReservation(t);
+			if (IsPlainRailTile(t)) return GetTrackReservation(t);
+			break;
+
+		case MP_ROAD:
+			if (IsLevelCrossing(t)) return GetRailCrossingReservation(t);
+			break;
+
+		case MP_STATION:
+			if (IsRailwayStation(t)) return GetRailStationReservation(t);
+			break;
+
+		case MP_TUNNELBRIDGE:
+			if (GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL) return GetRailTunnelBridgeReservation(t);
+			break;
+
+		default:
+			break;
+	}
+	return TRACK_BIT_NONE;
+}
+
+/**
+ * Set the reservation for a complete station platform.
+ * @pre IsRailwayStationTile(start)
+ * @param start starting tile of the platform
+ * @param dir the direction in which to follow the platform
+ * @param b the state the reservation should be set to
+ */
+void SetRailwayStationPlatformReservation(TileIndex start, DiagDirection dir, bool b)
+{
+	TileIndex     tile = start;
+	TileIndexDiff diff = TileOffsByDiagDir(dir);
+
+	assert(IsRailwayStationTile(start));
+	assert(GetRailStationAxis(start) == DiagDirToAxis(dir));
+
+	do {
+		SetRailwayStationReservation(tile, b);
+		MarkTileDirtyByTile(tile);
+		tile = TILE_ADD(tile, diff);
+	} while (IsCompatibleTrainStationTile(tile, start));
+}
+
+/**
+ * Try to reserve a specific track on a tile
+ * @param tile the tile
+ * @param t the track
+ * @return true if reservation was successfull, i.e. the track was
+ *     free and didn't cross any other reserved tracks.
+ */
+bool TryReserveRailTrack(TileIndex tile, Track t)
+{
+	assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0);
+
+	if (_settings_client.gui.show_track_reservation) {
+		MarkTileDirtyByTile(tile);
+	}
+
+	switch (GetTileType(tile)) {
+		case MP_RAILWAY:
+			if (IsPlainRailTile(tile)) return TryReserveTrack(tile, t);
+			if (IsRailWaypoint(tile) || IsRailDepot(tile)) {
+				if (!GetWaypointReservation(tile)) {
+					SetWaypointReservation(tile, true);
+					MarkTileDirtyByTile(tile);
+					return true;
+				}
+			}
+			break;
+
+		case MP_ROAD:
+			if (IsLevelCrossing(tile) && !GetCrossingReservation(tile)) {
+				SetCrossingReservation(tile, true);
+				BarCrossing(tile);
+				MarkTileDirtyByTile(tile);
+				return true;
+			}
+			break;
+
+		case MP_STATION:
+			if (IsRailwayStation(tile) && !GetRailwayStationReservation(tile)) {
+				SetRailwayStationReservation(tile, true);
+				MarkTileDirtyByTile(tile);
+				return true;
+			}
+			break;
+
+		case MP_TUNNELBRIDGE:
+			if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL && !GetRailTunnelBridgeReservation(tile)) {
+				SetTunnelBridgeReservation(tile, true);
+				return true;
+			}
+			break;
+
+		default:
+			break;
+	}
+	return false;
+}
+
+/**
+ * Lift the reservation of a specific track on a tile
+ * @param tile the tile
+ * @param t the track
+ */
+ void UnreserveRailTrack(TileIndex tile, Track t)
+{
+	assert((GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & TrackToTrackBits(t)) != 0);
+
+	if (_settings_client.gui.show_track_reservation) {
+		MarkTileDirtyByTile(tile);
+	}
+
+	switch (GetTileType(tile)) {
+		case MP_RAILWAY:
+			if (IsRailWaypoint(tile) || IsRailDepot(tile)) {
+				SetWaypointReservation(tile, false);
+				MarkTileDirtyByTile(tile);
+			}
+			if (IsPlainRailTile(tile)) UnreserveTrack(tile, t);
+			break;
+
+		case MP_ROAD:
+			if (IsLevelCrossing(tile)) {
+				SetCrossingReservation(tile, false);
+				UpdateLevelCrossing(tile);
+			}
+			break;
+
+		case MP_STATION:
+			if (IsRailwayStation(tile)) {
+				SetRailwayStationReservation(tile, false);
+				MarkTileDirtyByTile(tile);
+			}
+			break;
+
+		case MP_TUNNELBRIDGE:
+			if (GetTunnelBridgeTransportType(tile) == TRANSPORT_RAIL) SetTunnelBridgeReservation(tile, false);
+			break;
+
+		default:
+			break;
+	}
+}
+
+
+/**
+ *
+ *
+ */
+static PBSTileInfo FollowReservation(const Vehicle *v, RailTypes rts, TileIndex tile, Trackdir trackdir)
+{
+	/* Do not disallow 90 deg turns as the setting might have changed between reserving and now. */
+	CFollowTrackRail ft(v, rts);
+	while (ft.Follow(tile, trackdir)) {
+		TrackdirBits reserved = (TrackdirBits)(ft.m_new_td_bits & (GetReservedTrackbits(ft.m_new_tile) * 0x101));
+
+		/* No reservation --> path end found */
+		if (reserved == TRACKDIR_BIT_NONE) break;
+
+		/* Can't have more than one reserved trackdir */
+		Trackdir new_trackdir = FindFirstTrackdir(reserved);
+
+		/* One-way signal against us. The reservation can't be ours as it is not
+		 * a safe position from our direction and we can never pass the signal. */
+		if (HasOnewaySignalBlockingTrackdir(ft.m_new_tile, new_trackdir)) break;
+
+		tile = ft.m_new_tile;
+		trackdir = new_trackdir;
+
+		/* Depot tile? Can't continue. */
+		if (IsRailDepotTile(tile)) break;
+		/* Non-pbs signal? Reservation can't continue. */
+		if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) break;
+	}
+
+	return PBSTileInfo(tile, trackdir, false);
+}
+
+/**
+ * Follow a train reservation to the last tile.
+ *
+ * @param v the vehicle
+ * @returns The last tile of the reservation or the current train tile if no reservation present.
+ */
+PBSTileInfo FollowTrainReservation(const Vehicle *v)
+{
+	assert(v->type == VEH_TRAIN);
+
+	TileIndex tile = v->tile;
+	Trackdir  trackdir = GetVehicleTrackdir(v);
+
+	if (IsRailDepotTile(tile) && !GetRailDepotReservation(tile)) return PBSTileInfo(tile, trackdir, false);
+
+	PBSTileInfo res = FollowReservation(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes, tile, trackdir);
+	res.okay = IsSafeWaitingPosition(v, res.tile, res.trackdir, true, _settings_game.pf.forbid_90_deg);
+	return res;
+}
+
+/**
+ *
+ *
+ *
+ */
+Vehicle *GetTrainForReservation(TileIndex tile, Track track)
+{
+	Trackdir  trackdir = TrackToTrackdir(track);
+
+	/* Create a fake train. */
+	InvalidVehicle fake_veh;
+	fake_veh.owner = GetTileOwner(tile);
+
+	RailTypes rts = GetRailTypeInfo(GetTileRailType(tile))->compatible_railtypes;
+
+	/* */
+	for (int i = 0; i < 2; ++i, trackdir = ReverseTrackdir(trackdir)) {
+		PBSTileInfo dest = FollowReservation(&fake_veh, rts, tile, trackdir);
+
+//		Vehicle *v = VehicleFromPos();
+//		if (v != NULL) {
+
+//		}
+	}
+
+	return NULL;
+}
+
+/**
+ * Determine whether a certain track on a tile is a safe position to end a path.
+ *
+ * @param v the vehicle to test for
+ * @param tile The tile
+ * @param trackdir The trackdir to test
+ * @param include_line_end Should end-of-line tiles be considered safe?
+ * @param forbid_90def Don't allow trains to make 90 degree turns
+ * @return True if it is a safe position
+ */
+bool IsSafeWaitingPosition(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg)
+{
+	if (IsRailDepotTile(tile)) return true;
+
+	if (IsTileType(tile, MP_RAILWAY)) {
+		/* For non-pbs signals, stop on the signal tile. */
+		if (HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
+	}
+
+	/* Check next tile. For perfomance reasons, we check for 90 degree turns ourself. */
+	CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes);
+
+	/* End of track? */
+	if (!ft.Follow(tile, trackdir)) {
+		/* Last tile of a terminus station is a safe position. */
+		if (include_line_end) return true;
+	}
+
+	/* Check for reachable tracks. */
+	ft.m_new_td_bits &= DiagdirReachesTrackdirs(ft.m_exitdir);
+	if (ft.m_new_td_bits == TRACKDIR_BIT_NONE) return include_line_end;
+	if (forbid_90deg) ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(trackdir);
+
+	if (ft.m_new_td_bits != TRACKDIR_BIT_NONE && KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) {
+		/* PBS signal on next trackdir? Safe position. */
+		if (HasPbsSignalOnTrackdir(ft.m_new_tile, FindFirstTrackdir(ft.m_new_td_bits))) return true;
+	}
+
+	return false;
+}
+
+/**
+ * Check if a safe position is free.
+ *
+ * @param v the vehicle to test for
+ * @param tile The tile
+ * @param trackdir The trackdir to test
+ * @param forbid_90def Don't allow trains to make 90 degree turns
+ * @return True if the position is free
+ */
+bool IsWaitingPositionFree(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg)
+{
+	Track     track = TrackdirToTrack(trackdir);
+	TrackBits reserved = GetReservedTrackbits(tile);
+
+	/* Tile reserved? Can never be a free waiting position. */
+	if (TrackOverlapsTracks(reserved, track)) return false;
+
+	/* Not reserved and depot or not a pbs signal -> free. */
+	if (IsRailDepotTile(tile)) return true;
+	if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir) && !IsPbsSignal(GetSignalType(tile, track))) return true;
+
+	/* Check the next tile, if it's a PBS signal, it has to be free as well. */
+	CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes);
+
+	if (!ft.Follow(tile, trackdir)) return true;
+
+	/* Check for reachable tracks. */
+	ft.m_new_td_bits &= DiagdirReachesTrackdirs(ft.m_exitdir);
+	if (forbid_90deg) ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(trackdir);
+
+	return !HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits));
+}
diff --git a/src/pbs.h b/src/pbs.h
new file mode 100644
index 0000000..8a6a553
--- /dev/null
+++ b/src/pbs.h
@@ -0,0 +1,48 @@
+/* $Id$ */
+
+/** @file pbs.h PBS support routines */
+
+#ifndef PBS_H
+#define PBS_H
+
+#include "tile_type.h"
+#include "direction_type.h"
+#include "track_type.h"
+#include "vehicle_type.h"
+
+TrackBits GetReservedTrackbits(TileIndex t);
+
+void SetRailwayStationPlatformReservation(TileIndex start, DiagDirection dir, bool b);
+
+bool TryReserveRailTrack(TileIndex tile, Track t);
+void UnreserveRailTrack(TileIndex tile, Track t);
+
+/** This struct contains information about the end of a reserved path. */
+struct PBSTileInfo {
+	TileIndex tile;      ///< Tile the path ends, INVALID_TILE if no valid path was found.
+	Trackdir  trackdir;  ///< The reserved trackdir on the tile.
+	bool      okay;      ///< True if tile is a safe wairing position, false otherwise.
+
+	PBSTileInfo() : tile(INVALID_TILE), trackdir(INVALID_TRACKDIR), okay(false) {}
+	PBSTileInfo(TileIndex _t, Trackdir _td, bool _okay) : tile(_t), trackdir(_td), okay(_okay) {}
+};
+
+PBSTileInfo FollowTrainReservation(const Vehicle *v);
+bool IsSafeWaitingPosition(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg = false);
+bool IsWaitingPositionFree(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg = false);
+
+Vehicle *GetTrainForReservation(TileIndex tile, Track track);
+
+/**
+ * Check whether some of tracks is reserved on a tile.
+ *
+ * @param tile the tile
+ * @param tracks the tracks to test
+ * @return true if at least on of tracks is reserved
+ */
+static inline bool HasReservedTracks(TileIndex tile, TrackBits tracks)
+{
+	return (GetReservedTrackbits(tile) & tracks) != TRACK_BIT_NONE;
+}
+
+#endif /* PBS_H */
diff --git a/src/players.cpp b/src/players.cpp
index a024cd4..6633860 100644
--- a/src/players.cpp
+++ b/src/players.cpp
@@ -1270,11 +1270,8 @@ static void Load_PLYR()
 		_player_colors[index] = p->player_color;
 
 		/* This is needed so an AI is attached to a loaded AI */
-		if (p->is_ai && (!_networking || _network_server) && _ai.enabled) {
-			/* Clear the memory of the new AI, otherwise we might be doing wrong things. */
-			memset(&_players_ainew[index], 0, sizeof(PlayerAiNew));
+		if (p->is_ai && (!_networking || _network_server) && _ai.enabled)
 			AI_StartNewAI(p->index);
-		}
 	}
 }
 
diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp
index 7325c8c..7ff8ac3 100644
--- a/src/rail_cmd.cpp
+++ b/src/rail_cmd.cpp
@@ -46,6 +46,7 @@
 #include "functions.h"
 #include "elrail_func.h"
 #include "oldpool_func.h"
+#include "pbs.h"
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -439,7 +440,7 @@ CommandCost CmdBuildSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p
 /** Remove a single piece of track
  * @param tile tile to remove track from
  * @param flags operation to perform
- * @param p1 unused
+ * @param p1 force remove even if track is reserved
  * @param p2 rail orientation
  */
 CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
@@ -452,6 +453,8 @@ CommandCost CmdRemoveSingleRail(TileIndex tile, uint32 flags, uint32 p1, uint32
 	if (!ValParamTrackOrientation((Track)p2)) return CMD_ERROR;
 	trackbit = TrackToTrackBits(track);
 
+	if (p1 == 0 && HasReservedTracks(tile, trackbit)) return_cmd_error(STR_TRAIN_IS_APPROACHING);
+
 	/* Need to read tile owner now because it may change when the rail is removed
 	 * Also, in case of floods, _current_player != owner
 	 * There may be invalid tiletype even in exec run (when removing long track),
@@ -674,7 +677,7 @@ static CommandCost CmdRailTrackHelper(TileIndex tile, uint32 flags, uint32 p1, u
 	if (flags & DC_EXEC) SndPlayTileFx(SND_20_SPLAT_2, tile);
 
 	for (;;) {
-		ret = DoCommand(tile, railtype, TrackdirToTrack(trackdir), flags, (mode == 0) ? CMD_BUILD_SINGLE_RAIL : CMD_REMOVE_SINGLE_RAIL);
+		ret = DoCommand(tile, (mode == 0) ? railtype : 0, TrackdirToTrack(trackdir), flags, (mode == 0) ? CMD_BUILD_SINGLE_RAIL : CMD_REMOVE_SINGLE_RAIL);
 
 		if (CmdFailed(ret)) {
 			if ((_error_message != STR_1007_ALREADY_BUILT) && (mode == 0)) break;
@@ -793,8 +796,10 @@ CommandCost CmdBuildTrainDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p
  * - p1 = (bit 0-2) - track-orientation, valid values: 0-5 (Track enum)
  * - p1 = (bit 3)   - 1 = override signal/semaphore, or pre/exit/combo signal or (for bit 7) toggle variant (CTRL-toggle)
  * - p1 = (bit 4)   - 0 = signals, 1 = semaphores
- * - p1 = (bit 5-6) - type of the signal, for valid values see enum SignalType in rail_map.h
- * - p1 = (bit 7)   - convert the present signal type and variant
+ * - p1 = (bit 5-7) - type of the signal, for valid values see enum SignalType in rail_map.h
+ * - p1 = (bit 8)   - convert the present signal type and variant
+ * - p1 = (bit 9-11)- start cycle from this signal type
+ * - p1 = (bit 12-14)-wrap around after this signal type
  * @param p2 used for CmdBuildManySignals() to copy direction of first signal
  * TODO: p2 should be replaced by two bits for "along" and "against" the track.
  */
@@ -803,10 +808,14 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32
 	Track track = (Track)GB(p1, 0, 3);
 	bool ctrl_pressed = HasBit(p1, 3); // was the CTRL button pressed
 	SignalVariant sigvar = (ctrl_pressed ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC; // the signal variant of the new signal
-	SignalType sigtype = (SignalType)GB(p1, 5, 2); // the signal type of the new signal
-	bool convert_signal = HasBit(p1, 7); // convert button pressed
+	SignalType sigtype = (SignalType)GB(p1, 5, 3); // the signal type of the new signal
+	bool convert_signal = HasBit(p1, 8); // convert button pressed
+	SignalType cycle_start = (SignalType)GB(p1, 9, 3);
+	SignalType cycle_stop = (SignalType)GB(p1, 12, 3);
 	CommandCost cost;
 
+	if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
+
 	if (!ValParamTrackOrientation(track) || !IsTileType(tile, MP_RAILWAY) || !EnsureNoTrainOnTrack(tile, track))
 		return CMD_ERROR;
 
@@ -868,7 +877,7 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32
 		if (p2 == 0) {
 			if (!HasSignalOnTrack(tile, track)) {
 				/* build new signals */
-				SetPresentSignals(tile, GetPresentSignals(tile) | SignalOnTrack(track));
+				SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
 				SetSignalType(tile, track, sigtype);
 				SetSignalVariant(tile, track, sigvar);
 			} else {
@@ -882,13 +891,21 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32
 						/* convert the present signal to the chosen type and variant */
 						SetSignalType(tile, track, sigtype);
 						SetSignalVariant(tile, track, sigvar);
+						if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
+							SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | KillFirstBit(SignalOnTrack(track)));
+						}
 					}
 
 				} else if (ctrl_pressed) {
-					/* cycle between normal -> pre -> exit -> combo -> ... */
-					sigtype = GetSignalType(tile, track);
+					/* cycle between cycle_start and cycle_end */
+					sigtype = (SignalType)(GetSignalType(tile, track) + 1);
+
+					if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
 
-					SetSignalType(tile, track, sigtype == SIGTYPE_COMBO ? SIGTYPE_NORMAL : (SignalType)(sigtype + 1));
+					SetSignalType(tile, track, sigtype);
+					if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
+						SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | KillFirstBit(SignalOnTrack(track)));
+					}
 				} else {
 					/* cycle the signal side: both -> left -> right -> both -> ... */
 					CycleSignalSide(tile, track);
@@ -899,8 +916,13 @@ CommandCost CmdBuildSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint32
 			 * direction of the first signal given as parameter by CmdBuildManySignals */
 			SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (p2 & SignalOnTrack(track)));
 			SetSignalVariant(tile, track, sigvar);
+			SetSignalType(tile, track, sigtype);
 		}
 
+		if (IsPbsSignal(sigtype)) {
+			uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
+			SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetTrackReservation(tile), track) ? (uint)-1 : 0) & mask));
+		}
 		MarkTileDirtyByTile(tile);
 		AddTrackToSignalBuffer(tile, track, _current_player);
 		YapfNotifyTrackLayoutChange(tile, track);
@@ -974,6 +996,7 @@ static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal
  * - p2 = (bit  4)    - 0 = signals, 1 = semaphores
  * - p2 = (bit  5)    - 0 = build, 1 = remove signals
  * - p2 = (bit  6)    - 0 = selected stretch, 1 = auto fill
+ * - p2 = (bit  7- 9) - default signal type
  * - p2 = (bit 24-31) - user defined signals_density
  */
 static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
@@ -1011,6 +1034,9 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1,
 	/* Autofill must start on a valid track to be able to avoid loops */
 	if (autofill && !HasTrack(tile, track)) return CMD_ERROR;
 
+	SignalType sigtype = (SignalType)GB(p2, 7, 3);
+	if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
+
 	/* copy the signal-style of the first rail-piece if existing */
 	if (HasSignals(tile)) {
 		signals = GetPresentSignals(tile) & SignalOnTrack(track);
@@ -1018,8 +1044,10 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1,
 
 		/* copy signal/semaphores style (independent of CTRL) */
 		semaphores = GetSignalVariant(tile, track) != SIG_ELECTRIC;
+
+		sigtype = GetSignalType(tile, track);
 	} else { // no signals exist, drag a two-way signal stretch
-		signals = SignalOnTrack(track);
+		signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track);
 	}
 
 	byte signal_dir = 0;
@@ -1041,6 +1069,7 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1,
 			uint32 p1 = GB(TrackdirToTrack(trackdir), 0, 3);
 			SB(p1, 3, 1, mode);
 			SB(p1, 4, 1, semaphores);
+			SB(p1, 5, 3, sigtype);
 
 			/* Pick the correct orientation for the track direction */
 			signals = 0;
@@ -1090,6 +1119,7 @@ static CommandCost CmdSignalTrackHelper(TileIndex tile, uint32 flags, uint32 p1,
  * - p2 = (bit  4)    - 0 = signals, 1 = semaphores
  * - p2 = (bit  5)    - 0 = build, 1 = remove signals
  * - p2 = (bit  6)    - 0 = selected stretch, 1 = auto fill
+ * - p2 = (bit  7- 9) - default signal type
  * - p2 = (bit 24-31) - user defined signals_density
  * @see CmdSignalTrackHelper
  */
@@ -1152,6 +1182,7 @@ CommandCost CmdRemoveSingleSignal(TileIndex tile, uint32 flags, uint32 p1, uint3
  * - p2 = (bit  4)    - 0 = signals, 1 = semaphores
  * - p2 = (bit  5)    - 0 = build, 1 = remove signals
  * - p2 = (bit  6)    - 0 = selected stretch, 1 = auto fill
+ * - p2 = (bit  7- 9) - default signal type
  * - p2 = (bit 24-31) - user defined signals_density
  * @see CmdSignalTrackHelper
  */
@@ -1377,7 +1408,8 @@ static CommandCost ClearTile_Track(TileIndex tile, byte flags)
 			TrackBits tracks = GetTrackBits(tile);
 			while (tracks != TRACK_BIT_NONE) {
 				Track track = RemoveFirstTrack(&tracks);
-				ret = DoCommand(tile, 0, track, flags, CMD_REMOVE_SINGLE_RAIL);
+				/* NOTE: Force removal of reserved tracks, maybe remove if no bugs found. */
+				ret = DoCommand(tile, 1, track, flags, CMD_REMOVE_SINGLE_RAIL);
 				if (CmdFailed(ret)) return CMD_ERROR;
 				cost.AddCost(ret);
 			}
@@ -1451,7 +1483,7 @@ static void DrawSingleSignal(TileIndex tile, Track track, byte condition, uint i
 		sprite = SPR_ORIGINAL_SIGNALS_BASE + image + condition;
 	} else {
 		/* All other signals are picked from add on sprites. */
-		sprite = SPR_SIGNALS_BASE + (type - 1) * 16 + variant * 64 + image + condition;
+		sprite = SPR_SIGNALS_BASE + (type - 1) * 16 + variant * 64 + image + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0);
 	}
 
 	AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track));
@@ -1687,6 +1719,29 @@ static void DrawTrackBits(TileInfo* ti, TrackBits track)
 		if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
 	}
 
+	/* PBS debugging, draw reserved tracks darker */
+	if (_settings_client.gui.show_track_reservation) {
+		TrackBits pbs = GetTrackReservation(ti->tile);
+		if (pbs & TRACK_BIT_X) {
+			if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
+				DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH);
+			} else {
+				DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
+			}
+		}
+		if (pbs & TRACK_BIT_Y) {
+			if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
+				DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH);
+			} else {
+				DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
+			}
+		}
+		if (pbs & TRACK_BIT_UPPER) AddSortableSpriteToDraw(rti->base_sprites.single_n, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_N ? 8 : 0));
+		if (pbs & TRACK_BIT_LOWER) AddSortableSpriteToDraw(rti->base_sprites.single_s, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_S ? 8 : 0));
+		if (pbs & TRACK_BIT_LEFT)  AddSortableSpriteToDraw(rti->base_sprites.single_w, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_W ? 8 : 0));
+		if (pbs & TRACK_BIT_RIGHT) AddSortableSpriteToDraw(rti->base_sprites.single_e, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + (ti->tileh & SLOPE_E ? 8 : 0));
+	}
+
 	if (IsValidCorner(halftile_corner)) {
 		DrawFoundation(ti, HalftileFoundation(halftile_corner));
 
@@ -1845,6 +1900,11 @@ default_waypoint:
 
 		DrawGroundSprite(image, PAL_NONE);
 
+		/* PBS debugging, draw reserved tracks darker */
+		if (_settings_client.gui.show_track_reservation && GetWaypointReservation(ti->tile)) {
+			DrawGroundSprite(GetWaypointAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH);
+		}
+
 		if (HasCatenaryDrawn(GetRailType(ti->tile))) DrawCatenary(ti);
 
 		/* End now if buildings are invisible */
@@ -2146,12 +2206,13 @@ static TrackStatus GetTileTrackStatus_Track(TileIndex tile, TransportType mode,
 
 			b &= a;
 
-			/* When signals are not present (in neither
-			 * direction), we pretend them to be green. (So if
-			 * signals are only one way, the other way will
-			 * implicitely become `red' */
-			if ((a & 0xC) == 0) b |= 0xC;
-			if ((a & 0x3) == 0) b |= 0x3;
+			/* When signals are not present (in neither direction),
+			 * we pretend them to be green. Otherwise, it depends on
+			 * the signal type. For signals that are only active from
+			 * one side, we set the missing signals explicitely to
+			 * `green'. Otherwise, they implicitely become `red'. */
+			if (!IsOnewaySignal(tile, TRACK_UPPER) || (a & SignalOnTrack(TRACK_UPPER)) == 0) b |= ~a & SignalOnTrack(TRACK_UPPER);
+			if (!IsOnewaySignal(tile, TRACK_LOWER) || (a & SignalOnTrack(TRACK_LOWER)) == 0) b |= ~a & SignalOnTrack(TRACK_LOWER);
 
 			if ((b & 0x8) == 0) red_signals |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E);
 			if ((b & 0x4) == 0) red_signals |= (TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W);
@@ -2196,30 +2257,54 @@ static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
 			break;
 
 		case RAIL_TILE_SIGNALS: {
-			const StringID signal_type[4][4] = {
+			const StringID signal_type[6][6] = {
 				{
 					STR_RAILROAD_TRACK_WITH_NORMAL_SIGNALS,
 					STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS,
 					STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS,
-					STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS
+					STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS,
+					STR_RAILROAD_TRACK_WITH_NORMAL_PBSSIGNALS,
+					STR_RAILROAD_TRACK_WITH_NORMAL_NOENTRYSIGNALS
 				},
 				{
 					STR_RAILROAD_TRACK_WITH_NORMAL_PRESIGNALS,
 					STR_RAILROAD_TRACK_WITH_PRESIGNALS,
 					STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS,
-					STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS
+					STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS,
+					STR_RAILROAD_TRACK_WITH_PRE_PBSSIGNALS,
+					STR_RAILROAD_TRACK_WITH_PRE_NOENTRYSIGNALS
 				},
 				{
 					STR_RAILROAD_TRACK_WITH_NORMAL_EXITSIGNALS,
 					STR_RAILROAD_TRACK_WITH_PRE_EXITSIGNALS,
 					STR_RAILROAD_TRACK_WITH_EXITSIGNALS,
-					STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS
+					STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS,
+					STR_RAILROAD_TRACK_WITH_EXIT_PBSSIGNALS,
+					STR_RAILROAD_TRACK_WITH_EXIT_NOENTRYSIGNALS
 				},
 				{
 					STR_RAILROAD_TRACK_WITH_NORMAL_COMBOSIGNALS,
 					STR_RAILROAD_TRACK_WITH_PRE_COMBOSIGNALS,
 					STR_RAILROAD_TRACK_WITH_EXIT_COMBOSIGNALS,
-					STR_RAILROAD_TRACK_WITH_COMBOSIGNALS
+					STR_RAILROAD_TRACK_WITH_COMBOSIGNALS,
+					STR_RAILROAD_TRACK_WITH_COMBO_PBSSIGNALS,
+					STR_RAILROAD_TRACK_WITH_COMBO_NOENTRYSIGNALS
+				},
+				{
+					STR_RAILROAD_TRACK_WITH_NORMAL_PBSSIGNALS,
+					STR_RAILROAD_TRACK_WITH_PRE_PBSSIGNALS,
+					STR_RAILROAD_TRACK_WITH_EXIT_PBSSIGNALS,
+					STR_RAILROAD_TRACK_WITH_COMBO_PBSSIGNALS,
+					STR_RAILROAD_TRACK_WITH_PBSSIGNALS,
+					STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS
+				},
+				{
+					STR_RAILROAD_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
+					STR_RAILROAD_TRACK_WITH_PRE_NOENTRYSIGNALS,
+					STR_RAILROAD_TRACK_WITH_EXIT_NOENTRYSIGNALS,
+					STR_RAILROAD_TRACK_WITH_COMBO_NOENTRYSIGNALS,
+					STR_RAILROAD_TRACK_WITH_PBS_NOENTRYSIGNALS,
+					STR_RAILROAD_TRACK_WITH_NOENTRYSIGNALS
 				}
 			};
 
diff --git a/src/rail_gui.cpp b/src/rail_gui.cpp
index b6f836e..4dc6fde 100644
--- a/src/rail_gui.cpp
+++ b/src/rail_gui.cpp
@@ -51,6 +51,9 @@ static bool _convert_signal_button;          ///< convert signal button in the s
 static SignalVariant _cur_signal_variant;    ///< set the signal variant (for signal GUI)
 static SignalType _cur_signal_type;          ///< set the signal type (for signal GUI)
 
+/* Map _patches.default_signal_type to the corresponding signal type */
+static const SignalType _default_signal_type[] = {SIGTYPE_NORMAL, SIGTYPE_PBS, SIGTYPE_PBS_ONEWAY};
+
 static struct {
 	byte orientation;                 ///< Currently selected rail station orientation
 	byte numtracks;                   ///< Currently selected number of tracks in station (if not \c dragdrop )
@@ -77,7 +80,7 @@ void CcPlaySound1E(bool success, TileIndex tile, uint32 p1, uint32 p2)
 
 static void GenericPlaceRail(TileIndex tile, int cmd)
 {
-	DoCommandP(tile, _cur_railtype, cmd, CcPlaySound1E,
+	DoCommandP(tile, _remove_button_clicked ? 0 : _cur_railtype, cmd, CcPlaySound1E,
 		_remove_button_clicked ?
 		CMD_REMOVE_SINGLE_RAIL | CMD_MSG(STR_1012_CAN_T_REMOVE_RAILROAD_TRACK) | CMD_NO_WATER :
 		CMD_BUILD_SINGLE_RAIL | CMD_MSG(STR_1011_CAN_T_BUILD_RAILROAD_TRACK) | CMD_NO_WATER
@@ -216,6 +219,9 @@ static void GenericPlaceSignals(TileIndex tile)
 	} else {
 		const Window *w = FindWindowById(WC_BUILD_SIGNAL, 0);
 
+		/* Map _patches.cycle_signal_types to the lower and upper allowed signal type. */
+		static const uint cycle_bounds[] = {SIGTYPE_NORMAL | (SIGTYPE_LAST_NOPBS << 3), SIGTYPE_PBS | (SIGTYPE_LAST << 3), SIGTYPE_NORMAL | (SIGTYPE_LAST << 3)};
+
 		/* various bitstuffed elements for CmdBuildSingleSignal() */
 		uint32 p1 = track;
 
@@ -223,13 +229,15 @@ static void GenericPlaceSignals(TileIndex tile)
 			/* signal GUI is used */
 			SB(p1, 3, 1, _ctrl_pressed);
 			SB(p1, 4, 1, _cur_signal_variant);
-			SB(p1, 5, 2, _cur_signal_type);
-			SB(p1, 7, 1, _convert_signal_button);
+			SB(p1, 5, 3, _cur_signal_type);
+			SB(p1, 8, 1, _convert_signal_button);
+			SB(p1, 9, 6, cycle_bounds[_settings_client.gui.cycle_signal_types]);
 		} else {
 			SB(p1, 3, 1, _ctrl_pressed);
 			SB(p1, 4, 1, (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC));
-			SB(p1, 5, 2, SIGTYPE_NORMAL);
-			SB(p1, 7, 1, 0);
+			SB(p1, 5, 3, _default_signal_type[_settings_client.gui.default_signal_type]);
+			SB(p1, 8, 1, 0);
+			SB(p1, 9, 6, cycle_bounds[_settings_client.gui.cycle_signal_types]);
 		}
 
 		DoCommandP(tile, p1, 0, CcPlaySound1E, CMD_BUILD_SIGNALS |
@@ -547,11 +555,13 @@ static void HandleAutoSignalPlacement()
 		SB(p2,  3, 1, 0);
 		SB(p2,  4, 1, _cur_signal_variant);
 		SB(p2,  6, 1, _ctrl_pressed);
+		SB(p2,  7, 3, _cur_signal_type);
 		SB(p2, 24, 8, _settings_client.gui.drag_signals_density);
 	} else {
 		SB(p2,  3, 1, 0);
 		SB(p2,  4, 1, (_cur_year < _settings_client.gui.semaphore_build_before ? SIG_SEMAPHORE : SIG_ELECTRIC));
 		SB(p2,  6, 1, _ctrl_pressed);
+		SB(p2,  7, 3, _default_signal_type[_settings_client.gui.default_signal_type]);
 		SB(p2, 24, 8, _settings_client.gui.drag_signals_density);
 	}
 
@@ -1350,10 +1360,14 @@ enum BuildSignalWidgets {
 	BSW_SEMAPHORE_ENTRY,
 	BSW_SEMAPHORE_EXIT,
 	BSW_SEMAPHORE_COMBO,
+	BSW_SEMAPHORE_PBS,
+	BSW_SEMAPHORE_PBS_OWAY,
 	BSW_ELECTRIC_NORM,
 	BSW_ELECTRIC_ENTRY,
 	BSW_ELECTRIC_EXIT,
 	BSW_ELECTRIC_COMBO,
+	BSW_ELECTRIC_PBS,
+	BSW_ELECTRIC_PBS_OWAY,
 	BSW_CONVERT,
 	BSW_DRAG_SIGNALS_DENSITY,
 	BSW_DRAG_SIGNALS_DENSITY_DECREASE,
@@ -1400,10 +1414,14 @@ public:
 		this->DrawSignalSprite(BSW_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, -1, 13); // xsize of sprite + 1 == 10
 		this->DrawSignalSprite(BSW_SEMAPHORE_EXIT,  SPR_IMG_SIGNAL_SEMAPHORE_EXIT,   0, 12); // xsize of sprite + 1 ==  9
 		this->DrawSignalSprite(BSW_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_COMBO,  0, 12); // xsize of sprite + 1 ==  9
+		this->DrawSignalSprite(BSW_SEMAPHORE_PBS,   SPR_IMG_SIGNAL_SEMAPHORE_PBS,    0, 12); // xsize of sprite + 1 ==  9
+		this->DrawSignalSprite(BSW_SEMAPHORE_PBS_OWAY, SPR_IMG_SIGNAL_SEMAPHORE_PBSOWAY, -1, 13); // xsize of sprite + 1 == 10
 		this->DrawSignalSprite(BSW_ELECTRIC_NORM,   SPR_IMG_SIGNAL_ELECTRIC_NORM,   -1,  4);
 		this->DrawSignalSprite(BSW_ELECTRIC_ENTRY,  SPR_IMG_SIGNAL_ELECTRIC_ENTRY,  -2,  6);
 		this->DrawSignalSprite(BSW_ELECTRIC_EXIT,   SPR_IMG_SIGNAL_ELECTRIC_EXIT,   -2,  6);
 		this->DrawSignalSprite(BSW_ELECTRIC_COMBO,  SPR_IMG_SIGNAL_ELECTRIC_COMBO,  -2,  6);
+		this->DrawSignalSprite(BSW_ELECTRIC_PBS,    SPR_IMG_SIGNAL_ELECTRIC_PBS,    -1,  4);
+		this->DrawSignalSprite(BSW_ELECTRIC_PBS_OWAY,SPR_IMG_SIGNAL_ELECTRIC_OBSOWAY,-2,  6);
 
 		/* Draw dragging signal density value in the BSW_DRAG_SIGNALS_DENSITY widget */
 		SetDParam(0, _settings_client.gui.drag_signals_density);
@@ -1419,13 +1437,17 @@ public:
 			case BSW_SEMAPHORE_ENTRY:
 			case BSW_SEMAPHORE_EXIT:
 			case BSW_SEMAPHORE_COMBO:
+			case BSW_SEMAPHORE_PBS:
+			case BSW_SEMAPHORE_PBS_OWAY:
 			case BSW_ELECTRIC_NORM:
 			case BSW_ELECTRIC_ENTRY:
 			case BSW_ELECTRIC_EXIT:
 			case BSW_ELECTRIC_COMBO:
+			case BSW_ELECTRIC_PBS:
+			case BSW_ELECTRIC_PBS_OWAY:
 				this->RaiseWidget((_cur_signal_variant == SIG_ELECTRIC ? BSW_ELECTRIC_NORM : BSW_SEMAPHORE_NORM) + _cur_signal_type);
 
-				_cur_signal_type = (SignalType)((uint)((widget - BSW_SEMAPHORE_NORM) % (SIGTYPE_COMBO + 1)));
+				_cur_signal_type = (SignalType)((uint)((widget - BSW_SEMAPHORE_NORM) % (SIGTYPE_LAST + 1)));
 				_cur_signal_variant = widget >= BSW_ELECTRIC_NORM ? SIG_ELECTRIC : SIG_SEMAPHORE;
 				break;
 
@@ -1457,29 +1479,33 @@ public:
 /** Widget definition of the build signal window */
 static const Widget _signal_builder_widgets[] = {
 {   WWT_CLOSEBOX,   RESIZE_NONE,  7,   0,  10,   0,  13, STR_00C5,               STR_018B_CLOSE_WINDOW},                 // BSW_CLOSEBOX
-{    WWT_CAPTION,   RESIZE_NONE,  7,  11, 109,   0,  13, STR_SIGNAL_SELECTION,   STR_018C_WINDOW_TITLE_DRAG_THIS},       // BSW_CAPTION
+{    WWT_CAPTION,   RESIZE_NONE,  7,  11, 152,   0,  13, STR_SIGNAL_SELECTION,   STR_018C_WINDOW_TITLE_DRAG_THIS},       // BSW_CAPTION
 
 {      WWT_PANEL,   RESIZE_NONE,  7,   0,  21,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_NORM_TIP},   // BSW_SEMAPHORE_NORM
 {      WWT_PANEL,   RESIZE_NONE,  7,  22,  43,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_ENTRY_TIP},  // BSW_SEMAPHORE_ENTRY
 {      WWT_PANEL,   RESIZE_NONE,  7,  44,  65,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_EXIT_TIP},   // BSW_SEMAPHORE_EXIT
 {      WWT_PANEL,   RESIZE_NONE,  7,  66,  87,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_COMBO_TIP},  // BSW_SEMAPHORE_COMBO
+{      WWT_PANEL,   RESIZE_NONE,  7,  88, 109,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_PBS_TIP},    // BSW_SEMAPHORE_PBS
+{      WWT_PANEL,   RESIZE_NONE,  7, 109, 130,  14,  40, STR_NULL,               STR_BUILD_SIGNAL_SEMAPHORE_PBS_OWAY_TIP},// BSW_SEMAPHORE_PBS_OWAY
 
 {      WWT_PANEL,   RESIZE_NONE,  7,   0,  21,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_NORM_TIP},    // BSW_ELECTRIC_NORM
 {      WWT_PANEL,   RESIZE_NONE,  7,  22,  43,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_ENTRY_TIP},   // BSW_ELECTRIC_ENTRY
 {      WWT_PANEL,   RESIZE_NONE,  7,  44,  65,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_EXIT_TIP},    // BSW_ELECTRIC_EXIT
 {      WWT_PANEL,   RESIZE_NONE,  7,  66,  87,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_COMBO_TIP},   // BSW_ELECTRIC_COMBO
+{      WWT_PANEL,   RESIZE_NONE,  7,  88, 109,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_PBS_TIP},     // BSW_ELECTRIC_PBS
+{      WWT_PANEL,   RESIZE_NONE,  7, 109, 130,  41,  67, STR_NULL,               STR_BUILD_SIGNAL_ELECTRIC_PBS_OWAY_TIP},// BSW_ELECTRIC_PBS_OWAY
 
-{     WWT_IMGBTN,   RESIZE_NONE,  7,  88, 109,  14,  40, SPR_IMG_SIGNAL_CONVERT, STR_SIGNAL_CONVERT_TIP},                // BSW_CONVERT
-{      WWT_PANEL,   RESIZE_NONE,  7,  88, 109,  41,  67, STR_NULL,               STR_DRAG_SIGNALS_DENSITY_TIP},          // BSW_DRAG_SIGNALS_DENSITY
-{ WWT_PUSHIMGBTN,   RESIZE_NONE, 14,  90,  98,  54,  65, SPR_ARROW_LEFT,         STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_DECREASE
-{ WWT_PUSHIMGBTN,   RESIZE_NONE, 14,  99, 107,  54,  65, SPR_ARROW_RIGHT,        STR_DRAG_SIGNALS_DENSITY_INCREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_INCREASE
+{     WWT_IMGBTN,   RESIZE_NONE,  7, 131, 152,  14,  40, SPR_IMG_SIGNAL_CONVERT, STR_SIGNAL_CONVERT_TIP},                // BSW_CONVERT
+{      WWT_PANEL,   RESIZE_NONE,  7, 131, 152,  41,  67, STR_NULL,               STR_DRAG_SIGNALS_DENSITY_TIP},          // BSW_DRAG_SIGNALS_DENSITY
+{ WWT_PUSHIMGBTN,   RESIZE_NONE, 14, 133, 141,  54,  65, SPR_ARROW_LEFT,         STR_DRAG_SIGNALS_DENSITY_DECREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_DECREASE
+{ WWT_PUSHIMGBTN,   RESIZE_NONE, 14, 142, 150,  54,  65, SPR_ARROW_RIGHT,        STR_DRAG_SIGNALS_DENSITY_INCREASE_TIP}, // BSW_DRAG_SIGNALS_DENSITY_INCREASE
 
 {   WIDGETS_END},
 };
 
 /** Signal selection window description */
 static const WindowDesc _signal_builder_desc = {
-	WDP_AUTO, WDP_AUTO, 110, 68, 110, 68,
+	WDP_AUTO, WDP_AUTO, 153, 68, 153, 68,
 	WC_BUILD_SIGNAL, WC_BUILD_TOOLBAR,
 	WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS,
 	_signal_builder_widgets,
@@ -1775,6 +1801,6 @@ void InitializeRailGUI()
 	SetDefaultRailGui();
 
 	_convert_signal_button = false;
-	_cur_signal_type = SIGTYPE_NORMAL;
+	_cur_signal_type = _default_signal_type[_settings_client.gui.default_signal_type];
 	ResetSignalVariant();
 }
diff --git a/src/rail_map.h b/src/rail_map.h
index 8c436d0..ff9fcbb 100644
--- a/src/rail_map.h
+++ b/src/rail_map.h
@@ -224,19 +224,141 @@ static inline WaypointID GetWaypointIndex(TileIndex t)
 	return (WaypointID)_m[t].m2;
 }
 
+
+/**
+ * Returns the reserved track bits of the tile
+ * @pre IsPlainRailTile(t)
+ * @param t the tile to query
+ * @return the track bits
+ */
+static inline TrackBits GetTrackReservation(TileIndex t)
+{
+	assert(IsPlainRailTile(t));
+	byte track_b = GB(_m[t].m2, 8, 3);
+	Track track = (Track)(track_b - 1);    // map array saves Track+1
+	if (track_b == 0) return TRACK_BIT_NONE;
+	return (TrackBits)(TrackToTrackBits(track) | (HasBit(_m[t].m2, 11) ? TrackToTrackBits(TrackToOppositeTrack(track)) : 0));
+}
+
+/**
+ * Sets the reserved track bits of the tile
+ * @pre IsPlainRailTile(t) && !TracksOverlap(b)
+ * @param t the tile to change
+ * @param b the track bits
+ */
+static inline void SetTrackReservation(TileIndex t, TrackBits b)
+{
+	assert(IsPlainRailTile(t));
+	assert(b != INVALID_TRACK_BIT);
+	assert(!TracksOverlap(b));
+	Track track = RemoveFirstTrack(&b);
+	SB(_m[t].m2, 8, 3, track == INVALID_TRACK ? 0 : track+1);
+	SB(_m[t].m2, 11, 1, (byte)(b != TRACK_BIT_NONE));
+}
+
+/**
+ * Try to reserve a specific track on a tile
+ * @pre IsPlainRailTile(t) && HasTrack(tile, t)
+ * @param tile the tile
+ * @param t the rack to reserve
+ * @return true if successful
+ */
+static inline bool TryReserveTrack(TileIndex tile, Track t)
+{
+	assert(HasTrack(tile, t));
+	TrackBits bits = TrackToTrackBits(t);
+	TrackBits res = GetTrackReservation(tile);
+	if ((res & bits) != TRACK_BIT_NONE) return false;  // already reserved
+	res |= bits;
+	if (TracksOverlap(res)) return false;  // crossing reservation present
+	SetTrackReservation(tile, res);
+	return true;
+}
+
+/**
+ * Lift the reservation of a specific track on a tile
+ * @pre IsPlainRailTile(t) && HasTrack(tile, t)
+ * @param tile the tile
+ * @param t the track to free
+ */
+static inline void UnreserveTrack(TileIndex tile, Track t)
+{
+	assert(HasTrack(tile, t));
+	TrackBits res = GetTrackReservation(tile);
+	res &= ~TrackToTrackBits(t);
+	SetTrackReservation(tile, res);
+}
+
+/**
+ * Get the reservation state of the waypoint or depot
+ * @note Works for both waypoints and rail depots
+ * @pre IsRailWaypoint(t) || IsRailDepot(t)
+ * @param t the waypoint/depot tile
+ * @return reservation state
+ */
+static inline bool GetWaypointReservation(TileIndex t)
+{
+	assert(IsRailWaypoint(t) || IsRailDepot(t));
+	return HasBit(_m[t].m5, 4);
+}
+
+/**
+ * Set the reservation state of the waypoint or depot
+ * @note Works for both waypoints and rail depots
+ * @pre IsRailWaypoint(t) || IsRailDepot(t)
+ * @param t the waypoint/depot tile
+ * @param b the reservation state
+ */
+static inline void SetWaypointReservation(TileIndex t, bool b)
+{
+	assert(IsRailWaypoint(t) || IsRailDepot(t));
+	SB(_m[t].m5, 4, 1, (byte)b);
+}
+
+#define GetDepotReservation GetWaypointReservation
+#define SetDepotReservation SetWaypointReservation
+
+/**
+ * Get the reserved track bits for a waypoint
+ * @pre IsRailWaypoint(t)
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailWaypointReservation(TileIndex t)
+{
+	return GetWaypointReservation(t) ? GetRailWaypointBits(t) : TRACK_BIT_NONE;
+}
+
+/**
+ * Get the reserved track bits for a depot
+ * @pre IsRailDepot(t)
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailDepotReservation(TileIndex t)
+{
+	return GetWaypointReservation(t) ? TrackToTrackBits(GetRailDepotTrack(t)) : TRACK_BIT_NONE;
+}
+
+
+static inline bool IsPbsSignal(SignalType s)
+{
+	return s == SIGTYPE_PBS || s == SIGTYPE_PBS_ONEWAY;
+}
+
 static inline SignalType GetSignalType(TileIndex t, Track track)
 {
 	assert(GetRailTileType(t) == RAIL_TILE_SIGNALS);
 	byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0;
-	return (SignalType)GB(_m[t].m2, pos, 2);
+	return (SignalType)GB(_m[t].m2, pos, 3);
 }
 
 static inline void SetSignalType(TileIndex t, Track track, SignalType s)
 {
 	assert(GetRailTileType(t) == RAIL_TILE_SIGNALS);
 	byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 0;
-	SB(_m[t].m2, pos, 2, s);
-	if (track == INVALID_TRACK) SB(_m[t].m2, 4, 2, s);
+	SB(_m[t].m2, pos, 3, s);
+	if (track == INVALID_TRACK) SB(_m[t].m2, 4, 3, s);
 }
 
 static inline bool IsPresignalEntry(TileIndex t, Track track)
@@ -249,27 +371,33 @@ static inline bool IsPresignalExit(TileIndex t, Track track)
 	return GetSignalType(t, track) == SIGTYPE_EXIT || GetSignalType(t, track) == SIGTYPE_COMBO;
 }
 
+/** One-way signals can't be passed the 'wrong' way. */
+static inline bool IsOnewaySignal(TileIndex t, Track track)
+{
+	return GetSignalType(t, track) != SIGTYPE_PBS;
+}
+
 static inline void CycleSignalSide(TileIndex t, Track track)
 {
 	byte sig;
 	byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 4 : 6;
 
 	sig = GB(_m[t].m3, pos, 2);
-	if (--sig == 0) sig = 3;
+	if (--sig == 0) sig = IsPbsSignal(GetSignalType(t, track)) ? 2 : 3;
 	SB(_m[t].m3, pos, 2, sig);
 }
 
 static inline SignalVariant GetSignalVariant(TileIndex t, Track track)
 {
-	byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 6 : 2;
+	byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3;
 	return (SignalVariant)GB(_m[t].m2, pos, 1);
 }
 
 static inline void SetSignalVariant(TileIndex t, Track track, SignalVariant v)
 {
-	byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 6 : 2;
+	byte pos = (track == TRACK_LOWER || track == TRACK_RIGHT) ? 7 : 3;
 	SB(_m[t].m2, pos, 1, v);
-	if (track == INVALID_TRACK) SB(_m[t].m2, 6, 1, v);
+	if (track == INVALID_TRACK) SB(_m[t].m2, 7, 1, v);
 }
 
 /** These are states in which a signal can be. Currently these are only two, so
@@ -396,6 +524,34 @@ static inline void SetSignalStateByTrackdir(TileIndex tile, Trackdir trackdir, S
 	}
 }
 
+/**
+ * Is a pbs signal present along the trackdir?
+ * @param tile the tile to check
+ * @param td the trackdir to check
+ */
+static inline bool HasPbsSignalOnTrackdir(TileIndex tile, Trackdir td)
+{
+	return
+		IsTileType(tile, MP_RAILWAY) &&
+		HasSignalOnTrackdir(tile, td) &&
+		IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)));
+}
+
+/**
+ * Is a one-way signal blocking the trackdir? A one-way signal on the
+ * trackdir against will block, but signals on both trackdirs won't.
+ * @param tile the tile to check
+ * @param td the trackdir to check
+ */
+static inline bool HasOnewaySignalBlockingTrackdir(TileIndex tile, Trackdir td)
+{
+	return
+		IsTileType(tile, MP_RAILWAY) &&
+		HasSignalOnTrackdir(tile, ReverseTrackdir(td)) &&
+		!HasSignalOnTrackdir(tile, td) &&
+		IsOnewaySignal(tile, TrackdirToTrack(td));
+}
+
 
 /**
  * Return the rail type of tile, or INVALID_RAILTYPE if this is no rail tile.
diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp
index 9fbf16c..3449925 100644
--- a/src/road_cmd.cpp
+++ b/src/road_cmd.cpp
@@ -344,7 +344,10 @@ static CommandCost RemoveRoad(TileIndex tile, uint32 flags, RoadBits pieces, Roa
 			if (flags & DC_EXEC) {
 				RoadTypes rts = GetRoadTypes(tile) & ComplementRoadTypes(RoadTypeToRoadTypes(rt));
 				if (rts == ROADTYPES_NONE) {
-					MakeRailNormal(tile, GetTileOwner(tile), GetCrossingRailBits(tile), GetRailType(tile));
+					TrackBits tracks = GetCrossingRailBits(tile);
+					bool reserved = GetCrossingReservation(tile);
+					MakeRailNormal(tile, GetTileOwner(tile), tracks, GetRailType(tile));
+					if (reserved) SetTrackReservation(tile, tracks);
 				} else {
 					SetRoadTypes(tile, rts);
 				}
@@ -562,7 +565,9 @@ CommandCost CmdBuildRoad(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 			if (flags & DC_EXEC) {
 				YapfNotifyTrackLayoutChange(tile, FindFirstTrack(GetTrackBits(tile)));
 				/* Always add road to the roadtypes (can't draw without it) */
+				bool reserved = HasBit(GetTrackReservation(tile), AxisToTrack(OtherAxis(roaddir)));
 				MakeRoadCrossing(tile, _current_player, _current_player, _current_player, GetTileOwner(tile), roaddir, GetRailType(tile), RoadTypeToRoadTypes(rt) | ROADTYPES_ROAD, p2);
+				SetCrossingReservation(tile, reserved);
 				UpdateLevelCrossing(tile, false);
 				MarkTileDirtyByTile(tile);
 			}
@@ -1169,6 +1174,12 @@ static void DrawTile_Road(TileInfo *ti)
 			}
 
 			DrawGroundSprite(image, pal);
+
+			/* PBS debugging, draw reserved tracks darker */
+			if (_settings_client.gui.show_track_reservation && GetCrossingReservation(ti->tile)) {
+				DrawGroundSprite(GetCrossingRoadAxis(ti->tile) == AXIS_Y ? GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_y : GetRailTypeInfo(GetRailType(ti->tile))->base_sprites.single_x, PALETTE_CRASH);
+			}
+
 			if (HasTileRoadType(ti->tile, ROADTYPE_TRAM)) {
 				DrawGroundSprite(SPR_TRAMWAY_OVERLAY + (GetCrossingRoadAxis(ti->tile) ^ 1), pal);
 				DrawTramCatenary(ti, GetCrossingRoadBits(ti->tile));
diff --git a/src/road_map.h b/src/road_map.h
index 756e3fc..b8a7b2f 100644
--- a/src/road_map.h
+++ b/src/road_map.h
@@ -253,6 +253,43 @@ static inline TrackBits GetCrossingRailBits(TileIndex tile)
 	return AxisToTrackBits(GetCrossingRailAxis(tile));
 }
 
+
+/**
+ * Get the reservation state of the rail crossing
+ * @pre IsLevelCrossingTile(t)
+ * @param t the crossing tile
+ * @return reservation state
+ */
+static inline bool GetCrossingReservation(TileIndex t)
+{
+	assert(IsLevelCrossingTile(t));
+	return HasBit(_m[t].m5, 4);
+}
+
+/**
+ * Set the reservation state of the rail crossing
+ * @note Works for both waypoints and rail depots
+ * @pre IsLevelCrossingTile(t)
+ * @param t the crossing tile
+ * @param b the reservation state
+ */
+static inline void SetCrossingReservation(TileIndex t, bool b)
+{
+	assert(IsLevelCrossingTile(t));
+	SB(_m[t].m5, 4, 1, (byte)b & 1);
+}
+
+/**
+ * Get the reserved track bits for a rail crossing
+ * @pre IsLevelCrossingTile(t)
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailCrossingReservation(TileIndex t)
+{
+	return GetCrossingReservation(t) ? GetCrossingRailBits(t) : TRACK_BIT_NONE;
+}
+
 static inline bool IsCrossingBarred(TileIndex t)
 {
 	assert(IsLevelCrossing(t));
diff --git a/src/saveload.cpp b/src/saveload.cpp
index dd08245..640ebcc 100644
--- a/src/saveload.cpp
+++ b/src/saveload.cpp
@@ -36,7 +36,7 @@
 
 #include "table/strings.h"
 
-extern const uint16 SAVEGAME_VERSION = 98;
+extern const uint16 SAVEGAME_VERSION = 99;
 
 SavegameType _savegame_type; ///< type of savegame we are loading
 
diff --git a/src/settings.cpp b/src/settings.cpp
index a0ecdcf..e8d444f 100644
--- a/src/settings.cpp
+++ b/src/settings.cpp
@@ -1689,6 +1689,10 @@ const SettingDesc _patch_settings[] = {
 	     SDT_VAR(GameSettings, pf.wait_twoway_signal,                SLE_UINT8,                     0, 0,    41,     2,     100, 0, STR_NULL,                                  NULL),
 	SDT_CONDLISTO(GameSettings, economy.town_noise_population, 3,   SLE_UINT16, 96, SL_MAX_VERSION, 0,D0, "800,2000,4000",          STR_NULL,                                  NULL, CheckNoiseToleranceLevel),
 
+	 SDT_CONDVAR(GameSettings, pf.wait_for_pbs_path,                 SLE_UINT8, 99, SL_MAX_VERSION, 0, 0,    30,     2,     255, 0, STR_NULL,                                  NULL),
+	SDT_CONDBOOL(GameSettings, pf.reserve_paths,                                99, SL_MAX_VERSION, 0, 0, false,                    STR_NULL,                                  NULL),
+	 SDT_CONDVAR(GameSettings, pf.path_backoff_interval,             SLE_UINT8, 99, SL_MAX_VERSION, 0, 0,    20,     1,     100, 0, STR_NULL,                                  NULL),
+
 	     SDT_VAR(GameSettings, pf.opf.pf_maxlength,                          SLE_UINT16,                     0, 0,  4096,                    64,   65535, 0, STR_NULL,         NULL),
 	     SDT_VAR(GameSettings, pf.opf.pf_maxdepth,                            SLE_UINT8,                     0, 0,    48,                     4,     255, 0, STR_NULL,         NULL),
 
@@ -1700,6 +1704,8 @@ const SettingDesc _patch_settings[] = {
 	     SDT_VAR(GameSettings, pf.npf.npf_rail_slope_penalty,                  SLE_UINT,                     0, 0, (  1 * NPF_TILE_LENGTH),   0,  100000, 0, STR_NULL,         NULL),
 	     SDT_VAR(GameSettings, pf.npf.npf_rail_curve_penalty,                  SLE_UINT,                     0, 0, 1,                         0,  100000, 0, STR_NULL,         NULL),
 	     SDT_VAR(GameSettings, pf.npf.npf_rail_depot_reverse_penalty,          SLE_UINT,                     0, 0, ( 50 * NPF_TILE_LENGTH),   0,  100000, 0, STR_NULL,         NULL),
+	 SDT_CONDVAR(GameSettings, pf.npf.npf_rail_pbs_cross_penalty,              SLE_UINT, 99, SL_MAX_VERSION, 0, 0, (  3 * NPF_TILE_LENGTH),   0,  100000, 0, STR_NULL,         NULL),
+	 SDT_CONDVAR(GameSettings, pf.npf.npf_rail_pbs_signal_back_penalty,        SLE_UINT, 99, SL_MAX_VERSION, 0, 0, ( 15 * NPF_TILE_LENGTH),   0,  100000, 0, STR_NULL,         NULL),
 	     SDT_VAR(GameSettings, pf.npf.npf_buoy_penalty,                        SLE_UINT,                     0, 0, (  2 * NPF_TILE_LENGTH),   0,  100000, 0, STR_NULL,         NULL),
 	     SDT_VAR(GameSettings, pf.npf.npf_water_curve_penalty,                 SLE_UINT,                     0, 0, (NPF_TILE_LENGTH / 4),     0,  100000, 0, STR_NULL,         NULL),
 	     SDT_VAR(GameSettings, pf.npf.npf_road_curve_penalty,                  SLE_UINT,                     0, 0, 1,                         0,  100000, 0, STR_NULL,         NULL),
@@ -1714,9 +1720,9 @@ const SettingDesc _patch_settings[] = {
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_firstred_exit_penalty,             SLE_UINT, 28, SL_MAX_VERSION, 0, 0,   100 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_lastred_penalty,                   SLE_UINT, 28, SL_MAX_VERSION, 0, 0,    10 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_lastred_exit_penalty,              SLE_UINT, 28, SL_MAX_VERSION, 0, 0,   100 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
-	 SDT_CONDVAR(GameSettings, pf.yapf.rail_station_penalty,                   SLE_UINT, 28, SL_MAX_VERSION, 0, 0,    30 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
+	 SDT_CONDVAR(GameSettings, pf.yapf.rail_station_penalty,                   SLE_UINT, 28, SL_MAX_VERSION, 0, 0,    10 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_slope_penalty,                     SLE_UINT, 28, SL_MAX_VERSION, 0, 0,     2 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
-	 SDT_CONDVAR(GameSettings, pf.yapf.rail_curve45_penalty,                   SLE_UINT, 28, SL_MAX_VERSION, 0, 0,     1 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
+	 SDT_CONDVAR(GameSettings, pf.yapf.rail_curve45_penalty,                   SLE_UINT, 28, SL_MAX_VERSION, 0, 0,     3 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_curve90_penalty,                   SLE_UINT, 28, SL_MAX_VERSION, 0, 0,     6 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_depot_reverse_penalty,             SLE_UINT, 28, SL_MAX_VERSION, 0, 0,    50 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_crossing_penalty,                  SLE_UINT, 28, SL_MAX_VERSION, 0, 0,     3 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
@@ -1724,6 +1730,10 @@ const SettingDesc _patch_settings[] = {
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_look_ahead_signal_p0,               SLE_INT, 28, SL_MAX_VERSION, 0, 0,   500,              -1000000, 1000000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_look_ahead_signal_p1,               SLE_INT, 28, SL_MAX_VERSION, 0, 0,  -100,              -1000000, 1000000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_look_ahead_signal_p2,               SLE_INT, 28, SL_MAX_VERSION, 0, 0,     5,              -1000000, 1000000, 0, STR_NULL,         NULL),
+	 SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_cross_penalty,                 SLE_UINT, 99, SL_MAX_VERSION, 0, 0,     3 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
+	 SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_station_penalty,               SLE_UINT, 99, SL_MAX_VERSION, 0, 0,     8 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
+	 SDT_CONDVAR(GameSettings, pf.yapf.rail_pbs_signal_back_penalty,           SLE_UINT, 99, SL_MAX_VERSION, 0, 0,    15 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
+	 SDT_CONDVAR(GameSettings, pf.yapf.rail_doubleslip_penalty,                SLE_UINT, 99, SL_MAX_VERSION, 0, 0,     1 * YAPF_TILE_LENGTH,  0, 1000000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_longer_platform_penalty,           SLE_UINT, 33, SL_MAX_VERSION, 0, 0,     8 * YAPF_TILE_LENGTH,  0,   20000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_longer_platform_per_tile_penalty,  SLE_UINT, 33, SL_MAX_VERSION, 0, 0,     0 * YAPF_TILE_LENGTH,  0,   20000, 0, STR_NULL,         NULL),
 	 SDT_CONDVAR(GameSettings, pf.yapf.rail_shorter_platform_penalty,          SLE_UINT, 33, SL_MAX_VERSION, 0, 0,    40 * YAPF_TILE_LENGTH,  0,   20000, 0, STR_NULL,         NULL),
@@ -1788,6 +1798,9 @@ const SettingDesc _patch_settings[] = {
 	 SDTC_BOOL(gui.bridge_pillars,                       S,  0,  true,                        STR_NULL,                                       NULL),
 	 SDTC_BOOL(gui.auto_euro,                            S,  0,  true,                        STR_NULL,                                       NULL),
 	  SDTC_VAR(gui.news_message_timeout,      SLE_UINT8, S,  0,     2,        1,      255, 0, STR_NULL,                                       NULL),
+	 SDTC_BOOL(gui.show_track_reservation,               S,  0, false,                        STR_CONFIG_PATCHES_SHOW_TRACK_RESERVATION,      RedrawScreen),
+	  SDTC_VAR(gui.default_signal_type,       SLE_UINT8, S, MS,     0,        0,        2, 1, STR_CONFIG_PATCHES_DEFAULT_SIGNAL_TYPE,         NULL),
+	  SDTC_VAR(gui.cycle_signal_types,        SLE_UINT8, S, MS,     0,        0,        2, 1, STR_CONFIG_PATCHES_CYCLE_SIGNAL_TYPES,          NULL),
 
 #ifdef ENABLE_NETWORK
 	  SDTC_VAR(network.sync_freq,            SLE_UINT16,C|S,NO,   100,        0,      100, 0, STR_NULL,                                       NULL),
diff --git a/src/settings_gui.cpp b/src/settings_gui.cpp
index 378209a..02ea4a3 100644
--- a/src/settings_gui.cpp
+++ b/src/settings_gui.cpp
@@ -612,6 +612,7 @@ static const char *_patches_ui[] = {
 	"gui.timetable_in_ticks",
 	"gui.default_rail_type",
 	"gui.always_build_infrastructure",
+	"gui.show_track_reservation",
 };
 
 static const char *_patches_construction[] = {
@@ -625,6 +626,8 @@ static const char *_patches_construction[] = {
 	"gui.drag_signals_density",
 	"game_creation.oil_refinery_limit",
 	"gui.semaphore_build_before",
+	"gui.default_signal_type",
+	"gui.cycle_signal_types",
 };
 
 static const char *_patches_stations[] = {
diff --git a/src/settings_type.h b/src/settings_type.h
index 0926db6..c7341cd 100644
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -79,6 +79,9 @@ struct GUISettings {
 	int16  autorenew_months;                 ///< how many months from EOL of vehicles should autorenew trigger for new companies?
 	int32  autorenew_money;                  ///< how much money before autorenewing for new companies?
 	byte   news_message_timeout;             ///< how much longer than the news message "age" should we keep the message in the history
+	bool   show_track_reservation;           ///< highlight reserved tracks.
+	uint8  default_signal_type;              ///< the signal type to build by default.
+	uint8  cycle_signal_types;               ///< what signal types to cycle with the build signal tool.
 };
 
 /** Settings related to currency/unit systems. */
@@ -182,6 +185,8 @@ struct NPFSettings {
 	uint32 npf_rail_slope_penalty;           ///< the penalty for sloping upwards
 	uint32 npf_rail_curve_penalty;           ///< the penalty for curves
 	uint32 npf_rail_depot_reverse_penalty;   ///< the penalty for reversing in depots
+	uint32 npf_rail_pbs_cross_penalty;       ///< the penalty for crossing a reserved rail track
+	uint32 npf_rail_pbs_signal_back_penalty; ///< the penalty for passing a pbs signal from the backside
 	uint32 npf_buoy_penalty;                 ///< the penalty for going over (through) a buoy
 	uint32 npf_water_curve_penalty;          ///< the penalty for curves
 	uint32 npf_road_curve_penalty;           ///< the penalty for curves
@@ -215,6 +220,10 @@ struct YAPFSettings {
 	int32  rail_look_ahead_signal_p0;        ///< constant in polynomial penalty function
 	int32  rail_look_ahead_signal_p1;        ///< constant in polynomial penalty function
 	int32  rail_look_ahead_signal_p2;        ///< constant in polynomial penalty function
+	uint32 rail_pbs_cross_penalty;           ///< penalty for crossing a reserved tile
+	uint32 rail_pbs_station_penalty;         ///< penalty for crossing a reserved station tile
+	uint32 rail_pbs_signal_back_penalty;     ///< penalty for passing a pbs signal from the backside
+	uint32 rail_doubleslip_penalty;          ///< penalty for passing a double slip switch
 
 	uint32 rail_longer_platform_penalty;           ///< penalty for longer  station platform than train
 	uint32 rail_longer_platform_per_tile_penalty;  ///< penalty for longer  station platform than train (per tile)
@@ -235,6 +244,10 @@ struct PathfinderSettings {
 	byte   wait_oneway_signal;               ///< waitingtime in days before a oneway signal
 	byte   wait_twoway_signal;               ///< waitingtime in days before a twoway signal
 
+	bool   reserve_paths;                    ///< always reserve paths regardless of signal type.
+	byte   wait_for_pbs_path;                ///< how long to wait for a path reservation.
+	byte   path_backoff_interval;            ///< ticks between checks for a free path.
+
 	OPFSettings  opf;                        ///< pathfinder settings for the old pathfinder
 	NPFSettings  npf;                        ///< pathfinder settings for the new pathfinder
 	YAPFSettings yapf;                       ///< pathfinder settings for the yet another pathfinder
diff --git a/src/signal.cpp b/src/signal.cpp
index 5347964..a45621b 100644
--- a/src/signal.cpp
+++ b/src/signal.cpp
@@ -256,6 +256,7 @@ enum SigFlags {
 	SF_GREEN  = 1 << 3, ///< green exitsignal found
 	SF_GREEN2 = 1 << 4, ///< two or more green exits found
 	SF_FULL   = 1 << 5, ///< some of buffers was full, do not continue
+	SF_PBS    = 1 << 6, ///< pbs signal found
 };
 
 DECLARE_ENUM_AS_BIT_SET(SigFlags)
@@ -322,13 +323,19 @@ static SigFlags ExploreSegment(Owner owner)
 						Trackdir trackdir = (Trackdir)FindFirstBit((tracks * 0x101) & _enterdir_to_trackdirbits[enterdir]);
 						Trackdir reversedir = ReverseTrackdir(trackdir);
 						/* add (tile, reversetrackdir) to 'to-be-updated' set when there is
-						 * ANY signal in REVERSE direction
+						 * ANY conventional signal in REVERSE direction
 						 * (if it is a presignal EXIT and it changes, it will be added to 'to-be-done' set later) */
 						if (HasSignalOnTrackdir(tile, reversedir)) {
-							if (!_tbuset.Add(tile, reversedir)) return flags | SF_FULL;
+							if (IsPbsSignal(sig)) {
+								flags |= SF_PBS;
+							} else if (!_tbuset.Add(tile, reversedir)) {
+								return flags | SF_FULL;
+							}
 						}
+						if (HasSignalOnTrackdir(tile, trackdir) && !IsOnewaySignal(tile, track)) flags |= SF_PBS;
+
 						/* if it is a presignal EXIT in OUR direction and we haven't found 2 green exits yes, do special check */
-						if (!(flags & SF_GREEN2) && (sig & SIGTYPE_EXIT) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit
+						if (!(flags & SF_GREEN2) && IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, trackdir)) { // found presignal exit
 							if (flags & SF_EXIT) flags |= SF_EXIT2; // found two (or more) exits
 							flags |= SF_EXIT; // found at least one exit - allow for compiler optimizations
 							if (GetSignalStateByTrackdir(tile, trackdir) == SIGNAL_STATE_GREEN) { // found green presignal exit
@@ -336,6 +343,7 @@ static SigFlags ExploreSegment(Owner owner)
 								flags |= SF_GREEN;
 							}
 						}
+
 						continue;
 					}
 				}
@@ -433,13 +441,13 @@ static void UpdateSignalsAroundSegment(SigFlags flags)
 					newstate = SIGNAL_STATE_RED;
 				}
 			} else { // entry, at least one exit, no green exit
-				if (sig & SIGTYPE_ENTRY && (flags & SF_EXIT && !(flags & SF_GREEN))) newstate = SIGNAL_STATE_RED;
+				if (IsPresignalEntry(tile, TrackdirToTrack(trackdir)) && flags & SF_EXIT && !(flags & SF_GREEN)) newstate = SIGNAL_STATE_RED;
 			}
 		}
 
 		/* only when the state changes */
 		if (newstate != GetSignalStateByTrackdir(tile, trackdir)) {
-			if (sig & SIGTYPE_EXIT) {
+			if (IsPresignalExit(tile, TrackdirToTrack(trackdir))) {
 				/* for pre-signal exits, add block to the global set */
 				DiagDirection exitdir = TrackdirToExitdir(ReverseTrackdir(trackdir));
 				_globset.Add(tile, exitdir); // do not check for full global set, first update all signals
@@ -532,8 +540,10 @@ static SigSegState UpdateSignalsInBuffer(Owner owner)
 
 		if (first) {
 			first = false;
-			if ((flags & SF_TRAIN) || (flags & SF_EXIT && !(flags & SF_GREEN)) || (flags & SF_FULL)) {
-				/* SIGSEG_FREE is set by default */
+			/* SIGSEG_FREE is set by default */
+			if (flags & SF_PBS) {
+				state = SIGSEG_PBS;
+			} else if (flags & SF_TRAIN || (flags & SF_EXIT && !(flags & SF_GREEN)) || flags & SF_FULL) {
 				state = SIGSEG_FULL;
 			}
 		}
diff --git a/src/signal_func.h b/src/signal_func.h
index 7dcf3fd..7e35168 100644
--- a/src/signal_func.h
+++ b/src/signal_func.h
@@ -45,6 +45,7 @@ static inline byte SignalOnTrack(Track track)
 enum SigSegState {
 	SIGSEG_FREE,    ///< Free and has no pre-signal exits or at least one green exit
 	SIGSEG_FULL,    ///< Occupied by a train
+	SIGSEG_PBS,     ///< Segment is a PBS segment
 };
 
 SigSegState UpdateSignalsOnSegment(TileIndex tile, DiagDirection side, Owner owner);
diff --git a/src/signal_type.h b/src/signal_type.h
index 5dccff6..0bcd107 100644
--- a/src/signal_type.h
+++ b/src/signal_type.h
@@ -14,10 +14,14 @@ enum SignalVariant {
 
 /** Type of signal, i.e. how does the signal behave? */
 enum SignalType {
-	SIGTYPE_NORMAL  = 0, ///< normal signal
-	SIGTYPE_ENTRY   = 1, ///< presignal block entry
-	SIGTYPE_EXIT    = 2, ///< presignal block exit
-	SIGTYPE_COMBO   = 3  ///< presignal inter-block
+	SIGTYPE_NORMAL     = 0, ///< normal signal
+	SIGTYPE_ENTRY      = 1, ///< presignal block entry
+	SIGTYPE_EXIT       = 2, ///< presignal block exit
+	SIGTYPE_COMBO      = 3, ///< presignal inter-block
+	SIGTYPE_PBS        = 4, ///< normal pbs signal
+	SIGTYPE_PBS_ONEWAY = 5, ///< no-entry signal
+	SIGTYPE_LAST       = SIGTYPE_PBS_ONEWAY,
+	SIGTYPE_LAST_NOPBS = SIGTYPE_COMBO
 };
 
 
diff --git a/src/signs_func.h b/src/signs_func.h
index 519a470..5a38ba7 100644
--- a/src/signs_func.h
+++ b/src/signs_func.h
@@ -15,7 +15,6 @@ void PlaceProc_Sign(TileIndex tile);
 
 /* signs_gui.cpp */
 void ShowRenameSignWindow(const Sign *si);
-void HandleClickOnSign(const Sign *si);
 
 void ShowSignList();
 
diff --git a/src/signs_gui.cpp b/src/signs_gui.cpp
index 7a0e9c4..8a9aae2 100644
--- a/src/signs_gui.cpp
+++ b/src/signs_gui.cpp
@@ -8,7 +8,6 @@
 #include "textbuf_gui.h"
 #include "window_gui.h"
 #include "player_gui.h"
-#include "player_func.h"
 #include "signs_base.h"
 #include "signs_func.h"
 #include "debug.h"
@@ -302,15 +301,6 @@ static const WindowDesc _query_sign_edit_desc = {
 	_query_sign_edit_widgets,
 };
 
-void HandleClickOnSign(const Sign *si)
-{
-	if (_ctrl_pressed && si->owner == _local_player) {
-		RenameSign(si->index, "");
-		return;
-	}
-	ShowRenameSignWindow(si);
-}
-
 void ShowRenameSignWindow(const Sign *si)
 {
 	/* Delete all other edit windows and the save window */
diff --git a/src/slope_func.h b/src/slope_func.h
index 99722f6..7bfa5e5 100644
--- a/src/slope_func.h
+++ b/src/slope_func.h
@@ -347,11 +347,13 @@ static inline Corner GetRailFoundationCorner(Foundation f)
  * Returns the foundation needed to flatten a slope.
  * The returned foundation is either FOUNDATION_NONE if the tile was already flat, or FOUNDATION_LEVELED.
  *
+ * @pre      The slope must not be steep.
  * @param s  The current #Slope.
  * @return   The needed #Foundation.
  */
 static inline Foundation FlatteningFoundation(Slope s)
 {
+	assert(!IsSteepSlope(s));
 	return (s == SLOPE_FLAT ? FOUNDATION_NONE : FOUNDATION_LEVELED);
 }
 
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index be9b30f..5ef6498 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -43,6 +43,7 @@
 #include "oldpool_func.h"
 #include "animated_tile_func.h"
 #include "elrail_func.h"
+#include <vector>
 
 #include "table/sprites.h"
 #include "table/strings.h"
@@ -928,6 +929,20 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1,
 		if (st == CHECK_STATIONS_ERR) return CMD_ERROR;
 	}
 
+	std::vector<bool> reserved_tiles;
+	if (_settings_game.station.nonuniform_stations) {
+		reserved_tiles.resize(w_org * h_org, false);
+		/* Overbuilding of existing station tiles is possible, check for reserved tiles. */
+		BEGIN_TILE_LOOP(tile_cur, w_org, h_org, tile_org) {
+			if (IsRailwayStationTile(tile_cur)) {
+				bool reserved = GetRailwayStationReservation(tile_cur);
+				/* A reserved tile with the wrong axis can't be overbuilt. */
+				if (reserved && axis != GetRailStationAxis(tile_cur)) return_cmd_error(STR_TRAIN_IS_APPROACHING);
+				reserved_tiles[(h_org - h_cur) * w_org + w_org - w_cur] = reserved;
+			}
+		} END_TILE_LOOP(tile_cur, w_org, h_org, tile_org)
+	}
+
 	/* See if there is a deleted station close to us. */
 	if (st == NULL) st = GetClosestStationFromTile(tile_org);
 
@@ -1018,6 +1033,7 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1,
 
 		numtracks_orig = numtracks;
 
+		TileIndex start_tile = tile_org;
 		do {
 			TileIndex tile = tile_org;
 			int w = plat_len;
@@ -1027,6 +1043,11 @@ CommandCost CmdBuildRailroadStation(TileIndex tile_org, uint32 flags, uint32 p1,
 				SetCustomStationSpecIndex(tile, specindex);
 				SetStationTileRandomBits(tile, GB(Random(), 0, 4));
 				SetStationAnimationFrame(tile, 0);
+				if (_settings_game.station.nonuniform_stations) {
+					/* Restore saved reservation state. */
+					TileIndexDiffC diff = TileIndexToTileIndexDiffC(tile, start_tile);
+					SetRailwayStationReservation(tile, reserved_tiles[diff.y * w_org + diff.x]);
+				}
 
 				if (statspec != NULL) {
 					/* Use a fixed axis for GetPlatformInfo as our platforms / numtracks are always the right way around */
@@ -1148,6 +1169,9 @@ CommandCost CmdRemoveFromRailroadStation(TileIndex tile, uint32 flags, uint32 p1
 			continue;
 		}
 
+		/* Don't remove tile if it is reserved. */
+		if (GetRailwayStationReservation(tile2)) continue;
+
 		/* If there is a vehicle on ground, do not allow to remove (flood) the tile */
 		if (!EnsureNoVehicleOnGround(tile2)) {
 			continue;
@@ -2211,6 +2235,12 @@ static void DrawTile_Station(TileInfo *ti)
 			image += total_offset;
 		}
 		DrawGroundSprite(image, HasBit(image, PALETTE_MODIFIER_COLOR) ? palette : PAL_NONE);
+
+		/* PBS debugging, draw reserved tracks darker */
+		if (_settings_client.gui.show_track_reservation && IsRailwayStation(ti->tile) && GetRailwayStationReservation(ti->tile)) {
+			const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+			DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH);
+		}
 	}
 
 	if (IsRailwayStation(ti->tile) && HasCatenaryDrawn(GetRailType(ti->tile)) && IsStationTileElectrifiable(ti->tile)) DrawCatenary(ti);
diff --git a/src/station_map.h b/src/station_map.h
index 0ec6797..6d95e96 100644
--- a/src/station_map.h
+++ b/src/station_map.h
@@ -204,6 +204,41 @@ static inline bool IsCompatibleTrainStationTile(TileIndex t1, TileIndex t2)
 		!IsStationTileBlocked(t1);
 }
 
+/**
+ * Get the reservation state of the rail station
+ * @pre IsRailwayStationTile(t)
+ * @param t the station tile
+ * @return reservation state
+ */
+static inline bool GetRailwayStationReservation(TileIndex t)
+{
+	assert(IsRailwayStationTile(t));
+	return HasBit(_m[t].m6, 2);
+}
+
+/**
+ * Set the reservation state of the rail station
+ * @pre IsRailwayStationTile(t)
+ * @param t the station tile
+ * @param b the reservation state
+ */
+static inline void SetRailwayStationReservation(TileIndex t, bool b)
+{
+	assert(IsRailwayStationTile(t));
+	SB(_m[t].m6, 2, 1, (byte)b & 1);
+}
+
+/**
+ * Get the reserved track bits for a waypoint
+ * @pre IsRailwayStationTile(t)
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailStationReservation(TileIndex t)
+{
+	return GetRailwayStationReservation(t) ? AxisToTrackBits(GetRailStationAxis(t)) : TRACK_BIT_NONE;
+}
+
 
 static inline DiagDirection GetDockDirection(TileIndex t)
 {
@@ -277,6 +312,7 @@ static inline void MakeRailStation(TileIndex t, Owner o, StationID sid, Axis a,
 {
 	MakeStation(t, o, sid, STATION_RAIL, section + a);
 	SetRailType(t, rt);
+	SetRailwayStationReservation(t, false);
 }
 
 static inline void MakeRoadStop(TileIndex t, Owner o, StationID sid, RoadStopType rst, RoadTypes rt, DiagDirection d)
diff --git a/src/table/sprites.h b/src/table/sprites.h
index 7e73cdd..da4d14c 100644
--- a/src/table/sprites.h
+++ b/src/table/sprites.h
@@ -1297,15 +1297,19 @@ enum Sprites {
 	SPR_IMG_RAIL_STATION = 1298,
 	SPR_IMG_RAIL_SIGNALS = 1291,
 
-	SPR_IMG_SIGNAL_ELECTRIC_NORM   = 1287,
-	SPR_IMG_SIGNAL_ELECTRIC_ENTRY  = SPR_SIGNALS_BASE +  12,
-	SPR_IMG_SIGNAL_ELECTRIC_EXIT   = SPR_SIGNALS_BASE +  28,
-	SPR_IMG_SIGNAL_ELECTRIC_COMBO  = SPR_SIGNALS_BASE +  44,
-	SPR_IMG_SIGNAL_SEMAPHORE_NORM  = SPR_SIGNALS_BASE +  60,
-	SPR_IMG_SIGNAL_SEMAPHORE_ENTRY = SPR_SIGNALS_BASE +  76,
-	SPR_IMG_SIGNAL_SEMAPHORE_EXIT  = SPR_SIGNALS_BASE +  92,
-	SPR_IMG_SIGNAL_SEMAPHORE_COMBO = SPR_SIGNALS_BASE + 108,
-	SPR_IMG_SIGNAL_CONVERT         = SPR_OPENTTD_BASE + 135,
+	SPR_IMG_SIGNAL_ELECTRIC_NORM     = 1287,
+	SPR_IMG_SIGNAL_ELECTRIC_ENTRY    = SPR_SIGNALS_BASE +  12,
+	SPR_IMG_SIGNAL_ELECTRIC_EXIT     = SPR_SIGNALS_BASE +  28,
+	SPR_IMG_SIGNAL_ELECTRIC_COMBO    = SPR_SIGNALS_BASE +  44,
+	SPR_IMG_SIGNAL_ELECTRIC_PBS      = SPR_SIGNALS_BASE + 124,
+	SPR_IMG_SIGNAL_ELECTRIC_OBSOWAY  = SPR_SIGNALS_BASE + 140,
+	SPR_IMG_SIGNAL_SEMAPHORE_NORM    = SPR_SIGNALS_BASE +  60,
+	SPR_IMG_SIGNAL_SEMAPHORE_ENTRY   = SPR_SIGNALS_BASE +  76,
+	SPR_IMG_SIGNAL_SEMAPHORE_EXIT    = SPR_SIGNALS_BASE +  92,
+	SPR_IMG_SIGNAL_SEMAPHORE_COMBO   = SPR_SIGNALS_BASE + 108,
+	SPR_IMG_SIGNAL_SEMAPHORE_PBS     = SPR_SIGNALS_BASE + 188,
+	SPR_IMG_SIGNAL_SEMAPHORE_PBSOWAY = SPR_SIGNALS_BASE + 204,
+	SPR_IMG_SIGNAL_CONVERT           = SPR_OPENTTD_BASE + 135,
 
 	SPR_IMG_TUNNEL_RAIL   = 2430,
 	SPR_IMG_TUNNEL_MONO   = 2431,
diff --git a/src/terraform_gui.cpp b/src/terraform_gui.cpp
index a6c4929..c341d6c 100644
--- a/src/terraform_gui.cpp
+++ b/src/terraform_gui.cpp
@@ -135,7 +135,7 @@ bool GUIPlaceProcDragXY(ViewportDragDropSelectionProcess proc, TileIndex start_t
 			DoCommandP(end_tile, start_tile, _ctrl_pressed, CcBuildCanal, CMD_BUILD_CANAL | CMD_MSG(STR_CANT_BUILD_CANALS));
 			break;
 		case DDSP_CREATE_RIVER:
-			DoCommandP(end_tile, start_tile, 2, CcBuildCanal, CMD_BUILD_CANAL | CMD_MSG(STR_CANT_PLACE_RIVERS));
+			DoCommandP(end_tile, start_tile, 2, CcBuildCanal, CMD_BUILD_CANAL | CMD_MSG(STR_CANT_BUILD_CANALS));
 			break;
 		default:
 			return false;
diff --git a/src/toolbar_gui.cpp b/src/toolbar_gui.cpp
index 5cec2aa..f980c69 100644
--- a/src/toolbar_gui.cpp
+++ b/src/toolbar_gui.cpp
@@ -58,22 +58,6 @@ enum ToolbarMode {
 	TB_LOWER
 };
 
-enum ToolbarScenEditorWidgets {
-	TBSE_PAUSE        = 0,
-	TBSE_FASTFORWARD,
-	TBSE_SPACERPANEL  = 4,
-	TBSE_DATEBACKWARD = 6,
-	TBSE_DATEFORWARD,
-	TBSE_ZOOMIN       = 9,
-	TBSE_ZOOMOUT,
-	TBSE_LANDGENERATE,
-	TBSE_TOWNGENERATE,
-	TBSE_INDUSTRYGENERATE,
-	TBSE_BUILDROAD,
-	TBSE_PLANTTREES,
-	TBSE_PLACESIGNS,
-};
-
 static ToolbarMode _toolbar_mode;
 
 static void SelectSignTool()
@@ -583,7 +567,7 @@ static void ToolbarScenDateBackward(Window *w)
 {
 	/* don't allow too fast scrolling */
 	if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
-		w->HandleButtonClick(TBSE_DATEBACKWARD);
+		w->HandleButtonClick(6);
 		w->SetDirty();
 
 		_settings_newgame.game_creation.starting_year = Clamp(_settings_newgame.game_creation.starting_year - 1, MIN_YEAR, MAX_YEAR);
@@ -596,7 +580,7 @@ static void ToolbarScenDateForward(Window *w)
 {
 	/* don't allow too fast scrolling */
 	if ((w->flags4 & WF_TIMEOUT_MASK) <= 2 << WF_TIMEOUT_SHL) {
-		w->HandleButtonClick(TBSE_DATEFORWARD);
+		w->HandleButtonClick(7);
 		w->SetDirty();
 
 		_settings_newgame.game_creation.starting_year = Clamp(_settings_newgame.game_creation.starting_year + 1, MIN_YEAR, MAX_YEAR);
@@ -614,7 +598,7 @@ static void ToolbarScenMapTownDir(Window *w)
 static void ToolbarScenZoomIn(Window *w)
 {
 	if (DoZoomInOutWindow(ZOOM_IN, FindWindowById(WC_MAIN_WINDOW, 0))) {
-		w->HandleButtonClick(TBSE_ZOOMIN);
+		w->HandleButtonClick(9);
 		SndPlayFx(SND_15_BEEP);
 	}
 }
@@ -622,14 +606,14 @@ static void ToolbarScenZoomIn(Window *w)
 static void ToolbarScenZoomOut(Window *w)
 {
 	if (DoZoomInOutWindow(ZOOM_OUT, FindWindowById(WC_MAIN_WINDOW, 0))) {
-		w->HandleButtonClick(TBSE_ZOOMOUT);
+		w->HandleButtonClick(10);
 		SndPlayFx(SND_15_BEEP);
 	}
 }
 
 static void ToolbarScenGenLand(Window *w)
 {
-	w->HandleButtonClick(TBSE_LANDGENERATE);
+	w->HandleButtonClick(11);
 	SndPlayFx(SND_15_BEEP);
 
 	ShowEditorTerraformToolbar();
@@ -638,35 +622,35 @@ static void ToolbarScenGenLand(Window *w)
 
 static void ToolbarScenGenTown(Window *w)
 {
-	w->HandleButtonClick(TBSE_TOWNGENERATE);
+	w->HandleButtonClick(12);
 	SndPlayFx(SND_15_BEEP);
 	ShowBuildTownWindow();
 }
 
 static void ToolbarScenGenIndustry(Window *w)
 {
-	w->HandleButtonClick(TBSE_INDUSTRYGENERATE);
+	w->HandleButtonClick(13);
 	SndPlayFx(SND_15_BEEP);
 	ShowBuildIndustryWindow();
 }
 
 static void ToolbarScenBuildRoad(Window *w)
 {
-	w->HandleButtonClick(TBSE_BUILDROAD);
+	w->HandleButtonClick(14);
 	SndPlayFx(SND_15_BEEP);
 	ShowBuildRoadScenToolbar();
 }
 
 static void ToolbarScenPlantTrees(Window *w)
 {
-	w->HandleButtonClick(TBSE_PLANTTREES);
+	w->HandleButtonClick(15);
 	SndPlayFx(SND_15_BEEP);
 	ShowBuildTreesToolbar();
 }
 
 static void ToolbarScenPlaceSign(Window *w)
 {
-	w->HandleButtonClick(TBSE_PLACESIGNS);
+	w->HandleButtonClick(16);
 	SndPlayFx(SND_15_BEEP);
 	SelectSignTool();
 }
@@ -882,6 +866,12 @@ struct MainToolbarWindow : Window {
 		_place_proc(tile);
 	}
 
+	virtual void OnPlaceObjectAbort()
+	{
+		this->RaiseWidget(25);
+		this->SetDirty();
+	}
+
 	virtual void OnTick()
 	{
 		if (this->IsWidgetLowered(0) != !!_pause_game) {
@@ -999,7 +989,6 @@ static ToolbarButtonProc * const _scen_toolbar_button_procs[] = {
 };
 
 struct ScenarioEditorToolbarWindow : Window {
-public:
 	ScenarioEditorToolbarWindow(const WindowDesc *desc) : Window(desc)
 	{
 		CLRBITS(this->flags4, WF_WHITE_BORDER_MASK);
@@ -1011,8 +1000,8 @@ public:
 
 	virtual void OnPaint()
 	{
-		this->SetWidgetDisabledState(TBSE_DATEBACKWARD, _settings_newgame.game_creation.starting_year <= MIN_YEAR);
-		this->SetWidgetDisabledState(TBSE_DATEFORWARD, _settings_newgame.game_creation.starting_year >= MAX_YEAR);
+		this->SetWidgetDisabledState(6, _settings_newgame.game_creation.starting_year <= MIN_YEAR);
+		this->SetWidgetDisabledState(7, _settings_newgame.game_creation.starting_year >= MAX_YEAR);
 
 		/* Draw brown-red toolbar bg. */
 		GfxFillRect(0, 0, this->width - 1, this->height - 1, 0xB2);
@@ -1021,13 +1010,12 @@ public:
 		this->DrawWidgets();
 
 		SetDParam(0, ConvertYMDToDate(_settings_newgame.game_creation.starting_year, 0, 1));
-		DrawStringCenteredTruncated(this->widget[TBSE_DATEBACKWARD].right, this->widget[TBSE_DATEFORWARD].left, 6, STR_00AF, TC_FROMSTRING);
+		DrawStringCenteredTruncated(this->widget[6].right, this->widget[7].left, 6, STR_00AF, TC_FROMSTRING);
 
 		/* We hide this panel when the toolbar space gets too small */
-		const Widget *panel = &this->widget[TBSE_SPACERPANEL];
-		if (panel->left != panel->right) {
-			DrawStringCenteredTruncated(panel->left + 1, panel->right - 1,  1, STR_0221_OPENTTD, TC_FROMSTRING);
-			DrawStringCenteredTruncated(panel->left + 1, panel->right - 1, 11, STR_0222_SCENARIO_EDITOR, TC_FROMSTRING);
+		if (this->widget[4].left != this->widget[4].right) {
+			DrawStringCenteredTruncated(this->widget[4].left + 1, this->widget[4].right - 1,  1, STR_0221_OPENTTD, TC_FROMSTRING);
+			DrawStringCenteredTruncated(this->widget[4].left + 1, this->widget[4].right - 1, 11, STR_0222_SCENARIO_EDITOR, TC_FROMSTRING);
 		}
 	}
 
@@ -1058,13 +1046,13 @@ public:
 			case WKC_NUM_PLUS:
 			case WKC_EQUALS:
 			case WKC_SHIFT | WKC_EQUALS:
-			case WKC_SHIFT | WKC_F5: ToolbarScenZoomIn(this); break;
+			case WKC_SHIFT | WKC_F5: ToolbarZoomInClick(this); break;
 
 			/* those following are all fall through */
 			case WKC_NUM_MINUS:
 			case WKC_MINUS:
 			case WKC_SHIFT | WKC_MINUS:
-			case WKC_SHIFT | WKC_F6: ToolbarScenZoomOut(this); break;
+			case WKC_SHIFT | WKC_F6: ToolbarZoomOutClick(this); break;
 
 			case 'L': ShowEditorTerraformToolbar(); break;
 			case 'M': ShowSmallMap(); break;
@@ -1079,6 +1067,12 @@ public:
 		_place_proc(tile);
 	}
 
+	virtual void OnPlaceObjectAbort()
+	{
+		this->RaiseWidget(25);
+		this->SetDirty();
+	}
+
 	virtual void OnResize(Point new_size, Point delta)
 	{
 		/* There are 15 buttons plus some spacings if the space allows it.
@@ -1148,20 +1142,20 @@ public:
 
 	virtual void OnTick()
 	{
-		if (this->IsWidgetLowered(TBSE_PAUSE) != !!_pause_game) {
-			this->ToggleWidgetLoweredState(TBSE_PAUSE);
+		if (this->IsWidgetLowered(0) != !!_pause_game) {
+			this->ToggleWidgetLoweredState(0);
 			this->SetDirty();
 		}
 
-		if (this->IsWidgetLowered(TBSE_FASTFORWARD) != !!_fast_forward) {
-			this->ToggleWidgetLoweredState(TBSE_FASTFORWARD);
+		if (this->IsWidgetLowered(1) != !!_fast_forward) {
+			this->ToggleWidgetLoweredState(1);
 			this->SetDirty();
 		}
 	}
 
 	virtual void OnInvalidateData(int data)
 	{
-		if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) HandleZoomMessage(this, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, TBSE_ZOOMIN, TBSE_ZOOMOUT);
+		if (FindWindowById(WC_MAIN_WINDOW, 0) != NULL) HandleZoomMessage(this, FindWindowById(WC_MAIN_WINDOW, 0)->viewport, 17, 18);
 	}
 };
 
diff --git a/src/track_func.h b/src/track_func.h
index e659121..706de20 100644
--- a/src/track_func.h
+++ b/src/track_func.h
@@ -198,6 +198,21 @@ static inline bool IsValidTrackdir(Trackdir trackdir)
  */
 
 /**
+ * Find the opposite track to a given track.
+ *
+ * TRACK_LOWER -> TRACK_UPPER and vice versa, likewise for left/right.
+ * TRACK_X is mapped to TRACK_Y and reversed.
+ *
+ * @param t the track to convert
+ * @return the opposite track
+ */
+static inline Track TrackToOppositeTrack(Track t)
+{
+	assert(t != INVALID_TRACK);
+	return (Track)(t ^ 1);
+}
+
+/**
  * Maps a trackdir to the reverse trackdir.
  *
  * Returns the reverse trackdir of a Trackdir value. The reverse trackdir
@@ -575,6 +590,19 @@ static inline bool TracksOverlap(TrackBits bits)
 }
 
 /**
+ * Check if a given track is contained within or overlaps some other tracks.
+ *
+ * @param tracks Tracks to be testet against
+ * @param track The track to test
+ * @return true if the track is already in the tracks or overlaps the tracks.
+ */
+static inline bool TrackOverlapsTracks(TrackBits tracks, Track track)
+{
+	if (HasBit(tracks, track)) return true;
+	return TracksOverlap(tracks | TrackToTrackBits(track));
+}
+
+/**
  * Checks whether the trackdir means that we are reversing.
  * @param dir the trackdir to check
  * @return true if it is a reversing road trackdir
diff --git a/src/train.h b/src/train.h
index af7bb94..da6ae35 100644
--- a/src/train.h
+++ b/src/train.h
@@ -273,6 +273,8 @@ int CheckTrainInDepot(const Vehicle *v, bool needs_to_be_stopped);
 int CheckTrainStoppedInDepot(const Vehicle *v);
 void UpdateTrainAcceleration(Vehicle* v);
 
+void FreeTrainTrackReservation(const Vehicle *v, TileIndex origin = INVALID_TILE, Trackdir orig_td = INVALID_TRACKDIR);
+
 /**
  * This class 'wraps' Vehicle; you do not actually instantiate this class.
  * You create a Vehicle using AllocateVehicle, so it is added to the pool
diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp
index 88fa3d1..9f5b564 100644
--- a/src/train_cmd.cpp
+++ b/src/train_cmd.cpp
@@ -33,6 +33,7 @@
 #include "newgrf_text.h"
 #include "direction_func.h"
 #include "yapf/yapf.h"
+#include "yapf/follow_track.hpp"
 #include "cargotype.h"
 #include "group.h"
 #include "table/sprites.h"
@@ -51,13 +52,18 @@
 #include "order_func.h"
 #include "newgrf_station.h"
 #include "effectvehicle_func.h"
+#include "pbs.h"
 
 #include "table/strings.h"
 #include "table/train_cmd.h"
 
+static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck);
+static bool TryPathReserve(Vehicle *v, bool first_tile_okay = false);
 static bool TrainCheckIfLineEnds(Vehicle *v);
 static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image);
 static TileIndex TrainApproachingCrossingTile(const Vehicle *v);
+static void CheckIfTrainNeedsService(Vehicle *v);
+static void CheckNextTrainTile(Vehicle *v);
 
 static const byte _vehicle_initial_x_fract[4] = {10, 8, 4,  8};
 static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
@@ -211,8 +217,6 @@ void TrainConsistChanged(Vehicle *v)
 		u->u.rail.first_engine = v == u ? INVALID_ENGINE : first_engine;
 		u->u.rail.railtype = rvi_u->railtype;
 
-		if (IsTrainEngine(u)) first_engine = u->engine_type;
-
 		/* Set user defined data to its default value */
 		u->u.rail.user_def_data = rvi_u->user_def_data;
 	}
@@ -227,6 +231,8 @@ void TrainConsistChanged(Vehicle *v)
 
 		if (!HasBit(EngInfo(u->engine_type)->misc_flags, EF_RAIL_TILTS)) train_can_tilt = false;
 
+		if (IsTrainEngine(u)) first_engine = u->engine_type;
+
 		/* Cache wagon override sprite group. NULL is returned if there is none */
 		u->u.rail.cached_override = GetWagonOverrideSpriteSet(u->engine_type, u->cargo_type, u->u.rail.first_engine);
 
@@ -1550,10 +1556,27 @@ static inline void SetLastSpeed(Vehicle *v, int spd)
 	}
 }
 
-static void SwapTrainFlags(byte *swap_flag1, byte *swap_flag2)
+/** Mark a train as stuck and stop it if it isn't stopped right now. */
+static void MarkTrainAsStuck(Vehicle *v)
+{
+	if (!HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) {
+		/* It is the first time the problem occured, set the "train stuck" flag. */
+		SetBit(v->u.rail.flags, VRF_TRAIN_STUCK);
+		v->load_unload_time_rem	= 0;
+
+		/* Stop train */
+		v->cur_speed = 0;
+		v->subspeed = 0;
+		SetLastSpeed(v, 0);
+
+		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
+	}
+}
+
+static void SwapTrainFlags(uint16 *swap_flag1, uint16 *swap_flag2)
 {
-	byte flag1 = *swap_flag1;
-	byte flag2 = *swap_flag2;
+	uint16 flag1 = *swap_flag1;
+	uint16 flag2 = *swap_flag2;
 
 	/* Clear the flags */
 	ClrBit(*swap_flag1, VRF_GOINGUP);
@@ -1687,8 +1710,8 @@ void UpdateLevelCrossing(TileIndex tile, bool sound)
 {
 	assert(IsLevelCrossingTile(tile));
 
-	/* train on crossing || train approaching crossing */
-	bool new_state = VehicleFromPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile);
+	/* train on crossing || train approaching crossing || reserved */
+	bool new_state = VehicleFromPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile) || GetCrossingReservation(tile);
 
 	if (new_state != IsCrossingBarred(tile)) {
 		if (new_state && sound) {
@@ -1808,6 +1831,10 @@ static void ReverseTrainDirection(Vehicle *v)
 		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 	}
 
+	/* Clear path reservation in front. */
+	if (!IsFrontEngine(v)) error("Vehicle %d is no front engine! First %p, veh %p.", v->First()->unitnumber, v->First(), v);
+	FreeTrainTrackReservation(v);
+
 	/* Check if we were approaching a rail/road-crossing */
 	TileIndex crossing = TrainApproachingCrossingTile(v);
 
@@ -1844,6 +1871,29 @@ static void ReverseTrainDirection(Vehicle *v)
 	/* maybe we are approaching crossing now, after reversal */
 	crossing = TrainApproachingCrossingTile(v);
 	if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
+
+	/* If we are inside a depot after reversing, don't bother with path reserving. */
+	if (v->u.rail.track & TRACK_BIT_DEPOT) return;
+
+	/* TrainExitDir does not always produce the desired dir for depots and
+	 * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
+	DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
+	if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR;
+
+	if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
+		/* If we are currently on a tile with conventional signals, we can't treat the
+		 * current tile as a safe tile or we would enter a PBS block without a reservation. */
+		bool first_tile_okay = !(IsTileType(v->tile, MP_RAILWAY)
+				&& HasSignalOnTrackdir(v->tile, GetVehicleTrackdir(v))
+				&& !IsPbsSignal(GetSignalType(v->tile, FindFirstTrack(v->u.rail.track))));
+
+		if (!TryPathReserve(v, first_tile_okay)) {
+			MarkTrainAsStuck(v);
+		} else {
+			/* Do a look-ahead now in case our current tile was already a safe tile. */
+			CheckNextTrainTile(v);
+		}
+	}
 }
 
 /** Reverse train.
@@ -2051,14 +2101,17 @@ static TrainFindDepotData FindClosestTrainDepot(Vehicle *v, int max_distance)
 	tfdd.best_length = UINT_MAX;
 	tfdd.reverse = false;
 
-	TileIndex tile = v->tile;
-	if (IsRailDepotTile(tile)) {
-		tfdd.tile = tile;
+	PBSTileInfo origin = FollowTrainReservation(v);
+	if (IsRailDepotTile(origin.tile)) {
+		tfdd.tile = origin.tile;
 		tfdd.best_length = 0;
 		return tfdd;
 	}
 
-	switch (_settings_game.pf.pathfinder_for_trains) {
+	uint8 pathfinder = _settings_game.pf.pathfinder_for_trains;
+	if ((_settings_game.pf.reserve_paths || HasReservedTracks(v->tile, v->u.rail.track)) && pathfinder == VPF_NTP) pathfinder = VPF_NPF;
+
+	switch (pathfinder) {
 		case VPF_YAPF: { /* YAPF */
 			bool found = YapfFindNearestRailDepotTwoWay(v, max_distance, NPF_INFINITE_PENALTY, &tfdd.tile, &tfdd.reverse);
 			tfdd.best_length = found ? max_distance / 2 : UINT_MAX; // some fake distance or NOT_FOUND
@@ -2087,12 +2140,12 @@ static TrainFindDepotData FindClosestTrainDepot(Vehicle *v, int max_distance)
 		case VPF_NTP: { /* NTP */
 			/* search in the forward direction first. */
 			DiagDirection i = TrainExitDir(v->direction, v->u.rail.track);
-			NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd);
+			NewTrainPathfind(v->tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd);
 			if (tfdd.best_length == UINT_MAX){
 				tfdd.reverse = true;
 				/* search in backwards direction */
 				i = TrainExitDir(ReverseDir(v->direction), v->u.rail.track);
-				NewTrainPathfind(tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd);
+				NewTrainPathfind(v->tile, 0, v->u.rail.compatible_railtypes, i, (NTPEnumProc*)NtpCallbFindDepot, &tfdd);
 			}
 		} break;
 	}
@@ -2240,6 +2293,42 @@ void Train::PlayLeaveStationSound() const
 	SndPlayVehicleFx(sfx[RailVehInfo(engtype)->engclass], this);
 }
 
+/** Check if the train is on the last reserved tile and try to extend the path then. */
+static void CheckNextTrainTile(Vehicle *v)
+{
+	/* Exit if the current order doesn't have a destination, but the train has orders. */
+	if ((v->current_order.IsType(OT_NOTHING) || v->current_order.IsType(OT_LEAVESTATION)) && v->num_orders > 0) return;
+
+	/* Exit if we reached our destination or are inside a depot. */
+	if ((v->tile == v->dest_tile && !v->current_order.IsType(OT_GOTO_STATION)) || v->u.rail.track & TRACK_BIT_DEPOT) return;
+	/* Exit if we are on a station tile and are going to stop. */
+	if (IsRailwayStationTile(v->tile) && v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile))) return;
+
+	Trackdir td = GetVehicleTrackdir(v);
+
+	/* On a tile with a red non-pbs signal, don't look ahead. */
+	if (IsTileType(v->tile, MP_RAILWAY) && HasSignalOnTrackdir(v->tile, td) &&
+			!IsPbsSignal(GetSignalType(v->tile, TrackdirToTrack(td))) &&
+			GetSignalStateByTrackdir(v->tile, td) == SIGNAL_STATE_RED) return;
+
+	CFollowTrackRail ft(v);
+	if (!ft.Follow(v->tile, td)) return;
+
+	if (!HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits))) {
+		/* Next tile is not reserved. */
+		if (KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) {
+			if (HasPbsSignalOnTrackdir(ft.m_new_tile, FindFirstTrackdir(ft.m_new_td_bits))) {
+				/* If the next tile is a PBS signal, try to make a reservation. */
+				TrackBits tracks = TrackdirBitsToTrackBits(ft.m_new_td_bits);
+				if (_settings_game.pf.pathfinder_for_trains != VPF_NTP && _settings_game.pf.forbid_90_deg) {
+					tracks &= ~TrackCrossesTracks(TrackdirToTrack(ft.m_old_td));
+				}
+				ChooseTrainTrack(v, ft.m_new_tile, ft.m_exitdir, tracks, false, NULL, false);
+			}
+		}
+	}
+}
+
 static bool CheckTrainStayInDepot(Vehicle *v)
 {
 	/* bail out if not all wagons are in the same depot or not in a depot at all */
@@ -2254,7 +2343,10 @@ static bool CheckTrainStayInDepot(Vehicle *v)
 		return true;
 	}
 
+	SigSegState seg_state;
+
 	if (v->u.rail.force_proceed == 0) {
+		/* force proceed was not pressed */
 		if (++v->load_unload_time_rem < 37) {
 			InvalidateWindowClasses(WC_TRAINS_LIST);
 			return true;
@@ -2262,12 +2354,27 @@ static bool CheckTrainStayInDepot(Vehicle *v)
 
 		v->load_unload_time_rem = 0;
 
-		if (UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner) == SIGSEG_FULL) {
+		seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
+		if (seg_state == SIGSEG_FULL || GetDepotReservation(v->tile)) {
+			/* Full and no PBS signal in block or depot reserved, can't exit. */
 			InvalidateWindowClasses(WC_TRAINS_LIST);
 			return true;
 		}
+	} else {
+		seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
 	}
 
+	/* Only leave when we can reserve a path to our destination. */
+	if (seg_state == SIGSEG_PBS && !TryPathReserve(v) && v->u.rail.force_proceed == 0) {
+		/* No path and no force proceed. */
+		InvalidateWindowClasses(WC_TRAINS_LIST);
+		MarkTrainAsStuck(v);
+		return true;
+	}
+
+	SetDepotReservation(v->tile, true);
+	if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
+
 	VehicleServiceInDepot(v);
 	InvalidateWindowClasses(WC_TRAINS_LIST);
 	v->PlayLeaveStationSound();
@@ -2288,6 +2395,85 @@ static bool CheckTrainStayInDepot(Vehicle *v)
 	return false;
 }
 
+/** Clear the reservation of a tile that was just left by a wagon on track_dir. */
+static void ClearPathReservation(TileIndex tile, Trackdir track_dir)
+{
+	DiagDirection dir = TrackdirToExitdir(track_dir);
+
+	if (IsTileType(tile, MP_TUNNELBRIDGE)) {
+		/* Are we just leaving a tunnel/bridge? */
+		if (GetTunnelBridgeDirection(tile) == ReverseDiagDir(dir)) {
+			TileIndex end = GetOtherTunnelBridgeEnd(tile);
+
+			SetTunnelBridgeReservation(tile, false);
+			SetTunnelBridgeReservation(end, false);
+
+			if (_settings_client.gui.show_track_reservation) {
+				MarkTileDirtyByTile(tile);
+				MarkTileDirtyByTile(end);
+			}
+		}
+	} else if (IsRailwayStationTile(tile)) {
+		TileIndex new_tile = TileAddByDiagDir(tile, dir);
+		/* If the new tile is not a further tile of the same station, we
+		 * clear the reservation for the whole platform. */
+		if (!IsCompatibleTrainStationTile(new_tile, tile)) {
+			SetRailwayStationPlatformReservation(tile, ReverseDiagDir(dir), false);
+		}
+	} else {
+		/* Any other tile */
+		UnreserveRailTrack(tile, TrackdirToTrack(track_dir));
+	}
+}
+
+/** Free the reserved path in front of a vehicle. */
+void FreeTrainTrackReservation(const Vehicle *v, TileIndex origin, Trackdir orig_td)
+{
+	assert(IsFrontEngine(v));
+
+	TileIndex tile = origin != INVALID_TILE ? origin : v->tile;
+	Trackdir  td = orig_td != INVALID_TRACKDIR ? orig_td : GetVehicleTrackdir(v);
+	bool      free_tile = tile != v->tile || !(IsRailwayStationTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE));
+
+	/* Don't free reservation if it's not ours. */
+	if (TracksOverlap(GetReservedTrackbits(tile) | TrackToTrackBits(TrackdirToTrack(td)))) return;
+
+	CFollowTrackRail ft(v, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes);
+	while (ft.Follow(tile, td)) {
+		tile = ft.m_new_tile;
+		TrackdirBits bits = (TrackdirBits)(ft.m_new_td_bits & (GetReservedTrackbits(tile) * 0x101));
+		td = RemoveFirstTrackdir(&bits);
+		assert(bits == TRACKDIR_BIT_NONE);
+
+		if (!IsValidTrackdir(td)) break;
+
+		if (IsTileType(tile, MP_RAILWAY)) {
+			if (HasSignalOnTrackdir(tile, td) && !IsPbsSignal(GetSignalType(tile, TrackdirToTrack(td)))) {
+				/* Conventional signal along trackdir: remove reservation and stop. */
+				UnreserveRailTrack(tile, TrackdirToTrack(td));
+				break;
+			}
+			if (HasPbsSignalOnTrackdir(tile, td)) {
+				if (GetSignalStateByTrackdir(tile, td) == SIGNAL_STATE_RED) {
+					/* Red PBS signal? Can't be our reservation, would be green then. */
+					break;
+				} else {
+					/* Turn the signal back to red. */
+					SetSignalStateByTrackdir(tile, td, SIGNAL_STATE_RED);
+					MarkTileDirtyByTile(tile);
+				}
+			} else if (HasSignalOnTrackdir(tile, ReverseTrackdir(td)) && IsOnewaySignal(tile, TrackdirToTrack(td))) {
+				break;
+			}
+		}
+
+		/* Don't free first station/bridge/tunnel if we are on it. */
+		if (free_tile || (!ft.m_is_station && !ft.m_is_tunnel && !ft.m_is_bridge)) ClearPathReservation(tile, td);
+
+		free_tile = true;
+	}
+}
+
 /** Check for station tiles */
 struct TrainTrackFollowerData {
 	TileIndex dest_coords;
@@ -2353,25 +2539,34 @@ static const byte _search_directions[6][4] = {
 
 static const byte _pick_track_table[6] = {1, 3, 2, 2, 0, 0};
 
-/* choose a track */
-static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks)
+/**
+ * Perform pathfinding for a train.
+ *
+ * @param v The train
+ * @param tile The tile the train is about to enter
+ * @param enterdir Diagonal direction the train is coming from
+ * @param tracks Usable tracks on the new tile
+ * @param path_not_found [out] Set to false if the pathfinder couldn't find a way to the destination
+ * @param do_track_reservation
+ * @param dest [out]
+ * @return The best track the train should follow
+ */
+static Track DoTrainPathfind(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool do_track_reservation, PBSTileInfo *dest)
 {
 	Track best_track;
-	/* pathfinders are able to tell that route was only 'guessed' */
-	bool path_not_found = false;
 
 #ifdef PF_BENCHMARK
 	TIC()
 #endif
 
-	assert((tracks & ~TRACK_BIT_MASK) == 0);
+	if (path_not_found) *path_not_found = false;
 
-	/* quick return in case only one possible track is available */
-	if (KillFirstBit(tracks) == TRACK_BIT_NONE) return FindFirstTrack(tracks);
+	uint8 pathfinder = _settings_game.pf.pathfinder_for_trains;
+	if (do_track_reservation && pathfinder == VPF_NTP) pathfinder = VPF_NPF;
 
-	switch (_settings_game.pf.pathfinder_for_trains) {
+	switch (pathfinder) {
 		case VPF_YAPF: { /* YAPF */
-			Trackdir trackdir = YapfChooseRailTrack(v, tile, enterdir, tracks, &path_not_found);
+			Trackdir trackdir = YapfChooseRailTrack(v, tile, enterdir, tracks, path_not_found, do_track_reservation, dest);
 			if (trackdir != INVALID_TRACKDIR) {
 				best_track = TrackdirToTrack(trackdir);
 			} else {
@@ -2383,12 +2578,18 @@ static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir
 			void *perf = NpfBeginInterval();
 
 			NPFFindStationOrTileData fstd;
-			NPFFillWithOrderData(&fstd, v);
-			/* The enterdir for the new tile, is the exitdir for the old tile */
-			Trackdir trackdir = GetVehicleTrackdir(v);
-			assert(trackdir != INVALID_TRACKDIR);
+			NPFFillWithOrderData(&fstd, v, do_track_reservation);
+
+			PBSTileInfo origin = FollowTrainReservation(v);
+			assert(IsValidTrackdir(origin.trackdir));
 
-			NPFFoundTargetData ftd = NPFRouteToStationOrTile(tile - TileOffsByDiagDir(enterdir), trackdir, true, &fstd, TRANSPORT_RAIL, 0, v->owner, v->u.rail.compatible_railtypes);
+			NPFFoundTargetData ftd = NPFRouteToStationOrTile(origin.tile, origin.trackdir, true, &fstd, TRANSPORT_RAIL, 0, v->owner, v->u.rail.compatible_railtypes);
+
+			if (dest != NULL) {
+				dest->tile = ftd.node.tile;
+				dest->trackdir = (Trackdir)ftd.node.direction;
+				dest->okay = ftd.res_okay;
+			}
 
 			if (ftd.best_trackdir == INVALID_TRACKDIR) {
 				/* We are already at our target. Just do something
@@ -2400,7 +2601,7 @@ static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir
 				 * the direction we need to take to get there, if ftd.best_bird_dist is not 0,
 				 * we did not find our target, but ftd.best_trackdir contains the direction leading
 				 * to the tile closest to our target. */
-				if (ftd.best_bird_dist != 0) path_not_found = true;
+				if (ftd.best_bird_dist != 0 && path_not_found != NULL) *path_not_found = true;
 				/* Discard enterdir information, making it a normal track */
 				best_track = TrackdirToTrack(ftd.best_trackdir);
 			}
@@ -2425,7 +2626,7 @@ static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir
 				v->u.rail.compatible_railtypes, enterdir, (NTPEnumProc*)NtpCallbFindStation, &fd);
 
 			/* check whether the path was found or only 'guessed' */
-			if (fd.best_bird_dist != 0) path_not_found = true;
+			if (fd.best_bird_dist != 0 && path_not_found != NULL) *path_not_found = true;
 
 			if (fd.best_track == INVALID_TRACKDIR) {
 				/* blaha */
@@ -2439,38 +2640,413 @@ static Track ChooseTrainTrack(Vehicle *v, TileIndex tile, DiagDirection enterdir
 		} break;
 	}
 
-	/* handle "path not found" state */
-	if (path_not_found) {
-		/* PF didn't find the route */
-		if (!HasBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) {
-			/* it is first time the problem occurred, set the "path not found" flag */
-			SetBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION);
-			/* and notify user about the event */
-			if (_settings_client.gui.lost_train_warn && v->owner == _local_player) {
-				SetDParam(0, v->unitnumber);
-				AddNewsItem(
-					STR_TRAIN_IS_LOST,
-					NS_ADVICE,
-					v->index,
-					0);
+#ifdef PF_BENCHMARK
+	TOC("PF time = ", 1)
+#endif
+
+	return best_track;
+}
+
+/**
+ * Extend a train path as far as possible. Stops on encountering a safe tile,
+ * another reservation or a track choice.
+ * @return INVALID_TILE indicates that the reservation failed.
+ */
+static PBSTileInfo ExtendTrainReservation(const Vehicle *v, TrackBits *new_tracks, DiagDirection *enterdir)
+{
+	bool no_90deg_turns = _settings_game.pf.pathfinder_for_trains != VPF_NTP && _settings_game.pf.forbid_90_deg;
+	PBSTileInfo origin = FollowTrainReservation(v);
+
+	CFollowTrackRail ft(v);
+
+	TileIndex tile = origin.tile;
+	Trackdir  cur_td = origin.trackdir;
+	while (ft.Follow(tile, cur_td)) {
+		if (KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE) {
+			/* Possible signal tile. */
+			if (HasOnewaySignalBlockingTrackdir(ft.m_new_tile, FindFirstTrackdir(ft.m_new_td_bits))) break;
+		}
+
+		if (no_90deg_turns) {
+			ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(ft.m_old_td);
+			if (ft.m_new_td_bits == TRACKDIR_BIT_NONE) break;
+		}
+
+		/* Station, depot or waypoint are a possible target. */
+		bool target_seen = ft.m_is_station || (IsTileType(ft.m_new_tile, MP_RAILWAY) && !IsPlainRailTile(ft.m_new_tile));
+		if (target_seen || KillFirstBit(ft.m_new_td_bits) != TRACKDIR_BIT_NONE) {
+			/* Choice found or possible target encountered.
+			 * On finding a possible target, we need to stop and let the pathfinder handle the
+			 * remaining path. This is because we don't know if this target is in one of our
+			 * orders, so we might cause pathfinding to fail later on if we find a choice.
+			 * This failure would cause a bogous call to TryReserveSafePath which might reserve
+			 * a wrong path not leading to our next destination. */
+			if (HasReservedTracks(ft.m_new_tile, TrackdirBitsToTrackBits(ft.m_new_td_bits))) break;
+
+			/* Choice found, path valid but not okay. Save info about the choice tile as well. */
+			if (new_tracks) *new_tracks = TrackdirBitsToTrackBits(ft.m_new_td_bits);
+			if (enterdir) *enterdir = ft.m_exitdir;
+			return PBSTileInfo(ft.m_new_tile, ft.m_old_td, false);
+		}
+
+		tile = ft.m_new_tile;
+		cur_td = FindFirstTrackdir(ft.m_new_td_bits);
+
+		/* CFollowTrackRail skips station tiles, reserve those skipped tiles as well. */
+		if (ft.m_is_station && ft.m_tiles_skipped > 0) {
+			TileIndexDiff diff = TileOffsByDiagDir(ft.m_exitdir);
+			TileIndex st = TILE_ADD(ft.m_new_tile, -ft.m_tiles_skipped * diff);
+			for (int i = 0; i < ft.m_tiles_skipped; ++i, st += diff) {
+				if (GetRailwayStationReservation(st)) {
+					ft.m_new_tile = st;
+					break;
+				}
+				SetRailwayStationReservation(st, true);
 			}
 		}
+
+		if (IsSafeWaitingPosition(v, tile, cur_td, true, no_90deg_turns)) {
+			bool wp_free = IsWaitingPositionFree(v, tile, cur_td, no_90deg_turns);
+			if (!(wp_free && TryReserveRailTrack(tile, TrackdirToTrack(cur_td)))) break;
+			/* Safe position is all good, path valid and okay. */
+			return PBSTileInfo(tile, cur_td, true);
+		}
+
+		if (!TryReserveRailTrack(tile, TrackdirToTrack(cur_td))) break;
+	}
+
+	if (ft.m_err == CFollowTrackRail::EC_OWNER && ft.m_err == CFollowTrackRail::EC_NO_WAY) {
+		/* End of line, path valid and okay. */
+		return PBSTileInfo(ft.m_old_tile, ft.m_old_td, true);
+	}
+
+	/* Sorry, can't reserve path, back out. */
+	tile = ft.m_new_tile;
+	ft.m_new_tile = origin.tile;
+	ft.m_new_td_bits = TrackdirToTrackdirBits(origin.trackdir);
+	while (ft.Follow(ft.m_new_tile, FindFirstTrackdir(ft.m_new_td_bits))) {
+		/* Check for skipped station tiles. */
+		if (ft.m_is_station && ft.m_tiles_skipped > 0) {
+			TileIndexDiff diff = TileOffsByDiagDir(ft.m_exitdir);
+			TileIndex st = TILE_ADD(ft.m_new_tile, -ft.m_tiles_skipped * diff);
+			for (int i = 0; i < ft.m_tiles_skipped; ++i, st += diff) {
+				if (st == tile) break;
+				SetRailwayStationReservation(st, false);
+			}
+		}
+
+		/* Break on reaching the previous failure. */
+		if (ft.m_new_tile == tile) break;
+
+		if (no_90deg_turns) {
+			ft.m_new_td_bits &= ~TrackdirCrossesTrackdirs(ft.m_old_td);
+			assert(ft.m_new_td_bits != TRACKDIR_BIT_NONE);
+		}
+
+		assert(KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE);
+		UnreserveRailTrack(ft.m_new_tile, TrackdirToTrack(FindFirstTrackdir(ft.m_new_td_bits)));
+	}
+	/* Path invalid. */
+	return PBSTileInfo();
+}
+
+/**
+ * Try to reserve any path to a safe tile, ignoring the vehicle's destination.
+ * Safe tiles are tiles in front of a signal, depots and station tiles at end of line.
+ *
+ * @param v The vehicle.
+ * @param tile The tile the search should start from.
+ * @param td The trackdir the search should start from.
+ * @param override_tailtype Whether all physically compatible railtypes should be followed.
+ * @return True if a path to a safe stopping tile could be reserved.
+ */
+static bool TryReserveSafeTrack(const Vehicle* v, TileIndex tile, Trackdir td, bool override_tailtype)
+{
+	if (_settings_game.pf.pathfinder_for_trains == VPF_YAPF) {
+		return YapfRailFindNearestSafeTile(v, tile, td, override_tailtype);
 	} else {
-		/* route found, is the train marked with "path not found" flag? */
-		if (HasBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) {
-			/* clear the flag as the PF's problem was solved */
-			ClrBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION);
-			/* can we also delete the "News" item somehow? */
+		return NPFRouteToSafeTile(v, tile, td, override_tailtype).res_okay;
+	}
+}
+
+/**
+ * Query the next order after a certain order index.
+ *
+ * @param v The vehicle
+ * @param order_index [in/out] Index of the current order, returns index of the chosen order
+ * @return Pointer to the order or NULL if the order chain was completely followed
+ *          without finding a suitable order.
+ */
+static const Order* GetNextTrainOrder(const Vehicle *v, VehicleOrderID *order_index)
+{
+	++(*order_index);
+
+	do {
+		/* Wrap around. */
+		if (*order_index >= v->num_orders) *order_index = 0;
+
+		Order *order = GetVehicleOrder(v, *order_index);
+		assert(order != NULL);
+
+		switch (order->GetType()) {
+			case OT_GOTO_DEPOT:
+				/* Skip service in depot orders when the train doesn't need service. */
+				if ((order->GetDepotOrderType() & ODTFB_SERVICE) && !v->NeedsServicing()) break;
+			case OT_GOTO_STATION:
+			case OT_GOTO_WAYPOINT:
+				return order;
+			case OT_CONDITIONAL: {
+				VehicleOrderID next = ProcessConditionalOrder(order, v);
+				if (next != INVALID_VEH_ORDER_ID) {
+					*order_index = next;
+					continue;
+				}
+				break;
+			}
+			default:
+				break;
 		}
+
+		++(*order_index);
+	} while (*order_index != v->cur_order_index);
+
+	return NULL;
+}
+
+/* choose a track */
+static Track ChooseTrainTrack(Vehicle* v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck)
+{
+	Track best_track = INVALID_TRACK;
+	bool do_track_reservation = _settings_game.pf.reserve_paths || force_res;
+	bool changed_signal = false;
+
+	assert((tracks & ~TRACK_BIT_MASK) == 0);
+
+	if (got_reservation != NULL) *got_reservation = false;
+
+	/* Don't use tracks here as the setting to forbid 90 deg turns might have been switched between reservation and now. */
+	TrackBits res_tracks = (TrackBits)(GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir));
+	/* Do we have a suitable reserved track? */
+	if (res_tracks != TRACK_BIT_NONE) return FindFirstTrack(res_tracks);
+
+	/* Quick return in case only one possible track is available */
+	if (KillFirstBit(tracks) == TRACK_BIT_NONE) {
+		Track track = FindFirstTrack(tracks);
+		/* We need to check for signals only here, as a junction tile can't have signals. */
+		if (track != INVALID_TRACK && HasPbsSignalOnTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir))) {
+			do_track_reservation = true;
+			changed_signal = true;
+			SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(track, enterdir), SIGNAL_STATE_GREEN);
+		} else if (!do_track_reservation) {
+			return track;
+		}
+		best_track = track;
 	}
 
-#ifdef PF_BENCHMARK
-	TOC("PF time = ", 1)
-#endif
+	PBSTileInfo   res_dest(tile, INVALID_TRACKDIR, false);
+	DiagDirection dest_enterdir = enterdir;
+	if (do_track_reservation) {
+		/* Check if the train needs service here, so it has a chance to always find a depot.
+		 * Also check if the current order is a service order so we don't reserve a path to
+		 * the destination but instead to the next one if service isn't needed. */
+		CheckIfTrainNeedsService(v);
+		if (v->current_order.IsType(OT_DUMMY) || v->current_order.IsType(OT_CONDITIONAL) || v->current_order.IsType(OT_GOTO_DEPOT)) ProcessOrders(v);
+
+		res_dest = ExtendTrainReservation(v, &tracks, &dest_enterdir);
+		if (res_dest.tile == INVALID_TILE) {
+			/* Reservation failed? */
+			if (mark_stuck) MarkTrainAsStuck(v);
+			if (changed_signal) SetSignalStateByTrackdir(tile, TrackEnterdirToTrackdir(best_track, enterdir), SIGNAL_STATE_RED);
+			return FindFirstTrack(tracks);
+		}
+	}
+
+	if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
+		/* Pathfinders are able to tell that route was only 'guessed'. */
+		bool      path_not_found = false;
+		TileIndex new_tile = res_dest.tile;
+
+		Track next_track = DoTrainPathfind(v, new_tile, dest_enterdir, tracks, &path_not_found, do_track_reservation, &res_dest);
+		if (new_tile == tile) best_track = next_track;
+
+		/* handle "path not found" state */
+		if (path_not_found) {
+			/* PF didn't find the route */
+			if (!HasBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) {
+				/* it is first time the problem occurred, set the "path not found" flag */
+				SetBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION);
+				/* and notify user about the event */
+				if (_settings_client.gui.lost_train_warn && v->owner == _local_player) {
+					SetDParam(0, v->unitnumber);
+					AddNewsItem(
+						STR_TRAIN_IS_LOST,
+						NS_ADVICE,
+						v->index,
+						0);
+				}
+			}
+		} else {
+			/* route found, is the train marked with "path not found" flag? */
+			if (HasBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION)) {
+				/* clear the flag as the PF's problem was solved */
+				ClrBit(v->u.rail.flags, VRF_NO_PATH_TO_DESTINATION);
+				/* can we also delete the "News" item somehow? */
+			}
+		}
+	}
+
+	/* No track reservation requested -> finished. */
+	if (!do_track_reservation) return best_track;
+
+	/* A path was found, but could not be reserved. */
+	if (res_dest.tile != INVALID_TILE && !res_dest.okay) {
+		if (mark_stuck) MarkTrainAsStuck(v);
+		if (!IsFrontEngine(v)) error("Vehicle %d is no front engine! First %p, veh %p.", v->First()->unitnumber, v->First(), v);
+		FreeTrainTrackReservation(v);
+		return best_track;
+	}
+
+	/* No possible reservation target found, we are probably lost. */
+	if (res_dest.tile == INVALID_TILE) {
+		/* Try to find any safe destination. */
+		PBSTileInfo origin = FollowTrainReservation(v);
+		if (TryReserveSafeTrack(v, origin.tile, origin.trackdir, false)) {
+			TrackBits res = GetReservedTrackbits(tile) & DiagdirReachesTracks(enterdir);
+			best_track = FindFirstTrack(res);
+			TryReserveRailTrack(v->tile, TrackdirToTrack(GetVehicleTrackdir(v)));
+			if (got_reservation != NULL) *got_reservation = true;
+			if (changed_signal) MarkTileDirtyByTile(tile);
+		} else {
+			if (!IsFrontEngine(v)) error("Vehicle %d is no front engine! First %p, veh %p.", v->First()->unitnumber, v->First(), v);
+			FreeTrainTrackReservation(v);
+			if (mark_stuck) MarkTrainAsStuck(v);
+		}
+		return best_track;
+	}
+
+	if (got_reservation != NULL) *got_reservation = true;
+
+	/* Reservation target found and free, check if it is safe. */
+	Order          cur_order = v->current_order;
+	TileIndex      cur_dest_tile = v->dest_tile;
+	VehicleOrderID order_index = v->cur_order_index;
+	while (!IsSafeWaitingPosition(v, res_dest.tile, res_dest.trackdir, true, _settings_game.pf.forbid_90_deg)) {
+		/* Extend reservation until we have found a safe position. */
+		DiagDirection exitdir = TrackdirToExitdir(res_dest.trackdir);
+		TileIndex     next_tile = TileAddByDiagDir(res_dest.tile, exitdir);
+		TrackBits     reachable = TrackdirBitsToTrackBits((TrackdirBits)(GetTileTrackStatus(next_tile, TRANSPORT_RAIL, 0))) & DiagdirReachesTracks(exitdir);
+		if (_settings_game.pf.pathfinder_for_trains != VPF_NTP && _settings_game.pf.forbid_90_deg) {
+			reachable &= ~TrackCrossesTracks(TrackdirToTrack(res_dest.trackdir));
+		}
+
+		/* Get next order with destination. */
+		const Order *order = GetNextTrainOrder(v, &order_index);
+		if (order != NULL) {
+			v->current_order = *order;
+			UpdateOrderDest(v, order);
+			PBSTileInfo cur_dest;
+			DoTrainPathfind(v, next_tile, exitdir, reachable, NULL, true, &cur_dest);
+			if (cur_dest.tile != INVALID_TILE) {
+				res_dest = cur_dest;
+				if (res_dest.okay) continue;
+				/* Path found, but could not be reserved. */
+				if (!IsFrontEngine(v)) error("Vehicle %d is no front engine! First %p, veh %p.", v->First()->unitnumber, v->First(), v);
+				FreeTrainTrackReservation(v);
+				if (mark_stuck) MarkTrainAsStuck(v);
+				if (got_reservation != NULL) *got_reservation = false;
+				changed_signal = false;
+				break;
+			}
+		}
+		/* No order or no safe position found, try any position. */
+		if (!TryReserveSafeTrack(v, res_dest.tile, res_dest.trackdir, true)) {
+			if (!IsFrontEngine(v)) error("Vehicle %d is no front engine! First %p, veh %p.", v->First()->unitnumber, v->First(), v);
+			FreeTrainTrackReservation(v);
+			if (mark_stuck) MarkTrainAsStuck(v);
+			if (got_reservation != NULL) *got_reservation = false;
+			changed_signal = false;
+		}
+		break;
+	}
+	v->current_order = cur_order;
+	v->dest_tile = cur_dest_tile;
+
+	TryReserveRailTrack(v->tile, TrackdirToTrack(GetVehicleTrackdir(v)));
+
+	if (changed_signal) MarkTileDirtyByTile(tile);
 
 	return best_track;
 }
 
+/**
+ * Try to reserve a path to a safe position.
+ *
+ * @param v The vehicle
+ * @return True if a path could be reserved
+ */
+static bool TryPathReserve(Vehicle *v, bool first_tile_okay)
+{
+	assert(IsFrontEngine(v));
+
+	/* We have to handle depots specially as the track follower won't look
+	 * at the depot tile itself but starts from the next tile. If we are still
+	 * inside the depot, a depot reservation can never be ours. */
+	if (v->u.rail.track & TRACK_BIT_DEPOT) {
+		if (GetDepotReservation(v->tile)) {
+			return false;
+		} else {
+			/* Depot not reserved, but the next tile might be. */
+			TileIndex next_tile = TileAddByDiagDir(v->tile, GetRailDepotDirection(v->tile));
+			if (HasReservedTracks(next_tile, DiagdirReachesTracks(GetRailDepotDirection(v->tile)))) return false;
+		}
+	}
+
+	/* Special check if we are in front of a two-sided conventional signal. */
+	DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
+	TileIndex next_tile = TileAddByDiagDir(v->tile, dir);
+	if (IsTileType(next_tile, MP_RAILWAY) && HasReservedTracks(next_tile, DiagdirReachesTracks(dir))) {
+		/* Can have only one reserved trackdir. */
+		Trackdir td = FindFirstTrackdir((TrackdirBits)(GetReservedTrackbits(next_tile) * 0x101 & DiagdirReachesTrackdirs(dir)));
+		if (HasSignalOnTrackdir(next_tile, td) && HasSignalOnTrackdir(next_tile, ReverseTrackdir(td)) &&
+				!IsPbsSignal(GetSignalType(next_tile, TrackdirToTrack(td)))) {
+			/* Signal already reserved, is not ours. */
+			return false;
+		}
+	}
+
+	PBSTileInfo origin = FollowTrainReservation(v);
+	/* If we have a reserved path and the path ends at a safe tile, we are finished already. */
+	if (origin.okay && (v->tile != origin.tile || first_tile_okay)) {
+		/* Can't be stuck then. */
+		ClrBit(v->u.rail.flags, VRF_TRAIN_STUCK);
+		return true;
+	}
+
+	/* If we are in a depot, tentativly reserve the depot. */
+	if (v->u.rail.track & TRACK_BIT_DEPOT) {
+		SetDepotReservation(v->tile, true);
+		if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
+	}
+
+	DiagDirection exitdir = TrackdirToExitdir(origin.trackdir);
+	TileIndex     new_tile = TileAddByDiagDir(origin.tile, exitdir);
+	TrackBits     reachable = TrackdirBitsToTrackBits((TrackdirBits)GetTileTrackStatus(new_tile, TRANSPORT_RAIL, 0) & DiagdirReachesTrackdirs(exitdir));
+
+	if (_settings_game.pf.pathfinder_for_trains != VPF_NTP && _settings_game.pf.forbid_90_deg) reachable &= ~TrackCrossesTracks(TrackdirToTrack(origin.trackdir));
+
+	bool res_made = false;
+	ChooseTrainTrack(v, new_tile, exitdir, reachable, true, &res_made, false);
+
+	if (!res_made) {
+		/* Free the depot reservation as well. */
+		if (v->u.rail.track & TRACK_BIT_DEPOT) SetDepotReservation(v->tile, false);
+		return false;
+	}
+
+	ClrBit(v->u.rail.flags, VRF_TRAIN_STUCK);
+	return true;
+}
+
 
 static bool CheckReverseTrain(Vehicle *v)
 {
@@ -2606,7 +3182,7 @@ static int UpdateTrainSpeed(Vehicle *v)
 {
 	uint accel;
 
-	if (v->vehstatus & VS_STOPPED || HasBit(v->u.rail.flags, VRF_REVERSING)) {
+	if (v->vehstatus & VS_STOPPED || HasBit(v->u.rail.flags, VRF_REVERSING) || HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) {
 		if (_settings_game.vehicle.realistic_acceleration) {
 			accel = GetTrainAcceleration(v, AM_BRAKE) * 2;
 		} else {
@@ -2778,14 +3354,18 @@ static inline void AffectSpeedByZChange(Vehicle *v, byte old_z)
 	}
 }
 
-static void TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
+static bool TrainMovedChangeSignals(TileIndex tile, DiagDirection dir)
 {
 	if (IsTileType(tile, MP_RAILWAY) &&
 			GetRailTileType(tile) == RAIL_TILE_SIGNALS) {
 		TrackdirBits tracks = TrackBitsToTrackdirBits(GetTrackBits(tile)) & DiagdirReachesTrackdirs(dir);
 		Trackdir trackdir = FindFirstTrackdir(tracks);
-		UpdateSignalsOnSegment(tile, TrackdirToExitdir(trackdir), GetTileOwner(tile));
+		if (UpdateSignalsOnSegment(tile,  TrackdirToExitdir(trackdir), GetTileOwner(tile)) == SIGSEG_PBS && HasSignalOnTrackdir(tile, trackdir)) {
+			/* A PBS block with a non-PBS signal facing us? */
+			if (!IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) return true;
+		}
 	}
+	return false;
 }
 
 
@@ -2793,6 +3373,14 @@ static void SetVehicleCrashed(Vehicle *v)
 {
 	if (v->u.rail.crash_anim_pos != 0) return;
 
+	/* Free a possible path reservation and try to mark all tiles occupied by the train reserved. */
+	if (IsFrontEngine(v)) {
+		FreeTrainTrackReservation(v);
+		for (const Vehicle *u = v; u != NULL; u = u->Next()) {
+			TryReserveRailTrack(u->tile, TrackdirToTrack(GetVehicleTrackdir(u)));
+		}
+	}
+
 	/* we may need to update crossing we were approaching */
 	TileIndex crossing = TrainApproachingCrossingTile(v);
 
@@ -2948,6 +3536,10 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
 						TrainEnterStation(v, r >> VETS_STATION_ID_OFFSET);
 						return;
 					}
+					if (v->Next() == NULL && IsRailDepotTile(v->tile) && HasBit(r, VETS_ENTERED_WORMHOLE)) {
+						SetDepotReservation(v->tile, false);
+						if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
+					}
 
 					if (v->current_order.IsType(OT_LEAVESTATION)) {
 						v->current_order.Free();
@@ -2987,7 +3579,7 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
 				if (prev == NULL) {
 					/* Currently the locomotive is active. Determine which one of the
 					 * available tracks to choose */
-					chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits));
+					chosen_track = TrackToTrackBits(ChooseTrainTrack(v, gp.new_tile, enterdir, bits, false, NULL, true));
 					assert(chosen_track & bits);
 
 					/* Check if it's a red signal and that force proceed is not clicked. */
@@ -2995,6 +3587,9 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
 						/* In front of a red signal */
 						Trackdir i = FindFirstTrackdir(trackdirbits);
 
+						/* Don't handle stuck trains here. */
+						if (HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) return;
+
 						if (!HasSignalOnTrackdir(gp.new_tile, ReverseTrackdir(i))) {
 							v->cur_speed = 0;
 							v->subspeed = 0;
@@ -3012,6 +3607,13 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
 								if (VehicleFromPos(o_tile, &rdir, &CheckVehicleAtSignal) == NULL) return;
 							}
 						}
+
+						/* If we would reverse but are currently in a PBS block and reversing of
+						 * stuck trains is disabled, don't reverse and mark stuck instead. */
+						if (_settings_game.pf.wait_for_pbs_path == 255 && UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) {
+							MarkTrainAsStuck(v);
+							return;
+						}
 						goto reverse_train_direction;
 					}
 				} else {
@@ -3047,6 +3649,16 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
 				if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
 
 				if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
+					Track track = FindFirstTrack(chosen_track);
+					Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
+					if (IsFrontEngine(v) && HasPbsSignalOnTrackdir(gp.new_tile, tdir)) {
+						SetSignalStateByTrackdir(gp.new_tile, tdir, SIGNAL_STATE_RED);
+						MarkTileDirtyByTile(gp.new_tile);
+					}
+
+					/* Clear any track reservation when the last vehicle leaves the tile */
+					if (v->Next() == NULL) ClearPathReservation(v->tile, GetVehicleTrackdir(v));
+
 					v->tile = gp.new_tile;
 
 					if (GetTileRailType(gp.new_tile) != GetTileRailType(gp.old_tile)) {
@@ -3064,6 +3676,15 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
 				if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir);
 
 				v->direction = chosen_dir;
+
+				if (IsFrontEngine(v)) {
+					/* If we are approching a crossing that is reserved, play the sound now. */
+					TileIndex crossing = TrainApproachingCrossingTile(v);
+					if (crossing != INVALID_TILE && GetCrossingReservation(crossing)) SndPlayTileFx(SND_0E_LEVEL_CROSSING, crossing);
+
+					/* Always try to extend the reservation when entering a tile. */
+					CheckNextTrainTile(v);
+				}
 			}
 		} else {
 			/* In a tunnel or on a bridge
@@ -3074,7 +3695,10 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
 					min(v->cur_speed, GetBridgeSpec(GetBridgeType(v->tile))->speed);
 			}
 
-			if (!IsTileType(gp.new_tile, MP_TUNNELBRIDGE) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
+			if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
+				/* Perform look-ahead on tunnel exit. */
+				CheckNextTrainTile(v);
+			} else {
 				v->x_pos = gp.x;
 				v->y_pos = gp.y;
 				VehiclePositionChanged(v);
@@ -3099,7 +3723,21 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
 		}
 
 		if (update_signals_crossing) {
-			if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
+			if (IsFrontEngine(v)) {
+				if (TrainMovedChangeSignals(gp.new_tile, enterdir)) {
+					/* We are entering a block with PBS signals right now, but
+					 * not through a PBS signal. This means we don't have a
+					 * reservation right now. As a conventional signal will only
+					 * ever be green if no other train is in the block, getting
+					 * a path should always be possible. If the player built
+					 * such a strange network that it is not possible, the train
+					 * will be marked as stuck and the player has to deal with
+					 * the problem. */
+					if (!(HasReservedTracks(gp.new_tile, v->u.rail.track) ||
+								TryReserveRailTrack(gp.new_tile, FindFirstTrack(v->u.rail.track))) ||
+							!TryPathReserve(v)) MarkTrainAsStuck(v);
+				}
+			}
 
 			/* Signals can only change when the first
 			 * (above) or the last vehicle moves. */
@@ -3108,6 +3746,9 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
 				if (IsLevelCrossingTile(gp.old_tile)) UpdateLevelCrossing(gp.old_tile);
 			}
 		}
+
+		/* Do not check on every tick to save some computing time. */
+		if (IsFrontEngine(v) && v->tick_counter % _settings_game.pf.path_backoff_interval == 0) CheckNextTrainTile(v);
 	}
 	return;
 
@@ -3158,6 +3799,31 @@ static void DeleteLastWagon(Vehicle *v)
 
 	MarkSingleVehicleDirty(v);
 
+	/* Clear a possible path reservation */
+	if (IsFrontEngine(v)) {
+		if (IsTileType(v->tile, MP_TUNNELBRIDGE) && GetTunnelBridgeReservation(v->tile)) {
+			DiagDirection dir = ReverseDiagDir(GetTunnelBridgeDirection(v->tile));
+			ClearPathReservation(v->tile, DiagDirToDiagTrackdir(dir));
+		} else if (IsRailwayStationTile(v->tile) && GetRailwayStationReservation(v->tile)) {
+			DiagDirection dir = AxisToDiagDir(GetRailStationAxis(v->tile));
+			SetRailwayStationPlatformReservation(v->tile, dir, false);
+			SetRailwayStationPlatformReservation(v->tile, ReverseDiagDir(dir), false);
+		} else if (v->u.rail.track == TRACK_BIT_DEPOT && GetDepotReservation(v->tile)) {
+			SetDepotReservation(v->tile, false);
+		} else if (HasReservedTracks(v->tile, v->u.rail.track)) {
+			Trackdir td = TrackToTrackdir(TrackBitsToTrack(v->u.rail.track));
+			ClearPathReservation(v->tile, td);
+			if (HasPbsSignalOnTrackdir(v->tile, td)) SetSignalStateByTrackdir(v->tile, td, SIGNAL_STATE_RED);
+			if (HasPbsSignalOnTrackdir(v->tile, ReverseTrackdir(td))) SetSignalStateByTrackdir(v->tile, ReverseTrackdir(td), SIGNAL_STATE_RED);
+		}
+	} else if ((v->u.rail.track & ~TRACK_BIT_MASK) == TRACK_BIT_NONE && u->tile != v->tile && HasReservedTracks(v->tile, v->u.rail.track)) {
+		DiagDirection dir = DiagDirByTileIndexDiff(TileIndexToTileIndexDiffC(u->tile, v->tile));
+		Trackdir      td = TrackExitdirToTrackdir(TrackBitsToTrack(v->u.rail.track), dir);
+		assert(IsValidTrackdir(td));
+
+		ClearPathReservation(v->tile, td);
+	}
+
 	/* 'v' shouldn't be accessed after it has been deleted */
 	TrackBits track = v->u.rail.track;
 	TileIndex tile = v->tile;
@@ -3433,7 +4099,10 @@ static void TrainLocoHandler(Vehicle *v, bool mode)
 		return;
 	}
 
-	if (v->u.rail.force_proceed != 0) v->u.rail.force_proceed--;
+	if (v->u.rail.force_proceed != 0) {
+		v->u.rail.force_proceed--;
+		ClrBit(v->u.rail.flags, VRF_TRAIN_STUCK);
+	}
 
 	/* train is broken down? */
 	if (v->breakdown_ctr != 0) {
@@ -3451,6 +4120,7 @@ static void TrainLocoHandler(Vehicle *v, bool mode)
 	/* exit if train is stopped */
 	if (v->vehstatus & VS_STOPPED && v->cur_speed == 0) return;
 
+	bool valid_order = v->current_order.IsValid() && v->current_order.GetType() != OT_CONDITIONAL;
 	if (ProcessOrders(v) && CheckReverseTrain(v)) {
 		v->load_unload_time_rem = 0;
 		v->cur_speed = 0;
@@ -3467,6 +4137,42 @@ static void TrainLocoHandler(Vehicle *v, bool mode)
 
 	if (!mode) HandleLocomotiveSmokeCloud(v);
 
+	/* We had no order but have an order now, do look ahead. */
+	if (!valid_order && v->current_order.IsValid()) {
+		CheckNextTrainTile(v);
+	}
+
+	/* Handle stuck trains. */
+	if (!mode && HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) {
+		++v->load_unload_time_rem;
+
+		/* Should we try reversing this tick if still stuck? */
+		bool turn_around = v->load_unload_time_rem % (_settings_game.pf.wait_for_pbs_path * DAY_TICKS) == 0 && _settings_game.pf.wait_for_pbs_path < 255;
+
+		if (!turn_around && v->load_unload_time_rem % _settings_game.pf.path_backoff_interval != 0 && v->u.rail.force_proceed == 0) return;
+		if (!TryPathReserve(v)) {
+			/* Still stuck. */
+			if (turn_around) ReverseTrainDirection(v);
+
+			if (HasBit(v->u.rail.flags, VRF_TRAIN_STUCK) && v->load_unload_time_rem > 2 * _settings_game.pf.wait_for_pbs_path * DAY_TICKS) {
+				/* Show message to player. */
+				if (_settings_client.gui.lost_train_warn && v->owner == _local_player) {
+					SetDParam(0, v->unitnumber);
+					AddNewsItem(
+						STR_TRAIN_IS_STUCK,
+						NS_ADVICE,
+						v->index,
+						0);
+				}
+				v->load_unload_time_rem = 0;
+			}
+			/* Exit if force proceed not pressed, else reset stuck flag anyway. */
+			if (v->u.rail.force_proceed == 0) return;
+			ClrBit(v->u.rail.flags, VRF_TRAIN_STUCK);
+			v->load_unload_time_rem = 0;
+		}
+	}
+
 	int j = UpdateTrainSpeed(v);
 
 	/* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */
diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp
index ab05372..9bdff69 100644
--- a/src/tunnelbridge_cmd.cpp
+++ b/src/tunnelbridge_cmd.cpp
@@ -885,6 +885,13 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
 
 		image += tunnelbridge_direction * 2;
 		DrawGroundSprite(image, PAL_NONE);
+
+		/* PBS debugging, draw reserved tracks darker */
+		if (_settings_client.gui.show_track_reservation && (transport_type == TRANSPORT_RAIL && GetTunnelBridgeReservation(ti->tile))) {
+			const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+			DrawGroundSprite(DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH);
+		}
+
 		if (transport_type == TRANSPORT_ROAD) {
 			RoadTypes rts = GetRoadTypes(ti->tile);
 
@@ -961,6 +968,15 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
 		/* Bridge heads are drawn solid no matter how invisibility/transparency is set */
 		AddSortableSpriteToDraw(psid->sprite, psid->pal, ti->x, ti->y, 16, 16, ti->tileh == SLOPE_FLAT ? 0 : 8, ti->z);
 
+		if (_settings_client.gui.show_track_reservation && transport_type == TRANSPORT_RAIL && GetTunnelBridgeReservation(ti->tile)) {
+			const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
+			if (HasBridgeFlatRamp(ti->tileh, DiagDirToAxis(tunnelbridge_direction))) {
+				AddSortableSpriteToDraw(DiagDirToAxis(tunnelbridge_direction) == AXIS_X ? rti->base_sprites.single_y : rti->base_sprites.single_x, PALETTE_CRASH, ti->x, ti->y, 16, 16, 0, ti->z + 8);
+			} else {
+				AddSortableSpriteToDraw(rti->base_sprites.single_sloped + tunnelbridge_direction, PALETTE_CRASH, ti->x, ti->y, 16, 16, 8, ti->z);
+			}
+		}
+
 		if (transport_type == TRANSPORT_ROAD) {
 			RoadTypes rts = GetRoadTypes(ti->tile);
 
diff --git a/src/tunnelbridge_map.h b/src/tunnelbridge_map.h
index bdc7099..afcfb58 100644
--- a/src/tunnelbridge_map.h
+++ b/src/tunnelbridge_map.h
@@ -11,6 +11,7 @@
 #include "bridge_map.h"
 #include "tunnel_map.h"
 #include "transport_type.h"
+#include "track_func.h"
 
 
 /**
@@ -80,4 +81,42 @@ static inline TileIndex GetOtherTunnelBridgeEnd(TileIndex t)
 	return IsTunnel(t) ? GetOtherTunnelEnd(t) : GetOtherBridgeEnd(t);
 }
 
+
+/**
+ * Get the reservation state of the rail tunnel/bridge
+ * @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
+ * @param t the tile
+ * @return reservation state
+ */
+static inline bool GetTunnelBridgeReservation(TileIndex t)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL);
+	return HasBit(_m[t].m5, 4);
+}
+
+/**
+ * Set the reservation state of the rail tunnel/bridge
+ * @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
+ * @param t the tile
+ * @param b the reservation state
+ */
+static inline void SetTunnelBridgeReservation(TileIndex t, bool b)
+{
+	assert(IsTileType(t, MP_TUNNELBRIDGE));
+	assert(GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL);
+	SB(_m[t].m5, 4, 1, (byte)b & 1);
+}
+
+/**
+ * Get the reserved track bits for a rail tunnel/bridge
+ * @pre IsTileType(t, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(t) == TRANSPORT_RAIL
+ * @param t the tile
+ * @return reserved track bits
+ */
+static inline TrackBits GetRailTunnelBridgeReservation(TileIndex t)
+{
+	return GetTunnelBridgeReservation(t) ? DiagDirToDiagTrackBits(GetTunnelBridgeDirection(t)) : TRACK_BIT_NONE;
+}
+
 #endif /* TUNNELBRIDGE_MAP_H */
diff --git a/src/vehicle.cpp b/src/vehicle.cpp
index bda5efe..028f389 100644
--- a/src/vehicle.cpp
+++ b/src/vehicle.cpp
@@ -2069,7 +2069,8 @@ static const SaveLoad _train_desc[] = {
 	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, railtype),               SLE_UINT8),
 	    SLE_VARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, track),                  SLE_UINT8),
 
-	SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags),                  SLE_UINT8,  2, SL_MAX_VERSION),
+	SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags), SLE_FILE_U8  | SLE_VAR_U16,  2, 98),
+	SLE_CONDVARX(cpp_offsetof(Vehicle, u) + cpp_offsetof(VehicleRail, flags),                 SLE_UINT16, 99, SL_MAX_VERSION),
 	SLE_CONDNULL(2, 2, 59),
 
 	SLE_CONDNULL(2, 2, 19),
diff --git a/src/vehicle_base.h b/src/vehicle_base.h
index 0defc95..440f30d 100644
--- a/src/vehicle_base.h
+++ b/src/vehicle_base.h
@@ -115,7 +115,7 @@ struct VehicleRail {
 	 * 0xffff == not in train */
 	EngineID first_engine;
 
-	byte flags;
+	uint16 flags;
 	TrackBitsByte track;
 	byte force_proceed;
 	RailTypeByte railtype;
@@ -143,6 +143,9 @@ enum VehicleRailFlags {
 
 	/* used for vehicle var 0xFE bit 8 (toggled each time the train is reversed, accurate for first vehicle only) */
 	VRF_TOGGLE_REVERSE = 7,
+
+	/* used to mark a train that can't get a path reservation */
+	VRF_TRAIN_STUCK    = 8,
 };
 
 struct VehicleAir {
diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp
index 8b9fe17..6b20d31 100644
--- a/src/vehicle_gui.cpp
+++ b/src/vehicle_gui.cpp
@@ -1956,6 +1956,8 @@ struct VehicleViewWindow : Window {
 			} else { // no train
 				str = STR_8861_STOPPED;
 			}
+		} else if (v->type == VEH_TRAIN && HasBit(v->u.rail.flags, VRF_TRAIN_STUCK)) {
+			str = STR_TRAIN_STUCK;
 		} else { // vehicle is in a "normal" state, show current order
 			switch (v->current_order.GetType()) {
 				case OT_GOTO_STATION: {
diff --git a/src/viewport.cpp b/src/viewport.cpp
index fa37bcf..68e1494 100644
--- a/src/viewport.cpp
+++ b/src/viewport.cpp
@@ -1870,7 +1870,7 @@ static bool CheckClickOnSign(const ViewPort *vp, int x, int y)
 						y <  si->sign.top + 12 &&
 						x >= si->sign.left &&
 						x <  si->sign.left + si->sign.width_1) {
-					HandleClickOnSign(si);
+					ShowRenameSignWindow(si);
 					return true;
 				}
 			}
@@ -1884,7 +1884,7 @@ static bool CheckClickOnSign(const ViewPort *vp, int x, int y)
 						y <  si->sign.top + 24 &&
 						x >= si->sign.left &&
 						x <  si->sign.left + si->sign.width_1 * 2) {
-					HandleClickOnSign(si);
+					ShowRenameSignWindow(si);
 					return true;
 				}
 			}
@@ -1900,7 +1900,7 @@ static bool CheckClickOnSign(const ViewPort *vp, int x, int y)
 						y <  si->sign.top + ScaleByZoom(12, vp->zoom) &&
 						x >= si->sign.left &&
 						x <  si->sign.left + ScaleByZoom(si->sign.width_2, vp->zoom)) {
-					HandleClickOnSign(si);
+					ShowRenameSignWindow(si);
 					return true;
 				}
 			}
diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp
index b72b0a9..6caa8ab 100644
--- a/src/water_cmd.cpp
+++ b/src/water_cmd.cpp
@@ -734,16 +734,15 @@ static void GetTileDesc_Water(TileIndex tile, TileDesc *td)
 {
 	switch (GetWaterTileType(tile)) {
 		case WATER_TILE_CLEAR:
-			switch (GetWaterClass(tile)) {
-				case WATER_CLASS_SEA:   td->str = STR_3804_WATER;     break;
-				case WATER_CLASS_CANAL: td->str = STR_LANDINFO_CANAL; break;
-				case WATER_CLASS_RIVER: td->str = STR_LANDINFO_RIVER; break;
-				default: assert(0); break;
+			if (!IsCanal(tile)) {
+				td->str = STR_3804_WATER;
+			} else {
+				td->str = STR_LANDINFO_CANAL;
 			}
 			break;
 		case WATER_TILE_COAST: td->str = STR_3805_COAST_OR_RIVERBANK; break;
-		case WATER_TILE_LOCK : td->str = STR_LANDINFO_LOCK;           break;
-		case WATER_TILE_DEPOT: td->str = STR_3806_SHIP_DEPOT;         break;
+		case WATER_TILE_LOCK : td->str = STR_LANDINFO_LOCK; break;
+		case WATER_TILE_DEPOT: td->str = STR_3806_SHIP_DEPOT; break;
 		default: assert(0); break;
 	}
 
diff --git a/src/waypoint.cpp b/src/waypoint.cpp
index 6dd30cf..397c4ae 100644
--- a/src/waypoint.cpp
+++ b/src/waypoint.cpp
@@ -249,7 +249,9 @@ CommandCost CmdBuildTrainWaypoint(TileIndex tile, uint32 flags, uint32 p1, uint3
 
 		const StationSpec* statspec;
 
+		bool reserved = HasBit(GetTrackReservation(tile), AxisToTrack(axis));
 		MakeRailWaypoint(tile, GetTileOwner(tile), axis, GetRailType(tile), wp->index);
+		SetWaypointReservation(tile, reserved);
 		MarkTileDirtyByTile(tile);
 
 		statspec = GetCustomStationSpec(STAT_CLASS_WAYP, p1);
@@ -319,7 +321,10 @@ CommandCost RemoveTrainWaypoint(TileIndex tile, uint32 flags, bool justremove)
 		RedrawWaypointSign(wp);
 
 		if (justremove) {
-			MakeRailNormal(tile, GetTileOwner(tile), GetRailWaypointBits(tile), GetRailType(tile));
+			TrackBits tracks = GetRailWaypointBits(tile);
+			bool reserved = GetWaypointReservation(tile);
+			MakeRailNormal(tile, GetTileOwner(tile), tracks, GetRailType(tile));
+			if (reserved) SetTrackReservation(tile, tracks);
 			MarkTileDirtyByTile(tile);
 		} else {
 			DoClearSquare(tile);
diff --git a/src/yapf/follow_track.hpp b/src/yapf/follow_track.hpp
index d3c9c45..a2f727d 100644
--- a/src/yapf/follow_track.hpp
+++ b/src/yapf/follow_track.hpp
@@ -10,8 +10,8 @@
 
 /** Track follower helper template class (can serve pathfinders and vehicle
  *  controllers). See 6 different typedefs below for 3 different transport
- *  types w/ of w/o 90-deg turns allowed */
-template <TransportType Ttr_type_, bool T90deg_turns_allowed_ = true>
+ *  types w/ or w/o 90-deg turns allowed */
+template <TransportType Ttr_type_, bool T90deg_turns_allowed_ = true, bool Tmask_reserved_tracks = false>
 struct CFollowTrackT
 {
 	enum ErrorCode {
@@ -20,6 +20,7 @@ struct CFollowTrackT
 		EC_RAIL_TYPE,
 		EC_90DEG,
 		EC_NO_WAY,
+		EC_RESERVED,
 	};
 
 	const Vehicle      *m_veh;           ///< moving vehicle
@@ -34,13 +35,14 @@ struct CFollowTrackT
 	int                 m_tiles_skipped; ///< number of skipped tunnel or station tiles
 	ErrorCode           m_err;
 	CPerformanceTimer  *m_pPerf;
+	RailTypes           m_railtypes;
 
-	FORCEINLINE CFollowTrackT(const Vehicle *v = NULL, CPerformanceTimer* pPerf = NULL)
+	FORCEINLINE CFollowTrackT(const Vehicle *v = NULL, RailTypes railtype_override = INVALID_RAILTYPES, CPerformanceTimer *pPerf = NULL)
 	{
-		Init(v, pPerf);
+		Init(v, railtype_override, pPerf);
 	}
 
-	FORCEINLINE void Init(const Vehicle *v, CPerformanceTimer* pPerf)
+	FORCEINLINE void Init(const Vehicle *v, RailTypes railtype_override, CPerformanceTimer *pPerf)
 	{
 		assert(!IsRailTT() || (v != NULL && v->type == VEH_TRAIN));
 		m_veh = v;
@@ -52,6 +54,7 @@ struct CFollowTrackT
 		m_is_station = m_is_bridge = m_is_tunnel = false;
 		m_tiles_skipped = 0;
 		m_err = EC_NONE;
+		if (IsRailTT()) m_railtypes = railtype_override == INVALID_RAILTYPES ? v->u.rail.compatible_railtypes : railtype_override;
 	}
 
 	FORCEINLINE static TransportType TT() {return Ttr_type_;}
@@ -60,6 +63,7 @@ struct CFollowTrackT
 	FORCEINLINE bool IsTram() {return IsRoadTT() && HasBit(m_veh->u.road.compatible_roadtypes, ROADTYPE_TRAM);}
 	FORCEINLINE static bool IsRoadTT() {return TT() == TRANSPORT_ROAD;}
 	FORCEINLINE static bool Allow90degTurns() {return T90deg_turns_allowed_;}
+	FORCEINLINE static bool MaskReservedTracks() {return Tmask_reserved_tracks;}
 
 	/** Tests if a tile is a road tile with a single tramtrack (tram can reverse) */
 	FORCEINLINE DiagDirection GetSingleTramBit(TileIndex tile)
@@ -79,7 +83,7 @@ struct CFollowTrackT
 
 	/** main follower routine. Fills all members and return true on success.
 	 *  Otherwise returns false if track can't be followed. */
-	FORCEINLINE bool Follow(TileIndex old_tile, Trackdir old_td)
+	inline bool Follow(TileIndex old_tile, Trackdir old_td)
 	{
 		m_old_tile = old_tile;
 		m_old_td = old_td;
@@ -104,6 +108,21 @@ struct CFollowTrackT
 				return false;
 			}
 		}
+		if (MaskReservedTracks()) {
+			TrackBits reserved = GetReservedTrackbits(m_new_tile);
+			/* Mask already reserved trackdirs. */
+			m_new_td_bits &= ~TrackBitsToTrackdirBits(reserved);
+			/* Mask out all trackdirs that conflict with the reservation. */
+			uint bits = (uint)TrackdirBitsToTrackBits(m_new_td_bits);
+			int i;
+			FOR_EACH_SET_BIT(i, bits) {
+				if (TracksOverlap(reserved | TrackToTrackBits((Track)i))) m_new_td_bits &= ~TrackToTrackdirBits((Track)i);
+			}
+			if (m_new_td_bits == TRACKDIR_BIT_NONE) {
+				m_err = EC_RESERVED;
+				return false;
+			}
+		}
 		return true;
 	}
 
@@ -256,7 +275,7 @@ protected:
 		// rail transport is possible only on compatible rail types
 		if (IsRailTT()) {
 			RailType rail_type = GetTileRailType(m_new_tile);
-			if (!HasBit(m_veh->u.rail.compatible_railtypes, rail_type)) {
+			if (!HasBit(m_railtypes, rail_type)) {
 				// incompatible rail type
 				m_err = EC_RAIL_TYPE;
 				return false;
@@ -380,4 +399,7 @@ typedef CFollowTrackT<TRANSPORT_WATER, false> CFollowTrackWaterNo90;
 typedef CFollowTrackT<TRANSPORT_ROAD , false> CFollowTrackRoadNo90;
 typedef CFollowTrackT<TRANSPORT_RAIL , false> CFollowTrackRailNo90;
 
+typedef CFollowTrackT<TRANSPORT_RAIL , true , true> CFollowTrackFreeRail;
+typedef CFollowTrackT<TRANSPORT_RAIL , false, true> CFollowTrackFreeRailNo90;
+
 #endif /* FOLLOW_TRACK_HPP */
diff --git a/src/yapf/yapf.h b/src/yapf/yapf.h
index ca400a7..45ba94e 100644
--- a/src/yapf/yapf.h
+++ b/src/yapf/yapf.h
@@ -8,6 +8,7 @@
 #include "../debug.h"
 #include "../depot_type.h"
 #include "../direction_type.h"
+#include "../pbs.h"
 
 /** Finds the best path for given ship.
  * @param v        the ship that needs to find a path
@@ -32,9 +33,11 @@ Trackdir YapfChooseRoadTrack(const Vehicle *v, TileIndex tile, DiagDirection ent
  * @param enterdir diagonal direction which the RV will enter this new tile from
  * @param tracks   available trackdirs on the new tile (to choose from)
  * @param path_not_found [out] true is returned if no path can be found (returned Trackdir is only a 'guess')
+ * @param reserve_track indicates whether YAPF should try to reserve the found path
+ * @param target   [out] the target tile of the reservation, free is set to true if path was reserved
  * @return         the best trackdir for next turn or INVALID_TRACKDIR if the path could not be found
  */
-Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found);
+Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target);
 
 /** Used by RV multistop feature to find the nearest road stop that has a free slot.
  * @param v      RV (its current tile will be the origin)
@@ -63,6 +66,17 @@ bool YapfFindNearestRailDepotTwoWay(const Vehicle *v, int max_distance, int reve
 /** Returns true if it is better to reverse the train before leaving station */
 bool YapfCheckReverseTrain(const Vehicle* v);
 
+/**
+ * Try to extend the reserved path of a train to the nearest safe tile.
+ *
+ * @param v    The train that needs to find a safe tile.
+ * @param tile Last tile of the current reserved path.
+ * @param td   Last trackdir of the current reserved path.
+ * @param override_railtype Should all physically compabtible railtypes be searched, even if the vehicle can't on them on it own?
+ * @return True if the path could be extended to a safe tile.
+ */
+bool YapfRailFindNearestSafeTile(const Vehicle *v, TileIndex tile, Trackdir td, bool override_railtype);
+
 /** Use this function to notify YAPF that track layout (or signal configuration) has change */
 void YapfNotifyTrackLayoutChange(TileIndex tile, Track track);
 
diff --git a/src/yapf/yapf_costrail.hpp b/src/yapf/yapf_costrail.hpp
index abd4210..c19353f 100644
--- a/src/yapf/yapf_costrail.hpp
+++ b/src/yapf/yapf_costrail.hpp
@@ -5,6 +5,7 @@
 #ifndef  YAPF_COSTRAIL_HPP
 #define  YAPF_COSTRAIL_HPP
 
+#include "../pbs.h"
 
 template <class Types>
 class CYapfCostRailT
@@ -104,6 +105,16 @@ public:
 		return cost;
 	}
 
+	FORCEINLINE int SwitchCost(TileIndex tile1, TileIndex tile2, DiagDirection exitdir)
+	{
+		if (IsTileType(tile1, MP_RAILWAY) && IsTileType(tile2, MP_RAILWAY)) {
+			bool t1 = KillFirstBit(GetTrackBits(tile1) & DiagdirReachesTracks(ReverseDiagDir(exitdir))) != TRACK_BIT_NONE;
+			bool t2 = KillFirstBit(GetTrackBits(tile2) & DiagdirReachesTracks(exitdir)) != TRACK_BIT_NONE;
+			if (t1 && t2) return Yapf().PfGetSettings().rail_doubleslip_penalty;
+		}
+		return 0;
+	}
+
 	/** Return one tile cost (base cost + level crossing penalty). */
 	FORCEINLINE int OneTileCost(TileIndex& tile, Trackdir trackdir)
 	{
@@ -128,6 +139,19 @@ public:
 		return cost;
 	}
 
+	/** The cost for reserved tiles, including skipped ones. */
+	FORCEINLINE int ReservationCost(Node& n, TileIndex& tile, Trackdir trackdir, int skipped)
+	{
+		if (n.m_num_signals_passed >= m_sig_look_ahead_costs.Size() / 2) return 0;
+
+		if (TrackOverlapsTracks(GetReservedTrackbits(tile), TrackdirToTrack(trackdir))) {
+			int cost = IsRailwayStationTile(tile) ? Yapf().PfGetSettings().rail_pbs_station_penalty : Yapf().PfGetSettings().rail_pbs_cross_penalty;
+			if (!IsDiagonalTrackdir(trackdir)) cost = (cost * YAPF_TILE_CORNER_LENGTH) / YAPF_TILE_LENGTH;
+			return cost * (skipped + 1);
+		}
+		return 0;
+	}
+
 	int SignalCost(Node& n, TileIndex tile, Trackdir trackdir)
 	{
 		int cost = 0;
@@ -136,53 +160,61 @@ public:
 		if (IsTileType(tile, MP_RAILWAY)) {
 			bool has_signal_against = HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir));
 			bool has_signal_along = HasSignalOnTrackdir(tile, trackdir);
-			if (has_signal_against && !has_signal_along) {
+			if (has_signal_against && !has_signal_along && IsOnewaySignal(tile, TrackdirToTrack(trackdir))) {
 				// one-way signal in opposite direction
 				n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
-			} else if (has_signal_along) {
-				SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir);
-				// cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is
-				int look_ahead_cost = (n.m_num_signals_passed < m_sig_look_ahead_costs.Size()) ? m_sig_look_ahead_costs.Data()[n.m_num_signals_passed] : 0;
-				if (sig_state != SIGNAL_STATE_RED) {
-					// green signal
-					n.flags_u.flags_s.m_last_signal_was_red = false;
-					// negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal
-					if (look_ahead_cost < 0) {
-						// add its negation to the cost
-						cost -= look_ahead_cost;
-					}
-				} else {
-					// we have a red signal in our direction
-					// was it first signal which is two-way?
-					if (Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.m_choice_seen && has_signal_against && n.m_num_signals_passed == 0) {
-						// yes, the first signal is two-way red signal => DEAD END
-						n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
-						Yapf().m_stopped_on_first_two_way_signal = true;
-						return -1;
-					}
-					SignalType sig_type = GetSignalType(tile, TrackdirToTrack(trackdir));
-					n.m_last_red_signal_type = sig_type;
-					n.flags_u.flags_s.m_last_signal_was_red = true;
-
-					// look-ahead signal penalty
-					if (look_ahead_cost > 0) {
-						// add the look ahead penalty only if it is positive
-						cost += look_ahead_cost;
-					}
+			} else {
+				if (has_signal_along) {
+					SignalState sig_state = GetSignalStateByTrackdir(tile, trackdir);
+					// cache the look-ahead polynomial constant only if we didn't pass more signals than the look-ahead limit is
+					int look_ahead_cost = (n.m_num_signals_passed < m_sig_look_ahead_costs.Size()) ? m_sig_look_ahead_costs.Data()[n.m_num_signals_passed] : 0;
+					if (sig_state != SIGNAL_STATE_RED) {
+						// green signal
+						n.flags_u.flags_s.m_last_signal_was_red = false;
+						// negative look-ahead red-signal penalties would cause problems later, so use them as positive penalties for green signal
+						if (look_ahead_cost < 0) {
+							// add its negation to the cost
+							cost -= look_ahead_cost;
+						}
+					} else {
+						SignalType sig_type = GetSignalType(tile, TrackdirToTrack(trackdir));
+						// we have a red signal in our direction
+						// was it first signal which is two-way?
+						if (!IsPbsSignal(sig_type) && Yapf().TreatFirstRedTwoWaySignalAsEOL() && n.flags_u.flags_s.m_choice_seen && has_signal_against && n.m_num_signals_passed == 0) {
+							// yes, the first signal is two-way red signal => DEAD END
+							n.m_segment->m_end_segment_reason |= ESRB_DEAD_END;
+							Yapf().m_stopped_on_first_two_way_signal = true;
+							return -1;
+						}
+						n.m_last_red_signal_type = sig_type;
+						n.flags_u.flags_s.m_last_signal_was_red = true;
+
+						// look-ahead signal penalty
+						if (!IsPbsSignal(sig_type) && look_ahead_cost > 0) {
+							// add the look ahead penalty only if it is positive
+							cost += look_ahead_cost;
+						}
 
-					// special signal penalties
-					if (n.m_num_signals_passed == 0) {
-						switch (sig_type) {
-							case SIGTYPE_COMBO:
-							case SIGTYPE_EXIT:   cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
-							case SIGTYPE_NORMAL:
-							case SIGTYPE_ENTRY:  cost += Yapf().PfGetSettings().rail_firstred_penalty; break;
-						};
+						// special signal penalties
+						if (n.m_num_signals_passed == 0) {
+							switch (sig_type) {
+								case SIGTYPE_COMBO:
+								case SIGTYPE_EXIT:   cost += Yapf().PfGetSettings().rail_firstred_exit_penalty; break; // first signal is red pre-signal-exit
+								case SIGTYPE_NORMAL:
+								case SIGTYPE_ENTRY:  cost += Yapf().PfGetSettings().rail_firstred_penalty; break;
+								default: break;
+							};
+						}
 					}
+
+					n.m_num_signals_passed++;
+					n.m_segment->m_last_signal_tile = tile;
+					n.m_segment->m_last_signal_td = trackdir;
+				}
+
+				if (has_signal_against && IsPbsSignal(GetSignalType(tile, TrackdirToTrack(trackdir)))) {
+					cost += n.m_num_signals_passed < Yapf().PfGetSettings().rail_look_ahead_max_signals ? Yapf().PfGetSettings().rail_pbs_signal_back_penalty : 0;
 				}
-				n.m_num_signals_passed++;
-				n.m_segment->m_last_signal_tile = tile;
-				n.m_segment->m_last_signal_td = trackdir;
 			}
 		}
 		return cost;
@@ -271,7 +303,7 @@ public:
 
 		EndSegmentReasonBits end_segment_reason = ESRB_NONE;
 
-		TrackFollower tf_local(v, &Yapf().m_perf_ts_cost);
+		TrackFollower tf_local(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost);
 
 		if (!has_parent) {
 			/* We will jump to the middle of the cost calculator assuming that segment cache is not used. */
@@ -283,6 +315,7 @@ public:
 		for (;;) {
 			/* Transition cost (cost of the move from previous tile) */
 			transition_cost = Yapf().CurveCost(prev.td, cur.td);
+			transition_cost += Yapf().SwitchCost(prev.tile, cur.tile, TrackdirToExitdir(prev.td));
 
 			/* First transition cost counts against segment entry cost, other transitions
 			 * inside segment will come to segment cost (and will be cached) */
@@ -327,6 +360,9 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
 			/* Slope cost. */
 			segment_cost += Yapf().SlopeCost(cur.tile, cur.td);
 
+			/* Reserved tiles. */
+			segment_cost += Yapf().ReservationCost(n, cur.tile, cur.td, tf->m_tiles_skipped);
+
 			/* Signal cost (routine can modify segment data). */
 			segment_cost += Yapf().SignalCost(n, cur.tile, cur.td);
 			end_segment_reason = segment.m_end_segment_reason;
@@ -351,6 +387,11 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
 			} else if (cur.tile_type == MP_RAILWAY && IsRailWaypoint(cur.tile)) {
 				/* Waypoint is also a good reason to finish. */
 				end_segment_reason |= ESRB_WAYPOINT;
+			} else if (TrackFollower::MaskReservedTracks() && cur.tile_type == MP_RAILWAY) {
+				/* Searching for a safe tile? */
+				if (HasSignalOnTrackdir(cur.tile, cur.td) && !IsPbsSignal(GetSignalType(cur.tile, TrackdirToTrack(cur.td)))) {
+					end_segment_reason |= ESRB_SAFE_TILE;
+				}
 			}
 
 			/* Apply min/max speed penalties only when inside the look-ahead radius. Otherwise
@@ -373,7 +414,7 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
 
 			/* Move to the next tile/trackdir. */
 			tf = &tf_local;
-			tf_local.Init(v, &Yapf().m_perf_ts_cost);
+			tf_local.Init(v, Yapf().GetCompatibleRailTypes(), &Yapf().m_perf_ts_cost);
 
 			if (!tf_local.Follow(cur.tile, cur.td)) {
 				assert(tf_local.m_err != TrackFollower::EC_NONE);
@@ -383,6 +424,10 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
 				} else {
 					end_segment_reason |= ESRB_DEAD_END;
 				}
+
+				if (TrackFollower::MaskReservedTracks() && tf_local.m_err != TrackFollower::EC_90DEG) {
+					end_segment_reason |= ESRB_SAFE_TILE;
+				}
 				break;
 			}
 
@@ -396,6 +441,11 @@ no_entry_cost: // jump here at the beginning if the node has no parent (it is th
 			/* Gather the next tile/trackdir/tile_type/rail_type. */
 			TILE next(tf_local.m_new_tile, (Trackdir)FindFirstBit2x64(tf_local.m_new_td_bits));
 
+			if (TrackFollower::MaskReservedTracks() && HasPbsSignalOnTrackdir(next.tile, next.td)) {
+				/* Possible safe tile. */
+				end_segment_reason |= ESRB_SAFE_TILE;
+			}
+
 			/* Check the next tile for the rail type. */
 			if (next.rail_type != cur.rail_type) {
 				/* Segment must consist from the same rail_type tiles. */
diff --git a/src/yapf/yapf_destrail.hpp b/src/yapf/yapf_destrail.hpp
index aba4094..9790758 100644
--- a/src/yapf/yapf_destrail.hpp
+++ b/src/yapf/yapf_destrail.hpp
@@ -11,15 +11,21 @@ protected:
 	RailTypes m_compatible_railtypes;
 
 public:
-	void SetDestination(const Vehicle* v)
+	void SetDestination(const Vehicle *v, bool override_rail_type = false)
 	{
 		m_compatible_railtypes = v->u.rail.compatible_railtypes;
+		if (override_rail_type) m_compatible_railtypes |= GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes;
 	}
 
 	bool IsCompatibleRailType(RailType rt)
 	{
 		return HasBit(m_compatible_railtypes, rt);
 	}
+
+	RailTypes GetCompatibleRailTypes() const
+	{
+		return m_compatible_railtypes;
+	}
 };
 
 template <class Types>
@@ -57,6 +63,41 @@ public:
 };
 
 template <class Types>
+class CYapfDestinationAnySafeTileRailT
+	: public CYapfDestinationRailBase
+{
+public:
+	typedef typename Types::Tpf Tpf;              ///< the pathfinder class (derived from THIS class)
+	typedef typename Types::NodeList::Titem Node; ///< this will be our node type
+	typedef typename Node::Key Key;               ///< key to hash tables
+
+	/// to access inherited path finder
+	Tpf& Yapf() {return *static_cast<Tpf*>(this);}
+
+	/// Called by YAPF to detect if node ends in the desired destination
+	FORCEINLINE bool PfDetectDestination(Node& n)
+	{
+		return PfDetectDestination(n.GetLastTile(), n.GetLastTrackdir());
+	}
+
+	/// Called by YAPF to detect if node ends in the desired destination
+	FORCEINLINE bool PfDetectDestination(TileIndex tile, Trackdir td)
+	{
+		return
+			IsSafeWaitingPosition(Yapf().GetVehicle(), tile, td, true, Types::TrackFollower::Allow90degTurns()) &&
+			IsWaitingPositionFree(Yapf().GetVehicle(), tile, td, Types::TrackFollower::Allow90degTurns());
+	}
+
+	/** Called by YAPF to calculate cost estimate. Calculates distance to the destination
+	 *  adds it to the actual cost from origin and stores the sum to the Node::m_estimate. */
+	FORCEINLINE bool PfCalcEstimate(Node& n)
+	{
+		n.m_estimate = n.m_cost;
+		return true;
+	}
+};
+
+template <class Types>
 class CYapfDestinationTileOrStationRailT
 	: public CYapfDestinationRailBase
 {
diff --git a/src/yapf/yapf_node_rail.hpp b/src/yapf/yapf_node_rail.hpp
index 9745ff3..3906c14 100644
--- a/src/yapf/yapf_node_rail.hpp
+++ b/src/yapf/yapf_node_rail.hpp
@@ -39,6 +39,7 @@ enum EndSegmentReason {
 	ESR_DEPOT,             ///< stop in the depot (could be a target next time)
 	ESR_WAYPOINT,          ///< waypoint encountered (could be a target next time)
 	ESR_STATION,           ///< station encountered (could be a target next time)
+	ESR_SAFE_TILE,         ///< safe waiting position found (could be a target)
 
 	/* The following reasons are used only internally by PfCalcCost().
 	*   They should not be found in the cached segment. */
@@ -62,6 +63,7 @@ enum EndSegmentReasonBits {
 	ESRB_DEPOT             = 1 << ESR_DEPOT,
 	ESRB_WAYPOINT          = 1 << ESR_WAYPOINT,
 	ESRB_STATION           = 1 << ESR_STATION,
+	ESRB_SAFE_TILE         = 1 << ESR_SAFE_TILE,
 
 	ESRB_PATH_TOO_LONG     = 1 << ESR_PATH_TOO_LONG,
 	ESRB_FIRST_TWO_WAY_RED = 1 << ESR_FIRST_TWO_WAY_RED,
@@ -71,10 +73,10 @@ enum EndSegmentReasonBits {
 	/* Additional (composite) values. */
 
 	/* What reasons mean that the target can be found and needs to be detected. */
-	ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION,
+	ESRB_POSSIBLE_TARGET = ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
 
 	/* What reasons can be stored back into cached segment. */
-	ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION,
+	ESRB_CACHED_MASK = ESRB_DEAD_END | ESRB_RAIL_TYPE | ESRB_INFINITE_LOOP | ESRB_SEGMENT_TOO_LONG | ESRB_CHOICE_FOLLOWS | ESRB_DEPOT | ESRB_WAYPOINT | ESRB_STATION | ESRB_SAFE_TILE,
 
 	/* Reasons to abort pathfinding in this direction. */
 	ESRB_ABORT_PF_MASK = ESRB_DEAD_END | ESRB_PATH_TOO_LONG | ESRB_INFINITE_LOOP | ESRB_FIRST_TWO_WAY_RED,
@@ -177,6 +179,37 @@ struct CYapfRailNodeT
 	FORCEINLINE Trackdir GetLastTrackdir() const {assert(m_segment != NULL); return m_segment->m_last_td;}
 	FORCEINLINE void SetLastTileTrackdir(TileIndex tile, Trackdir td) {assert(m_segment != NULL); m_segment->m_last_tile = tile; m_segment->m_last_td = td;}
 
+	template <class Tbase, class Tfunc>
+	bool IterateTiles(const Vehicle *v, Tbase &obj, bool (Tfunc::*func)(TileIndex, Trackdir)) const
+	{
+		typename Tbase::TrackFollower ft(v);
+		TileIndex cur = base::GetTile();
+		Trackdir  cur_td = base::GetTrackdir();
+
+		while (cur != GetLastTile() || cur_td != GetLastTrackdir()) {
+			if (!((obj.*func)(cur, cur_td))) return false;
+
+			ft.Follow(cur, cur_td);
+			cur = ft.m_new_tile;
+			assert(KillFirstBit(ft.m_new_td_bits) == TRACKDIR_BIT_NONE);
+			cur_td = FindFirstTrackdir(ft.m_new_td_bits);
+
+			/* Did we skip tiles because of a station? */
+			if (ft.m_is_station && ft.m_tiles_skipped > 0) {
+				TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(cur_td));
+				TileIndex     tile = TILE_ADD(cur, -diff * ft.m_tiles_skipped);
+
+				/* Call func for all tiles in between. */
+				for (int i = 0; i < ft.m_tiles_skipped; ++i) {
+					if (!(obj.*func)(tile, cur_td)) return false;
+					tile = TILE_ADD(tile, diff);
+				}
+			}
+		}
+
+		return (obj.*func)(cur, cur_td);
+	}
+
 	void Dump(DumpTarget &dmp) const
 	{
 		base::Dump(dmp);
diff --git a/src/yapf/yapf_rail.cpp b/src/yapf/yapf_rail.cpp
index 7d7d4ee..bfb1360 100644
--- a/src/yapf/yapf_rail.cpp
+++ b/src/yapf/yapf_rail.cpp
@@ -9,6 +9,7 @@
 #include "yapf_costrail.hpp"
 #include "yapf_destrail.hpp"
 #include "../vehicle_func.h"
+#include "../pbs.h"
 
 #define DEBUG_YAPF_CACHE 0
 
@@ -16,7 +17,113 @@ int _total_pf_time_us = 0;
 
 
 
+template <class Types>
+class CYapfReserveTrack
+{
+public:
+	typedef typename Types::Tpf Tpf;                     ///< the pathfinder class (derived from THIS class)
+	typedef typename Types::TrackFollower TrackFollower;
+	typedef typename Types::NodeList::Titem Node;        ///< this will be our node type
+
+protected:
+	/// to access inherited pathfinder
+	FORCEINLINE Tpf& Yapf() {return *static_cast<Tpf*>(this);}
+
+private:
+	TileIndex m_res_dest;         ///< The reservation target tile
+	Trackdir  m_res_dest_td;      ///< The reservation target trackdir
+	Node      *m_res_node;        ///< The reservation target node
+	TileIndex m_res_fail_tile;    ///< The tile where the reservation failed
+
+	bool FindSafePositionProc(TileIndex tile, Trackdir td)
+	{
+		if (IsSafeWaitingPosition(Yapf().GetVehicle(), tile, td, true, !TrackFollower::Allow90degTurns())) {
+			m_res_dest = tile;
+			m_res_dest_td = td;
+			return false;   // Stop iterating segment
+		}
+		return true;
+	}
+
+	bool ReserveSingleTrack(TileIndex tile, Trackdir td)
+	{
+		if (!TryReserveRailTrack(tile, TrackdirToTrack(td))) {
+			/* Tile couldn't be reserved, undo. */
+			m_res_fail_tile = tile;
+			return false;
+		}
+		/* YAPF can sometimes skip parts of a station, so make sure we
+		 * always reserve the whole platform. */
+		if (IsRailwayStationTile(tile)) SetRailwayStationPlatformReservation(tile, TrackdirToExitdir(ReverseTrackdir(td)), true);
+		return tile != m_res_dest;
+	}
+
+	bool UnreserveSingleTrack(TileIndex tile, Trackdir td)
+	{
+		if (tile != m_res_fail_tile) UnreserveRailTrack(tile, TrackdirToTrack(td));
+		return tile != m_res_dest && tile != m_res_fail_tile;
+	}
+
+public:
+	/** Set the target to where the reservation should be extended. */
+	inline void SetReservationTarget(Node *node, TileIndex tile, Trackdir td)
+	{
+		m_res_node = node;
+		m_res_dest = tile;
+		m_res_dest_td = td;
+	}
+
+	/** Check the node for a possible reservation target. */
+	inline void FindSafePositionOnNode(Node *node)
+	{
+		assert(node->m_parent != NULL);
+
+		/* We will never pass more than two signals, no need to check for a safe tile. */
+		if (node->m_parent->m_num_signals_passed >= 2) return;
+
+		if (!node->IterateTiles(Yapf().GetVehicle(), *this, &CYapfReserveTrack<Types>::FindSafePositionProc)) {
+			m_res_node = node;
+		}
+	}
+
+	/** Try to reserve the path till the reservation target. */
+	bool TryReservePath(PBSTileInfo *target)
+	{
+		m_res_fail_tile = INVALID_TILE;
 
+		if (target != NULL) {
+			target->tile = m_res_dest;
+			target->trackdir = m_res_dest_td;
+			target->okay = false;
+		}
+
+		/* Don't bother if the target is reserved. */
+		if (!IsWaitingPositionFree(Yapf().GetVehicle(), m_res_dest, m_res_dest_td)) return false;
+
+		for (Node *node = m_res_node; node->m_parent != NULL; node = node->m_parent) {
+			node->IterateTiles(Yapf().GetVehicle(), *this, &CYapfReserveTrack<Types>::ReserveSingleTrack);
+			if (m_res_fail_tile != INVALID_TILE) {
+				/* Reservation failed, undo. */
+				Node *fail_node = m_res_node;
+				TileIndex stop_tile = m_res_fail_tile;
+				do {
+					/* If this is the node that failed, stop at the failed tile. */
+					m_res_fail_tile = fail_node == node ? stop_tile : INVALID_TILE;
+					fail_node->IterateTiles(Yapf().GetVehicle(), *this, &CYapfReserveTrack<Types>::UnreserveSingleTrack);
+				} while (fail_node != node && (fail_node = fail_node->m_parent) != NULL);
+
+				return false;
+			}
+		}
+
+		if (target != NULL) target->okay = true;
+
+		if (Yapf().CanUseGlobalCache(*m_res_node))
+			YapfNotifyTrackLayoutChange(INVALID_TILE, INVALID_TRACK);
+
+		return true;
+	}
+};
 
 template <class Types>
 class CYapfFollowAnyDepotRailT
@@ -95,7 +202,89 @@ public:
 };
 
 template <class Types>
-class CYapfFollowRailT
+class CYapfFollowAnySafeTileRailT : protected CYapfReserveTrack<Types>
+{
+public:
+	typedef typename Types::Tpf Tpf;                     ///< the pathfinder class (derived from THIS class)
+	typedef typename Types::TrackFollower TrackFollower;
+	typedef typename Types::NodeList::Titem Node;        ///< this will be our node type
+	typedef typename Node::Key Key;                      ///< key to hash tables
+
+protected:
+	/// to access inherited path finder
+	FORCEINLINE Tpf& Yapf() {return *static_cast<Tpf*>(this);}
+
+public:
+	/** Called by YAPF to move from the given node to the next tile. For each
+	 *  reachable trackdir on the new tile creates new node, initializes it
+	 *  and adds it to the open list by calling Yapf().AddNewNode(n) */
+	inline void PfFollowNode(Node& old_node)
+	{
+		TrackFollower F(Yapf().GetVehicle(), Yapf().GetCompatibleRailTypes());
+		if (F.Follow(old_node.GetLastTile(), old_node.GetLastTrackdir()))
+			Yapf().AddMultipleNodes(&old_node, F);
+	}
+
+	/** Return debug report character to identify the transportation type */
+	FORCEINLINE char TransportTypeChar() const {return 't';}
+
+	static bool stFindNearestSafeTile(const Vehicle *v, TileIndex t1, Trackdir td, bool override_railtype)
+	{
+		/* Create pathfinder instance */
+		Tpf pf1;
+#if !DEBUG_YAPF_CACHE
+		bool result1 = pf1.FindNearestSafeTile(v, t1, td, override_railtype, false);
+
+#else
+		bool result2 = pf1.FindNearestSafeTile(v, t1, td, override_railtype, true);
+		Tpf pf2;
+		pf2.DisableCache(true);
+		bool result1 = pf2.FindNearestSafeTile(v, t1, td, override_railtype, false);
+		if (result1 != result2) {
+			DEBUG(yapf, 0, "CACHE ERROR: FindSafeTile() = [%s, %s]", result2 ? "T" : "F", result1 ? "T" : "F");
+			DumpTarget dmp1, dmp2;
+			pf1.DumpBase(dmp1);
+			pf2.DumpBase(dmp2);
+			FILE *f1 = fopen("C:\\yapf1.txt", "wt");
+			FILE *f2 = fopen("C:\\yapf2.txt", "wt");
+			fwrite(dmp1.m_out.Data(), 1, dmp1.m_out.Size(), f1);
+			fwrite(dmp2.m_out.Data(), 1, dmp2.m_out.Size(), f2);
+			fclose(f1);
+			fclose(f2);
+		}
+#endif
+
+		return result1;
+	}
+
+	bool FindNearestSafeTile(const Vehicle *v, TileIndex t1, Trackdir td, bool override_railtype, bool dont_reserve)
+	{
+		/* Set origin and destination. */
+		Yapf().SetOrigin(t1, td);
+		Yapf().SetDestination(v, override_railtype);
+
+		bool bFound = Yapf().FindPath(v);
+		if (!bFound) return false;
+
+		/* Found a destination, set as reservation target. */
+		Node *pNode = Yapf().GetBestNode();
+		this->SetReservationTarget(pNode, pNode->GetLastTile(), pNode->GetLastTrackdir());
+
+		/* Walk through the path back to the origin. */
+		Node* pPrev = NULL;
+		while (pNode->m_parent != NULL) {
+			pPrev = pNode;
+			pNode = pNode->m_parent;
+
+			this->FindSafePositionOnNode(pPrev);
+		}
+
+		return dont_reserve || this->TryReservePath(NULL);
+	}
+};
+
+template <class Types>
+class CYapfFollowRailT : protected CYapfReserveTrack<Types>
 {
 public:
 	typedef typename Types::Tpf Tpf;                     ///< the pathfinder class (derived from THIS class)
@@ -121,16 +310,18 @@ public:
 	/// return debug report character to identify the transportation type
 	FORCEINLINE char TransportTypeChar() const {return 't';}
 
-	static Trackdir stChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found)
+	static Trackdir stChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target)
 	{
 		// create pathfinder instance
 		Tpf pf1;
-		Trackdir result1 = pf1.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found);
+#if !DEBUG_YAPF_CACHE
+		Trackdir result1 = pf1.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found, reserve_track, target);
 
-#if DEBUG_YAPF_CACHE
+#else
+		Trackdir result1 = pf1.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found, false, NULL);
 		Tpf pf2;
 		pf2.DisableCache(true);
-		Trackdir result2 = pf2.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found);
+		Trackdir result2 = pf2.ChooseRailTrack(v, tile, enterdir, tracks, path_not_found, reserve_track, target);
 		if (result1 != result2) {
 			DEBUG(yapf, 0, "CACHE ERROR: ChooseRailTrack() = [%d, %d]", result1, result2);
 			DumpTarget dmp1, dmp2;
@@ -148,10 +339,13 @@ public:
 		return result1;
 	}
 
-	FORCEINLINE Trackdir ChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found)
+	FORCEINLINE Trackdir ChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target)
 	{
+		if (target != NULL) target->tile = INVALID_TILE;
+
 		// set origin and destination nodes
-		Yapf().SetOrigin(v->tile, GetVehicleTrackdir(v), INVALID_TILE, INVALID_TRACKDIR, 1, true);
+		PBSTileInfo origin = FollowTrainReservation(v);
+		Yapf().SetOrigin(origin.tile, origin.trackdir, INVALID_TILE, INVALID_TRACKDIR, 1, true);
 		Yapf().SetDestination(v);
 
 		// find the best path
@@ -166,17 +360,23 @@ public:
 		Trackdir next_trackdir = INVALID_TRACKDIR;
 		Node *pNode = Yapf().GetBestNode();
 		if (pNode != NULL) {
+			// reserve till end of path
+			this->SetReservationTarget(pNode, pNode->GetLastTile(), pNode->GetLastTrackdir());
+
 			// path was found or at least suggested
 			// walk through the path back to the origin
 			Node* pPrev = NULL;
 			while (pNode->m_parent != NULL) {
 				pPrev = pNode;
 				pNode = pNode->m_parent;
+
+				this->FindSafePositionOnNode(pPrev);
 			}
 			// return trackdir from the best origin node (one of start nodes)
 			Node& best_next_node = *pPrev;
-			assert(best_next_node.GetTile() == tile);
 			next_trackdir = best_next_node.GetTrackdir();
+
+			if (reserve_track && path_found) this->TryReservePath(target);
 		}
 		return next_trackdir;
 	}
@@ -246,11 +446,14 @@ struct CYapfRail2         : CYapfT<CYapfRail_TypesT<CYapfRail2        , CFollowT
 struct CYapfAnyDepotRail1 : CYapfT<CYapfRail_TypesT<CYapfAnyDepotRail1, CFollowTrackRail    , CRailNodeListTrackDir, CYapfDestinationAnyDepotRailT     , CYapfFollowAnyDepotRailT> > {};
 struct CYapfAnyDepotRail2 : CYapfT<CYapfRail_TypesT<CYapfAnyDepotRail2, CFollowTrackRailNo90, CRailNodeListTrackDir, CYapfDestinationAnyDepotRailT     , CYapfFollowAnyDepotRailT> > {};
 
+struct CYapfAnySafeTileRail1 : CYapfT<CYapfRail_TypesT<CYapfAnySafeTileRail1, CFollowTrackFreeRail    , CRailNodeListTrackDir, CYapfDestinationAnySafeTileRailT , CYapfFollowAnySafeTileRailT> > {};
+struct CYapfAnySafeTileRail2 : CYapfT<CYapfRail_TypesT<CYapfAnySafeTileRail2, CFollowTrackFreeRailNo90, CRailNodeListTrackDir, CYapfDestinationAnySafeTileRailT , CYapfFollowAnySafeTileRailT> > {};
 
-Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found)
+
+Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool *path_not_found, bool reserve_track, PBSTileInfo *target)
 {
 	// default is YAPF type 2
-	typedef Trackdir (*PfnChooseRailTrack)(const Vehicle*, TileIndex, DiagDirection, TrackBits, bool*);
+	typedef Trackdir (*PfnChooseRailTrack)(const Vehicle*, TileIndex, DiagDirection, TrackBits, bool*, bool, PBSTileInfo*);
 	PfnChooseRailTrack pfnChooseRailTrack = &CYapfRail1::stChooseRailTrack;
 
 	// check if non-default YAPF type needed
@@ -258,7 +461,7 @@ Trackdir YapfChooseRailTrack(const Vehicle *v, TileIndex tile, DiagDirection ent
 		pfnChooseRailTrack = &CYapfRail2::stChooseRailTrack; // Trackdir, forbid 90-deg
 	}
 
-	Trackdir td_ret = pfnChooseRailTrack(v, tile, enterdir, tracks, path_not_found);
+	Trackdir td_ret = pfnChooseRailTrack(v, tile, enterdir, tracks, path_not_found, reserve_track, target);
 
 	return td_ret;
 }
@@ -330,11 +533,8 @@ bool YapfFindNearestRailDepotTwoWay(const Vehicle *v, int max_distance, int reve
 
 	const Vehicle *last_veh = GetLastVehicleInChain(v);
 
-	TileIndex tile = v->tile;
+	PBSTileInfo origin = FollowTrainReservation(v);
 	TileIndex last_tile = last_veh->tile;
-
-	// their trackdirs
-	Trackdir td = GetVehicleTrackdir(v);
 	Trackdir td_rev = ReverseTrackdir(GetVehicleTrackdir(last_veh));
 
 	typedef bool (*PfnFindNearestDepotTwoWay)(const Vehicle*, TileIndex, Trackdir, TileIndex, Trackdir, int, int, TileIndex*, bool*);
@@ -345,10 +545,23 @@ bool YapfFindNearestRailDepotTwoWay(const Vehicle *v, int max_distance, int reve
 		pfnFindNearestDepotTwoWay = &CYapfAnyDepotRail2::stFindNearestDepotTwoWay; // Trackdir, forbid 90-deg
 	}
 
-	bool ret = pfnFindNearestDepotTwoWay(v, tile, td, last_tile, td_rev, max_distance, reverse_penalty, depot_tile, reversed);
+	bool ret = pfnFindNearestDepotTwoWay(v, origin.tile, origin.trackdir, last_tile, td_rev, max_distance, reverse_penalty, depot_tile, reversed);
 	return ret;
 }
 
+bool YapfRailFindNearestSafeTile(const Vehicle *v, TileIndex tile, Trackdir td, bool override_railtype)
+{
+	typedef bool (*PfnFindNearestSafeTile)(const Vehicle*, TileIndex, Trackdir, bool);
+	PfnFindNearestSafeTile pfnFindNearestSafeTile = CYapfAnySafeTileRail1::stFindNearestSafeTile;
+
+	/* check if non-default YAPF type needed */
+	if (_settings_game.pf.forbid_90_deg) {
+		pfnFindNearestSafeTile = &CYapfAnySafeTileRail2::stFindNearestSafeTile;
+	}
+
+	return pfnFindNearestSafeTile(v, tile, td, override_railtype);
+}
+
 /** if any track changes, this counter is incremented - that will invalidate segment cost cache */
 int CSegmentCostCacheBase::s_rail_change_counter = 0;
 
