@@ -0,0 +1,347 @@
/*
* This file is aimed to provide an example on how to code a roadtype in NML.
* To keep the code readable, not every property or variable is documented in
* detail, refer to the object-specific reference in the documentation.
*

/**********************************************
* Header, containing some general stuff:
**********************************************/

/*
* First, define a grf block. This defines some basic properties of the grf,
* which are required for the grf to be valid and loadable.
*/
grf {
/* This grf is part of NML, therefore "NML" is chosen as the first three
* characters of the GRFID. It is the fifth real grf defined as part of
* NML, therefore the last character is set to 4. Successive grfs will
* have 5, 6, etc. there, to make sure each example grf has a unique GRFID.
*/
grfid : "NML\04";
name : string(STR_GRF_NAME);
desc : string(STR_GRF_DESCRIPTION);
version : 0; // must be numeric
min_compatible_version : 0;
}

/* Default ground tile template (re-use as needed) */
template ground_tile(x, y) { [x, y, 64, 31, -31, 0] }

/**********************************************
* Road underlays (tracks + ballast):
**********************************************/
/* Template for underlays; 2x straight road, 5x junctions, 4x corners, 4x slope, 4x half-tile road */

/* Used for bridge surfaces also, therefore the template is split */
template tmpl_underlay_straight() {
ground_tile( 0, 0)
ground_tile(75, 0)
}
template tmpl_underlay_junctions() {
ground_tile(150, 0)
ground_tile(225, 0)
ground_tile(300, 0)
ground_tile(375, 0)
ground_tile(450, 0)
}
template tmpl_underlay_corners() {
ground_tile( 0, 40)
ground_tile( 75, 40)
ground_tile(150, 40)
ground_tile(225, 40)
}

template tmpl_underlay_slope() {
[300, 40, 64, 39, -31, -8]
[375, 40, 64, 23, -31, 0]
[450, 40, 64, 23, -31, 0]
[525, 40, 64, 39, -31, -8]
}

template tmpl_underlay_half_tiles() {
ground_tile( 0, 80)
ground_tile( 75, 80)
ground_tile(150, 80)
ground_tile(225, 80)
}


template tmpl_underlay_roadtypes() {
tmpl_underlay_straight()
tmpl_underlay_junctions()
tmpl_underlay_corners()
tmpl_underlay_slope()
tmpl_underlay_half_tiles()

/* X-crossing */
ground_tile(0, 120)

/* underlay for crossings w/o tracks */
ground_tile( 0, 80)
ground_tile(225, 80)
ground_tile(150, 80)
ground_tile( 75, 80)
ground_tile(300, 80)
}
/* Spriteset containing all underlays */
spriteset(track_underlays, "gfx/roads_underlay.png") {
tmpl_underlay_roadtypes()
}

/**********************************************
* Track overlays (tracks without ballast):
**********************************************/
template tmpl_overlay_roadtypes() {
[ 0, 0, 64, 31, -31, 0]
[75, 0, 64, 31, -31, 0]

[150, 0, 64, 31, -31, 0]
[225, 0, 64, 31, -31, 0]
[300, 0, 64, 31, -31, 0]
[375, 0, 64, 31, -31, 0]
[450, 0, 64, 31, -31, 0]

[ 0, 40, 64, 31, -31, 0]
[ 75, 40, 64, 31, -31, 0]
[150, 40, 64, 31, -31, 0]
[225, 40, 64, 31, -31, 0]

[300, 40, 64, 39, -31, -8]
[375, 40, 64, 21, -31, 0]
[450, 40, 64, 21, -31, 0]
[525, 40, 64, 39, -31, -8]

[ 0, 80, 64, 31, -31, 0]
[ 75, 80, 64, 31, -31, 0]
[150, 80, 64, 31, -31, 0]
[225, 80, 64, 31, -31, 0]
}
/* Spriteset for overlays */
spriteset(road_overlays_red, "gfx/roads_red.png") {
tmpl_overlay_roadtypes()
}
/* Spriteset for overlays */
spriteset(road_overlays_blue, "gfx/roads_blue.png") {
tmpl_overlay_roadtypes()
}
/* Spriteset for overlays */
spriteset(road_overlays_yellow, "gfx/roads_yellow.png") {
tmpl_overlay_roadtypes()
}
/* Spriteset for overlays */
spriteset(tram_overlays_green, "gfx/tram_green.png") {
tmpl_overlay_roadtypes()
}

/**********************************************
* Depots:
**********************************************/
/* Template for depot sprites */
template tmpl_depot() {
[200, 10, 16, 8, 17, 7+4]
[118, 8, 64, 47, -9+8, -31]
[ 0, 10, 16, 8, -31, 7+4]
[ 37, 8, 64, 47, -53-8, -31]
[ 37, 63, 64, 47, -53-8, -31]
[118, 63, 64, 47, -9+8, -31]
}

/* Depots */
spriteset(depot_normal_road, "gfx/depot_normal.png") {
tmpl_depot()
}


/**********************************************
* Bridge surfaces:
**********************************************/
/* Bridge surface, uses the same sprites as track underlays, but in a different order */
template tmpl_bridges_underlay() {
tmpl_underlay_straight()
tmpl_underlay_slope()
tmpl_underlay_junctions()
}
/* Spriteset for bridge surfaces */
spriteset(bridge_underlay, "gfx/roads_red.png") {
tmpl_bridges_underlay()
}

/**********************************************
* GUI sprites:
**********************************************/

/* Template for a single icon sprite */
template tmpl_gui_icon(x, y) {
[x, y, 20, 20, 0, 0]
}

