In m4nfo, industry tiles are using four types of functions:
Functions for sprite layout definition
In m4nfo, two slightly different formats for sprite layouts are provided. First one is a simple layout for just one industry tile sprite:
The second format allows to combine more than one sprite per tile, as well as the inclusion of 'child sprites', i.e. those sharing their 3D-bounding boxes with the prior sprite:
def(16) spriteset(NOSPRITE, normal(0), xy(0,0), dxdydz(16,16,16))
This sprite layout uses no ground sprite, uses the first sprite from the coresponding sprite block as its building sprite, and its bounding box covers the whole tile (16 * 16) and has a height of 16 pixels.
def(22) spriteset( set(normal(WATER), normal(1), xyz(0,0,0), dxdydz(16,16,6)) set(normal(MOLESTAIRS), xyoff(19,6)) )
This sprite layout uses the original water tile as its ground sprite, uses the second sprite from the coresponding sprite block as its building sprite, and its bounding box covers the whole tile (16 * 16) with a height of 6 pixels. There's a child sprite sharing the parent's bounding box with its origin at coordinate x=19 and y=6 with the parent's graphics sprites.
def(50) spriteset( set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) set(normal(LIGREEN5), xyz(1,5,6), dxdydz(8,8,16)) )
This sprite layout uses the original water tile as its ground sprite, uses the third sprite from the coresponding sprite block as its building sprite, and its bounding box covers the whole tile (16 * 16) with a height of 6 px. There's another sprite located at coordinates x=1 and y=5, starting at a height of 6px with its own bounding box of 8 * 8 px and a height of 16 px.
def(51) spriteset( set(normal(WATER), normal(2), xyz(0,0,0), dxdydz(16,16,6)) set(normal(LIGREEN5), xyz(1,5,6), dxdydz(8,8,16)) set(normal(ANIMGREEN), xyoff(4,2)) )
This sprite layout is in principal the same, except of an additional child sprite added to its second sprite. The graphics associated with this child sprite is located at x=4 and y=2 in relation with its parent sprite.
The extended format also allows the use of multiple ground sprites for a tile, which is helpful if you want to use the usual TTD (or custom) terrain ground tiles, but still need to add features to it without using a new bounding box. To do so use the syntax of sprites sharing the previous bounding box, but use it before the first bounding box definition:
def(28) spriteset( set(normal(COASTYV), normal(SHD+75), xyoff(0,4)) // 2nd groundtile! set(normal(6), xyz(0,0,0), dxdydz(16,16,6)) // quay (1st bb definition) set(normal(11), xyoff(20,10)) // warning stripes for quay set(normal(SHD+49), xyz(0,0,1),dxdydz(8,8,16)) // shed extension )
Please note that x- and y pixel offsets refer to different spots of the "parent" sprite when being used for ground or building tiles. See picture.
Functions for industry tiles performance
These functions are used to evaluate game-intrinsic variables, and make them accessible to the industry tile's activation function. A typical application would use a multitude of these functions linked together to form a "chain", connecting the graphics' sprites of an industry tile with its activation function. See here for an example.
| Function | Description |
| anim_frame([<Coordinate>,] <block>) | Get current animation frame |
| callback(<block>) | Check incidence (and type) of callback |
| else | This is really a void statement |
| constructionstage(<block>) | Get construction stage (0 .. 4) |
| height(<Coordinate>, <block>) | Get height of lowest corner of tile |
| ismember(<Coordinate>, <block>) | Whether given tile belongs to current industry |
| lclass(<Coordinate>, <block>) | Get landscape class of tile |
| slope(<Coordinate>, <block>) | Get slope info of given tile |
| terrain(<Coordinate>, <block>) | Get terrain type |
| townzone(<Coordinate>, <block>) | Get town zone |
| type(<Coordinate>, <block>) | Get industry tile ID at offset |
| water(<Coordinate>, <block>) | Returns 1 if flat water, else 0 |
| waterclass(<Coordinate>, <block>) | Returns "class" of water |
| position(<block>) | Check position of tile in industry |
| randombits(<block>) | Get random bits at current tile when in callback |
| testposition(<block>) | Get position in layout for CB_SLOPE |
| testslope(<block>) | Slope check for CB_SLOPE |
| randomrel/randomabs(<trigger>, <randombit> <List::ref()>) | Get random reference |
| Auxiliary functions | |
| nibble(<Coordinate>) | Get position information from x/y coordinates |
| pos(<Coordinate>) | Construct position information from x/y coordinate |
anim_frame([<Coordinate>,] <block>)
This function returns the current animation frame being displayed. The return value is between zero and the number of animation frames specified in the industry tile's definition property anim_info(). Please note that the flag HASANIMATION in defineindustrytile() must be set for this to work.
Enabling animation on an industry tile by setting the HASANIMATION flag, property functions anim_info() and probably anim_speed() ensures that the returned value will indeed change with time, and the building is redrawn every time the animation counter changes. Please note that this kind of animation needs more CPU time and more sprites, so you should prefer palette animation if possible.
If an additional coordinate parameter is given, the actual value of the animation counter of the given neighbouring industry tile is returned. Please note that coordinates must be given by the auxiliary function pos(<x>, <y>,)
def(23) anim_frame( ref(51) if(0 .. 2, 5 .. 7) // light ref(50) else )
This function checks for a callback incidence, and if so, returns the type of the incurred callback. See example above.
This function returns the height of the lowest corner of the tile at the given coordinate. Please note that coordinates must be given using the auxiliary function pos(<x>, <y>,)
ismember(<Coordinate>, <block>)
This function returns "1" if the given tile is an industry tile, and belongs to the same industry as the current one.
This function returns the landscape class of the tile at the given coordinate:
| Value | Label | Description |
| 0 | LC_TERRAIN | bare land, grass, rocks, fields |
| 1 | LC_RAIL | railway track, with and w/o signals, fences |
| 2 | LC_ROAD | road, level crossings, depots |
| 3 | LC_HOUSE | town buildings |
| 4 | LC_TREES | climate-dependent trees |
| 5 | LC_STATIONTILE | railway station, airport, lorry and bus station, ship dock |
| 6 | LC_WATER | water, coast, river bank, ship depot |
| 7 | LC_VOID | invisible border at bottom edges of map |
| 8 | LC_INDUSTRYTILE | industry tile types |
| 9 | LC_TUNNELBRIDGE | railway or road tunnel, bridge |
| 10 | LC_OBJECT | transmitter, lighthouse, statue, company-owned land, headquarter |
This function returns byte-packed slope information for the given tile. Information is in relation to the lowest corner of the tile, i.e. WEST means that the western corner is above the lowest corner, etc. See example.
This figure illustrates which kind of slope belongs to which corners raised:
def(33) slope( ref(0) if(NORTH+WEST) // front x ref(1) if(NORTH+EAST) // front y ref(2) if(SOUTH+EAST) // back x ref(3) else // back y (SOUTH+WEST) )
You may as well check for so-called "steep slopes" (i.e. the corner opposite to the lowest one is two units higher) by using the value STEEP together with any three other corners, but keep in mind that not every house may be placed on a steep slope.
terrain(<Coordinate>, <block>)
This function returns the terrain type for the current tile. Values are NORMAL, DESERT, RAINFOREST, and SNOW (on or above snowline).
townzone(<Coordinate>, <block>)
This function returns the town zone value for the tile at the given coordinate in relation to the current industry tile. This is mostly useful for banks and other urban industries.
Returned values are as follows:
| Value | Meaning |
| TZ_CENTRE | Innermost zone, street lights |
| TZ_COMMERCIAL | Trees |
| TZ_RESIDENTIAL | Paved roads |
| TZ_PERIPHERY | Plain roads |
| TZ_OUTSKIRTS |
This function returns the industry tile ID from the geiven offset. Point of reference is the current tile instead of the north tile of the industry, and the offsets are considered to be signed.
This function returns a value of "1" if the tile given by <coordinate> is built on flat water, else "0" is returned. Again, the coordinate parameter needs to be given by the auxiliary function pos(<x>, <y>,)
waterclass(<Coordinate>, <block>)
This function returns the "water class" of the tile given by <coordinate>. Water class values are as follows:
| Value | Meaning | |
| 0 | WC_LAND | Undefined / land |
| 1 | WC_SEA | Sea, ocean |
| 2 | WC_CANAL | Canal |
| 3 | WC_RIVER | River |
Unlike the landscape class "water", the "water class" is not changed when a water tile is built over, e.g. by an object showing a ship. Using waterclass() might come in handy in this case.
Again, the coordinate parameter needs to be given by the auxiliary function pos(<x>, <y>)
This function returns the position of the given tile in relation to the northernmost tile of the îndustry, inside the industry's layout. The function requires to test its return value by using the auxiliary function nibble(), because its return value is a packed byte containing both coordinates. See example.
This function should be used to spare industry IDs because the same tile type can choose different representations depending on where exactly it is inside the industry layout.
def(30) position( ref(0) if(nibble(0,0)) // upper part ref(1) else // lower part )
This function evaluates the current tile's position in association with a "land slope check" callback (CB_SLOPE). Returned position values are byte-packed and have to be evaluated by the auxiliary function nibble(). The associated callback (CB_SLOPE) is executed just before house construction, and it does not require being set via the house's property function callback(). Same as for function testslope().
def(47) testposition( ref(45) if(nibble(0,1)) // front should be sloped ref(46) else // back should be flat )
This function evaluates the current tile's slope in association with a "land slope check" callback (CB_SLOPE). Returned slope values are the same as in objinfo_slope().
def(45) testslope( ref(44) if(NORTH+WEST, NORTH+EAST) // in x: back + front ref(44) if(SOUTH+WEST, SOUTH+EAST) // in y: back + front DISALLOW else )
randomrel(<trigger>, <randombit>, <List::ref()>)
Unlike the performance functions above, whose results are always determined by a predictable decision, one can also use random functions to pick one of several graphics sets or callback results.
| Value | Trigger |
| PERIODIC | The tile is processed in the periodic tile processing loop. |
| SYNCHRON | Simultaneous trigger for all tiles of the industry every 256 ticks. If the industry is a primary one, output cargo is generated at the same time. |
| DELIVERY | Cargo is delivered to the industry. If the industry is a processing one, output cargo is generated at the same time. |
Setting randombit determines the first bit to be re-randomized, as well as basing the random graphics on. The total number of bits used is the 2-logarithm of the number of references used, e.g., for 16 references, 4 bits are used.
Industry tiles have 8+16 random bits. Accessed through function randomrel(), you get 8 tile-specific bits, and accessed through function randomabs(), you get 16 industry-specific bits. The triggers are the same for both.
The number of referenced sets to choose from must be a power of 2, i.e. 2, 4, 8, 16 etc.
The periodic tile processing loop constantly processes the tiles of the map, processing any given tile in every 256 ticks (approx. 3.5 TTD days). Since no "real" event happens to town buildings, you have only this opportunity to re-randomize the look of your building.
def(14) randomrel(CONSTRUCT,0,ref(1),ref(2),ref(3),ref(4))
This pair of functions is used to evaluate coordinate offsets for some of the houseinfo_*() functions. They work in a somewhat "inverse" way.
This function is used to check a returned byte-packed coordinate value in the usual form (<x>, <y>). Parameter range is [-8 ... +7], both for x- and y-offsets. For an example see here.
This function supplies the given coordinates in a byte-packed form to another function. Parameter range is [-8 ... +7], both for x- and y-coordinates. Please note that the coordinate (0,0) refers to the current tile itself. For an example see here.