1
0
Fork 0

Compare commits

...

7 Commits

Author SHA1 Message Date
SamuXarick 829978c0b7
Merge f0382c37ee into 1d38cbafcb 2025-07-13 23:10:26 +00:00
Peter Nelson 1d38cbafcb Codechange: Use unique_ptr for ScriptInfo instances.
Replaces raw pointers, slightly.
2025-07-14 00:10:14 +01:00
Peter Nelson 992d58d799 Codechange: Pass ScriptInfo by reference to IsSameScript. 2025-07-14 00:10:14 +01:00
Peter Nelson 8f34b7a821 Codechange: Keep Squirrel engine in unique_ptr. 2025-07-14 00:10:14 +01:00
Peter Nelson bf6d0c4934
Codechange: Don't pre-fill font metrics when loading fonts. (#14436)
Each font cache implementation sets its own metrics based on the loaded font, so there is no need to pre-fill with (unscaled, invalid) default metrics.
2025-07-13 23:38:31 +01:00
Michael Lutz 3c4fb21a5e
Fix: [Win32] Link failure with newer Windows SDK version due to WinRT changes. (#14432) 2025-07-13 22:34:32 +01:00
SamuXarick f0382c37ee Fix #12193: [YAPF] Don't use the entire docking area when computing closest station tile
Don't use CalcClosestStationTile for ships. Instead, use a specialized GetShipDestinationTiles which creates a list of possible destination tiles. For docks, it only takes into consideration the tiles that pass both IsDockingTile and IsShipDestinationTile tests. For the other cases it just takes the ship's current dest_tile.

Modified CYapfDestinationTileWaterT class to accept multiple destinations. In this manner, the estimate cost is calculated for each of the destination tiles and the shortest estimate is returned. Some adaptation was necessary to make this possible to work for both the high- and low-level pathfinders and the existing functions. ChooseShipTrack and its FindWaterRegionPath brother both will take the same destination tiles which are calculated at either YapfShipChooseTrack or YapfShipCheckReverse.
2025-02-16 20:54:32 +00:00
13 changed files with 167 additions and 102 deletions

View File

@ -90,7 +90,7 @@ template <> SQInteger PushClassName<AIInfo, ScriptType::AI>(HSQUIRRELVM vm) { sq
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */ /* Remove the link to the real instance, else it might get deleted by RegisterAI() */
sq_setinstanceup(vm, 2, nullptr); sq_setinstanceup(vm, 2, nullptr);
/* Register the AI to the base system */ /* Register the AI to the base system */
info->GetScanner()->RegisterScript(info); info->GetScanner()->RegisterScript(std::unique_ptr<AIInfo>{info});
return 0; return 0;
} }
@ -136,22 +136,21 @@ bool AIInfo::CanLoadFromVersion(int version) const
/* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm) /* static */ SQInteger AILibrary::Constructor(HSQUIRRELVM vm)
{ {
/* Create a new library */ /* Create a new library */
AILibrary *library = new AILibrary(); auto library = std::make_unique<AILibrary>();
SQInteger res = ScriptInfo::Constructor(vm, *library); SQInteger res = ScriptInfo::Constructor(vm, *library);
if (res != 0) { if (res != 0) {
delete library;
return res; return res;
} }
/* Cache the category */ /* Cache the category */
if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethod(library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) { if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethod(library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) {
delete library;
return SQ_ERROR; return SQ_ERROR;
} }
/* Register the Library to the base system */ /* Register the Library to the base system */
library->GetScanner()->RegisterScript(library); ScriptScanner *scanner = library->GetScanner();
scanner->RegisterScript(std::move(library));
return 0; return 0;
} }

View File

