NMLTutorial/Tram purchase menu

From TTWiki
Jump to: navigation, search

The example used here is the HTM 4001 from the Dutch Tram Set. The original code and graphics for this are by FooBar. 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 third part of the tram example. We'll look into some things dealing with the purchase menu.


Purchase menu text

There is a callback that allows adding text to the purchase menu. In the Dutch Tram Set this is used to give you some information on how the vehicles were/are used in real life. For the tram in this example, we already defined such a text in the language files. Let's add it to the purchase menu.

The callback needed for this you must again look up in the NML Documentation and is named additional_text. And it's an easy one at that, because we can just return the string that must be displayed straight from the graphics block:

    graphics {
        additional_text:         return string(STR_DESC_HTM_4001); //Additional text shown in purchase list
        articulated_part:        switch_articulated_htm_4001;
        length:                  switch_length_htm_4001;
        cargo_capacity:          switch_capacity_htm_4001;
        default:                 switch_spriteset_htm_4001;
    }

Callback done. Easy!


Purchase menu sprite

You may remember a separate sprite and template for the purchase menu. In order to use this, you need a spriteset block and reference this from the graphics block. The spriteset is again pretty straightforward:

spriteset(spriteset_purchase_htm_4001, "gfx/htm_4001.png") {
    template_purchase(0, 140)
}

Referencing that from the graphics block isn't too difficult either, as you've done that before:

    graphics {
        purchase:                spriteset_purchase_htm_4001; //Purchase menu sprite
        additional_text:         return string(STR_DESC_HTM_4001); //Additional text shown in purchase list
        articulated_part:        switch_articulated_htm_4001;
        length:                  switch_length_htm_4001;
        cargo_capacity:          switch_capacity_htm_4001;
        default:                 switch_spriteset_htm_4001;
    }

The identifiers are of course the same in the graphics block and in the spriteset block. The spriteset block must be defined before the item block.


Fixing the purchase menu capacity display

Using the cargo capacity callback comes with a side-effect: the capacity display in the purchase menu is broken. The reason for this is that the position_in_consist variable is not available in the purchase menu. As a result, the game can't calculate the capacity for each vehicle part in the purchase menu, you'll get some unknown behaviour and as a result an incorrect capacity in the purchase menu.

The solution to this is to tell the game not to use the capacity from the callback in the purchase menu, but instead to use the capacity from the capacity property (form the property block) in the purchase menu.

The way this solution works is to define a separate cargo capacity callback for the purchase menu, but instead of returning a capacity value linking this callback directly to the purchase menu spriteset. Sounds a bit of a trick solution, and it is. The result is that the cargo capacity callback for the purchase menu will fail and that the cargo capacity property is used instead. And above all it's not a hack, but completely legal NML code.

The callback name for the graphics block to do this is called purchase_cargo_capacity and can be found in the usual place. Your final graphics block will now look like this:

    graphics {
        purchase:                spriteset_purchase_htm_4001; //Purchase menu sprite
        additional_text:         return string(STR_DESC_HTM_4001); //Additional text shown in purchase list
        articulated_part:        switch_articulated_htm_4001;
        length:                  switch_length_htm_4001;
        cargo_capacity:          switch_capacity_htm_4001;
        purchase_cargo_capacity: spriteset_purchase_htm_4001; //<-- linked to the purchase menu spriteset
        default:                 switch_spriteset_htm_4001;
    }

The cargo capacity property will now be applied to each part of the vehicle for display in the purchase menu. In this case, three times the cargo capacity property will be displayed as total capacity in the purchase menu. And this completes the tram example.


The complete code

The complete NML code for the tram vehicle:

// define the newgrf
grf {
	grfid:	"\FB\FB\01\02";
	name:	string(STR_GRF_NAME);
	desc:	string(STR_GRF_DESCRIPTION);
	version:		REPO_REVISION;
	min_compatible_version:	87;
}

////////////////////////
//Purchase menu sprite//
////////////////////////

spriteset(spriteset_purchase_htm_4001, "gfx/htm_4001.png") {
    template_purchase(0, 140)
}

///////////////////
//Vehicle sprites//
///////////////////

spriteset(spriteset_real_htm_4001, "gfx/htm_4001.png") {
    template_tram_28(0, 20)
}
spriteset(spriteset_real_htm_4001_1, "gfx/htm_4001.png") {
    template_tram_24(0, 60)
}
spriteset(spriteset_real_htm_4001_2, "gfx/htm_4001.png") {
    template_tram_28(0, 100)
}

switch (FEAT_ROADVEHS, SELF, switch_spriteset_htm_4001, position_in_consist ) {
    1: spriteset_real_htm_4001_1;
    2: spriteset_real_htm_4001_2;
    spriteset_real_htm_4001; //default
}

/////////////////////
//vehicle callbacks//
/////////////////////

//set number of articulated parts
switch (FEAT_ROADVEHS, SELF, switch_articulated_htm_4001, extra_callback_info1) {
    1..2: return item_tram_htm_4001; //use same vehicle for all parts
    return CB_RESULT_NO_MORE_ARTICULATED_PARTS; //stop adding vehicle parts
}

//set length of each part
switch (FEAT_ROADVEHS, SELF, switch_length_htm_4001, position_in_consist) {
    1: return 6;
    return 7;
}

//set capacity of each part
switch (FEAT_ROADVEHS, SELF, switch_capacity_htm_4001, position_in_consist ) {
    1: return 26;
    return 38; //default
}

//////////////////////
//vehicle properties//
//////////////////////

item (FEAT_ROADVEHS, item_tram_htm_4001) {
    property {
        name:                        string(STR_NAME_HTM_4001);
        introduction_date:           date(2006,1,1);
        model_life:                  VEHICLE_NEVER_EXPIRES;
        retire_early:                0;
        vehicle_life:                25;
        loading_speed:               25;
        cost_factor:                 224;
        running_cost_factor:         112;
        speed:                       81 km/h;
        power:                       966 hp;
        weight:                      58 ton;
        cargo_capacity:              (38*2+26)/3;
        tractive_effort_coefficient: 0.5;
        air_drag_coefficient:        0;
        //sound_effect:              no sound
        //visual_effect:             use default (none)
        
        //callback_flags:            no need to set this
        reliability_decay:           20;
        climates_available:          ALL_CLIMATES;
        refittable_cargo_classes:    bitmask(CC_PASSENGERS);
        sprite_id:                   SPRITE_ID_NEW_ROADVEH; //use custom sprites
        misc_flags:                  bitmask(ROADVEH_FLAG_TRAM); //make this a tram
        refit_cost:                  0; //refits are free
        running_cost_base:           RUNNING_COST_ROADVEH;
    }
    
    graphics {
        purchase:                spriteset_purchase_htm_4001; //Purchase menu sprite
        additional_text:         return string(STR_DESC_HTM_4001); //Additional text shown in purchase list
        articulated_part:        switch_articulated_htm_4001;
        length:                  switch_length_htm_4001;
        cargo_capacity:          switch_capacity_htm_4001;
        purchase_cargo_capacity: spriteset_purchase_htm_4001;
        default:                 switch_spriteset_htm_4001;
    }
}


That concludes the articulated tram example. The next part of this tutorial will a train example with some fancy features.


NML Tutorial: Tram purchase menu