diff --git a/src/pathfinder/pathfinder_func.h b/src/pathfinder/pathfinder_func.h index 444b100ce7..2271d60490 100644 --- a/src/pathfinder/pathfinder_func.h +++ b/src/pathfinder/pathfinder_func.h @@ -12,6 +12,40 @@ #include "../tile_cmd.h" #include "../waypoint_base.h" +#include "../ship.h" + +/** + * Creates a list containing possible destination tiles for a ship. + * @param v The ship + * return Vector of tiles filled with all possible destinations. + */ +inline std::vector GetShipDestinationTiles(const Ship *v) +{ + std::vector dest_tiles; + + if (v->current_order.IsType(OT_GOTO_STATION)) { + const StationID station = v->current_order.GetDestination(); + + const BaseStation *st = BaseStation::Get(station); + TileArea ta; + st->GetTileArea(&ta, STATION_DOCK); + /* If the dock station is (temporarily) not present, use the station sign to drive near the station. */ + if (ta.tile == INVALID_TILE) { + dest_tiles.push_back(st->xy); + } else { + for (const TileIndex &docking_tile : ta) { + if (!IsDockingTile(docking_tile) || !IsShipDestinationTile(docking_tile, station)) continue; + dest_tiles.push_back(docking_tile); + } + } + } else { + dest_tiles.push_back(v->dest_tile); + } + + assert(!dest_tiles.empty()); + + return dest_tiles; +} /** * Calculates the tile of given station that is closest to a given tile diff --git a/src/pathfinder/yapf/yapf_ship.cpp b/src/pathfinder/yapf/yapf_ship.cpp index 57d5e9d877..6ede829ebc 100644 --- a/src/pathfinder/yapf/yapf_ship.cpp +++ b/src/pathfinder/yapf/yapf_ship.cpp @@ -34,25 +34,21 @@ public: typedef typename Node::Key Key; ///< key to hash tables. protected: - TileIndex m_destTile; - TrackdirBits m_destTrackdirs; - StationID m_destStation; + std::span m_destTiles; + StationID m_destStation; bool m_has_intermediate_dest = false; TileIndex m_intermediate_dest_tile; WaterRegionPatchDesc m_intermediate_dest_region_patch; public: - void SetDestination(const Ship *v) + void SetDestination(const Ship *v, const std::span dest_tiles) { + m_destTiles = dest_tiles; if (v->current_order.IsType(OT_GOTO_STATION)) { m_destStation = v->current_order.GetDestination(); - m_destTile = CalcClosestStationTile(m_destStation, v->tile, STATION_DOCK); - m_destTrackdirs = INVALID_TRACKDIR_BIT; } else { m_destStation = INVALID_STATION; - m_destTile = v->dest_tile; - m_destTrackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0)); } } @@ -74,11 +70,8 @@ public: /** Called by YAPF to detect if node ends in the desired destination. */ inline bool PfDetectDestination(Node &n) { - return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td); - } + const TileIndex tile = n.m_segment_last_tile; - inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir) - { if (m_has_intermediate_dest) { /* GetWaterRegionInfo is much faster than GetWaterRegionPatchInfo so we try that first. */ if (GetWaterRegionInfo(tile) != m_intermediate_dest_region_patch) return false; @@ -87,23 +80,14 @@ public: if (m_destStation != INVALID_STATION) return IsDockingTile(tile) && IsShipDestinationTile(tile, m_destStation); - return tile == m_destTile && ((m_destTrackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE); + assert(m_destTiles.size() == 1); + return tile == m_destTiles.front(); } - /** - * Called by YAPF to calculate cost estimate. Calculates distance to the destination - * adds it to the actual cost from origin and stores the sum to the Node::m_estimate. - */ - inline bool PfCalcEstimate(Node &n) + static inline int CalcEstimate(Node &n, TileIndex destination_tile) { - const TileIndex destination_tile = m_has_intermediate_dest ? m_intermediate_dest_tile : m_destTile; - static const int dg_dir_to_x_offs[] = { -1, 0, 1, 0 }; static const int dg_dir_to_y_offs[] = { 0, 1, 0, -1 }; - if (PfDetectDestination(n)) { - n.m_estimate = n.m_cost; - return true; - } TileIndex tile = n.m_segment_last_tile; DiagDirection exitdir = TrackdirToExitdir(n.m_segment_last_td); @@ -116,8 +100,33 @@ public: int dmin = std::min(dx, dy); int dxy = abs(dx - dy); int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2); - n.m_estimate = n.m_cost + d; - assert(n.m_estimate >= n.m_parent->m_estimate); + int estimate = n.m_cost + d; + assert(estimate >= n.m_parent->m_estimate); + return estimate; + } + + /** + * Called by YAPF to calculate cost estimate. Calculates distance to the destination + * adds it to the actual cost from origin and stores the sum to the Node::m_estimate. + */ + inline bool PfCalcEstimate(Node &n) + { + if (PfDetectDestination(n)) { + n.m_estimate = n.m_cost; + return true; + } + + int shortest_estimate = std::numeric_limits::max(); + if (m_has_intermediate_dest) { + shortest_estimate = CalcEstimate(n, m_intermediate_dest_tile); + } else { + for (const TileIndex &destination_tile : m_destTiles) { + int estimate = CalcEstimate(n, destination_tile); + if (estimate < shortest_estimate) shortest_estimate = estimate; + } + } + + n.m_estimate = shortest_estimate; return true; } }; @@ -210,10 +219,10 @@ public: return result; } - static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs, + static Trackdir ChooseShipTrack(const Ship *v, TileIndex tile, TrackdirBits forward_dirs, TrackdirBits reverse_dirs, const std::span dest_tiles, bool &path_found, ShipPathCache &path_cache, Trackdir &best_origin_dir) { - const std::vector high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1); + const std::vector high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1, dest_tiles); if (high_level_path.empty()) { path_found = false; /* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */ @@ -228,7 +237,7 @@ public: /* Set origin and destination nodes */ pf.SetOrigin(v->tile, forward_dirs | reverse_dirs); - pf.SetDestination(v); + pf.SetDestination(v, dest_tiles); const bool is_intermediate_destination = static_cast(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1; if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back()); @@ -296,9 +305,10 @@ public: * Called when leaving depot. * @param v Ship. * @param trackdir [out] the best of all possible reversed trackdirs. + * @param dest_tiles List of destination tiles. * @return true if the reverse direction is better. */ - static bool CheckShipReverse(const Ship *v, Trackdir *trackdir) + static bool CheckShipReverse(const Ship *v, Trackdir *trackdir, const std::span dest_tiles) { bool path_found = false; ShipPathCache dummy_cache; @@ -309,13 +319,13 @@ public: const Trackdir reverse_dir = ReverseTrackdir(v->GetVehicleTrackdir()); const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir()); const TrackdirBits reverse_dirs = TrackdirToTrackdirBits(reverse_dir); - (void)ChooseShipTrack(v, v->tile, forward_dirs, reverse_dirs, path_found, dummy_cache, best_origin_dir); + (void)ChooseShipTrack(v, v->tile, forward_dirs, reverse_dirs, dest_tiles, path_found, dummy_cache, best_origin_dir); return path_found && best_origin_dir == reverse_dir; } else { /* This gets called when a ship suddenly can't move forward, e.g. due to terraforming. */ const DiagDirection entry = ReverseDiagDir(VehicleExitDir(v->direction, v->state)); const TrackdirBits reverse_dirs = DiagdirReachesTrackdirs(entry) & TrackStatusToTrackdirBits(GetTileTrackStatus(v->tile, TRANSPORT_WATER, 0, entry)); - (void)ChooseShipTrack(v, v->tile, TRACKDIR_BIT_NONE, reverse_dirs, path_found, dummy_cache, best_origin_dir); + (void)ChooseShipTrack(v, v->tile, TRACKDIR_BIT_NONE, reverse_dirs, dest_tiles, path_found, dummy_cache, best_origin_dir); *trackdir = path_found && best_origin_dir != INVALID_TRACKDIR ? best_origin_dir : GetRandomTrackdir(reverse_dirs); return true; } @@ -426,13 +436,15 @@ struct CYapfShip : CYapfT dest_tiles = GetShipDestinationTiles(v); Trackdir best_origin_dir = INVALID_TRACKDIR; const TrackdirBits origin_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir()); - const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, path_found, path_cache, best_origin_dir); + const Trackdir td_ret = CYapfShip::ChooseShipTrack(v, tile, origin_dirs, TRACKDIR_BIT_NONE, dest_tiles, path_found, path_cache, best_origin_dir); return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK; } bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir) { - return CYapfShip::CheckShipReverse(v, trackdir); + std::vector dest_tiles = GetShipDestinationTiles(v); + return CYapfShip::CheckShipReverse(v, trackdir, dest_tiles); } diff --git a/src/pathfinder/yapf/yapf_ship_regions.cpp b/src/pathfinder/yapf/yapf_ship_regions.cpp index fa54c2ec0e..719d137343 100644 --- a/src/pathfinder/yapf/yapf_ship_regions.cpp +++ b/src/pathfinder/yapf/yapf_ship_regions.cpp @@ -188,7 +188,7 @@ public: inline char TransportTypeChar() const { return '^'; } - static std::vector FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length) + static std::vector FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span dest_tiles) { const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(start_tile); @@ -197,18 +197,7 @@ public: Tpf pf(std::min(static_cast(Map::Size() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES)); pf.SetDestination(start_water_region_patch); - if (v->current_order.IsType(OT_GOTO_STATION)) { - DestinationID station_id = v->current_order.GetDestination(); - const BaseStation *station = BaseStation::Get(station_id); - TileArea tile_area; - station->GetTileArea(&tile_area, STATION_DOCK); - for (const auto &tile : tile_area) { - if (IsDockingTile(tile) && IsShipDestinationTile(tile, station_id)) { - pf.AddOrigin(GetWaterRegionPatchInfo(tile)); - } - } - } else { - TileIndex tile = v->dest_tile; + for (const TileIndex &tile : dest_tiles) { pf.AddOrigin(GetWaterRegionPatchInfo(tile)); } @@ -305,9 +294,10 @@ struct CYapfRegionWater : CYapfT YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length) +std::vector YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span dest_tiles) { - return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length); + return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length, dest_tiles); } diff --git a/src/pathfinder/yapf/yapf_ship_regions.h b/src/pathfinder/yapf/yapf_ship_regions.h index 8b75773cc8..a5023e6dc8 100644 --- a/src/pathfinder/yapf/yapf_ship_regions.h +++ b/src/pathfinder/yapf/yapf_ship_regions.h @@ -16,6 +16,6 @@ struct Ship; -std::vector YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length); +std::vector YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span dest_tiles); #endif /* YAPF_SHIP_REGIONS_H */