This is an in-depth description of industry callbacks. More basic information about callbacks can be found here.
A 'callback' is a special action that TTDPatch 'calls' in order to modify various attributes of a feature (e.g., an industry) previously defined. Examples would be the modifications of the input or output cargo types, or the production change of an industry.
This is done by identifying the incidence and the possible type of a callback in the callback() function. Depending on the type of the callback, control is eventually handed over to a function which handles the callback, or lets it fail. In the latter case, the value previously set by the appropriate property function will be kept using.
In addition, all animation callbacks also allow to trigger sound effects by returning a sound-ID value in the high byte of the callback result. This could be achieved in m4nfo by shifting the sound-ID value 8 bits to the left and OR-ing it with the appropriate animation state value, like this: cbr(eval((<sound_ID> <<8) | <animstate>)).
Instead, one of m4nfo's helper functions could be used, supplying a second parameter (in addition to the callback result) which will be interpreted as a sound effect ID, like this: animcontrol(0, SND_HORN) if(CB_ACONTROL). The corresponding sound effect will then be played on the appropriate object tile, according to the state of the corresponding animation.
Here's a list of all available callbacks for industries:
| Callback | Description |
| CB_ARRIVE | Production callback |
| CB_BUILD | Determine whether the industry can be built |
| CB_CARGOSUBTEXT | Cargo sub-type display |
| CB_COLOUR | Decide industry colour |
| CB_EFFECTS | Control special industry effects |
| CB_FUNDTEXT | Additional text in industry fund window |
| CB_INCARGO/CB_OUTCARGO | Decide input and output cargo types |
| CB_LOCATION | Determine whether the industry can be built on given spot |
| CB_MONTHLYPROD | Monthly random production change |
| CB_OPTOUT | Opt out of accepting cargo |
| CB_PERIODIC | Production callback |
| CB_RANDOMPROD | Control random production changes |
| CB_TEXT | Show additional text in industry window |
CB_BUILD - determine whether the industry can be built
Called when TTDPatch needs to know if a given industry type is available.
Return zero to make the industry type available, or any nonzero value to disable it. This callback is intended for limiting the industry type as a whole, so you can't access any industry-specific variables, just the global ones. A good example for the use of this callback is a nuclear power plant that isn't allowed to appear before 1970.
During this callback, function status() may return the following values:
| Value | Meaning | 0 | TTD is generating a map and needs to know if your type can appear. | 1 | TTD decided to build a new random industry during regular gameplay and needs to know if it can choose your type. | 2 | The user tries to build/prospect for your industry via the new industry window. TTD needs to know if the player is allowed to do this. |
CB_CARGOSUBTEXT - cargo sub-type display
This callback allows to display some text after the cargo name in the industry fund window and in industry windows. The return value must use the function grftext() to reference a previously declared text string set by defgrftext() in the same newGRF file. Returning 0xFF causes no text to be displayed.
During the callback, function cargosubtext() returns following values:
| Value | Label | Meaning |
| 0 | INCARGO1 | Return subtext for first accepted cargo type |
| 1 | INCARGO2 | Return subtext for second accepted cargo type |
| 2 | INCARGO3 | Return subtext for third accepted cargo type |
| 3 | OUTCARGO1 | Return subtext for first produced cargo type |
| 4 | OUTCARGO2 | Return subtext for second produced cargo type |
In addition, function cargosubtextdisplay() returns following values:
| Value | Label | Meaning |
| 0 | FUNDWINDOW | The text is to be displayed in the industry fund window. The industry isn't built yet, so you can't access the industry performance functions |
| 1 | INDUSTRYWINDOW | The text is to be displayed in the window of the industry. You can use the industry performance functions here. |
| 2 | DIRWINDOW | The text is to be displayed in the industry directory window. You can use the industry performance functions here. |
Since OpenTTD r17802, the contents of registers 0x100 .. 0x105 are copied onto the text reference stack.
CB_COLOUR - decide industry colour
This callback is called when the industry is being constructed, to override the selected colour() of the industry. This colour will be used for tile sprites that request recolouring, but don't supply a recolour sprite number. Since neither the industry nor its industry tiles are placed yet, almost none of the industry- or industrytile-specific variables are available, except colour() and owner().
Function owner() will return the number of the funder company (or 0x10 if the industry wasn't funded), and function colour() will return the number of the colour scheme randomly picked by TTD (a number between 0 and 15). If you don't want to change the colour picked by default, you can either make the callback fail or just supply the return value of colour() unchanged.
Other helpful functions to be used might be playerinfo() to get the company colour of the funder (if there's any), or a random function to select a random colour scheme from a given list, or of course decide on global variables like position or game year.
CB_EFFECTS - control special industry effects
This callback allows to control some aspects of the special effects enabled in property function flags(). Function effect() always contains the number of the special effect, derived from those being set in property function flags(). Currently only effects PLANTFIELDS and TREECUTTING are supported.
For effect PLANTFIELDS you should return zero to avoid planting fields and any other value to plant a field. The callback is called every 256 ticks for a given industry. Function randombits() returns 32 random bits that can be used to randomize the behaviour. Industry performance function prodcounter() can be used to make the plantings rarer, but please note that the low byte of its return value is always zero at this point. TTD's default behaviour is giving 1/8 chance to plant a new field every 256 ticks.
For effect TREECUTTING you should return zero to avoid cutting a tree and any other value to try cutting one. The callback is called every 256 ticks for a given industry. You don't get random values in this case, but you can still use function prodcounter(), but again the low byte of its return value is always zero here as well. TTD's default behaviour is trying to cut a tree every 512 ticks.
CB_FUNDTEXT - additional text in industry fund window
This callback allows to display extra information in the industry fund window. The return value must use the function grftext() to reference a previously declared text string set by defgrftext() in the same newGRF file. The text must begin with a colouring special character and should not be longer than three lines (automatic line breaks are provided, but you can use CRLF for a manual line break). Returning 0xFF causes no text to be displayed.
Since the industry isn't built yet, you can't access any industry variables during this callback.
Since TTDPatch r2354 and OpenTTD r20086, the contents of registers 0x100 .. 0x105 are copied onto the text reference stack.
CB_INCARGO/CB_OUTCARGO - decide input and output cargo types
These callbacks are called when the industry is built, and allow customizing the input and output cargo types dynamically. Both callbacks are called repeatedly, with the lowest byte of [variable 10] starting from zero and increasing after every call; you should return a cargo type each time, or 0xFF to terminate the list (a failed callback terminates the list, too). The same limitations apply as for callback CB_COLOUR: industry tiles aren't yet placed, and most industry variables are undefined. However, you can use random functions. The interpretation of the callback return value depends on two factors: the current newGRF version number and the presence of a cargo translation table:
| GRF version | Has CTT | Interpretation | <= 6 | Climate-dependent cargo slot number | >= 7 | No | Cargo bit | >= 7 | Yes | Index in the translation table |
Although currently callback CB_INCARGO is called no more than three times, and callback CB_OUTCARGO no more than twice, this may change between versions/implementations, to allow more input/output types. To be safe, you should return 0xFF as the last element even when you use all three input types or both output types.
CB_LOCATION - determine whether the industry can be built on given spot
Called to decide if the industry can be built on a given spot. Since the industry isn't built yet, you can only use a subset of TTD's variables: you can access the data of the nearest town without any problems, as well as functions lclass(), height(), water(), terrain(), waterclass(), slope(), distindustry(), disttown(), townzone(), industrycount(), closest(), and an additional number of special functions:
| Variable | Size | Meaning |
| _position() | Word | Coordinates of the selected position |
| _layoutnum() | Byte | Number of the selected layout, according to property 0A |
| _terrain() | Byte | Ground type of the selected spot (see terrain() for details) |
| _townzone() | Byte | Town zone of the selected spot (see townzone() for details) |
| _disttown() | Byte | Distance between the closest town and the selected position. |
| _height | Byte | Height of the lowest corner of the tile (between 0x00 and 0x80, one land height unit equals 8 units) |
| _distwater() | Word | Distance to the closest water tile if ONWATER in flags() is not set (i.e., built on land); distance to the closest empty dry land tile if ONWATER is set (built on water) |
| _disteuclid() | Word | Square of the Euclidean distance between the closest town and the selected position |
| _randombits() | Dword | 32 random bits (since r1816 and OpenTTD r11985) |
Unless explicitly noted, distances are Manhattan distance, not Euclidean distance, ie. |x-x0|+|y-y0| instead of sqrt((x-x0)^2+(y-y0)^2).
Function _distwater() will always return values below 0x80 if property function flags() does not set ONWATER. If ONWATER is set, _distwater() will return a max value of 0x200 in the pathological case of an all-water map.
During this callback, function status() may return the following values:
| Value | Meaning | 0 | TTD is generating a map. | 1 | TTD decided to build a new random industry during regular gameplay. | 2 | The user tries to build your industry via the new industry window. | 3 | The user tries to prospect for your industry via the new industry window. |
This callback must return a 15-bit value, which is interpreted as follows:
| Value | Meaning | 0000-03ff | Industry can't be built, display the misc. GRF text Dxxx as error message | 0400 | Industry can be built | 0401 | Industry can't be built, display the default "site unsuitable" error message. | 0402 | Industry can't be built, display the "...can only be built in rainforest areas" error message. | 0403 | Industry can't be built, display the "...can only be built in desert areas" error message. |
Since TTDPatch r1755, you can use the text reference stack for your error messages, similarly to callback CB_TEXT. The only difference is that only 4 registers are copied instead of 6 because error messages support 20 bytes of reference stack only, and two of those bytes may be used by TTD.
CB_MONTHLYPROD - monthly random production change
Works exactly the same way as callback CB_RANDOMPROD, except that it is called every month, allowing more frequent production changes.
CB_OPTOUT - opt out of accepting cargo
Using this callback, the industry can refuse accepting a cargo type even if it has been defined as one of its input cargo types. If there is another industry nearby that accepts this cargo type, that one will get it.
The lowest byte of var 18 contains the ID of the cargo delivered. If the newGRF has a cargo translation table installed, it will get the index from that table; otherwise, it will get the cargo bit associated with the cargo type. You must return zero if you don't want to accept the cargo, or 1 to accept it. Other return values are reserved for future use.
This callback should be used in conjunction with callback CB_CARGOTYPES for industry tiles, since the acceptance of the tiles and the acceptance of the industry itself should always agree. If you disable accepting a cargo via callback CB_OPTOUT, but forget to remove the acceptance from the tiles, the station will keep accepting the cargo and the player will keep getting the money, but the industry won't receive the cargo. On the other hand, if you remove acceptance from the tiles, but forget using callback CB_OPTOUT, the industry may still get the cargo. (For example, there may be another industry nearby whose tiles accept the cargo. This makes the station accept the cargo, and send it to the nearest industry, which happens to be yours, even though its tiles don't accept the cargo.)
CB_RANDOMPROD - Control random production changes
Called when TTD chooses the industry for a random production change. [Variable 18] Function randombits() returns 32 random bits that can be used to randomize the behaviour. The callback must return one of the following results:
| Value | Meaning |
| 0 | Do nothing |
| 1 | Halve industry production. If production goes below the quarter of the default, the industry is closed instead, as if you returned 03. |
| 2 | Double industry production if it hasn't reached eight times of the original yet. |
| 3 | The industry announces imminent closure, and is physically removed from the map next month. |
| 4 | Do the standard random production change as if this industry was a primary one. |
| 5 | Divide production by 4 |
| 6 | Divide production by 8 |
| 7 | Divide production by 16 |
| 8 | Divide production by 32 |
| 9 | Multiply production by 4 |
| 10 | Multiply production by 8 |
| 11 | Multiply production by 16 |
| 12 | Multiply production by 32 |
| 13 | Decrement production by 1 (2.6 r2046, OTTD r11532) |
| 14 | Increment production by 1 (2.6 r2046, OTTD r11532) |
| 15 | Set production as returned in bits 16 .. 23 of register 0x100 (clamped to 4 .. 128) (2.6 r2068, OTTD r14561) |
Since TTDPatch 2.5 beta 1, you can set bit 7 of this value to suppress the news message announcing the production change. Be careful with this, especially for closedown messages; most players wouldn't like those being suppressed.
For return values 5 .. 12, the double/halve news messages are displayed. You should probably replace the defaults with some text that doesn't explicitly say "double" or "halve", or use the message override feature (see below). If a divide operation brings the production below the quarter of the default, the industry is closed down instead. Multiply operations that would bring the production above 8 times of the original will only increase it to that value.
Since r1306, you can override the default news messages that appear after production changes. To do this, you must set bit 8 of the returned result, and put the textID into the low word of GRF register 0x100 (the high word of the register is ignored). The textID can be of a default TTD text or a DCxx text; it cannot be a D0xx text. If the given action generates a message, it will be the one you specified. It may, however, not generate messages at all; for example, operation 4 may decide not to change the production, and then your message won't be shown. There is one exception: operation 0 would never generate a message, but it still will when bit 8 is set. This is useful in conjunction with callback CB_MONTHLYPROD, to tell the player about things that don't change the production multiplier, but are still important enough to require a news message.
By using return values 13 .. 15 you can adjust production rates more smoothly. The production rate is a value between 4 and 128 (initial value 16). Returning 13 when the production is at 4 will close the industry. But returning 15 will never close the industry, instead the value is clamped to the allowed range. Note that OTTD before r15103 did not properly updated production rates provided by property function productionmultipliers().
CB_TEXT - Show additional text in industry window
This callback allows to display extra information in the industry window about the state of the industry. The return value should be the number of a D0xx text to be displayed. The text must begin with a colouring special character and should not be longer than one line. For example, you can use this callback to display the current production limit of a secondary industry.
Note that since r11987, OpenTTD allows resizing the window when this callback is enabled. It allows to specify a text containing more than one line.
Since TTDPatch 2.6 r1370, the contents of registers 0x100 .. 0x105 are copied onto the text reference stack. This allows to display dynamically calculated values in the text by using the according StringCodes. Please note, though, that the text handler will see it as an array of bytes, not as an array of DWORDs, so you will have to pack two words into a register when you use codes that read words from the stack. In the case of \7D (which gets a byte), you may have to pack more into a register, but it's probably easier to use \7C instead. Please also note that you can use \80 to display textIDs calculated on the fly, but DCxx textIDs won't work correctly. When you need to choose from your own texts dynamically, you must use D0xx IDs, and add 400h to them. (That is, use D400 to refer to D000, D401 for D001 etc.)