Exemple #1
0
    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
Exemple #2
0
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
Exemple #3
0
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