def preprocess_storageop(self, expr): assert isinstance(expr, expression.StorageOp) if expr.info["perm"] and not self.var_scope.has_persistent_storage: raise generic.ScriptError( "Persistent storage is not supported for feature '{}'".format( self.var_scope.name), expr.pos, ) if expr.info["store"]: op = nmlop.STO_PERM if expr.info["perm"] else nmlop.STO_TMP ret = expression.BinOp(op, expr.value, expr.register, expr.pos) else: var_num = 0x7C if expr.info["perm"] else 0x7D ret = expression.Variable(expression.ConstantNumeric(var_num), param=expr.register, pos=expr.pos) if expr.info[ "perm"] and self.var_scope is action2var_variables.scope_towns: # store grfid in register 0x100 for town persistent storage grfid = expression.ConstantNumeric( 0xFFFFFFFF if expr.grfid is None else expression. parse_string_to_dword(expr.grfid)) store_op = nmlop.STO_TMP(grfid, 0x100, expr.pos) ret = nmlop.VAL2(store_op, ret) elif expr.grfid is not None: raise generic.ScriptError( "Specifying a grfid is only possible for town persistent storage.", expr.pos) return ret
def parse_graphics_block_single_id(graphics_block, feature, id, is_livery_override, house_tile=None, house_north_tile_id=None): action6.free_parameters.save() prepend_action_list = [] action_list = [] act6 = action6.Action6() act3 = create_action3(feature, id, action_list, act6, is_livery_override) cargo_gfx = {} seen_callbacks = set() callbacks = [] livery_override = None # Used for rotor graphics for graphics in graphics_block.graphics_list: cargo_id = graphics.cargo_id if isinstance(cargo_id, expression.Identifier): cb_name = cargo_id.value cb_table = action3_callbacks.callbacks[feature] if cb_name in cb_table: if cb_name in seen_callbacks: raise generic.ScriptError( "Callback '{}' is defined multiple times.".format( cb_name), cargo_id.pos) seen_callbacks.add(cb_name) info_list = cb_table[cb_name] if not isinstance(info_list, list): info_list = [info_list] for info in info_list: if "deprecate_message" in info: generic.print_warning(generic.Warning.DEPRECATION, info["deprecate_message"], cargo_id.pos) if house_tile is not None and "tiles" in info and house_tile not in info[ "tiles"]: continue if info["type"] == "cargo": # Not a callback, but an alias for a certain cargo type if info["num"] in cargo_gfx: raise generic.ScriptError( "Graphics for '{}' are defined multiple times." .format(cb_name), cargo_id.pos) cargo_gfx[info["num"]] = graphics.result.value elif info["type"] == "cb": callbacks.append((info, graphics.result.value)) elif info["type"] == "override": assert livery_override is None livery_override = graphics.result.value else: raise AssertionError() continue # Not a callback, so it must be a 'normal' cargo (vehicles/stations only) cargo_id = cargo_id.reduce_constant(global_constants.const_list) # Raise the error only now, to let the 'unknown identifier' take precedence if feature >= 5: raise generic.ScriptError( "Associating graphics with a specific cargo is possible only for vehicles and stations.", cargo_id.pos) if cargo_id.value in cargo_gfx: raise generic.ScriptError( "Graphics for cargo {:d} are defined multiple times.".format( cargo_id.value), cargo_id.pos) cargo_gfx[cargo_id.value] = graphics.result.value if graphics_block.default_graphics is not None: if "default" not in action3_callbacks.callbacks[feature]: raise generic.ScriptError( "Default graphics may not be defined for this feature (0x{:02X})." .format(feature), graphics_block.default_graphics.pos, ) if None in cargo_gfx: raise generic.ScriptError("Default graphics are defined twice.", graphics_block.default_graphics.pos) cargo_gfx[None] = graphics_block.default_graphics.value # An in-between varaction2 is always needed for houses if len(callbacks) != 0 or feature == 0x07: cb_flags = 0 # Determine the default value if None not in cargo_gfx: cargo_gfx[None] = expression.SpriteGroupRef( expression.Identifier("CB_FAILED", None), [], None) default_val = cargo_gfx[None] cb_mapping = {} cb_buy_mapping = {} # Special case for vehicle cb 36, maps var10 values to spritegroups cb36_mapping = {} cb36_buy_mapping = {} # Sspecial case for industry production CB, maps var18 values to spritegroups prod_cb_mapping = {} for cb_info, gfx in callbacks: if "flag_bit" in cb_info: # Set a bit in the CB flags property cb_flags |= 1 << cb_info["flag_bit"] value_function = cb_info.get("value_function", None) mapping_val = (gfx, value_function) # See action3_callbacks for info on possible values purchase = cb_info.get("purchase", 0) if isinstance(purchase, str): # Not in purchase list, if separate purchase CB is set purchase = 0 if purchase in seen_callbacks else 1 # Explicit purchase CBs will need a purchase cargo, even if not needed for graphics if purchase == 2 and 0xFF not in cargo_gfx: cargo_gfx[0xFF] = default_val num = cb_info["num"] if num == 0x36: if purchase != 2: cb36_mapping[cb_info["var10"]] = mapping_val if purchase != 0: cb36_buy_mapping[cb_info["var10"]] = mapping_val elif feature == 0x0A and num == 0x00: # Industry production CB assert purchase == 0 prod_cb_mapping[cb_info["var18"]] = mapping_val else: if purchase != 2: cb_mapping[num] = mapping_val if purchase != 0: cb_buy_mapping[num] = mapping_val if cb_flags != 0: prepend_action_list.extend( action0.get_callback_flags_actions(feature, id, cb_flags)) # Handle CB 36 if len(cb36_mapping) != 0: expr = expression.Variable(expression.ConstantNumeric(0x10), mask=expression.ConstantNumeric(0xFF)) actions, cb36_ref = create_cb_choice_varaction2( feature, expr, cb36_mapping, default_val, graphics_block.pos) prepend_action_list.extend(actions) cb_mapping[0x36] = (cb36_ref, None) if len(cb36_buy_mapping) != 0: expr = expression.Variable(expression.ConstantNumeric(0x10), mask=expression.ConstantNumeric(0xFF)) actions, cb36_ref = create_cb_choice_varaction2( feature, expr, cb36_buy_mapping, default_val, graphics_block.pos) prepend_action_list.extend(actions) cb_buy_mapping[0x36] = (cb36_ref, None) if len(prod_cb_mapping) != 0: expr = expression.Variable(expression.ConstantNumeric(0x18), mask=expression.ConstantNumeric(0xFF)) actions, cb_ref = create_cb_choice_varaction2( feature, expr, prod_cb_mapping, default_val, graphics_block.pos) prepend_action_list.extend(actions) cb_mapping[0x00] = (cb_ref, None) for cargo in sorted(cargo_gfx, key=lambda x: -1 if x is None else x): mapping = cb_buy_mapping if cargo == 0xFF else cb_mapping if len(mapping) == 0 and feature != 0x07: # No callbacks here, so move along # Except for houses, where we need to store some stuff in a register continue if cargo_gfx[cargo] != default_val: # There are cargo-specific graphics, be sure to handle those # Unhandled callbacks should chain to the default, though mapping = mapping.copy() mapping[0x00] = (cargo_gfx[cargo], None) expr = expression.Variable(expression.ConstantNumeric(0x0C), mask=expression.ConstantNumeric(0xFFFF)) if feature == 0x07: # Store relative x/y, item id (of the north tile) and house tile (HOUSE_TILE_XX constant) in register FF # Format: 0xIIHHYYXX: II: item ID, HH: house tile, YY: relative y, XX: relative x lowbytes_dict = { "n": 0x000000, "e": 0x010100, "w": 0x020001, "s": 0x030101, } lowbytes = expression.ConstantNumeric( lowbytes_dict[house_tile]) highbyte = nmlop.SHIFT_LEFT(house_north_tile_id, 24) register_FF = nmlop.OR(lowbytes, highbyte).reduce() register_FF = nmlop.STO_TMP(register_FF, 0xFF) expr = nmlop.VAL2(register_FF, expr) if len(mapping) == 0: # mapping must not be empty mapping[0x00] = (default_val, None) actions, cb_ref = create_cb_choice_varaction2( feature, expr, mapping, default_val, graphics_block.pos) prepend_action_list.extend(actions) cargo_gfx[cargo] = cb_ref # Make sure to sort to make the order well-defined offset = 7 if feature <= 3 else 5 for cargo_id in sorted(cg for cg in cargo_gfx if cg is not None): result, comment = action2var.parse_result(cargo_gfx[cargo_id], action_list, act6, offset + 1, act3, None, 0x89) act3.cid_mappings.append((cargo_id, result, comment)) offset += 3 if None in cargo_gfx: result, comment = action2var.parse_result(cargo_gfx[None], action_list, act6, offset, act3, None, 0x89) act3.def_cid = result act3.default_comment = comment else: act3.def_cid = None act3.default_comment = "" if livery_override is not None: act6livery = action6.Action6() # Add any extra actions before the main action3 (TTDP requirement) act3livery = create_action3(feature, id, action_list, act6livery, True) offset = 7 if feature <= 3 else 5 result, comment = action2var.parse_result(livery_override, action_list, act6livery, offset, act3livery, None, 0x89) act3livery.def_cid = result act3livery.default_comment = comment if len(act6.modifications) > 0: action_list.append(act6) action_list.append(act3) if livery_override is not None: if len(act6livery.modifications) > 0: action_list.append(act6livery) action_list.append(act3livery) # lgtm[py/uninitialized-local-variable] action6.free_parameters.restore() return prepend_action_list + action_list
def parse_randomswitch_type(random_switch): """ Parse the type of a random switch to determine the type and random bits to use. @param random_switch: Random switch to parse the type of @type random_switch: L{RandomSwitch} @return: A tuple containing the following: - The type byte of the resulting random action2. - The value to use as <count>, None if N/A. - Expression to parse in a preceding switch-block, None if N/A. - The first random bit that should be used (often 0) - The number of random bits available @rtype: C{tuple} of (C{int}, C{int} or C{None}, L{Expression} or C{None}, C{int}, C{int}) """ # Extract some stuff we'll often need type_str = random_switch.type.value type_pos = random_switch.type.pos feature_val = next(iter(random_switch.feature_set)) # Validate type name / param combination if type_str not in random_types[feature_val]: raise generic.ScriptError( "Invalid combination for random_switch feature {:d} and type '{}'. ".format(feature_val, type_str), type_pos ) type_info = random_types[feature_val][type_str] count_expr = None if random_switch.type_count is None: # No param given if type_info["param"] == 1: raise generic.ScriptError( "Value '{}' for random_switch parameter 2 'type' requires a parameter.".format(type_str), type_pos ) count = None else: # Param given if type_info["param"] == 0: raise generic.ScriptError( "Value '{}' for random_switch parameter 2 'type' should not have a parameter.".format(type_str), type_pos, ) if ( isinstance(random_switch.type_count, expression.ConstantNumeric) and 1 <= random_switch.type_count.value <= 15 ): count = random_switch.type_count.value else: count = 0 count_expr = nmlop.STO_TMP(random_switch.type_count, 0x100, type_pos) count = type_info["value"] | count if random_switch.triggers.value != 0 and not type_info["triggers"]: raise generic.ScriptError( "Triggers may not be set for random_switch feature {:d} and type '{}'. ".format(feature_val, type_str), type_pos, ) # Determine type byte and random bits type_byte = type_info["type"] start_bit = type_info["first_bit"] bits_available = type_info["num_bits"] return type_byte, count, count_expr, start_bit, bits_available