@ -29,7 +29,7 @@ void AIScannerInfo::Initialize()
{ {
ScriptScanner::Initialize("AIScanner"); ScriptScanner::Initialize("AIScanner");
ScriptAllocatorScope alloc_scope(this->engine); ScriptAllocatorScope alloc_scope(this->engine.get());
/* Create the dummy AI */ /* Create the dummy AI */
this->main_script = "%_dummy"; this->main_script = "%_dummy";

View File

@ -22,9 +22,10 @@
#include "safeguards.h" #include "safeguards.h"
/** Default heights for the different sizes of fonts. */ /** Default unscaled heights for the different sizes of fonts. */
static const int _default_font_height[FS_END] = {10, 6, 18, 10}; /* static */ const int FontCache::DEFAULT_FONT_HEIGHT[FS_END] = {10, 6, 18, 10};
static const int _default_font_ascender[FS_END] = { 8, 5, 15, 8}; /** Default unscaled ascenders for the different sizes of fonts. */
/* static */ const int FontCache::DEFAULT_FONT_ASCENDER[FS_END] = {8, 5, 15, 8};
FontCacheSettings _fcsettings; FontCacheSettings _fcsettings;
@ -32,8 +33,7 @@ FontCacheSettings _fcsettings;
* Create a new font cache. * Create a new font cache.
* @param fs The size of the font. * @param fs The size of the font.
*/ */
FontCache::FontCache(FontSize fs) : parent(FontCache::Get(fs)), fs(fs), height(_default_font_height[fs]), FontCache::FontCache(FontSize fs) : parent(FontCache::Get(fs)), fs(fs)
ascender(_default_font_ascender[fs]), descender(_default_font_ascender[fs] - _default_font_height[fs])
{ {
assert(this->parent == nullptr || this->fs == this->parent->fs); assert(this->parent == nullptr || this->fs == this->parent->fs);
FontCache::caches[this->fs] = this; FontCache::caches[this->fs] = this;
@ -50,7 +50,7 @@ FontCache::~FontCache()
int FontCache::GetDefaultFontHeight(FontSize fs) int FontCache::GetDefaultFontHeight(FontSize fs)
{ {
return _default_font_height[fs]; return FontCache::DEFAULT_FONT_HEIGHT[fs];
} }
/** /**

View File

@ -23,9 +23,9 @@ protected:
static FontCache *caches[FS_END]; ///< All the font caches. static FontCache *caches[FS_END]; ///< All the font caches.
FontCache *parent; ///< The parent of this font cache. FontCache *parent; ///< The parent of this font cache.
const FontSize fs; ///< The size of the font. const FontSize fs; ///< The size of the font.
int height; ///< The height of the font. int height = 0; ///< The height of the font.
int ascender; ///< The ascender value of the font. int ascender = 0; ///< The ascender value of the font.
int descender; ///< The descender value of the font. int descender = 0; ///< The descender value of the font.
public: public:
FontCache(FontSize fs); FontCache(FontSize fs);
@ -33,6 +33,11 @@ public:
static void InitializeFontCaches(); static void InitializeFontCaches();
/** Default unscaled font heights. */
static const int DEFAULT_FONT_HEIGHT[FS_END];
/** Default unscaled font ascenders. */
static const int DEFAULT_FONT_ASCENDER[FS_END];
static int GetDefaultFontHeight(FontSize fs); static int GetDefaultFontHeight(FontSize fs);
/** /**

View File

@ -78,7 +78,7 @@ template <> SQInteger PushClassName<GameInfo, ScriptType::GS>(HSQUIRRELVM vm) {
/* Remove the link to the real instance, else it might get deleted by RegisterGame() */ /* Remove the link to the real instance, else it might get deleted by RegisterGame() */
sq_setinstanceup(vm, 2, nullptr); sq_setinstanceup(vm, 2, nullptr);
/* Register the Game to the base system */ /* Register the Game to the base system */
info->GetScanner()->RegisterScript(info); info->GetScanner()->RegisterScript(std::unique_ptr<GameInfo>{info});
return 0; return 0;
} }
@ -106,22 +106,21 @@ bool GameInfo::CanLoadFromVersion(int version) const
/* static */ SQInteger GameLibrary::Constructor(HSQUIRRELVM vm) /* static */ SQInteger GameLibrary::Constructor(HSQUIRRELVM vm)
{ {
/* Create a new library */ /* Create a new library */
GameLibrary *library = new GameLibrary(); auto library = std::make_unique<GameLibrary>();
SQInteger res = ScriptInfo::Constructor(vm, *library); SQInteger res = ScriptInfo::Constructor(vm, *library);
if (res != 0) { if (res != 0) {
delete library;
return res; return res;
} }
/* Cache the category */ /* Cache the category */
if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethod(library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) { if (!library->CheckMethod("GetCategory") || !library->engine->CallStringMethod(library->SQ_instance, "GetCategory", &library->category, MAX_GET_OPS)) {
delete library;
return SQ_ERROR; return SQ_ERROR;
} }
/* Register the Library to the base system */ /* Register the Library to the base system */
library->GetScanner()->RegisterScript(library); ScriptScanner *scanner = library->GetScanner();
scanner->RegisterScript(std::move(library));
return 0; return 0;
} }

View File

@ -92,8 +92,8 @@ GameLibrary *GameScannerLibrary::FindLibrary(const std::string &library, int ver
std::string library_name = fmt::format("{}.{}", library, version); std::string library_name = fmt::format("{}.{}", library, version);
/* Check if the library + version exists */ /* Check if the library + version exists */
ScriptInfoList::iterator it = this->info_list.find(library_name); auto it = this->info_list.find(library_name);
if (it == this->info_list.end()) return nullptr; if (it == this->info_list.end()) return nullptr;
return static_cast<GameLibrary *>((*it).second); return static_cast<GameLibrary *>(it->second);
} }

View File

@ -12,6 +12,40 @@
#include "../tile_cmd.h" #include "../tile_cmd.h"
#include "../waypoint_base.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<TileIndex> GetShipDestinationTiles(const Ship *v)
{
std::vector<TileIndex> 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 * Calculates the tile of given station that is closest to a given tile

View File

@ -33,8 +33,7 @@ public:
typedef typename Node::Key Key; ///< key to hash tables. typedef typename Node::Key Key; ///< key to hash tables.
protected: protected:
TileIndex dest_tile; std::span<TileIndex> dest_tiles;
TrackdirBits dest_trackdirs;
StationID dest_station; StationID dest_station;
bool has_intermediate_dest = false; bool has_intermediate_dest = false;
@ -42,16 +41,13 @@ protected:
WaterRegionPatchDesc intermediate_dest_region_patch; WaterRegionPatchDesc intermediate_dest_region_patch;
public: public:
void SetDestination(const Ship *v) void SetDestination(const Ship *v, const std::span<TileIndex> destination_tiles)
{ {
this->dest_tiles = destination_tiles;
if (v->current_order.IsType(OT_GOTO_STATION)) { if (v->current_order.IsType(OT_GOTO_STATION)) {
this->dest_station = v->current_order.GetDestination().ToStationID(); 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 { } else {
this->dest_station = StationID::Invalid(); 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. */ /** Called by YAPF to detect if node ends in the desired destination. */
inline bool PfDetectDestination(Node &n) 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) { if (this->has_intermediate_dest) {
/* GetWaterRegionInfo is much faster than GetWaterRegionPatchInfo so we try that first. */ /* GetWaterRegionInfo is much faster than GetWaterRegionPatchInfo so we try that first. */
if (GetWaterRegionInfo(tile) != this->intermediate_dest_region_patch) return false; 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); 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();
} }
/** static inline int CalcEstimate(Node &n, TileIndex destination_tile)
* 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)
{ {
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_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)) {
n.estimate = n.cost;
return true;
}
TileIndex tile = n.segment_last_tile; TileIndex tile = n.segment_last_tile;
DiagDirection exitdir = TrackdirToExitdir(n.segment_last_td); DiagDirection exitdir = TrackdirToExitdir(n.segment_last_td);
@ -115,8 +99,33 @@ public:
int dmin = std::min(dx, dy); int dmin = std::min(dx, dy);
int dxy = abs(dx - dy); int dxy = abs(dx - dy);
int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2); int d = dmin * YAPF_TILE_CORNER_LENGTH + (dxy - 1) * (YAPF_TILE_LENGTH / 2);
n.estimate = n.cost + d; int estimate = n.cost + d;
assert(n.estimate >= n.parent->estimate); 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<int>::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; return true;
} }
}; };
@ -211,10 +220,10 @@ 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, const std::span<TileIndex> dest_tiles,
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); const std::vector<WaterRegionPatchDesc> high_level_path = YapfShipFindWaterRegionPath(v, tile, NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1, dest_tiles);
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. */
@ -229,7 +238,7 @@ 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); pf.SetDestination(v, dest_tiles);
const bool is_intermediate_destination = static_cast<int>(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1; const bool is_intermediate_destination = static_cast<int>(high_level_path.size()) >= NUMBER_OR_WATER_REGIONS_LOOKAHEAD + 1;
if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back()); if (is_intermediate_destination) pf.SetIntermediateDestination(high_level_path.back());
@ -297,9 +306,10 @@ public:
* Called when leaving depot. * Called when leaving depot.
* @param v Ship. * @param v Ship.
* @param trackdir [out] the best of all possible reversed trackdirs. * @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. * @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<TileIndex> dest_tiles)
{ {
bool path_found = false; bool path_found = false;
ShipPathCache dummy_cache; ShipPathCache dummy_cache;
@ -310,13 +320,13 @@ 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, v->tile, forward_dirs, reverse_dirs, dest_tiles, 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, 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); *trackdir = path_found && best_origin_dir != INVALID_TRACKDIR ? best_origin_dir : GetRandomTrackdir(reverse_dirs);
return true; return true;
} }
@ -420,13 +430,15 @@ struct CYapfShip : CYapfT<CYapfShip_TypesT<CYapfShip, CFollowTrackWater, CShipNo
/** Ship controller helper - path finder invoker. */ /** Ship controller helper - path finder invoker. */
Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipPathCache &path_cache) Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipPathCache &path_cache)
{ {
std::vector<TileIndex> dest_tiles = GetShipDestinationTiles(v);
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, dest_tiles, 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;
} }
bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir) bool YapfShipCheckReverse(const Ship *v, Trackdir *trackdir)
{ {
return CYapfShip::CheckShipReverse(v, trackdir); std::vector<TileIndex> dest_tiles = GetShipDestinationTiles(v);
return CYapfShip::CheckShipReverse(v, trackdir, dest_tiles);
} }