/* Template for a single cursor sprite */
template tmpl_gui_cursor(x, y) {
[x, y, 32, 32, 0, 0]
}
/* Template for all the GUI sprites (8 icons + 8 cursors) */
template tmpl_gui() {
tmpl_gui_icon( 0, 0)
tmpl_gui_icon( 25, 0)
tmpl_gui_icon( 50, 0)
tmpl_gui_icon( 75, 0)
tmpl_gui_icon(100, 0)
tmpl_gui_icon(125, 0)
tmpl_gui_icon(150, 0)
tmpl_gui_icon(175, 0)

tmpl_gui_cursor(200, 0)
tmpl_gui_cursor(250, 0)
tmpl_gui_cursor(300, 0)
tmpl_gui_cursor(350, 0)
tmpl_gui_cursor(400, 0)
tmpl_gui_cursor(450, 0)
tmpl_gui_cursor(500, 0)
tmpl_gui_cursor(550, 0)
}

/* Spritesets for the normal and electric GUI */
spriteset(gui_normal, "gfx/gui_rail.png") {
tmpl_gui()
}

/**********************************************
* Roadstop sprites:
**********************************************/

template tmpl_underlay_roadstop() {
ground_tile( 0, 120)
ground_tile( 75, 120)
ground_tile(150, 120)
ground_tile(225, 120)
}

spriteset(roadstop_underlay_red, "gfx/roads_red.png") {
tmpl_underlay_roadstop()
}
spriteset(roadstop_underlay_blue, "gfx/roads_blue.png") {
tmpl_underlay_roadstop()
}
spriteset(roadstop_underlay_yellow, "gfx/roads_yellow.png") {
tmpl_underlay_roadstop()
}

/**********************************************
* Roadtype definitions:
**********************************************/

item(FEAT_ROADTYPES, red_road, 5) {
/* Set only the most essential properties,
* Few compatible roadtypes are defined as there are no other sets out there yet */
property {
name: string(STR_NAME_RED_ROAD);
label: "REDR";
powered_roadtype_list: ["BLUE", "REDR", "ROAD", "ELRD"];
toolbar_caption: string(STR_TOOLBAR_CAPTION_RED_ROAD);
menu_text: string(STR_MENU_TEXT_RED_ROAD);
build_window_caption: string(STR_BUILD_WINDOW_CAPTION_RED_ROAD);
autoreplace_text: string(STR_AUTOREPLACE_TEXT_RED_ROAD);
new_engine_text: string(STR_NEW_ENGINE_TEXT_RED_ROAD);
roadtype_flags: bitmask(ROADTYPE_FLAG_CATENARY);
sort_order: 101;
}

/* Associate graphics with this roadtype */
graphics {
track_overlay: road_overlays_red;
underlay: track_underlays;
depots: depot_normal_road;
bridge_surfaces: bridge_underlay;
roadstops: roadstop_underlay_red;
// gui: gui_normal;
/* Catenary is not not implemented here, use the default */
}
}

item(FEAT_ROADTYPES, blue_road, 6) {
/* Set only the most essential properties,
* Few compatible roadtypes are defined as there are no other sets out there yet */
property {
name: string(STR_NAME_BLUE_ROAD);
label: "BLUE";
powered_roadtype_list: ["ROAD"];
toolbar_caption: string(STR_TOOLBAR_CAPTION_BLUE_ROAD);
menu_text: string(STR_MENU_TEXT_BLUE_ROAD);
build_window_caption: string(STR_BUILD_WINDOW_CAPTION_BLUE_ROAD);
autoreplace_text: string(STR_AUTOREPLACE_TEXT_BLUE_ROAD);
new_engine_text: string(STR_NEW_ENGINE_TEXT_BLUE_ROAD);
roadtype_flags: 0;
sort_order: 99;
}

/* Associate graphics with this roadtype */
graphics {
track_overlay: road_overlays_blue;
underlay: track_underlays;
depots: depot_normal_road;
bridge_surfaces: bridge_underlay;
roadstops: roadstop_underlay_blue;
// gui: gui_normal;
/* Catenary is not not implemented here, use the default */
}
}

item(FEAT_ROADTYPES, yellow_road, 7) {
/* Set only the most essential properties,
* Few compatible roadtypes are defined as there are no other sets out there yet */
property {
name: string(STR_NAME_YELLOW_ROAD);
label: "YELL";
powered_roadtype_list: ["ROAD"];
toolbar_caption: string(STR_TOOLBAR_CAPTION_YELLOW_ROAD);
menu_text: string(STR_MENU_TEXT_YELLOW_ROAD);
build_window_caption: string(STR_BUILD_WINDOW_CAPTION_YELLOW_ROAD);
autoreplace_text: string(STR_AUTOREPLACE_TEXT_YELLOW_ROAD);
new_engine_text: string(STR_NEW_ENGINE_TEXT_YELLOW_ROAD);
roadtype_flags: 0;
sort_order: 100;
}

/* Associate graphics with this roadtype */
graphics {
track_overlay: road_overlays_yellow;
underlay: track_underlays;
depots: depot_normal_road;
bridge_surfaces: bridge_underlay;
roadstops: roadstop_underlay_yellow;
// gui: gui_normal;
/* Catenary is not not implemented here, use the default */
}
}
/**********************************************
* Tramtype definitions:
**********************************************/

