NMLTutorial/Template

From TTWiki
Jump to: navigation, search

If you've ever coded a NewGRF before, you'll know that figuring out the position of sprites and size in a graphics file and aligning them ingame by changing the offsets is a b****. Using templates for the sprite definitions inside spriteset blocks will save you a lot of trouble.

Templatable graphics files

First you need to make sure that your graphics file(s) are actually templatable. This means that every group of sprites for e.g. a vehicle needs to have the same sprite sizes and the same distances between those sprites. Futhermore each vehicle needs to be aligned consistently within their sprite boxes, otherwise you still end up having to specify custom offsets for each sprite.

If you have vehicles (or buildings or whatever) of different sizes, and still want to use a single template for that, you can make your sprite boxes as big as the largest vehicle (or whatever) and use these big boxes also for smaller sprites.

An example of how to template graphics for trams and road vehicles has been included below. As long as you stick your vehicle sprites in one of these predefined sprite boxes and aligned correctly like the example 'blocks', you can use the same template for the NML sprite code for multiple vehicles. This particular sprite template actually has eight different sprite templates for the eight (default, not counting overhangs) vehicle lengths available in OpenTTD (only use the first four in TTDPatch) and one for the purchase menu sprite. Each of these come with exactly one NML template with the sprite sizes and offsets. So if you have a full length (32px) vehicle, you use the 32px sprite template and the 32px NML templates that belongs to this.

Sprite templates for trams/road vehicles in eight different vehicle lengths. Licensed GPLv2 or higher.

We'll look at the NML template code that belongs to these sprite templates later. The tram vehicle example that will follow later in this tutorial actually uses some of these sprite templates and code. For other premade templates, you can consider looking into the OpenGFX+ Road Vehicles or OpenGFX+ Trains source.

The actual position of each block of sprites within your graphics file doesn't matter. For this, arguments will be used in the template code. So you can align them nicely in a grid, or just ploink them somewhere randomly, as long as you keep the block of sprites together.


Template block

Let's now focus on the NML code that should go with a sprite template. The general syntax of the template block is as follows:

template <identifier>([<arg1> [, <arg2> [, <arg3>...]]]) {
	<list_of_realsprites>
}

The <identifier> is again a unique name that you choose, although you're encouraged to start it with template_. Behind the identifier, between brackets and separated by commas, you can supply a list of template argument names that you will use as variables in your template. You can provide any number of template arguments, including none at all. Inside the template block you supply a list of realsprites, just like you would in a normal spriteset.

Template argument magic

The beauty of the templates are the template arguments. You can vary their values every time you call a template in your NML code, so the template argument values can be different for every road vehicle. This means that you for instance can make the left_x a variable instead of a fixed value.

Combine this with the fact that you can do any mathematical calculation with template arguments on the values, you can make a template as complex as you want.

Now you might find this very theoretical, so let's look at an example right quick. This will surely help you understand the template concept.

Example NML templates

The template examples below belong to the sprite templates from the image above. From the name of the template you can see which one you need for a specific sprite template. So if you used the 6/8 (24px) sprite template, the template_tram_24 is the one that goes with this.

template template_tram_32(x, y) {
    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [x,         y,          10,        28,           -4,         -11]
    [x+ 20,     y,          26,        28,          -17,         -14]
    [x+ 50,     y,          36,        28,          -18,         -20]
    [x+ 90,     y,          26,        28,           -9,         -15]
    [x+120,     y,          10,        28,           -4,         -11]
    [x+140,     y,          26,        28,          -16,         -16]
    [x+170,     y,          36,        28,          -18,         -20]
    [x+210,     y,          26,        28,           -8,         -16]
}

template template_tram_28(x, y) {
    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [x,         y,          10,        28,           -4,         -11]
    [x+ 20,     y,          26,        28,          -17,         -14]
    [x+ 50,     y,          36,        28,          -20,         -20]
    [x+ 90,     y,          26,        28,           -9,         -15]
    [x+120,     y,          10,        28,           -4,         -13]
    [x+140,     y,          26,        28,          -16,         -16]
    [x+170,     y,          36,        28,          -16,         -20]
    [x+210,     y,          26,        28,           -8,         -16]
}

template template_tram_24(x, y) {
    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [x,         y,          10,        28,           -4,         -11]
    [x+ 20,     y,          26,        28,          -17,         -14]
    [x+ 50,     y,          36,        28,          -22,         -20]
    [x+ 90,     y,          26,        28,           -9,         -15]
    [x+120,     y,          10,        28,           -4,         -15]
    [x+140,     y,          26,        28,          -16,         -16]
    [x+170,     y,          36,        28,          -14,         -20]
    [x+210,     y,          26,        28,           -8,         -16]
}

