Skip to content

Commit 289fce3

Browse files
committed
Add: Support for NewGRF badges.
1 parent ad6b00a commit 289fce3

File tree

16 files changed

+433
-14
lines changed

16 files changed

+433
-14
lines changed

nml/actions/action0.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ def find_unused(self, length):
211211
BlockAllocation(0, 62, "Roadtype"),
212212
BlockAllocation(0, 62, "Tramtype"),
213213
BlockAllocation(0, 0xFFFE, "RoadStop"), # UINT16_MAX - 1
214+
BlockAllocation(0, 64000, "Badge"),
214215
]
215216

216217

@@ -780,13 +781,49 @@ def get_size(self):
780781
return len(self.id_list) * 4 + 1
781782

782783

784+
class StringListProp(BaseAction0Property):
785+
def __init__(self, prop_num, string_list):
786+
self.prop_num = prop_num
787+
self.string_list = string_list
788+
789+
def write(self, file):
790+
file.print_bytex(self.prop_num)
791+
for i, string_val in enumerate(self.string_list):
792+
if i > 0 and i % 5 == 0:
793+
file.newline()
794+
file.print_string(string_val.value, True, True)
795+
file.newline()
796+
797+
def get_size(self):
798+
size = 1
799+
for i, string_val in enumerate(self.string_list):
800+
size += grfstrings.get_string_size(string_val.value, True, True)
801+
return size
802+
803+
783804
def get_cargolist_action(cargo_list):
784805
action0 = Action0(0x08, 0)
785806
action0.prop_list.append(IDListProp(0x09, cargo_list))
786807
action0.num_ids = len(cargo_list)
787808
return [action0]
788809

789810

811+
def get_badgelist_action(badge_list):
812+
index = 0
813+
actions = []
814+
while index < len(badge_list):
815+
last = min(index + 250, len(badge_list))
816+
817+
action0 = Action0(0x08, index)
818+
action0.prop_list.append(StringListProp(0x18, badge_list[index:last]))
819+
action0.num_ids = last - index
820+
actions.append(action0)
821+
822+
index = last
823+
824+
return actions
825+
826+
790827
def get_tracktypelist_action(table_prop_id, cond_tracktype_not_defined, tracktype_list):
791828
action6.free_parameters.save()
792829
act6 = action6.Action6()

nml/actions/action0properties.py

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
import itertools
1717

18-
from nml import generic, nmlop, global_constants
18+
from nml import generic, grfstrings, nmlop, global_constants
1919
from nml.expression import (
2020
AcceptCargo,
2121
Array,
@@ -170,7 +170,7 @@ def get_size(self):
170170
#
171171
# 'required' (value doesn't matter) if the property is required for the item to be valid.
172172

173-
properties = 0x15 * [None]
173+
properties = 0x16 * [None]
174174

175175
#
176176
# Some helper functions that are used for multiple features
@@ -335,6 +335,24 @@ def VariableByteListProp(prop_num, data, len_size=1):
335335
return VariableListProp(prop_num, data, 1, len_size)
336336

337337

338+
class StringProp(BaseAction0Property):
339+
"""
340+
Property value that is zero-terminated string.
341+
"""
342+
343+
def __init__(self, prop_num, string):
344+
self.prop_num = prop_num
345+
self.string = string
346+
347+
def write(self, file):
348+
file.print_bytex(self.prop_num)
349+
file.print_string(self.string.value, True, True)
350+
file.newline()
351+
352+
def get_size(self):
353+
return grfstrings.get_string_size(self.string.value) + 1
354+
355+
338356
def ctt_list(prop_num, *values):
339357
# values may have multiple entries, if more than one item ID is set (e.g. multitile houses)
340358
# Each value is an expression.Array of cargo types
@@ -353,6 +371,38 @@ def VariableWordListProp(num_prop, data, len_size=1):
353371
return VariableListProp(num_prop, data, 2, len_size)
354372

355373

374+
def badge_list(prop_num, *values):
375+
# values may have multiple entries, if more than one item ID is set
376+
# Each value is an expression.Array of badge labels
377+
378+
table = global_constants.badge_numbers
379+
380+
for value in values:
381+
if not isinstance(value, Array):
382+
raise generic.ScriptError("Value of badgelist property must be an array", value.pos)
383+
384+
for badge in value.values:
385+
if not isinstance(badge, StringLiteral) or badge.value not in table:
386+
raise generic.ScriptError(
387+
"Parameter for badges must be a string literal that is also in your badge table", value.pos
388+
)
389+
390+
return [
391+
VariableListProp(
392+
prop_num,
393+
[[table[badge.value] for badge in single_item_array.values] for single_item_array in values],
394+
2,
395+
2,
396+
)
397+
]
398+
399+
400+
def string_property(prop_num, value):
401+
if not isinstance(value, StringLiteral):
402+
raise generic.ScriptError("Value of label property must be a StringLiteral", value.pos)
403+
return [StringProp(prop_num, value)]
404+
405+
356406
def accepted_cargos(prop_num, *values):
357407
# values may have multiple entries, if more than one item ID is set (e.g. multitile houses)
358408
# Each value is an expression.Array of cargo types and amount arrays
@@ -486,6 +536,7 @@ def single_or_list(prop_name, single_num, multiple_num, value):
486536
"curve_speed_mod": {"size": 2, "num": 0x2E, "unit_conversion": 256},
487537
"variant_group": {"size": 2, "num": 0x2F},
488538
"extra_flags": {"size": 4, "num": 0x30},
539+
"badges": {"custom_function": lambda value: badge_list(0x33, value)},
489540
}
490541
# fmt: on
491542

