diff options
-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 { |