mirror of https://github.com/OpenTTD/OpenTTD.git
(svn r22518) -Feature: [NewGRF] Advanced sprite layouts with register modifiers.
This commit is contained in:
parent
5b449145f7
commit
a241a4ce97
|
@ -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!
|
||||
|
|
190
src/newgrf.cpp
190
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 ®s = 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
@ -453,6 +456,26 @@ void NewGRFSpriteLayout::Clone(const DrawTileSeqStruct *source)
|
|||
this->seq = sprites;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
@ -26,14 +27,75 @@ enum TileContext {
|
|||
TCX_ON_BRIDGE, ///< Querying information about stuff on the bridge (via some bridgehead).
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
tmp_rail_layout.seq = layout->GetLayout(&tmp_rail_layout.ground);
|
||||
sprites = &tmp_rail_layout;
|
||||
total_offset = 0;
|
||||
} else {
|
||||
/* 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;
|
||||
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;
|
||||
}
|
||||
image += rti->fallback_railtype;
|
||||
} else {
|
||||
image += rti->GetRailtypeSpriteOffset();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in New Issue