changeset 2008:9e0f41852bf8 draft

(svn r2516) - Feature: [pbs] Implement path-based-signalling. This allows multiple trains within the same signal block, provided their paths dont intersect. For this the block must have all exit and entry signals be pbs signals. Place these by ctrl-clicking 4 times on a normal signal. - Feature: [pbs] Implement autoplacement of pbs blocks, when a block has an entry and an exit pbs signal, covert the entire block to pbs. Can be turned off in the patch settings. - Feature: [pbs] Allow showing of reserved status by making the tracks darker, when the pbs debug level is at least 1.
author hackykid <hackykid@openttd.org>
date Mon, 04 Jul 2005 14:58:55 +0000
parents a204394fecfe
children 6571069a6a78
files Makefile ai_pathfinder.c aystar.c aystar.h data/nsignalsw.grf data/signalsw.grf debug.c debug.h docs/landscape.html lang/english.txt npf.c npf.h pbs.c pbs.h rail.c rail.h rail_cmd.c road_cmd.c roadveh_cmd.c settings.c settings_gui.c ship_cmd.c station_cmd.c table/files.h table/sprites.h train_cmd.c tunnelbridge_cmd.c variables.h vehicle.c vehicle.h
diffstat 30 files changed, 1051 insertions(+), 77 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile
+++ b/Makefile
@@ -651,6 +651,7 @@
 C_SOURCES += order_gui.c
 C_SOURCES += openttd.c
 C_SOURCES += pathfind.c
+C_SOURCES += pbs.c
 C_SOURCES += player_gui.c
 C_SOURCES += players.c
 C_SOURCES += pool.c
--- a/ai_pathfinder.c
+++ b/ai_pathfinder.c
@@ -97,6 +97,8 @@
 	result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode;
 	result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours;
 
+	result->BeforeExit = NULL;
+
 	result->free = AyStar_AiPathFinder_Free;
 
 	// Set some information
--- a/aystar.c
+++ b/aystar.c
@@ -230,6 +230,10 @@
 	else if (r == AYSTAR_LIMIT_REACHED)
 		printf("[AyStar] Exceeded search_nodes, no path found\n");
 #endif
+
+	if (aystar->BeforeExit != NULL)
+		aystar->BeforeExit(aystar);
+
 	if (r != AYSTAR_STILL_BUSY)
 		/* We're done, clean up */
 		aystar->clear(aystar);
--- a/aystar.h
+++ b/aystar.h
@@ -96,6 +96,11 @@
  */
 typedef void AyStar_FoundEndNode(AyStar *aystar, OpenListNode *current);
 
+/*
+ * Is called when aystar ends it pathfinding, but before cleanup.
+ */
+typedef void AyStar_BeforeExit(AyStar *aystar);
+
 // For internal use, see aystar.c
 typedef void AyStar_AddStartNode(AyStar *aystar, AyStarNode* start_node, uint g);
 typedef int AyStar_Main(AyStar *aystar);
@@ -115,6 +120,7 @@
 	AyStar_GetNeighbours* GetNeighbours;
 	AyStar_EndNodeCheck* EndNodeCheck;
 	AyStar_FoundEndNode* FoundEndNode;
+	AyStar_BeforeExit* BeforeExit;
 
 	/* These are completely untouched by AyStar, they can be accesed by
 	 * the application specific routines to input and output data.
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..03dee78637caea405c4ddc47652b30bca757a758
GIT binary patch
literal 50012
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
Uy_dx9ph$Z!ogI?4nE%rM11YNy)&Kwi
deleted file mode 100644
index ca78876d09756ac8fb2dc87f631df0e59a1b6044..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/debug.c
+++ b/debug.c
@@ -14,6 +14,7 @@
 int _debug_net_level;
 int _debug_spritecache_level;
 int _debug_oldloader_level;
+int _debug_pbs_level;
 int _debug_npf_level;
 
 
@@ -44,6 +45,7 @@
 	DEBUG_LEVEL(net),
 	DEBUG_LEVEL(spritecache),
 	DEBUG_LEVEL(oldloader),
+	DEBUG_LEVEL(pbs),
 	DEBUG_LEVEL(npf)
 	};
 #undef DEBUG_LEVEL
--- a/debug.h
+++ b/debug.h
@@ -14,6 +14,7 @@
 	extern int _debug_net_level;
 	extern int _debug_spritecache_level;
 	extern int _debug_oldloader_level;
+	extern int _debug_pbs_level;
 	extern int _debug_npf_level;
 #endif
 
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -93,11 +93,12 @@
 <li>map2 bits 7..4: bit clear = signal shows red; same bits as in map3_lo</li>
 <li>OpenTTD bits in map3_hi:
 <table>
-<tr><td nowrap valign=top>bits 1..0: </td><td align=left>type of signal:</td></tr>
-<tr><td nowrap valign=top><tt>00</tt>: </td><td align=left>normal signals</td></tr>
-<tr><td nowrap valign=top><tt>01</tt>: </td><td align=left>pre-signals</td></tr>
-<tr><td nowrap valign=top><tt>10</tt>: </td><td align=left>exit-signals</td></tr>
-<tr><td nowrap valign=top><tt>11</tt>: </td><td align=left>combo-signals</td></tr>
+<tr><td nowrap valign=top>bits 2..0: </td><td align=left>type of signal:</td></tr>
+<tr><td nowrap valign=top><tt>000</tt>: </td><td align=left>normal signals</td></tr>
+<tr><td nowrap valign=top><tt>001</tt>: </td><td align=left>pre-signals</td></tr>
+<tr><td nowrap valign=top><tt>010</tt>: </td><td align=left>exit-signals</td></tr>
+<tr><td nowrap valign=top><tt>011</tt>: </td><td align=left>combo-signals</td></tr>
+<tr><td nowrap valign=top><tt>100</tt>: </td><td align=left>PBS signals</td></tr>
 <tr><td nowrap valign=top>bit 3: </td><td align=left>set = semaphore signals, clear = light signals</td></tr>
 </table></li>
 </ul></li>
@@ -120,7 +121,11 @@
 <tr><td nowrap valign=top><tt>C</tt>&nbsp; </td><td align=left>on snow or desert</td></tr>
 </table></li>
 <li>map3_lo bits 0..3 = <a name="TrackType">track type</a>: <tt>0</tt> - conventional railway, <tt>1</tt> - monorail, <tt>2</tt> - maglev
-</li>
+<li>map3_lo bits 4..7 = Pbs reserved status:
+<table>
+<tr><td nowrap valign=top><tt>bits 4..6</tt>&nbsp; </td><td align=left>'Track'number of reserved track + 1, if this is zero it means nothing is reserved on this tile</td></tr>
+<tr><td nowrap valign=top><tt>bit 7</tt>&nbsp; </td><td align=left>If this is set, then the opposite track ('Track'number^1) is also reserved</td></tr>
+</table></li>
 </ul>
 map5 bits 7 and 6 set: railway depot / checkpoints
 <ul>
@@ -133,6 +138,7 @@
 <li>map_owner: <a href="#OwnershipInfo">owner</a> of the depot / checkpoint</li>
 <li>map3_lo bits 0..3 = <a href="#TrackType">track type</a></li>
 <li>map3_lo bit 4 = use custom sprite (valid only for the checkpoint)</li>
+<li>map3_lo bit 6 = track on this tile is reserved by pbs</li>
 <li>map3_hi = custom station id</li>
 </ul>
 </td></tr>
@@ -157,6 +163,7 @@
 <ul>
 <li>map5 bit 3: clear - road in the X direction, set - road in the Y direction (railway track always perpendicular)</li>
 <li>map5 bit 2: set if crossing lights are on</li>
+<li>map5 bit 0: set if rail track is reserved by pbs</li>
 <li>map_owner: <a href="#OwnershipInfo">owner</a> of the railway track</li>
 <li>map2: Index into the array of towns, 0 for non-town roads</li>
 <li>map3_lo bits 0..7: <a href="#OwnershipInfo">owner</a> of the road</li>
@@ -372,6 +379,7 @@
 <li>map2: index into the <a href="#_StationArray">array of stations</a></li>
 <li>map3_lo bits 0..3: <a href="#TrackType">track type</a> for railway stations, must be 0 for all the other stations</li>
 <li>map3_lo bit 4 = use custom sprite (valid only railway stations FOR NOW)</li>
+<li>map3lo bit 6 set = track is reserved by pbs (railway stations only)</li>
 <li>map3_hi = custom station id</li>
 </ul>
 </td></tr>
@@ -542,6 +550,8 @@
 <li>map_owner: <a href="#OwnershipInfo">owner</a> of the tunnel</li>
 <li>map3_lo bits 3..0 = <a href="#TrackType">track type</a> for railway tunnel, must be 0 for road tunnel</li>
 <li>map3_hi bit 7 set = on snow or desert</li>
+<li>map3hi bit 0 set = track with 'Track'number 0 is reserved by pbs</li>
+<li>map3hi bit 1 set = track with 'Track'number 1 is reserved by pbs</li>
 </ul>
 map5 bit 7 set: bridge
 <ul><li>
--- a/lang/english.txt
+++ b/lang/english.txt
@@ -1073,6 +1073,7 @@
 STR_CONFIG_PATCHES_SMOOTH_ECONOMY                               :{LTBLUE}Enable smooth economy (more, smaller changes)
 STR_CONFIG_PATCHES_ALLOW_SHARES                                 :{LTBLUE}Allow buying shares from other companies
 STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY                         :{LTBLUE}When dragging, place signals every: {ORANGE}{STRING} tile(s)
+STR_CONFIG_AUTO_PBS_PLACEMENT                                   :{LTBLUE}Allow automatic placement of pbs signals: {ORANGE}{STRING}
 STR_CONFIG_PATCHES_TOOLBAR_POS                                  :{LTBLUE}Position of main toolbar: {ORANGE}{STRING}
 STR_CONFIG_PATCHES_TOOLBAR_POS_LEFT                             :Left
 STR_CONFIG_PATCHES_TOOLBAR_POS_CENTER                           :Centre
--- a/npf.c
+++ b/npf.c
@@ -21,6 +21,57 @@
 	NPF_TILE_LENGTH, NPF_TILE_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH, NPF_STRAIGHT_LENGTH
 };
 
+/**
+ * Check if a rail track is the end of the line. Will also consider 1-way signals to be the end of a line.
+ * @param tile The tile on which the current track is.
+ * @param trackdir The (track)direction in which you want to look
+ */
+bool IsEndOfLine(TileIndex tile, Trackdir trackdir)
+{
+	byte exitdir = TrackdirToExitdir(trackdir);
+	TileIndex dst_tile;
+	uint32 ts;
+
+	// tunnel entrance?
+	if (IsTileType(tile, MP_TUNNELBRIDGE) && (_map5[tile] & 0xF0)==0 && (_map5[tile] & 3) == exitdir)
+		return false;
+
+	// depot
+	if (IsTileDepotType(tile, TRANSPORT_RAIL))
+		return false;
+
+	/* Calculate next tile */
+	dst_tile = tile + TileOffsByDir(exitdir);
+	// determine the track status on the next tile.
+	ts = GetTileTrackStatus(dst_tile, TRANSPORT_RAIL) & TrackdirReachesTrackdirs(trackdir);
+
+	// when none of the trackdir bits are set, we cant enter the new tile
+	if ( (ts & TRACKDIR_BIT_MASK) == 0)
+		return true;
+
+	{
+		byte src_type = GetTileRailType(tile, trackdir);
+		byte dst_type = GetTileRailType(dst_tile, TrackdirToExitdir(trackdir));
+		if (src_type != dst_type) {
+			return true;
+		}
+		if (GetTileOwner(tile) != GetTileOwner(dst_tile))
+			return true;
+
+		if (IsTileDepotType(dst_tile, TRANSPORT_RAIL) && (TrackdirToExitdir(trackdir) != ReverseDiagdir(GetDepotDirection(dst_tile, TRANSPORT_RAIL))))
+			return true;
+
+		/* Check for oneway signal against us */
+		if (IsTileType(dst_tile, MP_RAILWAY) && GetRailTileType(dst_tile) == RAIL_TYPE_SIGNALS) {
+			if (HasSignalOnTrackdir(dst_tile, ReverseTrackdir(FindFirstBit2x64(ts))) && !HasSignalOnTrackdir(dst_tile, FindFirstBit2x64(ts)))
+				// if one way signal not pointing towards us, stop going in this direction.
+				return true;
+		}
+
+		return false;
+	}
+};
+
 static uint NTPHash(uint key1, uint key2)
 {
 	/* This function uses the old hash, which is fixed on 10 bits (1024 buckets) */
@@ -76,6 +127,82 @@
 	return TileXY(x, y);
 };
 
