summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lang/english.txt1
-rw-r--r--src/newgrf.cpp190
-rw-r--r--src/newgrf_airporttiles.cpp2
-rw-r--r--src/newgrf_commons.cpp170
-rw-r--r--src/newgrf_commons.h92
-rw-r--r--src/newgrf_house.cpp3
-rw-r--r--src/newgrf_industrytiles.cpp3
-rw-r--r--src/newgrf_object.cpp4
-rw-r--r--src/newgrf_spritegroup.cpp23
-rw-r--r--src/newgrf_spritegroup.h5
-rw-r--r--src/newgrf_station.cpp51
-rw-r--r--src/sprite.cpp30
-rw-r--r--src/station_cmd.cpp50
13 files changed, 558 insertions, 66 deletions
diff --git a/src/lang/english.txt b/src/lang/english.txt
index f61962892..ef4dadd43 100644
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -2475,6 +2475,7 @@ STR_NEWGRF_ERROR_READ_BOUNDS :Read past end o
STR_NEWGRF_ERROR_MISSING_SPRITES :{WHITE}The currently used base graphics set is missing a number of sprites.{}Please update the base graphics set
STR_NEWGRF_ERROR_GRM_FAILED :Requested GRF resources not available
STR_NEWGRF_ERROR_FORCEFULLY_DISABLED :{2:RAW_STRING} was disabled by {4:RAW_STRING}
+STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT :Invalid/unknown sprite layout format
# NewGRF related 'general' warnings
STR_NEWGRF_POPUP_CAUTION_CAPTION :{WHITE}Caution!
diff --git a/src/newgrf.cpp b/src/newgrf.cpp
index 605b17023..a2ff7a5a1 100644
--- a/src/newgrf.cpp
+++ b/src/newgrf.cpp
@@ -482,16 +482,19 @@ static void MapSpriteMappingRecolour(PalSpriteID *grf_sprite)
* Read a sprite and a palette from the GRF and convert them into a format
* suitable to OpenTTD.
* @param buf Input stream.
+ * @param read_flags Whether to read TileLayoutFlags.
* @param invert_action1_flag Set to true, if palette bit 15 means 'not from action 1'.
* @param action1_offset Offset to add to action 1 sprites.
* @param action1_pitch Factor to multiply action 1 sprite indices with.
* @param action1_max Maximal valid action 1 index.
* @param [out] grf_sprite Read sprite and palette.
+ * @return read TileLayoutFlags.
*/
-static void ReadSpriteLayoutSprite(ByteReader *buf, bool invert_action1_flag, uint action1_offset, uint action1_pitch, uint action1_max, PalSpriteID *grf_sprite)
+static TileLayoutFlags ReadSpriteLayoutSprite(ByteReader *buf, bool read_flags, bool invert_action1_flag, uint action1_offset, uint action1_pitch, uint action1_max, PalSpriteID *grf_sprite)
{
grf_sprite->sprite = buf->ReadWord();
grf_sprite->pal = buf->ReadWord();
+ TileLayoutFlags flags = read_flags ? (TileLayoutFlags)buf->ReadWord() : TLF_NOTHING;
MapSpriteMappingRecolour(grf_sprite);
@@ -509,7 +512,143 @@ static void ReadSpriteLayoutSprite(ByteReader *buf, bool invert_action1_flag, ui
SB(grf_sprite->sprite, 0, SPRITE_WIDTH, sprite);
SetBit(grf_sprite->sprite, SPRITE_MODIFIER_CUSTOM_SPRITE);
}
+ } else if ((flags & TLF_SPRITE_VAR10) && !(flags & TLF_SPRITE_REG_FLAGS)) {
+ grfmsg(1, "ReadSpriteLayoutSprite: Spritelayout specifies var10 value for non-action-1 sprite");
+ DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
+ return flags;
}
+
+ if (flags & TLF_CUSTOM_PALETTE) {
+ /* Use palette from Action 1 */
+ uint index = GB(grf_sprite->pal, 0, 14);
+ if (action1_pitch == 0 || index >= action1_max) {
+ grfmsg(1, "ReadSpriteLayoutSprite: Spritelayout uses undefined custom spriteset %d for 'palette'", index);
+ grf_sprite->pal = PAL_NONE;
+ } else {
+ SpriteID sprite = action1_offset + index * action1_pitch;
+ SB(grf_sprite->pal, 0, SPRITE_WIDTH, sprite);
+ SetBit(grf_sprite->pal, SPRITE_MODIFIER_CUSTOM_SPRITE);
+ }
+ } else if ((flags & TLF_PALETTE_VAR10) && !(flags & TLF_PALETTE_REG_FLAGS)) {
+ grfmsg(1, "ReadSpriteLayoutRegisters: Spritelayout specifies var10 value for non-action-1 palette");
+ DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
+ return flags;
+ }
+
+ return flags;
+}
+
+/**
+ * Preprocess the TileLayoutFlags and read register modifiers from the GRF.
+ * @param buf Input stream.
+ * @param flags TileLayoutFlags to process.
+ * @param is_parent Whether the sprite is a parentsprite with a bounding box.
+ * @param dts Sprite layout to insert data into.
+ * @param index Sprite index to process; 0 for ground sprite.
+ */
+static void ReadSpriteLayoutRegisters(ByteReader *buf, TileLayoutFlags flags, bool is_parent, NewGRFSpriteLayout *dts, uint index)
+{
+ if (!(flags & TLF_DRAWING_FLAGS)) return;
+
+ if (dts->registers == NULL) dts->AllocateRegisters();
+ TileLayoutRegisters &regs = const_cast<TileLayoutRegisters&>(dts->registers[index]);
+ regs.flags = flags & TLF_DRAWING_FLAGS;
+
+ if (flags & TLF_DODRAW) regs.dodraw = buf->ReadByte();
+ if (flags & TLF_SPRITE) regs.sprite = buf->ReadByte();
+ if (flags & TLF_PALETTE) regs.palette = buf->ReadByte();
+
+ if (is_parent) {
+ if (flags & TLF_BB_XY_OFFSET) {
+ regs.delta.parent[0] = buf->ReadByte();
+ regs.delta.parent[1] = buf->ReadByte();
+ }
+ if (flags & TLF_BB_Z_OFFSET) regs.delta.parent[2] = buf->ReadByte();
+ } else {
+ if (flags & TLF_CHILD_X_OFFSET) regs.delta.child[0] = buf->ReadByte();
+ if (flags & TLF_CHILD_Y_OFFSET) regs.delta.child[1] = buf->ReadByte();
+ }
+
+ if (flags & TLF_SPRITE_VAR10) {
+ regs.sprite_var10 = buf->ReadByte();
+ if (regs.sprite_var10 > TLR_MAX_VAR10) {
+ grfmsg(1, "ReadSpriteLayoutRegisters: Spritelayout specifies var10 (%d) exceeding the maximal allowed value %d", regs.sprite_var10, TLR_MAX_VAR10);
+ DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
+ return;
+ }
+ }
+
+ if (flags & TLF_PALETTE_VAR10) {
+ regs.palette_var10 = buf->ReadByte();
+ if (regs.palette_var10 > TLR_MAX_VAR10) {
+ grfmsg(1, "ReadSpriteLayoutRegisters: Spritelayout specifies var10 (%d) exceeding the maximal allowed value %d", regs.palette_var10, TLR_MAX_VAR10);
+ DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
+ return;
+ }
+ }
+}
+
+/**
+ * Read a spritelayout from the GRF.
+ * @param buf Input
+ * @param num_building_sprites Number of building sprites to read
+ * @param action1_offset Offset to add to action 1 sprites
+ * @param action1_pitch Factor to multiply action 1 sprite indices with
+ * @param action1_max Maximal valid action 1 index
+ * @param allow_var10 Whether the spritelayout may specifiy var10 values for resolving multiple action-1-2-3 chains
+ * @param no_z_position Whether bounding boxes have no Z offset
+ * @param dts Layout container to output into
+ * @return true on error (GRF was disabled)
+ */
+static bool ReadSpriteLayout(ByteReader *buf, uint num_building_sprites, uint action1_offset, uint action1_pitch, uint action1_max, bool allow_var10, bool no_z_position, NewGRFSpriteLayout *dts)
+{
+ bool has_flags = HasBit(num_building_sprites, 6);
+ ClrBit(num_building_sprites, 6);
+ TileLayoutFlags valid_flags = TLF_KNOWN_FLAGS;
+ if (!allow_var10) valid_flags &= ~TLF_VAR10_FLAGS;
+ dts->Allocate(num_building_sprites); // allocate before reading groundsprite flags
+
+ /* Groundsprite */
+ TileLayoutFlags flags = ReadSpriteLayoutSprite(buf, has_flags, false, action1_offset, action1_pitch, action1_max, &dts->ground);
+ if (_skip_sprites < 0) return true;
+
+ if (flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS)) {
+ grfmsg(1, "ReadSpriteLayout: Spritelayout uses invalid flag 0x%x for ground sprite", flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS));
+ DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
+ return true;
+ }
+
+ ReadSpriteLayoutRegisters(buf, flags, false, dts, 0);
+ if (_skip_sprites < 0) return true;
+
+ for (uint i = 0; i < num_building_sprites; i++) {
+ DrawTileSeqStruct *seq = const_cast<DrawTileSeqStruct*>(&dts->seq[i]);
+
+ flags = ReadSpriteLayoutSprite(buf, has_flags, false, action1_offset, action1_pitch, action1_max, &seq->image);
+ if (_skip_sprites < 0) return true;
+
+ if (flags & ~valid_flags) {
+ grfmsg(1, "ReadSpriteLayout: Spritelayout uses unknown flag 0x%x", flags & ~valid_flags);
+ DisableGrf(STR_NEWGRF_ERROR_INVALID_SPRITE_LAYOUT);
+ return true;
+ }
+
+ seq->delta_x = buf->ReadByte();
+ seq->delta_y = buf->ReadByte();
+
+ if (!no_z_position) seq->delta_z = buf->ReadByte();
+
+ if (seq->IsParentSprite()) {
+ seq->size_x = buf->ReadByte();
+ seq->size_y = buf->ReadByte();
+ seq->size_z = buf->ReadByte();
+ }
+
+ ReadSpriteLayoutRegisters(buf, flags, seq->IsParentSprite(), dts, i + 1);
+ if (_skip_sprites < 0) return true;
+ }
+
+ return false;
}
/**
@@ -540,6 +679,7 @@ static void ConvertTTDBasePrice(uint32 base_pointer, const char *error_location,
enum ChangeInfoResult {
CIR_SUCCESS, ///< Variable was parsed and read
+ CIR_DISABLED, ///< GRF was disabled due to error
CIR_UNHANDLED, ///< Variable was parsed but unread
CIR_UNKNOWN, ///< Variable is unknown
CIR_INVALID_ID, ///< Attempt to modify an invalid ID
@@ -1269,7 +1409,9 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte
continue;
}
- ReadSpriteLayoutSprite(buf, false, 0, 1, UINT_MAX, &dts->ground);
+ ReadSpriteLayoutSprite(buf, false, false, 0, 1, UINT_MAX, &dts->ground);
+ /* On error, bail out immediately. Temporary GRF data was already freed */
+ if (_skip_sprites < 0) return CIR_DISABLED;
static SmallVector<DrawTileSeqStruct, 8> tmp_layout;
tmp_layout.Clear();
@@ -1286,7 +1428,9 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte
dtss->size_y = buf->ReadByte();
dtss->size_z = buf->ReadByte();
- ReadSpriteLayoutSprite(buf, true, 0, 1, UINT_MAX, &dtss->image);
+ ReadSpriteLayoutSprite(buf, false, true, 0, 1, UINT_MAX, &dtss->image);
+ /* On error, bail out immediately. Temporary GRF data was already freed */
+ if (_skip_sprites < 0) return CIR_DISABLED;
}
dts->Clone(tmp_layout.Begin());
}
@@ -1428,6 +1572,19 @@ static ChangeInfoResult StationChangeInfo(uint stid, int numinfo, int prop, Byte
statspec->animation.triggers = buf->ReadWord();
break;
+ case 0x20: // Advanced sprite layout
+ statspec->tiles = buf->ReadExtendedByte();
+ delete[] statspec->renderdata; // delete earlier loaded stuff
+ statspec->renderdata = new NewGRFSpriteLayout[statspec->tiles];
+
+ for (uint t = 0; t < statspec->tiles; t++) {
+ NewGRFSpriteLayout *dts = &statspec->renderdata[t];
+ uint num_building_sprites = buf->ReadByte();
+ /* On error, bail out immediately. Temporary GRF data was already freed */
+ if (ReadSpriteLayout(buf, num_building_sprites, 0, 1, UINT_MAX, true, false, dts)) return CIR_DISABLED;
+ }
+ break;
+
default:
ret = CIR_UNKNOWN;
break;
@@ -3524,6 +3681,10 @@ static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uin
switch (cir) {
default: NOT_REACHED();
+ case CIR_DISABLED:
+ /* Error has already been printed; just stop parsing */
+ return true;
+
case CIR_SUCCESS:
return false;
@@ -3961,7 +4122,6 @@ static void NewSpriteGroup(ByteReader *buf)
byte num_spriteset_ents = _cur_grffile->spriteset_numents;
byte num_spritesets = _cur_grffile->spriteset_numsets;
byte num_building_sprites = max((uint8)1, type);
- uint i;
assert(TileLayoutSpriteGroup::CanAllocateItem());
TileLayoutSpriteGroup *group = new TileLayoutSpriteGroup();
@@ -3969,26 +4129,8 @@ static void NewSpriteGroup(ByteReader *buf)
/* num_building_stages should be 1, if we are only using non-custom sprites */
group->num_building_stages = max((uint8)1, num_spriteset_ents);
- /* Groundsprite */
- ReadSpriteLayoutSprite(buf, false, _cur_grffile->spriteset_start, num_spriteset_ents, num_spritesets, &group->dts.ground);
-
- group->dts.Allocate(num_building_sprites);
- for (i = 0; i < num_building_sprites; i++) {
- DrawTileSeqStruct *seq = const_cast<DrawTileSeqStruct*>(&group->dts.seq[i]);
-
- ReadSpriteLayoutSprite(buf, false, _cur_grffile->spriteset_start, num_spriteset_ents, num_spritesets, &seq->image);
- seq->delta_x = buf->ReadByte();
- seq->delta_y = buf->ReadByte();
-
- if (type > 0) {
- seq->delta_z = buf->ReadByte();
- if (!seq->IsParentSprite()) continue;
- }
-
- seq->size_x = buf->ReadByte();
- seq->size_y = buf->ReadByte();
- seq->size_z = buf->ReadByte();
- }
+ /* On error, bail out immediately. Temporary GRF data was already freed */
+ if (ReadSpriteLayout(buf, num_building_sprites, _cur_grffile->spriteset_start, num_spriteset_ents, num_spritesets, false, type == 0, &group->dts)) return;
break;
}
diff --git a/src/newgrf_airporttiles.cpp b/src/newgrf_airporttiles.cpp
index 329c66075..e00d23ab0 100644
--- a/src/newgrf_airporttiles.cpp
+++ b/src/newgrf_airporttiles.cpp
@@ -264,7 +264,7 @@ uint16 GetAirportTileCallback(CallbackID callback, uint32 param1, uint32 param2,
static void AirportDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, byte colour, StationGfx gfx)
{
- const DrawTileSprites *dts = &group->dts;
+ const DrawTileSprites *dts = group->ProcessRegisters(NULL);
SpriteID image = dts->ground.sprite;
SpriteID pal = dts->ground.pal;
diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp
index ce4a6807a..d0d1c1c5f 100644
--- a/src/newgrf_commons.cpp
+++ b/src/newgrf_commons.cpp
@@ -23,6 +23,7 @@
#include "tunnelbridge_map.h"
#include "newgrf_object.h"
#include "genworld.h"
+#include "newgrf_spritegroup.h"
/**
* Constructor of generic class
@@ -435,6 +436,8 @@ uint32 GetNearbyTileInformation(TileIndex tile)
return tile_type << 24 | z << 16 | terrain_type << 8 | tileh;
}
+/* static */ SmallVector<DrawTileSeqStruct, 8> NewGRFSpriteLayout::result_seq;
+
/**
* Clone the building sprites of a spritelayout.
* @param source The building sprites to copy.
@@ -454,6 +457,26 @@ void NewGRFSpriteLayout::Clone(const DrawTileSeqStruct *source)
}
/**
+ * Clone a spritelayout.
+ * @param source The spritelayout to copy.
+ */
+void NewGRFSpriteLayout::Clone(const NewGRFSpriteLayout *source)
+{
+ this->Clone((const DrawTileSprites*)source);
+
+ if (source->registers != NULL) {
+ size_t count = 1; // 1 for the ground sprite
+ const DrawTileSeqStruct *element;
+ foreach_draw_tile_seq(element, source->seq) count++;
+
+ TileLayoutRegisters *regs = MallocT<TileLayoutRegisters>(count);
+ MemCpyT(regs, source->registers, count);
+ this->registers = regs;
+ }
+}
+
+
+/**
* Allocate a spritelayout for \a num_sprites building sprites.
* @param num_sprites Number of building sprites to allocate memory for. (not counting the terminator)
*/
@@ -465,3 +488,150 @@ void NewGRFSpriteLayout::Allocate(uint num_sprites)
sprites[num_sprites].MakeTerminator();
this->seq = sprites;
}
+
+/**
+ * Allocate memory for register modifiers.
+ */
+void NewGRFSpriteLayout::AllocateRegisters()
+{
+ assert(this->seq != NULL);
+ assert(this->registers == NULL);
+
+ size_t count = 1; // 1 for the ground sprite
+ const DrawTileSeqStruct *element;
+ foreach_draw_tile_seq(element, this->seq) count++;
+
+ this->registers = CallocT<TileLayoutRegisters>(count);
+}
+
+/**
+ * Prepares a sprite layout before resolving action-1-2-3 chains.
+ * Integrates offsets into the layout and determines which chains to resolve.
+ * @note The function uses statically allocated temporary storage, which is reused everytime when calling the function.
+ * That means, you have to use the sprite layout before calling #PrepareLayout() the next time.
+ * @param orig_offset Offset to apply to non-action-1 sprites.
+ * @param newgrf_ground_offset Offset to apply to action-1 ground sprites.
+ * @param newgrf_offset Offset to apply to action-1 non-ground sprites.
+ * @param separate_ground Whether the ground sprite shall be resolved by a separate action-1-2-3 chain by default.
+ * @return Bitmask of values for variable 10 to resolve action-1-2-3 chains for.
+ */
+uint32 NewGRFSpriteLayout::PrepareLayout(uint32 orig_offset, uint32 newgrf_ground_offset, uint32 newgrf_offset, bool separate_ground) const
+{
+ result_seq.Clear();
+ uint32 var10_values = 0;
+
+ /* Create a copy of the spritelayout, so we can modify some values.
+ * Also include the groundsprite into the sequence for easier processing. */
+ DrawTileSeqStruct *result = result_seq.Append();
+ result->image = ground;
+ result->delta_x = 0;
+ result->delta_y = 0;
+ result->delta_z = 0x80;
+
+ const DrawTileSeqStruct *dtss;
+ foreach_draw_tile_seq(dtss, this->seq) {
+ *result_seq.Append() = *dtss;
+ }
+ result_seq.Append()->MakeTerminator();
+
+ /* Determine the var10 values the action-1-2-3 chains needs to be resolved for,
+ * and apply the default sprite offsets (unless disabled). */
+ const TileLayoutRegisters *regs = this->registers;
+ bool ground = true;
+ foreach_draw_tile_seq(result, result_seq.Begin()) {
+ TileLayoutFlags flags = TLF_NOTHING;
+ if (regs != NULL) flags = regs->flags;
+
+ /* Record var10 value for the sprite */
+ if (HasBit(result->image.sprite, SPRITE_MODIFIER_CUSTOM_SPRITE) || (flags & TLF_SPRITE_REG_FLAGS)) {
+ uint8 var10 = (flags & TLF_SPRITE_VAR10) ? regs->sprite_var10 : (ground && separate_ground ? 1 : 0);
+ SetBit(var10_values, var10);
+ }
+
+ /* Add default sprite offset, unless there is a custom one */
+ if (!(flags & TLF_SPRITE)) {
+ if (HasBit(result->image.sprite, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
+ result->image.sprite += ground ? newgrf_ground_offset : newgrf_offset;
+ } else {
+ result->image.sprite += orig_offset;
+ }
+ }
+
+ /* Record var10 value for the palette */
+ if (HasBit(result->image.pal, SPRITE_MODIFIER_CUSTOM_SPRITE) || (flags & TLF_PALETTE_REG_FLAGS)) {
+ uint8 var10 = (flags & TLF_PALETTE_VAR10) ? regs->palette_var10 : (ground && separate_ground ? 1 : 0);
+ SetBit(var10_values, var10);
+ }
+
+ /* Add default palette offset, unless there is a custom one */
+ if (!(flags & TLF_PALETTE)) {
+ if (HasBit(result->image.pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
+ result->image.sprite += ground ? newgrf_ground_offset : newgrf_offset;
+ }
+ }
+
+ ground = false;
+ if (regs != NULL) regs++;
+ }
+
+ return var10_values;
+}
+
+/**
+ * Evaluates the register modifiers and integrates them into the preprocessed sprite layout.
+ * @pre #PrepareLayout() needs calling first.
+ * @param resolved_var10 The value of var10 the action-1-2-3 chain was evaluated for.
+ * @param resolved_sprite Result sprite of the action-1-2-3 chain.
+ * @param separate_ground Whether the ground sprite is resolved by a separate action-1-2-3 chain.
+ * @return Resulting spritelayout after processing the registers.
+ */
+void NewGRFSpriteLayout::ProcessRegisters(uint8 resolved_var10, uint32 resolved_sprite, bool separate_ground) const
+{
+ DrawTileSeqStruct *result;
+ const TileLayoutRegisters *regs = this->registers;
+ bool ground = true;
+ foreach_draw_tile_seq(result, result_seq.Begin()) {
+ TileLayoutFlags flags = TLF_NOTHING;
+ if (regs != NULL) flags = regs->flags;
+
+ /* Is the sprite or bounding box affected by an action-1-2-3 chain? */
+ if (HasBit(result->image.sprite, SPRITE_MODIFIER_CUSTOM_SPRITE) || (flags & TLF_SPRITE_REG_FLAGS)) {
+ /* Does the var10 value apply to this sprite? */
+ uint8 var10 = (flags & TLF_SPRITE_VAR10) ? regs->sprite_var10 : (ground && separate_ground ? 1 : 0);
+ if (var10 == resolved_var10) {
+ /* Apply registers */
+ if ((flags & TLF_DODRAW) && GetRegister(regs->dodraw) == 0) {
+ result->image.sprite = 0;
+ continue;
+ }
+ if (HasBit(result->image.sprite, SPRITE_MODIFIER_CUSTOM_SPRITE)) result->image.sprite += resolved_sprite;
+ if (flags & TLF_SPRITE) result->image.sprite += (int16)GetRegister(regs->sprite); // mask to 16 bits to avoid trouble
+
+ if (result->IsParentSprite()) {
+ if (flags & TLF_BB_XY_OFFSET) {
+ result->delta_x += (int32)GetRegister(regs->delta.parent[0]);
+ result->delta_y += (int32)GetRegister(regs->delta.parent[1]);
+ }
+ if (flags & TLF_BB_Z_OFFSET) result->delta_z += (int32)GetRegister(regs->delta.parent[2]);
+ } else {
+ if (flags & TLF_CHILD_X_OFFSET) result->delta_x += (int32)GetRegister(regs->delta.child[0]);
+ if (flags & TLF_CHILD_Y_OFFSET) result->delta_y += (int32)GetRegister(regs->delta.child[1]);
+ }
+ }
+ }
+
+ /* Is the palette affected by an action-1-2-3 chain? */
+ if (HasBit(result->image.pal, SPRITE_MODIFIER_CUSTOM_SPRITE) || (flags & TLF_PALETTE_REG_FLAGS)) {
+ /* Does the var10 value apply to this sprite? */
+ uint8 var10 = (flags & TLF_PALETTE_VAR10) ? regs->palette_var10 : (ground && separate_ground ? 1 : 0);
+ if (var10 == resolved_var10) {
+ /* Apply registers */
+ if (HasBit(result->image.pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) result->image.pal += resolved_sprite;
+ if (flags & TLF_PALETTE) result->image.pal += (int16)GetRegister(regs->palette); // mask to 16 bits to avoid trouble
+ }
+ }
+
+ ground = false;
+ if (regs != NULL) regs++;
+ }
+}
diff --git a/src/newgrf_commons.h b/src/newgrf_commons.h
index 1ddfdc181..d0e556faf 100644
--- a/src/newgrf_commons.h
+++ b/src/newgrf_commons.h
@@ -18,6 +18,7 @@
#include "tile_type.h"
#include "sprite.h"
#include "core/alloc_type.hpp"
+#include "core/smallvec_type.hpp"
/** Context for tile accesses */
enum TileContext {
@@ -27,13 +28,74 @@ enum TileContext {
};
/**
+ * Flags to enable register usage in sprite layouts.
+ */
+enum TileLayoutFlags {
+ TLF_NOTHING = 0x00,
+
+ TLF_DODRAW = 0x01, ///< Only draw sprite if value of register TileLayoutRegisters::dodraw is non-zero.
+ TLF_SPRITE = 0x02, ///< Add signed offset to sprite from register TileLayoutRegisters::sprite.
+ TLF_PALETTE = 0x04, ///< Add signed offset to palette from register TileLayoutRegisters::palette.
+ TLF_CUSTOM_PALETTE = 0x08, ///< Palette is from Action 1 (moved to SPRITE_MODIFIER_CUSTOM_SPRITE in palette during loading).
+
+ TLF_BB_XY_OFFSET = 0x10, ///< Add signed offset to bounding box X and Y positions from register TileLayoutRegisters::delta.parent[0..1].
+ TLF_BB_Z_OFFSET = 0x20, ///< Add signed offset to bounding box Z positions from register TileLayoutRegisters::delta.parent[2].
+
+ TLF_CHILD_X_OFFSET = 0x10, ///< Add signed offset to child sprite X positions from register TileLayoutRegisters::delta.child[0].
+ TLF_CHILD_Y_OFFSET = 0x20, ///< Add signed offset to child sprite Y positions from register TileLayoutRegisters::delta.child[1].
+
+ TLF_SPRITE_VAR10 = 0x40, ///< Resolve sprite with a specific value in variable 10.
+ TLF_PALETTE_VAR10 = 0x80, ///< Resolve palette with a specific value in variable 10.
+
+ TLF_KNOWN_FLAGS = 0x7F, ///< Known flags. Any unknown set flag will disable the GRF.
+
+ /** Flags which are still required after loading the GRF. */
+ TLF_DRAWING_FLAGS = ~TLF_CUSTOM_PALETTE,
+
+ /** Flags which do not work for the (first) ground sprite. */
+ TLF_NON_GROUND_FLAGS = TLF_BB_XY_OFFSET | TLF_BB_Z_OFFSET | TLF_CHILD_X_OFFSET | TLF_CHILD_Y_OFFSET,
+
+ /** Flags which refer to using multiple action-1-2-3 chains. */
+ TLF_VAR10_FLAGS = TLF_SPRITE_VAR10 | TLF_PALETTE_VAR10,
+
+ /** Flags which require resolving the action-1-2-3 chain for the sprite, even if it is no action-1 sprite. */
+ TLF_SPRITE_REG_FLAGS = TLF_DODRAW | TLF_SPRITE | TLF_BB_XY_OFFSET | TLF_BB_Z_OFFSET | TLF_CHILD_X_OFFSET | TLF_CHILD_Y_OFFSET,
+
+ /** Flags which require resolving the action-1-2-3 chain for the palette, even if it is no action-1 palette. */
+ TLF_PALETTE_REG_FLAGS = TLF_PALETTE,
+};
+DECLARE_ENUM_AS_BIT_SET(TileLayoutFlags)
+
+/**
+ * Additional modifiers for items in sprite layouts.
+ */
+struct TileLayoutRegisters {
+ TileLayoutFlags flags; ///< Flags defining which members are valid and to be used.
+ uint8 dodraw; ///< Register deciding whether the sprite shall be drawn at all. Non-zero means drawing.
+ uint8 sprite; ///< Register specifying a signed offset for the sprite.
+ uint8 palette; ///< Register specifying a signed offset for the palette.
+ union {
+ uint8 parent[3]; ///< Registers for signed offsets for the bounding box position of parent sprites.
+ uint8 child[2]; ///< Registers for signed offsets for the position of child sprites.
+ } delta;
+ uint8 sprite_var10; ///< Value for variable 10 when resolving the sprite.
+ uint8 palette_var10; ///< Value for variable 10 when resolving the palette.
+};
+
+static const uint TLR_MAX_VAR10 = 7; ///< Maximum value for var 10.
+
+/**
* NewGRF supplied spritelayout.
* In contrast to #DrawTileSprites this struct is for allocated
* layouts on the heap. It allocates data and frees them on destruction.
*/
struct NewGRFSpriteLayout : ZeroedMemoryAllocator, DrawTileSprites {
+ const TileLayoutRegisters *registers;
+
void Allocate(uint num_sprites);
+ void AllocateRegisters();
void Clone(const DrawTileSeqStruct *source);
+ void Clone(const NewGRFSpriteLayout *source);
/**
* Clone a spritelayout.
@@ -49,7 +111,37 @@ struct NewGRFSpriteLayout : ZeroedMemoryAllocator, DrawTileSprites {
virtual ~NewGRFSpriteLayout()
{
free(const_cast<DrawTileSeqStruct*>(this->seq));
+ free(const_cast<TileLayoutRegisters*>(this->registers));
+ }
+
+ /**
+ * Tests whether this spritelayout needs preprocessing by
+ * #PrepareLayout() and #ProcessRegisters(), or whether it can be
+ * used directly.
+ * @return true if preprocessing is needed
+ */
+ bool NeedsPreprocessing() const
+ {
+ return this->registers != NULL;
+ }
+
+ uint32 PrepareLayout(uint32 orig_offset, uint32 newgrf_ground_offset, uint32 newgrf_offset, bool separate_ground) const;
+ void ProcessRegisters(uint8 resolved_var10, uint32 resolved_sprite, bool separate_ground) const;
+
+ /**
+ * Returns the result spritelayout after preprocessing.
+ * @pre #PrepareLayout() and #ProcessRegisters() need calling first.
+ * @return result spritelayout
+ */
+ const DrawTileSeqStruct *GetLayout(PalSpriteID *ground) const
+ {
+ DrawTileSeqStruct *front = result_seq.Begin();
+ *ground = front->image;
+ return front + 1;
}
+
+private:
+ static SmallVector<DrawTileSeqStruct, 8> result_seq; ///< Temporary storage when preprocessing spritelayouts.
};
/**
diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp
index f9fb4b16b..1f525bd8e 100644
--- a/src/newgrf_house.cpp
+++ b/src/newgrf_house.cpp
@@ -412,7 +412,7 @@ uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, House
static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, byte stage, HouseID house_id)
{
- const DrawTileSprites *dts = &group->dts;
+ const DrawTileSprites *dts = group->ProcessRegisters(&stage);
const HouseSpec *hs = HouseSpec::Get(house_id);
PaletteID palette = hs->random_colour[TileHash2Bit(ti->x, ti->y)] + PALETTE_RECOLOUR_START;
@@ -428,6 +428,7 @@ static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *grou
PaletteID pal = dts->ground.pal;
if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) image += stage;
+ if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += stage;
if (GB(image, 0, SPRITE_WIDTH) != 0) {
DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp
index 9d38ab5af..397fecad7 100644
--- a/src/newgrf_industrytiles.cpp
+++ b/src/newgrf_industrytiles.cpp
@@ -176,12 +176,13 @@ static void NewIndustryTileResolver(ResolverObject *res, IndustryGfx gfx, TileIn
static void IndustryDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, byte rnd_colour, byte stage, IndustryGfx gfx)
{
- const DrawTileSprites *dts = &group->dts;
+ const DrawTileSprites *dts = group->ProcessRegisters(&stage);
SpriteID image = dts->ground.sprite;
PaletteID pal = dts->ground.pal;
if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) image += stage;
+ if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += stage;
if (GB(image, 0, SPRITE_WIDTH) != 0) {
/* If the ground sprite is the default flat water sprite, draw also canal/river borders
diff --git a/src/newgrf_object.cpp b/src/newgrf_object.cpp
index 4c3b7c3e1..967017039 100644
--- a/src/newgrf_object.cpp
+++ b/src/newgrf_object.cpp
@@ -417,7 +417,7 @@ uint16 GetObjectCallback(CallbackID callback, uint32 param1, uint32 param2, cons
*/
static void DrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, const ObjectSpec *spec)
{
- const DrawTileSprites *dts = &group->dts;
+ const DrawTileSprites *dts = group->ProcessRegisters(NULL);
PaletteID palette = ((spec->flags & OBJECT_FLAG_2CC_COLOUR) ? SPR_2CCMAP_BASE : PALETTE_RECOLOUR_START) + Object::GetByTile(ti->tile)->colour;
SpriteID image = dts->ground.sprite;
@@ -468,7 +468,7 @@ void DrawNewObjectTileInGUI(int x, int y, const ObjectSpec *spec, uint8 view)
const SpriteGroup *group = SpriteGroup::Resolve(GetObjectSpriteGroup(spec, NULL), &object);
if (group == NULL || group->type != SGT_TILELAYOUT) return;
- const DrawTileSprites *dts = &((const TileLayoutSpriteGroup *)group)->dts;
+ const DrawTileSprites *dts = ((const TileLayoutSpriteGroup *)group)->ProcessRegisters(NULL);
PaletteID palette;
if (Company::IsValidID(_local_company)) {
diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp
index b36df7016..3b1693b4e 100644
--- a/src/newgrf_spritegroup.cpp
+++ b/src/newgrf_spritegroup.cpp
@@ -226,3 +226,26 @@ const SpriteGroup *RealSpriteGroup::Resolve(ResolverObject *object) const
{
return object->ResolveReal(object, this);
}
+
+/**
+ * Process registers and the construction stage into the sprite layout.
+ * The passed construction stage might get reset to zero, if it gets incorporated into the layout
+ * during the preprocessing.
+ * @param [in, out] stage Construction stage (0-3), or NULL if not applicable.
+ * @return sprite layout to draw.
+ */
+const DrawTileSprites *TileLayoutSpriteGroup::ProcessRegisters(uint8 *stage) const
+{
+ if (!this->dts.NeedsPreprocessing()) return &this->dts;
+
+ static DrawTileSprites result;
+ uint8 actual_stage = stage != NULL ? *stage : 0;
+ this->dts.PrepareLayout(0, actual_stage, actual_stage, false);
+ this->dts.ProcessRegisters(0, 0, false);
+ result.seq = this->dts.GetLayout(&result.ground);
+
+ /* Stage has been processed by PrepareLayout(), set it to zero. */
+ if (stage != NULL) *stage = 0;
+
+ return &result;
+}
diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h
index 89be1dc08..3a13a3bbe 100644
--- a/src/newgrf_spritegroup.h
+++ b/src/newgrf_spritegroup.h
@@ -278,12 +278,17 @@ struct ResultSpriteGroup : SpriteGroup {
byte GetNumResults() const { return this->num_sprites; }
};
+/**
+ * Action 2 sprite layout for houses, industry tiles, objects and airport tiles.
+ */
struct TileLayoutSpriteGroup : SpriteGroup {
TileLayoutSpriteGroup() : SpriteGroup(SGT_TILELAYOUT) {}
~TileLayoutSpriteGroup() {}
byte num_building_stages; ///< Number of building stages to show for this house/industry tile
NewGRFSpriteLayout dts;
+
+ const DrawTileSprites *ProcessRegisters(uint8 *stage) const;
};
struct IndustryProductionSpriteGroup : SpriteGroup {
diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp
index 5d2707b70..af0945c05 100644
--- a/src/newgrf_station.cpp
+++ b/src/newgrf_station.cpp
@@ -746,7 +746,7 @@ void DeallocateSpecFromStation(BaseStation *st, byte specindex)
bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID sclass, uint station)
{
const StationSpec *statspec;
- const DrawTileSprites *sprites;
+ const DrawTileSprites *sprites = NULL;
const RailtypeInfo *rti = GetRailTypeInfo(railtype);
PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company);
uint tile = 2;
@@ -754,36 +754,57 @@ bool DrawStationTile(int x, int y, RailType railtype, Axis axis, StationClassID
statspec = StationClass::Get(sclass, station);
if (statspec == NULL) return false;
- uint relocation = GetCustomStationRelocation(statspec, NULL, INVALID_TILE);
-
if (HasBit(statspec->callback_mask, CBM_STATION_SPRITE_LAYOUT)) {
uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0x2110000, 0, statspec, NULL, INVALID_TILE);
if (callback != CALLBACK_FAILED) tile = callback;
}
+ uint32 total_offset = rti->GetRailtypeSpriteOffset();
+ uint32 relocation = 0;
+ uint32 ground_relocation = 0;
+ const NewGRFSpriteLayout *layout = NULL;
+ DrawTileSprites tmp_rail_layout;
+
if (statspec->renderdata == NULL) {
sprites = GetStationTileLayout(STATION_RAIL, tile + axis);
} else {
- sprites = &statspec->renderdata[(tile < statspec->tiles) ? tile + axis : (uint)axis];
+ layout = &statspec->renderdata[(tile < statspec->tiles) ? tile + axis : (uint)axis];
+ if (!layout->NeedsPreprocessing()) {
+ sprites = layout;
+ layout = NULL;
+ }
}
- SpriteID image = sprites->ground.sprite;
- PaletteID pal = sprites->ground.pal;
- if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
- if (HasBit(statspec->flags, SSF_SEPARATE_GROUND)) {
- /* Use separate action 1-2-3 chain for ground sprite */
- image += GetCustomStationRelocation(statspec, NULL, INVALID_TILE, 1);
- } else {
- image += relocation;
+ if (layout != NULL) {
+ /* Sprite layout which needs preprocessing */
+ bool separate_ground = HasBit(statspec->flags, SSF_SEPARATE_GROUND);
+ uint32 var10_values = layout->PrepareLayout(total_offset, rti->fallback_railtype, 0, separate_ground);
+ uint8 var10;
+ FOR_EACH_SET_BIT(var10, var10_values) {
+ uint32 var10_relocation = GetCustomStationRelocation(statspec, NULL, INVALID_TILE, var10);
+ layout->ProcessRegisters(var10, var10_relocation, separate_ground);
}
- image += rti->fallback_railtype;
+
+ tmp_rail_layout.seq = layout->GetLayout(&tmp_rail_layout.ground);
+ sprites = &tmp_rail_layout;
+ total_offset = 0;
} else {
- image += rti->GetRailtypeSpriteOffset();
+ /* Simple sprite layout */
+ ground_relocation = relocation = GetCustomStationRelocation(statspec, NULL, INVALID_TILE, 0);
+ if (HasBit(sprites->ground.sprite, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
+ ground_relocation = GetCustomStationRelocation(statspec, NULL, INVALID_TILE, 1);
+ }
+ ground_relocation += rti->fallback_railtype;
}
+ SpriteID image = sprites->ground.sprite;
+ PaletteID pal = sprites->ground.pal;
+ image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
+ if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
+
DrawSprite(image, GroundSpritePaletteTransform(image, pal, palette), x, y);
- DrawRailTileSeqInGUI(x, y, sprites, rti->GetRailtypeSpriteOffset(), relocation, palette);
+ DrawRailTileSeqInGUI(x, y, sprites, total_offset, relocation, palette);
return true;
}
diff --git a/src/sprite.cpp b/src/sprite.cpp
index e9a83052c..ee051dc7e 100644
--- a/src/sprite.cpp
+++ b/src/sprite.cpp
@@ -32,18 +32,29 @@ void DrawCommonTileSeq(const TileInfo *ti, const DrawTileSprites *dts, Transpare
{
bool parent_sprite_encountered = false;
const DrawTileSeqStruct *dtss;
+ bool skip_childs = false;
foreach_draw_tile_seq(dtss, dts->seq) {
SpriteID image = dtss->image.sprite;
+ PaletteID pal = dtss->image.pal;
+
+ if (skip_childs) {
+ if (!dtss->IsParentSprite()) continue;
+ skip_childs = false;
+ }
/* TTD sprite 0 means no sprite */
- if (GB(image, 0, SPRITE_WIDTH) == 0 && !HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) continue;
+ if (GB(image, 0, SPRITE_WIDTH) == 0 && !HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
+ skip_childs = dtss->IsParentSprite();
+ continue;
+ }
/* Stop drawing sprite sequence once we meet a sprite that doesn't have to be opaque */
if (IsInvisibilitySet(to) && !HasBit(image, SPRITE_MODIFIER_OPAQUE)) return;
image += (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? newgrf_offset : orig_offset);
+ if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += newgrf_offset;
- PaletteID pal = SpriteLayoutPaletteTransform(image, dtss->image.pal, default_palette);
+ pal = SpriteLayoutPaletteTransform(image, pal, default_palette);
if (dtss->IsParentSprite()) {
parent_sprite_encountered = true;
@@ -86,15 +97,26 @@ void DrawCommonTileSeqInGUI(int x, int y, const DrawTileSprites *dts, int32 orig
const DrawTileSeqStruct *dtss;
Point child_offset = {0, 0};
+ bool skip_childs = false;
foreach_draw_tile_seq(dtss, dts->seq) {
SpriteID image = dtss->image.sprite;
+ PaletteID pal = dtss->image.pal;
+
+ if (skip_childs) {
+ if (!dtss->IsParentSprite()) continue;
+ skip_childs = false;
+ }
/* TTD sprite 0 means no sprite */
- if (GB(image, 0, SPRITE_WIDTH) == 0 && !HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) continue;
+ if (GB(image, 0, SPRITE_WIDTH) == 0 && !HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
+ skip_childs = dtss->IsParentSprite();
+ continue;
+ }
image += (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? newgrf_offset : orig_offset);
+ if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += newgrf_offset;
- PaletteID pal = SpriteLayoutPaletteTransform(image, dtss->image.pal, default_palette);
+ pal = SpriteLayoutPaletteTransform(image, pal, default_palette);
if (dtss->IsParentSprite()) {
Point pt = RemapCoords(dtss->delta_x, dtss->delta_y, dtss->delta_z);
diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp
index 7cd32972d..73be9d1d2 100644
--- a/src/station_cmd.cpp
+++ b/src/station_cmd.cpp
@@ -2495,12 +2495,14 @@ const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx)
static void DrawTile_Station(TileInfo *ti)
{
+ const NewGRFSpriteLayout *layout = NULL;
+ DrawTileSprites tmp_rail_layout;
const DrawTileSprites *t = NULL;
RoadTypes roadtypes;
int32 total_offset;
- int32 custom_ground_offset;
const RailtypeInfo *rti = NULL;
uint32 relocation = 0;
+ uint32 ground_relocation = 0;
const BaseStation *st = NULL;
const StationSpec *statspec = NULL;
uint tile_layout = 0;
@@ -2509,7 +2511,6 @@ static void DrawTile_Station(TileInfo *ti)
rti = GetRailTypeInfo(GetRailType(ti->tile));
roadtypes = ROADTYPES_NONE;
total_offset = rti->GetRailtypeSpriteOffset();
- custom_ground_offset = rti->fallback_railtype;
if (IsCustomStationSpecIndex(ti->tile)) {
/* look for customization */
@@ -2519,8 +2520,6 @@ static void DrawTile_Station(TileInfo *ti)
if (statspec != NULL) {
tile_layout = GetStationGfx(ti->tile);
- relocation = GetCustomStationRelocation(statspec, st, ti->tile);
-
if (HasBit(statspec->callback_mask, CBM_STATION_SPRITE_LAYOUT)) {
uint16 callback = GetStationCallback(CBID_STATION_SPRITE_LAYOUT, 0, 0, statspec, st, ti->tile);
if (callback != CALLBACK_FAILED) tile_layout = (callback & ~1) + GetRailStationAxis(ti->tile);
@@ -2528,14 +2527,17 @@ static void DrawTile_Station(TileInfo *ti)
/* Ensure the chosen tile layout is valid for this custom station */
if (statspec->renderdata != NULL) {
- t = &statspec->renderdata[tile_layout < statspec->tiles ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
+ layout = &statspec->renderdata[tile_layout < statspec->tiles ? tile_layout : (uint)GetRailStationAxis(ti->tile)];
+ if (!layout->NeedsPreprocessing()) {
+ t = layout;
+ layout = NULL;
+ }
}
}
}
} else {
roadtypes = IsRoadStop(ti->tile) ? GetRoadTypes(ti->tile) : ROADTYPES_NONE;
total_offset = 0;
- custom_ground_offset = 0;
}
if (IsAirport(ti->tile)) {
@@ -2579,7 +2581,7 @@ static void DrawTile_Station(TileInfo *ti)
palette = PALETTE_TO_GREY;
}
- if (t == NULL || t->seq == NULL) t = GetStationTileLayout(GetStationType(ti->tile), GetStationGfx(ti->tile));
+ if (layout == NULL && (t == NULL || t->seq == NULL)) t = GetStationTileLayout(GetStationType(ti->tile), GetStationGfx(ti->tile));
/* don't show foundation for docks */
if (ti->tileh != SLOPE_FLAT && !IsDock(ti->tile)) {
@@ -2665,6 +2667,27 @@ draw_default_foundation:
}
}
} else {
+ if (layout != NULL) {
+ /* Sprite layout which needs preprocessing */
+ bool separate_ground = HasBit(statspec->flags, SSF_SEPARATE_GROUND);
+ uint32 var10_values = layout->PrepareLayout(total_offset, rti->fallback_railtype, 0, separate_ground);
+ uint8 var10;
+ FOR_EACH_SET_BIT(var10, var10_values) {
+ uint32 var10_relocation = GetCustomStationRelocation(statspec, st, ti->tile, var10);
+ layout->ProcessRegisters(var10, var10_relocation, separate_ground);
+ }
+ tmp_rail_layout.seq = layout->GetLayout(&tmp_rail_layout.ground);
+ t = &tmp_rail_layout;
+ total_offset = 0;
+ } else if (statspec != NULL) {
+ /* Simple sprite layout */
+ ground_relocation = relocation = GetCustomStationRelocation(statspec, st, ti->tile, 0);
+ if (HasBit(statspec->flags, SSF_SEPARATE_GROUND)) {
+ ground_relocation = GetCustomStationRelocation(statspec, st, ti->tile, 1);
+ }
+ ground_relocation += rti->fallback_railtype;
+ }
+
SpriteID image = t->ground.sprite;
PaletteID pal = t->ground.pal;
if (rti != NULL && rti->UsesOverlay() && (image == SPR_RAIL_TRACK_X || image == SPR_RAIL_TRACK_Y)) {
@@ -2677,17 +2700,8 @@ draw_default_foundation:
DrawGroundSprite(overlay + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PALETTE_CRASH);
}
} else {
- if (HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE)) {
- if (HasBit(statspec->flags, SSF_SEPARATE_GROUND)) {
- /* Use separate action 1-2-3 chain for ground sprite */
- image += GetCustomStationRelocation(statspec, st, ti->tile, 1);
- } else {
- image += relocation;
- }
- image += custom_ground_offset;
- } else {
- image += total_offset;
- }
+ image += HasBit(image, SPRITE_MODIFIER_CUSTOM_SPRITE) ? ground_relocation : total_offset;
+ if (HasBit(pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) pal += ground_relocation;
DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
/* PBS debugging, draw reserved tracks darker */