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 = BinOp(nmlop.AND, cargo, ConstantNumeric(0xFF, cargo.pos), cargo.pos) if i == 0: ret = byte else: byte = BinOp(nmlop.SHIFT_LEFT, byte, ConstantNumeric(i * 8, cargo.pos), cargo.pos) ret = BinOp(nmlop.OR, ret, byte, cargo.pos) return ret.reduce()
def mt_house_prop09(value, num_ids, size_bit): # Only bit 5 should be set for additional tiles # Additionally, correctly set the size bit (0, 2, 3 or 4) for the first tile if isinstance(value, ConstantNumeric) and (value.value & 0x1D) != 0: raise generic.ScriptError("Invalid bits set in house property 'building_flags'.", value.pos) ret = [BinOp(nmlop.OR, value, ConstantNumeric(1 << size_bit, value.pos), value.pos).reduce()] for _i in range(1, num_ids): ret.append(BinOp(nmlop.AND, value, ConstantNumeric(1 << 5, value.pos), value.pos).reduce()) return ret
def roadveh_speed_prop(prop_info): # prop 08 value is min(value, 255) prop08_value = lambda value: BinOp(nmlop.MIN, value, ConstantNumeric(0xFF, value.pos), value.pos).reduce() # prop 15 value is (value + 3) / 4 prop15_value = lambda value: BinOp(nmlop.DIV, BinOp(nmlop.ADD, value, ConstantNumeric(3, value.pos), value.pos), ConstantNumeric(4, value.pos), value.pos).reduce() # prop 15 should not be set if value(prop08_value) <= 255. But as we test prop15 and prop15 = 0.25/prop08, test for 64: prop15_test = lambda value: isinstance(value, ConstantNumeric) and value.value >= 0x40 prop08 = {'size': 1, 'num': 0x08, 'value_function': prop08_value} prop15 = {'size': 1, 'num': 0x15, 'value_function': prop15_value, 'test_function': prop15_test} for key in prop_info: prop08[key] = prop15[key] = prop_info[key] return [prop08, prop15]
def house_random_colours(value): # User sets array with 4 values (range 0..15) # Output is a dword, each byte being a value from the array if not isinstance(value, Array) or len(value.values) != 4: raise generic.ScriptError("Random colours must be an array with exactly four values", value.pos) ret = None for i, colour in enumerate(value.values): if isinstance(colour, ConstantNumeric): generic.check_range(colour.value, 0, 15, "Random house colours", colour.pos) byte = BinOp(nmlop.AND, colour, ConstantNumeric(0xFF, colour.pos), colour.pos) if i == 0: ret = byte else: byte = BinOp(nmlop.SHIFT_LEFT, byte, ConstantNumeric(i * 8, colour.pos), colour.pos) ret = BinOp(nmlop.OR, ret, byte, colour.pos) return ret.reduce()
def mt_house_old_id(value, num_ids, size_bit): # For substitute / override properties # Set value for tile i (0 .. 3) to (value + i) # Also validate that the size of the old house matches if isinstance(value, ConstantNumeric) and not value.value in old_houses[size_bit]: raise generic.ScriptError("Substitute / override house type must have the same size as the newly defined house.", value.pos) ret = [value] for i in range(1, num_ids): ret.append(BinOp(nmlop.ADD, value, ConstantNumeric(i, value.pos), value.pos).reduce()) return ret
def house_available_mask(value): # User sets [town_zones, climates] array # Which is mapped to (town_zones | (climates & 0x800) | ((climates & 0xF) << 12)) if not isinstance(value, Array) or len(value.values) != 2: raise generic.ScriptError("availability_mask must be an array with exactly 2 values", value.pos) climates = BinOp(nmlop.AND, value.values[1], ConstantNumeric(0xF, value.pos), value.pos) climates = BinOp(nmlop.SHIFT_LEFT, climates, ConstantNumeric(12, value.pos), value.pos) above_snow = BinOp(nmlop.AND, value.values[1], ConstantNumeric(0x800, value.pos), value.pos) ret = BinOp(nmlop.OR, climates, value.values[0], value.pos) ret = BinOp(nmlop.OR, ret, above_snow, value.pos) return ret.reduce()
def house_accepted_cargo_types(value): if not isinstance(value, Array) or len(value.values) > 3: raise generic.ScriptError("accepted_cargos must be an array with no more than 3 values", value.pos) cargoes = [] for i in range(3): if i < len(value.values): cargo_amount_pair = value.values[i] if not isinstance(cargo_amount_pair, Array) or len(cargo_amount_pair.values) != 2: raise generic.ScriptError("Each element of accepted_cargos must be an array with two elements: cargoid and amount", cargo_amount_pair.pos) cargoes.append(cargo_amount_pair.values[0]) else: cargoes.append(ConstantNumeric(0xFF, value.pos)) ret = None for i, cargo in enumerate(cargoes): byte = BinOp(nmlop.AND, cargo, ConstantNumeric(0xFF, cargo.pos), cargo.pos) if i == 0: ret = byte else: byte = BinOp(nmlop.SHIFT_LEFT, byte, ConstantNumeric(i * 8, cargo.pos), cargo.pos) ret = BinOp(nmlop.OR, ret, byte, cargo.pos) return ret.reduce()
def house_prop_0A(value): # User sets an array [min_year, max_year] as property value # House property 0A is set to ((max_year - 1920) << 8) | (min_year - 1920) # With both bytes clamped to the 0 .. 255 range if not isinstance(value, Array) or len(value.values) != 2: raise generic.ScriptError("Availability years must be an array with exactly two values", value.pos) min_year = BinOp(nmlop.SUB, value.values[0], ConstantNumeric(1920, value.pos), value.pos) min_year = BinOp(nmlop.MAX, min_year, ConstantNumeric(0, value.pos), value.pos) min_year = BinOp(nmlop.MIN, min_year, ConstantNumeric(255, value.pos), value.pos) max_year = BinOp(nmlop.SUB, value.values[1], ConstantNumeric(1920, value.pos), value.pos) max_year = BinOp(nmlop.MAX, max_year, ConstantNumeric(0, value.pos), value.pos) max_year = BinOp(nmlop.MIN, max_year, ConstantNumeric(255, value.pos), value.pos) max_year = BinOp(nmlop.SHIFT_LEFT, max_year, ConstantNumeric(8, value.pos), value.pos) return BinOp(nmlop.OR, max_year, min_year, value.pos).reduce()
def two_byte_property(low_prop, high_prop, low_prop_info = {}, high_prop_info = {}): """ Decode a two byte value into two action 0 properties. @param low_prop: Property number for the low 8 bits of the value. @type low_prop: C{int} @param high_prop: Property number for the high 8 bits of the value. @type high_prop: C{int} @param low_prop_info: Dictionary with additional property information for the low byte. @type low_prop_info: C{dict} @param high_prop_info: Dictionary with additional property information for the low byte. @type high_prop_info: C{dict} @return: Sequence of two dictionaries with property information (low part, high part). @rtype: C{list} of C{dict} """ low_byte_info = {'num': low_prop, 'size': 1, 'value_function': lambda value: BinOp(nmlop.AND, value, ConstantNumeric(0xFF, value.pos), value.pos).reduce()} high_byte_info = {'num': high_prop, 'size': 1, 'value_function': lambda value: BinOp(nmlop.SHIFT_RIGHT, value, ConstantNumeric(8, value.pos), value.pos).reduce()} low_byte_info.update(low_prop_info) high_byte_info.update(high_prop_info) return [low_byte_info, high_byte_info]
def mt_house_mask(mask, value, num_ids, size_bit): # Mask out the bits not present in the 'mask' parameter for additional tiles ret = [value] for _i in range(1, num_ids): ret.append(BinOp(nmlop.AND, value, ConstantNumeric(mask, value.pos), value.pos).reduce()) return ret
def aircraft_is_large(value): return BinOp(nmlop.AND, value, ConstantNumeric(1, value.pos), value.pos).reduce()
def aircraft_is_heli(value): if isinstance(value, ConstantNumeric) and not value.value in (0, 2, 3): raise generic.ScriptError("Invalid value for aircraft_type", value.pos) return BinOp(nmlop.AND, value, ConstantNumeric(2, value.pos), value.pos).reduce()
def speed_fraction(value): # Unit is already converted to 0 .. 255 range when we get here if isinstance(value, ConstantNumeric) and not (0 <= value.value <= 255): # Do not use check_range to provide better error message raise generic.ScriptError("speed fraction must be in range 0 .. 1", value.pos) return BinOp(nmlop.SUB, ConstantNumeric(255, value.pos), value, value.pos).reduce()
def vehicle_length(value): if isinstance(value, ConstantNumeric): generic.check_range(value.value, 1, 8, "vehicle length", value.pos) return BinOp(nmlop.SUB, ConstantNumeric(8, value.pos), value, value.pos).reduce()