+/* On PBS pathfinding runs, this is called before pathfinding ends (BeforeExit aystar callback), and will
+ * reserve the appropriate tracks, if needed. */
+void NPFReservePBSPath(AyStar *as)
+{
+	NPFFoundTargetData* ftd = (NPFFoundTargetData*)as->user_path;
+	bool eol_end = false;
+
+	if (ftd->best_trackdir == 0xFF)
+		return;
+
+	if (!NPFGetFlag(&ftd->node, NPF_FLAG_PBS_EXIT) && IsEndOfLine(ftd->node.tile, ftd->node.direction) && !NPFGetFlag(&ftd->node, NPF_FLAG_SEEN_SIGNAL)) {
+		/* The path ends in an end of line, we'll need to reserve a path.
+		 * We treat and end of line as a red exit signal */
+		eol_end = true;
+		NPFSetFlag(&ftd->node, NPF_FLAG_PBS_EXIT, true);
+		if (!NPFGetFlag(&ftd->node, NPF_FLAG_PBS_TARGET_SEEN))
+			NPFSetFlag(&ftd->node, NPF_FLAG_PBS_RED, true);
+	}
+
+	if (!NPFGetFlag(&ftd->node, NPF_FLAG_PBS_CHOICE)) {
+		/* there have been no choices to make on our path, we dont care if our end signal is red */
+		NPFSetFlag(&ftd->node, NPF_FLAG_PBS_RED, false);
+	}
+
+	if (NPFGetFlag(&ftd->node, NPF_FLAG_PBS_EXIT) && // we passed an exit signal
+		 !NPFGetFlag(&ftd->node, NPF_FLAG_PBS_BLOCKED) && // we didnt encounter reserver tracks
+		 ((as->user_data[NPF_PBS_MODE] != PBS_MODE_GREEN) || (!NPFGetFlag(&ftd->node, NPF_FLAG_PBS_RED))) ) { // our mode permits having a red exit signal, or the signal is green
+		PathNode parent;
+		PathNode *curr;
+		PathNode *prev;
+		TileIndex start = INVALID_TILE;
+		byte trackdir = 0;
+
+		parent.node = ftd->node;
+		parent.parent = &ftd->path;
+		curr = &parent;
+		prev = NULL;
+
+		do {
+			if (!NPFGetFlag(&curr->node, NPF_FLAG_PBS_EXIT) || eol_end) {
+				/* check for already reserved track on this path, if they clash with what we
+				   currently trying to reserve, we have a self-crossing path :-( */
+				if ((PBSTileUnavail(curr->node.tile) & (1 << curr->node.direction))
+				&& !(PBSTileReserved(curr->node.tile) & (1 << (curr->node.direction & 7)))
+				&& (start != INVALID_TILE)) {
+					/* It's actually quite bad if this happens, it means the pathfinder
+					 * found a path that is intersecting with itself, which is a very bad
+					 * thing in a pbs block. Also there is not much we can do about it at
+					 * this point....
+					 * BUT, you have to have a pretty fucked up junction layout for this to happen,
+					 * so we'll just stop this train, the user will eventually notice, so he can fix it.
+					 */
+					PBSClearPath(start, trackdir);
+					NPFSetFlag(&ftd->node, NPF_FLAG_PBS_BLOCKED, true);
+					DEBUG(pbs, 1) ("PBS: Self-crossing path!!!");
+					return;
+				};
+
+				PBSReserveTrack(curr->node.tile, (curr->node.direction & 7) );
+
+				/* we want to reserve the last tile (with the signal) on the path too */
+				if (prev != NULL && start == INVALID_TILE) {
+					PBSReserveTrack(prev->node.tile, (prev->node.direction & 7) );
+					start = prev->node.tile;
+					trackdir = ReverseTrackdir(prev->node.direction);
+				}
+			}
+
+			prev = curr;
+			curr = curr->parent;
+		} while (curr != NULL);
+	}
+
+}
+
+
 /* Calcs the heuristic to the target station or tile. For train stations, it
  * takes into account the direction of approach.
  */
@@ -98,15 +225,27 @@
 		/* Ships and trains can also go diagonal, so the minimum distance is shorter */
 		dist = DistanceTrack(from, to) * NPF_TILE_LENGTH;
 
-	if (dist < ftd->best_bird_dist) {
+	DEBUG(npf, 4)("Calculating H for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), dist);
+
+	/* for pbs runs, we ignore tiles inside the pbs block for the tracking
+	   of the 'closest' tile */
+	if ((as->user_data[NPF_PBS_MODE] != PBS_MODE_NONE)
+	&&  (!NPFGetFlag(current , NPF_FLAG_SEEN_SIGNAL))
+	&&  (!IsEndOfLine(current->tile, current->direction)))
+		return dist;
+
+	if ((dist < ftd->best_bird_dist) ||
+		/* for pbs runs, prefer tiles that pass a green exit signal to the pbs blocks */
+		((as->user_data[NPF_PBS_MODE] != PBS_MODE_NONE) && !NPFGetFlag(current, NPF_FLAG_PBS_RED) && NPFGetFlag(&ftd->node, NPF_FLAG_PBS_RED))
+) {
 		ftd->best_bird_dist = dist;
 		ftd->best_trackdir = current->user_data[NPF_TRACKDIR_CHOICE];
+		ftd->path = parent->path;
+		ftd->node = *current;
 	}
-	DEBUG(npf, 4)("Calculating H for: (%d, %d). Result: %d", TileX(current->tile), TileY(current->tile), dist);
 	return dist;
 }
 
-
 /* Fills AyStarNode.user_data[NPF_TRACKDIRCHOICE] with the chosen direction to
  * get here, either getting it from the current choice or from the parent's
  * choice */
@@ -301,6 +440,11 @@
 
 	/* Determine extra costs */
 
+	/* Check for reserved tracks (PBS) */
+	if ((as->user_data[NPF_PBS_MODE] != PBS_MODE_NONE) && !(NPFGetFlag(current, NPF_FLAG_PBS_EXIT)) && !(NPFGetFlag(current, NPF_FLAG_PBS_BLOCKED)) && (PBSTileUnavail(tile) & (1<<trackdir))) {
+		NPFSetFlag(current, NPF_FLAG_PBS_BLOCKED, true);
+	};
+
 	/* Check for signals */
 	if (IsTileType(tile, MP_RAILWAY) && HasSignalOnTrackdir(tile, trackdir)) {
 		/* Ordinary track with signals */
@@ -317,6 +461,10 @@
 					cost += _patches.npf_rail_firstred_exit_penalty;
 				else
 					cost += _patches.npf_rail_firstred_penalty;
+
+				/* for pbs runs, store the fact that the exit signal to the pbs block was red */
+				if (!(NPFGetFlag(current, NPF_FLAG_PBS_EXIT)) && !(NPFGetFlag(current, NPF_FLAG_PBS_RED)) && NPFGetFlag(current, NPF_FLAG_PBS_CHOICE))
+					NPFSetFlag(current, NPF_FLAG_PBS_RED, true);
 			}
 			/* Record the state of this signal */
 			NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, true);
@@ -324,6 +472,15 @@
 			/* Record the state of this signal */
 			NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
 		}
+
+		if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL) && NPFGetFlag(current, NPF_FLAG_PBS_BLOCKED)) {
+			/* penalise a path through the pbs block if it crosses reserved tracks */
+			cost += 1000;
+		}
+		if ((PBSIsPbsSignal(tile, trackdir)) && !NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
+			/* we've encountered an exit signal to the pbs block */
+			NPFSetFlag(current, NPF_FLAG_PBS_EXIT, true);
+		}
 		NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
 	}
 
@@ -344,12 +501,27 @@
 	//TODO, with realistic acceleration, also the amount of straight track between
 	//      curves should be taken into account, as this affects the speed limit.
 
-	/* Check for reverse in depot */
-	if (IsTileDepotType(tile, TRANSPORT_RAIL) && !as->EndNodeCheck(as, &new_node)==AYSTAR_FOUND_END_NODE)
+
+	/* Check for depots */
+	if (IsTileDepotType(tile, TRANSPORT_RAIL)) {
 		/* Penalise any depot tile that is not the last tile in the path. This
 		 * _should_ penalise every occurence of reversing in a depot (and only
 		 * that) */
-		cost += _patches.npf_rail_depot_reverse_penalty;
+		if (as->EndNodeCheck(as, &new_node) != AYSTAR_FOUND_END_NODE)
+			cost += _patches.npf_rail_depot_reverse_penalty;
+
+		/* Do we treat this depot as a pbs signal? */
+		if (!NPFGetFlag(current, NPF_FLAG_SEEN_SIGNAL)) {
+			if (NPFGetFlag(current, NPF_FLAG_PBS_BLOCKED)) {
+				cost += 1000;
+			}
+			if (PBSIsPbsDepot(tile)) {
+				NPFSetFlag(current, NPF_FLAG_PBS_EXIT, true);
+				NPFSetFlag(current, NPF_FLAG_SEEN_SIGNAL, true);
+			}
+		}
+		NPFSetFlag(current, NPF_FLAG_LAST_SIGNAL_RED, false);
+	}
 
 	/* Check for occupied track */
 	//TODO
@@ -379,12 +551,22 @@
 	AyStarNode *node = &current->path.node;
 	TileIndex tile = node->tile;
 
