In m4nfo, real sprites have to be 'grouped' in one way or the other to be accessible by the newGRF's code. The way how to group sprites is dependent from the feature in use. In any way, there are only three functions dealing with sprites: sprite(), which defines a single real sprite, set(), responsible for the grouping of a fixed number of real (or recolour) sprites, and spriteblock(), which makes grouped sprites available for feature functions.
A sprite grouping has the following format:
Syntax and usage of the sprite() function has been explained on its own page.
set([<String>,] {sprite(<sprite_attributes>)})
The set() function is used to define a set of sprite() functions. The number of real sprites to use depends on the number of different 'views' of the feature in question.
For vehicles, this number corresponds to the different directions it can be in. If a vehicle is symmetrical there are 4 views, and if it isn't symmetrical it has to use 8 views. Note that shortened vehicles are always regarded as unsymmetrical, even if their graphical representations would look 'symmetrical'.
Stations are usually made out of 18 sprites, although this number may vary, depending on the sprite layout used.
For canals and rivers, it depends on the canal feature.
For houses and industry tiles, the number of views should be between 1 and 4 and specifies the number of different construction stages.
For bridges, cargoes, objects and recolour tables, this number should always be 1.
set( sprite(voyageurs_courte.pcx 260 10 01 11 17 -16 -7) )Example 2 (train, set of 4 real sprites):
set( sprite(voyageurs.pcx 10 550 01 18 6 -2 -11) sprite(voyageurs.pcx 26 550 09 14 16 -11 -7) sprite(voyageurs.pcx 58 550 01 11 24 -7 -7) sprite(voyageurs.pcx 100 550 09 14 16 -3 -7) )Example 3 (train, set of 8 real sprites):
set( sprite(messagerie.pcx 10 10 01 12 6 -2 -11) sprite(messagerie.pcx 26 10 09 11 10 -5 -7) sprite(messagerie.pcx 58 10 01 11 13 4 -7) sprite(messagerie.pcx 100 10 09 11 10 3 -4) sprite(messagerie.pcx 128 10 01 12 6 -2 -1) sprite(messagerie.pcx 144 10 09 11 10 -11 -4) sprite(messagerie.pcx 176 10 01 11 13 -16 -7) sprite(messagerie.pcx 218 10 09 11 10 -3 -7) )
Sets may be 'labeled' for easy access by function spriteset(). Labels are local with regard to the spriteblock() they're being used. They can be overridden by quoting them, but the only way to get rid of a label is by use of function undefine(<set-name>).
spriteblock( set( // empty sprite(db#slow.pcx 66 8 01 16 8 -3 -8) ... sprite(db#slow.pcx 258 8 09 12 17 -3 -5) ) set( // 1/4 sprite(db#slow.pcx 66 30 01 16 8 -3 -8) ... sprite(db#slow.pcx 258 30 09 12 17 -3 -5) ) set( // 1/2 sprite(db#slow.pcx 66 50 01 16 8 -3 -8) ... sprite(db#slow.pcx 258 50 09 12 17 -3 -5) ) set( // 3/4 sprite(db#slow.pcx 66 70 01 16 8 -3 -8) ... sprite(db#slow.pcx 258 70 09 12 17 -3 -5) ) set( // 4/4 sprite(db#slow.pcx 66 90 01 16 8 -3 -8) ... sprite(db#slow.pcx 258 90 09 12 17 -3 -5) ) set(__tarp_blue, // Plane blau sprite(db#slow.pcx 66 110 01 16 8 -3 -8) ... sprite(db#slow.pcx 258 110 09 12 17 -3 -5) ) set(__tarp_grey, // Plane grau sprite(db#slow.pcx 66 130 01 16 8 -3 -8) ... sprite(db#slow.pcx 258 130 09 12 17 -3 -5) ) ) // open def(0) spriteset(move(0, 1, 2, 3, 4), load(0, 1, 2, 3, 4)) // tarpaulin def(1) spriteset(move(0, 1, 2, 3, 4, __tarp_blue), load(0, 1, 2, 3, 4, __tarp_blue)) def(1) spriteset(move(0, 1, 2, 3, 4, __tarp_grey), load(0, 1, 2, 3, 4, __tarp_grey))
Note that use of labels in extended format spriteblocks needs special handling with regards to the 'base sprite' given. This can be achieved by use of function setbaselabel() where the parameter must agree with the base sprite given for the extended format spriteblock following.
spriteblock([parameter,[<parameter>,]]{set({sprite(<sprite_attributes>)})})
This function is used to group sets of sprites and make them available for the chain of functions decribing the flow of control for a feature. Because sprite layouts are different for each feature, the special function spriteset() is used to set up the needed sprite layout from spriteblock().
The spriteblock() function acts as a wrapper for quite different types of sprites.
Depending on the type of sprites, spriteblock() might get supplied a special parameter:
Type of sprites | Parameter |
real sprites | none |
real sprites[*] | see below |
special real sprites | see below |
replaced TTD sprites | sprite-ID |
user-supplied real or recolour sprites | ALLOCATE |
[*] (OpenTTD extended format, since r22925)
Note that in case of replacing original TTD sprites, there may be only one set() in the spriteblock(), and the spriteblock()'s parameter must be set to the sprite-ID of the first original TTD sprite to replace. When replacing more than one TTD sprite, these must be a contiguous block of max 255 sprites.
In case of allocated real or recolour sprites, there may be also only one set() in the spriteblock() and up to 32,640 real or recolour sprites. See chapter about recolouring for in-depth documentation.
// 120T menu entry spriteblock( set( sprite(120T.pcx 260 10 01 11 16 -15 -7) // black ) )Example 2 (replacing original TTD sprites):
spriteblock(0xFE6, set( sprite(v_dock.pcx 2 10 09 36 34 -32 -20) sprite(v_dock.pcx 40 10 09 36 34 0 -20) sprite(v_dock.pcx 80 10 09 59 33 -30 -42) sprite(v_dock.pcx 120 10 09 59 33 -1 -42) sprite(v_dock.pcx 160 10 09 52 67 -31 -36) sprite(v_dock.pcx 230 10 09 52 67 -34 -36) ) )Example 3 (recolour sprites):
spriteblock(ALLOCATE, set( // +00 coal -> 02 03 04 05 06 // dark blue colourtable(DOSMAP, 63 .. 67, 02 .. 06 ) // pale green colourtable(DOSMAP, 63 .. 67, 02 .. 06, c6 .. cd, 60 .. 67 ) ... ) )
This format is only available in OpenTTD >r22925. It both allows to use more than 255 sprite sets, and to address sprite sets defined in another spriteblock() function.
Parameter is a plus sign, followed by a number specifying the first set's ID to define. That way individual sprite sets can be used concurrently and be redefined individually. I.e., if one spriteblock() defines sprite sets 0 .. 4, and later a second spriteblock() function redefines sprite set 0, sprite sets 1 .. 4 still refer to the first spriteblock().
spriteblock(+15, set(template({NG_6M},voyageurs_courte.pcx,x(LAYOUT_SYMMETRIC),y(40))) set(template({NG_6M},voyageurs_courte.pcx,x(LAYOUT_SYMMETRIC),y(70))) set(template({NG_6M},voyageurs_courte.pcx,x(LAYOUT_SYMMETRIC),y(100))) ... )
There are no restrictions on the order the sprite sets are defined. If you define sprite sets 23 .. 25 in one spriteblock(), and define sprite sets 0 .. 5 in another spriteblock(), then a spriteset() function can access both sets 0 .. 5 and 23 .. 25 individually. Later, another spriteblock() can redefine sprite sets 2 .. 3, and following spriteset() functions could access sprite sets 0 .. 1, 2 .. 3, 4, 23 .. 25 from various spriteblock() functions.
Note that all spritesets defined in a single spriteblock() function must have the same amount of sprites per sprite set. Using multiple spriteblock() functions you can define sprite sets with different amounts of sprites per set. Function spriteset() will individually use the size of each referenced sprite set. There is no requirement for consistent numbers of sprites in spriteset().
Both TTDPatch and OpenTTD are using pre-defined additional sprites ('base sprites') for certain new features, like additional signals, catenary, tram tracks, GUI sprites, etc.
Defining graphics for these new features requires the appropriate parameter in function spriteblock(), and to provide the correct number of real sprites for this feature in the spriteblock. Since version 1.2, OpenTTD supports partial definition of base set sprites by adding an offset parameter (*) for several feature types (see table and example).
ID | Parameter | Feature | num sprites |
04 | *SIGNALS | Pre-signal, semaphore, and PBS graphics | 240 |
05 | *CATENARY | Overhead wires and pylon graphics | 48 |
06 | *FOUNDATIONS | Foundations (retaining walls) for BuildOnSlopes, and 'half-tiles' (OTTD) | 74/90 |
07 | TTDPATCHGUI | TTDPatch GUI sprites | 93 |
09 | *ONEWAYMARKS | One-way road arrows | 6 |
0A | *TWOCCMAPS | Two company color translation maps | 256 |
0B | *TRAMTRACKS | Tram tracks | 113 |
0C | SNOWYTREES | Snowy temperate trees | 133 |
0D | COASTTILES | Coast tile graphics | 16/18 |
0E | NEWSIGNALS | New Signals | any |
0F | *TRACKRESERVE | Sprites for marking tracks on slopes (for track reservation systems like PBS or YAPP) | 12 |
10 | *AIRPORTGRAPHICS | Additional airport graphics (OTTD) | 15 |
11 | *ROADSTOPS | Road stop graphics | 8 |
12 | *AQUAEDUCTS | Aqueduct graphics | 8 |
13 | *AUTORAIL | Autorail sprites (OTTD) | 55 |
14 | *FLAGSPRITES | Flag sprites (OTTD) | up to 36 |
15 | *OTTDGUI | OpenTTD GUI sprites | up to 160 |
16 | *AIRPORTPREVIEWS | Airport preview sprites (OTTD) | up to 9 |
17 | *TUNNELBASE | Railtype tunnel base sprites (OTTD) (see also) | 16 |
18 | *EXTRABLACK | Extra all black palette (OTTD, 'more height levels') | 1 |
For an in-depth analysis of each feature see the appropriate chapter in the nfospecs.
spriteblock(SIGNALS, set( // light signals - pre-signals sprite(psignals2.pcx 66 8 01 21 6 -2 -19) sprite(psignals2.pcx 82 8 01 21 6 -2 -19) sprite(psignals2.pcx 98 8 01 21 6 -2 -19) sprite(psignals2.pcx 114 8 01 21 6 -2 -19) ... ) )Example 2 (defining base sprites using offsets):
spriteblock(TUNNELBASE, 6, set( sprite(xtunnel.pcx 10 10 09 23 33 -26 0) sprite(xtunnel.pcx 45 10 09 23 33 -5 0) ) )