NMLTutorial

From TTWiki
Revision as of 19:26, 28 August 2011 by FooBar (talk | contribs) (sweeten the difference between NFO and NML a bit more in favour of NML)
Jump to navigationJump to search

Since June 2010 a new coding language for creating TTDpatch/OpenTTD GRF's exists: the NewGRF Meta Language or NML for short. It differs significantly from the old way of writing NewGRFs in NFO and although documentation is available it may still be a challenge for people interested in using NML code. After all NML is still a type of programming language and not a GUI-based editor.

The NML tutorial aims at providing entry-level instructions and examples of how coding in NML works and hopefully inspires people who previously thought making a NewGRF themselves was impossible to give NML a whirl.


How to read the NML tutorial

The NML Tutorial starts with the installation of NML and continues from there as single "story" if you like. The bottom of each tutorial page has a link to the next page (and the previous in case you missed something) and all pages from the tutorial are also listed in navigation box at the top right of each page.

Example NML code is displayed in a code box and a blue font colour (much like the NML documentation itself). Code syntax definitions have a similar code box but are displayed in a black font. Code references in line with the text are displayed in a green font colour to make them easily distinguishable from normal text.

If you have any questions about the text and examples within the NML tutorial, you are welcome to post those in the NML Tutorial topic at TT-Forums. If you see bugs in the tutorial, you may of course discuss them there as well. Expanding or changing the tutorial should only be done by people who actually know NML and don't need this tutorial to code their NML files.


NML vs. NFO

Background

NML is a high-level NewGRF language compiler which compiles NML and it's language files into NewGRF files (and NFO files, if asked to do so). Coding a NewGRF in NML is similar to writing in any other programming language except that there are no real sub-routines; you can use the usual definition and condition statements.

NFO is a low-level language that is almost completely written in hexadecimal bytes. Coding a NewGRF in NFO is similar to thinking like a computer and an art in itself.

Language support

NML comes with language file support in a file format that is easy to use by translators that don't understand NML themselves. These language files are separated from the actual NML code and each language will have it's own language file for easy maintenance.

NFO doesn't come with language file support and translations are fully integrated in the NFO code. This makes translations hard to maintain unless you write your own language file preprocessor.

Image format support

NML supports every paletted image file supported by the Python Image Library. NFO (or rather GRFCodec) only supports PCX and PNG files. As the PNG image format is the recommended format for both methods of NewGRF creation, this can hardly be considered an advantage on the part of NML.

Graphic files used with NML or NFO code are in principle identical, so for graphic artists there is no difference when making a choice between NML or NFO.

Example

Does the above sound impressive, maybe even threatening to you? An example will surely help you make the choice between NML and NFO very quickly. Both examples below are the source code of the very same object NewGRF, on the left in NFO and on the right in NML.

Both examples are shown without any comments in the source code. In NFO you need a comment on almost every single hexadecimal byte, or at least one for every line of code, to make it human readable. NML code is largely human readable by itself. As comments are not what defines a programming language, they are left out here to show both languages in their purest form.

Surely the NML file is slightly longer[1], but what would you prefer to write yourself? Would you be willing to try and understand the code on the left? Or rather the code from the right? I thought so...


