Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change: Implement GRFv9 #357

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions nml/actions/action0.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,7 @@ def write(self, file):
file.print_bytex(0)
file.print_bytex(self.feature)
file.print_byte(len(self.prop_list))
file.print_bytex(self.num_ids)
file.print_bytex(0xFF)
file.print_wordx(self.num_ids)
file.print_wordx(self.id)
file.newline()
for prop in self.prop_list:
Expand Down Expand Up @@ -1080,7 +1079,7 @@ def parse_sort_block(feature, vehid_list):
idx = len(vehid_list) - 1
while idx >= 0:
cur = vehid_list[idx]
prop = Action0Property(prop_num[feature], [last], 3)
prop = Action0Property(prop_num[feature], [last], 2)
action_list.append(Action0(feature, cur.value))
action_list[-1].prop_list.append(prop)
last = cur
Expand Down Expand Up @@ -1214,5 +1213,5 @@ def get_layout_action0(feature, id, layouts):
def get_copy_layout_action0(feature, id, source_id):
act0, offset = create_action0(feature, id, None, None)
act0.num_ids = 1
act0.prop_list.append(Action0Property(0x0A, source_id, 1 if source_id.value < 0xFF else 3))
act0.prop_list.append(Action0Property(0x0A, source_id, 2))
return [act0]
152 changes: 31 additions & 121 deletions nml/actions/action0properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,40 +243,6 @@ def animation_info(value, loop_bit=8, max_frame=253):
return ConstantNumeric((looping << loop_bit) + frames - 1)


def cargo_list(value, max_num_cargos):
"""
Encode an array of cargo types in a single property value. If less than the maximum
number of cargos are given the rest is filled up with 0xFF (=invalid cargo).

@param value: Array of cargo types.
@type value: C{Array}

@param max_num_cargos: The maximum number of cargos in the array.
@type max_num_cargos: C{int}

@param prop_num: Property number.
@type prop_num: C{int}

@param prop_size: Property size in bytes.
@type prop_size: C{int}
"""
if not isinstance(value, Array) or len(value.values) > max_num_cargos:
raise generic.ScriptError(
"Cargo list must be an array with no more than {:d} values".format(max_num_cargos), value.pos
)
cargoes = value.values + [ConstantNumeric(0xFF, value.pos) for _ in range(max_num_cargos - len(value.values))]

ret = None
for i, cargo in enumerate(cargoes):
byte = nmlop.AND(cargo, 0xFF)
if i == 0:
ret = byte
else:
byte = nmlop.SHIFT_LEFT(byte, i * 8)
ret = nmlop.OR(ret, byte)
return ret.reduce()


