NML:Spritelayout
Vehicles, Stations, Canals, Bridges, Towns, Houses, Industries (Tiles), Cargos, Airports+Tiles, Objects, Railtypes, Roadtypes, Tramtypes, Terrain
Stations, houses, industry tiles, objects, and airport tiles use spritelayouts to define layouts how sprites are supposed to be arranged on a tile. A spritelayout
can combine multiple sprites into one entity, which represents everything that is to be drawn on a particular tile. A simple example to illustrate how it works:
spritelayout airport_building1 { ground { sprite: GROUNDSPRITE_NORMAL; } childsprite { sprite: spr_small_dirt_ne; // custom spriteset always_draw: 1; // also draw in transparent mode } building { sprite: 0xA67; // reuse this existing base set sprite xoffset: 0x0F; xextent: 1; zextent: 6; recolour_mode: RECOLOUR_REMAP; palette: PALETTE_USE_DEFAULT; } }
The sprite layout is composed of one or more sprites. Each sprite is defined by a ground
, building
or childsprite
block. What block to use depends on how the sprite is to be placed on the tile.
ground
sprites are drawn at the base of the tile, like grass and concrete, rails etc. in normal TTD. With a few exceptions (e.g. when using custom foundations), you should always provide a ground sprite, even when the building above fully covers it. This because in transparent mode, the building becomes invisible. A tile can only have one ground sprite.building
sprites are drawn on top of the ground sprite. To determine their location and drawing order (what goes in front of what), they have a 3D bounding box.- With
childsprite
(s), you can effectively compose sprites from multiple parts. When placing these after a building sprite, they will use the same bounding box as the previous building sprite. When placing them before the first building sprite, they will have no bounding box, as if they would use the 'bounding box' of the ground tile. Multiple childsprites may be used per ground / building sprite.
The sprite order is genrally determined by the sprite sorter, that evaluates the bounding box (see below) of each sprite. Child sprites are always part of the building (or ground) sprite that precedes them. In the GUI, sprites are always drawn in the order specified in the layout, so be sure to get this correct for spritelayouts that are to be displayed there.
Per sprite, a number of parameters may be set. These are listed below. Unless otherwise indicated, each may have a value that is dependant on variables, parameters or registers. Accessed variables are always in the SELF
scope. Accessing variables inside the purchase list is not supported, make sure that layouts accessed from there do not use them. Using variables and parameters, it's possible to create wildly differing looks with few spritelayouts.
Sprite
The most important parameter is sprite
, this determines the actual sprite to be drawn. If you set this to a number, a TTD sprite will be drawn, usually from the base set. Pre-defined constants for some base set sprites are:
GROUNDSPRITE_NORMAL (a)
default terrain type for that climate (temperate, toyland: normal, arctic: without snow, tropical: rainforest).GROUNDSPRITE_DESERT (a)
desert tileGROUNDSPRITE_DESERT_1_2 (a)
transition tile between desert and grassGROUNDSPRITE_SNOW (a)
snowy ground tileGROUNDSPRITE_SNOW_1_4 (a)
1/4 snowy ground tileGROUNDSPRITE_SNOW_2_4 (a)
half-snowy ground tileGROUNDSPRITE_SNOW_3_4 (a)
3/4 snowy ground tileGROUNDSPRITE_SNOW_4_4 (a)
snowy ground tileGROUNDSPRITE_CONCRETE
concrete ground tileGROUNDSPRITE_WATER
flat water tileGROUNDSPRITE_CLEARED (a)
ground tile that has just been bulldozed (with brown colour)
(a) These constants can be used for sprites on flat ground. There are also equivalent sprites for sloped ground, however. These sprites follow the initial ground sprite, the builtin function slope_to_sprite_offset(slope)
may be used to compute the required offset. Refer to the reference on slopes or the example below.
Alternatively, you can set the value to (the name of) a spriteset, to provide a custom sprite. When the value is just a spriteset identifier, a sprite from that set will be selected depending on the construction stage, see below. All spritesets used in a layout must have the same number of sprites, due to a restriction in the NFO format. If you supply an argument (e.g. sprite: some_sprite_set(2);
), the corresponding sprite from the sprite set will be used, with an argument of 0 corresponding to the first sprite. Note that the argument may also be variable, so you could e.g. use sprite: some_set(construction_state);
to use the construction state to select the sprite (as is the default). If the spriteset has labels defined, you can use these labels in the argument expression.
- For stations, objects and airport tiles, there are no construction stages, so the first sprite from the set is used always.
- For houses and industry tiles, sprite chosen depends on the construction stage and the number of sprites available.
- If there is only one sprite in the set, it is used always.
- If there are two sprites, one is used during construction (stages 0-2) and one for the finished building (stage 3)
- If there are three sprites, one is used for the beginning of construction (stage 0), one for the other construction stages (stages 1-2) and one for the finished building (stage 3).
- If there are four sprites, one is used for each construction stage.
- Sprites after the first four are always ignored.
Recolouring
Next, you have the option to apply recolouring, i.e. to change the colours of the sprite. This is done via the following attributes:
recolour_mode
: This must be compile-time constant. Available recolour modes are:RECOLOUR_NONE
: Use no colour translation (default)RECOLOUR_REMAP
: Use a colour translation table as defined bypalette
. This tables maps all colours of the sprite to a new colour.RECOLOUR_TRANSPARENT
: Draw the sprite in transparant mode, using a colour translation table as defined bypalette
. Normally the palette will be set to PALETTE_TO_TRANSPARANT to draw all underlying colours somewhat darker. Note that the selected palette is applied to the colours of the underlying sprite, whatever that happens to be. The supplied sprite is only used to determine what pixels to recolour.
palette
: This defines the palette which is used for the colour translation. It may only (and must!) be set whenrecolour_mode
is set toRECOLOUR_REMAP
orRECOLOUR_TRANSPARENT
. The available values are the same as forsprite
, i.e. you can use either a default sprite or a sprite from a sprite set. In this case, however, the referenced sprite must not be a real sprite, but a recolour sprite. For available default recolour sprites, see the appendix on available palettes.
Display yes/no
The following attributes allow configuring whether the sprite will be displayed or not.
hide_sprite
: If set to 1, this sprite will not be drawn at all. Default is 0. If a building sprite is not drawn, all child sprites that share its bounding box are not drawn either. Setting this to a constant value makes little sense, but you can use this to enable/disable drawing certain sprites at runtime depending on certain conditions.always_draw
: This must be a compile-time-constant. If set to 1, this sprite will also be drawn when the user has enabled transparant mode. The default value is 0. This is not available for ground sprites (those are drawn always), but it is for child sprites that share their bounding box with the ground sprite.
Positioning
How to position the sprite depends on the type of sprite:
ground
: Ground sprites cannot be positioned.building
: A three-dimensional bounding box may be defined. The X-axis runs from top-right to bottom-left and the Y-axis from top-left to bottom-right. The Z axis is vertical. All units are relative to the tile, with a tile being 16 units in the X and Y directions. Extending the bounding box over the edges of a tile is possible, but not recommended as it may lead to glitches. To define the bounding box, the following attributes can be used:xoffset
: Offset from the northwestern edge to the start of the bounding box (X).yoffset
: Offset from the northeastern edge to the start of the bounding box (Y).zoffset
: Offset from the lowest tile corner (with foundation added) to the start of the bounding box (Z).xextent
: Size of the bounding box, in the X-direction. Must be a compile-time constant.yextent
: Size of the bounding box, in the Y-direction. Must be a compile-time constant.zextent
: Size of the bounding box, in the Z-direction. Must be a compile-time constant. Default values are 0, 0, 0, 16, 16, 16, respectively. In NewGRF developer mode in OpenTTD, it's possible to view the bounding boxes of all sprites by pressing Ctrl+B.
childsprite
: Child sprites may be positioned relative to their 'parent' sprite that defines the bounding box.xoffset
andyoffset
may be set to specify an offset in pixels between the origin of the parent and child sprite. When the parent sprite is the ground sprite, TTDPatch does not support offsets other than 0,0. Note that all child sprites should fit inside the bounding box of the parent sprite to avoid visual glitches.
Example (advanced) Spritelayout
OpenTTD 1.2 (r22723) allows for nice shorthands in defining multiple views, e.g. for different slopes: Spritelayout can have parameters and may use variables and temporary storage inside of a layout. A common usage for such parametrized spritelayout is taking care of the tile slope and ground type as illustrated in this example:
spritelayout company_land_layout { ground { // normal ground sprite - always draw sprite: LOAD_TEMP(0) + LOAD_TEMP(1); } childsprite { // company-coloured border - always draw sprite: cc_frame(LOAD_TEMP(0)); always_draw: 1; recolour_mode: RECOLOUR_REMAP; palette: PALETTE_USE_DEFAULT; } childsprite { // again the normal ground sprite. Thus in non-transparent view // only the normal ground sprite is shown. In transparent view // this acts as sprite which darkens the other two sprites via // a translation to transparency. sprite: LOAD_TEMP(0) + LOAD_TEMP(1); } } // A pseudo-switch which sets the temporary parameters for the sprite layout, storing the sprite number // which belongs to the terrain type and the corresponding offset due to the tile slope switch (FEAT_OBJECTS, SELF, company_land_terrain_switch, [ // We store the offset into the spriteset due to the tile slope into the 1st temporary variable STORE_TEMP(slope_to_sprite_offset(tile_slope), 0), // We store the offset to the flat groundsprite we use into the 2nd temporary variable STORE_TEMP(GROUNDSPRITE_NORMAL, 1), STORE_TEMP(terrain_type == TILETYPE_DESERT ? GROUNDSPRITE_DESERT : LOAD_TEMP(1), 1), STORE_TEMP(terrain_type == TILETYPE_SNOW ? GROUNDSPRITE_SNOW : LOAD_TEMP(1), 1), 1 ]) { company_land_layout; }