item(FEAT_TRAMTYPES, green_tram, 9) {
/* Set only the most essential properties,
* Few compatible tramtypes are defined as there are no other sets out there yet */
property {
name: string(STR_NAME_GREEN_TRAM);
label: "GRTR";
powered_tramtype_list: ["TRAM"];
toolbar_caption: string(STR_TOOLBAR_CAPTION_GREEN_TRAM);
menu_text: string(STR_MENU_TEXT_GREEN_TRAM);
build_window_caption: string(STR_BUILD_WINDOW_CAPTION_GREEN_TRAM);
autoreplace_text: string(STR_AUTOREPLACE_TEXT_GREEN_TRAM);
new_engine_text: string(STR_NEW_ENGINE_TEXT_GREEN_TRAM);
tramtype_flags: 0;
}

/* Associate graphics with this roadtype */
graphics {
track_overlay: tram_overlays_green;
underlay: track_underlays;
depots: depot_normal_road;
bridge_surfaces: bridge_underlay;
/* Catenary is not not implemented here, use the default */
}
}



Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,31 @@
##grflangid 0x01
STR_GRF_NAME :NML Example NewGRF: Roadtype and Tramtype
STR_GRF_DESCRIPTION :{ORANGE}NML Example NewGRF: Roadtype and Tramtype{}{BLACK}This NewGRF is intended to provide a coding example for the high-level NewGRF-coding language NML.{}Original graphics by {SILVER}Irwe, {BLACK}coding by {SILVER}andythenorth.

STR_NAME_RED_ROAD :(Name 0x1B) Red Electric Road
STR_TOOLBAR_CAPTION_RED_ROAD :(Toolbar Caption 0x09) Red Electric Road
STR_MENU_TEXT_RED_ROAD :(Menu Text 0x0A) Red Electric Road
STR_BUILD_WINDOW_CAPTION_RED_ROAD :(Build Window Caption 0x0B) Red Electric Road
STR_AUTOREPLACE_TEXT_RED_ROAD :(Autoreplace Text 0x0C) Red Electric Road
STR_NEW_ENGINE_TEXT_RED_ROAD :(New Engine Text 0x0D) red Electric Road

STR_NAME_BLUE_ROAD :(Name 0x1B) Blue Road
STR_TOOLBAR_CAPTION_BLUE_ROAD :(Toolbar Caption 0x09) Blue Road
STR_MENU_TEXT_BLUE_ROAD :(Menu Text 0x0A) Blue Road
STR_BUILD_WINDOW_CAPTION_BLUE_ROAD :(Build Window Caption 0x0B) Blue Road
STR_AUTOREPLACE_TEXT_BLUE_ROAD :(Autoreplace Text 0x0C) Blue Road
STR_NEW_ENGINE_TEXT_BLUE_ROAD :(New Engine Text 0x0D) Blue Road

STR_NAME_YELLOW_ROAD :(Name 0x1B) Yellow Road
STR_TOOLBAR_CAPTION_YELLOW_ROAD :(Toolbar Caption 0x09) Yellow Road
STR_MENU_TEXT_YELLOW_ROAD :(Menu Text 0x0A) Yellow Road
STR_BUILD_WINDOW_CAPTION_YELLOW_ROAD :(Build Window Caption 0x0B) Yellow Road
STR_AUTOREPLACE_TEXT_YELLOW_ROAD :(Autoreplace Text 0x0C) Yellow Road
STR_NEW_ENGINE_TEXT_YELLOW_ROAD :(New Engine Text 0x0D) Yellow Road

STR_NAME_GREEN_TRAM :(Name 0x1B) Green Tram
STR_TOOLBAR_CAPTION_GREEN_TRAM :(Toolbar Caption 0x09) Green Tram
STR_MENU_TEXT_GREEN_TRAM :(Menu Text 0x0A) Green Tram
STR_BUILD_WINDOW_CAPTION_GREEN_TRAM :(Build Window Caption 0x0B) Green Tram
STR_AUTOREPLACE_TEXT_GREEN_TRAM :(Autoreplace Text 0x0C) Green Tram
STR_NEW_ENGINE_TEXT_GREEN_TRAM :(New Engine Text 0x0D) Green Tram
@@ -204,6 +204,8 @@ def find_unused(self, length):
BlockAllocation( 0, 255, "Object"),
BlockAllocation( 0, 15, "Railtype"),
BlockAllocation( 0, 255, "Airport Tile"),
BlockAllocation( 0, 15, "Roadtype"),
BlockAllocation( 0, 15, "Tramtype"),
]

def print_stats():
@@ -735,6 +737,68 @@ def get_railtypelist_action(railtype_list):
action6.free_parameters.restore()
return action_list

def get_roadtypelist_action(roadtype_list):
action6.free_parameters.save()
act6 = action6.Action6()

action_list = []
action0, offset = create_action0(0x08, expression.ConstantNumeric(0), act6, action_list)
id_table = []
offset += 1 # Skip property number
for roadtype in roadtype_list:
if isinstance(roadtype, expression.StringLiteral):
id_table.append(roadtype)
offset+=4
continue
param, extra_actions = actionD.get_tmp_parameter(expression.ConstantNumeric(expression.parse_string_to_dword(roadtype[-1])))
action_list.extend(extra_actions)
for idx in range(len(roadtype)-2, -1, -1):
val = expression.ConstantNumeric(expression.parse_string_to_dword(roadtype[idx]))
action_list.append(action7.SkipAction(0x09, 0x00, 4, (0x0F, None), val.value, 1))
action_list.append(actionD.ActionD(expression.ConstantNumeric(param), expression.ConstantNumeric(0xFF), nmlop.ASSIGN, expression.ConstantNumeric(0xFF), val))
act6.modify_bytes(param, 4, offset)
id_table.append(expression.StringLiteral(r"\00\00\00\00", None))
offset += 4
action0.prop_list.append(IDListProp(0x16, id_table))
action0.num_ids = len(roadtype_list)

if len(act6.modifications) > 0: action_list.append(act6)

action_list.append(action0)
action6.free_parameters.restore()
return action_list

def get_tramtypelist_action(tramtype_list):
action6.free_parameters.save()
act6 = action6.Action6()