View File

@ -175,7 +175,7 @@ public:
inline char TransportTypeChar() const { return '^'; } inline char TransportTypeChar() const { return '^'; }
static std::vector<WaterRegionPatchDesc> FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length) static std::vector<WaterRegionPatchDesc> FindWaterRegionPath(const Ship *v, TileIndex start_tile, int max_returned_path_length, const std::span<TileIndex> dest_tiles)
{ {
const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(start_tile); const WaterRegionPatchDesc start_water_region_patch = GetWaterRegionPatchInfo(start_tile);
@ -184,18 +184,7 @@ public:
Tpf pf(std::min(static_cast<int>(Map::Size() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES)); Tpf pf(std::min(static_cast<int>(Map::Size() * NODES_PER_REGION) / WATER_REGION_NUMBER_OF_TILES, MAX_NUMBER_OF_NODES));
pf.SetDestination(start_water_region_patch); pf.SetDestination(start_water_region_patch);
if (v->current_order.IsType(OT_GOTO_STATION)) { for (const TileIndex &tile : dest_tiles) {
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;
pf.AddOrigin(GetWaterRegionPatchInfo(tile)); pf.AddOrigin(GetWaterRegionPatchInfo(tile));
} }
@ -292,9 +281,10 @@ struct CYapfRegionWater : CYapfT<CYapfRegion_TypesT<CYapfRegionWater, CRegionNod
* @param v The ship to find a path for. * @param v The ship to find a path for.
* @param start_tile The tile to start searching from. * @param start_tile The tile to start searching from.
* @param max_returned_path_length The maximum length of the path that will be returned. * @param max_returned_path_length The maximum length of the path that will be returned.
* @param dest_tiles List of destination tiles.
* @returns A path of water region patches, or an empty vector if no path was found. * @returns A path of water region patches, or an empty vector if no path was found.
*/ */
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 std::span<TileIndex> dest_tiles)
{ {
return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length); return CYapfRegionWater::FindWaterRegionPath(v, start_tile, max_returned_path_length, dest_tiles);
} }

View File

@ -15,6 +15,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, const std::span<TileIndex> dest_tiles);
#endif /* YAPF_SHIP_REGIONS_H */ #endif /* YAPF_SHIP_REGIONS_H */