+	if (tile == 0x4611c) {
+		tile++;
+		tile--;
+	}
+
 	/* If GetNeighbours said we could get here, we assume the station type
 	 * is correct */
 	if (
 		(fstd->station_index == -1 && tile == fstd->dest_coords) || /* We've found the tile, or */
-		(IsTileType(tile, MP_STATION) && _map2[tile] == fstd->station_index) /* the station */
+		(IsTileType(tile, MP_STATION) && _map2[tile] == fstd->station_index) || /* the station */
+		(NPFGetFlag(node, NPF_FLAG_PBS_TARGET_SEEN)) /* or, we've passed it already (for pbs) */
 	) {
+		NPFSetFlag(&current->path.node, NPF_FLAG_PBS_TARGET_SEEN, true);
+		/* for pbs runs, only accept we've found the target if we've also found a way out of the block */
+		if ((as->user_data[NPF_PBS_MODE] != PBS_MODE_NONE) && !NPFGetFlag(node, NPF_FLAG_SEEN_SIGNAL) && !IsEndOfLine(node->tile, node->direction))
+			return AYSTAR_DONE;
 		return AYSTAR_FOUND_END_NODE;
 	} else {
 		return AYSTAR_DONE;
@@ -402,6 +584,7 @@
 	ftd->best_path_dist = current->g;
 	ftd->best_bird_dist = 0;
 	ftd->node = current->path.node;
+	ftd->path = current->path;
 }
 
 /**
@@ -478,6 +661,8 @@
 	aystar->num_neighbours = 0;
 	DEBUG(npf, 4)("Expanding: (%d, %d, %d) [%d]", TileX(src_tile), TileY(src_tile), src_trackdir, src_tile);
 
+	aystar->EndNodeCheck(aystar, current);
+
 	/* Find dest tile */
 	if (IsTileType(src_tile, MP_TUNNELBRIDGE) && (_map5[src_tile] & 0xF0)==0 && (DiagDirection)(_map5[src_tile] & 3) == src_exitdir) {
 		/* This is a tunnel. We know this tunnel is our type,
@@ -555,13 +740,23 @@
 	} else {
 		ts = GetTileTrackStatus(dst_tile, type);
 	}
-	trackdirbits = ts & 0x3F3F; /* Filter out signal status and the unused bits */
+	trackdirbits = ts & TRACKDIR_BIT_MASK; /* Filter out signal status and the unused bits */
 
 	DEBUG(npf, 4)("Next node: (%d, %d) [%d], possible trackdirs: %#x", TileX(dst_tile), TileY(dst_tile), dst_tile, trackdirbits);
 	/* Select only trackdirs we can reach from our current trackdir */
 	trackdirbits &= TrackdirReachesTrackdirs(src_trackdir);
 	if (_patches.forbid_90_deg && (type == TRANSPORT_RAIL || type == TRANSPORT_WATER)) /* Filter out trackdirs that would make 90 deg turns for trains */
-		trackdirbits &= ~TrackdirCrossesTrackdirs(src_trackdir);
+
+	trackdirbits &= ~TrackdirCrossesTrackdirs(src_trackdir);
+
+	if (KillFirstBit2x64(trackdirbits) != 0)
+		NPFSetFlag(&current->path.node, NPF_FLAG_PBS_CHOICE, true);
+
+	/* When looking for 'any' route, ie when already inside a pbs block, discard all tracks that would cross
+	   other reserved tracks, so we *always* will find a valid route if there is one */
+	if (!(NPFGetFlag(&current->path.node, NPF_FLAG_PBS_EXIT)) && (aystar->user_data[NPF_PBS_MODE] == PBS_MODE_ANY))
+		trackdirbits &= ~PBSTileUnavail(dst_tile);
+
 	DEBUG(npf,6)("After filtering: (%d, %d), possible trackdirs: %#x", TileX(dst_tile), TileY(dst_tile), trackdirbits);
 
 	i = 0;
@@ -602,7 +797,7 @@
  * multiple targets that are spread around, we should perform a breadth first
  * search by specifiying CalcZero as our heuristic.
  */
-static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start2, NPFFindStationOrTileData* target, AyStar_EndNodeCheck target_proc, AyStar_CalculateH heuristic_proc, TransportType type, Owner owner, RailType railtype, uint reverse_penalty)
+static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start2, NPFFindStationOrTileData* target, AyStar_EndNodeCheck target_proc, AyStar_CalculateH heuristic_proc, TransportType type, Owner owner, RailType railtype, uint reverse_penalty, byte pbs_mode)
 {
 	int r;
 	NPFFoundTargetData result;
@@ -621,6 +816,11 @@
 	else
 		assert(0);
 
+	if (pbs_mode != PBS_MODE_NONE)
+		_npf_aystar.BeforeExit = NPFReservePBSPath;
+	else
+		_npf_aystar.BeforeExit = NULL;
+
 	/* Initialize Start Node(s) */
 	start1->user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 	start1->user_data[NPF_NODE_FLAGS] = 0;
@@ -645,6 +845,7 @@
 	_npf_aystar.user_data[NPF_TYPE] = type;
 	_npf_aystar.user_data[NPF_OWNER] = owner;
 	_npf_aystar.user_data[NPF_RAILTYPE] = railtype;
+	_npf_aystar.user_data[NPF_PBS_MODE] = pbs_mode;
 
 	/* GO! */
 	r = AyStarMain_Main(&_npf_aystar);
@@ -662,7 +863,7 @@
 	return result;
 }
 
-NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype)
+NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype, byte pbs_mode)
 {
 	AyStarNode start1;
 	AyStarNode start2;
@@ -676,12 +877,12 @@
 	start2.direction = trackdir2;
 	start2.user_data[NPF_TRACKDIR_CHOICE] = INVALID_TRACKDIR;
 
-	return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), target, NPFFindStationOrTile, NPFCalcStationOrTileHeuristic, type, owner, railtype, 0);
+	return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), target, NPFFindStationOrTile, NPFCalcStationOrTileHeuristic, type, owner, railtype, 0, pbs_mode);
 }
 
-NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype)
+NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype, byte pbs_mode)
 {
-	return NPFRouteToStationOrTileTwoWay(tile, trackdir, INVALID_TILE, 0, target, type, owner, railtype);
+	return NPFRouteToStationOrTileTwoWay(tile, trackdir, INVALID_TILE, 0, target, type, owner, railtype, pbs_mode);
 }
 
 NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, TransportType type, Owner owner, RailType railtype, uint reverse_penalty)
@@ -700,7 +901,7 @@
 
 	/* perform a breadth first search. Target is NULL,
 	 * since we are just looking for any depot...*/
-	return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), NULL, NPFFindDepot, NPFCalcZero, type, owner, railtype, reverse_penalty);
+	return NPFRouteInternal(&start1, (IsValidTile(tile2) ? &start2 : NULL), NULL, NPFFindDepot, NPFCalcZero, type, owner, railtype, reverse_penalty, PBS_MODE_NONE);
 }
 
 NPFFoundTargetData NPFRouteToDepotBreadthFirst(TileIndex tile, Trackdir trackdir, TransportType type, Owner owner, RailType railtype)
@@ -753,6 +954,8 @@
 	else
 		assert(0);
 
+	_npf_aystar.BeforeExit = NULL;
+
 	/* Initialize target */
 	target.station_index = -1; /* We will initialize dest_coords inside the loop below */
 	_npf_aystar.user_target = &target;
@@ -760,6 +963,7 @@
 	/* Initialize user_data */
 	_npf_aystar.user_data[NPF_TYPE] = type;
 	_npf_aystar.user_data[NPF_OWNER] = owner;
+	_npf_aystar.user_data[NPF_PBS_MODE] = PBS_MODE_NONE;
 
 	/* Initialize Start Node */
 	start.tile = tile;
--- a/npf.h
+++ b/npf.h
@@ -4,6 +4,7 @@
 #include "openttd.h"
 #include "aystar.h"
 #include "vehicle.h"
+#include "pbs.h"
 #include "tile.h"
 #include "rail.h"
 
@@ -36,16 +37,23 @@
 	NPF_TYPE = 0, /* Contains a TransportTypes value */
 	NPF_OWNER, /* Contains an Owner value */
 	NPF_RAILTYPE, /* Contains the RailType value of the engine when NPF_TYPE == TRANSPORT_RAIL. Unused otherwise. */
+	NPF_PBS_MODE, /* Contains the pbs mode, see pbs.h */
 };
 
 enum { /* Indices into AyStarNode.userdata[] */
 	NPF_TRACKDIR_CHOICE = 0, /* The trackdir chosen to get here */
 	NPF_NODE_FLAGS,
 };
+
 typedef enum { /* Flags for AyStarNode.userdata[NPF_NODE_FLAGS]. Use NPFGetBit() and NPFGetBit() to use them. */
 	NPF_FLAG_SEEN_SIGNAL, /* Used to mark that a signal was seen on the way, for 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_PBS_EXIT, /* Used to mark tracks inside a pbs block, for rail only, for the end node, this is set when the path found goes through a pbs block */
+	NPF_FLAG_PBS_BLOCKED, /* Used to mark that this path crosses another pbs path */
+	NPF_FLAG_PBS_RED, /* Used to mark that this path goes through a red exit-pbs signal */
+	NPF_FLAG_PBS_CHOICE, /* Used to mark that the train has had a choice on this path */
+	NPF_FLAG_PBS_TARGET_SEEN, /* Used to mark that a target tile has been passed on this path */
 } NPFNodeFlag;
 
 typedef struct NPFFoundTargetData { /* Meant to be stored in AyStar.userpath */
@@ -53,6 +61,7 @@
 	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 */
+	PathNode path;
 } NPFFoundTargetData;
 
 /* These functions below are _not_ re-entrant, in favor of speed! */
@@ -60,11 +69,12 @@
 /* Will search from the given tile and direction, for a route to the given
  * station for the given transport type. See the declaration of
  * NPFFoundTargetData above for the meaning of the result. */
-NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype);
+NPFFoundTargetData NPFRouteToStationOrTile(TileIndex tile, Trackdir trackdir, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype, byte pbs_mode);
+
 /* Will search as above, but with two start nodes, the second being the
  * reverse. Look at the NPF_FLAG_REVERSE flag in the result node to see which
  * direction was taken (NPFGetBit(result.node, NPF_FLAG_REVERSE)) */
-NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype);
+NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir trackdir1, TileIndex tile2, Trackdir trackdir2, NPFFindStationOrTileData* target, TransportType type, Owner owner, RailType railtype, byte pbs_mode);
 
 /* Will search a route to the closest depot. */
 