action_list = []
action0, offset = create_action0(0x08, expression.ConstantNumeric(0), act6, action_list)
id_table = []
offset += 1 # Skip property number
for tramtype in tramtype_list:
if isinstance(tramtype, expression.StringLiteral):
id_table.append(tramtype)
offset+=4
continue
param, extra_actions = actionD.get_tmp_parameter(expression.ConstantNumeric(expression.parse_string_to_dword(tramtype[-1])))
action_list.extend(extra_actions)
for idx in range(len(tramtype)-2, -1, -1):
val = expression.ConstantNumeric(expression.parse_string_to_dword(tramtype[idx]))
action_list.append(action7.SkipAction(0x09, 0x00, 4, (0x11, None), val.value, 1))
action_list.append(actionD.ActionD(expression.ConstantNumeric(param), expression.ConstantNumeric(0xFF), nmlop.ASSIGN, expression.ConstantNumeric(0xFF), val))
act6.modify_bytes(param, 4, offset)
id_table.append(expression.StringLiteral(r"\00\00\00\00", None))
offset += 4
action0.prop_list.append(IDListProp(0x17, id_table))
action0.num_ids = len(tramtype_list)

if len(act6.modifications) > 0: action_list.append(act6)

action_list.append(action0)
action6.free_parameters.restore()
return action_list

class ByteListProp(BaseAction0Property):
def __init__(self, prop_num, data):
self.prop_num = prop_num
@@ -151,7 +151,7 @@ def get_size(self):
#
# 'first' (value doesn't matter) if the property should be set first (generally a substitute type)

properties = 0x12 * [None]
properties = 0x14 * [None]

#
# Some helper functions that are used for multiple features
@@ -361,6 +361,8 @@ def roadveh_speed_prop(prop_info):
return [prop08, prop15]

properties[0x01] = {
'road_type' : {'size': 1, 'num': 0x05},
'tram_type' : {'size': 1, 'num': 0x05},
'speed' : roadveh_speed_prop({'unit_type': 'speed', 'unit_conversion': (10000, 1397), 'adjust_value': lambda val, unit: ottd_display_speed(val, 2, unit)}),
'running_cost_factor' : {'size': 1, 'num': 0x09},
'running_cost_base' : {'size': 4, 'num': 0x0A},
@@ -1022,3 +1024,97 @@ def railtype_list(value, prop_num):
'animation_speed' : {'size': 1, 'num': 0x10},
'animation_triggers' : {'size': 1, 'num': 0x11},
}

#
# Feature 0x12 (Road Types)
#

class RoadtypeListProp(BaseAction0Property):
def __init__(self, prop_num, roadtype_list):
self.prop_num = prop_num
self.roadtype_list = roadtype_list

def write(self, file):
file.print_bytex(self.prop_num)
file.print_byte(len(self.roadtype_list))
for roadtype in self.roadtype_list:
roadtype.write(file, 4)
file.newline()

def get_size(self):
return len(self.roadtype_list) * 4 + 2

def roadtype_list(value, prop_num):
if not isinstance(value, Array):
raise generic.ScriptError("Roadtype list must be an array of literal strings", value.pos)
for val in value.values:
if not isinstance(val, StringLiteral): raise generic.ScriptError("Roadtype list must be an array of literal strings", val.pos)
return [RoadtypeListProp(prop_num, value.values)]

properties[0x12] = {
'label' : {'size': 4, 'num': 0x08, 'string_literal': 4}, # is allocated during reservation stage, setting label first is thus not needed
'toolbar_caption' : {'size': 2, 'num': 0x09, 'string': 0xDC},
'menu_text' : {'size': 2, 'num': 0x0A, 'string': 0xDC},
'build_window_caption' : {'size': 2, 'num': 0x0B, 'string': 0xDC},
'autoreplace_text' : {'size': 2, 'num': 0x0C, 'string': 0xDC},
'new_engine_text' : {'size': 2, 'num': 0x0D, 'string': 0xDC},
'powered_roadtype_list' : {'custom_function': lambda x: roadtype_list(x, 0x0F)},
'roadtype_flags' : {'size': 1, 'num': 0x10},
'construction_cost' : {'size': 2, 'num': 0x13},
'speed_limit' : {'size': 2, 'num': 0x14, 'unit_type': 'speed', 'unit_conversion': (10000, 1397)},
'map_colour' : {'size': 1, 'num': 0x16},
'introduction_date' : {'size': 4, 'num': 0x17},
'requires_roadtype_list' : {'custom_function': lambda x: roadtype_list(x, 0x18)},
'introduces_roadtype_list' : {'custom_function': lambda x: roadtype_list(x, 0x19)},
'sort_order' : {'size': 1, 'num': 0x1A},
'name' : {'size': 2, 'num': 0x1B, 'string': 0xDC},
'maintenance_cost' : {'size': 2, 'num': 0x1C},
'alternative_roadtype_list': {'custom_function': lambda x: roadtype_list(x, 0x1D)},
}

#
# Feature 0x13 (Tram Types)
#

class TramtypeListProp(BaseAction0Property):
def __init__(self, prop_num, tramtype_list):
self.prop_num = prop_num
self.tramtype_list = tramtype_list

def write(self, file):
file.print_bytex(self.prop_num)
file.print_byte(len(self.tramtype_list))
for tramtype in self.tramtype_list:
tramtype.write(file, 4)
file.newline()

def get_size(self):
return len(self.tramtype_list) * 4 + 2

def tramtype_list(value, prop_num):
if not isinstance(value, Array):
raise generic.ScriptError("Tramtype list must be an array of literal strings", value.pos)
for val in value.values:
if not isinstance(val, StringLiteral): raise generic.ScriptError("Tramtype list must be an array of literal strings", val.pos)
return [TramtypeListProp(prop_num, value.values)]

