The m4nfo User Manual and Report

Sprite grouping

Introduction

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.

Format

A sprite grouping has the following format:

spriteblock([<parameter>,[<parameter>,]]{set({sprite(<sprite_attributes>)})})
spriteblock(set({colourtable(<byte*256> | <table>,{<dest>,<source>})}))

Description

sprite(<sprite_attributes>)

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.

Example 1 (train, set of 1 real sprite for menu):
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>).

Example (using named sets):
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 spritesParameter
real spritesnone
real sprites[*] see below
special real spritessee below
replaced TTD spritessprite-ID
user-supplied real or recolour spritesALLOCATE

[*] (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.

Example 1 (user-supplied graphics' sprites):
// 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 
    )
    ...
  )
)

Extended format (OpenTTD)

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().

Example (extended format 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().

Special real sprites

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).

IDParameterFeaturenum sprites
04*SIGNALSPre-signal, semaphore, and PBS graphics240
05*CATENARYOverhead wires and pylon graphics48
06*FOUNDATIONSFoundations (retaining walls) for BuildOnSlopes, and 'half-tiles' (OTTD)74/90
07TTDPATCHGUITTDPatch GUI sprites93
09*ONEWAYMARKSOne-way road arrows6
0A*TWOCCMAPSTwo company color translation maps256
0B*TRAMTRACKSTram tracks113
0CSNOWYTREESSnowy temperate trees133
0DCOASTTILESCoast tile graphics16/18
0ENEWSIGNALSNew Signalsany
0F*TRACKRESERVESprites for marking tracks on slopes (for track reservation systems like PBS or YAPP)12
10*AIRPORTGRAPHICSAdditional airport graphics (OTTD)15
11*ROADSTOPSRoad stop graphics8
12*AQUAEDUCTSAqueduct graphics8
13*AUTORAILAutorail sprites (OTTD)55
14*FLAGSPRITESFlag sprites (OTTD)up to 36
15*OTTDGUIOpenTTD GUI spritesup to 160
16*AIRPORTPREVIEWSAirport preview sprites (OTTD)up to 9
17*TUNNELBASERailtype tunnel base sprites (OTTD) (see also)16
18*EXTRABLACKExtra all black palette (OTTD, 'more height levels')1

For an in-depth analysis of each feature see the appropriate chapter in the nfospecs.

Example 1 (defining base sprites):
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)
  )
)