new file mode 100644
--- /dev/null
+++ b/pbs.c
@@ -0,0 +1,291 @@
+#include "stdafx.h"
+#include "openttd.h"
+#include "pbs.h"
+#include "functions.h"
+#include "debug.h"
+#include "map.h"
+#include "tile.h"
+#include "npf.h"
+#include "pathfind.h"
+#include "depot.h"
+
+/** @file pbs.c Path-Based-Signalling implementation file
+ *  @see pbs.h */
+
+/* reserved track encoding:
+ normal railway tracks:
+   map3lo bits 4..6 = 'Track'number of reserved track + 1, if this is zero it means nothing is reserved on this tile
+   map3lo bit  7    = if this is set, then the opposite track ('Track'number^1) is also reserved
+ waypoints/stations:
+   map3lo bit 6 set = track is reserved
+ tunnels/bridges:
+   map3hi bit 0 set = track with 'Track'number 0 is reserved
+   map3hi bit 1 set = track with 'Track'number 1 is reserved
+ level crossings:
+   map5 bit 0 set = the rail track is reserved
+*/
+
+/**
+ * maps an encoded reserved track (from map3lo bits 4..7)
+ * to the tracks that are reserved.
+ * 0xFF are invalid entries and should never be accessed.
+ */
+static const byte encrt_to_reserved[16] = {
+	0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0xFF,
+	0xFF, 0xFF, 0xFF, 0x0C, 0x0C, 0x30, 0x30, 0xFF
+};
+
+/**
+ * maps an encoded reserved track (from map3lo bits 4..7)
+ * to the track(dir)s that are unavailable due to reservations.
+ * 0xFFFF are invalid entries and should never be accessed.
+ */
+static const int16 encrt_to_unavail[16] = {
+	0x0000, 0x3F3F, 0x3F3F, 0x3737, 0x3B3B, 0x1F1F, 0x2F2F, 0xFFFF,
+	0xFFFF, 0xFFFF, 0xFFFF, 0x3F3F, 0x3F3F, 0x3F3F, 0x3F3F, 0xFFFF
+};
+
+void PBSReserveTrack(TileIndex tile, Track track) {
+	assert(IsValidTile(tile));
+	assert(track <= 5);
+	switch (GetTileType(tile)) {
+		case MP_RAILWAY:
+			if ((_map5[tile] & ~1) == 0xC4) {
+				// waypoint
+				SETBIT(_map3_lo[tile], 6);
+			} else {
+				// normal rail track
+				byte encrt = (_map3_hi[tile] & 0xF0) >> 4; // get current encoded info (see comments at top of file)
+
+				if (encrt == 0) // nothing reserved before
+					encrt = track + 1;
+				else if (encrt == (track^1) + 1) // opposite track reserved before
+					encrt |= 8;
+
+				_map3_hi[tile] &= ~0xF0;
+				_map3_hi[tile] |= encrt << 4;
+			}
+			break;
+		case MP_TUNNELBRIDGE:
+			_map3_hi[tile] |= (1 << track) & 3;
+			break;
+		case MP_STATION:
+			SETBIT(_map3_lo[tile], 6);
+			break;
+		case MP_STREET:
+			// make sure it is a railroad crossing
+			if (!IsLevelCrossing(tile)) return;
+			SETBIT(_map5[tile], 0);
+			break;
+		default:
+			return;
+	};
+	// if debugging, mark tile dirty to show reserved status
+	if (_debug_pbs_level >= 1)
+		MarkTileDirtyByTile(tile);
+}
+
+byte PBSTileReserved(TileIndex tile) {
+	assert(IsValidTile(tile));
+	switch (GetTileType(tile)) {
+		case MP_RAILWAY:
+			if ((_map5[tile] & ~1) == 0xC4) {
+				// waypoint
+				// check if its reserved
+				if (!HASBIT(_map3_lo[tile], 6)) return 0;
+				// return the track for the correct direction
+				return HASBIT(_map5[tile], 0) ? 2 : 1;
+			} else {
+				// normal track
+				byte res = encrt_to_reserved[(_map3_hi[tile] & 0xF0) >> 4];
+				assert(res != 0xFF);
+				return res;
+			};
+		case MP_TUNNELBRIDGE:
+			return (_map3_hi[tile] & 3);
+		case MP_STATION:
+			// check if its reserved
+			if (!HASBIT(_map3_lo[tile], 6)) return 0;
+			// return the track for the correct direction
+			return HASBIT(_map5[tile], 0) ? 2 : 1;
+		case MP_STREET:
+			// make sure its a railroad crossing
+			if (!IsLevelCrossing(tile)) return 0;
+			// check if its reserved
+			if (!HASBIT(_map5[tile], 0)) return 0;
+			// return the track for the correct direction
+			return HASBIT(_map5[tile], 3) ? 1 : 2;
+		default:
+			return 0;
+	};
+};
+
+uint16 PBSTileUnavail(TileIndex tile) {
+	assert(IsValidTile(tile));
+	switch (GetTileType(tile)) {
+		case MP_RAILWAY:
+			if ((_map5[tile] & ~1) == 0xC4) {
+				// waypoint
+				return HASBIT(_map3_lo[tile], 6) ? TRACKDIR_BIT_MASK : 0;
+			} else {
+				// normal track
+				uint16 res = encrt_to_unavail[(_map3_hi[tile] & 0xF0) >> 4];
+				assert(res != 0xFFFF);
+				return res;
+			};
+		case MP_TUNNELBRIDGE:
+			return (_map3_hi[tile] & 3) | ((_map3_hi[tile] & 3) << 8);
+		case MP_STATION:
+			return HASBIT(_map3_lo[tile], 6) ? TRACKDIR_BIT_MASK : 0;
+		case MP_STREET:
+			// make sure its a railroad crossing
+			if (!IsLevelCrossing(tile)) return 0;
+			// check if its reserved
+			return (HASBIT(_map5[tile], 0)) ? TRACKDIR_BIT_MASK : 0;
+		default:
+			return 0;
+	};
+};
+
+void PBSClearTrack(TileIndex tile, Track track) {
+	assert(IsValidTile(tile));
+	assert(track <= 5);
+	switch (GetTileType(tile)) {
+		case MP_RAILWAY:
+			if ((_map5[tile] & ~1) == 0xC4) {
+				// waypoint
+				CLRBIT(_map3_lo[tile], 6);
+			} else {
+				// normal rail track
+				byte encrt = (_map3_hi[tile] & 0xF0) >> 4;
+
+				if (encrt == track + 1)
+					encrt = 0;
+				else if (encrt == track + 1 + 8)
+					encrt = (track^1) + 1;
+				else if (encrt == (track^1) + 1 + 8)
+					encrt &= 7;
+
+				_map3_hi[tile] &= ~0xF0;
+				_map3_hi[tile] |= encrt << 4;
+			}
+			break;
+		case MP_TUNNELBRIDGE:
+			_map3_hi[tile] &= ~((1 << track) & 3);
+			break;
+		case MP_STATION:
+			CLRBIT(_map3_lo[tile], 6);
+			break;
+		case MP_STREET:
+			// make sure it is a railroad crossing
+			if (!IsLevelCrossing(tile)) return;
+			CLRBIT(_map5[tile], 0);
+			break;
+		default:
+			return;
+	};
+	// if debugging, mark tile dirty to show reserved status
+	if (_debug_pbs_level >= 1)
+		MarkTileDirtyByTile(tile);
+};
+
+void PBSClearPath(TileIndex tile, Trackdir trackdir) {
+	uint16 res;
+	FindLengthOfTunnelResult flotr;
+	assert(IsValidTile(tile));
+	assert((trackdir & ~8) <= 5);
+	do {
+		PBSClearTrack(tile, trackdir & 7);
+
+		if (IsTileType(tile, MP_TUNNELBRIDGE) && (_map5[tile] & 0xF0)==0 && (unsigned)(_map5[tile] & 3) == TrackdirToExitdir(trackdir)) {
+			// this is a tunnel
+			flotr = FindLengthOfTunnel(tile, TrackdirToExitdir(trackdir));
+
+			tile = flotr.tile;
+		} else {
+			byte exitdir = TrackdirToExitdir(trackdir);
+			if (IsTileDepotType(tile, TRANSPORT_RAIL) && (exitdir != GetDepotDirection(tile, TRANSPORT_RAIL)))
+				return;
+			tile = AddTileIndexDiffCWrap(tile, TileIndexDiffCByDir(exitdir));
+			if (IsTileDepotType(tile, TRANSPORT_RAIL) && (exitdir != ReverseDiagdir(GetDepotDirection(tile, TRANSPORT_RAIL))))
+				return;
+		};
+
+		res = PBSTileReserved(tile);
+		res |= res << 8;
+		res &= TrackdirReachesTrackdirs(trackdir);
+		trackdir = FindFirstBit2x64(res);
+
+	} while (res != 0);
+};
+
+bool PBSIsPbsSignal(TileIndex tile, Trackdir trackdir)
+{
+	assert(IsValidTile(tile));
+	assert(IsValidTrackdir(trackdir));
+
+	if (!_patches.new_pathfinding_all)
+		return false;
+
+	if (!IsTileType(tile, MP_RAILWAY))
+		return false;
+
+	if (GetRailTileType(tile) != RAIL_TYPE_SIGNALS)
+		return false;
+
+	if (!HasSignalOnTrackdir(tile, trackdir))
+		return false;
+
+	if (GetSignalType(tile, TrackdirToTrack(trackdir)) == 4)
+		return true;
+	else
+		return false;
+};
+
+typedef struct SetSignalsDataPbs {
+	int cur;
+
+	// these are used to keep track of the signals.
+	byte bit[NUM_SSD_ENTRY];
+	TileIndex tile[NUM_SSD_ENTRY];
+} SetSignalsDataPbs;
+
+// This function stores the signals inside the SetSignalsDataPbs struct, passed as callback to FollowTrack() in the PBSIsPbsSegment() function below
+static bool SetSignalsEnumProcPBS(uint tile, SetSignalsDataPbs *ssd, int trackdir, uint length, byte *state)
+{
+	// the tile has signals?
+	if (IsTileType(tile, MP_RAILWAY)) {
+		if (HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) {
+
+				if (ssd->cur != NUM_SSD_ENTRY) {
+					ssd->tile[ssd->cur] = tile; // remember the tile index
+					ssd->bit[ssd->cur] = TrackdirToTrack(trackdir); // and the controlling bit number
+					ssd->cur++;
+				}
+				return true;
+		} else if (IsTileDepotType(tile, TRANSPORT_RAIL))
+			return true; // don't look further if the tile is a depot
+	}
+	return false;
+}
+
+bool PBSIsPbsDepot(uint tile)
+{
+	SetSignalsDataPbs ssd;
+	bool result = false;
+	DiagDirection direction = GetDepotDirection(tile,TRANSPORT_RAIL);
+	int i;
+
+	ssd.cur = 0;
+
+	FollowTrack(tile, 0xC000 | TRANSPORT_RAIL, direction, (TPFEnumProc*)SetSignalsEnumProcPBS, NULL, &ssd);
+	for(i=0; i!=ssd.cur; i++) {
+		uint tile = ssd.tile[i];
+		byte bit = ssd.bit[i];
+		if (!PBSIsPbsSignal(tile, bit) && !PBSIsPbsSignal(tile, bit | 8))
+			return false;
+		result = true;
+	};
+
+	return result;
+}
new file mode 100644
--- /dev/null
+++ b/pbs.h
@@ -0,0 +1,82 @@
+#ifndef PBS_H
+#define PBS_H
+
+/** @file pbs.h Path-Based-Signalling header file
+ *  @see pbs.c */
+
+#include "vehicle.h"
+#include "tile.h"
+#include "map.h"
+#include "rail.h"
+
+/**
+ * constants used for pbs_mode argument of npf-functions
+ */
+enum pbs_modes {
+	PBS_MODE_NONE = 0,    // no pbs
+	PBS_MODE_GREEN = 1,   // look for green exit signal from pbs block
+	PBS_MODE_ANY = 2,     // look for any exit signal from block
+};
+
+/**
+ * constants used for v->u.rail.pbs_status
+ */
+enum PBSStatus {
+	PBS_STAT_NONE = 0,
+	PBS_STAT_HAS_PATH = 1,
+	PBS_STAT_NEED_PATH = 2,
+};
+
+
+void PBSReserveTrack(TileIndex tile, Track track);
+/**<
+ * Marks a track as reserved.
+ * @param tile The tile of the track.
+ * @param track The track to reserve, valid values 0-5.
+ */
+
+byte PBSTileReserved(TileIndex tile);
+/**<
+ * Check which tracks are reserved on a tile.
+ * @param tile The tile which you want to check.
+ * @return The tracks reserved on that tile, each of the bits 0-5 is set when the corresponding track is reserved.
+ */
+
+uint16 PBSTileUnavail(TileIndex tile);
+/**<
+ * Check which trackdirs are unavailable due to reserved tracks on a tile.
+ * @param tile The tile which you want to check.
+ * @return The tracks reserved on that tile, each of the bits 0-5,8-13 is set when the corresponding trackdir is unavailable.
+ */
+
+void PBSClearTrack(TileIndex tile, Track track);
+/**<
+ * Unreserves a track.
+ * @param tile The tile of the track.
+ * @param track The track to unreserve, valid values 0-5.
+ */
+
+void PBSClearPath(TileIndex tile, Trackdir trackdir);
+/**<
+ * Follows a planned(reserved) path, and unreserves the tracks.
+ * @param tile The tile on which the path starts
+ * @param trackdir The trackdirection in which the path starts
+ */
+
+bool PBSIsPbsSignal(TileIndex tile, Trackdir trackdir);
+/**<
+ * Checks if there are pbs signals on a track.
+ * @param tile The tile you want to check
+ * @param trackdir The trackdir you want to check
+ * @return True when there are pbs signals on that tile
+ */
+
+bool PBSIsPbsDepot(uint tile);
+/**<
+ * Checks if a depot is inside a pbs block.
+ * Tis means that the block it is in needs to have at least 1 signal, and that all signals in it need to be pbs signals.
+ * @param tile The depot tile to check
+ * @return True when the depot is inside a pbs block.
+ */
+
+#endif
--- a/rail.c
+++ b/rail.c
@@ -75,6 +75,15 @@
 	{TRACKDIR_RIGHT_N,  TRACKDIR_RIGHT_S,  INVALID_TRACKDIR,  INVALID_TRACKDIR}
 };
 