Example uncommented NFO code
// Automatically generated by GRFCODEC. Do not modify!
// (Info version 7)
// Escapes: 2+ 2- 2< 2> 2u< 2u> 2/ 2% 2u/ 2u% 2* 2& 2| 2^ 2sto = 2s 2rst = 2r 2psto 2ror = 2rot 2cmp 2ucmp 2<< 2u>> 2>>
// Escapes: 71 70 7= 7! 7< 7> 7G 7g 7gG 7GG 7gg 7c 7C
// Escapes: D= = DR D+ = DF D- = DC Du* = DM D* = DnF Du<< = DnC D<< = DO D& D| Du/ D/ Du% D%
// Format: spritenum pcxfile xpos ypos compression ysize xsize xrel yrel
    0 * 4	 1D 00 00 00
    1 * 46	 14 "CINFOBVRSN" 04 00 00 00 00 00 "BMINV" 04 00 00 00 00 00 "BNPAR" 01 00 00 "BPALS" 01 00 57 00 00
    2 * 213	 08 07 "NMLT" "NMLTutorial Object Example" 00 "Description: " 89 "Dutch Road Furniture is an eyecandy object NewGRF that features road furniture that can be found alongside Dutch roads. " 0D "(c)2011 FooBar. " 0D 98 "License: " 89 "GPLv2 or higher." 00
    3 * 5	 0D 41 \D= A1 00
    4 * 9	 0D 40 \D- 41 FF C3 58 00 12
    5 * 9	 0D 40 \Du<< 40 FF E1 FF FF FF
    6 * 9	 09 40 04 \7= 00 00 00 00 01
    7 * 19	 0B 03 7F 06 31 2E 32 2E 30 20 28 72 32 32 37 32 33 29 00
    8 * 6	 01 0F 01 FF 04 00
    9 sprites/fingerpost.png 162 8 01 32 20 -10 -28
   10 sprites/fingerpost.png 194 8 01 32 20 -10 -28
   11 sprites/fingerpost.png 226 8 01 32 20 -10 -28
   12 sprites/fingerpost.png 258 8 01 32 20 -10 -28
   13 * 24	 02 0F FE 41 00 00 00 00 02 00 81 00 00 00 80 02 00 06 0C 00 04 04 18 82
   14 * 68	 02 0F FE 89 7D 01 20 FF FF FF FF \2sto 1A 20 80 00 00 00 \2rst 7D 00 20 FF FF FF FF \2+ 7D 80 20 FF FF FF FF \2sto 1A 20 81 00 00 00 \2rst 1A 20 00 00 00 00 \2sto 1A 00 82 00 00 00 01 FE 00 00 00 00 00 00 00 00 00 FE 00
   15 * 24	 02 0F FD 41 00 00 00 00 02 00 81 00 00 00 80 02 00 0C 06 00 04 04 18 82
   16 * 68	 02 0F FD 89 7D 01 20 FF FF FF FF \2sto 1A 20 80 00 00 00 \2rst 7D 00 20 FF FF FF FF \2+ 7D 80 20 FF FF FF FF \2sto 1A 20 81 00 00 00 \2rst 1A 20 01 00 00 00 \2sto 1A 00 82 00 00 00 01 FD 00 00 00 00 00 00 00 00 00 FD 00
   17 * 24	 02 0F FC 41 00 00 00 00 02 00 81 00 00 00 80 02 00 06 00 00 04 04 18 82
   18 * 68	 02 0F FC 89 7D 01 20 FF FF FF FF \2sto 1A 20 80 00 00 00 \2rst 7D 00 20 FF FF FF FF \2+ 7D 80 20 FF FF FF FF \2sto 1A 20 81 00 00 00 \2rst 1A 20 02 00 00 00 \2sto 1A 00 82 00 00 00 01 FC 00 00 00 00 00 00 00 00 00 FC 00
   19 * 24	 02 0F FB 41 00 00 00 00 02 00 81 00 00 00 80 02 00 00 06 00 04 04 18 82
   20 * 68	 02 0F FB 89 7D 01 20 FF FF FF FF \2sto 1A 20 80 00 00 00 \2rst 7D 00 20 FF FF FF FF \2+ 7D 80 20 FF FF FF FF \2sto 1A 20 81 00 00 00 \2rst 1A 20 03 00 00 00 \2sto 1A 00 82 00 00 00 01 FB 00 00 00 00 00 00 00 00 00 FB 00
   21 * 43	 02 0F FE 89 48 00 FF 00 00 00 03 FD 00 01 00 00 00 01 00 00 00 FC 00 02 00 00 00 02 00 00 00 FB 00 03 00 00 00 03 00 00 00 FE 00
   22 * 484	 02 0F FB 89 41 28 1F 00 00 00 \2cmp 1A 20 1E 00 00 00 \2& 1A 20 01 00 00 00 \2* 1A 20 12 00 00 00 \2sto 1A 20 80 00 00 00 \2rst 41 28 1F 00 00 00 \2cmp 1A 20 1D 00 00 00 \2& 1A 20 01 00 00 00 \2* 1A 20 0F 00 00 00 \2sto 1A 20 81 00 00 00 \2rst 41 28 1F 00 00 00 \2cmp 1A 20 1B 00 00 00 \2& 1A 20 01 00 00 00 \2* 1A 20 11 00 00 00 \2sto 1A 20 82 00 00 00 \2rst 41 28 1F 00 00 00 \2cmp 1A 20 17 00 00 00 \2& 1A 20 01 00 00 00 \2* 1A 20 10 00 00 00 \2sto 1A 20 83 00 00 00 \2rst 41 28 1F 00 00 00 \2cmp 1A 20 00 00 00 00 \2< 1A 20 01 00 00 00 \2sto 1A 20 84 00 00 00 \2rst 41 28 1F 00 00 00 \2cmp 1A 20 0E 00 00 00 \2^ 1A 20 02 00 00 00 \2< 1A 20 01 00 00 00 \2& 7D 84 20 FF FF FF FF \2* 41 28 1F 00 00 00 \2+ 7D 83 20 FF FF FF FF \2+ 7D 82 20 FF FF FF FF \2+ 7D 81 20 FF FF FF FF \2+ 7D 80 20 FF FF FF FF \2sto 1A 20 00 00 00 00 \2rst 1A 20 8D 0F 00 00 \2sto 1A 20 01 00 00 00 \2rst 41 20 07 00 00 00 \2cmp 1A 20 01 00 00 00 \2& 1A 20 01 00 00 00 \2u< 1A 20 01 00 00 00 \2sto 1A 20 85 00 00 00 \2^ 1A 20 01 00 00 00 \2sto 1A 20 86 00 00 00 \2rst 7D 01 20 FF FF FF FF \2* 7D 86 20 FF FF FF FF \2sto 1A 20 87 00 00 00 \2rst 7D 85 20 FF FF FF FF \2* 1A 20 C6 11 00 00 \2+ 7D 87 20 FF FF FF FF \2sto 1A 20 01 00 00 00 \2rst 41 20 07 00 00 00 \2cmp 1A 20 04 00 00 00 \2& 1A 20 01 00 00 00 \2u< 1A 20 01 00 00 00 \2sto 1A 20 88 00 00 00 \2^ 1A 20 01 00 00 00 \2sto 1A 20 89 00 00 00 \2rst 7D 01 20 FF FF FF FF \2* 7D 89 20 FF FF FF FF \2sto 1A 20 8A 00 00 00 \2rst 7D 88 20 FF FF FF FF \2* 1A 20 C6 11 00 00 \2+ 7D 8A 20 FF FF FF FF \2sto 1A 00 01 00 00 00 01 FE 00 00 00 00 00 00 00 00 00 FE 00
   23 * 44	 02 0F FE 89 1A 20 00 00 00 00 \2sto 1A 20 00 00 00 00 \2rst 1A 20 8D 0F 00 00 \2sto 1A 00 01 00 00 00 01 FE 00 00 00 00 00 00 00 00 00 FE 00
   24 * 41	 00 0F 0C 01 00 08 "NLRF" 09 00 D0 0A 01 D0 0B 0F 0C 11 0D 02 14 08 0E D1 ED 0A 00 0F FF FF FF FF 10 30 08 16 02 17 04
   25 * 8	 00 0F 01 01 00 15 30 00
   26 * 23	 02 0F FC 89 0C 00 FF FF 00 00 01 00 80 5D 01 00 00 5D 01 00 00 FB 00
   27 * 33	 02 0F FB 89 0C 00 FF FF 00 00 02 FE 00 00 00 00 00 00 00 00 00 02 80 5C 01 00 00 5C 01 00 00 FB 00
   28 * 10	 03 0F 01 00 01 FF FB 00 FC 00
   29 * 222	 04 08 FF 03 00 "╨Dutch Road Furniture" 00 "Dutch Fingerpost three-way" 00 "The three-way fingerpost is centered at one side of the tile and facing outward. Intended to be placed directly opposite of the secondary road at a three-way junction." 00
