This page is meant as a basic guide on how to code a train set.
First of all, I will start with some basic things a newGRF needs to have in general,
after that we can start making a super simple train. Defining basic stats, and using some template I will provide for you.
Then, I will describe how to make your own template.
And after we have a basic train and a template to go from, I will go over all NUTS features and deviations of each train, and show how such function is coded.
Note that you can read about ALL of the NML specs and features on the tt-wiki mainly here
The problem is that some features are only reachable through NFO and then you need to consult also the darker side of tt-wiki:

This page is meant to clearly demonstrate how to code a train set, with everything that I have experienced with NUTS.

Note: unless I state otherwise, everything is written in the .nml file, in my case nuts.nml.

Starting a newGRF

In the nuts.nml, put this:

grf {
    grfid: "XX\01\01";
    name: string(STR_GRF_NAME);
    desc: string(STR_GRF_DESC);
    version: 1;
    url  : string(STR_GRF_WEBSITE);
    min_compatible_version: 1;

in your lang/english.lng should then be:

STR_GRF_NAME        :NUTS Unrealistic Train Set {VERSION}
STR_GRF_DESC        :Train set based on gameplay, ...

Note the {VERSION} - this is a reference from custom_tags.txt ... this is useful if or example you load the version in multiple places - that way you can change it in just one spot whenever you need to do that. So create custom_tags.txt in the main folder and put there

VERSION        :0.0.1

Making it compile

Assuming you have installed NML, we can create a script to put stuff together. My NUTS compile.bat looks like this:

nmlc -c --default-lang=english.lng --grf=nuts.grf nuts.nml
copy /Y nuts.grf "C:\Users\Vasek\Documents\OpenTTD\data" 

The command simply says "tell nmlc -c that default language is english.lng and create a nuts.GRF from nuts.NML, and then copy that grf to the C:\Users\...

Creating a train

Before we actually put down the chunks which actually create our train, we should disable the default trains, so here goes:


Sprite Template

For now, just copy this thing and be done with it. As you will later understand, this is the table of hell. Creating this thing is extremely tedious and takes a lot of time.

template template_NormalTrain(x, y) {
    //x = model type
    //y = colour
    [   0+216*x, 29*y, 12, 28,  -5, -15]
    [  13+216*x, 29*y, 30, 28, -17, -17]
    [  44+216*x, 29*y, 32, 16, -16, -11]
    [  77+216*x, 29*y, 30, 28,  -9, -16]
    [ 108+216*x, 29*y, 12, 28,  -5, -17]
    [ 121+216*x, 29*y, 30, 28, -17, -17]
    [ 152+216*x, 29*y, 32, 16, -16, -11]
    [ 185+216*x, 29*y, 30, 28,  -9, -16]

This is the recipe for a spriteset how to load the images - what sizes, positions, ... so now we can do exactly that:

spriteset(spriteset_monoice1_CC_PASS, "gfx/MonoICE.png") { template_NormalTrain(0, 0) }

This is creating a spriteset called spriteset_monoice1_CC_PASS, using file from folder gfx, called MonoICE.png. And this spriteset uses our template with parameters 0, 0.

Train Item

Now that we have our images ready to be applied somewhere, how about we code an engine which will use them?
Coding engines is simple, there is just one item where everything belongs.

item(FEAT_TRAINS, item_monoice1, 171) { //ID171
    property {
    name:                         string(STR_NAME_MONOICE1);
        climates_available:           ALL_CLIMATES;
        introduction_date:            date(1995, 1, 1);

        model_life:                     26;
        vehicle_life:                 255;
        reliability_decay:            5;

    cargo_capacity:               24;
        loading_speed:                5;

        refittable_cargo_classes:     bitmask(CC_PASSENGERS, CC_MAIL, CC_ARMOURED);
        non_refittable_cargo_classes: bitmask();

        cost_factor:                  10;
        refit_cost:                   0;
        running_cost_factor:          100;
        running_cost_base:            RUNNING_COST_ELECTRIC;

        sprite_id:                    SPRITE_ID_NEW_TRAIN;

        speed:                        310 km/h;
        power:                        41000 hp;
        weight:                       110 ton;
        tractive_effort_coefficient:  1;
        air_drag_coefficient:         0;
        dual_headed:                  1;

        engine_class:               ENGINE_CLASS_MONORAIL;
        track_type:                   MONO;
        misc_flags:                    bitmask(TRAIN_FLAG_FLIP, TRAIN_FLAG_MU, TRAIN_FLAG_2CC);

        ai_special_flag:              AI_FLAG_CARGO;
        ai_engine_rank:               0;

        length: 8;
        visual_effect_and_powered:    visual_effect_and_powered(VISUAL_EFFECT_DEFAULT, 1, DISABLE_WAGON_POWER);
        extra_weight_per_wagon:       1 ton;
        bitmask_vehicle_info:         0;
    graphics {
        default: MonoICE_subtype_switch;