+const Trackdir _track_enterdir_to_trackdir[][DIAGDIR_END] = { // TODO: replace magic with enums
+	{TRACKDIR_DIAG1_NE, INVALID_TRACKDIR,  TRACKDIR_DIAG1_SW, INVALID_TRACKDIR},
+	{INVALID_TRACKDIR,  TRACKDIR_DIAG2_SE, INVALID_TRACKDIR,  TRACKDIR_DIAG2_NW},
+	{INVALID_TRACKDIR,  TRACKDIR_UPPER_E,  TRACKDIR_UPPER_W,  INVALID_TRACKDIR},
+	{TRACKDIR_LOWER_E,  INVALID_TRACKDIR,  INVALID_TRACKDIR,  TRACKDIR_LOWER_W},
+	{TRACKDIR_LEFT_N,   TRACKDIR_LEFT_S,   INVALID_TRACKDIR,  INVALID_TRACKDIR},
+	{INVALID_TRACKDIR,  INVALID_TRACKDIR,  TRACKDIR_RIGHT_S,  TRACKDIR_RIGHT_N}
+};
+
 const Trackdir _track_direction_to_trackdir[][DIR_END] = {
 	{INVALID_TRACKDIR, TRACKDIR_DIAG1_NE, INVALID_TRACKDIR, INVALID_TRACKDIR,  INVALID_TRACKDIR, TRACKDIR_DIAG1_SW, INVALID_TRACKDIR, INVALID_TRACKDIR},
 	{INVALID_TRACKDIR, INVALID_TRACKDIR,  INVALID_TRACKDIR, TRACKDIR_DIAG2_SE, INVALID_TRACKDIR, INVALID_TRACKDIR,  INVALID_TRACKDIR, TRACKDIR_DIAG2_NW},
--- a/rail.h
+++ b/rail.h
@@ -43,8 +43,9 @@
   SIGTYPE_ENTRY   = 1,        // presignal block entry
   SIGTYPE_EXIT    = 2,        // presignal block exit
   SIGTYPE_COMBO   = 3,        // presignal inter-block
+	SIGTYPE_PBS     = 4,        // pbs signal
 	SIGTYPE_END,
-	SIGTYPE_MASK    = 3,
+	SIGTYPE_MASK    = 7,
 } SignalType;
 
 typedef enum RailTypes {
@@ -134,6 +135,11 @@
 	SIGNAL_STATE_GREEN = 1,
 } SignalState;
 
+// these are the maximums used for updating signal blocks, and checking if a depot is in a pbs block
+enum {
+	NUM_SSD_ENTRY = 256, // max amount of blocks
+	NUM_SSD_STACK = 32 ,// max amount of blocks to check recursively
+};
 
 /**
  * Maps a Trackdir to the corresponding TrackdirBits value
@@ -317,6 +323,15 @@
 }
 
 /**
+ * Maps a track and an (4-way) dir to the trackdir that represents the track
+ * with the exit in the given direction.
+ */
+static inline Trackdir TrackEnterdirToTrackdir(Track track, DiagDirection diagdir) {
+	extern const Trackdir _track_enterdir_to_trackdir[TRACK_END][DIAGDIR_END];
+	return _track_enterdir_to_trackdir[track][diagdir];
+}
+
+/**
  * Maps a track and a full (8-way) direction to the trackdir that represents
  * the track running in the given direction.
  */
@@ -359,6 +374,14 @@
 	return _reverse_diagdir[diagdir];
 }
 
+/**
+ * Maps a (8-way) direction to a (4-way) DiagDirection
+ */
+static inline DiagDirection DirToDiagdir(Direction dir) {
+	assert(dir < DIR_END);
+	return (DiagDirection)(dir >> 1);
+}
+
 /* Checks if a given Track is diagonal */
 static inline bool IsDiagonalTrack(Track track) { return (track == TRACK_DIAG1) || (track == TRACK_DIAG2); }
 
--- a/rail_cmd.c
+++ b/rail_cmd.c
@@ -15,7 +15,9 @@
 #include "station.h"
 #include "sprite.h"
 #include "depot.h"
+#include "pbs.h"
 #include "waypoint.h"
+#include "npf.h"
 #include "rail.h"
 
 extern uint16 _custom_sprites_base;
@@ -757,10 +759,10 @@
 				_map3_lo[tile] |= SignalOnTrack(track);
 			} else {
 				if (pre_signal) {
-					// cycle between normal -> pre -> exit -> combo -> ...
-					byte type = (GetSignalType(tile, track) + 1) & 0x03;
-					_map3_hi[tile] &= ~0x03;
-					_map3_hi[tile] |= type;
+					// cycle between normal -> pre -> exit -> combo -> pbs ->...
+					byte type = ((GetSignalType(tile, track) + 1) % 5);
+					_map3_hi[tile] &= ~0x07;
+					_map3_hi[tile] |= type ;
 				} else {
 					// cycle between two-way -> one-way -> one-way -> ...
 					/* TODO: Rewrite switch into something more general */
@@ -1123,21 +1125,24 @@
 	0x1333,
 	0x1343,
 
-	0x0,	//PBS place, light signal
-	0x0,	//reserved for future use
-	0x0,	//reserved for future use
-	0x0,	//reserved for future use
+	// pbs signals
+	0x1393,
+	0x13A3,  // not used (yet?)
+	0x13B3,  // not used (yet?)
+	0x13C3,  // not used (yet?)
 
-	// use semaphores instead of signals?
+	// semaphores
 	0x1353,
 	0x1363,
 	0x1373,
 	0x1383,
 
-	0x0,	//PBS place, semaphore
-	0x0,	//reserved for future use
-	0x0,	//reserved for future use
-	0x0,	//reserved for future use
+	// pbs semaphores
+	0x13D3,
+	0x13E3,  // not used (yet?)
+	0x13F3,  // not used (yet?)
+	0x1403,  // not used (yet?)
+
 
 	// mirrored versions
 	0x4FB,
@@ -1145,20 +1150,23 @@
 	0x1333,
 	0x1343,
 
-	0x0,	//PBS place, semaphore
-	0x0,	//reserved for future use
-	0x0,	//reserved for future use
-	0x0,	//reserved for future use
+	// pbs signals
+	0x1393,
+	0x13A3,  // not used (yet?)
+	0x13B3,  // not used (yet?)
+	0x13C3,  // not used (yet?)
 
-	0x13C6,
-	0x13D6,
-	0x13E6,
-	0x13F6,
+	// semaphores
+	0x1446,
+	0x1456,
+	0x1466,
+	0x1476,
 
-	0x0,	//PBS place, semaphore
-	0x0,	//reserved for future use
-	0x0,	//reserved for future use
-	0x0,	//reserved for future use
+	// pbs semaphores
+	0x14C6,
+	0x14D6,  // not used (yet?)
+	0x14E6,  // not used (yet?)
+	0x14F6,  // not used (yet?)
 };
 
 // used to determine the side of the road for the signal
@@ -1466,6 +1474,16 @@
 			if (m5 & TRACK_BIT_RIGHT) DrawGroundSprite(TrackSet[SINGLE_EAST]);
 		}
 
