From GRFSpecs
Jump to navigationJump to search
Block Syntax

NML 0.5 syntax

NML 0.5 Supported by OpenTTD 1.91.9 The produce block is only available for industries and describes how the industry consumes incoming and creates outgoing cargo. The syntax can be one of the following:

produce (<ID>, [ <consume_cargo>: <consume_amount>; <...> ], [ <produce_cargo>: <produce_amount>; <...> ])
produce (<ID>, [ <consume_cargo>: <consume_amount>; <...> ], [ <produce_cargo>: <produce_amount>; <...> ], <run_again>)

Note that the square brackets [ ] are part of the syntax and must be typed. The semicolon ; after each pair must also be included even after the last pair in a list. See the example below.

NML 0.5 will always generate production callback version 2 in the final GRF file, this is only understood by OpenTTD 1.9 and later.


  • ID is a unique name of the block
  • consume_cargo and produce_cargo are cargolabels (unescaped/not as a string) for a cargo that will be consumed/produced from the industry during the production step.
  • consume_amount and produce_amount are expressions giving the amount of the cargo that will be consumed/produced during the production step.
  • run_again is a boolean expression which evaluates to either 0 or 1. It indicates whether the produce callback shall be called again. This is optional, with a default value of 0.

Up to 16 consume_cargo: consume_amount pairs and 16 produce_cargo: produce_amount pairs may be given, and either of the consume and produce lists may also be empty.

All cargo labels used in the produce block must appear in the industry's cargo_types array.


/* Produce GIFT from TOYS, SWTR and PAPR.
 * Amount of TOYS to consume is stored in permanent slot 4, amount of SWTR in slot 5, and amount of PAPR is sum of the two.
 * Amount of GIFT produced is the same as amount of TOYS and SWTR consumed.
 * Temporary slot 3 contains whether the entire callback chain will be repeated.

/* An empty produce statement which simply does nothing. */
produce(empty_prod, [], [])

