|
|
|
@ -18,6 +18,7 @@
|
|
|
|
|
#include "station_base.h"
|
|
|
|
|
#include "newgrf_engine.h"
|
|
|
|
|
#include "pathfinder/yapf/yapf.h"
|
|
|
|
|
#include "pathfinder/yapf/yapf_ship_regions.h"
|
|
|
|
|
#include "newgrf_sound.h"
|
|
|
|
|
#include "spritecache.h"
|
|
|
|
|
#include "strings_func.h"
|
|
|
|
@ -38,11 +39,16 @@
|
|
|
|
|
#include "industry_map.h"
|
|
|
|
|
#include "core/checksum_func.hpp"
|
|
|
|
|
#include "articulated_vehicles.h"
|
|
|
|
|
#include "core/ring_buffer.hpp"
|
|
|
|
|
#include "3rdparty/robin_hood/robin_hood.h"
|
|
|
|
|
|
|
|
|
|
#include "table/strings.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;
|
|
|
|
|
|
|
|
|
|
/** Directions to search towards given track bits and the ship's enter direction. */
|
|
|
|
|
const DiagDirection _ship_search_directions[6][4] = {
|
|
|
|
|
{ DIAGDIR_NE, INVALID_DIAGDIR, DIAGDIR_SW, INVALID_DIAGDIR },
|
|
|
|
@ -155,21 +161,50 @@ void Ship::GetImage(Direction direction, EngineImageType image_type, VehicleSpri
|
|
|
|
|
|
|
|
|
|
static const Depot *FindClosestShipDepot(const Vehicle *v, uint max_distance)
|
|
|
|
|
{
|
|
|
|
|
/* Find the closest depot */
|
|
|
|
|
const Depot *best_depot = nullptr;
|
|
|
|
|
/* If we don't have a maximum distance, i.e. distance = 0,
|
|
|
|
|
* we want to find any depot so the best distance of no
|
|
|
|
|
* depot must be more than any correct distance. On the
|
|
|
|
|
* other hand if we have set a maximum distance, any depot
|
|
|
|
|
* further away than max_distance can safely be ignored. */
|
|
|
|
|
uint best_dist = max_distance == 0 ? UINT_MAX : max_distance + 1;
|
|
|
|
|
const uint max_region_distance = (max_distance / WATER_REGION_EDGE_LENGTH) + 1;
|
|
|
|
|
|
|
|
|
|
static robin_hood::unordered_flat_set<uint32_t> visited_patch_hashes;
|
|
|
|
|
static ring_buffer<WaterRegionPatchDesc> patches_to_search;
|
|
|
|
|
visited_patch_hashes.clear();
|
|
|
|
|
patches_to_search.clear();
|
|
|
|
|
|
|
|
|
|
/* Step 1: find a set of reachable Water Region Patches using BFS. */
|
|
|
|
|
const WaterRegionPatchDesc start_patch = GetWaterRegionPatchInfo(v->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 neighbors of the current patch to the search queue. */
|
|
|
|
|
TVisitWaterRegionPatchCallBack visitFunc = [&](const WaterRegionPatchDesc &water_region_patch) {
|
|
|
|
|
/* Note that we check the max distance per axis, not the total distance. */
|
|
|
|
|
if (Delta(water_region_patch.x, start_patch.x) > max_region_distance ||
|
|
|
|
|
Delta(water_region_patch.y, start_patch.y) > max_region_distance) return;
|
|
|
|
|
|
|
|
|
|
const uint32_t hash = CalculateWaterRegionPatchHash(water_region_patch);
|
|
|
|
|
auto res = visited_patch_hashes.insert(hash);
|
|
|
|
|
if (res.second) {
|
|
|
|
|
patches_to_search.push_back(water_region_patch);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
VisitWaterRegionPatchNeighbors(current_node, visitFunc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Step 2: Find the closest depot within the reachable Water Region Patches. */
|
|
|
|
|
const uint max_distance_sq = max_distance * max_distance;
|
|
|
|
|
const Depot *best_depot = nullptr;
|
|
|
|
|
uint best_dist_sq = std::numeric_limits<uint>::max();
|
|
|
|
|
for (const Depot *depot : Depot::Iterate()) {
|
|
|
|
|
TileIndex tile = depot->xy;
|
|
|
|
|
const TileIndex tile = depot->xy;
|
|
|
|
|
if (IsShipDepotTile(tile) && IsInfraTileUsageAllowed(VEH_SHIP, v->owner, tile)) {
|
|
|
|
|
uint dist = DistanceManhattan(tile, v->tile);
|
|
|
|
|
if (dist < best_dist) {
|
|
|
|
|
best_dist = dist;
|
|
|
|
|
const uint dist_sq = DistanceSquare(tile, v->tile);
|
|
|
|
|
if (dist_sq < best_dist_sq && dist_sq <= max_distance_sq &&
|
|
|
|
|
visited_patch_hashes.count(CalculateWaterRegionPatchHash(GetWaterRegionPatchInfo(tile))) > 0) {
|
|
|
|
|
best_dist_sq = dist_sq;
|
|
|
|
|
best_depot = depot;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@ -1190,7 +1225,7 @@ CommandCost CmdBuildShip(TileIndex tile, DoCommandFlag flags, const Engine *e, V
|
|
|
|
|
|
|
|
|
|
ClosestDepot Ship::FindClosestDepot()
|
|
|
|
|
{
|
|
|
|
|
const Depot *depot = FindClosestShipDepot(this, 0);
|
|
|
|
|
const Depot *depot = FindClosestShipDepot(this, MAX_SHIP_DEPOT_SEARCH_DISTANCE);
|
|
|
|
|
if (depot == nullptr) return ClosestDepot();
|
|
|
|
|
|
|
|
|
|
return ClosestDepot(depot->xy, depot->index);
|
|
|
|
|