1
0
Fork 0

Compare commits

...

3 Commits

Author SHA1 Message Date
SamuXarick f10f5a145e
Merge f0382c37ee into a8650c6b06 2025-07-19 22:49:23 +00:00
Peter Nelson a8650c6b06
Codechange: Make SpriteCacheCtrlFlags an enum bit set. (#14462)
Due to header dependencies, this requires types to split from the spritecache header.
2025-07-19 23:49:15 +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
15 changed files with 138 additions and 82 deletions

View File

@ -454,6 +454,7 @@ add_files(
spritecache.cpp spritecache.cpp
spritecache.h spritecache.h
spritecache_internal.h spritecache_internal.h
spritecache_type.h
station.cpp station.cpp
station_base.h station_base.h
station_cmd.cpp station_cmd.cpp

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

@ -535,7 +535,7 @@ static void *ReadSprite(const SpriteCache *sc, SpriteID id, SpriteType sprite_ty
struct GrfSpriteOffset { struct GrfSpriteOffset {
size_t file_pos; size_t file_pos;
uint8_t control_flags; SpriteCacheCtrlFlags control_flags{};
}; };
/** Map from sprite numbers to position in the GRF file. */ /** Map from sprite numbers to position in the GRF file. */
@ -565,7 +565,7 @@ void ReadGRFSpriteOffsets(SpriteFile &file)
size_t old_pos = file.GetPos(); size_t old_pos = file.GetPos();
file.SeekTo(data_offset, SEEK_CUR); file.SeekTo(data_offset, SEEK_CUR);
GrfSpriteOffset offset = { 0, 0 }; GrfSpriteOffset offset{0};
/* Loop over all sprite section entries and store the file /* Loop over all sprite section entries and store the file
* offset for each newly encountered ID. */ * offset for each newly encountered ID. */
@ -574,7 +574,6 @@ void ReadGRFSpriteOffsets(SpriteFile &file)
if (id != prev_id) { if (id != prev_id) {
_grf_sprite_offsets[prev_id] = offset; _grf_sprite_offsets[prev_id] = offset;
offset.file_pos = file.GetPos() - 4; offset.file_pos = file.GetPos() - 4;
offset.control_flags = 0;
} }
prev_id = id; prev_id = id;
uint length = file.ReadDword(); uint length = file.ReadDword();
@ -585,11 +584,11 @@ void ReadGRFSpriteOffsets(SpriteFile &file)
uint8_t zoom = file.ReadByte(); uint8_t zoom = file.ReadByte();
length--; length--;
if (colour.Any() && zoom == 0) { // ZoomLevel::Normal (normal zoom) if (colour.Any() && zoom == 0) { // ZoomLevel::Normal (normal zoom)
SetBit(offset.control_flags, (colour != SpriteComponent::Palette) ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP : SCCF_ALLOW_ZOOM_MIN_1X_PAL); offset.control_flags.Set((colour != SpriteComponent::Palette) ? SpriteCacheCtrlFlag::AllowZoomMin1x32bpp : SpriteCacheCtrlFlag::AllowZoomMin1xPal);
SetBit(offset.control_flags, (colour != SpriteComponent::Palette) ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL); offset.control_flags.Set((colour != SpriteComponent::Palette) ? SpriteCacheCtrlFlag::AllowZoomMin2x32bpp : SpriteCacheCtrlFlag::AllowZoomMin2xPal);
} }
if (colour.Any() && zoom == 2) { // ZoomLevel::In2x (2x zoomed in) if (colour.Any() && zoom == 2) { // ZoomLevel::In2x (2x zoomed in)
SetBit(offset.control_flags, (colour != SpriteComponent::Palette) ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL); offset.control_flags.Set((colour != SpriteComponent::Palette) ? SpriteCacheCtrlFlag::AllowZoomMin2x32bpp : SpriteCacheCtrlFlag::AllowZoomMin2xPal);
} }
} }
} }
@ -621,7 +620,7 @@ bool LoadNextSprite(SpriteID load_index, SpriteFile &file, uint file_sprite_id)
uint8_t grf_type = file.ReadByte(); uint8_t grf_type = file.ReadByte();
SpriteType type; SpriteType type;
uint8_t control_flags = 0; SpriteCacheCtrlFlags control_flags;
if (grf_type == 0xFF) { if (grf_type == 0xFF) {
/* Some NewGRF files have "empty" pseudo-sprites which are 1 /* Some NewGRF files have "empty" pseudo-sprites which are 1
* byte long. Catch these so the sprites won't be displayed. */ * byte long. Catch these so the sprites won't be displayed. */

View File

@ -11,24 +11,9 @@
#define SPRITECACHE_H #define SPRITECACHE_H
#include "gfx_type.h" #include "gfx_type.h"
#include "spritecache_type.h"
#include "spriteloader/spriteloader.hpp" #include "spriteloader/spriteloader.hpp"
/** Data structure describing a sprite. */
struct Sprite {
uint16_t height; ///< Height of the sprite.
uint16_t width; ///< Width of the sprite.
int16_t x_offs; ///< Number of pixels to shift the sprite to the right.
int16_t y_offs; ///< Number of pixels to shift the sprite downwards.
std::byte data[]; ///< Sprite data.
};
enum SpriteCacheCtrlFlags : uint8_t {
SCCF_ALLOW_ZOOM_MIN_1X_PAL = 0, ///< Allow use of sprite min zoom setting at 1x in palette mode.
SCCF_ALLOW_ZOOM_MIN_1X_32BPP = 1, ///< Allow use of sprite min zoom setting at 1x in 32bpp mode.
SCCF_ALLOW_ZOOM_MIN_2X_PAL = 2, ///< Allow use of sprite min zoom setting at 2x in palette mode.
SCCF_ALLOW_ZOOM_MIN_2X_32BPP = 3, ///< Allow use of sprite min zoom setting at 2x in 32bpp mode.
};
extern uint _sprite_cache_size; extern uint _sprite_cache_size;
/** SpriteAllocator that allocates memory via a unique_ptr array. */ /** SpriteAllocator that allocates memory via a unique_ptr array. */

View File

@ -12,6 +12,7 @@
#include "core/math_func.hpp" #include "core/math_func.hpp"
#include "gfx_type.h" #include "gfx_type.h"
#include "spritecache_type.h"
#include "spriteloader/spriteloader.hpp" #include "spriteloader/spriteloader.hpp"
#include "table/sprites.h" #include "table/sprites.h"
@ -27,7 +28,7 @@ struct SpriteCache {
uint32_t lru = 0; uint32_t lru = 0;
SpriteType type = SpriteType::Invalid; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour sprite. If the recolour sprite gets into the cache it might be drawn as real sprite which causes enormous trouble. SpriteType type = SpriteType::Invalid; ///< In some cases a single sprite is misused by two NewGRFs. Once as real sprite and once as recolour sprite. If the recolour sprite gets into the cache it might be drawn as real sprite which causes enormous trouble.
bool warned = false; ///< True iff the user has been warned about incorrect use of this sprite bool warned = false; ///< True iff the user has been warned about incorrect use of this sprite
uint8_t control_flags = 0; ///< Control flags, see SpriteCacheCtrlFlags SpriteCacheCtrlFlags control_flags{}; ///< Control flags, see SpriteCacheCtrlFlags
void ClearSpriteData(); void ClearSpriteData();
}; };

View File

@ -0,0 +1,33 @@
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file spritecache_type.h Types related to the sprite cache. */
#ifndef SPRITECACHE_TYPE_H
#define SPRITECACHE_TYPE_H
#include "core/enum_type.hpp"
/** Data structure describing a sprite. */
struct Sprite {
uint16_t height; ///< Height of the sprite.
uint16_t width; ///< Width of the sprite.
int16_t x_offs; ///< Number of pixels to shift the sprite to the right.
int16_t y_offs; ///< Number of pixels to shift the sprite downwards.
std::byte data[]; ///< Sprite data.
};
enum class SpriteCacheCtrlFlag : uint8_t {
AllowZoomMin1xPal, ///< Allow use of sprite min zoom setting at 1x in palette mode.
AllowZoomMin1x32bpp, ///< Allow use of sprite min zoom setting at 1x in 32bpp mode.
AllowZoomMin2xPal, ///< Allow use of sprite min zoom setting at 2x in palette mode.
AllowZoomMin2x32bpp, ///< Allow use of sprite min zoom setting at 2x in 32bpp mode.
};
using SpriteCacheCtrlFlags = EnumBitSet<SpriteCacheCtrlFlag, uint8_t>;
#endif /* SPRITECACHE_TYPE_H */

View File

@ -256,7 +256,7 @@ static ZoomLevels LoadSpriteV1(SpriteLoader::SpriteCollection &sprite, SpriteFil
return {}; return {};
} }
static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp)
{ {
static const ZoomLevel zoom_lvl_map[6] = {ZoomLevel::Normal, ZoomLevel::In4x, ZoomLevel::In2x, ZoomLevel::Out2x, ZoomLevel::Out4x, ZoomLevel::Out8x}; static const ZoomLevel zoom_lvl_map[6] = {ZoomLevel::Normal, ZoomLevel::In4x, ZoomLevel::In2x, ZoomLevel::Out2x, ZoomLevel::Out4x, ZoomLevel::Out8x};
@ -295,11 +295,11 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil
is_wanted_zoom_lvl = true; is_wanted_zoom_lvl = true;
ZoomLevel zoom_min = sprite_type == SpriteType::Font ? ZoomLevel::Min : _settings_client.gui.sprite_zoom_min; ZoomLevel zoom_min = sprite_type == SpriteType::Font ? ZoomLevel::Min : _settings_client.gui.sprite_zoom_min;
if (zoom_min >= ZoomLevel::In2x && if (zoom_min >= ZoomLevel::In2x &&
HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_2X_32BPP : SCCF_ALLOW_ZOOM_MIN_2X_PAL) && zoom_lvl < ZoomLevel::In2x) { control_flags.Test(load_32bpp ? SpriteCacheCtrlFlag::AllowZoomMin2x32bpp : SpriteCacheCtrlFlag::AllowZoomMin2xPal) && zoom_lvl < ZoomLevel::In2x) {
is_wanted_zoom_lvl = false; is_wanted_zoom_lvl = false;
} }
if (zoom_min >= ZoomLevel::Normal && if (zoom_min >= ZoomLevel::Normal &&
HasBit(control_flags, load_32bpp ? SCCF_ALLOW_ZOOM_MIN_1X_32BPP : SCCF_ALLOW_ZOOM_MIN_1X_PAL) && zoom_lvl < ZoomLevel::Normal) { control_flags.Test(load_32bpp ? SpriteCacheCtrlFlag::AllowZoomMin1x32bpp : SpriteCacheCtrlFlag::AllowZoomMin1xPal) && zoom_lvl < ZoomLevel::Normal) {
is_wanted_zoom_lvl = false; is_wanted_zoom_lvl = false;
} }
} else { } else {
@ -359,7 +359,7 @@ static ZoomLevels LoadSpriteV2(SpriteLoader::SpriteCollection &sprite, SpriteFil
return loaded_sprites; return loaded_sprites;
} }
ZoomLevels SpriteLoaderGrf::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) ZoomLevels SpriteLoaderGrf::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp)
{ {
if (this->container_ver >= 2) { if (this->container_ver >= 2) {
return LoadSpriteV2(sprite, file, file_pos, sprite_type, load_32bpp, control_flags, avail_8bpp, avail_32bpp); return LoadSpriteV2(sprite, file, file_pos, sprite_type, load_32bpp, control_flags, avail_8bpp, avail_32bpp);

View File

@ -17,7 +17,7 @@ class SpriteLoaderGrf : public SpriteLoader {
uint8_t container_ver; uint8_t container_ver;
public: public:
SpriteLoaderGrf(uint8_t container_ver) : container_ver(container_ver) {} SpriteLoaderGrf(uint8_t container_ver) : container_ver(container_ver) {}
ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override; ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override;
}; };
#endif /* SPRITELOADER_GRF_HPP */ #endif /* SPRITELOADER_GRF_HPP */

View File

@ -48,7 +48,7 @@ static void Convert32bppTo8bpp(SpriteLoader::Sprite &sprite)
} }
} }
ZoomLevels SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) ZoomLevels SpriteLoaderMakeIndexed::LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp)
{ {
ZoomLevels avail = this->baseloader.LoadSprite(sprite, file, file_pos, sprite_type, true, control_flags, avail_8bpp, avail_32bpp); ZoomLevels avail = this->baseloader.LoadSprite(sprite, file, file_pos, sprite_type, true, control_flags, avail_8bpp, avail_32bpp);

View File

@ -17,7 +17,7 @@ class SpriteLoaderMakeIndexed : public SpriteLoader {
SpriteLoader &baseloader; SpriteLoader &baseloader;
public: public:
SpriteLoaderMakeIndexed(SpriteLoader &baseloader) : baseloader(baseloader) {} SpriteLoaderMakeIndexed(SpriteLoader &baseloader) : baseloader(baseloader) {}
ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override; ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) override;
}; };
#endif /* SPRITELOADER_MAKEINDEXED_H */ #endif /* SPRITELOADER_MAKEINDEXED_H */

