From 164bf881e7d3d4e8928ec9db6080d8223fa44187 Mon Sep 17 00:00:00 2001 From: Peter Nelson Date: Tue, 28 Jul 2026 10:43:50 +0100 Subject: [PATCH] Add: Support for Trees feature --- nml/actions/action0.py | 1 + nml/actions/action0properties.py | 14 +++++++++++++- nml/actions/action2.py | 2 +- nml/actions/action2random.py | 1 + nml/actions/action2var_variables.py | 23 +++++++++++++++++++++++ nml/actions/action3_callbacks.py | 8 +++++++- nml/ast/general.py | 1 + nml/editors/extract_tables.py | 2 ++ nml/global_constants.py | 7 +++++++ 9 files changed, 56 insertions(+), 3 deletions(-) diff --git a/nml/actions/action0.py b/nml/actions/action0.py index e0562f757..33aca2c80 100644 --- a/nml/actions/action0.py +++ b/nml/actions/action0.py @@ -212,6 +212,7 @@ def find_unused(self, length): BlockAllocation(0, 62, "Tramtype"), BlockAllocation(0, 0xFFFE, "RoadStop"), # UINT16_MAX - 1 BlockAllocation(0, 64000, "Badge"), + BlockAllocation(0, 255, "Trees"), ] diff --git a/nml/actions/action0properties.py b/nml/actions/action0properties.py index d48c8d0fa..3a6f9fe18 100644 --- a/nml/actions/action0properties.py +++ b/nml/actions/action0properties.py @@ -170,7 +170,7 @@ def get_size(self): # # 'required' (value doesn't matter) if the property is required for the item to be valid. -properties = 0x16 * [None] +properties = 0x17 * [None] # # Some helper functions that are used for multiple features @@ -1754,3 +1754,15 @@ def byte_sequence_list(value, prop_num, description, expected_count): 'flags': {'size': 4, 'num': 0x09}, 'name': {'num': -1, 'string': None}, } + +# +# Feature 0x16 (Trees) +# + +properties[0x16] = { + "substitute": {"size": 1, "num": 0x08, "first": None}, + "override": {"size": 1, "num": 0x09}, + "climates_available": {"size": 1, "num": 0x0A}, + "tropiczones_available": {"size": 1, "num": 0x0B}, + "probability": {"size": 1, "num": 0x0C}, +} diff --git a/nml/actions/action2.py b/nml/actions/action2.py index d472e0942..ae401a8aa 100644 --- a/nml/actions/action2.py +++ b/nml/actions/action2.py @@ -221,7 +221,7 @@ def free_references(source_action): # badges features_sprite_group = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x0B, 0x0D, 0x10, 0x12, 0x13, 0x15] # Features using sprite layouts: houses, industry tiles, objects, airport tiles, and road stops -features_sprite_layout = [0x07, 0x09, 0x0F, 0x11, 0x14] +features_sprite_layout = [0x07, 0x09, 0x0F, 0x11, 0x14, 0x16] # All features that need sprite sets features_sprite_set = features_sprite_group + features_sprite_layout diff --git a/nml/actions/action2random.py b/nml/actions/action2random.py index 04343778f..54114e7b2 100644 --- a/nml/actions/action2random.py +++ b/nml/actions/action2random.py @@ -100,6 +100,7 @@ def __init__(self, result, prob, comment): }, 0x12: {"SELF": {"type": 0x80, "param": 0, "first_bit": 0, "num_bits": 2, "triggers": False}}, 0x13: {"SELF": {"type": 0x80, "param": 0, "first_bit": 0, "num_bits": 2, "triggers": False}}, + 0x16: {"SELF": {"type": 0x80, "param": 0, "first_bit": 0, "num_bits": 2, "triggers": False}}, } diff --git a/nml/actions/action2var_variables.py b/nml/actions/action2var_variables.py index ab518bde1..1c19a1d30 100644 --- a/nml/actions/action2var_variables.py +++ b/nml/actions/action2var_variables.py @@ -881,6 +881,27 @@ def industry_cargotype(name, args, pos, info): 'intro_date' : {'var': 0x40, 'start': 0, 'size': 32}, } +# +# Trees (feature 0x16) +# + +varact2vars_trees = { + 'terrain_type' : {'var': 0x40, 'start': 0, 'size': 8}, + 'tree_count' : {'var': 0x41, 'start': 0, 'size': 8}, + 'variant' : {'var': 0x42, 'start': 0, 'size': 2}, + 'town_zone' : {'var': 0x43, 'start': 0, 'size': 8}, +} + +varact2vars60x_trees = { + 'nearby_tile_info' : {'var': 0x60, 'start': 0, 'size': 32, 'param_function': signed_tile_offset}, + 'nearby_tile_slope' : {'var': 0x60, 'start': 0, 'size': 5, 'param_function': signed_tile_offset}, + 'nearby_tile_is_water' : {'var': 0x60, 'start': 9, 'size': 1, 'param_function': signed_tile_offset}, + 'nearby_tile_terrain_type' : {'var': 0x60, 'start': 10, 'size': 3, 'param_function': signed_tile_offset}, + 'nearby_tile_water_class' : {'var': 0x60, 'start': 13, 'size': 2, 'param_function': signed_tile_offset}, + 'nearby_tile_height' : {'var': 0x60, 'start': 16, 'size': 8, 'param_function': signed_tile_offset}, + 'nearby_tile_class' : {'var': 0x60, 'start': 24, 'size': 4, 'param_function': signed_tile_offset}, +} + class VarAct2Scope: def __init__(self, name, vars_normal, vars_60x, has_persistent_storage=False): self.name = name @@ -921,6 +942,7 @@ def get_scope(self, var_range): scope_tramtypes = VarAct2Scope("TramTypes", varact2vars_tramtype, varact2vars60x_tramtype) scope_roadstops = VarAct2Scope("RoadStops", varact2vars_roadstop, varact2vars60x_roadstop) scope_badges = VarAct2Scope("Badges", varact2vars_badges, {}) +scope_trees = VarAct2Scope("Trees", varact2vars_trees, varact2vars60x_trees) varact2features = [ VarAct2Feature(scope_trains, scope_trains), @@ -945,4 +967,5 @@ def get_scope(self, var_range): VarAct2Feature(scope_tramtypes, None), VarAct2Feature(scope_roadstops, scope_towns), VarAct2Feature(scope_badges, None), + VarAct2Feature(scope_trees, None), ] diff --git a/nml/actions/action3_callbacks.py b/nml/actions/action3_callbacks.py index 50ed1ea4d..35cca48bf 100644 --- a/nml/actions/action3_callbacks.py +++ b/nml/actions/action3_callbacks.py @@ -15,7 +15,7 @@ from nml import nmlop -callbacks = 0x16 * [{}] +callbacks = 0x17 * [{}] # Possible values for 'purchase': # 0 (or not set): not called from purchase list @@ -340,3 +340,9 @@ def vehicle_length(value): 'roadstops' : {'type': 'cargo', 'num': 0x14}, 'default' : {'type': 'cargo', 'num': None}, } + +# Trees +callbacks[0x16] = { + 'default' : {'type': 'cargo', 'num': None}, + 'purchase' : {'type': 'cargo', 'num': 0xFF}, +} diff --git a/nml/ast/general.py b/nml/ast/general.py index 757750c32..7ac697c5f 100644 --- a/nml/ast/general.py +++ b/nml/ast/general.py @@ -39,6 +39,7 @@ "FEAT_TRAMTYPES": 0x13, "FEAT_ROADSTOPS": 0x14, "FEAT_BADGES": 0x15, + "FEAT_TREES": 0x16, } diff --git a/nml/editors/extract_tables.py b/nml/editors/extract_tables.py index 8d6db8c92..63665a111 100644 --- a/nml/editors/extract_tables.py +++ b/nml/editors/extract_tables.py @@ -94,6 +94,7 @@ action0properties.properties[0x13], action0properties.properties[0x14], action0properties.properties[0x15], + action0properties.properties[0x16], ] properties = set() @@ -124,6 +125,7 @@ action3_callbacks.callbacks[0x13], action3_callbacks.callbacks[0x14], action3_callbacks.callbacks[0x15], + action3_callbacks.callbacks[0x16], ] callbacks = set() diff --git a/nml/global_constants.py b/nml/global_constants.py index cf808621e..1dfcd1a3b 100644 --- a/nml/global_constants.py +++ b/nml/global_constants.py @@ -38,6 +38,13 @@ def constant_number(name, info, pos): "NO_CLIMATE" : 0x00, "ALL_CLIMATES" : 0x0F, + # tropic zones + "TROPICZONE_NORMAL" : 0, + "TROPICZONE_DESERT" : 1, + "TROPICZONE_RAINFOREST" : 2, + "NO_TROPICZONES" : 0x00, + "ALL_TROPICZONES" : 0x07, + # never expire "VEHICLE_NEVER_EXPIRES" : 0xFF,