Difference between revisions of "NMLTutorial"
(→Image format support: note on graphicstutorial) |
|||
(6 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
+ | {{NMLTutorial}} |
||
− | __TOC__ |
||
+ | 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 [http://newgrf-specs.tt-wiki.net/wiki/NML:Main 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. |
||
− | ==Introduction== |
||
+ | |||
− | Since june 2010 a new coding language for creating TTDpatch/OpenTTD GRF's exists: Newgrf Meta Language or NML. It differs significantly from the old way of GRF editing and although [http://hg.openttdcoop.org/nml/raw-file/tip/docs/index.html documentation] is available it may still be a challenge for people interested in using NML code. |
||
+ | 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 [[NMLTutorial/Installation|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 [http://www.tt-forums.net/viewtopic.php?t=56361 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.''' |
||
− | There will be detailed examples of how coding in NML works on this new TT-wiki. Make sure to check the progress of this NML tutorial regularly. |
||
==NML vs. NFO== |
==NML vs. NFO== |
||
===Background=== |
===Background=== |
||
− | NML is a high-level NewGRF language compiler which compiles NML and |
+ | 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=== |
===Language support=== |
||
− | NML |
+ | 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=== |
===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. |
||
− | One of the conveniences of NML is the possibility to read other image formats, most notably [http://en.wikipedia.org/wiki/Portable_Network_Graphics PNG]. Remember though that those images still need to supply the proper 8bpp palette and are supported by the python image library. |
||
+ | |||
+ | 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. How to draw graphics is not covered here, but in the [[GraphicsTutorial]]. |
||
===Example=== |
===Example=== |
||
− | Does the above sound impressive, maybe even threatening to you? |
+ | 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<ref>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.</ref>, 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... |
||
+ | |||
+ | |||
+ | <div style="float:left;width:50%;"><span style="font-weight:bold;font-size:medium;">Example uncommented NFO code</span><br /> |
||
+ | <pre class="example"> |
||
+ | // 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 |
||
+ | </pre></div> |
||
+ | <div style="float:left;width:50%;"><span style="font-weight:bold;font-size:medium;">Example uncommented NML code</span><br /> |
||
+ | <pre class="example"> |
||
+ | 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); |
||
+ | } |
||
+ | } |
||
+ | </pre><span style="font-weight:bold;font-size:medium;">Example uncommented NML language file</span><br /> |
||
+ | <pre class="example"> |
||
+ | ##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. |
||
+ | </pre></div> |
||
+ | <div style="clear:left"></div> |
||
+ | |||
+ | {{NMLTutorialNavbar||Installation}} |
||
− | {| cellspacing="0" class="mainpagetable" text-align="left" style="background-color: #F8EABA; border: 1px solid #C0C090;" |
||
− | |- valign="top" |
||
+ | <references/> |
||
− | |width="50%"| |
||
− | <font size="3">'''NFO code snippet'''</font> |
||
− | :// Automatically generated by GRFCODEC. Do not modify! |
||
− | :// (Info version 7) |
||
− | :// Escapes: 2+ = 71 = D= = DR 2- = 70 = D+ = DF 2< = 7= = D- = DC 2> = 7! = Du* = DM 2u< = 7< = D* = DnF 2u> = 7> = Du<< = DnC |
||
− | :2/ = 7G = D<< = DO 2% = 7g = D& 2u/ = 7gG = D| 2u% = 7GG = Du/ 2* = 7gg = D/ 2& = 7c = Du% 2| = 7C = D% 2^ 2sto = 2s 2rst = 2r 2+ 2ror = 2rot |
||
− | :// Format: spritenum pcxfile xpos ypos compression ysize xsize xrel yrel |
||
− | :0 * 4 \d416 |
||
− | :1 * 92 08 07 "SER0" "ÞSwedish Rails nightly-r52M" 00 "ÞSwedish rails are a replacement for the default rails" 00 |
||
− | |width="50%"| |
||
− | <font size="3">'''NML code snippet'''</font> |
||
− | :grf { |
||
− | ::grfid : "SER0"; |
||
− | ::name : string(STR_GRF_NAME); |
||
− | ::desc : string(STR_GRF_DESCRIPTION); |
||
− | :} |
||
− | <font size="3">'''NML laguage file snippet'''</font> |
||
− | :<nowiki>##</nowiki>grflangid 0x01 |
||
− | :STR_GRF_NAME :Swedish Rails {VERSION} |
||
− | :STR_GRF_DESCRIPTION :Swedish rails are a replacement for the default rails |
||
− | |} |
||
[[Category:NML]][[Category:Tutorials]] |
[[Category:NML]][[Category:Tutorials]] |
Latest revision as of 21:08, 28 August 2011
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. How to draw graphics is not covered here, but in the GraphicsTutorial.
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...
// 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
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
- ↑ 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.