Changeset - r17728:6a6e98f25cac
[Not reviewed]
master
0 13 0
frosch - 13 years ago 2011-05-29 16:56:22
frosch@openttd.org
(svn r22518) -Feature: [NewGRF] Advanced sprite layouts with register modifiers.
13 files changed with 560 insertions and 68 deletions:
0 comments (0 inline, 0 general)
src/lang/english.txt
Show inline comments
 
@@ -2472,12 +2472,13 @@ STR_NEWGRF_ERROR_INVALID_ID             
 
STR_NEWGRF_ERROR_CORRUPT_SPRITE                                 :{YELLOW}{RAW_STRING} contains a corrupt sprite. All corrupt sprites will be shown as a red question mark (?)
 
STR_NEWGRF_ERROR_MULTIPLE_ACTION_8                              :Contains multiple Action 8 entries
 
STR_NEWGRF_ERROR_READ_BOUNDS                                    :Read past end of pseudo-sprite
 
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!
 
STR_NEWGRF_CONFIRMATION_TEXT                                    :{YELLOW}You are about to make changes to a running game. This can crash OpenTTD or break the game state. Do not file bug reports about these issues.{}Are you absolutely sure about this?
 

	
 
STR_NEWGRF_DUPLICATE_GRFID                                      :{WHITE}Can't add file: duplicate GRF ID
src/newgrf.cpp
Show inline comments
 
@@ -479,22 +479,25 @@ static void MapSpriteMappingRecolour(Pal
 
}
 

	
 
/**
 
 * 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);
 

	
 
	bool custom_sprite = HasBit(grf_sprite->pal, 15) != invert_action1_flag;
 
	ClrBit(grf_sprite->pal, 15);
 
	if (custom_sprite) {
 
@@ -506,13 +509,149 @@ static void ReadSpriteLayoutSprite(ByteR
 
			grf_sprite->pal = PAL_NONE;
 
		} else {
 
			SpriteID sprite = action1_offset + index * action1_pitch;
 
			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;
 
}
 

	
 
/**
 
 * Converts TTD(P) Base Price pointers into the enum used by OTTD
 
 * See http://wiki.ttdpatch.net/tiki-index.php?page=BaseCosts
 
 * @param base_pointer TTD(P) Base Price Pointer
 
@@ -537,12 +676,13 @@ static void ConvertTTDBasePrice(uint32 b
 

	
 
	*index = (Price)((base_pointer - start) / size);
 
}
 

	
 
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
 
};
 

	
 
typedef ChangeInfoResult (*VCI_Handler)(uint engine, int numinfo, int prop, ByteReader *buf);
 
@@ -1266,13 +1406,15 @@ static ChangeInfoResult StationChangeInf
 
						buf->Skip(4);
 
						extern const DrawTileSprites _station_display_datas_rail[8];
 
						dts->Clone(&_station_display_datas_rail[t % 8]);
 
						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();
 
					for (;;) {
 
						/* no relative bounding box support */
 
						DrawTileSeqStruct *dtss = tmp_layout.Append();
 
@@ -1283,13 +1425,15 @@ static ChangeInfoResult StationChangeInf
 
						dtss->delta_y = buf->ReadByte();
 
						dtss->delta_z = buf->ReadByte();
 
						dtss->size_x = buf->ReadByte();
 
						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());
 
				}
 
				break;
 

	
 
			case 0x0A: { // Copy sprite layout
 
@@ -1425,12 +1569,25 @@ static ChangeInfoResult StationChangeInf
 
				break;
 

	
 
			case 0x18: // Animation triggers
 
				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;
 
		}
 
	}
 

	
 
@@ -3521,12 +3678,16 @@ static ChangeInfoResult AirportTilesChan
 

	
 
static bool HandleChangeInfoResult(const char *caller, ChangeInfoResult cir, uint8 feature, uint8 property)
 
