NMLTutorial/Road vehicle graphics template
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 the third part of the road vehicle example. On this page, we'll template the spritesets of the roadvehicle.
Spritesets
Let's recall the spritesets we already had for this road vehicle:
//graphics definition spriteset(spriteset_flatbed_truck_1_goods_empty, "gfx/flatbed_truck_1_goods.png") { //left_x, upper_y, width, height, offset_x, offset_y [ 0, 0, 8, 18, -3, -10] [ 16, 0, 20, 16, -14, -7] [ 48, 0, 28, 12, -14, -6] [ 96, 0, 20, 16, -6, -7] [ 128, 0, 8, 18, -3, -10] [ 144, 0, 20, 16, -14, -7] [ 176, 0, 28, 12, -14, -6] [ 224, 0, 20, 16, -6, -7] } spriteset(spriteset_flatbed_truck_1_goods_full, "gfx/flatbed_truck_1_goods.png") { //left_x, upper_y, width, height, offset_x, offset_y [ 260, 0, 8, 18, -3, -10] [ 276, 0, 20, 16, -14, -7] [ 308, 0, 28, 12, -14, -6] [ 356, 0, 20, 16, -6, -7] [ 388, 0, 8, 18, -3, -10] [ 404, 0, 20, 16, -14, -7] [ 436, 0, 28, 12, -14, -6] [ 484, 0, 20, 16, -6, -7] }
As you can see, the width, height and offset columns in both spritesets are identical. This means that our graphics file already uses some kind of consistent sprite template, which is a great basis for an NML code template. When looking at the upper_y column, you can see that all values are 0. This indicates that all sprite boxes are aligned nicely on one row and that we can parameterize the upper_y column if we want. The left_x column is a bit more tricky, but if you look closely, you'll notice that column of the second spriteset is exactly 260 higher than for the first spriteset. Substitute 260 with + x
and also this column is templatable!
Template
Having assessed that the graphic are indeed templatable, we can decide what template parameters we want for the template. x
and y
are usually the two template parameters you want for the most basic template, so let's go with those.
What you do now is start a template block with two template parameters x
and y
. Give the template block a name of your choice. Next, copy the contents of one of the spritesets into the template block. Add + x
to the first column left_x and replace the 0's of the second column upper_y with y
.
If the first value of the first column is not 0 + x
, but <value> + x
, subtract <value>
from each entry in the first column such that the first value will read 0 + x
while maintaining the relative distance between sprites.
For the road vehicle template, you'll end up with something like this:
/* 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] }
Template call
Now instead of the sprite definitions in the spritesets, we can call the template. Remove the contents of the spriteset and replace them with a template call. Supply the correct values for the x
and y
template parameters. For the first spriteset this will be (0 , 0)
and for the second (260, 0)
.
Why these template parameter values? Well, the distance from the top left of the graphics file to the top left of the first sprite of the block of sprites is 0 in both horizontal (x) and vertical (y) direction. The distance to the first sprite of the second block of sprites is 260 in horizontal (x) direction and again 0 vertically.
This gives the following replacement code for the spritesets:
//graphics definition 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) }
Easy, right?
Updating the complete code
Now let's look at the full code. Because the template needs to be defined before you can use it, it needs to go somewhere near the top. Let's put it below the grf block for no particular reason other than that it's higher than the spriteset blocks.
Furthermore, replace the two existing spriteset blocks with the new templated spriteset blocks.
You should get something 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] } //graphics definition 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 { default: spritegroup_flatbed_truck_1_goods; // use a goods container for a (loaded) flatbed truck } }
When you compile this into a NewGRF, it should be exactly the same as the one we already had, but now your code is slightly shorter and much easier to handle if you need to change a sprite offset or two.
Now that the graphics are templated, we can use this template to add some graphics for specific cargo loads of this truck. This will be the final step in our road vehicle example. Before we do that, you'll need to learn some things about cargotables first.
NML Tutorial: Road vehicle graphics template