Example uncommented NML code
grf {
    grfid:                  "NMLT";
    name:                   string(STR_GRF_NAME);
    desc:                   string(STR_GRF_DESCRIPTION);
    version:                0;
    min_compatible_version: 0;
}
if (version_openttd(1,2,0,22723) > openttd_version) {
	error(FATAL, REQUIRES_OPENTTD, string(STR_OPENTTD_VERSION));
}
spriteset (spriteset_fingerpost_3, "gfx/dutch_fingerpost.png") {
    [0,   0,      20,     32,     -10,    -28]
    [30,  0,      20,     32,     -10,    -28]
    [60,  0,      20,     32,     -10,    -28]
    [90,  0,      20,     32,     -10,    -28]
}
spritelayout spritelayout_fingerpost_3_SE {
    ground {
        sprite: LOAD_TEMP(0) + LOAD_TEMP(1);
    }
    building {
        sprite: spriteset_fingerpost_3(0);
        xextent: 4;
        yextent: 4;
        zextent: 24;
        xoffset: 6;
        yoffset: 12;
        zoffset: 0;
    }
}
spritelayout spritelayout_fingerpost_3_SW {
    ground {
        sprite: LOAD_TEMP(0) + LOAD_TEMP(1);
    }
    building {
        sprite: spriteset_fingerpost_3(1);
        xextent: 4;
        yextent: 4;
        zextent: 24;
        xoffset: 12;
        yoffset: 6;
        zoffset: 0;
    }
}
spritelayout spritelayout_fingerpost_3_NW {
    ground {
        sprite: LOAD_TEMP(0) + LOAD_TEMP(1);
    }
    building {
        sprite: spriteset_fingerpost_3(2);
        xextent: 4;
        yextent: 4;
        zextent: 24;
        xoffset: 6;
        yoffset: 0;
        zoffset: 0;
    }
}
spritelayout spritelayout_fingerpost_3_NE {
    ground {
        sprite: LOAD_TEMP(0) + LOAD_TEMP(1);
    }
    building {
        sprite: spriteset_fingerpost_3(3);
        xextent: 4;
        yextent: 4;
        zextent: 24;
        xoffset: 0;
        yoffset: 6;
        zoffset: 0;
    }
}
switch (FEAT_OBJECTS, SELF, switch_fingerpost_3_view, view) {
    1:  spritelayout_fingerpost_3_SW;
    2:  spritelayout_fingerpost_3_NW;
    3:  spritelayout_fingerpost_3_NE;
    spritelayout_fingerpost_3_SE;
}
switch (FEAT_OBJECTS, SELF, switch_fingerpost_3_object, [
        STORE_TEMP(slope_to_sprite_offset(tile_slope), 0),
        STORE_TEMP(GROUNDSPRITE_NORMAL, 1),
        STORE_TEMP(terrain_type == TILETYPE_DESERT ? GROUNDSPRITE_DESERT : LOAD_TEMP(1), 1),
        STORE_TEMP(terrain_type == TILETYPE_SNOW   ? GROUNDSPRITE_SNOW   : LOAD_TEMP(1), 1)
        ]) {
    switch_fingerpost_3_view;
}
switch (FEAT_OBJECTS, SELF, switch_fingerpost_3_purchase, [
        STORE_TEMP(0, 0),
        STORE_TEMP(GROUNDSPRITE_NORMAL, 1),
        ]) {
	switch_fingerpost_3_view;
}
item (FEAT_OBJECTS, item_fingerpost_3) {
    property {
        class:                  "NLRF";
        classname:              string(STR_NLRF);
        name:                   string(STR_FINGERPOST_3);
        climates_available:     ALL_CLIMATES;
        size:                   [1,1];
        build_cost_multiplier:  2;
        remove_cost_multiplier: 8;
        introduction_date:      date(1961,1,1);
        end_of_life_date:       0xFFFFFFFF;
        object_flags:           bitmask(OBJ_FLAG_REMOVE_IS_INCOME, OBJ_FLAG_NO_FOUNDATIONS, OBJ_FLAG_ALLOW_BRIDGE);
        height:                 2;
        num_views:              4;
    }
    graphics {
        default:            switch_fingerpost_3_object;
        purchase:           switch_fingerpost_3_purchase;
        autoslope:          return(CB_RESULT_AUTOSLOPE);
        additional_text:    string(STR_FINGERPOST_3_PURCHASE);
    }
}
Example uncommented NML language file
##grflangid 0x01
STR_GRF_NAME        :NMLTutorial Object Example
STR_GRF_DESCRIPTION :Description: {SILVER}Dutch Road Furniture is an eyecandy object NewGRF that features road furniture that can be found alongside Dutch roads. {}(c)2011 FooBar. {}{BLACK}License: {SILVER}GPLv2 or higher.
STR_OPENTTD_VERSION :1.2.0 (r22723)
STR_NLRF            :Dutch Road Furniture
STR_FINGERPOST_3            :Dutch Fingerpost three-way
STR_FINGERPOST_3_PURCHASE   :The three-way fingerpost is centered at one side of the tile and facing outward. Intended to be placed directly opposite of the secondary road at a three-way junction.


NML Tutorial: NMLTutorial


  1. The example NFO file is 4.96 kB; the example NML file and it's language file combined are 3.99 kB. A fully commented NFO file is bound to be both significantly longer and bigger when compared to a fully commented NML file.