template template_tram_20(x, y) {
    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [x,         y,          10,        28,           -4,         -10]
    [x+ 20,     y,          26,        28,          -17,         -14]
    [x+ 50,     y,          36,        28,          -24,         -20]
    [x+ 90,     y,          26,        28,           -9,         -15]
    [x+120,     y,          10,        28,           -4,         -16]
    [x+140,     y,          26,        28,          -16,         -16]
    [x+170,     y,          36,        28,          -12,         -20]
    [x+210,     y,          26,        28,           -8,         -16]
}

template template_tram_16(x, y) {
    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [x,         y,          10,        28,           -4,          -9]
    [x+ 20,     y,          26,        28,          -17,         -14]
    [x+ 50,     y,          36,        28,          -26,         -20]
    [x+ 90,     y,          26,        28,           -9,         -15]
    [x+120,     y,          10,        28,           -4,         -17]
    [x+140,     y,          26,        28,          -16,         -16]
    [x+170,     y,          36,        28,          -10,         -20]
    [x+210,     y,          26,        28,           -8,         -16]
}

template template_tram_12(x, y) {
    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [x,         y,          10,        28,           -4,          -8]
    [x+ 20,     y,          26,        28,          -17,         -14]
    [x+ 50,     y,          36,        28,          -28,         -20]
    [x+ 90,     y,          26,        28,           -9,         -15]
    [x+120,     y,          10,        28,           -4,         -18]
    [x+140,     y,          26,        28,          -16,         -16]
    [x+170,     y,          36,        28,           -8,         -20]
    [x+210,     y,          26,        28,           -8,         -16]
}

template template_tram_8(x, y) {
    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [x,         y,          10,        28,           -4,          -7]
    [x+ 20,     y,          26,        28,          -17,         -14]
    [x+ 50,     y,          36,        28,          -30,         -20]
    [x+ 90,     y,          26,        28,           -9,         -15]
    [x+120,     y,          10,        28,           -4,         -19]
    [x+140,     y,          26,        28,          -16,         -16]
    [x+170,     y,          36,        28,           -6,         -20]
    [x+210,     y,          26,        28,           -8,         -16]
}

template template_tram_4(x, y) {
    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [x,         y,          10,        28,           -4,          -6]
    [x+ 20,     y,          26,        28,          -17,         -14]
    [x+ 50,     y,          36,        28,          -32,         -20]
    [x+ 90,     y,          26,        28,           -9,         -15]
    [x+120,     y,          10,        28,           -4,         -20]
    [x+140,     y,          26,        28,          -16,         -16]
    [x+170,     y,          36,        28,           -4,         -20]
    [x+210,     y,          26,        28,           -8,         -16]
}

template template_purchase(x, y) {
    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [x,         y,          50,        12,          -25,          -6]
}

In these templates, there are two template arguments x and y. Whenever you call one of these templates, you specify the horizontal distance between the top left of the graphics file and the top left of the block of sprites as value for x and the vertical distance as value for y. NML will then calculate the left_x for each sprite for you and will insert the upper_y as well.

With some rearranging of the position of the vehicles in their sprite boxes, you could probably come up with a single template that can be used for vehicles of any length, but that is a challenge for you to be accepted.


Calling a template

As with everything, a template can only be called if it's defined. So templates are best placed shortly after the grf block.

Because a template is an alternative for a sprite definition, you call a template inside a spriteset block (or inside replace or replacenew blocks as those also define sprites). You can use multiple templates in a spriteset block or use one template multiple times consecutively.

So for example if you want to use the 6/8 (24px) sprite template from above, you would define the spriteset for that using a template like this:

spriteset(spriteset_tram_example, "gfx/FooBarTramVehicleTemplates.png") {
	template_tram_24(0, 100)
}

This will use 0 for the value of x and 100 for the value of y for the template arguments. Also note that you don't get a semicolon after the call to the template (there are no semicolons inside spriteset blocks).

With template argument values 0 and 100, NML will silently substitute template_tram_24(0, 100) with the recalculated contents from that particular template. The substituted content in this case would be:

    //[left_x,  upper_y,    width,     height,     offset_x,     offset_y]
    [  0,       100,        10,        28,           -4,         -11]
    [ 20,       100,        26,        28,          -17,         -14]
    [ 50,       100,        36,        28,          -22,         -20]
    [ 90,       100,        26,        28,           -9,         -15]
    [120,       100,        10,        28,           -4,         -15]
    [140,       100,        26,        28,          -16,         -16]
    [170,       100,        36,        28,          -14,         -20]
    [210,       100,        26,        28,           -8,         -16]


Remember our road vehicle had two spritesets? These are templatable, so let's do that next!


NML Tutorial: Template