properties[0x13] = {
'label' : {'size': 4, 'num': 0x08, 'string_literal': 4}, # is allocated during reservation stage, setting label first is thus not needed
'toolbar_caption' : {'size': 2, 'num': 0x09, 'string': 0xDC},
'menu_text' : {'size': 2, 'num': 0x0A, 'string': 0xDC},
'build_window_caption' : {'size': 2, 'num': 0x0B, 'string': 0xDC},
'autoreplace_text' : {'size': 2, 'num': 0x0C, 'string': 0xDC},
'new_engine_text' : {'size': 2, 'num': 0x0D, 'string': 0xDC},
'powered_tramtype_list' : {'custom_function': lambda x: tramtype_list(x, 0x0F)},
'tramtype_flags' : {'size': 1, 'num': 0x10},
'construction_cost' : {'size': 2, 'num': 0x13},
'speed_limit' : {'size': 2, 'num': 0x14, 'unit_type': 'speed', 'unit_conversion': (10000, 1397)},
'map_colour' : {'size': 1, 'num': 0x16},
'introduction_date' : {'size': 4, 'num': 0x17},
'requires_tramtype_list' : {'custom_function': lambda x: tramtype_list(x, 0x18)},
'introduces_tramtype_list' : {'custom_function': lambda x: tramtype_list(x, 0x19)},
'sort_order' : {'size': 1, 'num': 0x1A},
'name' : {'size': 2, 'num': 0x1B, 'string': 0xDC},
'maintenance_cost' : {'size': 2, 'num': 0x1C},
'alternative_tramtype_list': {'custom_function': lambda x: tramtype_list(x, 0x1D)},
}
@@ -193,8 +193,8 @@ def free_references(source_action):
act2.num_refs -= 1
if act2.num_refs == 0: free_action2_ids.append(act2.id)

# Features using sprite groups directly: vehicles, canals, cargos, railtypes, airports
features_sprite_group = [0x00, 0x01, 0x02, 0x03, 0x05, 0x0B, 0x0D, 0x10]
# Features using sprite groups directly: vehicles, canals, cargos, railtypes, airports, roadtypes, tramtypes
features_sprite_group = [0x00, 0x01, 0x02, 0x03, 0x05, 0x0B, 0x0D, 0x10, 0x12, 0x13]
# Features using sprite layouts: stations, houses, industry tiles, objects and airport tiles
features_sprite_layout = [0x04, 0x07, 0x09, 0x0F, 0x11]
# All features that need sprite sets
@@ -15,11 +15,11 @@

from nml import expression, nmlop, generic

# Use feature 0x12 for towns (accessible via station/house/industry parent scope)
varact2vars = 0x13 * [{}]
varact2vars60x = 0x13 * [{}]
# feature number: 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12
varact2parent_scope = [0x00, 0x01, 0x02, 0x03, 0x12, None, 0x12, 0x12, None, 0x0A, 0x12, None, None, None, None, 0x12, None, None, None]
# Use feature 0x14 for towns (accessible via station/house/industry parent scope)
varact2vars = 0x15 * [{}]
varact2vars60x = 0x15 * [{}]
# feature number: 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13
varact2parent_scope = [0x00, 0x01, 0x02, 0x03, 0x14, None, 0x14, 0x14, None, 0x0A, 0x14, None, None, None, None, 0x14, None, None, None, None]