+		if (_debug_pbs_level >= 1) {
+			byte pbs = PBSTileReserved(ti->tile);
+			if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite(TrackSet[SINGLE_Y] | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite(TrackSet[SINGLE_X] | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(TrackSet[SINGLE_NORTH] | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(TrackSet[SINGLE_SOUTH] | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_LEFT)  DrawGroundSprite(TrackSet[SINGLE_WEST] | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(TrackSet[SINGLE_EAST] | PALETTE_CRASH);
+		}
+
 		if (_display_opt & DO_FULL_DETAIL) {
 			_detailed_track_proc[_map2[ti->tile] & RAIL_MAP2LO_GROUND_MASK](ti);
 		}
@@ -1575,6 +1593,16 @@
 
 		DrawGroundSprite(image);
 
+		if (_debug_pbs_level >= 1) {
+			byte pbs = PBSTileReserved(ti->tile);
+			if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite((0x3ED + tracktype_offs) | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite((0x3EE + tracktype_offs) | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_UPPER) DrawGroundSprite((0x3EF + tracktype_offs) | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_LOWER) DrawGroundSprite((0x3F0 + tracktype_offs) | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_LEFT)  DrawGroundSprite((0x3F2 + tracktype_offs) | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite((0x3F1 + tracktype_offs) | PALETTE_CRASH);
+		}
+
 		while ((image=drss->image) != 0) {
 			DrawSpecialBuilding(image, type < 4 ? tracktype_offs : 0, ti,
 			                    drss->subcoord_x, drss->subcoord_y, 0,
@@ -1611,15 +1639,17 @@
 	}
 }
 
-#define NUM_SSD_ENTRY 256
-#define NUM_SSD_STACK 32
-
 typedef struct SetSignalsData {
 	int cur;
 	int cur_stack;
 	bool stop;
 	bool has_presignal;
 
+	bool has_pbssignal;
+		// lowest 2 bits = amount of pbs signals in the block, clamped at 2
+		// bit 2 = there is a pbs entry signal in this block
+		// bit 3 = there is a pbs exit signal in this block
+
 	// presignal info
 	int presignal_exits;
 	int presignal_exits_free;
@@ -1628,6 +1658,10 @@
 	byte bit[NUM_SSD_ENTRY];
 	TileIndex tile[NUM_SSD_ENTRY];
 
+	int pbs_cur;
+	// these are used to keep track of all signals in the block
+	TileIndex pbs_tile[NUM_SSD_ENTRY];
+
 	// these are used to keep track of the stack that modifies presignals recursively
 	TileIndex next_tile[NUM_SSD_STACK];
 	byte next_dir[NUM_SSD_STACK];
@@ -1647,15 +1681,34 @@
 					ssd->cur++;
 				}
 
+			if (PBSIsPbsSignal(tile, ReverseTrackdir(track)))
+				SETBIT(ssd->has_pbssignal, 2);
+
 				// remember if this block has a presignal.
 				ssd->has_presignal |= (_map3_hi[tile]&1);
 			}
 
-			// is this an exit signal that points out from the segment?
-			if ((_map3_hi[tile]&2) && _map3_lo[tile]&_signals_table_other[track]) {
-				ssd->presignal_exits++;
-				if ((_map2[tile]&_signals_table_other[track]) != 0)
-					ssd->presignal_exits_free++;
+			if (PBSIsPbsSignal(tile, ReverseTrackdir(track)) || PBSIsPbsSignal(tile, track)) {
+				byte num = ssd->has_pbssignal & 3;
+				num = clamp(num + 1, 0, 2);
+				ssd->has_pbssignal &= ~3;
+				ssd->has_pbssignal |= num;
+			}
+
+			if ((_map3_lo[tile] & _signals_table_both[track]) != 0) {
+				ssd->pbs_tile[ssd->pbs_cur] = tile; // remember the tile index
+				ssd->pbs_cur++;
+			}
+
+			if (_map3_lo[tile]&_signals_table_other[track]) {
+				if (_map3_hi[tile]&2) {
+					// this is an exit signal that points out from the segment
+					ssd->presignal_exits++;
+					if ((_map2[tile]&_signals_table_other[track]) != 0)
+						ssd->presignal_exits_free++;
+				}
+				if (PBSIsPbsSignal(tile, track))
+					SETBIT(ssd->has_pbssignal, 3);
 			}
 
 			return true;
@@ -1792,6 +1845,15 @@
 	//   there is at least one green exit signal OR
 	//   there are no exit signals in the segment
 
+	// convert the block to pbs, if needed
+	if (_patches.auto_pbs_placement && !(ssd->stop) && (ssd->has_pbssignal == 0xE) && !ssd->has_presignal && (ssd->presignal_exits == 0)) // 0xE means at least 2 pbs signals, and at least 1 entry and 1 exit, see comments ssd->has_pbssignal
+	for(i=0; i!=ssd->pbs_cur; i++) {
+		TileIndex tile = ssd->pbs_tile[i];
+		_map3_hi[tile] &= ~0x07;
+		_map3_hi[tile] |= 0x04;
+		MarkTileDirtyByTile(tile);
+	};
+
 	// then mark the signals in the segment accordingly
 	for(i=0; i!=ssd->cur; i++) {
 		TileIndex tile = ssd->tile[i];
@@ -1852,8 +1914,9 @@
 
 	for(;;) {
 		// go through one segment and update all signals pointing into that segment.
-		ssd.cur = ssd.presignal_exits = ssd.presignal_exits_free = 0;
+		ssd.cur = ssd.pbs_cur = ssd.presignal_exits = ssd.presignal_exits_free = 0;
 		ssd.has_presignal = false;
+		ssd.has_pbssignal = false;
 
 		FollowTrack(tile, 0xC000 | TRANSPORT_RAIL, direction, (TPFEnumProc*)SetSignalsEnumProc, SetSignalsAfterProc, &ssd);
 		ChangeSignalStates(&ssd);
@@ -2162,6 +2225,8 @@
 	} else if (_fractcoords_enter[dir] == fract_coord) {
 		if (_enter_directions[dir] == v->direction) {
 			/* enter the depot */
+			if (v->next == NULL)
+				PBSClearTrack(v->tile, FIND_FIRST_BIT(v->u.rail.track));
 			v->u.rail.track = 0x80,
 			v->vehstatus |= VS_HIDDEN; /* hide it */
 			v->direction ^= 4;
--- a/road_cmd.c
+++ b/road_cmd.c
@@ -1,5 +1,6 @@
 #include "stdafx.h"
 #include "openttd.h"
+#include "table/sprites.h"
 #include "table/strings.h"
 #include "map.h"
 #include "tile.h"
@@ -11,6 +12,8 @@
 #include "gfx.h"
 #include "sound.h"
 #include "depot.h"
+#include "pbs.h"
+#include "debug.h"
 
 /* When true, GetTrackStatus for roads will treat roads under reconstruction
  * as normal roads instead of impassable. This is used when detecting whether
@@ -246,6 +249,7 @@
 
 			cost = _price.remove_road * 2;
 			if (flags & DC_EXEC) {
+				byte pbs_track = PBSTileReserved(tile);
 				ChangeTownRating(t, -road_remove_cost[(byte)edge_road], RATING_ROAD_MINIMUM);
 
 				ModifyTile(tile,
@@ -254,6 +258,8 @@
 					_map3_hi[tile] & 0xF, /* map3_lo */
 					c											/* map5 */
 				);
+				if (pbs_track != 0)
+					PBSReserveTrack(tile, FIND_FIRST_BIT(pbs_track));
 			}
 			return cost;
 		} else
@@ -396,6 +402,7 @@
 			goto do_clear;
 
 		if (flags & DC_EXEC) {
+			byte pbs_track = PBSTileReserved(tile);
 			ModifyTile(tile,
 				MP_SETTYPE(MP_STREET) |
 				MP_MAP2 | MP_MAP3LO | MP_MAP3HI | MP_MAP5,
@@ -404,6 +411,8 @@
 				_map3_lo[tile] & 0xF, /* map3_hi */
 				m5 /* map5 */
 			);
+			if (pbs_track != 0)
+				PBSReserveTrack(tile, FIND_FIRST_BIT(pbs_track));
 		}
 		return _price.build_road * 2;
 	} else if (ti.type == MP_TUNNELBRIDGE) {
@@ -826,6 +835,17 @@
 		}
 
 		DrawGroundSprite(image + (_map3_hi[ti->tile] & 0xF) * 12);
+
+		if (_debug_pbs_level >= 1) {
+			byte pbs = PBSTileReserved(ti->tile);
+			if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite(0x3ED | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite(0x3EE | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(0x3EF | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(0x3F0 | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_LEFT)  DrawGroundSprite(0x3F2 | PALETTE_CRASH);
+			if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(0x3F1 | PALETTE_CRASH);
+		}
+
 	} else {
 		uint32 ormod;
 		int player;
--- a/roadveh_cmd.c
+++ b/roadveh_cmd.c
@@ -1086,7 +1086,7 @@
 		trackdir = DiagdirToDiagTrackdir(enterdir);
 		//debug("Finding path. Enterdir: %d, Trackdir: %d", enterdir, trackdir);
 
-		ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE);
+		ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE, PBS_MODE_NONE);
 		if (ftd.best_trackdir == 0xff) {
 			/* We are already at our target. Just do something */
 			//TODO: maybe display error?
@@ -1163,7 +1163,7 @@
   fstd.dest_coords = tile;
   fstd.station_index = -1;	// indicates that the destination is a tile, not a station
 
-  return NPFRouteToStationOrTile(v->tile, trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE).best_path_dist;
+  return NPFRouteToStationOrTile(v->tile, trackdir, &fstd, TRANSPORT_ROAD, v->owner, INVALID_RAILTYPE, PBS_MODE_NONE).best_path_dist;
 }
 
 typedef struct RoadDriveEntry {
--- a/settings.c
+++ b/settings.c
@@ -845,6 +845,7 @@
 // Non-static, needed in network_server.c
 const SettingDesc patch_settings[] = {
 	{"build_on_slopes",			SDT_BOOL,		(void*)true,	&_patches.build_on_slopes,			NULL},
+	{"auto_pbs_placement",	SDT_BOOL,		(void*)true,	&_patches.auto_pbs_placement,		NULL},
 	{"mammoth_trains",			SDT_BOOL,		(void*)true,	&_patches.mammoth_trains,				NULL},
 	{"join_stations",				SDT_BOOL,		(void*)true,	&_patches.join_stations,				NULL},
 	{"station_spread",			SDT_UINT8,	(void*)12,		&_patches.station_spread,				NULL},
--- a/settings_gui.c
+++ b/settings_gui.c
@@ -669,6 +669,7 @@
 
 	{PE_BOOL,		0, STR_CONFIG_PATCHES_SMALL_AIRPORTS,		"always_small_airport", &_patches.always_small_airport,			0,  0,  0, NULL},
 	{PE_UINT8,	PF_PLAYERBASED, STR_CONFIG_PATCHES_DRAG_SIGNALS_DENSITY, "drag_signals_density", &_patches.drag_signals_density, 1, 20,  1, NULL},
+	{PE_BOOL,		0, STR_CONFIG_AUTO_PBS_PLACEMENT, "auto_pbs_placement", &_patches.auto_pbs_placement, 1, 20,  1, NULL},
 
 };
 
--- a/ship_cmd.c
+++ b/ship_cmd.c
@@ -565,7 +565,7 @@
 
 		NPFFillWithOrderData(&fstd, v);
 
-		ftd = NPFRouteToStationOrTile(src_tile, trackdir, &fstd, TRANSPORT_WATER, v->owner, INVALID_RAILTYPE);
+		ftd = NPFRouteToStationOrTile(src_tile, trackdir, &fstd, TRANSPORT_WATER, v->owner, INVALID_RAILTYPE, PBS_MODE_NONE);
 
 		if (ftd.best_trackdir != 0xff)
 			/* If ftd.best_bird_dist is 0, we found our target and ftd.best_trackdir contains
--- a/station_cmd.c
+++ b/station_cmd.c
@@ -19,6 +19,7 @@
 #include "airport.h"
 #include "sprite.h"
 #include "depot.h"
+#include "pbs.h"
 
 enum {
 	/* Max stations: 64000 (64 * 1000) */
@@ -2120,6 +2121,7 @@
 	const DrawTileSeqStruct *dtss;
 	const DrawTileSprites *t = NULL;
 	byte railtype = _map3_lo[ti->tile] & 0xF;
+	int type_offset;
 	uint32 relocation = 0;
 
 	{
@@ -2154,15 +2156,27 @@
 	if (image & 0x8000)
 		image |= image_or_modificator;
 
+	// For custom sprites, there's no railtype-based pitching.
+	type_offset = railtype * ((image & 0x3FFF) < _custom_sprites_base ? TRACKTYPE_SPRITE_PITCH : 1);
+
 	// station_land array has been increased from 82 elements to 114
 	// but this is something else. If AI builds station with 114 it looks all weird
-	image += railtype * ((image & 0x3FFF) < _custom_sprites_base ? TRACKTYPE_SPRITE_PITCH : 1);
+	image += type_offset;
 	DrawGroundSprite(image);
 
+	if (_debug_pbs_level >= 1) {
+		byte pbs = PBSTileReserved(ti->tile);
+		if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite((0x3ED + type_offset) | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite((0x3EE + type_offset) | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_UPPER) DrawGroundSprite((0x3EF + type_offset) | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_LOWER) DrawGroundSprite((0x3F0 + type_offset) | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_LEFT)  DrawGroundSprite((0x3F2 + type_offset) | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite((0x3F1 + type_offset) | PALETTE_CRASH);
+	}
+
 	foreach_draw_tile_seq(dtss, t->seq) {
-		image =	dtss->image + relocation;
-		// For custom sprites, there's no railtype-based pitching.
-		image += railtype * ((image & 0x3FFF) < _custom_sprites_base ? TRACKTYPE_SPRITE_PITCH : 0);
+		image = dtss->image + relocation;
+		image += type_offset;
 		if (_display_opt & DO_TRANS_BUILDINGS) {
 			image = (image & 0x3FFF) | 0x03224000;
 		} else {
--- a/table/files.h
+++ b/table/files.h
@@ -26,7 +26,7 @@
 		{ "TRG1.GRF", {0x93,0x11,0x67,0x62,0x80,0xe5,0xb1,0x40,0x77,0xa8,0xee,0x41,0xc1,0xb4,0x21,0x92} },     //    0 - 4792 inclusive
 		{ "TRGI.GRF", {0xda,0x6a,0x6c,0x9d,0xcc,0x45,0x1e,0xec,0x88,0xd7,0x92,0x11,0x43,0x7b,0x76,0xa8} },     // 4793 - 4889 inclusive
 		{ "dosdummy.grf", {0x07,0x01,0xe6,0xc4,0x07,0x6a,0x5b,0xc3,0xf4,0x9f,0x01,0xad,0x21,0x6c,0xa0,0xc2} }, // 4890 - 4895 inclusive
-		{ "signalsw.grf", {0x76,0x1b,0x42,0x25,0x44,0x0d,0x21,0xc7,0xe0,0xb4,0x25,0xd8,0x2f,0xc8,0x52,0x38} }, // 4896 - 5125 inclusive
+		{ "nsignalsw.grf", {0x65,0xb9,0xd7,0x30,0x56,0x06,0xcc,0x9e,0x27,0x57,0xc8,0xe4,0x9b,0xb3,0x66,0x81} }, // 4896 - 5381 inclusive
 		{ NULL, { 0 } }
 	},
 	{	{ "TRGC.GRF", {0xed,0x44,0x66,0x37,0xe0,0x34,0x10,0x4c,0x55,0x59,0xb3,0x2c,0x18,0xaf,0xe7,0x8d} },
@@ -39,7 +39,7 @@
 	{
 		{ "TRG1R.GRF", {0xb0,0x4c,0xe5,0x93,0xd8,0xc5,0x01,0x6e,0x07,0x47,0x3a,0x74,0x3d,0x7d,0x33,0x58} },    //    0 - 4792 inclusive
 		{ "TRGIR.GRF", {0x0c,0x24,0x84,0xff,0x6b,0xe4,0x9f,0xc6,0x3a,0x83,0xbe,0x6a,0xb5,0xc3,0x8f,0x32} },    // 4793 - 4895 inclusive
-		{ "signalsw.grf", {0x76,0x1b,0x42,0x25,0x44,0x0d,0x21,0xc7,0xe0,0xb4,0x25,0xd8,0x2f,0xc8,0x52,0x38} }, // 4896 - 5125 inclusive
+		{ "nsignalsw.grf", {0x65,0xb9,0xd7,0x30,0x56,0x06,0xcc,0x9e,0x27,0x57,0xc8,0xe4,0x9b,0xb3,0x66,0x81} }, // 4896 - 5381 inclusive
 		{ NULL, { 0 } },
 		{ NULL, { 0 } }
 	},
--- a/table/sprites.h
+++ b/table/sprites.h
@@ -42,7 +42,7 @@
 	SPR_ASCII_SPACE_BIG   = 450,
 
 	/* Extra graphic spritenumbers */
-	SPR_CANALS_BASE		= 5126,
+	SPR_CANALS_BASE		= 5382,
 	SPR_SLOPES_BASE		= SPR_CANALS_BASE + 70,
 	SPR_AUTORAIL_BASE		= SPR_SLOPES_BASE + 78,
 	SPR_OPENTTD_BASE	= SPR_AUTORAIL_BASE + 55, // can be lowered once autorail.grf is finalized
--- a/train_cmd.c
+++ b/train_cmd.c
@@ -15,6 +15,7 @@
 #include "player.h"
 #include "sound.h"
 #include "depot.h"
+#include "debug.h"
 #include "waypoint.h"
 #include "vehicle_gui.h"
 
@@ -1296,14 +1297,86 @@
 	}
 }
 
+TileIndex GetVehicleTileOutOfTunnel(const Vehicle *v, bool reverse)
+{
+	TileIndex tile;
+	byte direction = (!reverse) ? DirToDiagdir(v->direction) : ReverseDiagdir(v->direction >> 1);
+	TileIndexDiff delta = TileOffsByDir(direction);
+
+	if (v->u.rail.track != 0x40)
+		return v->tile;
+
+	for (tile = v->tile;; tile += delta) {
+		if (IsTileType(tile, MP_TUNNELBRIDGE) &&
+				(_map5[tile] & 0xF3) != (direction) &&
+				GetTileZ(tile) == v->z_pos)
+ 			break;
+ 	}
+ 	return tile;
+
+};
+
 static void ReverseTrainDirection(Vehicle *v)
 {
 	int l = 0, r = -1;
 	Vehicle *u;
+	TileIndex tile;
+	byte trackdir;
+
+	u = GetLastVehicleInChain(v);
+	tile = GetVehicleTileOutOfTunnel(u, false);
+	trackdir = ReverseTrackdir(GetVehicleTrackdir(u));
+
+	if (PBSTileReserved(tile) & (1 << (trackdir&7))) {
+		NPFFindStationOrTileData fstd;
+		NPFFoundTargetData ftd;
+
+		NPFFillWithOrderData(&fstd, v);
+
+		tile = GetVehicleTileOutOfTunnel(u, true);
+
+		DEBUG(pbs, 2) ("pbs: (%i) choose reverse (RV), tile:%x, trackdir:%i",v->unitnumber,  u->tile, trackdir);
+		ftd = NPFRouteToStationOrTile(tile, trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_ANY);
+
+		if (ftd.best_trackdir == 0xFF) {
+			DEBUG(pbs, 0) ("pbs: (%i) no nodes encountered (RV)", v->unitnumber);
+			CLRBIT(v->u.rail.flags, VRF_REVERSING);
+			return;
+		}
+
+    // we found a way out of the pbs block
+		if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_EXIT)) {
+			if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_BLOCKED)) {
+				CLRBIT(v->u.rail.flags, VRF_REVERSING);
+				return;
+			}
+		}
+	}
+
+	tile = GetVehicleTileOutOfTunnel(v, false);
+	trackdir = GetVehicleTrackdir(v);
+
+	if (v->u.rail.pbs_status == PBS_STAT_HAS_PATH) {
+		byte trackdir = GetVehicleTrackdir(v);
+		TileIndex tile = AddTileIndexDiffCWrap(v->tile, TileIndexDiffCByDir(TrackdirToExitdir(trackdir)));
+		uint32 ts;
+		assert(tile != INVALID_TILE);
+		ts = GetTileTrackStatus(tile, TRANSPORT_RAIL);
+		ts &= TrackdirReachesTrackdirs(trackdir);
+		assert(ts != 0 && KillFirstBit2x64(ts) == 0);
+		trackdir = FindFirstBit2x64(ts);
+		PBSClearPath(tile, trackdir);
+		v->u.rail.pbs_status = PBS_STAT_NONE;
+	} else if (PBSTileReserved(tile) & (1 << (trackdir&7))) {
+		PBSClearPath(tile, trackdir);
+		if (v->u.rail.track != 0x40)
+			PBSReserveTrack(tile, trackdir & 7);
+	};
 
 	if (IsTileDepotType(v->tile, TRANSPORT_RAIL))
 		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
+
 	/* Check if we were approaching a rail/road-crossing */
 	{
 		TileIndex tile = v->tile;
@@ -1748,12 +1821,35 @@
 
 		v->load_unload_time_rem = 0;
 
+		if (PBSIsPbsDepot(v->tile)) {
+			byte trackdir = GetVehicleTrackdir(v);
+			NPFFindStationOrTileData fstd;
+			NPFFoundTargetData ftd;
+
+			if (PBSTileUnavail(v->tile) & (1 << trackdir))
+				return true;
+
+			NPFFillWithOrderData(&fstd, v);
+
+			DEBUG(pbs, 2) ("pbs: (%i) choose depot (DP), tile:%x, trackdir:%i",v->unitnumber,  v->tile, trackdir);
+			ftd = NPFRouteToStationOrTile(v->tile, trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_GREEN);
+
+			// we found a way out of the pbs block
+			if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_EXIT)) {
+				if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_BLOCKED) || NPFGetFlag(&ftd.node, NPF_FLAG_PBS_RED))
+					return true;
+				else
+					goto green;
+			}
+		}
+
+
 		if (UpdateSignalsOnSegment(v->tile, v->direction)) {
 			InvalidateWindowClasses(WC_TRAINS_LIST);
 			return true;
 		}
 	}
-
+green:
 	VehicleServiceInDepot(v);
 	InvalidateWindowClasses(WC_TRAINS_LIST);
 	TrainPlayLeaveStationSound(v);
@@ -1904,13 +2000,26 @@
 		NPFFindStationOrTileData fstd;
 		NPFFoundTargetData ftd;
 		Trackdir trackdir;
+		uint16 pbs_tracks;
 
 		NPFFillWithOrderData(&fstd, v);
 		/* The enterdir for the new tile, is the exitdir for the old tile */
 		trackdir = GetVehicleTrackdir(v);
 		assert(trackdir != 0xff);
 
-		ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype);
+		pbs_tracks = PBSTileReserved(tile);
+		pbs_tracks |= pbs_tracks << 8;
+		pbs_tracks &= TrackdirReachesTrackdirs(trackdir);
+		if (pbs_tracks || (v->u.rail.pbs_status == PBS_STAT_NEED_PATH)) {
+			DEBUG(pbs, 2) ("pbs: (%i) choosefromblock, tile_org:%x tile_dst:%x  trackdir:%i  pbs_tracks:%i",v->unitnumber, tile,tile - TileOffsByDir(enterdir), trackdir, pbs_tracks);
+			// clear the currently planned path
+			if (v->u.rail.pbs_status != PBS_STAT_NEED_PATH) PBSClearPath(tile, FindFirstBit2x64(pbs_tracks));
+
+			// try to find a route to a green exit signal
+			ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_ANY);
+
+		} else
+			ftd = NPFRouteToStationOrTile(tile - TileOffsByDir(enterdir), trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_NONE);
 
 		if (ftd.best_trackdir == 0xff) {
 			/* We are already at our target. Just do something */
@@ -1923,7 +2032,7 @@
 			we did not find our target, but ftd.best_trackdir contains the direction leading
 			to the tile closest to our target. */
 			/* Discard enterdir information, making it a normal track */
-			best_track = ftd.best_trackdir & 7; /* TODO: Wrapper function? */
+			best_track = TrackdirToTrack(ftd.best_trackdir);
 		}
 	} else {
 
@@ -2048,7 +2157,8 @@
 		assert(trackdir != 0xff);
 		assert(trackdir_rev != 0xff);
 
-		ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, last->tile, trackdir_rev, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype);
+		ftd = NPFRouteToStationOrTileTwoWay(v->tile, trackdir, last->tile, trackdir_rev, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_NONE);
+
 		if (ftd.best_bird_dist != 0) {
 			/* We didn't find anything, just keep on going straight ahead */
 			reverse_best = false;
@@ -2644,7 +2754,7 @@
 				} else {
 					/* is not inside depot */
 
-					if (!TrainCheckIfLineEnds(v))
+					if ((prev == NULL) && (!TrainCheckIfLineEnds(v)))
 						return;
 
 					r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
@@ -2699,11 +2809,54 @@
 				}
 
 				if (prev == NULL) {
+					byte trackdir;
 					/* Currently the locomotive is active. Determine which one of the
 					 * available tracks to choose */
 					chosen_track = 1 << ChooseTrainTrack(v, gp.new_tile, enterdir, bits);
 					assert(chosen_track & tracks);
 
+					trackdir = TrackEnterdirToTrackdir(FIND_FIRST_BIT(chosen_track), enterdir);
+					assert(trackdir != 0xff);
+
+					if (PBSIsPbsSignal(gp.new_tile,trackdir)) {
+						// encountered a pbs signal, and possible a pbs block
+						DEBUG(pbs, 3) ("pbs: (%i) arrive AT signal, tile:%x  pbs_stat:%i",v->unitnumber, gp.new_tile, v->u.rail.pbs_status);
+
+						if (v->u.rail.pbs_status == PBS_STAT_NONE) {
+							// we havent planned a path already, so try to find one now
+							NPFFindStationOrTileData fstd;
+							NPFFoundTargetData ftd;
+
+							NPFFillWithOrderData(&fstd, v);
+
+							DEBUG(pbs, 2) ("pbs: (%i) choose signal (TC), tile:%x, trackdir:%i",v->unitnumber,  gp.new_tile, trackdir);
+							ftd = NPFRouteToStationOrTile(gp.new_tile, trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_GREEN);
+
+							if (v->u.rail.force_proceed != 0)
+								goto green_light;
+
+							if (ftd.best_trackdir == 0xFF)
+								goto red_light;
+
+							// we found a way out of the pbs block
+							if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_EXIT)) {
+								if (NPFGetFlag(&ftd.node, NPF_FLAG_PBS_BLOCKED) || NPFGetFlag(&ftd.node, NPF_FLAG_PBS_RED))
+									goto red_light;
+								else {
+									goto green_light;
+								}
+
+							};
+
+						} else {
+							// we have already planned a path through this pbs block
+							// on entering the block, we reset our status
+							v->u.rail.pbs_status = PBS_STAT_NONE;
+							goto green_light;
+						};
+						DEBUG(pbs, 3) ("pbs: (%i) no green light found, or was no pbs-block",v->unitnumber);
+					};
+
 					/* Check if it's a red signal and that force proceed is not clicked. */
 					if ( (tracks>>16)&chosen_track && v->u.rail.force_proceed == 0) goto red_light;
 				} else {
@@ -2712,6 +2865,9 @@
 					/* The wagon is active, simply follow the prev vehicle. */
 					chosen_track = (byte)(_matching_tracks[GetDirectionToVehicle(prev, gp.x, gp.y)] & bits);
 				}
+green_light:
+				if (v->next == NULL)
+					PBSClearTrack(gp.old_tile, FIND_FIRST_BIT(v->u.rail.track));
 
 				/* make sure chosen track is a valid track */
 				assert(chosen_track==1 || chosen_track==2 || chosen_track==4 || chosen_track==8 || chosen_track==16 || chosen_track==32);
@@ -2740,12 +2896,12 @@
 				}
 
 				if (v->subtype == TS_Front_Engine)
- 				TrainMovedChangeSignals(gp.new_tile, enterdir);
+				TrainMovedChangeSignals(gp.new_tile, enterdir);
 
 				/* Signals can only change when the first
 				 * (above) or the last vehicle moves. */
 				if (v->next == NULL)
- 				TrainMovedChangeSignals(gp.old_tile, (enterdir) ^ 2);
+				TrainMovedChangeSignals(gp.old_tile, (enterdir) ^ 2);
 
 				if (prev == NULL) {
 					AffectSpeedByDirChange(v, chosen_dir);
@@ -2860,6 +3016,17 @@
 	EndVehicleMove(v);
 	DeleteVehicle(v);
 
+	// clear up reserved pbs tracks
+	if (PBSTileReserved(v->tile) & v->u.rail.track) {
+		if (v == u) {
+			PBSClearPath(v->tile, FIND_FIRST_BIT(v->u.rail.track));
+			PBSClearPath(v->tile, FIND_FIRST_BIT(v->u.rail.track) + 8);
+		};
+		if (v->tile != u->tile) {
+			PBSClearTrack(v->tile, FIND_FIRST_BIT(v->u.rail.track));
+		};
+	}
+
 	if (!(v->u.rail.track & 0xC0))
 		SetSignalsOnBothDir(v->tile, FIND_FIRST_BIT(v->u.rail.track));
 
@@ -2987,6 +3154,7 @@
 	uint x,y;
 	int t;
 	uint32 ts;
+	byte trackdir;
 
 	if ((uint)(t=v->breakdown_ctr) > 1) {
 		v->vehstatus |= VS_TRAIN_SLOWING;
@@ -3002,6 +3170,10 @@
 	if (v->u.rail.track & 0x40)
 		return true;
 
+	// exit if inside a depot
+	if (v->u.rail.track & 0x80)
+		return true;
+
 	tile = v->tile;
 
 	// tunnel entrance?
@@ -3025,6 +3197,12 @@
 	// determine the track status on the next tile.
 	ts = GetTileTrackStatus(tile, TRANSPORT_RAIL) & _reachable_tracks[t];
 
+	// if there are tracks on the new tile, pick one (trackdir will only be used when its a signal tile, in which case only 1 trackdir is accessible for us)
+	if (ts & TRACKDIR_BIT_MASK)
+		trackdir = FindFirstBit2x64(ts & TRACKDIR_BIT_MASK);
+	else
+		trackdir = INVALID_TRACKDIR;
+
 	/* Calc position within the current tile ?? */
 	x = v->x_pos & 0xF;
 	y = v->y_pos & 0xF;
@@ -3077,6 +3255,26 @@
 		return false;
 	}
 
+	if  (v->u.rail.pbs_status == PBS_STAT_HAS_PATH)
+		return true;
+
+	if ((trackdir != INVALID_TRACKDIR) && (PBSIsPbsSignal(tile,trackdir)) && !(IsTileType(v->tile, MP_STATION) && (v->current_order.station == _map2[v->tile]))) {
+		NPFFindStationOrTileData fstd;
+		NPFFoundTargetData ftd;
+
+		NPFFillWithOrderData(&fstd, v);
+
+		DEBUG(pbs, 2) ("pbs: (%i) choose signal (CEOL), tile:%x  trackdir:%i", v->unitnumber, tile, trackdir);
+		ftd = NPFRouteToStationOrTile(tile, trackdir, &fstd, TRANSPORT_RAIL, v->owner, v->u.rail.railtype, PBS_MODE_GREEN);
+
+		if (ftd.best_trackdir != 0xFF && NPFGetFlag(&ftd.node, NPF_FLAG_PBS_EXIT)) {
+			if (!(NPFGetFlag(&ftd.node, NPF_FLAG_PBS_BLOCKED) || NPFGetFlag(&ftd.node, NPF_FLAG_PBS_RED))) {
+				v->u.rail.pbs_status = PBS_STAT_HAS_PATH;
+				return true;
+			}
+		};
+	};
+
 	// slow down
 	v->vehstatus |= VS_TRAIN_SLOWING;
 	t = _breakdown_speeds[x & 0xF];
@@ -3237,6 +3435,12 @@
 	Depot *depot;
 	TrainFindDepotData tfdd;
 
+	if (PBSTileReserved(v->tile) & v->u.rail.track)
+		return;
+
+	if (v->u.rail.pbs_status == PBS_STAT_HAS_PATH)
+		return;
+
 	if (_patches.servint_trains == 0)
 		return;
 
--- a/tunnelbridge_cmd.c
+++ b/tunnelbridge_cmd.c
@@ -10,6 +10,8 @@
 #include "player.h"
 #include "town.h"
 #include "sound.h"
+#include "pbs.h"
+#include "debug.h"
 
 extern void DrawCanalWater(TileIndex tile);
 
@@ -770,6 +772,7 @@
 		byte m5;
 		uint c = tile;
 		uint16 new_data;
+		byte pbs;
 
 		//checks if the owner is town then decrease town rating by RATING_TUNNEL_BRIDGE_DOWN_STEP until
 		// you have a "Poor" (0) town rating
@@ -778,6 +781,7 @@
 
 		do {
 			m5 = _map5[c];
+			pbs = PBSTileReserved(c);
 
 			if (m5 & 0x40) {
 				if (m5 & 0x20) {
@@ -791,6 +795,9 @@
 				SetTileType(c, new_data >> 12);
 				_map5[c] = (byte)new_data;
 				_map2[c] = 0;
+				_map3_hi[c] &= 0x0F;
+				if (direction ? HASBIT(pbs,0) : HASBIT(pbs,1))
+					PBSReserveTrack(c, direction ? 0 : 1);
 
 				MarkTileDirtyByTile(c);
 
@@ -1144,6 +1151,16 @@
 			}
 		}
 	}
+
+	if (_debug_pbs_level >= 1) {
+		byte pbs = PBSTileReserved(ti->tile);
+		if (pbs & TRACK_BIT_DIAG1) DrawGroundSprite(0x3ED | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_DIAG2) DrawGroundSprite(0x3EE | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(0x3EF | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(0x3F0 | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_LEFT)  DrawGroundSprite(0x3F2 | PALETTE_CRASH);
+		if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(0x3F1 | PALETTE_CRASH);
+	}
 }
 
 static uint GetSlopeZ_TunnelBridge(TileInfo *ti) {
@@ -1426,6 +1443,8 @@
 					return 0;
 				}
 				if (fc == _tunnel_fractcoord_2[dir]) {
+					if (v->next == NULL)
+						PBSClearTrack(v->tile, FIND_FIRST_BIT(v->u.rail.track));
 					v->tile = tile;
 					v->u.rail.track = 0x40;
 					v->vehstatus |= VS_HIDDEN;
--- a/variables.h
+++ b/variables.h
@@ -106,6 +106,7 @@
 	bool modified_catchment;	//different-size catchment areas
 	bool vehicle_speed;			// show vehicle speed
 	bool build_on_slopes;		// allow building on slopes
+	bool auto_pbs_placement;// automatic pbs signal placement
 	bool mammoth_trains;		// allow very long trains
 	bool join_stations;			// allow joining of train stations
 	bool full_load_any;			// new full load calculation, any cargo must be full
--- a/vehicle.c
+++ b/vehicle.c
@@ -1949,8 +1949,9 @@
 	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,flags), SLE_UINT8, 2, 255),
 	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,days_since_order_progr), SLE_UINT16, 2, 255),
 
-	// reserve extra space in savegame here. (currently 13 bytes)
-	SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 13, 2, 255),
+	SLE_CONDVARX(offsetof(Vehicle,u)+offsetof(VehicleRail,pbs_status), SLE_UINT8, 2, 255),
+	// reserve extra space in savegame here. (currently 12 bytes)
+	SLE_CONDARR(NullStruct,null,SLE_FILE_U8 | SLE_VAR_NULL, 12, 2, 255),
 
 	SLE_END()
 };
--- a/vehicle.h
+++ b/vehicle.h
@@ -68,6 +68,8 @@
 	byte railtype;
 
 	byte flags;
+
+	byte pbs_status;
 } VehicleRail;
 
 enum {