@@ -564,6 +615,7 @@ def prop15_test(value):
564615
],
565616
"variant_group": {"size": 2, "num": 0x26},
566617
"extra_flags": {"size": 4, "num": 0x27},
618+
"badges": {"custom_function": lambda value: badge_list(0x2A, value)},
567619
}
568620
# fmt: on
569621

@@ -654,6 +706,7 @@ def prop23_test(value):
654706
"variant_group": {"size": 2, "num": 0x20},
655707
"extra_flags": {"size": 4, "num": 0x21},
656708
"acceleration": {"size": 1, "num": 0x24},
709+
"badges": {"custom_function": lambda value: badge_list(0x26, value)},
657710
}
658711
# fmt: on
659712

@@ -715,6 +768,7 @@ def aircraft_is_large(value):
715768
"range": {"size": 2, "num": 0x1F},
716769
"variant_group": {"size": 2, "num": 0x20},
717770
"extra_flags": {"size": 4, "num": 0x21},
771+
"badges": {"custom_function": lambda value: badge_list(0x24, value)},
718772
}
719773
# fmt: on
720774

@@ -857,6 +911,7 @@ def station_tile_list(value, prop_num, description):
857911
"name": {"size": 2, "num": (256, -1, 0x1C), "string": (256, 0xC5, 0xDC), "required": True},
858912
"classname": {"size": 2, "num": (256, -1, 0x1D), "string": (256, 0xC4, 0xDC)},
859913
"tile_flags": {"custom_function": station_tile_flags}, # = prop 1E
914+
"badges": {"custom_function": lambda value: badge_list(0x1F, value)},
860915
"heights": {"custom_function": lambda x: station_tile_list(x, 0x20, "Station height")},
861916
"blocked_pillars": {"custom_function": lambda x: station_tile_list(x, 0x21, "Station blocked pillar")},
862917
}
@@ -1067,6 +1122,7 @@ def mt_house_class(value, num_ids, size_bit):
10671122
"multitile_function": mt_house_same,
10681123
"custom_function": lambda *values: accepted_cargos(0x23, *values),
10691124
},
1125+
"badges": {"custom_function": lambda value: badge_list(0x24, value)},
10701126
}
10711127
# fmt: on
10721128

@@ -1088,6 +1144,7 @@ def mt_house_class(value, num_ids, size_bit):
10881144
"animation_triggers": {"size": 1, "num": 0x11},
10891145
"special_flags": {"size": 1, "num": 0x12},
10901146
"accepted_cargos": {"custom_function": lambda value: accepted_cargos(0x13, value)},
1147+
"badges": {"custom_function": lambda value: badge_list(0x14, value)},
10911148
}
10921149
# fmt: on
10931150

