13#include "../newgrf_engine.h"
14#include "../newgrf_cargo.h"
16#include "../vehicle_base.h"
21#include "../table/strings.h"
23#include "../safeguards.h"
68 bool custom_sprite =
HasBit(grf_sprite->
pal, 15) != invert_action1_flag;
72 uint index =
GB(grf_sprite->
sprite, 0, 14);
74 GrfMsg(1,
"ReadSpriteLayoutSprite: Spritelayout uses undefined custom spriteset {}", index);
75 grf_sprite->
sprite = SPR_IMG_QUERY;
76 grf_sprite->
pal = PAL_NONE;
79 if (max_sprite_offset !=
nullptr) *max_sprite_offset = use_cur_spritesets ? _cur.
GetNumEnts(feature, index) : UINT16_MAX;
84 GrfMsg(1,
"ReadSpriteLayoutSprite: Spritelayout specifies var10 value for non-action-1 sprite");
85 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
91 uint index =
GB(grf_sprite->
pal, 0, 14);
93 GrfMsg(1,
"ReadSpriteLayoutSprite: Spritelayout uses undefined custom spriteset {} for 'palette'", index);
94 grf_sprite->
pal = PAL_NONE;
97 if (max_palette_offset !=
nullptr) *max_palette_offset = use_cur_spritesets ? _cur.
GetNumEnts(feature, index) : UINT16_MAX;
102 GrfMsg(1,
"ReadSpriteLayoutRegisters: Spritelayout specifies var10 value for non-action-1 palette");
103 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
144 GrfMsg(1,
"ReadSpriteLayoutRegisters: Spritelayout specifies var10 ({}) exceeding the maximal allowed value {}", regs.
sprite_var10,
TLR_MAX_VAR10);
145 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
153 GrfMsg(1,
"ReadSpriteLayoutRegisters: Spritelayout specifies var10 ({}) exceeding the maximal allowed value {}", regs.
palette_var10,
TLR_MAX_VAR10);
154 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
173 bool has_flags =
HasBit(num_building_sprites, 6);
174 ClrBit(num_building_sprites, 6);
176 if (!allow_var10) valid_flags &= ~TLF_VAR10_FLAGS;
177 dts->
Allocate(num_building_sprites);
179 std::vector<uint16_t> max_sprite_offset(num_building_sprites + 1, 0);
180 std::vector<uint16_t> max_palette_offset(num_building_sprites + 1, 0);
187 GrfMsg(1,
"ReadSpriteLayout: Spritelayout uses invalid flag 0x{:X} for ground sprite", flags & ~(valid_flags & ~
TLF_NON_GROUND_FLAGS));
188 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
195 for (uint i = 0; i < num_building_sprites; i++) {
198 flags =
ReadSpriteLayoutSprite(buf, has_flags,
false, use_cur_spritesets, feature, &seq->image, max_sprite_offset.data() + i + 1, max_palette_offset.data() + i + 1);
201 if (flags & ~valid_flags) {
202 GrfMsg(1,
"ReadSpriteLayout: Spritelayout uses unknown flag 0x{:X}", flags & ~valid_flags);
203 DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
223 bool is_consistent =
true;
225 for (uint i = 0; i < num_building_sprites + 1; i++) {
226 if (max_sprite_offset[i] > 0) {
230 is_consistent =
false;
234 if (max_palette_offset[i] > 0) {
238 is_consistent =
false;
247 if (!is_consistent || !dts->registers.empty()) {
251 for (uint i = 0; i < num_building_sprites + 1; i++) {
261using CachedCallback = std::pair<uint16_t, SpriteGroupID>;
264void ResetCallbacks(
bool final)
270static const SpriteGroup *GetCallbackResultGroup(uint16_t value)
274 if (_cur.
grffile->grf_version < 8 &&
GB(value, 8, 8) == 0xFF) {
293static const SpriteGroup *GetGroupFromGroupID(uint8_t setid, uint8_t type, uint16_t groupid)
295 if (
HasBit(groupid, 15))
return GetCallbackResultGroup(groupid);
297 if (groupid >
MAX_SPRITEGROUP || _cur.spritegroups[groupid] ==
nullptr) {
298 GrfMsg(1,
"GetGroupFromGroupID(0x{:02X}:0x{:02X}): Groupid 0x{:04X} does not exist, leaving empty", setid, type, groupid);
302 return _cur.spritegroups[groupid];
315 if (
HasBit(spriteid, 15))
return GetCallbackResultGroup(spriteid);
318 GrfMsg(1,
"CreateGroupFromGroupID(0x{:02X}:0x{:02X}): Sprite set {} invalid", setid, type, spriteid);
323 uint num_sprites = _cur.
GetNumEnts(feature, spriteid);
326 assert(spriteset_start + num_sprites <= _cur.
spriteid);
348 if (feature >= GSF_END) {
349 GrfMsg(1,
"NewSpriteGroup: Unsupported feature 0x{:02X}, skipping", feature);
378 switch (
GB(type, 2, 2)) {
379 default: NOT_REACHED();
380 case 0: group->size = DSG_SIZE_BYTE; varsize = 1;
break;
381 case 1: group->size = DSG_SIZE_WORD; varsize = 2;
break;
382 case 2: group->size = DSG_SIZE_DWORD; varsize = 4;
break;
393 if (adjust.variable == 0x7E) {
395 adjust.subroutine = GetGroupFromGroupID(setid, type, buf.
ReadByte());
401 adjust.shift_num =
GB(varadjust, 0, 5);
402 adjust.type = (DeterministicSpriteGroupAdjustType)
GB(varadjust, 6, 2);
405 if (adjust.type != DSGA_TYPE_NONE) {
408 if (adjust.divmod_val == 0) adjust.divmod_val = 1;
411 adjust.divmod_val = 0;
415 }
while (
HasBit(varadjust, 5));
417 std::vector<DeterministicSpriteGroupRange> ranges;
419 for (
auto &range : ranges) {
420 range.group = GetGroupFromGroupID(setid, type, buf.
ReadWord());
425 group->default_group = GetGroupFromGroupID(setid, type, buf.
ReadWord());
426 group->error_group = ranges.empty() ? group->default_group : ranges[0].group;
428 group->calculated_result = ranges.empty();
431 std::vector<uint32_t> bounds;
432 bounds.reserve(ranges.size());
433 for (
const auto &range : ranges) {
434 bounds.push_back(range.low);
435 if (range.high != UINT32_MAX) bounds.push_back(range.high + 1);
437 std::sort(bounds.begin(), bounds.end());
438 bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end());
440 std::vector<const SpriteGroup *> target;
441 target.reserve(bounds.size());
442 for (
const auto &bound : bounds) {
444 for (
const auto &range : ranges) {
445 if (range.low <= bound && bound <= range.high) {
452 assert(target.size() == bounds.size());
454 for (uint j = 0; j < bounds.size(); ) {
455 if (target[j] != group->default_group) {
459 while (j < bounds.size() && target[j] == r.group) {
462 r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX;
488 group->triggers =
GB(triggers, 0, 7);
489 group->
cmp_mode =
HasBit(triggers, 7) ? RSG_CMP_ALL : RSG_CMP_ANY;
492 uint8_t num_groups = buf.
ReadByte();
494 GrfMsg(1,
"NewSpriteGroup: Random Action 2 nrand should be power of 2");
497 group->
groups.reserve(num_groups);
498 for (uint i = 0; i < num_groups; i++) {
499 group->
groups.push_back(GetGroupFromGroupID(setid, type, buf.
ReadWord()));
510 case GSF_ROADVEHICLES:
522 uint8_t num_loaded = type;
523 uint8_t num_loading = buf.
ReadByte();
526 GrfMsg(0,
"NewSpriteGroup: No sprite set to work on! Skipping");
530 GrfMsg(6,
"NewSpriteGroup: New SpriteGroup 0x{:02X}, {} loaded, {} loading",
531 setid, num_loaded, num_loading);
533 if (num_loaded + num_loading == 0) {
534 GrfMsg(1,
"NewSpriteGroup: no result, skipping invalid RealSpriteGroup");
538 if (num_loaded + num_loading == 1) {
542 GrfMsg(8,
"NewSpriteGroup: one result, skipping RealSpriteGroup = subset {}", spriteid);
546 std::vector<uint16_t> loaded;
547 std::vector<uint16_t> loading;
549 loaded.reserve(num_loaded);
550 for (uint i = 0; i < num_loaded; i++) {
552 GrfMsg(8,
"NewSpriteGroup: + rg->loaded[{}] = subset {}", i, loaded[i]);
555 loading.reserve(num_loading);
556 for (uint i = 0; i < num_loading; i++) {
558 GrfMsg(8,
"NewSpriteGroup: + rg->loading[{}] = subset {}", i, loading[i]);
561 bool loaded_same = !loaded.empty() && std::adjacent_find(loaded.begin(), loaded.end(), std::not_equal_to<>()) == loaded.end();
562 bool loading_same = !loading.empty() && std::adjacent_find(loading.begin(), loading.end(), std::not_equal_to<>()) == loading.end();
563 if (loaded_same && loading_same && loaded[0] == loading[0]) {
566 GrfMsg(8,
"NewSpriteGroup: same result, skipping RealSpriteGroup = subset {}", loaded[0]);
575 if (loaded_same && loaded.size() > 1) loaded.resize(1);
576 group->
loaded.reserve(loaded.size());
577 for (uint16_t spriteid : loaded) {
579 group->
loaded.push_back(t);
582 if (loading_same && loading.size() > 1) loading.resize(1);
583 group->
loading.reserve(loading.size());
584 for (uint16_t spriteid : loading) {
593 case GSF_AIRPORTTILES:
595 case GSF_INDUSTRYTILES:
596 case GSF_ROADSTOPS: {
597 uint8_t num_building_sprites = std::max((uint8_t)1, type);
605 if (
ReadSpriteLayout(buf, num_building_sprites,
true, feature,
false, type == 0, &group->dts))
return;
609 case GSF_INDUSTRIES: {
611 GrfMsg(1,
"NewSpriteGroup: Unsupported industry production version {}, skipping", type);
630 }
else if (type == 1) {
640 }
else if (type == 2) {
644 error->
data =
"too many inputs (max 16)";
647 for (uint i = 0; i < group->
num_input; i++) {
655 }
else if (
auto v = group->
cargo_input | std::views::take(i); std::ranges::find(v, cargo) != v.end()) {
657 error->
data =
"duplicate input cargo";
666 error->
data =
"too many outputs (max 16)";
669 for (uint i = 0; i < group->
num_output; i++) {
675 }
else if (
auto v = group->
cargo_output | std::views::take(i); std::ranges::find(v, cargo) != v.end()) {
677 error->
data =
"duplicate output cargo";
691 default: GrfMsg(1,
"NewSpriteGroup: Unsupported feature 0x{:02X}, skipping", feature);
696 _cur.spritegroups[setid] = act_group;
debug_inline constexpr bool HasBit(const T x, const uint8_t y)
Checks if a bit in a value is set.
constexpr bool HasExactlyOneBit(T value)
Test whether value has exactly 1 bit set.
constexpr T SB(T &x, const uint8_t s, const uint8_t n, const U d)
Set n bits in x starting at bit s to d.
constexpr T SetBit(T &x, const uint8_t y)
Set a bit in a variable.
debug_inline static constexpr uint GB(const T x, const uint8_t s, const uint8_t n)
Fetch n bits from x, started at bit s.
constexpr T ClrBit(T &x, const uint8_t y)
Clears a bit in a variable.
uint8_t CargoType
Cargo slots to indicate a cargo type within a game.
bool IsValidCargoType(CargoType cargo)
Test whether cargo type is not INVALID_CARGO.
Class to read from a NewGRF file.
uint16_t ReadWord()
Read a single Word (16 bits).
uint32_t ReadVarSize(uint8_t size)
Read a value of the given number of bytes.
uint8_t ReadByte()
Read a single byte (8 bits).
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
static const int INDUSTRY_ORIGINAL_NUM_INPUTS
Original number of accepted cargo types.
static const int INDUSTRY_ORIGINAL_NUM_OUTPUTS
Original number of produced cargo types.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
GRFError * DisableGrf(StringID message, GRFConfig *config)
Disable a GRF.
static const SpriteGroup * CreateGroupFromGroupID(uint8_t feature, uint8_t setid, uint8_t type, uint16_t spriteid)
Helper function to either create a callback or a result sprite group.
static void ReadSpriteLayoutRegisters(ByteReader &buf, TileLayoutFlags flags, bool is_parent, NewGRFSpriteLayout *dts, uint index)
Preprocess the TileLayoutFlags and read register modifiers from the GRF.
bool ReadSpriteLayout(ByteReader &buf, uint num_building_sprites, bool use_cur_spritesets, uint8_t feature, bool allow_var10, bool no_z_position, NewGRFSpriteLayout *dts)
Read a spritelayout from the GRF.
TileLayoutFlags ReadSpriteLayoutSprite(ByteReader &buf, bool read_flags, bool invert_action1_flag, bool use_cur_spritesets, int feature, PalSpriteID *grf_sprite, uint16_t *max_sprite_offset, uint16_t *max_palette_offset)
Read a sprite and a palette from the GRF and convert them into a format suitable to OpenTTD.
void MapSpriteMappingRecolour(PalSpriteID *grf_sprite)
Map the colour modifiers of TTDPatch to those that Open is using.
static std::vector< CachedCallback > _cached_callback_groups
Sorted list of cached callback result spritegroups.
NewGRF buffer reader definition.
CargoType GetCargoTranslation(uint8_t cargo, const GRFFile *grffile, bool usebit)
Translate a GRF-local cargo slot/bitnum into a CargoType.
TileLayoutFlags
Flags to enable register usage in sprite layouts.
@ TLF_BB_Z_OFFSET
Add signed offset to bounding box Z positions from register TileLayoutRegisters::delta....
@ TLF_CUSTOM_PALETTE
Palette is from Action 1 (moved to SPRITE_MODIFIER_CUSTOM_SPRITE in palette during loading).
@ TLF_SPRITE
Add signed offset to sprite from register TileLayoutRegisters::sprite.
@ TLF_CHILD_X_OFFSET
Add signed offset to child sprite X positions from register TileLayoutRegisters::delta....
@ TLF_DRAWING_FLAGS
Flags which are still required after loading the GRF.
@ TLF_DODRAW
Only draw sprite if value of register TileLayoutRegisters::dodraw is non-zero.
@ TLF_PALETTE_REG_FLAGS
Flags which require resolving the action-1-2-3 chain for the palette, even if it is no action-1 palet...
@ TLF_NON_GROUND_FLAGS
Flags which do not work for the (first) ground sprite.
@ TLF_BB_XY_OFFSET
Add signed offset to bounding box X and Y positions from register TileLayoutRegisters::delta....
@ TLF_SPRITE_REG_FLAGS
Flags which require resolving the action-1-2-3 chain for the sprite, even if it is no action-1 sprite...
@ TLF_PALETTE_VAR10
Resolve palette with a specific value in variable 10.
@ TLF_SPRITE_VAR10
Resolve sprite with a specific value in variable 10.
@ TLF_KNOWN_FLAGS
Known flags. Any unknown set flag will disable the GRF.
@ TLF_PALETTE
Add signed offset to palette from register TileLayoutRegisters::palette.
@ TLF_CHILD_Y_OFFSET
Add signed offset to child sprite Y positions from register TileLayoutRegisters::delta....
static const uint TLR_MAX_VAR10
Maximum value for var 10.
NewGRF internal processing state.
static constexpr uint MAX_SPRITEGROUP
Maximum GRF-local ID for a spritegroup.
DeterministicSpriteGroupAdjustOperation
@ VSG_SCOPE_SELF
Resolved object itself.
@ VSG_SCOPE_PARENT
Related object of the resolved one.
@ VSG_SCOPE_RELATIVE
Relative position (vehicles only)
static constexpr uint8_t SPRITE_MODIFIER_OPAQUE
Set when a sprite must not ever be displayed transparently.
static constexpr uint8_t PALETTE_MODIFIER_TRANSPARENT
when a sprite is to be displayed transparently, this bit needs to be set.
static constexpr uint8_t SPRITE_MODIFIER_CUSTOM_SPRITE
these masks change the colours of the palette for a sprite.
static constexpr uint8_t SPRITE_WIDTH
number of bits for the sprite number
static constexpr uint8_t PALETTE_MODIFIER_COLOUR
this bit is set when a recolouring process is in action
uint8_t parameter
Used for variables between 0x60 and 0x7F inclusive.
A tile child sprite and palette to draw for stations etc, with 3D bounding box.
int8_t delta_z
0x80 identifies child sprites
bool IsParentSprite() const
Check whether this is a parent sprite with a boundingbox.
PalSpriteID ground
Palette and sprite for the ground.
Information about why GRF had problems during initialisation.
std::string data
Additional data for message and custom_message.
GRFFile * grffile
Currently processed GRF file.
uint32_t nfo_line
Currently processed pseudo sprite number in the GRF.
SpriteID spriteid
First available SpriteID for loading realsprites.
SpriteID GetSprite(uint8_t feature, uint set) const
Returns the first sprite of a spriteset.
bool IsValidSpriteSet(uint8_t feature, uint set) const
Check whether a specific set is defined.
uint GetNumEnts(uint8_t feature, uint set) const
Returns the number of sprites in a spriteset.
bool HasValidSpriteSets(uint8_t feature) const
Check whether there are any valid spritesets for a feature.
int skip_sprites
Number of pseudo sprites to skip before processing the next one. (-1 to skip to end of file)
std::array< uint16_t, INDUSTRY_NUM_OUTPUTS > add_output
Add this much output cargo when successful (unsigned, is indirect in cb version 1+)
std::array< CargoType, INDUSTRY_NUM_OUTPUTS > cargo_output
Which output cargoes to add to (only cb version 2)
std::array< CargoType, INDUSTRY_NUM_INPUTS > cargo_input
Which input cargoes to take from (only cb version 2)
std::array< int16_t, INDUSTRY_NUM_INPUTS > subtract_input
Take this much of the input cargo (can be negative, is indirect in cb version 1+)
uint8_t num_input
How many subtract_input values are valid.
uint8_t version
Production callback version used, or 0xFF if marked invalid.
uint8_t num_output
How many add_output values are valid.
NewGRF supplied spritelayout.
void Allocate(uint num_sprites)
Allocate a spritelayout for num_sprites building sprites.
uint consistent_max_offset
Number of sprites in all referenced spritesets.
void AllocateRegisters()
Allocate memory for register modifiers.
Combination of a palette sprite and a 'real' sprite.
SpriteID sprite
The 'real' sprite.
PaletteID pal
The palette (use PAL_NONE) if not needed)
static Titem * Get(auto index)
Returns Titem with given index.
Tindex index
Index of this pool item.
static bool CanAllocateItem(size_t n=1)
Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function()
uint8_t lowest_randbit
Look for this in the per-object randomized bitmask:
VarSpriteGroupScope var_scope
Take this object:
std::vector< const SpriteGroup * > groups
Take the group with appropriate index:
RandomizedSpriteGroupCompareMode cmp_mode
Check for these triggers:
std::vector< const SpriteGroup * > loaded
List of loaded groups (can be SpriteIDs or Callback results)
std::vector< const SpriteGroup * > loading
List of loading groups (can be SpriteIDs or Callback results)
Additional modifiers for items in sprite layouts.
uint8_t parent[3]
Registers for signed offsets for the bounding box position of parent sprites.
TileLayoutFlags flags
Flags defining which members are valid and to be used.
uint8_t dodraw
Register deciding whether the sprite shall be drawn at all. Non-zero means drawing.
uint16_t max_sprite_offset
Maximum offset to add to the sprite. (limited by size of the spriteset)
uint8_t palette
Register specifying a signed offset for the palette.
uint8_t sprite_var10
Value for variable 10 when resolving the sprite.
uint16_t max_palette_offset
Maximum offset to add to the palette. (limited by size of the spriteset)
uint8_t palette_var10
Value for variable 10 when resolving the palette.
uint8_t child[2]
Registers for signed offsets for the position of child sprites.
uint8_t sprite
Register specifying a signed offset for the sprite.
Action 2 sprite layout for houses, industry tiles, objects and airport tiles.