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_60x_var(name, args, pos, info): if "param_function" in info: # Special function to extract parameters if there is more than one param, extra_params = info["param_function"](name, args, pos, info) else: # Default function to extract parameters param, extra_params = action2var_variables.default_60xvar(name, args, pos, info) if isinstance(param, expression.ConstantNumeric) and (0 <= param.value <= 255): res = expression.Variable( expression.ConstantNumeric(info["var"]), expression.ConstantNumeric(info["start"]), expression.ConstantNumeric((1 << info["size"]) - 1), param, pos, ) res.extra_params.extend(extra_params) else: # Make use of var 7B to pass non-constant parameters var = expression.Variable( expression.ConstantNumeric(0x7B), expression.ConstantNumeric(info["start"]), expression.ConstantNumeric((1 << info["size"]) - 1), expression.ConstantNumeric(info["var"]), pos, ) var.extra_params.extend(extra_params) # Set the param in the accumulator beforehand res = nmlop.VAL2(param, var, pos) if "value_function" in info: res = info["value_function"](res, info) return res
def house_same_class(var, info): # Just using var 44 fails for non-north house tiles, as these have no class # Therefore work around it using var 61 # Load ID of the north tile from register FF bits 24..31, and use that as param for var 61 north_tile = expression.Variable(expression.ConstantNumeric(0x7D), expression.ConstantNumeric(24), expression.ConstantNumeric(0xFF), expression.ConstantNumeric(0xFF), var.pos) var61 = expression.Variable(expression.ConstantNumeric(0x7B), expression.ConstantNumeric(info['start']), expression.ConstantNumeric((1 << info['size']) - 1), expression.ConstantNumeric(0x61), var.pos) return nmlop.VAL2(north_tile, var61, var.pos)
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