@@ -1360,6 +1417,7 @@ def check_accept(acp):
13601417
"nearby_station_name": {"size": 2, "num": 0x24, "string": 0xDC},
13611418
# prop 25+26+27+28 combined in one structure
13621419
"cargo_types": {"custom_function": industry_cargo_types},
1420+
"badges": {"custom_function": lambda value: badge_list(0x29, value)},
13631421
}
13641422
# fmt: on
13651423

@@ -1463,6 +1521,7 @@ def airport_layouts(value):
14631521
"noise_level": {"size": 1, "num": 0x0F},
14641522
"name": {"size": 2, "num": 0x10, "string": 0xDC},
14651523
"maintenance_cost": {"size": 2, "num": 0x11},
1524+
"badges": {"custom_function": lambda value: badge_list(0x12, value)},
14661525
}
14671526
# fmt: on
14681527

@@ -1503,6 +1562,7 @@ def object_size(value):
15031562
"height": {"size": 1, "num": 0x16},
15041563
"num_views": {"size": 1, "num": 0x17},
15051564
"count_per_map256": {"size": 1, "num": 0x18},
1565+
"badges": {"custom_function": lambda value: badge_list(0x19, value)},
15061566
}
15071567
# fmt: on
15081568

@@ -1549,6 +1609,7 @@ def label_list(value, prop_num, description):
15491609
"sort_order": {"size": 1, "num": 0x1A},
15501610
"name": {"size": 2, "num": 0x1B, "string": 0xDC},
15511611
"maintenance_cost": {"size": 2, "num": 0x1C},
1612+
"badges": {"custom_function": lambda value: badge_list(0x1E, value)},
15521613
}
15531614

15541615
#
@@ -1587,6 +1648,7 @@ def label_list(value, prop_num, description):
15871648
"animation_info": {"size": 2, "num": 0x0F, "value_function": animation_info},
15881649
"animation_speed": {"size": 1, "num": 0x10},
15891650
"animation_triggers": {"size": 1, "num": 0x11},
1651+
"badges": {"custom_function": lambda value: badge_list(0x12, value)},
15901652
}
15911653

15921654
#
@@ -1675,4 +1737,15 @@ def byte_sequence_list(value, prop_num, description, expected_count):
16751737
"heights": {"custom_function": lambda x: station_tile_list(x, 0x13, "Station height")},
16761738
"blocked_pillars": {"custom_function": lambda x: station_tile_list(x, 0x14, "Station blocked pillar")},
16771739
"cost_multipliers": {"custom_function": lambda x: byte_sequence_list(x, 0x15, "Cost multipliers", 2)},
1740+
"badges": {"custom_function": lambda value: badge_list(0x16, value)},
1741+
}
1742+
1743+
#
1744+
# Feature 0x15 (Badges)
1745+
#
1746+
1747+
properties[0x15] = {
1748+
'label': {'custom_function': lambda x: string_property(0x08, x), "required": True},
1749+
'flags': {'size': 4, 'num': 0x09},
1750+
'name': {'num': -1, 'string': None},
16781751
}

nml/actions/action2.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,8 +217,9 @@ def free_references(source_action):
217217
free_action2_ids.append(act2.id)
218218

219219

220-
# Features using sprite groups directly: vehicles, stations, canals, cargos, railtypes, airports, roadtypes, tramtypes
221-
features_sprite_group = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x0B, 0x0D, 0x10, 0x12, 0x13]
220+
# Features using sprite groups directly: vehicles, stations, canals, cargos, railtypes, airports, roadtypes, tramtypes,
221+
# badges
222+
features_sprite_group = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x0B, 0x0D, 0x10, 0x12, 0x13, 0x15]
222223
# Features using sprite layouts: houses, industry tiles, objects, airport tiles, and road stops
223224
features_sprite_layout = [0x07, 0x09, 0x0F, 0x11, 0x14]
224225
# All features that need sprite sets

0 commit comments

Comments
 (0)