Difference between revisions of "NMLTutorial/NML Syntax"

From TTWiki
Jump to navigationJump to search
(comment examples)
(something on that identifiers need to be unique and a suggestion how to name them.)
Line 26: Line 26:
   
 
== NML structure: blocks ==
 
== NML structure: blocks ==
NML files are mainly composed from blocks. A block starts with the type of the block, optional arguments and then the contents enclosed by curly braces. Some blocks are supplied with an identifier (ID, ''a name''), some with even more arguments and some are only referenced by their type. The basic block syntax is as follows:
+
NML files are mainly composed from blocks. A block starts with the type of the block, optional arguments and then the contents enclosed by curly braces. Some blocks are supplied with an identifier (''a name''), some with even more arguments and some are only referenced by their type. The basic block syntax is as follows:
   
 
<pre>
 
<pre>
Line 35: Line 35:
 
or
 
or
 
<pre>
 
<pre>
type name {
+
type identifier {
 
//block contents
 
//block contents
 
}
 
}
Line 41: Line 41:
 
or
 
or
 
<pre>
 
<pre>
type (name, argument1, [argument2 [,...]] {
+
type (identifier, argument1, [argument2 [,...]] {
 
//block contents
 
//block contents
 
}
 
}
 
</pre>
 
</pre>
  +
''Note: the identifier isn't necessarily the first argument of a block in case of multiple arguments.''
   
 
Currently, the following block types exist:
 
Currently, the following block types exist:
Line 85: Line 86:
   
 
If you need to do some calculations you do those outside the blocks, or in some special cases in a block's ''expression'' argument, but never inside a block. <code>if</code>s and <code>else</code>s also have no place inside blocks and can only be used outside them.
 
If you need to do some calculations you do those outside the blocks, or in some special cases in a block's ''expression'' argument, but never inside a block. <code>if</code>s and <code>else</code>s also have no place inside blocks and can only be used outside them.
  +
  +
=== Identifiers ===
  +
As you've read earlier, some blocks need to have an ''identifier''. You can see this identifier as a name for the block and you choose this identifier yourself.
  +
  +
'''All identifiers need to be unique throughout you NML file!''' This even applies for identifiers of totally different blocks. An easy method to avoid duplicate identifiers is to precede the identifier name with the name of the block type. While this isn't foolproof, it simply reduces the chances for duplicates. It is also a good idea to add the name of the item (vehicle, object, etc.) an identifier belongs to to the name of the identifier.
  +
  +
With both the block name and item name in the identifier, you can also see quickly from the identifier what this is for, without having to write that down somewhere seperately.
   
   

Revision as of 13:01, 24 August 2011

This page will be a theoretical introduction to the NML syntax. It is very well possible that you don't understand anything of what is written here. If that is the case, please read the page carefully once and then just continue with the next page which will take you on an example journey through the world of coding in NML.

If you do understand this page completely, then it's very likely that you don't need the rest of this NML tutorial and just can look up what you need in the NML Documentation. At your option you can browse through the tutorial quickly or just start making your NewGRF and figure things out as you go.


NML is a programming language

NFO is not a programming language: it's hex editing. NML on the other hand is a human-readable programming language and contains things like if/else statements, operators such as &&, ||, !=, >= etc. and curly brackets to enclose groups of statements.

If you already know a programming language such as C++ or PHP, it's probably very easy to understand and adapt to the NML syntax. If you're new to programming, you should understand a few concepts:

If you're new to programming

Name things the way you want
If you define something, it's you who gives it a name. There's no set of rules how things should be called. If you want to name everything foo0001 through foo9999 that's up to you. That doesn't make it very easy to remember which is which, but it isn't wrong per se. However, don't use any fancy symbols in names: you may use (capital) letters a through z, numbers 0 through 9 and an underscore (_). Numbers furthermore cannot be at the beginning of the name of an identifier.
When referencing something, it needs to exist
If you want to use a string, the string needs to exists. That probably makes as much sense to you as that a circle is round, right? But it goes further than that: if you define a vehicle and want it to use this and that graphics, the graphics need to be defined before you define the vehicle that uses it. This applies to everything; if your reference something somewhere, that something needs to exists and essentially needs to be before the reference.
Compilers don't understand typos
If you make a typo, the compiler (in this case nmlc) doesn't understand what you mean. This especially applies to the names of things we discussed earlier. If you name your vehicle definition KirbyPaulTank, then you must use the exact capitalization everywhere. For a compiler, Kirbypaultank, kirbypaultank, kirbypaulTank etc. are all completely different things. You need to pay special attention to this if you come from Windows. In Windows there's no difference if you write a file or directory with or without captials; in a programming language with or without capitals is a completely different thing and essentially a typo when not used consistently.
Add more comments than you think is necessary
When working on a piece of code, you easily remember what does what and why you made it the way you did. However, if you don't look at a piece of code for a couple of months, chances are you have no clue what it does if you haven't commented it. Using decent names for definitions helps a great deal towards reducing the amount of comments, but ideally every code block should have a line stating what it's for and more complicated things also get comments as you go. If you do certain things differently for a reason, state why you did that. If you work with more people on one project, comments are vital in helping fellow developers understand what you did.


NML structure: blocks

NML files are mainly composed from blocks. A block starts with the type of the block, optional arguments and then the contents enclosed by curly braces. Some blocks are supplied with an identifier (a name), some with even more arguments and some are only referenced by their type. The basic block syntax is as follows:

type {
    //block contents
}

or

type identifier {
    //block contents
}

or

type (identifier, argument1, [argument2 [,...]] {
    //block contents
}

Note: the identifier isn't necessarily the first argument of a block in case of multiple arguments.

Currently, the following block types exist:

  • grf
  • item
  • recolour_sprite
  • template
  • spriteset
  • spritegroup
  • spritelayout
  • tilelayout
  • switch
  • produce
  • random_switch
  • cargotable
  • railtypetable
  • error
  • disable_item
  • deactivate
  • engine_override
  • replace
  • replacenew
  • font_glpyh
  • alt_sprites
  • town_names
  • snowline
  • basecost

Some special "blocks", which aren't really blocks but language constructs, exist as well:

  • if
  • else

Furthermore, there are too many functions to list here. For now you can forget these if you want, but it's good to have seen their names.

The blocks define things that can be referenced or make new things available in the game. Some blocks can contain sub-blocks. The grf block can contain a param subblock to add parameter settings. The item block can contain property, graphics and livery_override subblocks.

If you need to do some calculations you do those outside the blocks, or in some special cases in a block's expression argument, but never inside a block. ifs and elses also have no place inside blocks and can only be used outside them.

Identifiers

As you've read earlier, some blocks need to have an identifier. You can see this identifier as a name for the block and you choose this identifier yourself.

All identifiers need to be unique throughout you NML file! This even applies for identifiers of totally different blocks. An easy method to avoid duplicate identifiers is to precede the identifier name with the name of the block type. While this isn't foolproof, it simply reduces the chances for duplicates. It is also a good idea to add the name of the item (vehicle, object, etc.) an identifier belongs to to the name of the identifier.

With both the block name and item name in the identifier, you can also see quickly from the identifier what this is for, without having to write that down somewhere seperately.


Features

Some blocks can do multiple things. For example, the item can define a train, but also a road vehicle, ship, or aircraft. In case of such a block, you need to set which feature the block is for and supply this to the block as an argument. Below is a table of available features:

Name Description
FEAT_TRAINS Trains
FEAT_ROADVEHS Road vehicles
FEAT_SHIPS Ships
FEAT_AIRCRAFT Aircraft
FEAT_STATIONS Train stations
FEAT_CANALS Canals
FEAT_BRIDGES Bridges
FEAT_HOUSES Town houses
FEAT_GLOBALVARS Various global variables
FEAT_INDUSTRYTILES Industry tiles (visible part of industries)
FEAT_INDUSTRIES Industries
FEAT_CARGOS Cargo types
FEAT_SOUNDEFFECTS Sound effects
FEAT_AIRPORTS Airports
FEAT_SIGNALS Train signals
FEAT_OBJECTS Non-interactive objects (example: lighthouse)
FEAT_RAILTYPES Rail types
FEAT_AIRPORTTILES Airport tiles (visible part of airports)


Comments

As said before, it's useful to add comments to your NML code to indicate what things are for.

Comments can be written as comment block or as inline comment. Comment blocks go between different lines of NML code, while inline comments can also go there as well as at the end of each line.

Comment block example:

/*
This is a comment block, it starts with a slash and an asterisk.
It can span multiple lines
Before it ends with an asterisk and a slash
*/

Inline comment example:

//An inline comment is preceded by two slashes
//If you need multiple lines, each line gets two slashes in front

grf {
    name: string(STR_GRF_NAME); //it can also go at the end of a line
    
}


Conclusion

Yes, that's a bunch of stuff with little hands-on examples, but I warned you about that. You'll find (most of) these different blocks explained in the remainder of this tutorial. And, of course, how every block should be written is clearly documented in the block syntax chapter of the NML Documentation.


NML Tutorial: NML Syntax