{
 
	switch (cir) {
 
		default: NOT_REACHED();
 

	
 
		case CIR_DISABLED:
 
			/* Error has already been printed; just stop parsing */
 
			return true;
 

	
 
		case CIR_SUCCESS:
 
			return false;
 

	
 
		case CIR_UNHANDLED:
 
			grfmsg(1, "%s: Ignoring property 0x%02X of feature 0x%02X (not implemented)", caller, property, feature);
 
			return false;
 
@@ -3958,40 +4119,21 @@ static void NewSpriteGroup(ByteReader *b
 
				case GSF_AIRPORTTILES:
 
				case GSF_OBJECTS:
 
				case GSF_INDUSTRYTILES: {
 
					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();
 
					act_group = group;
 
					/* 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;
 
				}
 

	
 
				case GSF_INDUSTRIES: {
 
					if (type > 1) {
 
						grfmsg(1, "NewSpriteGroup: Unsupported industry production version %d, skipping", type);
src/newgrf_airporttiles.cpp
Show inline comments
 
@@ -261,13 +261,13 @@ uint16 GetAirportTileCallback(CallbackID
 

	
 
	return group->GetCallbackResult();
 
}
 

	
 
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;
 

	
 
	if (GB(image, 0, SPRITE_WIDTH) != 0) {
 
		if (image == SPR_FLAT_WATER_TILE && IsTileOnWater(ti->tile)) {
src/newgrf_commons.cpp
Show inline comments
 
@@ -20,12 +20,13 @@
 
#include "clear_map.h"
 
#include "station_map.h"
 
#include "tree_map.h"
 
#include "tunnelbridge_map.h"
 
#include "newgrf_object.h"
 
#include "genworld.h"
 
#include "newgrf_spritegroup.h"
 

	
 
/**
 
 * Constructor of generic class
 
 * @param offset end of original data for this entity. i.e: houses = 110
 
 * @param maximum of entities this manager can deal with. i.e: houses = 512
 
 * @param invalid is the ID used to identify an invalid entity id
 
@@ -432,12 +433,14 @@ uint32 GetNearbyTileInformation(TileInde
 
	uint z;
 
	Slope tileh = GetTileSlope(tile, &z);
 
	byte terrain_type = GetTerrainType(tile) << 2 | (tile_type == MP_WATER ? 1 : 0) << 1;
 
	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.
 
 */
 
void NewGRFSpriteLayout::Clone(const DrawTileSeqStruct *source)
 
{
 
@@ -451,17 +454,184 @@ void NewGRFSpriteLayout::Clone(const Dra
 
	DrawTileSeqStruct *sprites = MallocT<DrawTileSeqStruct>(count);
 
	MemCpyT(sprites, source, count);
 
	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)
 
 */
 
void NewGRFSpriteLayout::Allocate(uint num_sprites)
 
{
 
	assert(this->seq == NULL);
 

	
 
	DrawTileSeqStruct *sprites = CallocT<DrawTileSeqStruct>(num_sprites + 1);
 
	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++;
 
	}
 
}
src/newgrf_commons.h
Show inline comments
 
@@ -15,28 +15,90 @@
 
#ifndef NEWGRF_COMMONS_H
 
#define NEWGRF_COMMONS_H
 

	
 
#include "tile_type.h"
 
#include "sprite.h"
 
#include "core/alloc_type.hpp"
 
#include "core/smallvec_type.hpp"
 

	
 
/** Context for tile accesses */
 
enum TileContext {
 
	TCX_NORMAL,         ///< Nothing special.
 
	TCX_UPPER_HALFTILE, ///< Querying information about the upper part of a tile with halftile foundation.
 
	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.
 
	 * @param source The spritelayout to copy.
 
	 */
 
	void Clone(const DrawTileSprites *source)
 
@@ -46,13 +108,43 @@ struct NewGRFSpriteLayout : ZeroedMemory
 
		this->Clone(source->seq);
 
	}
 

	
 
	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.
 
};
 

	
 
/**
 
 * Maps an entity id stored on the map to a GRF file.
 
 * Entities are objects used ingame (houses, industries, industry tiles) for
 
 * which we need to correlate the ids from the grf files with the ones in the
src/newgrf_house.cpp
Show inline comments
 
@@ -409,13 +409,13 @@ uint16 GetHouseCallback(CallbackID callb
 

	
 
	return group->GetCallbackResult();
 
}
 

	
 
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;
 
	if (HasBit(hs->callback_mask, CBM_HOUSE_COLOUR)) {
 
		uint16 callback = GetHouseCallback(CBID_HOUSE_COLOUR, 0, 0, house_id, Town::GetByTile(ti->tile), ti->tile);
 
		if (callback != CALLBACK_FAILED) {
 
@@ -425,12 +425,13 @@ static void DrawTileLayout(const TileInf
 
	}
 

	
 
	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) {
 
		DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, palette));
 
	}
 

	
 
	DrawNewGRFTileSeq(ti, dts, TO_HOUSES, stage, palette);
src/newgrf_industrytiles.cpp
Show inline comments
 
@@ -173,18 +173,19 @@ static void NewIndustryTileResolver(Reso
 
	const IndustryTileSpec *its = GetIndustryTileSpec(gfx);
 
	res->grffile         = (its != NULL ? its->grf_prop.grffile : NULL);
 
}
 

	
 
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
 
		 * Do not do this if the tile's WaterClass is 'land'. */
 
		if (image == SPR_FLAT_WATER_TILE && IsTileOnWater(ti->tile)) {
 
			DrawWaterClassGround(ti);
src/newgrf_object.cpp
Show inline comments
 
@@ -414,13 +414,13 @@ uint16 GetObjectCallback(CallbackID call
 
 * @param ti    Information about the tile to draw on.
 
 * @param group The group of sprites to draw.
 
 * @param spec  Object spec to draw.
 
 */
 
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;
 
	PaletteID pal  = dts->ground.pal;
 

	
 
	if (GB(image, 0, SPRITE_WIDTH) != 0) {
 
@@ -465,13 +465,13 @@ void DrawNewObjectTileInGUI(int x, int y
 
	ResolverObject object;
 
	NewObjectResolver(&object, spec, NULL, INVALID_TILE, 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)) {
 
		/* Get the colours of our company! */
 
		if (spec->flags & OBJECT_FLAG_2CC_COLOUR) {
 
			const Livery *l = Company::Get(_local_company)->livery;
src/newgrf_spritegroup.cpp
Show inline comments
 
@@ -223,6 +223,29 @@ const SpriteGroup *RandomizedSpriteGroup
 

	
 

	
 
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;
 
}
src/newgrf_spritegroup.h
Show inline comments
 
@@ -275,18 +275,23 @@ struct ResultSpriteGroup : SpriteGroup {
 
	SpriteID sprite;
 
	byte num_sprites;
 
	SpriteID GetResult() const { return this->sprite; }
 
	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 {
 
	IndustryProductionSpriteGroup() : SpriteGroup(SGT_INDUSTRY_PRODUCTION) {}
 

	
 
	uint8 version;
src/newgrf_station.cpp
Show inline comments
 
@@ -743,50 +743,71 @@ void DeallocateSpecFromStation(BaseStati
 
 * @param station station ID
 
 * @return True if the tile was drawn (allows for fallback to default graphic)
 
 */
 
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;
 

	
 
	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;
 
}
 

	
 

	
 
const StationSpec *GetStationSpec(TileIndex t)
src/sprite.cpp
Show inline comments
 
@@ -29,24 +29,35 @@
 
 * @param child_offset_is_unsigned Whether child sprite offsets are interpreted signed or unsigned
 
 */
 
void DrawCommonTileSeq(const TileInfo *ti, const DrawTileSprites *dts, TransparencyOption to, int32 orig_offset, uint32 newgrf_offset, PaletteID default_palette, bool child_offset_is_unsigned)
 
{
 
	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;
 
			AddSortableSpriteToDraw(
 
				image, pal,
 
				ti->x + dtss->delta_x, ti->y + dtss->delta_y,
 
@@ -83,21 +94,32 @@ void DrawCommonTileSeq(const TileInfo *t
 
 */
 
void DrawCommonTileSeqInGUI(int x, int y, const DrawTileSprites *dts, int32 orig_offset, uint32 newgrf_offset, PaletteID default_palette, bool child_offset_is_unsigned)
 
{
 
	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);
 
			DrawSprite(image, pal, x + pt.x, y + pt.y);
 

	
 
			const Sprite *spr = GetSprite(image & SPRITE_MASK, ST_NORMAL);
src/station_cmd.cpp
Show inline comments
 
@@ -2492,53 +2492,55 @@ const DrawTileSprites *GetStationTileLay
 
{
 
	return &_station_display_datas[st][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;
 

	
 
	if (HasStationRail(ti->tile)) {
 
		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 */
 
			st = BaseStation::GetByTile(ti->tile);
 
			statspec = st->speclist[GetCustomStationSpecIndex(ti->tile)].spec;
 

	
 
			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);
 
				}
 

	
 
				/* 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)) {
 
		StationGfx gfx = GetAirportGfx(ti->tile);
 
		if (gfx >= NEW_AIRPORTTILE_OFFSET) {
 
			const AirportTileSpec *ats = AirportTileSpec::Get(gfx);
 
@@ -2576,13 +2578,13 @@ static void DrawTile_Station(TileInfo *t
 
		palette = COMPANY_SPRITE_COLOUR(owner);
 
	} else {
 
		/* Some stations are not owner by a company, namely oil rigs */
 
		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)) {
 
		if (statspec != NULL && HasBit(statspec->flags, SSF_CUSTOM_FOUNDATIONS)) {
 
			/* Station has custom foundations.
 
			 * Check whether the foundation continues beyond the tile's upper sides. */
 
@@ -2662,35 +2664,47 @@ draw_default_foundation:
 
				DrawShoreTile(ti->tileh);
 
			} else {
 
				DrawClearLandTile(ti, 3);
 
			}
 
		}
 
	} 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)) {
 
			SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
 
			DrawGroundSprite(SPR_FLAT_GRASS_TILE, PAL_NONE);
 
			DrawGroundSprite(ground + (image == SPR_RAIL_TRACK_X ? RTO_X : RTO_Y), PAL_NONE);
 

	
 
			if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationReservation(ti->tile)) {
 
				SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
 
				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 */
 
			if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasStationRail(ti->tile) && HasStationReservation(ti->tile)) {
 
				const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
 
				DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
0 comments (0 inline, 0 general)