# HG changeset patch # User frosch # Date 1315753809 0 # Node ID 8adf9c55b5e54f88bb4c9656d97170670249aa8e # Parent 7338f18ce5e4706d86162c99120ef907cd25bf86 (svn r22924) -Codechange: Enhance NewGRFSpriteLayout for drawing construction stages in spritelayouts with inconsistent number of sprites per spriteset. diff --git a/src/newgrf.cpp b/src/newgrf.cpp --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -607,9 +607,11 @@ * @param use_cur_spritesets Whether to use currently referenceable action 1 sets. * @param feature GrfSpecFeature to use spritesets from. * @param [out] grf_sprite Read sprite and palette. + * @param [out] max_sprite_offset Optionally returns the number of sprites in the spriteset of the sprite. (0 if no spritset) + * @param [out] max_palette_offset Optionally returns the number of sprites in the spriteset of the palette. (0 if no spritset) * @return Read TileLayoutFlags. */ -static TileLayoutFlags ReadSpriteLayoutSprite(ByteReader *buf, bool read_flags, bool invert_action1_flag, bool use_cur_spritesets, byte feature, PalSpriteID *grf_sprite) +static TileLayoutFlags ReadSpriteLayoutSprite(ByteReader *buf, bool read_flags, bool invert_action1_flag, bool use_cur_spritesets, int feature, PalSpriteID *grf_sprite, uint16 *max_sprite_offset = NULL, uint16 *max_palette_offset = NULL) { grf_sprite->sprite = buf->ReadWord(); grf_sprite->pal = buf->ReadWord(); @@ -628,6 +630,7 @@ grf_sprite->pal = PAL_NONE; } else { SpriteID sprite = use_cur_spritesets ? _cur.GetSprite(feature, index) : index; + if (max_sprite_offset != NULL) *max_sprite_offset = use_cur_spritesets ? _cur.GetNumEnts(feature, index) : UINT16_MAX; SB(grf_sprite->sprite, 0, SPRITE_WIDTH, sprite); SetBit(grf_sprite->sprite, SPRITE_MODIFIER_CUSTOM_SPRITE); } @@ -645,6 +648,7 @@ grf_sprite->pal = PAL_NONE; } else { SpriteID sprite = use_cur_spritesets ? _cur.GetSprite(feature, index) : index; + if (max_palette_offset != NULL) *max_palette_offset = use_cur_spritesets ? _cur.GetNumEnts(feature, index) : UINT16_MAX; SB(grf_sprite->pal, 0, SPRITE_WIDTH, sprite); SetBit(grf_sprite->pal, SPRITE_MODIFIER_CUSTOM_SPRITE); } @@ -726,8 +730,13 @@ if (!allow_var10) valid_flags &= ~TLF_VAR10_FLAGS; dts->Allocate(num_building_sprites); // allocate before reading groundsprite flags + uint16 *max_sprite_offset = AllocaM(uint16, num_building_sprites + 1); + uint16 *max_palette_offset = AllocaM(uint16, num_building_sprites + 1); + MemSetT(max_sprite_offset, 0, num_building_sprites + 1); + MemSetT(max_palette_offset, 0, num_building_sprites + 1); + /* Groundsprite */ - TileLayoutFlags flags = ReadSpriteLayoutSprite(buf, has_flags, false, use_cur_spritesets, feature, &dts->ground); + TileLayoutFlags flags = ReadSpriteLayoutSprite(buf, has_flags, false, use_cur_spritesets, feature, &dts->ground, max_sprite_offset, max_palette_offset); if (_cur.skip_sprites < 0) return true; if (flags & ~(valid_flags & ~TLF_NON_GROUND_FLAGS)) { @@ -742,7 +751,7 @@ for (uint i = 0; i < num_building_sprites; i++) { DrawTileSeqStruct *seq = const_cast(&dts->seq[i]); - flags = ReadSpriteLayoutSprite(buf, has_flags, false, use_cur_spritesets, feature, &seq->image); + flags = ReadSpriteLayoutSprite(buf, has_flags, false, use_cur_spritesets, feature, &seq->image, max_sprite_offset + i + 1, max_palette_offset + i + 1); if (_cur.skip_sprites < 0) return true; if (flags & ~valid_flags) { @@ -766,6 +775,42 @@ if (_cur.skip_sprites < 0) return true; } + /* Check if the number of sprites per spriteset is consistent */ + bool is_consistent = true; + dts->consistent_max_offset = 0; + for (uint i = 0; i < num_building_sprites + 1; i++) { + if (max_sprite_offset[i] > 0) { + if (dts->consistent_max_offset == 0) { + dts->consistent_max_offset = max_sprite_offset[i]; + } else if (dts->consistent_max_offset != max_sprite_offset[i]) { + is_consistent = false; + break; + } + } + if (max_palette_offset[i] > 0) { + if (dts->consistent_max_offset == 0) { + dts->consistent_max_offset = max_palette_offset[i]; + } else if (dts->consistent_max_offset != max_palette_offset[i]) { + is_consistent = false; + break; + } + } + } + + /* When the Action1 sets are unknown, everything should be 0 (no spriteset usage) or UINT16_MAX (some spriteset usage) */ + assert(use_cur_spritesets || (is_consistent && (dts->consistent_max_offset == 0 || dts->consistent_max_offset == UINT16_MAX))); + + if (!is_consistent || dts->registers != NULL) { + dts->consistent_max_offset = 0; + if (dts->registers == NULL) dts->AllocateRegisters(); + + for (uint i = 0; i < num_building_sprites + 1; i++) { + TileLayoutRegisters ®s = const_cast(dts->registers[i]); + regs.max_sprite_offset = max_sprite_offset[i]; + regs.max_palette_offset = max_palette_offset[i]; + } + } + return false; } @@ -1584,6 +1629,7 @@ for (uint t = 0; t < statspec->tiles; t++) { NewGRFSpriteLayout *dts = &statspec->renderdata[t]; + dts->consistent_max_offset = UINT16_MAX; // Spritesets are unknown, so no limit. if (buf->HasData(4) && *(uint32*)buf->Data() == 0) { buf->Skip(4); @@ -4403,13 +4449,6 @@ assert(TileLayoutSpriteGroup::CanAllocateItem()); TileLayoutSpriteGroup *group = new TileLayoutSpriteGroup(); act_group = group; - /* num_building_stages should be 1, if we are only using non-custom sprites */ - if (_cur.HasValidSpriteSets(feature)) { - /* This assumes that all spritesets have the same number of sprites. */ - group->num_building_stages = max(1u, _cur.GetNumEnts(feature, 0)); - } else { - group->num_building_stages = 1; - } /* On error, bail out immediately. Temporary GRF data was already freed */ if (ReadSpriteLayout(buf, num_building_sprites, true, feature, false, type == 0, &group->dts)) return; diff --git a/src/newgrf_commons.cpp b/src/newgrf_commons.cpp --- a/src/newgrf_commons.cpp +++ b/src/newgrf_commons.cpp @@ -571,10 +571,11 @@ * @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 constr_stage Construction stage (0-3) to apply to all action-1 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 +uint32 NewGRFSpriteLayout::PrepareLayout(uint32 orig_offset, uint32 newgrf_ground_offset, uint32 newgrf_offset, uint constr_stage, bool separate_ground) const { result_seq.Clear(); uint32 var10_values = 0; @@ -611,6 +612,7 @@ if (!(flags & TLF_SPRITE)) { if (HasBit(result->image.sprite, SPRITE_MODIFIER_CUSTOM_SPRITE)) { result->image.sprite += ground ? newgrf_ground_offset : newgrf_offset; + if (constr_stage > 0 && regs != NULL) result->image.sprite += GetConstructionStageOffset(constr_stage, regs->max_sprite_offset); } else { result->image.sprite += orig_offset; } @@ -626,6 +628,7 @@ if (!(flags & TLF_PALETTE)) { if (HasBit(result->image.pal, SPRITE_MODIFIER_CUSTOM_SPRITE)) { result->image.sprite += ground ? newgrf_ground_offset : newgrf_offset; + if (constr_stage > 0 && regs != NULL) result->image.sprite += GetConstructionStageOffset(constr_stage, regs->max_palette_offset); } } @@ -663,7 +666,14 @@ result->image.sprite = 0; } else { 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 (flags & TLF_SPRITE) { + int16 offset = (int16)GetRegister(regs->sprite); // mask to 16 bits to avoid trouble + if (!HasBit(result->image.sprite, SPRITE_MODIFIER_CUSTOM_SPRITE) || (offset >= 0 && offset < regs->max_sprite_offset)) { + result->image.sprite += offset; + } else { + result->image.sprite = SPR_IMG_QUERY; + } + } if (result->IsParentSprite()) { if (flags & TLF_BB_XY_OFFSET) { @@ -686,7 +696,15 @@ 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 + if (flags & TLF_PALETTE) { + int16 offset = (int16)GetRegister(regs->palette); // mask to 16 bits to avoid trouble + if (!HasBit(result->image.pal, SPRITE_MODIFIER_CUSTOM_SPRITE) || (offset >= 0 && offset < regs->max_palette_offset)) { + result->image.pal += offset; + } else { + result->image.sprite = SPR_IMG_QUERY; + result->image.pal = PAL_NONE; + } + } } } diff --git a/src/newgrf_commons.h b/src/newgrf_commons.h --- a/src/newgrf_commons.h +++ b/src/newgrf_commons.h @@ -96,6 +96,8 @@ 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. + uint16 max_sprite_offset; ///< Maximum offset to add to the sprite. (limited by size of the spriteset) + uint16 max_palette_offset; ///< Maximum offset to add to the palette. (limited by size of the spriteset) 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. @@ -114,6 +116,12 @@ struct NewGRFSpriteLayout : ZeroedMemoryAllocator, DrawTileSprites { const TileLayoutRegisters *registers; + /** + * Number of sprites in all referenced spritesets. + * If these numbers are inconsistent, then this is 0 and the real values are in \c registers. + */ + uint consistent_max_offset; + void Allocate(uint num_sprites); void AllocateRegisters(); void Clone(const DrawTileSeqStruct *source); @@ -147,7 +155,7 @@ return this->registers != NULL; } - uint32 PrepareLayout(uint32 orig_offset, uint32 newgrf_ground_offset, uint32 newgrf_offset, bool separate_ground) const; + uint32 PrepareLayout(uint32 orig_offset, uint32 newgrf_ground_offset, uint32 newgrf_offset, uint constr_stage, bool separate_ground) const; void ProcessRegisters(uint8 resolved_var10, uint32 resolved_sprite, bool separate_ground) const; /** diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -236,12 +236,14 @@ */ const DrawTileSprites *TileLayoutSpriteGroup::ProcessRegisters(uint8 *stage) const { - if (stage != NULL) *stage = GetConstructionStageOffset(*stage, this->num_building_stages); - if (!this->dts.NeedsPreprocessing()) return &this->dts; + if (!this->dts.NeedsPreprocessing()) { + if (stage != NULL && this->dts.consistent_max_offset > 0) *stage = GetConstructionStageOffset(*stage, this->dts.consistent_max_offset); + return &this->dts; + } static DrawTileSprites result; uint8 actual_stage = stage != NULL ? *stage : 0; - this->dts.PrepareLayout(0, actual_stage, actual_stage, false); + this->dts.PrepareLayout(0, 0, 0, actual_stage, false); this->dts.ProcessRegisters(0, 0, false); result.seq = this->dts.GetLayout(&result.ground); diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -288,7 +288,6 @@ 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; diff --git a/src/newgrf_station.cpp b/src/newgrf_station.cpp --- a/src/newgrf_station.cpp +++ b/src/newgrf_station.cpp @@ -838,7 +838,7 @@ 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); + uint32 var10_values = layout->PrepareLayout(total_offset, rti->fallback_railtype, 0, 0, separate_ground); uint8 var10; FOR_EACH_SET_BIT(var10, var10_values) { uint32 var10_relocation = GetCustomStationRelocation(statspec, NULL, INVALID_TILE, var10); diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -2687,7 +2687,7 @@ 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); + uint32 var10_values = layout->PrepareLayout(total_offset, rti->fallback_railtype, 0, 0, separate_ground); uint8 var10; FOR_EACH_SET_BIT(var10, var10_values) { uint32 var10_relocation = GetCustomStationRelocation(statspec, st, ti->tile, var10);