(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.
replace/41b28d7194a279bdc17475d4fbe2ea6ec885a466
hackykid 19 years ago
parent b872cf7f7b
commit 60ddaf95f0

@ -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

@ -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

@ -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);

@ -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.

Binary file not shown.

Binary file not shown.

@ -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

@ -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

@ -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>

@ -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

234
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;

14
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. */

291
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;
}

82
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

@ -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},

@ -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
@ -316,6 +322,15 @@ static inline Trackdir TrackExitdirToTrackdir(Track track, DiagDirection diagdir
return _track_exitdir_to_trackdir[track][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); }

@ -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;

@ -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;

@ -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 {

@ -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},

@ -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},
};

@ -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

@ -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 {

@ -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 } }
},

@ -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

@ -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;

@ -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;

@ -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

@ -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()
};

@ -68,6 +68,8 @@ typedef struct VehicleRail {
byte railtype;
byte flags;
byte pbs_status;
} VehicleRail;
enum {

Loading…
Cancel
Save