NMLTutorial/Road vehicle cargo graphics

From TTWiki
Jump to: navigation, search

The example used here is from the NML source. The code for this was originally written by Terkhen and planetmaker. The graphics used in the example are by DanMack and Zephyris. Code and graphics are both licensed according to the GPL v2 or later. The code has been modified for the purpose of this tutorial


This continues and concludes the fourth part of the road vehicle example. We'll add some different graphics for different cargos.


More graphics

So far, our truck is rather boring, showing just a container when it's loaded regardless of the cargo it carries. NML makes it easy to supply different graphics depending the cargo the vehicle is refitted to.

Because the graphics are open source, I'll give them to you so you don't have to draw them first:

Graphics for copper cargo (not to be confused with copper ore)
Graphics for paper cargo
Graphics for steel cargo
Graphics for wood cargo


More spritesets and spritegroups

Each block of sprites will need a spriteset and the empty and full states again need to be combined in a spriteset. Look back if you've forgotten how to do that.

The spritesets of course will use the template we made earlier. As there's nothing new to say about the spritesets and spritegroups, I'll just give them to you:

/* Define various cargo-specific graphics */

/* Paper */
spriteset(spriteset_flatbed_truck_1_paper_empty, "gfx/flatbed_truck_1_paper.png") { 
    tmpl_truck(0, 0) 
}
spriteset(spriteset_flatbed_truck_1_paper_full, "gfx/flatbed_truck_1_paper.png") { 
    tmpl_truck(260, 0) 
}
spritegroup spritegroup_flatbed_truck_1_paper {
    loaded: [spriteset_flatbed_truck_1_paper_empty, spriteset_flatbed_truck_1_paper_full];
    loading: [spriteset_flatbed_truck_1_paper_empty, spriteset_flatbed_truck_1_paper_full];
}

/* Steel */
spriteset(spriteset_flatbed_truck_1_steel_empty, "gfx/flatbed_truck_1_steel.png") { 
    tmpl_truck(0, 0) 
}
spriteset(spriteset_flatbed_truck_1_steel_full, "gfx/flatbed_truck_1_steel.png") { 
    tmpl_truck(260, 0) 
}
spritegroup spritegroup_flatbed_truck_1_steel {
    loaded: [spriteset_flatbed_truck_1_steel_empty, spriteset_flatbed_truck_1_steel_full];
    loading: [spriteset_flatbed_truck_1_steel_empty, spriteset_flatbed_truck_1_steel_full];
}

/* Wood */
spriteset(spriteset_flatbed_truck_1_wood_empty, "gfx/flatbed_truck_1_wood.png") { 
    tmpl_truck(0, 0) 
}
spriteset(spriteset_flatbed_truck_1_wood_full, "gfx/flatbed_truck_1_wood.png") { 
    tmpl_truck(260, 0) 
}
spritegroup spritegroup_flatbed_truck_1_wood {
    loaded: [spriteset_flatbed_truck_1_wood_empty, spriteset_flatbed_truck_1_wood_full];
    loading: [spriteset_flatbed_truck_1_wood_empty, spriteset_flatbed_truck_1_wood_full];
}

/* Copper */
spriteset(spriteset_flatbed_truck_1_copper_empty, "gfx/flatbed_truck_1_copper.png") { 
    tmpl_truck(0, 0) 
}
spriteset(spriteset_flatbed_truck_1_copper_full, "gfx/flatbed_truck_1_copper.png") { 
    tmpl_truck(260, 0) 
}
spritegroup spritegroup_flatbed_truck_1_copper {
    loaded: [spriteset_flatbed_truck_1_copper_empty, spriteset_flatbed_truck_1_copper_full];
    loading: [spriteset_flatbed_truck_1_copper_empty, spriteset_flatbed_truck_1_copper_full];
}

These new spriteset and spritegroup definitions can be added to the one you already have.

You may have noticed that some of the empty vehicle graphics are the same. To reduce the filesize of the NewGRF and to limit the number of sprites ingame, it is better not to duplicate sprites if you don't have to. In this case that can be solved by removing the spritesets that define duplicate sprites. From the spritegroups, you need to change the references to removed spritesets to point to the one remaining spriteset instead. Because spritesets need to be defined before they can be referenced, this will involve some reordering of the spritesets and spritegroups. Once you have the road vehicle working with different graphics, it will be a nice challenge for you to try and remove the duplicates yourself.


