mirror of https://github.com/OpenTTD/OpenTTD
Compare commits
6 Commits
bc0cd0e3ec
...
47f204575f
Author | SHA1 | Date |
---|---|---|
|
47f204575f | |
|
03f5f7145f | |
|
0dc40877fd | |
|
8031d68d01 | |
|
7df9426000 | |
|
0b44deedd0 |
|
@ -110,7 +110,7 @@ struct Aircraft final : public SpecializedVehicle<Aircraft, VEH_AIRCRAFT> {
|
|||
uint Crash(bool flooded = false) override;
|
||||
TileIndex GetOrderStationLocation(StationID station) override;
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
/* If the station is not a valid airport or if it has no hangars */
|
||||
|
|
|
@ -2369,7 +2369,7 @@ static bool ConFont(std::span<std::string_view> argv)
|
|||
FontCacheSubSetting *setting = GetFontCacheSubSetting(fs);
|
||||
/* Make sure all non sprite fonts are loaded. */
|
||||
if (!setting->font.empty() && !fc->HasParent()) {
|
||||
InitFontCache(fs == FS_MONO);
|
||||
InitFontCache(fs);
|
||||
fc = FontCache::Get(fs);
|
||||
}
|
||||
IConsolePrint(CC_DEFAULT, "{} font:", FontSizeToName(fs));
|
||||
|
|
|
@ -126,10 +126,10 @@ void SetFont(FontSize fontsize, const std::string &font, uint size)
|
|||
CheckForMissingGlyphs();
|
||||
_fcsettings = std::move(backup);
|
||||
} else {
|
||||
InitFontCache(true);
|
||||
InitFontCache(fontsize);
|
||||
}
|
||||
|
||||
LoadStringWidthTable(fontsize == FS_MONO);
|
||||
LoadStringWidthTable(fontsize);
|
||||
UpdateAllVirtCoords();
|
||||
ReInitAllWindows(true);
|
||||
|
||||
|
@ -213,15 +213,13 @@ std::string GetFontCacheFontName(FontSize fs)
|
|||
|
||||
/**
|
||||
* (Re)initialize the font cache related things, i.e. load the non-sprite fonts.
|
||||
* @param monospace Whether to initialise the monospace or regular fonts.
|
||||
* @param fontsizes Font sizes to be initialised.
|
||||
*/
|
||||
void InitFontCache(bool monospace)
|
||||
void InitFontCache(FontSizes fontsizes)
|
||||
{
|
||||
FontCache::InitializeFontCaches();
|
||||
|
||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||
if (monospace != (fs == FS_MONO)) continue;
|
||||
|
||||
for (FontSize fs : fontsizes) {
|
||||
FontCache *fc = FontCache::Get(fs);
|
||||
if (fc->HasParent()) delete fc;
|
||||
|
||||
|
|
|
@ -167,9 +167,9 @@ inline void InitializeUnicodeGlyphMap()
|
|||
}
|
||||
}
|
||||
|
||||
inline void ClearFontCache()
|
||||
inline void ClearFontCache(FontSizes fontsizes)
|
||||
{
|
||||
for (FontSize fs = FS_BEGIN; fs < FS_END; fs++) {
|
||||
for (FontSize fs : fontsizes) {
|
||||
FontCache::Get(fs)->ClearFontCache();
|
||||
}
|
||||
}
|
||||
|
@ -231,7 +231,7 @@ inline FontCacheSubSetting *GetFontCacheSubSetting(FontSize fs)
|
|||
|
||||
uint GetFontCacheFontSize(FontSize fs);
|
||||
std::string GetFontCacheFontName(FontSize fs);
|
||||
void InitFontCache(bool monospace);
|
||||
void InitFontCache(FontSizes fontsizes);
|
||||
void UninitFontCache();
|
||||
|
||||
bool GetFontAAState();
|
||||
|
|
29
src/gfx.cpp
29
src/gfx.cpp
|
@ -8,6 +8,7 @@
|
|||
/** @file gfx.cpp Handling of drawing text and other gfx related stuff. */
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "gfx_func.h"
|
||||
#include "gfx_layout.h"
|
||||
#include "progress.h"
|
||||
#include "zoom_func.h"
|
||||
|
@ -579,7 +580,7 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
|||
|
||||
const uint shadow_offset = ScaleGUITrad(1);
|
||||
|
||||
auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation) {
|
||||
auto draw_line = [&](const ParagraphLayouter::Line &line, bool do_shadow, int left, int min_x, int max_x, bool truncation, TextColour &last_colour) {
|
||||
const DrawPixelInfo *dpi = _cur_dpi;
|
||||
int dpi_left = dpi->left;
|
||||
int dpi_right = dpi->left + dpi->width - 1;
|
||||
|
@ -592,10 +593,15 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
|||
|
||||
FontCache *fc = f->fc;
|
||||
TextColour colour = f->colour;
|
||||
if (colour == TC_INVALID || HasFlag(default_colour, TC_FORCED)) colour = default_colour;
|
||||
if (colour == TC_INVALID || HasFlag(last_colour, TC_FORCED)) {
|
||||
colour = last_colour;
|
||||
} else {
|
||||
/* Update the last colour for the truncation ellipsis. */
|
||||
last_colour = colour;
|
||||
}
|
||||
bool colour_has_shadow = (colour & TC_NO_SHADE) == 0 && colour != TC_BLACK;
|
||||
SetColourRemap(do_shadow ? TC_BLACK : colour); // the last run also sets the colour for the truncation dots
|
||||
if (do_shadow && (!fc->GetDrawGlyphShadow() || !colour_has_shadow)) continue;
|
||||
SetColourRemap(do_shadow ? TC_BLACK : colour);
|
||||
|
||||
for (int i = 0; i < run.GetGlyphCount(); i++) {
|
||||
GlyphID glyph = glyphs[i];
|
||||
|
@ -623,11 +629,12 @@ static int DrawLayoutLine(const ParagraphLayouter::Line &line, int y, int left,
|
|||
|
||||
/* Draw shadow, then foreground */
|
||||
for (bool do_shadow : {true, false}) {
|
||||
draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation);
|
||||
TextColour last_colour = default_colour;
|
||||
draw_line(line, do_shadow, left - offset_x, min_x, max_x, truncation, last_colour);
|
||||
|
||||
if (truncation) {
|
||||
int x = (_current_text_dir == TD_RTL) ? left : (right - truncation_width);
|
||||
draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false);
|
||||
draw_line(*truncation_layout->front(), do_shadow, x, INT32_MIN, INT32_MAX, false, last_colour);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1240,14 +1247,14 @@ static void GfxMainBlitter(const Sprite *sprite, int x, int y, BlitterMode mode,
|
|||
}
|
||||
|
||||
/**
|
||||
* Initialize _stringwidth_table cache
|
||||
* @param monospace Whether to load the monospace cache or the normal fonts.
|
||||
* Initialize _stringwidth_table cache for the specified font sizes.
|
||||
* @param fontsizes Font sizes to initialise.
|
||||
*/
|
||||
void LoadStringWidthTable(bool monospace)
|
||||
void LoadStringWidthTable(FontSizes fontsizes)
|
||||
{
|
||||
ClearFontCache();
|
||||
ClearFontCache(fontsizes);
|
||||
|
||||
for (FontSize fs = monospace ? FS_MONO : FS_BEGIN; fs < (monospace ? FS_END : FS_MONO); fs++) {
|
||||
for (FontSize fs : fontsizes) {
|
||||
for (uint i = 0; i != 224; i++) {
|
||||
_stringwidth_table[fs][i] = GetGlyphWidth(fs, i + 32);
|
||||
}
|
||||
|
@ -1812,7 +1819,7 @@ bool AdjustGUIZoom(bool automatic)
|
|||
if (old_font_zoom != _font_zoom) {
|
||||
GfxClearFontSpriteCache();
|
||||
}
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
LoadStringWidthTable();
|
||||
|
||||
SetupWidgetDimensions();
|
||||
|
|
|
@ -149,7 +149,7 @@ int GetStringHeight(StringID str, int maxw);
|
|||
int GetStringLineCount(std::string_view str, int maxw);
|
||||
Dimension GetStringMultiLineBoundingBox(StringID str, const Dimension &suggestion);
|
||||
Dimension GetStringMultiLineBoundingBox(std::string_view str, const Dimension &suggestion, FontSize fontsize = FS_NORMAL);
|
||||
void LoadStringWidthTable(bool monospace = false);
|
||||
void LoadStringWidthTable(FontSizes fontsizes = FONTSIZES_REQUIRED);
|
||||
|
||||
void DrawDirtyBlocks();
|
||||
void AddDirtyBlock(int left, int top, int right, int bottom);
|
||||
|
|
|
@ -258,6 +258,13 @@ enum FontSize : uint8_t {
|
|||
};
|
||||
DECLARE_INCREMENT_DECREMENT_OPERATORS(FontSize)
|
||||
|
||||
using FontSizes = EnumBitSet<FontSize, uint8_t>;
|
||||
|
||||
/** Mask of all possible font sizes. */
|
||||
constexpr FontSizes FONTSIZES_ALL{FS_NORMAL, FS_SMALL, FS_LARGE, FS_MONO};
|
||||
/** Mask of font sizes required to be present. */
|
||||
constexpr FontSizes FONTSIZES_REQUIRED{FS_NORMAL, FS_SMALL, FS_LARGE};
|
||||
|
||||
inline std::string_view FontSizeToName(FontSize fs)
|
||||
{
|
||||
static const std::string_view SIZE_TO_NAME[] = { "medium", "small", "large", "mono" };
|
||||
|
|
|
@ -245,7 +245,7 @@ static void RealChangeBlitter(std::string_view repl_blitter)
|
|||
|
||||
/* Clear caches that might have sprites for another blitter. */
|
||||
VideoDriver::GetInstance()->ClearSystemSprites();
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
GfxClearSpriteCache();
|
||||
ReInitAllWindows(false);
|
||||
}
|
||||
|
@ -326,7 +326,7 @@ void CheckBlitter()
|
|||
{
|
||||
if (!SwitchNewGRFBlitter()) return;
|
||||
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
GfxClearSpriteCache();
|
||||
ReInitAllWindows(false);
|
||||
}
|
||||
|
@ -338,7 +338,7 @@ void GfxLoadSprites()
|
|||
|
||||
SwitchNewGRFBlitter();
|
||||
VideoDriver::GetInstance()->ClearSystemSprites();
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
GfxInitSpriteMem();
|
||||
LoadSpriteTables();
|
||||
GfxInitPalettes();
|
||||
|
|
|
@ -700,7 +700,7 @@ int openttd_main(std::span<std::string_view> arguments)
|
|||
InitializeLanguagePacks();
|
||||
|
||||
/* Initialize the font cache */
|
||||
InitFontCache(false);
|
||||
InitFontCache(FONTSIZES_REQUIRED);
|
||||
|
||||
/* This must be done early, since functions use the SetWindowDirty* calls */
|
||||
InitWindowSystem();
|
||||
|
|
|
@ -1896,10 +1896,11 @@ VehicleOrderID ProcessConditionalOrder(const Order *order, const Vehicle *v)
|
|||
* Update the vehicle's destination tile from an order.
|
||||
* @param order the order the vehicle currently has
|
||||
* @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 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()) {
|
||||
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;
|
||||
|
||||
/* 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) {
|
||||
/* PBS reservations cannot reverse */
|
||||
|
@ -2018,7 +2019,7 @@ bool UpdateOrderDest(Vehicle *v, const Order *order, int conditional_depth, bool
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 DeleteVehicleOrders(Vehicle *v, bool keep_orderlist = false, bool reset_order_indices = true);
|
||||
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);
|
||||
uint GetOrderDistance(VehicleOrderID prev, VehicleOrderID cur, const Vehicle *v, int conditional_depth = 0);
|
||||
|
||||
|
|
|
@ -182,6 +182,6 @@ bool SetFallbackFont(FontCacheSettings *settings, const std::string &language_is
|
|||
if (best_font == nullptr) return false;
|
||||
|
||||
callback->SetFontNames(settings, best_font, &best_index);
|
||||
InitFontCache(callback->Monospace());
|
||||
InitFontCache(callback->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -430,3 +430,83 @@ void PrintWaterRegionDebugInfo(TileIndex tile)
|
|||
{
|
||||
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);
|
||||
|
||||
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 */
|
||||
|
|
|
@ -34,6 +34,17 @@ Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipP
|
|||
*/
|
||||
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.
|
||||
* @param v the RV that needs to find a path
|
||||
|
|
|
@ -36,6 +36,7 @@ protected:
|
|||
TileIndex dest_tile;
|
||||
TrackdirBits dest_trackdirs;
|
||||
StationID dest_station;
|
||||
bool any_ship_depot = false;
|
||||
|
||||
bool has_intermediate_dest = false;
|
||||
TileIndex intermediate_dest_tile;
|
||||
|
@ -55,6 +56,11 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void SetAnyShipDepotDestination()
|
||||
{
|
||||
this->any_ship_depot = true;
|
||||
}
|
||||
|
||||
void SetIntermediateDestination(const WaterRegionPatchDesc &water_region_patch)
|
||||
{
|
||||
this->has_intermediate_dest = true;
|
||||
|
@ -69,10 +75,16 @@ protected:
|
|||
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:
|
||||
/** Called by YAPF to detect if node ends in the desired destination. */
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -89,6 +101,11 @@ public:
|
|||
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
|
||||
* 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_y_offs[] = { 0, 1, 0, -1 };
|
||||
if (this->PfDetectDestination(n)) {
|
||||
if (this->any_ship_depot || this->PfDetectDestination(n)) {
|
||||
n.estimate = n.cost;
|
||||
return true;
|
||||
}
|
||||
|
@ -158,7 +175,7 @@ public:
|
|||
}
|
||||
|
||||
/** 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();
|
||||
for (const WaterRegionPatchDesc &path_entry : path) this->water_region_corridor.push_back(path_entry);
|
||||
|
@ -211,16 +228,20 @@ 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, int max_penalty,
|
||||
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()) {
|
||||
path_found = false;
|
||||
/* 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);
|
||||
}
|
||||
|
||||
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.
|
||||
* 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. */
|
||||
|
@ -229,13 +250,28 @@ public:
|
|||
|
||||
/* Set origin and destination nodes */
|
||||
pf.SetOrigin(v->tile, forward_dirs | reverse_dirs);
|
||||
pf.SetDestination(v);
|
||||
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 (find_closest_depot) {
|
||||
pf.SetAnyShipDepotDestination();
|
||||
} 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
|
||||
* 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. */
|
||||
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. */
|
||||
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
|
||||
* 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. */
|
||||
|
@ -254,7 +296,7 @@ public:
|
|||
while (node->parent) {
|
||||
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;
|
||||
|
||||
/* 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;
|
||||
ShipPathCache dummy_cache;
|
||||
TileIndex tile = v->tile;
|
||||
Trackdir best_origin_dir = INVALID_TRACKDIR;
|
||||
|
||||
if (trackdir == nullptr) {
|
||||
|
@ -310,17 +353,45 @@ 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, tile, forward_dirs, reverse_dirs, 0, 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, 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);
|
||||
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. */
|
||||
|
@ -333,6 +404,11 @@ public:
|
|||
typedef typename Types::NodeList::Item Node; ///< this will be our node type.
|
||||
typedef typename Node::Key Key; ///< key to hash tables.
|
||||
|
||||
protected:
|
||||
int max_cost;
|
||||
|
||||
CYapfCostShipT() : max_cost(0) {}
|
||||
|
||||
/** to access inherited path finder */
|
||||
Tpf &Yapf()
|
||||
{
|
||||
|
@ -340,6 +416,11 @@ public:
|
|||
}
|
||||
|
||||
public:
|
||||
inline void SetMaxCost(int cost)
|
||||
{
|
||||
this->max_cost = cost;
|
||||
}
|
||||
|
||||
inline int CurveCost(Trackdir td1, Trackdir td2)
|
||||
{
|
||||
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;
|
||||
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. */
|
||||
n.cost = n.parent->cost + c;
|
||||
return true;
|
||||
|
@ -422,7 +507,7 @@ Track YapfShipChooseTrack(const Ship *v, TileIndex tile, bool &path_found, ShipP
|
|||
{
|
||||
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, 0, path_found, path_cache, best_origin_dir);
|
||||
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);
|
||||
}
|
||||
|
||||
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:
|
||||
Key dest;
|
||||
bool any_ship_depot = false;
|
||||
|
||||
public:
|
||||
void SetDestination(const WaterRegionPatchDesc &water_region_patch)
|
||||
|
@ -126,18 +127,32 @@ public:
|
|||
this->dest.Set(water_region_patch);
|
||||
}
|
||||
|
||||
void SetAnyShipDepotDestination()
|
||||
{
|
||||
this->any_ship_depot = true;
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
inline bool PfCalcEstimate(Node &n)
|
||||
{
|
||||
if (this->PfDetectDestination(n)) {
|
||||
if (this->any_ship_depot || this->PfDetectDestination(n)) {
|
||||
n.estimate = n.cost;
|
||||
return true;
|
||||
}
|
||||
|
@ -218,6 +233,31 @@ public:
|
|||
assert(!path.empty());
|
||||
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. */
|
||||
|
@ -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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -16,5 +16,6 @@
|
|||
struct Ship;
|
||||
|
||||
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 */
|
||||
|
|
|
@ -132,7 +132,7 @@ struct RoadVehicle final : public GroundVehicle<RoadVehicle, VEH_ROAD> {
|
|||
uint Crash(bool flooded = false) override;
|
||||
Trackdir GetVehicleTrackdir() const override;
|
||||
TileIndex GetOrderStationLocation(StationID station) override;
|
||||
ClosestDepot FindClosestDepot() override;
|
||||
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
|
||||
|
||||
bool IsBus() const;
|
||||
|
||||
|
|
|
@ -346,7 +346,7 @@ static FindDepotData FindClosestRoadDepot(const RoadVehicle *v, int max_distance
|
|||
return YapfRoadVehicleFindNearestDepot(v, max_distance);
|
||||
}
|
||||
|
||||
ClosestDepot RoadVehicle::FindClosestDepot()
|
||||
ClosestDepot RoadVehicle::FindClosestDepot([[maybe_unused]] bool may_reverse)
|
||||
{
|
||||
FindDepotData rfdd = FindClosestRoadDepot(this, 0);
|
||||
if (rfdd.best_length == UINT_MAX) return ClosestDepot();
|
||||
|
|
|
@ -1036,9 +1036,8 @@ struct GameOptionsWindow : Window {
|
|||
this->SetWidgetDisabledState(WID_GO_GUI_FONT_AA, _fcsettings.prefer_sprite);
|
||||
this->SetDirty();
|
||||
|
||||
InitFontCache(false);
|
||||
InitFontCache(true);
|
||||
ClearFontCache();
|
||||
InitFontCache(FONTSIZES_ALL);
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
CheckForMissingGlyphs();
|
||||
SetupWidgetDimensions();
|
||||
UpdateAllVirtCoords();
|
||||
|
@ -1051,7 +1050,7 @@ struct GameOptionsWindow : Window {
|
|||
this->SetWidgetLoweredState(WID_GO_GUI_FONT_AA, _fcsettings.global_aa);
|
||||
MarkWholeScreenDirty();
|
||||
|
||||
ClearFontCache();
|
||||
ClearFontCache(FONTSIZES_ALL);
|
||||
break;
|
||||
#endif /* HAS_TRUETYPE_FONT */
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ struct Ship final : public SpecializedVehicle<Ship, VEH_SHIP> {
|
|||
void OnNewEconomyDay() override;
|
||||
Trackdir GetVehicleTrackdir() const override;
|
||||
TileIndex GetOrderStationLocation(StationID station) override;
|
||||
ClosestDepot FindClosestDepot() override;
|
||||
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
|
||||
void UpdateCache();
|
||||
void SetDestTile(TileIndex tile) override;
|
||||
};
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#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"
|
||||
|
@ -39,13 +38,8 @@
|
|||
|
||||
#include "table/strings.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#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.
|
||||
* @param tile Tile of interest
|
||||
|
@ -148,57 +142,15 @@ void Ship::GetImage(Direction direction, EngineImageType image_type, VehicleSpri
|
|||
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;
|
||||
static std::deque<WaterRegionPatchDesc> patches_to_search;
|
||||
visited_patch_hashes.clear();
|
||||
patches_to_search.clear();
|
||||
FindDepotData sfdd = YapfShipFindNearestDepot(Ship::From(v), max_distance, may_reverse);
|
||||
|
||||
/* 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 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;
|
||||
if (sfdd.tile == INVALID_TILE) return nullptr;
|
||||
return Depot::GetByTile(sfdd.tile);
|
||||
}
|
||||
|
||||
static void CheckIfShipNeedsService(Vehicle *v)
|
||||
|
@ -209,7 +161,7 @@ static void CheckIfShipNeedsService(Vehicle *v)
|
|||
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);
|
||||
|
||||
|
@ -954,9 +906,9 @@ CommandCost CmdBuildShip(DoCommandFlags flags, TileIndex tile, const Engine *e,
|
|||
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();
|
||||
|
||||
return ClosestDepot(depot->xy, depot->index);
|
||||
|
|
|
@ -2278,7 +2278,7 @@ std::string_view GetCurrentLanguageIsoCode()
|
|||
*/
|
||||
bool MissingGlyphSearcher::FindMissingGlyphs()
|
||||
{
|
||||
InitFontCache(this->Monospace());
|
||||
InitFontCache(this->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
|
||||
this->Reset();
|
||||
for (auto text = this->NextString(); text.has_value(); text = this->NextString()) {
|
||||
|
@ -2395,7 +2395,7 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
|
|||
/* Our fallback font does miss characters too, so keep the
|
||||
* user chosen font as that is more likely to be any good than
|
||||
* the wild guess we made */
|
||||
InitFontCache(searcher->Monospace());
|
||||
InitFontCache(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -2412,12 +2412,12 @@ void CheckForMissingGlyphs(bool base_font, MissingGlyphSearcher *searcher)
|
|||
ShowErrorMessage(GetEncodedString(STR_JUST_RAW_STRING, std::move(err_str)), {}, WL_WARNING);
|
||||
|
||||
/* Reset the font width */
|
||||
LoadStringWidthTable(searcher->Monospace());
|
||||
LoadStringWidthTable(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update the font with cache */
|
||||
LoadStringWidthTable(searcher->Monospace());
|
||||
LoadStringWidthTable(searcher->Monospace() ? FontSizes{FS_MONO} : FONTSIZES_REQUIRED);
|
||||
|
||||
#if !(defined(WITH_ICU_I18N) && defined(WITH_HARFBUZZ)) && !defined(WITH_UNISCRIBE) && !defined(WITH_COCOA)
|
||||
/*
|
||||
|
|
|
@ -129,7 +129,7 @@ struct Train final : public GroundVehicle<Train, VEH_TRAIN> {
|
|||
uint Crash(bool flooded = false) override;
|
||||
Trackdir GetVehicleTrackdir() const override;
|
||||
TileIndex GetOrderStationLocation(StationID station) override;
|
||||
ClosestDepot FindClosestDepot() override;
|
||||
ClosestDepot FindClosestDepot(bool may_reverse = false) override;
|
||||
|
||||
void ReserveTrackUnderConsist() const;
|
||||
|
||||
|
|
|
@ -2194,7 +2194,7 @@ static FindDepotData FindClosestTrainDepot(Train *v, int max_distance)
|
|||
return YapfTrainFindNearestDepot(v, max_distance);
|
||||
}
|
||||
|
||||
ClosestDepot Train::FindClosestDepot()
|
||||
ClosestDepot Train::FindClosestDepot([[maybe_unused]] bool may_reverse)
|
||||
{
|
||||
FindDepotData tfdd = FindClosestTrainDepot(this, 0);
|
||||
if (tfdd.best_length == UINT_MAX) return ClosestDepot();
|
||||
|
|
|
@ -2583,7 +2583,7 @@ CommandCost Vehicle::SendToDepot(DoCommandFlags flags, DepotCommandFlags command
|
|||
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};
|
||||
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,
|
||||
* 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.
|
||||
*/
|
||||
virtual ClosestDepot FindClosestDepot() { return {}; }
|
||||
virtual ClosestDepot FindClosestDepot([[maybe_unused]] bool may_reverse = false) { return {}; }
|
||||
|
||||
virtual void SetDestTile(TileIndex tile) { this->dest_tile = tile; }
|
||||
|
||||
|
|
Loading…
Reference in New Issue