View File

@ -44,10 +44,7 @@ bool ScriptScanner::AddFile(const std::string &filename, size_t, const std::stri
return true; return true;
} }
ScriptScanner::ScriptScanner() : ScriptScanner::ScriptScanner() = default;
engine(nullptr)
{
}
void ScriptScanner::ResetEngine() void ScriptScanner::ResetEngine()
{ {
@ -58,7 +55,7 @@ void ScriptScanner::ResetEngine()
void ScriptScanner::Initialize(std::string_view name) void ScriptScanner::Initialize(std::string_view name)
{ {
this->engine = new Squirrel(name); this->engine = std::make_unique<Squirrel>(name);
this->RescanDir(); this->RescanDir();
@ -68,8 +65,6 @@ void ScriptScanner::Initialize(std::string_view name)
ScriptScanner::~ScriptScanner() ScriptScanner::~ScriptScanner()
{ {
this->Reset(); this->Reset();
delete this->engine;
} }
void ScriptScanner::RescanDir() void ScriptScanner::RescanDir()
@ -83,15 +78,12 @@ void ScriptScanner::RescanDir()
void ScriptScanner::Reset() void ScriptScanner::Reset()
{ {
for (const auto &item : this->info_list) {
delete item.second;
}
this->info_list.clear(); this->info_list.clear();
this->info_single_list.clear(); this->info_single_list.clear();
this->info_vector.clear();
} }
void ScriptScanner::RegisterScript(ScriptInfo *info) void ScriptScanner::RegisterScript(std::unique_ptr<ScriptInfo> &&info)
{ {
std::string script_original_name = this->GetScriptName(*info); std::string script_original_name = this->GetScriptName(*info);
std::string script_name = fmt::format("{}.{}", script_original_name, info->GetVersion()); std::string script_name = fmt::format("{}.{}", script_original_name, info->GetVersion());
@ -99,7 +91,6 @@ void ScriptScanner::RegisterScript(ScriptInfo *info)
/* Check if GetShortName follows the rules */ /* Check if GetShortName follows the rules */
if (info->GetShortName().size() != 4) { if (info->GetShortName().size() != 4) {
Debug(script, 0, "The script '{}' returned a string from GetShortName() which is not four characters. Unable to load the script.", info->GetName()); Debug(script, 0, "The script '{}' returned a string from GetShortName() which is not four characters. Unable to load the script.", info->GetName());
delete info;
return; return;
} }
@ -111,7 +102,6 @@ void ScriptScanner::RegisterScript(ScriptInfo *info)
#else #else
if (it->second->GetMainScript() == info->GetMainScript()) { if (it->second->GetMainScript() == info->GetMainScript()) {
#endif #endif
delete info;
return; return;
} }
@ -120,20 +110,20 @@ void ScriptScanner::RegisterScript(ScriptInfo *info)
Debug(script, 1, " 2: {}", info->GetMainScript()); Debug(script, 1, " 2: {}", info->GetMainScript());
Debug(script, 1, "The first is taking precedence."); Debug(script, 1, "The first is taking precedence.");
delete info;
return; return;
} }
this->info_list[script_name] = info; ScriptInfo *script_info = this->info_vector.emplace_back(std::move(info)).get();
this->info_list[script_name] = script_info;
if (!info->IsDeveloperOnly() || _settings_client.gui.ai_developer_tools) { if (!script_info->IsDeveloperOnly() || _settings_client.gui.ai_developer_tools) {
/* Add the script to the 'unique' script list, where only the highest version /* Add the script to the 'unique' script list, where only the highest version
* of the script is registered. */ * of the script is registered. */
auto it = this->info_single_list.find(script_original_name); auto it = this->info_single_list.find(script_original_name);
if (it == this->info_single_list.end()) { if (it == this->info_single_list.end()) {
this->info_single_list[script_original_name] = info; this->info_single_list[script_original_name] = script_info;
} else if (it->second->GetVersion() < info->GetVersion()) { } else if (it->second->GetVersion() < script_info->GetVersion()) {
it->second = info; it->second = script_info;
} }
} }
} }
@ -195,17 +185,17 @@ struct ScriptFileChecksumCreator : FileScanner {
* @param info The script to get the shortname and md5 sum from. * @param info The script to get the shortname and md5 sum from.
* @return True iff they're the same. * @return True iff they're the same.
*/ */
static bool IsSameScript(const ContentInfo &ci, bool md5sum, ScriptInfo *info, Subdirectory dir) static bool IsSameScript(const ContentInfo &ci, bool md5sum, const ScriptInfo &info, Subdirectory dir)
{ {
uint32_t id = 0; uint32_t id = 0;
auto str = std::string_view{info->GetShortName()}.substr(0, 4); auto str = std::string_view{info.GetShortName()}.substr(0, 4);
for (size_t j = 0; j < str.size(); j++) id |= static_cast<uint8_t>(str[j]) << (8 * j); for (size_t j = 0; j < str.size(); j++) id |= static_cast<uint8_t>(str[j]) << (8 * j);
if (id != ci.unique_id) return false; if (id != ci.unique_id) return false;
if (!md5sum) return true; if (!md5sum) return true;
ScriptFileChecksumCreator checksum(dir); ScriptFileChecksumCreator checksum(dir);
const auto &tar_filename = info->GetTarFile(); const auto &tar_filename = info.GetTarFile();
TarList::iterator iter; TarList::iterator iter;
if (!tar_filename.empty() && (iter = _tar_list[dir].find(tar_filename)) != _tar_list[dir].end()) { if (!tar_filename.empty() && (iter = _tar_list[dir].find(tar_filename)) != _tar_list[dir].end()) {
/* The main script is in a tar file, so find all files that /* The main script is in a tar file, so find all files that
@ -224,7 +214,7 @@ static bool IsSameScript(const ContentInfo &ci, bool md5sum, ScriptInfo *info, S
/* There'll always be at least 1 path separator character in a script /* There'll always be at least 1 path separator character in a script
* main script name as the search algorithm requires the main script to * main script name as the search algorithm requires the main script to
* be in a subdirectory of the script directory; so <dir>/<path>/main.nut. */ * be in a subdirectory of the script directory; so <dir>/<path>/main.nut. */
const std::string &main_script = info->GetMainScript(); const std::string &main_script = info.GetMainScript();
std::string path = main_script.substr(0, main_script.find_last_of(PATHSEPCHAR)); std::string path = main_script.substr(0, main_script.find_last_of(PATHSEPCHAR));
checksum.Scan(".nut", path); checksum.Scan(".nut", path);
} }
@ -235,7 +225,7 @@ static bool IsSameScript(const ContentInfo &ci, bool md5sum, ScriptInfo *info, S
bool ScriptScanner::HasScript(const ContentInfo &ci, bool md5sum) bool ScriptScanner::HasScript(const ContentInfo &ci, bool md5sum)
{ {
for (const auto &item : this->info_list) { for (const auto &item : this->info_list) {
if (IsSameScript(ci, md5sum, item.second, this->GetDirectory())) return true; if (IsSameScript(ci, md5sum, *item.second, this->GetDirectory())) return true;
} }
return false; return false;
} }
@ -243,7 +233,7 @@ bool ScriptScanner::HasScript(const ContentInfo &ci, bool md5sum)
std::optional<std::string_view> ScriptScanner::FindMainScript(const ContentInfo &ci, bool md5sum) std::optional<std::string_view> ScriptScanner::FindMainScript(const ContentInfo &ci, bool md5sum)
{ {
for (const auto &item : this->info_list) { for (const auto &item : this->info_list) {
if (IsSameScript(ci, md5sum, item.second, this->GetDirectory())) return item.second->GetMainScript(); if (IsSameScript(ci, md5sum, *item.second, this->GetDirectory())) return item.second->GetMainScript();
} }
return std::nullopt; return std::nullopt;
} }

View File

@ -13,7 +13,7 @@
#include "../fileio_func.h" #include "../fileio_func.h"
#include "../string_func.h" #include "../string_func.h"
typedef std::map<std::string, class ScriptInfo *, CaseInsensitiveComparator> ScriptInfoList; ///< Type for the list of scripts. using ScriptInfoList = std::map<std::string, class ScriptInfo *, CaseInsensitiveComparator>; ///< Type for the list of scripts.
/** Scanner to help finding scripts. */ /** Scanner to help finding scripts. */
class ScriptScanner : public FileScanner { class ScriptScanner : public FileScanner {
@ -26,7 +26,7 @@ public:
/** /**
* Get the engine of the main squirrel handler (it indexes all available scripts). * Get the engine of the main squirrel handler (it indexes all available scripts).
*/ */
class Squirrel *GetEngine() { return this->engine; } class Squirrel *GetEngine() { return this->engine.get(); }
/** /**
* Get the current main script the ScanDir is currently tracking. * Get the current main script the ScanDir is currently tracking.
@ -51,7 +51,7 @@ public:
/** /**
* Register a ScriptInfo to the scanner. * Register a ScriptInfo to the scanner.
*/ */
void RegisterScript(class ScriptInfo *info); void RegisterScript(std::unique_ptr<class ScriptInfo> &&info);
/** /**
* Get the list of registered scripts to print on the console. * Get the list of registered scripts to print on the console.
@ -84,11 +84,13 @@ public:
void RescanDir(); void RescanDir();
protected: protected:
class Squirrel *engine; ///< The engine we're scanning with. std::unique_ptr<class Squirrel> engine; ///< The engine we're scanning with.
std::string main_script; ///< The full path of the script. std::string main_script; ///< The full path of the script.
std::string tar_file; ///< If, which tar file the script was in. std::string tar_file; ///< If, which tar file the script was in.
ScriptInfoList info_list; ///< The list of all script. std::vector<std::unique_ptr<ScriptInfo>> info_vector;
ScriptInfoList info_list; ///< The list of all script.
ScriptInfoList info_single_list; ///< The list of all unique script. The best script (highest version) is shown. ScriptInfoList info_single_list; ///< The list of all unique script. The best script (highest version) is shown.
/** /**

View File

@ -401,6 +401,40 @@ static void CancelIMEComposition(HWND hwnd)
HandleTextInput({}, true); HandleTextInput({}, true);
} }
#if defined(_MSC_VER) && defined(NTDDI_WIN10_RS4)
/* We only use WinRT functions on Windows 10 or later. Unfortunately, newer Windows SDKs are now
* linking the two functions below directly instead of using dynamic linking as previously.
* To avoid any runtime linking errors on Windows 7 or older, we stub in our own dynamic
* linking trampoline. */
static LibraryLoader _combase("combase.dll");
extern "C" int32_t __stdcall WINRT_IMPL_RoOriginateLanguageException(int32_t error, void *message, void *languageException) noexcept
{
typedef BOOL(WINAPI *PFNRoOriginateLanguageException)(int32_t, void *, void *);
static PFNRoOriginateLanguageException RoOriginateLanguageException = _combase.GetFunction("RoOriginateLanguageException");
if (RoOriginateLanguageException != nullptr) {
return RoOriginateLanguageException(error, message, languageException);
} else {
return TRUE;
}
}
extern "C" int32_t __stdcall WINRT_IMPL_RoGetActivationFactory(void *classId, winrt::guid const &iid, void **factory) noexcept
{
typedef BOOL(WINAPI *PFNRoGetActivationFactory)(void *, winrt::guid const &, void **);
static PFNRoGetActivationFactory RoGetActivationFactory = _combase.GetFunction("RoGetActivationFactory");
if (RoGetActivationFactory != nullptr) {
return RoGetActivationFactory(classId, iid, factory);
} else {
*factory = nullptr;
return winrt::impl::error_class_not_available;
}
}
#endif
static bool IsDarkModeEnabled() static bool IsDarkModeEnabled()
{ {
/* Only build if SDK is Windows 10 1803 or later. */ /* Only build if SDK is Windows 10 1803 or later. */