def default_60xvar(name, args, pos, info):
"""
@@ -148,7 +148,6 @@ def unsigned_tile_offset(name, args, pos, info):
'vehicle_is_testing' : {'var': 0x48, 'start': 1, 'size': 1},
'vehicle_is_offered' : {'var': 0x48, 'start': 2, 'size': 1},
'build_year' : {'var': 0x49, 'start': 0, 'size': 32},
'current_railtype' : {'var': 0x4A, 'start': 0, 'size': 8},
'vehicle_is_potentially_powered' : {'var': 0x4A, 'start': 8, 'size': 1},
'date_of_last_service' : {'var': 0x4B, 'start': 0, 'size': 32},
'position_in_articulated_veh' : {'var': 0x4D, 'start': 0, 'size': 8},
@@ -183,6 +182,7 @@ def unsigned_tile_offset(name, args, pos, info):
#for train speed
'max_speed' : {'var': 0x98, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x4786, 0x10000)},
'current_speed' : {'var': 0xB4, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x4786, 0x10000)},
'current_railtype' : {'var': 0x4A, 'start': 0, 'size': 8},
'current_max_speed' : {'var': 0x4C, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x4786, 0x10000)},
'vehicle_is_in_depot' : {'var': 0xE2, 'start': 7, 'size': 1},
}
@@ -193,6 +193,8 @@ def unsigned_tile_offset(name, args, pos, info):
#for road vehicle speed
'max_speed' : {'var': 0x98, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x23C3, 0x10000)},
'current_speed' : {'var': 0xB4, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x23C3, 0x10000)},
'current_roadtype' : {'var': 0x4A, 'start': 0, 'size': 8},
'current_tramtype' : {'var': 0x4A, 'start': 0, 'size': 8},
'current_max_speed' : {'var': 0x4C, 'start': 0, 'size': 16, 'value_function': value_mul_div(0x23C3, 0x10000)},
'vehicle_is_in_depot' : {'var': 0xE2, 'start': 0, 'size': 8, 'value_function': value_equals(0xFE)},
}
@@ -684,6 +686,35 @@ def industry_town_count(name, args, pos, info):
'nearby_tile_airporttile_id' : {'var': 0x62, 'start': 0, 'size': 16, 'param_function': signed_tile_offset},
}

#
# Roadtypes (feature 0x12)
#

varact2vars_roadtype = {
'terrain_type' : {'var': 0x40, 'start': 0, 'size': 8},
'enhanced_tunnels' : {'var': 0x41, 'start': 0, 'size': 8},
'level_crossing_status' : {'var': 0x42, 'start': 0, 'size': 8},
'build_date' : {'var': 0x43, 'start': 0, 'size': 32},
'town_zone' : {'var': 0x44, 'start': 0, 'size': 8},
'random_bits' : {'var': 0x5F, 'start': 8, 'size': 2},
}
# Roadtypes have no 60+x variables

#
# Tramtypes (feature 0x13)
#

varact2vars_tramtype = {
'terrain_type' : {'var': 0x40, 'start': 0, 'size': 8},
'enhanced_tunnels' : {'var': 0x41, 'start': 0, 'size': 8},
'level_crossing_status' : {'var': 0x42, 'start': 0, 'size': 8},
'build_date' : {'var': 0x43, 'start': 0, 'size': 32},
'town_zone' : {'var': 0x44, 'start': 0, 'size': 8},
'random_bits' : {'var': 0x5F, 'start': 8, 'size': 2},
}
# Tramtypes have no 60+x variables


#
# Towns are not a true feature, but accessible via the parent scope of e.g. industries, stations
#
@@ -729,4 +760,6 @@ def industry_town_count(name, args, pos, info):
varact2vars[0x10] = varact2vars_railtype
varact2vars[0x11] = varact2vars_airporttiles
varact2vars60x[0x11] = varact2vars60x_airporttiles
varact2vars[0x12] = varact2vars_towns
varact2vars[0x12] = varact2vars_roadtype
varact2vars[0x13] = varact2vars_tramtype
varact2vars[0x14] = varact2vars_towns
@@ -15,7 +15,7 @@

from nml import expression, nmlop

callbacks = 0x12 * [{}]
callbacks = 0x14 * [{}]

# Possible values for 'purchase':
# 0 (or not set): not called from purchase list
@@ -274,3 +274,28 @@ def cargo_profit_value(value):
'anim_speed' : {'type': 'cb', 'num': 0x154, 'flag_bit': 1},
'default' : {'type': 'cargo', 'num': None},
}

# Roadtypes
callbacks[0x12] = {
# No default here, it makes no sense
'gui' : {'type': 'cargo', 'num': 0x00},
'track_overlay' : {'type': 'cargo', 'num': 0x01},
'underlay' : {'type': 'cargo', 'num': 0x02},
'catenary_front' : {'type': 'cargo', 'num': 0x04},
'catenary_back' : {'type': 'cargo', 'num': 0x05},
'bridge_surfaces' : {'type': 'cargo', 'num': 0x06},
'depots' : {'type': 'cargo', 'num': 0x08},
'roadstops' : {'type': 'cargo', 'num': 0x0A},
}

# Tramtypes
callbacks[0x13] = {
# No default here, it makes no sense
'gui' : {'type': 'cargo', 'num': 0x00},
'track_overlay' : {'type': 'cargo', 'num': 0x01},
'underlay' : {'type': 'cargo', 'num': 0x02},
'catenary_front' : {'type': 'cargo', 'num': 0x04},
'catenary_back' : {'type': 'cargo', 'num': 0x05},
'bridge_surfaces' : {'type': 'cargo', 'num': 0x06},
'depots' : {'type': 'cargo', 'num': 0x08},
}
@@ -35,6 +35,8 @@
'FEAT_OBJECTS': 0x0F,
'FEAT_RAILTYPES': 0x10,
'FEAT_AIRPORTTILES': 0x11,
'FEAT_ROADTYPES': 0x12,
'FEAT_TRAMTYPES': 0x13,
}

def feature_name(feature):
@@ -0,0 +1,63 @@
__license__ = """
NML is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
NML is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""

from nml import generic, global_constants, expression
from nml.ast import assignment
from nml.actions import action0
from nml.ast import base_statement

class RoadtypeTable(base_statement.BaseStatement):
def __init__(self, roadtype_list, pos):
base_statement.BaseStatement.__init__(self, "road type table", pos, False, False)
self.roadtype_list = roadtype_list
generic.OnlyOnce.enforce(self, "road type table")
global_constants.is_default_roadtype_table = False
global_constants.roadtype_table.clear()

def register_names(self):
for i, roadtype in enumerate(self.roadtype_list):
if isinstance(roadtype, assignment.Assignment):
name = roadtype.name
val_list = []
for rt in roadtype.value:
if isinstance(rt, expression.Identifier):
val_list.append(expression.StringLiteral(rt.value, rt.pos))
else:
val_list.append(rt)
expression.parse_string_to_dword(val_list[-1]) # we don't care about the result, only validate the input
self.roadtype_list[i] = val_list if len(val_list) > 1 else val_list[0]
else:
name = roadtype
if isinstance(roadtype, expression.Identifier):
self.roadtype_list[i] = expression.StringLiteral(roadtype.value, roadtype.pos)
expression.parse_string_to_dword(self.roadtype_list[i]) # we don't care about the result, only validate the input
global_constants.roadtype_table[name.value] = i

def pre_process(self):
pass

def debug_print(self, indentation):
generic.print_dbg(indentation, 'Roadtype table')
for roadtype in self.roadtype_list:
generic.print_dbg(indentation + 2, 'Roadtype: ', roadtype.value)

def get_action_list(self):
return action0.get_roadtypelist_action(self.roadtype_list)

