diff options
author | hackykid <hackykid@openttd.org> | 2005-07-04 14:58:55 +0000 |
---|---|---|
committer | hackykid <hackykid@openttd.org> | 2005-07-04 14:58:55 +0000 |
commit | 60ddaf95f0b52a09fad18ea05b2b9db4509d0511 (patch) | |
tree | c0f8f7ba8535ff0f5e9fd8581b36c737ac10e0b1 | |
parent | b872cf7f7bd6fdcc8b09903130cb961c004ad5de (diff) | |
download | openttd-60ddaf95f0b52a09fad18ea05b2b9db4509d0511.tar.xz |
(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.
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | ai_pathfinder.c | 2 | ||||
-rw-r--r-- | aystar.c | 4 | ||||
-rw-r--r-- | aystar.h | 6 | ||||
-rw-r--r-- | data/nsignalsw.grf | bin | 0 -> 50012 bytes | |||
-rw-r--r-- | data/signalsw.grf | bin | 22939 -> 0 bytes | |||
-rw-r--r-- | debug.c | 2 | ||||
-rw-r--r-- | debug.h | 1 | ||||
-rw-r--r-- | docs/landscape.html | 22 | ||||
-rw-r--r-- | lang/english.txt | 1 | ||||
-rw-r--r-- | npf.c | 234 | ||||
-rw-r--r-- | npf.h | 14 | ||||
-rw-r--r-- | pbs.c | 291 | ||||
-rw-r--r-- | pbs.h | 82 | ||||
-rw-r--r-- | rail.c | 9 | ||||
-rw-r--r-- | rail.h | 25 | ||||
-rw-r--r-- | rail_cmd.c | 137 | ||||
-rw-r--r-- | road_cmd.c | 20 | ||||
-rw-r--r-- | roadveh_cmd.c | 4 | ||||
-rw-r--r-- | settings.c | 1 | ||||
-rw-r--r-- | settings_gui.c | 1 | ||||
-rw-r--r-- | ship_cmd.c | 2 | ||||
-rw-r--r-- | station_cmd.c | 22 | ||||
-rw-r--r-- | table/files.h | 4 | ||||
-rw-r--r-- | table/sprites.h | 2 | ||||
-rw-r--r-- | train_cmd.c | 218 | ||||
-rw-r--r-- | tunnelbridge_cmd.c | 19 | ||||
-rw-r--r-- | variables.h | 1 | ||||
-rw-r--r-- | vehicle.c | 5 | ||||
-rw-r--r-- | vehicle.h | 2 |
30 files changed, 1053 insertions, 79 deletions
@@ -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 @@ -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. diff --git a/data/nsignalsw.grf b/data/nsignalsw.grf Binary files differnew file mode 100644 index 000000000..03dee7863 --- /dev/null +++ b/data/nsignalsw.grf diff --git a/data/signalsw.grf b/data/signalsw.grf Binary files differdeleted file mode 100644 index ca78876d0..000000000 --- a/data/signalsw.grf +++ /dev/null @@ -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 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> </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> </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> </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 @@ -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 = ¤t->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(¤t->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(¤t->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(¤t->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 = ⌖ @@ -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; @@ -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. */ @@ -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; +} @@ -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 @@ -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 @@ -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 { |