diff --git a/src/pathfinder/pathfinder_func.h b/src/pathfinder/pathfinder_func.h index 444b100ce7..3297c591d0 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().ToStationID(); + + const BaseStation *st = BaseStation::Get(station); + TileArea ta; + st->GetTileArea(&ta, StationType::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 a47df5abb3..a13f18c813 100644 --- a/src/pathfinder/yapf/yapf_ship.cpp +++ b/src/pathfinder/yapf/yapf_ship.cpp @@ -33,8 +33,7 @@ public: typedef typename Node::Key Key; ///< key to hash tables. protected: - TileIndex dest_tile; - TrackdirBits dest_trackdirs; + std::span dest_tiles; StationID dest_station; bool has_intermediate_dest = false; @@ -42,16 +41,13 @@ protected: WaterRegionPatchDesc intermediate_dest_region_patch; public: - void SetDestination(const Ship *v) + void SetDestination(const Ship *v, const std::span destination_tiles) { + this->dest_tiles = destination_tiles; if (v->current_order.IsType(OT_GOTO_STATION)) { this->dest_station = v->current_order.GetDestination().ToStationID(); - this->dest_tile = CalcClosestStationTile(this->dest_station, v->tile, StationType::Dock); - this->dest_trackdirs = INVALID_TRACKDIR_BIT; } else { this->dest_station = StationID::Invalid(); - this->dest_tile = v->dest_tile; - this->dest_trackdirs = TrackStatusToTrackdirBits(GetTileTrackStatus(v->dest_tile, TRANSPORT_WATER, 0)); } } @@ -73,11 +69,8 @@ public: /** Called by YAPF to detect if node ends in the desired destination. */ inline bool PfDetectDestination(Node &n) { - return this->PfDetectDestinationTile(n.segment_last_tile, n.segment_last_td); - } + const TileIndex tile = n.segment_last_tile; - inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir) - { if (this->has_intermediate_dest) { /* GetWaterRegionInfo is much faster than GetWaterRegionPatchInfo so we try that first. */ if (GetWaterRegionInfo(tile) != this->intermediate_dest_region_patch) return false; @@ -86,23 +79,14 @@ public: if (this->dest_station != StationID::Invalid()) return IsDockingTile(tile) && IsShipDestinationTile(tile, this->dest_station); - return tile == this->dest_tile && ((this->dest_trackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE); + assert(this->dest_tiles.size() == 1); + return tile == this->dest_tiles.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::estimate. - */ - inline bool PfCalcEstimate(Node &n) + static inline int CalcEstimate(Node &n, TileIndex destination_tile) { - const TileIndex destination_tile = this->has_intermediate_dest ? this->intermediate_dest_tile : this->dest_tile; - 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 (this->PfDetectDestination(n)) { - n.estimate = n.cost; - return true; - } TileIndex tile = n.segment_last_tile; DiagDirection exitdir = TrackdirToExitdir(n.segment_last_td); @@ -115,8 +99,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.estimate = n.cost + d; - assert(n.estimate >= n.parent->estimate); + int estimate = n.cost + d; + assert(estimate >= n.parent->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::estimate. + */ + inline bool PfCalcEstimate(Node &n) + { + if (this->PfDetectDestination(n)) { + n.estimate = n.cost; + return true; + } + + int shortest_estimate = std::numeric_limits::max(); + if (this->has_intermediate_dest) { + shortest_estimate = this->CalcEstimate(n, this->intermediate_dest_tile); + } else { + for (const TileIndex &destination_tile : this->dest_tiles) { + int estimate = this->CalcEstimate(n, destination_tile); + if (estimate < shortest_estimate) shortest_estimate = estimate; + } + } + + n.estimate = shortest_estimate; return true; } }; @@ -211,10 +220,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. */ @@ -229,7 +238,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()); @@ -297,9 +306,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; @@ -310,13 +320,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; } @@ -420,13 +430,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 0a1a831a05..20d7bb6446 100644 --- a/src/pathfinder/yapf/yapf_ship_regions.cpp +++ b/src/pathfinder/yapf/yapf_ship_regions.cpp @@ -175,7 +175,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); @@ -184,18 +184,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)) { - StationID station_id = v->current_order.GetDestination().ToStationID(); - const BaseStation *station = BaseStation::Get(station_id); - TileArea tile_area; - station->GetTileArea(&tile_area, StationType::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)); } @@ -292,9 +281,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 dc488754b4..207865f179 100644 --- a/src/pathfinder/yapf/yapf_ship_regions.h +++ b/src/pathfinder/yapf/yapf_ship_regions.h @@ -15,6 +15,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 */