def __str__(self):
ret = 'roadtypetable {\n'
ret += ', '.join([expression.identifier_to_print(roadtype.value) for roadtype in self.roadtype_list])
ret += '\n}\n'
return ret
@@ -0,0 +1,63 @@
__license__ = """
NML is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
NML is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with NML; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""

from nml import generic, global_constants, expression
from nml.ast import assignment
from nml.actions import action0
from nml.ast import base_statement

class TramtypeTable(base_statement.BaseStatement):
def __init__(self, tramtype_list, pos):
base_statement.BaseStatement.__init__(self, "tram type table", pos, False, False)
self.tramtype_list = tramtype_list
generic.OnlyOnce.enforce(self, "tram type table")
global_constants.is_default_tramtype_table = False
global_constants.tramtype_table.clear()

def register_names(self):
for i, tramtype in enumerate(self.tramtype_list):
if isinstance(tramtype, assignment.Assignment):
name = tramtype.name
val_list = []
for rt in tramtype.value:
if isinstance(rt, expression.Identifier):
val_list.append(expression.StringLiteral(rt.value, rt.pos))
else:
val_list.append(rt)
expression.parse_string_to_dword(val_list[-1]) # we don't care about the result, only validate the input
self.tramtype_list[i] = val_list if len(val_list) > 1 else val_list[0]
else:
name = tramtype
if isinstance(tramtype, expression.Identifier):
self.tramtype_list[i] = expression.StringLiteral(tramtype.value, tramtype.pos)
expression.parse_string_to_dword(self.tramtype_list[i]) # we don't care about the result, only validate the input
global_constants.tramtype_table[name.value] = i

def pre_process(self):
pass

def debug_print(self, indentation):
generic.print_dbg(indentation, 'Tramtype table')
for tramtype in self.tramtype_list:
generic.print_dbg(indentation + 2, 'Tramtype: ', tramtype.value)

def get_action_list(self):
return action0.get_tramtypelist_action(self.tramtype_list)

def __str__(self):
ret = 'tramtypetable {\n'
ret += ', '.join([expression.identifier_to_print(tramtype.value) for tramtype in self.tramtype_list])
ret += '\n}\n'
return ret
@@ -55,6 +55,8 @@
action2var_variables.varact2vars_objects,
action2var_variables.varact2vars60x_objects,
action2var_variables.varact2vars_railtype,
action2var_variables.varact2vars_roadtype,
action2var_variables.varact2vars_tramtype,
action2var_variables.varact2vars_airporttiles,
action2var_variables.varact2vars60x_airporttiles,
action2var_variables.varact2vars_towns]
@@ -78,7 +80,9 @@
action0properties.properties[0x0D],
action0properties.properties[0x0F],
action0properties.properties[0x10],
action0properties.properties[0x11]]
action0properties.properties[0x11],
action0properties.properties[0x12],
action0properties.properties[0x13]]

properties = set()
for d in prop_tables:
@@ -103,7 +107,9 @@
action3_callbacks.callbacks[0x0D],
action3_callbacks.callbacks[0x0F],
action3_callbacks.callbacks[0x10],
action3_callbacks.callbacks[0x11]]
action3_callbacks.callbacks[0x11],
action3_callbacks.callbacks[0x12],
action3_callbacks.callbacks[0x13]]

callbacks = set()
for d in cb_tables:
@@ -132,7 +138,9 @@
global_constants.patch_variables,
global_constants.config_flags,
global_constants.unified_maglev_var,
global_constants.railtype_table]
global_constants.railtype_table,
global_constants.roadtype_table,
global_constants.tramtype_table]

constant_names = set()
for d in const_tables:
@@ -38,7 +38,7 @@

def identifier_to_print(name):
"""
Check whether the given name is a valid 4 letter identifier to print (for cargoes and railtypes).
Check whether the given name is a valid 4 letter identifier to print (for cargoes, railtypes, roadtypes and tramtypes).
@param name: Name to check.
@return The identifier itself, if it is a valid name, else a string literal text with the name.
@@ -292,7 +292,7 @@ def builtin_cargotype_available(name, args, pos):

def builtin_railtype_available(name, args, pos):
"""
railtype_available(cargo_label) builtin function.
railtype_available(railtype_label) builtin function.
@return 1 if the railtype label is available, 0 otherwise.
"""
@@ -301,6 +301,28 @@ def builtin_railtype_available(name, args, pos):
label = args[0].reduce()
return SpecialCheck((0x0D, None), 0, (0, 1), parse_string_to_dword(label), "{}({})".format(name, str(label)), pos = args[0].pos)

def builtin_roadtype_available(name, args, pos):
"""
roadtype_available(roadtype_label) builtin function.
@return 1 if the roadtype label is available, 0 otherwise.
"""
if len(args) != 1:
raise generic.ScriptError(name + "() must have exactly 1 parameter", pos)
label = args[0].reduce()
return SpecialCheck((0x0F, None), 0, (0, 1), parse_string_to_dword(label), "{}({})".format(name, str(label)), pos = args[0].pos)

def builtin_tramtype_available(name, args, pos):
"""
tramtype_available(tramtype_label) builtin function.
@return 1 if the roadtype label is available, 0 otherwise.
"""
if len(args) != 1:
raise generic.ScriptError(name + "() must have exactly 1 parameter", pos)
label = args[0].reduce()
return SpecialCheck((0x11, None), 0, (0, 1), parse_string_to_dword(label), "{}({})".format(name, str(label)), pos = args[0].pos)