Cargotable

Before you can link the graphics to the vehicle and the different cargos, you need to tell NML which cargos you'll be using in your NML file. This means adding a cargotable.

First, you need to look up the labels of the cargos we have custom graphics for. For paper, steel, copper and wood these labels are PAPR, STEL, COPR and WOOD respectively.

This will give us the following cargotable:

cargotable {
    PAPR, // Paper
    STEL, // Steel
    COPR, // Copper
    WOOD, // Wood
}

The final comma (after WOOD) isn't needed, but doesn't hurt either. The cargotable itself should go somewhere near the top of your NML file, as it needs to be defined before you can use any of the cargo labels elsewhere. Below the template definition seems a good place for it.


Linking the graphics

Linking the graphics to the vehicle and a specific cargo label is easy. It is done in the graphics block of the item block of the vehicle. There you just write the label of the cargo, followed by a colon and the identifier of the spritegroup (or spriteset if you don't have a spritegroup). Finish the line with a simicolon.

This will change our graphics block into the following:

    graphics {
        PAPR: spritegroup_flatbed_truck_1_paper;
        STEL: spritegroup_flatbed_truck_1_steel;
        COPR: spritegroup_flatbed_truck_1_copper;
        WOOD: spritegroup_flatbed_truck_1_wood;
        default: spritegroup_flatbed_truck_1_goods; // Default to Goods.
    }

Now this truck will show different sprites when refitted to paper, steel, copper or wood. When refitted to something else, it will show the container we already had.


The complete code

The complete NML file that defines this road vehicle with it's different (templated) graphics will now look like this:

//define the grf
grf {
    grfid: "NML\03";
    name: string(STR_GRF_NAME);
    desc: string(STR_GRF_DESC);
    version: 0;
    min_compatible_version: 0;
}

/* Sprite template for a truck */
template tmpl_truck(x, y) {
    //left_x, upper_y, width, height, offset_x, offset_y
    [  0 + x, y,        8,    18,      -3,      -10]
    [ 16 + x, y,       20,    16,     -14,       -7]
    [ 48 + x, y,       28,    12,     -14,       -6]
    [ 96 + x, y,       20,    16,      -6,       -7]
    [128 + x, y,        8,    18,      -3,      -10]
    [144 + x, y,       20,    16,     -14,       -7]
    [176 + x, y,       28,    12,     -14,       -6]
    [224 + x, y,       20,    16,      -6,       -7]
}

/* Define a cargo translation table
 * All cargo types that need any special treatment must be included here */
cargotable {
    PAPR, // Paper
    STEL, // Steel
    COPR, // Copper
    WOOD, // Wood
}

/* Define various cargo-specific graphics */

/* Paper */
spriteset(spriteset_flatbed_truck_1_paper_empty, "gfx/flatbed_truck_1_paper.png") { 
    tmpl_truck(0, 0) 
}
spriteset(spriteset_flatbed_truck_1_paper_full, "gfx/flatbed_truck_1_paper.png") { 
    tmpl_truck(260, 0) 
}
spritegroup spritegroup_flatbed_truck_1_paper {
    loaded: [spriteset_flatbed_truck_1_paper_empty, spriteset_flatbed_truck_1_paper_full];
    loading: [spriteset_flatbed_truck_1_paper_empty, spriteset_flatbed_truck_1_paper_full];
}

/* Steel */
spriteset(spriteset_flatbed_truck_1_steel_empty, "gfx/flatbed_truck_1_steel.png") { 
    tmpl_truck(0, 0) 
}
spriteset(spriteset_flatbed_truck_1_steel_full, "gfx/flatbed_truck_1_steel.png") { 
    tmpl_truck(260, 0) 
}
spritegroup spritegroup_flatbed_truck_1_steel {
    loaded: [spriteset_flatbed_truck_1_steel_empty, spriteset_flatbed_truck_1_steel_full];
    loading: [spriteset_flatbed_truck_1_steel_empty, spriteset_flatbed_truck_1_steel_full];
}

/* Wood */
spriteset(spriteset_flatbed_truck_1_wood_empty, "gfx/flatbed_truck_1_wood.png") { 
    tmpl_truck(0, 0) 
}
spriteset(spriteset_flatbed_truck_1_wood_full, "gfx/flatbed_truck_1_wood.png") { 
    tmpl_truck(260, 0) 
}
spritegroup spritegroup_flatbed_truck_1_wood {
    loaded: [spriteset_flatbed_truck_1_wood_empty, spriteset_flatbed_truck_1_wood_full];
    loading: [spriteset_flatbed_truck_1_wood_empty, spriteset_flatbed_truck_1_wood_full];
}

/* Copper */
spriteset(spriteset_flatbed_truck_1_copper_empty, "gfx/flatbed_truck_1_copper.png") { 
    tmpl_truck(0, 0) 
}
spriteset(spriteset_flatbed_truck_1_copper_full, "gfx/flatbed_truck_1_copper.png") { 
    tmpl_truck(260, 0) 
}
spritegroup spritegroup_flatbed_truck_1_copper {
    loaded: [spriteset_flatbed_truck_1_copper_empty, spriteset_flatbed_truck_1_copper_full];
    loading: [spriteset_flatbed_truck_1_copper_empty, spriteset_flatbed_truck_1_copper_full];
}

/* Goods */
spriteset(spriteset_flatbed_truck_1_goods_empty, "gfx/flatbed_truck_1_goods.png") {
    tmpl_truck(0, 0)
}
spriteset(spriteset_flatbed_truck_1_goods_full, "gfx/flatbed_truck_1_goods.png") {
    tmpl_truck(260, 0)
}
spritegroup spritegroup_flatbed_truck_1_goods {
    loaded: [spriteset_flatbed_truck_1_goods_empty, spriteset_flatbed_truck_1_goods_full];
    loading: [spriteset_flatbed_truck_1_goods_empty, spriteset_flatbed_truck_1_goods_full];
}

/* Define the road vehicle */
item(FEAT_ROADVEHS, item_flatbed_truck_1) {
    property {
        /* Properties common to all vehicle types */
        name:                           string(STR_NAME_FLATBED_TRUCK_1);
        climates_available:             bitmask(CLIMATE_TEMPERATE, CLIMATE_ARCTIC, CLIMATE_TROPICAL);
        introduction_date:              date(1926,01,01);
        model_life:                     65;
        /* retire_early not set, use default retirement behaviour */
        vehicle_life:                   15;
        reliability_decay:              20;
        refittable_cargo_classes:       bitmask(CC_PIECE_GOODS, CC_EXPRESS);
        non_refittable_cargo_classes:   bitmask(CC_PASSENGERS, CC_REFRIGERATED);
        loading_speed:                  5;
        cost_factor:                    108;
        running_cost_factor:            90;
        /* cargo_age_period is left at default */

        /* RV-specific properties */
        sprite_id:                      SPRITE_ID_NEW_ROADVEH; //enable new graphics
        speed:                          48 km/h;
        misc_flags:                     bitmask(ROADVEH_FLAG_2CC);
        refit_cost:                     0; // Refitting is free
        /* callback_flags are not set, no need to manually enable callbacks */
        running_cost_base:              RUNNING_COST_ROADVEH;
        power:                          120 hp;
        weight:                         9.5 ton;
        /* TE and air drag coefficient is left at default */
        cargo_capacity:                 20;
        sound_effect:                   SOUND_BUS_START_PULL_AWAY;
        /* Visual effect is left at default (no effect) */
    }

    /* Define graphics */
    graphics {
        PAPR: spritegroup_flatbed_truck_1_paper;
        STEL: spritegroup_flatbed_truck_1_steel;
        COPR: spritegroup_flatbed_truck_1_copper;
        WOOD: spritegroup_flatbed_truck_1_wood;
        default: spritegroup_flatbed_truck_1_goods; // Default to a goods container.
    }
}

This concludes our road vehicle example. If you think this was easy or not too bad, with some more practice, you'll make a great NML coder. If you think this was really difficult, you'd better give up now that you haven't spent too much time; maybe you should concentrate on graphics and leave the code to others.

For the ones brave enough to continue, we have some really nice topics in store for you. You'll learn to do some funky stuff with callbacks and switches (amonst others) and we have three more fully featured examples for you, each a little more advanced than the previous.

If you don't care about all the other fancy things you can do, but do want to add 32 bit sprites, for this time we'll allow you to skip ahead to the part on 32 bit sprites. Adding 32 bit sprites does not require any more knowledge than what you have gained now.


NML Tutorial: Road vehicle cargo graphics

or

NML Tutorial: Road vehicle cargo graphics