View File

@ -13,6 +13,7 @@
#include "../core/alloc_type.hpp" #include "../core/alloc_type.hpp"
#include "../core/enum_type.hpp" #include "../core/enum_type.hpp"
#include "../gfx_type.h" #include "../gfx_type.h"
#include "../spritecache_type.h"
#include "sprite_file_type.hpp" #include "sprite_file_type.hpp"
struct Sprite; struct Sprite;
@ -94,7 +95,7 @@ public:
* @param[out] avail_32bpp Available 32bpp sprites. * @param[out] avail_32bpp Available 32bpp sprites.
* @return Available sprites matching \a load_32bpp. * @return Available sprites matching \a load_32bpp.
*/ */
virtual ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, uint8_t control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) = 0; virtual ZoomLevels LoadSprite(SpriteLoader::SpriteCollection &sprite, SpriteFile &file, size_t file_pos, SpriteType sprite_type, bool load_32bpp, SpriteCacheCtrlFlags control_flags, ZoomLevels &avail_8bpp, ZoomLevels &avail_32bpp) = 0;
virtual ~SpriteLoader() = default; virtual ~SpriteLoader() = default;
}; };

View File

@ -33,7 +33,7 @@ static bool MockLoadNextSprite(SpriteID load_index)
sc->id = 0; sc->id = 0;
sc->type = is_mapgen ? SpriteType::MapGen : SpriteType::Normal; sc->type = is_mapgen ? SpriteType::MapGen : SpriteType::Normal;
sc->warned = false; sc->warned = false;
sc->control_flags = 0; sc->control_flags = {};
/* Fill with empty sprites up until the default sprite count. */ /* Fill with empty sprites up until the default sprite count. */
return load_index < SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT; return load_index < SPR_OPENTTD_BASE + OPENTTD_SPRITE_COUNT;