#
# General vehicle properties that apply to feature 0x00 .. 0x03
#
Expand Down Expand Up @@ -305,22 +271,17 @@ class VariableListProp(BaseAction0Property):
Property value that is a variable-length list of variable sized values, the list length is written before the data.
"""

def __init__(self, prop_num, data, size, extended):
def __init__(self, prop_num, data, size):
# data is a list, each element belongs to an item ID
# Each element in the list is a list of cargo types
self.prop_num = prop_num
self.data = data
self.size = size
self.extended = extended

def write(self, file):
file.print_bytex(self.prop_num)
for elem in self.data:
if self.extended:
file.print_bytex(0xFF)
file.print_word(len(elem))
else:
file.print_byte(len(elem))
file.print_word(len(elem))
for i, val in enumerate(elem):
if i % 8 == 0:
file.newline()
Expand All @@ -330,13 +291,17 @@ def write(self, file):
def get_size(self):
total_len = 1 # Prop number
for elem in self.data:
# For each item ID to set, make space for all values + 3 or 1 for the length
total_len += len(elem) * self.size + (3 if self.extended else 1)
# For each item ID to set, make space for all values + 2 for the length
total_len += len(elem) * self.size + 2
return total_len


def VariableByteListProp(prop_num, data, extended=False):
return VariableListProp(prop_num, data, 1, extended)
def VariableByteListProp(prop_num, data):
return VariableListProp(prop_num, data, 1)


def VariableWordListProp(prop_num, data):
return VariableListProp(prop_num, data, 2)


def ctt_list(prop_num, *values):
Expand All @@ -346,17 +311,13 @@ def ctt_list(prop_num, *values):
if not isinstance(value, Array):
raise generic.ScriptError("Value of cargolist property must be an array", value.pos)
return [
VariableByteListProp(
VariableWordListProp(
prop_num,
[[ctype.reduce_constant().value for ctype in single_item_array.values] for single_item_array in values],
)
]


def VariableWordListProp(num_prop, data, extended=False):
return VariableListProp(num_prop, data, 2, extended)


def accepted_cargos(prop_num, *values):
# values may have multiple entries, if more than one item ID is set (e.g. multitile houses)
# Each value is an expression.Array of cargo types and amount arrays
Expand Down Expand Up @@ -419,7 +380,7 @@ def prop_test(value):
# fmt: off
properties[0x00] = {
**general_veh_props,
"track_type": {"size": 1, "num": 0x05},
"track_type": {"size": 2, "num": 0x05},
"ai_special_flag": {"size": 1, "num": 0x08},
"speed": {
"size": 2,
Expand All @@ -437,7 +398,7 @@ def prop_test(value):
"sprite_id": {"size": 1, "num": 0x12},
"dual_headed": {"size": 1, "num": 0x13},
"cargo_capacity": {"size": 1, "num": 0x14},
"default_cargo_type": {"size": 1, "num": 0x15},
"default_cargo_type": {"size": 2, "num": 0x15},
"weight": two_byte_property(
0x16,
0x24,
Expand Down Expand Up @@ -510,8 +471,8 @@ def prop15_test(value):
# fmt: off
properties[0x01] = {
**general_veh_props,
"road_type": {"size": 1, "num": 0x05},
"tram_type": {"size": 1, "num": 0x05},
"road_type": {"size": 2, "num": 0x05},
"tram_type": {"size": 2, "num": 0x05},
"speed": roadveh_speed_prop(
{
"unit_type": "speed",
Expand All @@ -524,9 +485,9 @@ def prop15_test(value):
# 0B -0D don"t exist
"sprite_id": {"size": 1, "num": 0x0E},
"cargo_capacity": {"size": 1, "num": 0x0F},
"default_cargo_type": {"size": 1, "num": 0x10},
"default_cargo_type": {"size": 2, "num": 0x10},
"cost_factor": {"size": 1, "num": 0x11},
"sound_effect": {"size": 1, "num": 0x12},
"sound_effect": {"size": 2, "num": 0x12},
"power": {"size": 1, "num": 0x13, "unit_type": "power", "unit_conversion": (1, 10)},
"weight": {"size": 1, "num": 0x14, "unit_type": "weight", "unit_conversion": 4},
# 15 is set together with 08 (see above)
Expand Down Expand Up @@ -605,11 +566,11 @@ def prop23_test(value):
"adjust_value": lambda val, unit: ottd_display_speed(val, 1, 2, unit),
}
),
"default_cargo_type": {"size": 1, "num": 0x0C},
"default_cargo_type": {"size": 2, "num": 0x0C},
"cargo_capacity": {"size": 2, "num": 0x0D},
# 0E does not exist
"running_cost_factor": {"size": 1, "num": 0x0F},
"sound_effect": {"size": 1, "num": 0x10},
"sound_effect": {"size": 2, "num": 0x10},
# 11 (refittable cargo types) is removed, it is zeroed when setting a different refit property
# 12 (callback flags) is not set by user
"refit_cost": {"size": 1, "num": 0x13},
Expand Down Expand Up @@ -685,7 +646,7 @@ def aircraft_is_large(value):
"passenger_capacity": {"size": 2, "num": 0x0F},
# 10 does not exist
"mail_capacity": {"size": 1, "num": 0x11},
"sound_effect": {"size": 1, "num": 0x12},
"sound_effect": {"size": 2, "num": 0x12},
# 13 (refittable cargo types) is removed, it is zeroed when setting a different refit property
# 14 (callback flags) is not set by user
"refit_cost": {"size": 1, "num": 0x15},
Expand Down Expand Up @@ -763,7 +724,7 @@ def get_size(self):

def station_layouts(value):
if isinstance(value, ConstantNumeric):
return [Action0Property(0x0F, value, 1 if value.value < 0xFF else 3)]
return [Action0Property(0x0F, value, 2)]
if not isinstance(value, Array) or len(value.values) == 0:
raise generic.ScriptError("station_layouts must be an array of layouts, or the ID of a station", value.pos)
layouts = {}
Expand Down Expand Up @@ -799,7 +760,7 @@ def station_tile_flags(value):
if not isinstance(value, Array) or len(value.values) % 2 != 0:
raise generic.ScriptError("Flag list must be an array of even length", value.pos)
if len(value.values) > 8:
return [VariableByteListProp(0x1E, [[flags.reduce_constant().value for flags in value.values]], True)]
return [VariableByteListProp(0x1E, [[flags.reduce_constant().value for flags in value.values]])]
pylons = 0
wires = 0
blocked = 0
Expand Down Expand Up @@ -1112,29 +1073,19 @@ def industry_layouts(value):
return [IndustryLayoutProp(layouts)]


def industry_prod_multiplier(value):
if not isinstance(value, Array) or len(value.values) > 2:
raise generic.ScriptError("Prod multiplier must be an array of up to two values", value.pos)
props = []
for i in range(0, 2):
val = value.values[i].reduce_constant() if i < len(value.values) else ConstantNumeric(0)
props.append(Action0Property(0x12 + i, val, 1))
return props


class RandomSoundsProp(BaseAction0Property):
def __init__(self, sound_list):
self.sound_list = sound_list

def write(self, file):
file.print_bytex(0x15)
file.print_byte(len(self.sound_list))
file.print_word(len(self.sound_list))
for sound in self.sound_list:
sound.write(file, 1)
sound.write(file, 2)
file.newline()

def get_size(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_size needs adjusting. See frosch123@7ba80f2
(triggered by FIRS 5.0.0-beta-3

return len(self.sound_list) + 2
return len(self.sound_list) + 3


def random_sounds(value):
Expand Down Expand Up @@ -1172,20 +1123,6 @@ def industry_conflicting_types(value):
return [ConflictingTypesProp(types_list)]


def industry_input_multiplier(value, prop_num):
if not isinstance(value, Array) or len(value.values) > 2:
raise generic.ScriptError("Input multiplier must be an array of up to two values", value.pos)
val1 = value.values[0].reduce() if len(value.values) > 0 else ConstantNumeric(0)
val2 = value.values[1].reduce() if len(value.values) > 1 else ConstantNumeric(0)
if not isinstance(val1, (ConstantNumeric, ConstantFloat)) or not isinstance(val2, (ConstantNumeric, ConstantFloat)):
raise generic.ScriptError("Expected a compile-time constant", value.pos)
generic.check_range(val1.value, 0, 256, "input_multiplier", val1.pos)
generic.check_range(val2.value, 0, 256, "input_multiplier", val2.pos)
mul1 = int(val1.value * 256)
mul2 = int(val2.value * 256)
return [Action0Property(prop_num, ConstantNumeric(mul1 | (mul2 << 16)), 4)]


class IndustryInputMultiplierProp(BaseAction0Property):
def __init__(self, prop_num, data):
self.prop_num = prop_num
Expand Down Expand Up @@ -1282,8 +1219,8 @@ def check_accept(acp):
has_inpmult = True

return [
VariableByteListProp(0x25, [output_cargos]),
VariableByteListProp(0x26, [input_cargos]),
VariableWordListProp(0x25, [output_cargos]),
VariableWordListProp(0x26, [input_cargos]),
VariableByteListProp(0x27, [prod_multipliers]),
IndustryInputMultiplierProp(0x28, input_multipliers if has_inpmult else []),
]
Expand All @@ -1299,23 +1236,7 @@ def check_accept(acp):
"prod_increase_msg": {"size": 2, "num": 0x0D, "string": 0xDC},
"prod_decrease_msg": {"size": 2, "num": 0x0E, "string": 0xDC},
"fund_cost_multiplier": {"size": 1, "num": 0x0F},
"prod_cargo_types": {
"size": 2,
"num": 0x10,
"value_function": lambda value: cargo_list(value, 2),
"replaced_by": "cargo_types",
},
"accept_cargo_types": {
"size": 4,
"num": 0x11,
"value_function": lambda value: cargo_list(value, 3),
"replaced_by": "cargo_types",
},
# prop 12,13
"prod_multiplier": {
"custom_function": industry_prod_multiplier,
"replaced_by": "cargo_types",
},
# props 10+11+12+13 are replaced by cargo_types
"min_cargo_distr": {"size": 1, "num": 0x14},
"random_sound_effects": {"custom_function": random_sounds}, # = prop 15
"conflicting_ind_types": {"custom_function": industry_conflicting_types}, # = prop 16
Expand All @@ -1325,18 +1246,7 @@ def check_accept(acp):
"map_colour": {"size": 1, "num": 0x19},
"spec_flags": {"size": 4, "num": 0x1A},
"new_ind_msg": {"size": 2, "num": 0x1B, "string": 0xDC},
"input_multiplier_1": {
"custom_function": lambda value: industry_input_multiplier(value, 0x1C),
"replaced_by": "cargo_types",
},
"input_multiplier_2": {
"custom_function": lambda value: industry_input_multiplier(value, 0x1D),
"replaced_by": "cargo_types",
},
"input_multiplier_3": {
"custom_function": lambda value: industry_input_multiplier(value, 0x1E),
"replaced_by": "cargo_types",
},
# prop 1C+1D+1E are replaced by cargo_types
"name": {"size": 2, "num": 0x1F, "string": 0xDC},
"prospect_chance": {"size": 4, "num": 0x20, "unit_conversion": 0xFFFFFFFF},
# prop 21, 22 (callback flags) are not set by user
Expand Down Expand Up @@ -1502,14 +1412,14 @@ def __init__(self, prop_num, labels):

def write(self, file):
file.print_bytex(self.prop_num)
file.print_byte(len(self.labels))
file.print_word(len(self.labels))
for label in self.labels:
parse_string_to_dword(label) # Error if the wrong length or not ASCII
label.write(file, 4)
file.newline()

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


def label_list(value, prop_num, description):
Expand Down
12 changes: 6 additions & 6 deletions nml/actions/action1.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,22 +42,22 @@ def __init__(self, feature, first_set, num_sets, num_ent):
def write(self, file):
if self.first_set == 0 and self.num_sets < 256:
# <Sprite-number> * <Length> 01 <feature> <num-sets> <num-ent>
file.start_sprite(6)
file.start_sprite(5)
file.print_bytex(1)
file.print_bytex(self.feature)
file.print_byte(self.num_sets)
file.print_varx(self.num_ent, 3)
file.print_wordx(self.num_ent)
file.newline()
file.end_sprite()
else:
# <Sprite-number> * <Length> 01 <feature> 00 <first_set> <num-sets> <num-ent>
file.start_sprite(12)
file.start_sprite(9)
file.print_bytex(1)
file.print_bytex(self.feature)
file.print_bytex(0)
file.print_varx(self.first_set, 3)
file.print_varx(self.num_sets, 3)
file.print_varx(self.num_ent, 3)
file.print_wordx(self.first_set)
file.print_wordx(self.num_sets)
file.print_wordx(self.num_ent)
file.newline()
file.end_sprite()

Expand Down
5 changes: 3 additions & 2 deletions nml/actions/action10.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@
class Action10(base_action.BaseAction):
def __init__(self, label):
self.label = label
assert self.label >= 0x8000 and self.label <= 0xFFFF

def write(self, file):
file.start_sprite(2)
file.start_sprite(3)
file.print_bytex(0x10)
file.print_bytex(self.label)
file.print_wordx(self.label)
file.newline()
file.end_sprite()

Expand Down
Loading
Loading