/* When one of TOYS or SWTR is zero, determine how much of the other can be consumed.
switch(FEAT_INDUSTRIES, SELF, warehouse_prod_cb2, [
    /* Store smallest amount of TOYS or PAPR in permanent slot 4 - amount of TOYS that will be consumed */
    STORE_PERM(min(incoming_cargo_waiting("TOYS"), incoming_cargo_waiting("PAPR")), 4),
    /* Store smallest amount of SWTR or PAPR in permanent slot 5 - amount of SWTR that will be consumed */
    STORE_PERM(min(incoming_cargo_waiting("SWTR"), incoming_cargo_waiting("PAPR")), 5),
    /* There will never be more to produce after this step, both TOYS and SWTR will be zero after the produce has executed */
    STORE_TEMP(0, 3)
]) {
    /* Only one case, use the produce statement unconditionally */

/* Check if all of TOYS, SWTR, and PAPR are available.
 * If they all are, produce as much cargo as possible using all three.
switch(FEAT_INDUSTRIES, SELF, warehouse_prod_cb1, [
    /* Distributing the PAPR half for TOYS and half for SWTR, store max amount of each to consume in temporary slot 0 */
    STORE_TEMP(incoming_cargo_waiting("PAPR")/2, 0),
    /* Calculate how much TOYS+PAPR production is possible, the smallest of TOYS and half of PAPR - stored in permanent slot 4 */
    STORE_PERM(min(incoming_cargo_waiting("TOYS"), LOAD_TEMP(0)), 4),
    /* Calculate how much SWTR+PAPR production is possible, the smallest of SWTR and half of PAPR - stored in permanent slot 5 */
    STORE_PERM(min(incoming_cargo_waiting("SWTR"), LOAD_TEMP(0)), 5),
    /* There may be more to do after this production step, set temporary slot 3 to indicate this */
    STORE_TEMP(1, 3),
    /* Multiply TOYS and SWTR amounts, if either is zero then the switched value is zero, and production via both cargo types is not possible */
    incoming_cargo_waiting("TOYS") * incoming_cargo_waiting("SWTR")
]) {
    /* If either of TOYS or SWTR stored was zero, then try the just-one-of-them switch above */
    0: warehouse_prod_cb2;
    /* Otherwise both TOYS and SWTR were available and the production callback can execute */

/* Initial check for production possible, any production requires PAPR + either TOYS or SWTR.
switch(FEAT_INDUSTRIES, SELF, warehouse_prod_cb0, [
    /* Set permanent storage slots 4 and 5 (amounts of TOYS and SWTR to consume) to zero initially */
    STORE_PERM(0, 4),
    STORE_PERM(0, 5),
    /* Multiplying amount of PAPR ready by the sum of amount of TOYS and SWTR yields zero if no production would be possible */
    incoming_cargo_waiting("PAPR") * (incoming_cargo_waiting("TOYS") + incoming_cargo_waiting("SWTR"))
]) {
    /* Production not possible currently, so produce nothing */
    0: empty_prod;
    /* Production should be possible, go to the first step above */

/* Define a new industry that accepts cargo types TOYS, SWTR, PAPR, and produces cargo type GIFT.
 * It has an on-arrival production callback routing via the switches above.
item(FEAT_INDUSTRIES, warehouse) {
    property {
        substitute: INDUSTRYTYPE_TOY_SHOP;
        name: string(STR_WAREHOUSE_NAME);
        spec_flags: 0;
        life_type: IND_LIFE_TYPE_PROCESSING;
        cargo_types: [
            produce_cargo("GIFT", 0)
    graphics {
        produce_cargo_arrival: warehouse_prod_cb0;

NML 0.4 and earlier syntax

NML 0.4 The produce block is only available for industries and describes how the industry consumes incoming and creates outgoing cargo. The syntax is:

produce (<ID>, <consume_1>, <consume_2>, <consume_3>, <produce_1>, <produce_2> [, run_again]);


  • ID is a unique name of the block
  • consume_1, consume_2, consume_3 are expressions which give the amount of the respective input cargo consumed during this call of the produce block. The given amounts are subtracted from the stockpile of cargo waiting to be processed.
  • produce_1, produce_2 are expressions which give the amount of the produced (outgoing) cargos in the produce block.
  • run_again is a boolean expression which evaluates to either 0 or 1. It indicates whether the produce block shall be called again. This is optional, with a default value of 0.

Note that industry variables can be used in the produce-block, so you can for example query the amount of waiting cargo directly and use that in your expression.

The produce block should be the result of the production callback of an industry. An example on how it can be used without actually processing cargo but counting the time an industry exists unserviced:

 /* Every month, the counter is increased. The counter is reset to zero whenever the power plant
  * receives cargo. When the counter reaches 61, the industry will close when the random production
  * change callback is called. */
 /* When we receive cargo, the counter is reset. */
 produce(power_plant_cargo_arrive_produce, 0, 0, 0, 0, STORE_PERM(0, 0x00))
 /* Every month the counter is increased. */
 switch (FEAT_INDUSTRIES, SELF, power_plant_monthly_prod_change_switch, STORE_PERM(LOAD_PERM(0x00) + 1, 0x00)) {
 /* Only check industry closure if the counter is greater than 60. */
 switch (FEAT_INDUSTRIES, SELF, power_plant_random_prod_change_switch, LOAD_PERM(0x00)) {
 item(FEAT_INDUSTRIES, industry_power_plant, INDUSTRYTYPE_POWER_PLANT) {
 	graphics {
 		produce_cargo_arrival: power_plant_cargo_arrive_produce;
 		monthly_prod_change: power_plant_monthly_prod_change_switch;
 		random_prod_change: power_plant_random_prod_change_switch;

NML 0.4 and earlier will generate production callback version 0 or 1 (depending on specific parameters given) in the final GRF file.