def builtin_grf_status(name, args, pos):
"""
grf_(current_status|future_status|order_behind)(grfid[, mask]) builtin function.
@@ -643,6 +665,8 @@ def builtin_format_string(name, args, pos):
'version_openttd' : builtin_version_openttd,
'cargotype_available' : builtin_cargotype_available,
'railtype_available' : builtin_railtype_available,
'roadtype_available' : builtin_roadtype_available,
'tramtype_available' : builtin_tramtype_available,
'grf_current_status' : builtin_grf_status,
'grf_future_status' : builtin_grf_status,
'grf_order_behind' : builtin_grf_status,
@@ -427,6 +427,16 @@
'RAILTYPE_FLAG_CATENARY' : 0,
'RAILTYPE_FLAG_NO_LEVEL_CROSSING' : 1, # for OpenTTD > r20049

#roadtype flags
'ROADTYPE_FLAG_CATENARY' : 0,
'ROADTYPE_FLAG_NO_LEVEL_CROSSING' : 1,
'ROADTYPE_FLAG_NO_HOUSES' : 2,

#tramtype flags
'TRAMTYPE_FLAG_CATENARY' : 0,
'TRAMTYPE_FLAG_NO_LEVEL_CROSSING' : 1,
'TRAMTYPE_FLAG_ALLOW_HOUSES' : 2,

#type of default station graphics used for a railtype
'RAILTYPE_STATION_NORMAL' : 0,
'RAILTYPE_STATION_MONORAIL' : 1,
@@ -1198,8 +1208,19 @@ def create_spritegroup_ref(info, pos):
return expression.SpriteGroupRef(expression.Identifier(info), [], pos)

cargo_numbers = {}

is_default_railtype_table = True
# if no railtype_table is provided, OpenTTD assumes these 4 railtypes
railtype_table = {'RAIL': 0, 'ELRL': 1, 'MONO': 1, 'MGLV': 2}

is_default_roadtype_table = True
# if no roadtype_table is provided, OpenTTD sets all vehicles to ROAD
roadtype_table = {'ROAD': 0}

is_default_tramtype_table = True
# if no tramtype_table is provided, OpenTTD sets all vehicles to ELRL
tramtype_table = {'ELRL': 0}

item_names = {}
settings = {}
named_parameters = {}
@@ -1213,6 +1234,8 @@ def create_spritegroup_ref(info, pos):
(named_parameters, param_from_name),
cargo_numbers,
railtype_table,
roadtype_table,
tramtype_table,
(item_names, item_to_id),
(settings, setting_from_info),
(config_flags, config_flag),
@@ -1229,3 +1252,7 @@ def print_stats():
generic.print_info("Cargo translation table: {}/{}".format(len(cargo_numbers), 0xFE))
if not is_default_railtype_table:
generic.print_info("Railtype translation table: {}/{}".format(len(railtype_table), 0x100))
if not is_default_roadtype_table:
generic.print_info("Roadtype translation table: {}/{}".format(len(roadtype_table), 0x100))
if not is_default_tramtype_table:
generic.print_info("Tramtype translation table: {}/{}".format(len(tramtype_table), 0x100))
@@ -14,7 +14,7 @@
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA."""

from nml import generic, expression, tokens, nmlop, unit
from nml.ast import assignment, basecost, cargotable, conditional, deactivate, disable_item, error, font, general, grf, item, loop, produce, railtypetable, replace, spriteblock, switch, townnames, snowline, skipall, tilelayout, alt_sprites, base_graphics, override, sort_vehicles
from nml.ast import assignment, basecost, cargotable, conditional, deactivate, disable_item, error, font, general, grf, item, loop, produce, railtypetable, roadtypetable, tramtypetable, replace, spriteblock, switch, townnames, snowline, skipall, tilelayout, alt_sprites, base_graphics, override, sort_vehicles
from nml.actions import actionD, real_sprite
import ply.yacc as yacc

@@ -88,6 +88,8 @@ def p_main_block(self, t):
| town_names
| cargotable
| railtype
| roadtype
| tramtype
| grf_block
| param_assignment
| skip_all
@@ -663,6 +665,42 @@ def p_railtypetable_item(self, t):
if len(t) == 2: t[0] = t[1]
else: t[0] = assignment.Assignment(t[1], t[4], t[1].pos)

def p_roadtypetable(self, t):
'''roadtype : ROADTYPETABLE LBRACE roadtypetable_list RBRACE
| ROADTYPETABLE LBRACE roadtypetable_list COMMA RBRACE'''
t[0] = roadtypetable.RoadtypeTable(t[3], t.lineno(1))

def p_roadtypetable_list(self, t):
'''roadtypetable_list : roadtypetable_item
| roadtypetable_list COMMA roadtypetable_item'''
if len(t) == 2: t[0] = [t[1]]
else: t[0] = t[1] + [t[3]]

def p_roadtypetable_item(self, t):
'''roadtypetable_item : ID
| STRING_LITERAL
| ID COLON LBRACKET expression_list RBRACKET'''
if len(t) == 2: t[0] = t[1]
else: t[0] = assignment.Assignment(t[1], t[4], t[1].pos)

def p_tramtypetable(self, t):
'''tramtype : TRAMTYPETABLE LBRACE tramtypetable_list RBRACE
| TRAMTYPETABLE LBRACE tramtypetable_list COMMA RBRACE'''
t[0] = tramtypetable.TramtypeTable(t[3], t.lineno(1))

def p_tramtypetable_list(self, t):
'''tramtypetable_list : tramtypetable_item
| tramtypetable_list COMMA tramtypetable_item'''
if len(t) == 2: t[0] = [t[1]]
else: t[0] = t[1] + [t[3]]

def p_tramtypetable_item(self, t):
'''tramtypetable_item : ID
| STRING_LITERAL
| ID COLON LBRACKET expression_list RBRACKET'''
if len(t) == 2: t[0] = t[1]
else: t[0] = assignment.Assignment(t[1], t[4], t[1].pos)

def p_basecost(self, t):
'basecost : BASECOST LBRACE generic_assignment_list RBRACE'
t[0] = basecost.BaseCost(t[3], t.lineno(1))
@@ -23,6 +23,8 @@
'param' : 'PARAMETER',
'cargotable' : 'CARGOTABLE',
'railtypetable' : 'RAILTYPETABLE',
'roadtypetable' : 'ROADTYPETABLE',
'tramtypetable' : 'TRAMTYPETABLE',
'if' : 'IF',
'else' : 'ELSE',
'while' : 'WHILE', # reserved