summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile1
-rw-r--r--ai_pathfinder.c2
-rw-r--r--aystar.c4
-rw-r--r--aystar.h6
-rw-r--r--data/nsignalsw.grfbin0 -> 50012 bytes
-rw-r--r--data/signalsw.grfbin22939 -> 0 bytes
-rw-r--r--debug.c2
-rw-r--r--debug.h1
-rw-r--r--docs/landscape.html22
-rw-r--r--lang/english.txt1
-rw-r--r--npf.c234
-rw-r--r--npf.h14
-rw-r--r--pbs.c291
-rw-r--r--pbs.h82
-rw-r--r--rail.c9
-rw-r--r--rail.h25
-rw-r--r--rail_cmd.c137
-rw-r--r--road_cmd.c20
-rw-r--r--roadveh_cmd.c4
-rw-r--r--settings.c1
-rw-r--r--settings_gui.c1
-rw-r--r--ship_cmd.c2
-rw-r--r--station_cmd.c22
-rw-r--r--table/files.h4
-rw-r--r--table/sprites.h2
-rw-r--r--train_cmd.c218
-rw-r--r--tunnelbridge_cmd.c19
-rw-r--r--variables.h1
-rw-r--r--vehicle.c5
-rw-r--r--vehicle.h2
30 files changed, 1053 insertions, 79 deletions
diff --git a/Makefile b/Makefile
index be76e3d74..c1e20799c 100644
--- a/Makefile
+++ b/Makefile
@@ -651,6 +651,7 @@ C_SOURCES += order_cmd.c
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
diff --git a/ai_pathfinder.c b/ai_pathfinder.c
index a3ddda982..f26cead73 100644
--- a/ai_pathfinder.c
+++ b/ai_pathfinder.c
@@ -97,6 +97,8 @@ AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFin
result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode;
result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours;
+ result->BeforeExit = NULL;
+
result->free = AyStar_AiPathFinder_Free;
// Set some information
diff --git a/aystar.c b/aystar.c
index e2f9109fe..785746185 100644
--- a/aystar.c
+++ b/aystar.c
@@ -230,6 +230,10 @@ int AyStarMain_Main(AyStar *aystar) {
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);
diff --git a/aystar.h b/aystar.h
index adda33aba..52c93e911 100644
--- a/aystar.h
+++ b/aystar.h
@@ -96,6 +96,11 @@ typedef void AyStar_GetNeighbours(AyStar *aystar, OpenListNode *current);
*/
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 @@ struct AyStar {
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.
diff --git a/data/nsignalsw.grf b/data/nsignalsw.grf
new file mode 100644
index 000000000..03dee7863
--- /dev/null
+++ b/data/nsignalsw.grf
Binary files differ
diff --git a/data/signalsw.grf b/data/signalsw.grf
deleted file mode 100644
index ca78876d0..000000000
--- a/data/signalsw.grf
+++ /dev/null
Binary files differ
diff --git a/debug.c b/debug.c
index 3230c96d7..fe36348fc 100644
--- a/debug.c
+++ b/debug.c
@@ -14,6 +14,7 @@ int _debug_ms_level;
int _debug_net_level;
int _debug_spritecache_level;
int _debug_oldloader_level;
+int _debug_pbs_level;
int _debug_npf_level;
@@ -44,6 +45,7 @@ typedef struct DebugLevel {
DEBUG_LEVEL(net),
DEBUG_LEVEL(spritecache),
DEBUG_LEVEL(oldloader),
+ DEBUG_LEVEL(pbs),
DEBUG_LEVEL(npf)
};
#undef DEBUG_LEVEL
diff --git a/debug.h b/debug.h
index 44237feca..320e1d5de 100644
--- 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
diff --git a/docs/landscape.html b/docs/landscape.html
index de298b09d..7bbed0a5e 100644
--- a/docs/landscape.html
+++ b/docs/landscape.html
@@ -93,11 +93,12 @@ map5 bit 7 clear: railway track
<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 @@ map5 bit 7 clear: railway track
<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 @@ map5 bits 7 and 6 set: railway depot / checkpoints
<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 @@ map5 bit 4 set, bits 7..5 clear: level crossing
<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 @@ exit towards: <tt>47</tt> - NE, <tt>48</tt> - SE, <tt>49</tt> - SW, <tt>4A</tt>
<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 @@ map5 bits 7..4 clear: tunnel entrance/exit
<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>
diff --git a/lang/english.txt b/lang/english.txt
index b97214009..81cb29d35 100644
--- a/lang/english.txt
+++ b/lang/english.txt
@@ -1073,6 +1073,7 @@ STR_CONFIG_PATCHES_ENDING_DATE :{LTBLUE}End gam
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
diff --git a/npf.c b/npf.c
index bbc992fed..bdb26af52 100644
--- a/npf.c
+++ b/npf.c
@@ -21,6 +21,57 @@ static const uint _trackdir_length[TRACKDIR_END] = {
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 @@ static TileIndex CalcClosestStationTile(StationID station, TileIndex tile)
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 @@ static int32 NPFCalcStationOrTileHeuristic(AyStar* as, AyStarNode* current, Open
/* 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 @@ static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare
/* 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 @@ static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare
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 @@ static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare
/* 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 @@ static int32 NPFRailPathCost(AyStar* as, AyStarNode* current, OpenListNode* pare
//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 @@ static int32 NPFFindStationOrTile(AyStar* as, OpenListNode *current)
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 @@ static void NPFSaveTargetData(AyStar* as, OpenListNode* current)
ftd->best_path_dist = current->g;
ftd->best_bird_dist = 0;
ftd->node = current->path.node;
+ ftd->path = current->path;
}
/**
@@ -478,6 +661,8 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current)
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 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current)
} 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 @@ static void NPFFollowTrack(AyStar* aystar, OpenListNode* current)
* 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 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start
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 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start
_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 @@ static NPFFoundTargetData NPFRouteInternal(AyStarNode* start1, AyStarNode* start
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 @@ NPFFoundTargetData NPFRouteToStationOrTileTwoWay(TileIndex tile1, Trackdir track
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 @@ NPFFoundTargetData NPFRouteToDepotBreadthFirstTwoWay(TileIndex tile1, Trackdir t
/* 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 @@ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir,
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 @@ NPFFoundTargetData NPFRouteToDepotTrialError(TileIndex tile, Trackdir trackdir,
/* 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;
diff --git a/npf.h b/npf.h
index 847a36b4e..0ded7fcfa 100644
--- 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 @@ enum { /* Indices into AyStar.userdata[] */
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 @@ typedef struct NPFFoundTargetData { /* Meant to be stored in AyStar.userpath */
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 @@ typedef struct NPFFoundTargetData { /* Meant to be stored in AyStar.userpath */
/* 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. */
diff --git a/pbs.c b/pbs.c
new file mode 100644
index 000000000..fdb47854d
--- /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;
+}
diff --git a/pbs.h b/pbs.h
new file mode 100644
index 000000000..398bd73d9
--- /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
diff --git a/rail.c b/rail.c
index 670b8345b..ecd6be937 100644
--- a/rail.c
+++ b/rail.c
@@ -75,6 +75,15 @@ const Trackdir _track_exitdir_to_trackdir[][DIAGDIR_END] = {
{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},
diff --git a/rail.h b/rail.h
index 323e0f983..d8611787e 100644
--- a/rail.h
+++ b/rail.h
@@ -43,8 +43,9 @@ typedef enum SignalTypes {
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 @@ typedef enum SignalStates {
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 @@ static inline Trackdir TrackExitdirToTrackdir(Track track, DiagDirection diagdir
}
/**
+ * 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 @@ static inline DiagDirection ReverseDiagdir(DiagDirection diagdir) {
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); }
diff --git a/rail_cmd.c b/rail_cmd.c
index a1340428b..796ba88fd 100644
--- 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 @@ int32 CmdBuildSingleSignal(int x, int y, uint32 flags, uint32 p1, uint32 p2)
_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 @@ static const SpriteID _signal_base_sprites[32] = {
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 @@ static const SpriteID _signal_base_sprites[32] = {
0x1333,
0x1343,
- 0x0, //PBS place, semaphore
- 0x0, //reserved for future use
- 0x0, //reserved for future use
- 0x0, //reserved for future use
-
- 0x13C6,
- 0x13D6,
- 0x13E6,
- 0x13F6,
-
- 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?)
+
+ // semaphores
+ 0x1446,
+ 0x1456,
+ 0x1466,
+ 0x1476,
+
+ // 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 @@ static void DrawTile_Track(TileInfo *ti)
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 @@ static void DrawTile_Track(TileInfo *ti)
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 @@ void DrawTrainDepotSprite(int x, int y, int image, int railtype)
}
}
-#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 @@ typedef struct SetSignalsData {
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 @@ static bool SetSignalsEnumProc(TileIndex tile, SetSignalsData *ssd, int track, u
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 @@ static void ChangeSignalStates(SetSignalsData *ssd)
// 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 @@ bool UpdateSignalsOnSegment(TileIndex tile, byte direction)
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 @@ static uint32 VehicleEnter_Track(Vehicle *v, TileIndex tile, int x, int y)
} 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;
diff --git a/road_cmd.c b/road_cmd.c
index 2b954ed23..c44b38d8a 100644
--- 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 @@ int32 CmdRemoveRoad(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdRemoveRoad(int x, int y, uint32 flags, uint32 p1, uint32 p2)
_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 @@ int32 CmdBuildRoad(int x, int y, uint32 flags, uint32 p1, uint32 p2)
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 @@ int32 CmdBuildRoad(int x, int y, uint32 flags, uint32 p1, uint32 p2)
_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 @@ static void DrawTile_Road(TileInfo *ti)
}
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;
diff --git a/roadveh_cmd.c b/roadveh_cmd.c
index 1796d1a32..22f3cf0a4 100644
--- a/roadveh_cmd.c
+++ b/roadveh_cmd.c
@@ -1086,7 +1086,7 @@ static int RoadFindPathToDest(Vehicle *v, TileIndex tile, int enterdir)
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 @@ static uint RoadFindPathToStation(const Vehicle *v, TileIndex tile)
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 {
diff --git a/settings.c b/settings.c
index 03d0abbe4..f6dfdf3df 100644
--- a/settings.c
+++ b/settings.c
@@ -845,6 +845,7 @@ static const SettingDesc patch_player_settings[] = {
// 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},
diff --git a/settings_gui.c b/settings_gui.c
index 4583e6e06..14458be3d 100644
--- a/settings_gui.c
+++ b/settings_gui.c
@@ -669,6 +669,7 @@ static const PatchEntry _patches_construction[] = {
{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},
};
diff --git a/ship_cmd.c b/ship_cmd.c
index 08c652851..78f7959b0 100644
--- a/ship_cmd.c
+++ b/ship_cmd.c
@@ -565,7 +565,7 @@ static int ChooseShipTrack(Vehicle *v, TileIndex tile, int enterdir, uint tracks
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
diff --git a/station_cmd.c b/station_cmd.c
index 9e5e6ffc4..edc9ccdfa 100644
--- 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 @@ static void DrawTile_Station(TileInfo *ti)
const DrawTileSeqStruct *dtss;
const DrawTileSprites *t = NULL;
byte railtype = _map3_lo[ti->tile] & 0xF;
+ int type_offset;
uint32 relocation = 0;
{
@@ -2154,15 +2156,27 @@ static void DrawTile_Station(TileInfo *ti)
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 {
diff --git a/table/files.h b/table/files.h
index 7b3b91c89..7b284295a 100644
--- a/table/files.h
+++ b/table/files.h
@@ -26,7 +26,7 @@ FileList files_dos = {
{ "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 @@ FileList files_win = {
{
{ "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 } }
},
diff --git a/table/sprites.h b/table/sprites.h
index 91b350de6..0c46dcdcc 100644
--- a/table/sprites.h
+++ b/table/sprites.h
@@ -42,7 +42,7 @@ enum Sprites {
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
diff --git a/train_cmd.c b/train_cmd.c
index d62e8251b..d8ac7a051 100644
--- 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 @@ static void AdvanceWagons(Vehicle *v, bool before)
}
}
+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 @@ static bool CheckTrainStayInDepot(Vehicle *v)
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 @@ static byte ChooseTrainTrack(Vehicle *v, TileIndex tile, int enterdir, TrackdirB
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 @@ static byte ChooseTrainTrack(Vehicle *v, TileIndex tile, int enterdir, TrackdirB
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 @@ static bool CheckReverseTrain(Vehicle *v)
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 @@ static void TrainController(Vehicle *v)
} 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 @@ static void TrainController(Vehicle *v)
}
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 @@ static void TrainController(Vehicle *v)
/* 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 @@ static void TrainController(Vehicle *v)
}
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 @@ static void DeleteLastWagon(Vehicle *v)
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 @@ static bool TrainCheckIfLineEnds(Vehicle *v)
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 @@ static bool TrainCheckIfLineEnds(Vehicle *v)
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 @@ static bool TrainCheckIfLineEnds(Vehicle *v)
// 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 @@ static bool TrainCheckIfLineEnds(Vehicle *v)
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 @@ static void CheckIfTrainNeedsService(Vehicle *v)
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;
diff --git a/tunnelbridge_cmd.c b/tunnelbridge_cmd.c
index 36924686c..0ae9bb5c6 100644
--- 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 @@ static int32 DoClearBridge(TileIndex tile, uint32 flags)
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 @@ static int32 DoClearBridge(TileIndex tile, uint32 flags)
do {
m5 = _map5[c];
+ pbs = PBSTileReserved(c);
if (m5 & 0x40) {
if (m5 & 0x20) {
@@ -791,6 +795,9 @@ static int32 DoClearBridge(TileIndex tile, uint32 flags)
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 @@ static void DrawTile_TunnelBridge(TileInfo *ti)
}
}
}
+
+ 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 @@ static uint32 VehicleEnter_TunnelBridge(Vehicle *v, TileIndex tile, int x, int y
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;
diff --git a/variables.h b/variables.h
index 4b6d38245..5d505af41 100644
--- a/variables.h
+++ b/variables.h
@@ -106,6 +106,7 @@ typedef struct Patches {
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
diff --git a/vehicle.c b/vehicle.c
index 5e5d4de8c..6a526f577 100644
--- a/vehicle.c
+++ b/vehicle.c
@@ -1949,8 +1949,9 @@ static const SaveLoad _train_desc[] = {
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()
};
diff --git a/vehicle.h b/vehicle.h
index 37aaf6de2..a413426af 100644
--- a/vehicle.h
+++ b/vehicle.h
@@ -68,6 +68,8 @@ typedef struct VehicleRail {
byte railtype;
byte flags;
+
+ byte pbs_status;
} VehicleRail;
enum {