(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.
authorhackykid
Mon, 04 Jul 2005 14:58:55 +0000
changeset 2008 c9d6585c96c8
parent 2007 b3bdf698ee26
child 2009 e5981b336208
(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.
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
--- a/Makefile	Mon Jul 04 10:01:06 2005 +0000
+++ b/Makefile	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/ai_pathfinder.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/aystar.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/aystar.h	Mon Jul 04 14:58:55 2005 +0000
@@ -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.
Binary file data/nsignalsw.grf has changed
Binary file data/signalsw.grf has changed
--- a/debug.c	Mon Jul 04 10:01:06 2005 +0000
+++ b/debug.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/debug.h	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/docs/landscape.html	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/lang/english.txt	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/npf.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/npf.h	Mon Jul 04 14:58:55 2005 +0000
@@ -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. */
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pbs.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pbs.h	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/rail.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/rail.h	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/rail_cmd.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/road_cmd.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/roadveh_cmd.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/settings.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/settings_gui.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/ship_cmd.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/station_cmd.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/table/files.h	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/table/sprites.h	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/train_cmd.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/tunnelbridge_cmd.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/variables.h	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/vehicle.c	Mon Jul 04 14:58:55 2005 +0000
@@ -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	Mon Jul 04 10:01:06 2005 +0000
+++ b/vehicle.h	Mon Jul 04 14:58:55 2005 +0000
@@ -68,6 +68,8 @@
 	byte railtype;
 
 	byte flags;
+
+	byte pbs_status;
 } VehicleRail;
 
 enum {