mirror of https://github.com/OpenTTD/OpenTTD
Merge 8031d68d01
into bd2430dc94
commit
16276085a6
|
@ -110,7 +110,7 @@ struct Aircraft final : public SpecializedVehicle<Aircraft, VEH_AIRCRAFT> {
|
||||||
uint Crash(bool flooded = false) override;
|
uint Crash(bool flooded = false) override;
|
||||||
TileIndex GetOrderStationLocation(StationID station) override;
|
TileIndex GetOrderStationLocation(StationID station) override;
|
||||||
TileIndex GetCargoTile() const override { return this->First()->tile; }
|
TileIndex GetCargoTile() const override { return this->First()->tile; }
|
||||||
ClosestDepot FindClosestDepot() override;
|
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the aircraft type is a normal flying device; eg
|
* Check if the aircraft type is a normal flying device; eg
|
||||||
|
|
|
@ -396,7 +396,7 @@ CommandCost CmdBuildAircraft(DoCommandFlags flags, TileIndex tile, const Engine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ClosestDepot Aircraft::FindClosestDepot()
|
ClosestDepot Aircraft::FindClosestDepot([[maybe_unused]] bool may_reverse)
|
||||||
{
|
{
|
||||||
const Station *st = GetTargetAirportIfValid(this);
|
const Station *st = GetTargetAirportIfValid(this);
|
||||||
/* If the station is not a valid airport or if it has no hangars */
|
/* If the station is not a valid airport or if it has no hangars */
|
||||||
|
|
|
@ -1896,10 +1896,11 @@ VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
|
||||||
* Update the vehicle's destination tile from an order.
|
* Update the vehicle's destination tile from an order.
|
||||||
* @param order the order the vehicle currently has
|
* @param order the order the vehicle currently has
|
||||||
* @param v the vehicle to update
|
* @param v the vehicle to update
|
||||||
|
* @param may_reverse Whether the vehicle is allowed to reverse when executing the updated order.
|
||||||
* @param conditional_depth the depth (amount of steps) to go with conditional orders. This to prevent infinite loops.
|
* @param conditional_depth the depth (amount of steps) to go with conditional orders. This to prevent infinite loops.
|
||||||
* @param pbs_look_ahead Whether we are forecasting orders for pbs reservations in advance. If true, the order indices must not be modified.
|
* @param pbs_look_ahead Whether we are forecasting orders for pbs reservations in advance. If true, the order indices must not be modified.
|
||||||
*/
|
*/
|
||||||
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool pbs_look_ahead)
|
bool UpdateOrderDest(Vehicle *v, const Order *order, bool may_reverse, int conditional_depth, bool pbs_look_ahead)
|
||||||
{
|
{
|
||||||
if (conditional_depth > v->GetNumOrders()) {
|
if (conditional_depth > v->GetNumOrders()) {
|
||||||
v->current_order.Free();
|
v->current_order.Free();
|
||||||
|
@ -1926,7 +1927,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool
|
||||||
if (v->dest_tile == 0 && TimerGameEconomy::date_fract != (v->index % Ticks::DAY_TICKS)) break;
|
if (v->dest_tile == 0 && TimerGameEconomy::date_fract != (v->index % Ticks::DAY_TICKS)) break;
|
||||||
|
|
||||||
/* We need to search for the nearest depot (hangar). */
|
/* We need to search for the nearest depot (hangar). */
|
||||||
ClosestDepot closest_depot = v->FindClosestDepot();
|
ClosestDepot closest_depot = v->FindClosestDepot(may_reverse);
|
||||||
|
|
||||||
if (closest_depot.found) {
|
if (closest_depot.found) {
|
||||||
/* PBS reservations cannot reverse */
|
/* PBS reservations cannot reverse */
|
||||||
|
@ -2018,7 +2019,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool
|
||||||
}
|
}
|
||||||
|
|
||||||
v->current_order = *order;
|
v->current_order = *order;
|
||||||
return UpdateOrderDest(v, order, conditional_depth + 1, pbs_look_ahead);
|
return UpdateOrderDest(v, order, may_reverse, conditional_depth + 1, pbs_look_ahead);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2116,7 +2117,7 @@ bool ProcessOrders(Vehicle *v)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return UpdateOrderDest(v, order) && may_reverse;
|
return UpdateOrderDest(v, order, may_reverse) && may_reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,7 +20,7 @@ void InvalidateVehicleOrder(const Vehicle *v, int data);
|
||||||
void CheckOrders(const Vehicle*);
|
void CheckOrders(const Vehicle*);
|
||||||
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false, bool reset_order_indices = true);
|
void DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false, bool reset_order_indices = true);
|
||||||
bool ProcessOrders(Vehicle *v);
|
bool ProcessOrders(Vehicle *v);
|
||||||
bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth = 0, bool pbs_look_ahead = false);
|
bool UpdateOrderDest(Vehicle *v, const Order *order, bool may_reverse = false, int conditional_depth = 0, bool pbs_look_ahead = false);
|
||||||
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v);
|
VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v);
|
||||||
uint GetOrderDistance(VehicleOrderID prev, VehicleOrderID cur, const Vehicle *v, int conditional_depth = 0);
|
uint GetOrderDistance(VehicleOrderID prev, VehicleOrderID cur, const Vehicle *v, int conditional_depth = 0);
|
||||||
|
|
||||||
|
|
|
@ -430,3 +430,83 @@ void PrintWaterRegionDebugInfo(TileIndex tile)
|
||||||
{
|
{
|
||||||
GetUpdatedWaterRegion(tile).PrintDebugInfo();
|
GetUpdatedWaterRegion(tile).PrintDebugInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the provided callback function on all tiles of the water patch of the region
|
||||||
|
* and returns true on the first tile that passes the callback test.
|
||||||
|
* @param callback The test function that will be called for the water patch.
|
||||||
|
* @param water_region_patch Water patch within the water region to test the callback.
|
||||||
|
* @return true if it passes the callback test, or false if the callback failed.
|
||||||
|
*/
|
||||||
|
bool TestTileInWaterRegionPatch(const WaterRegionPatchDesc &water_region_patch, TestTileIndexCallBack &callback)
|
||||||
|
{
|
||||||
|
const WaterRegion region = GetUpdatedWaterRegion(water_region_patch.x, water_region_patch.y);
|
||||||
|
|
||||||
|
/* Check if the region has a tile which passes the callback test. */
|
||||||
|
for (const TileIndex tile : region) {
|
||||||
|
if (region.GetLabel(tile) != water_region_patch.label || !callback(tile)) continue;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests the provided callback function on all tiles of the current water patch of the region, collects the
|
||||||
|
* tiles which passed the callback and returns the tile closest to the edge from where the region is entered from.
|
||||||
|
* @param high_level_path A span containing at least current and parent water patches.
|
||||||
|
* @param callback The test function that will be called for each tile in the water patch.
|
||||||
|
* @return The tile closest to the edge from where it came from that passed the callback test, or INVALID_TILE if no tile passed.
|
||||||
|
*/
|
||||||
|
TileIndex FindClosestEnteringTile(const std::span<WaterRegionPatchDesc> high_level_path, TestTileIndexCallBack &callback)
|
||||||
|
{
|
||||||
|
assert(high_level_path.size() > 1);
|
||||||
|
|
||||||
|
const WaterRegionPatchDesc ¤t_water_region_patch = high_level_path.back();
|
||||||
|
const WaterRegion current_region = GetUpdatedWaterRegion(current_water_region_patch.x, current_water_region_patch.y);
|
||||||
|
|
||||||
|
/* Check if the current region has a tile which passes the callback test. */
|
||||||
|
std::vector<TileIndex> tile_list;
|
||||||
|
for (const TileIndex tile : current_region) {
|
||||||
|
if (current_region.GetLabel(tile) != current_water_region_patch.label || !callback(tile)) continue;
|
||||||
|
|
||||||
|
/* We collect the tiles when we know which region we came from for further evaluation. */
|
||||||
|
tile_list.push_back(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there aren't any tiles that passed the callback, return with an invalid tile. */
|
||||||
|
if (tile_list.empty()) return INVALID_TILE;
|
||||||
|
|
||||||
|
/* If there's only one, just return it. */
|
||||||
|
if (tile_list.size() == 1) return tile_list.front();
|
||||||
|
|
||||||
|
const TileIndex top_tile = current_region.begin();
|
||||||
|
const TileIndex bot_tile = TileAddXY(top_tile, WATER_REGION_EDGE_LENGTH - 1, WATER_REGION_EDGE_LENGTH - 1);
|
||||||
|
|
||||||
|
/* Get the side from which the current region is entered from. */
|
||||||
|
const WaterRegionPatchDesc &parent_water_region_patch = high_level_path[high_level_path.size() - 2];
|
||||||
|
const WaterRegion parent_region = GetUpdatedWaterRegion(parent_water_region_patch.x, parent_water_region_patch.y);
|
||||||
|
const DiagDirection side = DiagdirBetweenTiles(top_tile, parent_region.begin());
|
||||||
|
|
||||||
|
/* Depending on the side, determine which corner tile to use to extract their x or y coordinates. */
|
||||||
|
const bool is_at_top = side == DIAGDIR_NE || side == DIAGDIR_NW;
|
||||||
|
const TileIndex edge_tile = is_at_top ? top_tile : bot_tile;
|
||||||
|
const bool is_axis_x = DiagDirToAxis(side) == AXIS_X;
|
||||||
|
const int x_or_y_edge = is_axis_x ? TileX(edge_tile) : TileY(edge_tile);
|
||||||
|
|
||||||
|
/* With more than one tile passing the callback, calculate the tile that is closest to the edge from whence it came. */
|
||||||
|
TileIndex best_tile = INVALID_TILE;
|
||||||
|
int best_dist = WATER_REGION_EDGE_LENGTH;
|
||||||
|
for (const TileIndex &tile : tile_list) {
|
||||||
|
const int x_or_y_tile = is_axis_x ? TileX(tile) : TileY(tile);
|
||||||
|
const int dist_to_edge = std::abs(x_or_y_tile - x_or_y_edge);
|
||||||
|
assert(dist_to_edge < WATER_REGION_EDGE_LENGTH);
|
||||||
|
if (dist_to_edge >= best_dist) continue;
|
||||||
|
|
||||||
|
best_dist = dist_to_edge;
|
||||||
|
best_tile = tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
return best_tile;
|
||||||
|
}
|
||||||
|
|
|
@ -64,4 +64,8 @@ void AllocateWaterRegions();
|
||||||
|
|
||||||
void PrintWaterRegionDebugInfo(TileIndex tile);
|
void PrintWaterRegionDebugInfo(TileIndex tile);
|
||||||
|
|
||||||
|
using TestTileIndexCallBack = std::function<bool(const TileIndex)>;
|
||||||
|
bool TestTileInWaterRegionPatch(const WaterRegionPatchDesc &water_region_patch, TestTileIndexCallBack &callback);
|
||||||
|
TileIndex FindClosestEnteringTile(const std::span<WaterRegionPatchDesc> high_level_path, TestTileIndexCallBack &callback);
|
||||||
|
|
||||||
#endif /* WATER_REGIONS_H */
|
#endif /* WATER_REGIONS_H */
|
||||||
|
|
|
@ -34,6 +34,17 @@ Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipP
|
||||||
*/
|
*/
|
||||||
bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir);
|
bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used when user sends ship to the nearest depot or if ship needs servicing using YAPF.
|
||||||
|
* @param v ship that needs to go to some depot
|
||||||
|
* @param max_penalty max distance (in pathfinder penalty) from the current ship position
|
||||||
|
* (used also as optimization - the pathfinder can stop path finding if max_penalty
|
||||||
|
* was reached and no depot was seen)
|
||||||
|
* @param may_reverse whether the ship is allowed to reverse
|
||||||
|
* @return the data about the depot
|
||||||
|
*/
|
||||||
|
FindDepotData YapfShipFindNearestDepot(const Ship *v, int max_penalty, bool may_reverse);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds the best path for given road vehicle using YAPF.
|
* Finds the best path for given road vehicle using YAPF.
|
||||||
* @param v the RV that needs to find a path
|
* @param v the RV that needs to find a path
|
||||||
|
|
|
@ -36,6 +36,7 @@ protected:
|
||||||
TileIndex dest_tile;
|
TileIndex dest_tile;
|
||||||
TrackdirBits dest_trackdirs;
|
TrackdirBits dest_trackdirs;
|
||||||
StationID dest_station;
|
StationID dest_station;
|
||||||
|
bool any_ship_depot = false;
|
||||||
|
|
||||||
bool has_intermediate_dest = false;
|
bool has_intermediate_dest = false;
|
||||||
TileIndex intermediate_dest_tile;
|
TileIndex intermediate_dest_tile;
|
||||||
|
@ -55,6 +56,11 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetAnyShipDepotDestination()
|
||||||
|
{
|
||||||
|
this->any_ship_depot = true;
|
||||||
|
}
|
||||||
|
|
||||||
void SetIntermediateDestination(const WaterRegionPatchDesc &water_region_patch)
|
void SetIntermediateDestination(const WaterRegionPatchDesc &water_region_patch)
|
||||||
{
|
{
|
||||||
this->has_intermediate_dest = true;
|
this->has_intermediate_dest = true;
|
||||||
|
@ -69,10 +75,16 @@ protected:
|
||||||
return *static_cast<Tpf*>(this);
|
return *static_cast<Tpf*>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TestTileIndexCallBack detect_ship_depot = [&](const TileIndex tile)
|
||||||
|
{
|
||||||
|
return IsShipDepotTile(tile) && GetShipDepotPart(tile) == DEPOT_PART_NORTH && IsTileOwner(tile, Yapf().GetVehicle()->owner);
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Called by YAPF to detect if node ends in the desired destination. */
|
/** Called by YAPF to detect if node ends in the desired destination. */
|
||||||
inline bool PfDetectDestination(Node &n)
|
inline bool PfDetectDestination(Node &n)
|
||||||
{
|
{
|
||||||
|
if (this->any_ship_depot) return this->detect_ship_depot(n.key.tile);
|
||||||
return this->PfDetectDestinationTile(n.segment_last_tile, n.segment_last_td);
|
return this->PfDetectDestinationTile(n.segment_last_tile, n.segment_last_td);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,6 +101,11 @@ public:
|
||||||
return tile == this->dest_tile && ((this->dest_trackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE);
|
return tile == this->dest_tile && ((this->dest_trackdirs & TrackdirToTrackdirBits(trackdir)) != TRACKDIR_BIT_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline TileIndex GetShipDepotDestination(const std::span<WaterRegionPatchDesc> high_level_path)
|
||||||
|
{
|
||||||
|
return FindClosestEnteringTile(high_level_path, this->detect_ship_depot);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by YAPF to calculate cost estimate. Calculates distance to the destination
|
* 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.
|
* adds it to the actual cost from origin and stores the sum to the Node::estimate.
|
||||||
|
@ -99,7 +116,7 @@ public:
|
||||||
|
|
||||||
static const int dg_dir_to_x_offs[] = { -1, 0, 1, 0 };
|
static const int dg_dir_to_x_offs[] = { -1, 0, 1, 0 };
|
||||||
static const int dg_dir_to_y_offs[] = { 0, 1, 0, -1 };
|
static const int dg_dir_to_y_offs[] = { 0, 1, 0, -1 };
|
||||||
if (this->PfDetectDestination(n)) {
|
if (this->any_ship_depot || this->PfDetectDestination(n)) {
|
||||||
n.estimate = n.cost;
|
n.estimate = n.cost;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -158,7 +175,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Restricts the search by creating corridor or water regions through which the ship is allowed to travel. */
|
/** Restricts the search by creating corridor or water regions through which the ship is allowed to travel. */
|
||||||
inline void RestrictSearch(const std::vector<WaterRegionPatchDesc> &path)
|
inline void RestrictSearch(const std::span<WaterRegionPatchDesc> &path)
|
||||||
{
|
{
|
||||||
this->water_region_corridor.clear();
|
this->water_region_corridor.clear();
|
||||||
for (const WaterRegionPatchDesc &path_entry : path) this->water_region_corridor.push_back(path_entry);
|
for (const WaterRegionPatchDesc &path_entry : path) this->water_region_corridor.push_back(path_entry);
|
||||||
|
@ -211,16 +228,20 @@ public:
|
||||||
return result;
|
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, int max_penalty,
|
||||||
bool &path_found, ShipPathCache &path_cache, Trackdir &best_origin_dir)
|
bool &path_found, ShipPathCache &path_cache, Trackdir &best_origin_dir)
|
||||||
{
|
{
|
||||||
const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1);
|
std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1);
|
||||||
if (high_level_path.empty()) {
|
if (high_level_path.empty()) {
|
||||||
path_found = false;
|
path_found = false;
|
||||||
/* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
|
/* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
|
||||||
return CreateRandomPath(v, path_cache, SHIP_LOST_PATH_LENGTH);
|
return CreateRandomPath(v, path_cache, SHIP_LOST_PATH_LENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bool find_closest_depot = tile == INVALID_TILE;
|
||||||
|
if (find_closest_depot) tile = v->tile;
|
||||||
|
const bool automatic_servicing = find_closest_depot && max_penalty != 0;
|
||||||
|
|
||||||
/* Try one time without restricting the search area, which generally results in better and more natural looking paths.
|
/* Try one time without restricting the search area, which generally results in better and more natural looking paths.
|
||||||
* However the pathfinder can hit the node limit in certain situations such as long aqueducts or maze-like terrain.
|
* However the pathfinder can hit the node limit in certain situations such as long aqueducts or maze-like terrain.
|
||||||
* If that happens we run the pathfinder again, but restricted only to the regions provided by the region pathfinder. */
|
* If that happens we run the pathfinder again, but restricted only to the regions provided by the region pathfinder. */
|
||||||
|
@ -229,13 +250,28 @@ public:
|
||||||
|
|
||||||
/* Set origin and destination nodes */
|
/* Set origin and destination nodes */
|
||||||
pf.SetOrigin(v->tile, forward_dirs | reverse_dirs);
|
pf.SetOrigin(v->tile, forward_dirs | reverse_dirs);
|
||||||
pf.SetDestination(v);
|
if (find_closest_depot) {
|
||||||
const bool is_intermediate_destination = static_cast<int>(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1;
|
pf.SetAnyShipDepotDestination();
|
||||||
if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back());
|
} else {
|
||||||
|
pf.SetDestination(v);
|
||||||
|
}
|
||||||
|
pf.SetMaxCost(max_penalty);
|
||||||
|
|
||||||
|
const std::span<WaterRegionPatchDesc> high_level_path_span(high_level_path.data(), std::min<size_t>(high_level_path.size(), NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1));
|
||||||
|
const bool is_intermediate_destination = static_cast<int>(high_level_path_span.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1;
|
||||||
|
if (is_intermediate_destination) {
|
||||||
|
if (automatic_servicing) {
|
||||||
|
/* Automatic servicing requires a valid path cost from start to end.
|
||||||
|
* However, when an intermediate destination is set, the resulting cost
|
||||||
|
* cannot be used to determine if it falls within the maximum allowed penalty. */
|
||||||
|
return INVALID_TRACKDIR;
|
||||||
|
}
|
||||||
|
pf.SetIntermediateDestination(high_level_path_span.back());
|
||||||
|
}
|
||||||
|
|
||||||
/* Restrict the search area to prevent the low level pathfinder from expanding too many nodes. This can happen
|
/* Restrict the search area to prevent the low level pathfinder from expanding too many nodes. This can happen
|
||||||
* when the terrain is very "maze-like" or when the high level path "teleports" via a very long aqueduct. */
|
* when the terrain is very "maze-like" or when the high level path "teleports" via a very long aqueduct. */
|
||||||
if (attempt > 0) pf.RestrictSearch(high_level_path);
|
if (attempt > 0) pf.RestrictSearch(high_level_path_span);
|
||||||
|
|
||||||
/* Find best path. */
|
/* Find best path. */
|
||||||
path_found = pf.FindPath(v);
|
path_found = pf.FindPath(v);
|
||||||
|
@ -245,6 +281,12 @@ public:
|
||||||
/* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
|
/* Make the ship move around aimlessly. This prevents repeated pathfinder calls and clearly indicates that the ship is lost. */
|
||||||
if (!path_found) return CreateRandomPath(v, path_cache, SHIP_LOST_PATH_LENGTH);
|
if (!path_found) return CreateRandomPath(v, path_cache, SHIP_LOST_PATH_LENGTH);
|
||||||
|
|
||||||
|
/* Return early when only searching for the closest depot tile. */
|
||||||
|
if (find_closest_depot) {
|
||||||
|
tile = is_intermediate_destination ? pf.GetShipDepotDestination(high_level_path) : node->GetTile();
|
||||||
|
return INVALID_TRACKDIR;
|
||||||
|
}
|
||||||
|
|
||||||
/* Return only the path within the current water region if an intermediate destination was returned. If not, cache the entire path
|
/* Return only the path within the current water region if an intermediate destination was returned. If not, cache the entire path
|
||||||
* to the final destination tile. The low-level pathfinder might actually prefer a different docking tile in a nearby region. Without
|
* to the final destination tile. The low-level pathfinder might actually prefer a different docking tile in a nearby region. Without
|
||||||
* caching the full path the ship can get stuck in a loop. */
|
* caching the full path the ship can get stuck in a loop. */
|
||||||
|
@ -254,7 +296,7 @@ public:
|
||||||
while (node->parent) {
|
while (node->parent) {
|
||||||
const WaterRegionPatchDesc node_water_patch = GetWaterRegionPatchInfo(node->GetTile());
|
const WaterRegionPatchDesc node_water_patch = GetWaterRegionPatchInfo(node->GetTile());
|
||||||
|
|
||||||
const bool node_water_patch_on_high_level_path = std::ranges::find(high_level_path, node_water_patch) != high_level_path.end();
|
const bool node_water_patch_on_high_level_path = std::ranges::find(high_level_path_span, node_water_patch) != high_level_path_span.end();
|
||||||
const bool add_full_path = !is_intermediate_destination && node_water_patch != end_water_patch;
|
const bool add_full_path = !is_intermediate_destination && node_water_patch != end_water_patch;
|
||||||
|
|
||||||
/* The cached path must always lead to a region patch that's on the high level path.
|
/* The cached path must always lead to a region patch that's on the high level path.
|
||||||
|
@ -303,6 +345,7 @@ public:
|
||||||
{
|
{
|
||||||
bool path_found = false;
|
bool path_found = false;
|
||||||
ShipPathCache dummy_cache;
|
ShipPathCache dummy_cache;
|
||||||
|
TileIndex tile = v->tile;
|
||||||
Trackdir best_origin_dir = INVALID_TRACKDIR;
|
Trackdir best_origin_dir = INVALID_TRACKDIR;
|
||||||
|
|
||||||
if (trackdir == nullptr) {
|
if (trackdir == nullptr) {
|
||||||
|
@ -310,17 +353,45 @@ public:
|
||||||
const Trackdir reverse_dir = ReverseTrackdir(v->GetVehicleTrackdir());
|
const Trackdir reverse_dir = ReverseTrackdir(v->GetVehicleTrackdir());
|
||||||
const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
|
const TrackdirBits forward_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
|
||||||
const TrackdirBits reverse_dirs = TrackdirToTrackdirBits(reverse_dir);
|
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, tile, forward_dirs, reverse_dirs, 0, path_found, dummy_cache, best_origin_dir);
|
||||||
return path_found && best_origin_dir == reverse_dir;
|
return path_found && best_origin_dir == reverse_dir;
|
||||||
} else {
|
} else {
|
||||||
/* This gets called when a ship suddenly can't move forward, e.g. due to terraforming. */
|
/* 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 DiagDirection entry = ReverseDiagDir(VehicleExitDir(v->direction, v->state));
|
||||||
const TrackdirBits reverse_dirs = DiagdirReachesTrackdirs(entry) & TrackStatusToTrackdirBits(GetTileTrackStatus(v->tile, TRANSPORT_WATER, 0, entry));
|
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, tile, TRACKDIR_BIT_NONE, reverse_dirs, 0, path_found, dummy_cache, best_origin_dir);
|
||||||
*trackdir = path_found && best_origin_dir != INVALID_TRACKDIR ? best_origin_dir : GetRandomTrackdir(reverse_dirs);
|
*trackdir = path_found && best_origin_dir != INVALID_TRACKDIR ? best_origin_dir : GetRandomTrackdir(reverse_dirs);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the best depot for a ship.
|
||||||
|
* @param v Ship
|
||||||
|
* @param max_penalty maximum pathfinder cost.
|
||||||
|
* @param may_reverse whether the ship is allowed to reverse.
|
||||||
|
* @return FindDepotData with the best depot tile, cost and whether to reverse.
|
||||||
|
*/
|
||||||
|
static inline FindDepotData FindNearestDepot(const Ship *v, int max_penalty, bool may_reverse)
|
||||||
|
{
|
||||||
|
FindDepotData depot;
|
||||||
|
|
||||||
|
bool path_found = false;
|
||||||
|
ShipPathCache dummy_cache;
|
||||||
|
TileIndex tile = INVALID_TILE;
|
||||||
|
Trackdir best_origin_dir = INVALID_TRACKDIR;
|
||||||
|
const bool search_both_ways = may_reverse && max_penalty == 0;
|
||||||
|
const Trackdir forward_dir = v->GetVehicleTrackdir();
|
||||||
|
const Trackdir reverse_dir = ReverseTrackdir(forward_dir);
|
||||||
|
const TrackdirBits forward_dirs = TrackdirToTrackdirBits(forward_dir);
|
||||||
|
const TrackdirBits reverse_dirs = search_both_ways ? TrackdirToTrackdirBits(reverse_dir) : TRACKDIR_BIT_NONE;
|
||||||
|
(void)ChooseShipTrack(v, tile, forward_dirs, reverse_dirs, max_penalty, path_found, dummy_cache, best_origin_dir);
|
||||||
|
if (path_found) {
|
||||||
|
assert(tile != INVALID_TILE);
|
||||||
|
depot.tile = tile;
|
||||||
|
}
|
||||||
|
return depot;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Cost Provider module of YAPF for ships. */
|
/** Cost Provider module of YAPF for ships. */
|
||||||
|
@ -333,6 +404,11 @@ public:
|
||||||
typedef typename Types::NodeList::Item Node; ///< this will be our node type.
|
typedef typename Types::NodeList::Item Node; ///< this will be our node type.
|
||||||
typedef typename Node::Key Key; ///< key to hash tables.
|
typedef typename Node::Key Key; ///< key to hash tables.
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int max_cost;
|
||||||
|
|
||||||
|
CYapfCostShipT() : max_cost(0) {}
|
||||||
|
|
||||||
/** to access inherited path finder */
|
/** to access inherited path finder */
|
||||||
Tpf &Yapf()
|
Tpf &Yapf()
|
||||||
{
|
{
|
||||||
|
@ -340,6 +416,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
inline void SetMaxCost(int cost)
|
||||||
|
{
|
||||||
|
this->max_cost = cost;
|
||||||
|
}
|
||||||
|
|
||||||
inline int CurveCost(Trackdir td1, Trackdir td2)
|
inline int CurveCost(Trackdir td1, Trackdir td2)
|
||||||
{
|
{
|
||||||
assert(IsValidTrackdir(td1));
|
assert(IsValidTrackdir(td1));
|
||||||
|
@ -384,6 +465,10 @@ public:
|
||||||
uint8_t speed_frac = (GetEffectiveWaterClass(n.GetTile()) == WATER_CLASS_SEA) ? svi->ocean_speed_frac : svi->canal_speed_frac;
|
uint8_t speed_frac = (GetEffectiveWaterClass(n.GetTile()) == WATER_CLASS_SEA) ? svi->ocean_speed_frac : svi->canal_speed_frac;
|
||||||
if (speed_frac > 0) c += YAPF_TILE_LENGTH * (1 + tf->tiles_skipped) * speed_frac / (256 - speed_frac);
|
if (speed_frac > 0) c += YAPF_TILE_LENGTH * (1 + tf->tiles_skipped) * speed_frac / (256 - speed_frac);
|
||||||
|
|
||||||
|
/* Finish if we already exceeded the maximum path cost (i.e. when
|
||||||
|
* searching for the nearest depot). */
|
||||||
|
if (this->max_cost > 0 && (n.parent->cost + c) > this->max_cost) return false;
|
||||||
|
|
||||||
/* Apply it. */
|
/* Apply it. */
|
||||||
n.cost = n.parent->cost + c;
|
n.cost = n.parent->cost + c;
|
||||||
return true;
|
return true;
|
||||||
|
@ -422,7 +507,7 @@ Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipP
|
||||||
{
|
{
|
||||||
Trackdir best_origin_dir = INVALID_TRACKDIR;
|
Trackdir best_origin_dir = INVALID_TRACKDIR;
|
||||||
const TrackdirBits origin_dirs = TrackdirToTrackdirBits(v->GetVehicleTrackdir());
|
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, 0, path_found, path_cache, best_origin_dir);
|
||||||
return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
|
return (td_ret != INVALID_TRACKDIR) ? TrackdirToTrack(td_ret) : INVALID_TRACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,3 +515,8 @@ bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir)
|
||||||
{
|
{
|
||||||
return CYapfShip::CheckShipReverse(v, trackdir);
|
return CYapfShip::CheckShipReverse(v, trackdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FindDepotData YapfShipFindNearestDepot(const Ship *v, int max_penalty, bool may_reverse)
|
||||||
|
{
|
||||||
|
return CYapfShip::FindNearestDepot(v, max_penalty, may_reverse);
|
||||||
|
}
|
||||||
|
|
|
@ -119,6 +119,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Key dest;
|
Key dest;
|
||||||
|
bool any_ship_depot = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void SetDestination(const WaterRegionPatchDesc &water_region_patch)
|
void SetDestination(const WaterRegionPatchDesc &water_region_patch)
|
||||||
|
@ -126,18 +127,32 @@ public:
|
||||||
this->dest.Set(water_region_patch);
|
this->dest.Set(water_region_patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetAnyShipDepotDestination()
|
||||||
|
{
|
||||||
|
this->any_ship_depot = true;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
TestTileIndexCallBack detect_ship_depot = [&](const TileIndex tile)
|
||||||
|
{
|
||||||
|
return IsShipDepotTile(tile) && GetShipDepotPart(tile) == DEPOT_PART_NORTH && IsTileOwner(tile, Yapf().GetVehicle()->owner);
|
||||||
|
};
|
||||||
|
|
||||||
Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
Tpf &Yapf() { return *static_cast<Tpf*>(this); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inline bool PfDetectDestination(Node &n) const
|
inline bool PfDetectDestination(Node &n)
|
||||||
{
|
{
|
||||||
|
if (this->any_ship_depot) {
|
||||||
|
return TestTileInWaterRegionPatch(n.key.water_region_patch, this->detect_ship_depot);
|
||||||
|
}
|
||||||
|
|
||||||
return n.key == this->dest;
|
return n.key == this->dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool PfCalcEstimate(Node &n)
|
inline bool PfCalcEstimate(Node &n)
|
||||||
{
|
{
|
||||||
if (this->PfDetectDestination(n)) {
|
if (this->any_ship_depot || this->PfDetectDestination(n)) {
|
||||||
n.estimate = n.cost;
|
n.estimate = n.cost;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -218,6 +233,31 @@ public:
|
||||||
assert(!path.empty());
|
assert(!path.empty());
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<WaterRegionPatchDesc> FindShipDepotRegionPath(const Ship *v)
|
||||||
|
{
|
||||||
|
const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(v->tile);
|
||||||
|
|
||||||
|
/* We reserve 4 nodes (patches) per water region. The vast majority of water regions have 1 or 2 regions so this should be a pretty
|
||||||
|
* safe limit. We cap the limit at 65536 which is at a region size of 16x16 is equivalent to one node per region for a 4096x4096 map. */
|
||||||
|
Tpf pf(std::min(static_cast<int>(Map::Size() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES));
|
||||||
|
pf.AddOrigin(start_water_region_patch);
|
||||||
|
pf.SetAnyShipDepotDestination();
|
||||||
|
|
||||||
|
/* Find best path. */
|
||||||
|
if (!pf.FindPath(v)) return {}; // Path not found.
|
||||||
|
|
||||||
|
std::vector<WaterRegionPatchDesc> path;
|
||||||
|
Node *node = pf.GetBestNode();
|
||||||
|
while (node != nullptr) {
|
||||||
|
path.push_back(node->key.water_region_patch);
|
||||||
|
node = node->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(!path.empty());
|
||||||
|
std::ranges::reverse(path);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Cost Provider of YAPF for water regions. */
|
/** Cost Provider of YAPF for water regions. */
|
||||||
|
@ -296,5 +336,8 @@ struct CYapfRegionWater : CYapfT<CYapfRegion_TypesT<CYapfRegionWater, CRegionNod
|
||||||
*/
|
*/
|
||||||
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
|
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length)
|
||||||
{
|
{
|
||||||
|
const bool find_closest_depot = start_tile == INVALID_TILE;
|
||||||
|
|
||||||
|
if (find_closest_depot) return CYapfRegionWater::FindShipDepotRegionPath(v);
|
||||||
return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length);
|
return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,5 +16,6 @@
|
||||||
struct Ship;
|
struct Ship;
|
||||||
|
|
||||||
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length);
|
std::vector<WaterRegionPatchDesc> YapfShipFindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length);
|
||||||
|
std::vector<WaterRegionPatchDesc> YapfFindShipDepotRegionPath(const Ship *v);
|
||||||
|
|
||||||
#endif /* YAPF_SHIP_REGIONS_H */
|
#endif /* YAPF_SHIP_REGIONS_H */
|
||||||
|
|
|
@ -132,7 +132,7 @@ struct RoadVehicle final : public GroundVehicle<RoadVehicle, VEH_ROAD> {
|
||||||
uint Crash(bool flooded = false) override;
|
uint Crash(bool flooded = false) override;
|
||||||
Trackdir GetVehicleTrackdir() const override;
|
Trackdir GetVehicleTrackdir() const override;
|
||||||
TileIndex GetOrderStationLocation(StationID station) override;
|
TileIndex GetOrderStationLocation(StationID station) override;
|
||||||
ClosestDepot FindClosestDepot() override;
|
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
|
||||||
|
|
||||||
bool IsBus() const;
|
bool IsBus() const;
|
||||||
|
|
||||||
|
|
|
@ -346,7 +346,7 @@ static FindDepotData FindClosestRoadDepot(const RoadVehicle *v, int max_distance
|
||||||
return YapfRoadVehicleFindNearestDepot(v, max_distance);
|
return YapfRoadVehicleFindNearestDepot(v, max_distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosestDepot RoadVehicle::FindClosestDepot()
|
ClosestDepot RoadVehicle::FindClosestDepot([[maybe_unused]] bool may_reverse)
|
||||||
{
|
{
|
||||||
FindDepotData rfdd = FindClosestRoadDepot(this, 0);
|
FindDepotData rfdd = FindClosestRoadDepot(this, 0);
|
||||||
if (rfdd.best_length == UINT_MAX) return ClosestDepot();
|
if (rfdd.best_length == UINT_MAX) return ClosestDepot();
|
||||||
|
|
|
@ -57,7 +57,7 @@ struct Ship final : public SpecializedVehicle<Ship, VEH_SHIP> {
|
||||||
void OnNewEconomyDay() override;
|
void OnNewEconomyDay() override;
|
||||||
Trackdir GetVehicleTrackdir() const override;
|
Trackdir GetVehicleTrackdir() const override;
|
||||||
TileIndex GetOrderStationLocation(StationID station) override;
|
TileIndex GetOrderStationLocation(StationID station) override;
|
||||||
ClosestDepot FindClosestDepot() override;
|
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
|
||||||
void UpdateCache();
|
void UpdateCache();
|
||||||
void SetDestTile(TileIndex tile) override;
|
void SetDestTile(TileIndex tile) override;
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
#include "station_base.h"
|
#include "station_base.h"
|
||||||
#include "newgrf_engine.h"
|
#include "newgrf_engine.h"
|
||||||
#include "pathfinder/yapf/yapf.h"
|
#include "pathfinder/yapf/yapf.h"
|
||||||
#include "pathfinder/yapf/yapf_ship_regions.h"
|
|
||||||
#include "newgrf_sound.h"
|
#include "newgrf_sound.h"
|
||||||
#include "spritecache.h"
|
#include "spritecache.h"
|
||||||
#include "strings_func.h"
|
#include "strings_func.h"
|
||||||
|
@ -39,13 +38,8 @@
|
||||||
|
|
||||||
#include "table/strings.h"
|
#include "table/strings.h"
|
||||||
|
|
||||||
#include <unordered_set>
|
|
||||||
|
|
||||||
#include "safeguards.h"
|
#include "safeguards.h"
|
||||||
|
|
||||||
/** Max distance in tiles (as the crow flies) to search for depots when user clicks "go to depot". */
|
|
||||||
constexpr int MAX_SHIP_DEPOT_SEARCH_DISTANCE = 80;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine the effective #WaterClass for a ship travelling on a tile.
|
* Determine the effective #WaterClass for a ship travelling on a tile.
|
||||||
* @param tile Tile of interest
|
* @param tile Tile of interest
|
||||||
|
@ -148,57 +142,15 @@ void Ship::GetImage(Direction direction, EngineImageType image_type, VehicleSpri
|
||||||
result->Set(_ship_sprites[spritenum] + direction);
|
result->Set(_ship_sprites[spritenum] + direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
|
static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance, bool may_reverse = false)
|
||||||
{
|
{
|
||||||
const int max_region_distance = (max_distance / WATER_REGION_EDGE_LENGTH) + 1;
|
const TileIndex tile = v->tile;
|
||||||
|
if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) return Depot::GetByTile(tile);
|
||||||
|
|
||||||
static std::unordered_set<int> visited_patch_hashes;
|
FindDepotData sfdd = YapfShipFindNearestDepot(Ship::From(v), max_distance, may_reverse);
|
||||||
static std::deque<WaterRegionPatchDesc> patches_to_search;
|
|
||||||
visited_patch_hashes.clear();
|
|
||||||
patches_to_search.clear();
|
|
||||||
|
|
||||||
/* Step 1: find a set of reachable Water Region Patches using BFS. */
|
if (sfdd.tile == INVALID_TILE) return nullptr;
|
||||||
const WaterRegionPatchDesc start_patch = GetWaterRegionPatchInfo(v->tile);
|
return Depot::GetByTile(sfdd.tile);
|
||||||
patches_to_search.push_back(start_patch);
|
|
||||||
visited_patch_hashes.insert(CalculateWaterRegionPatchHash(start_patch));
|
|
||||||
|
|
||||||
while (!patches_to_search.empty()) {
|
|
||||||
/* Remove first patch from the queue and make it the current patch. */
|
|
||||||
const WaterRegionPatchDesc current_node = patches_to_search.front();
|
|
||||||
patches_to_search.pop_front();
|
|
||||||
|
|
||||||
/* Add neighbours of the current patch to the search queue. */
|
|
||||||
VisitWaterRegionPatchCallback visit_func = [&](const WaterRegionPatchDesc &water_region_patch) {
|
|
||||||
/* Note that we check the max distance per axis, not the total distance. */
|
|
||||||
if (std::abs(water_region_patch.x - start_patch.x) > max_region_distance ||
|
|
||||||
std::abs(water_region_patch.y - start_patch.y) > max_region_distance) return;
|
|
||||||
|
|
||||||
const int hash = CalculateWaterRegionPatchHash(water_region_patch);
|
|
||||||
if (visited_patch_hashes.count(hash) == 0) {
|
|
||||||
visited_patch_hashes.insert(hash);
|
|
||||||
patches_to_search.push_back(water_region_patch);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
VisitWaterRegionPatchNeighbours(current_node, visit_func);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Step 2: Find the closest depot within the reachable Water Region Patches. */
|
|
||||||
const Depot *best_depot = nullptr;
|
|
||||||
uint best_dist_sq = std::numeric_limits<uint>::max();
|
|
||||||
for (const Depot *depot : Depot::Iterate()) {
|
|
||||||
const TileIndex tile = depot->xy;
|
|
||||||
if (IsShipDepotTile(tile) && IsTileOwner(tile, v->owner)) {
|
|
||||||
const uint dist_sq = DistanceSquare(tile, v->tile);
|
|
||||||
if (dist_sq < best_dist_sq && dist_sq <= max_distance * max_distance &&
|
|
||||||
visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) {
|
|
||||||
best_dist_sq = dist_sq;
|
|
||||||
best_depot = depot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return best_depot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CheckIfShipNeedsService(Vehicle *v)
|
static void CheckIfShipNeedsService(Vehicle *v)
|
||||||
|
@ -209,7 +161,7 @@ static void CheckIfShipNeedsService(Vehicle *v)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint max_distance = _settings_game.pf.yapf.maximum_go_to_depot_penalty / YAPF_TILE_LENGTH;
|
uint max_distance = _settings_game.pf.yapf.maximum_go_to_depot_penalty;
|
||||||
|
|
||||||
const Depot *depot = FindClosestShipDepot(v, max_distance);
|
const Depot *depot = FindClosestShipDepot(v, max_distance);
|
||||||
|
|
||||||
|
@ -954,9 +906,9 @@ CommandCost CmdBuildShip(DoCommandFlags flags, TileIndex tile, const Engine *e,
|
||||||
return CommandCost();
|
return CommandCost();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosestDepot Ship::FindClosestDepot()
|
ClosestDepot Ship::FindClosestDepot(bool may_reverse)
|
||||||
{
|
{
|
||||||
const Depot *depot = FindClosestShipDepot(this, MAX_SHIP_DEPOT_SEARCH_DISTANCE);
|
const Depot *depot = FindClosestShipDepot(this, 0, may_reverse);
|
||||||
if (depot == nullptr) return ClosestDepot();
|
if (depot == nullptr) return ClosestDepot();
|
||||||
|
|
||||||
return ClosestDepot(depot->xy, depot->index);
|
return ClosestDepot(depot->xy, depot->index);
|
||||||
|
|
|
@ -129,7 +129,7 @@ struct Train final : public GroundVehicle<Train, VEH_TRAIN> {
|
||||||
uint Crash(bool flooded = false) override;
|
uint Crash(bool flooded = false) override;
|
||||||
Trackdir GetVehicleTrackdir() const override;
|
Trackdir GetVehicleTrackdir() const override;
|
||||||
TileIndex GetOrderStationLocation(StationID station) override;
|
TileIndex GetOrderStationLocation(StationID station) override;
|
||||||
ClosestDepot FindClosestDepot() override;
|
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
|
||||||
|
|
||||||
void ReserveTrackUnderConsist() const;
|
void ReserveTrackUnderConsist() const;
|
||||||
|
|
||||||
|
|
|
@ -2194,7 +2194,7 @@ static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
|
||||||
return YapfTrainFindNearestDepot(v, max_distance);
|
return YapfTrainFindNearestDepot(v, max_distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosestDepot Train::FindClosestDepot()
|
ClosestDepot Train::FindClosestDepot([[maybe_unused]] bool may_reverse)
|
||||||
{
|
{
|
||||||
FindDepotData tfdd = FindClosestTrainDepot(this, 0);
|
FindDepotData tfdd = FindClosestTrainDepot(this, 0);
|
||||||
if (tfdd.best_length == UINT_MAX) return ClosestDepot();
|
if (tfdd.best_length == UINT_MAX) return ClosestDepot();
|
||||||
|
|
|
@ -2583,7 +2583,7 @@ CommandCost Vehicle::SendToDepot(DoCommandFlags flags, DepotCommandFlags command
|
||||||
return CommandCost();
|
return CommandCost();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosestDepot closest_depot = this->FindClosestDepot();
|
ClosestDepot closest_depot = this->FindClosestDepot(true);
|
||||||
static const StringID no_depot[] = {STR_ERROR_UNABLE_TO_FIND_ROUTE_TO, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR};
|
static const StringID no_depot[] = {STR_ERROR_UNABLE_TO_FIND_ROUTE_TO, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_UNABLE_TO_FIND_LOCAL_DEPOT, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR};
|
||||||
if (!closest_depot.found) return CommandCost(no_depot[this->type]);
|
if (!closest_depot.found) return CommandCost(no_depot[this->type]);
|
||||||
|
|
||||||
|
|
|
@ -792,9 +792,10 @@ public:
|
||||||
/**
|
/**
|
||||||
* Find the closest depot for this vehicle and tell us the location,
|
* Find the closest depot for this vehicle and tell us the location,
|
||||||
* DestinationID and whether we should reverse.
|
* DestinationID and whether we should reverse.
|
||||||
|
* @param may_reverse Whether the vehicle is allowed to reverse.
|
||||||
* @return A structure with information about the closest depot, if found.
|
* @return A structure with information about the closest depot, if found.
|
||||||
*/
|
*/
|
||||||
virtual ClosestDepot FindClosestDepot() { return {}; }
|
virtual ClosestDepot FindClosestDepot([[maybe_unused]] bool may_reverse = false) { return {}; }
|
||||||
|
|
||||||
virtual void SetDestTile(TileIndex tile) { this->dest_tile = tile; }
|
virtual void SetDestTile(TileIndex tile) { this->dest_tile = tile; }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue