示例#1
0
文件: switch.py 项目: planetmaker/nml
    def pre_process(self):
        for choice in self.choices:
            choice.reduce_expressions(next(iter(self.feature_set)))

        for dep_list in (self.dependent, self.independent):
            for i, dep in enumerate(dep_list[:]):
                if dep.is_return:
                    raise generic.ScriptError(
                        "Expected a random_switch identifier after (in)dependent, not a return.",
                        dep.pos)
                dep_list[i] = dep.value.reduce(global_constants.const_list)
                # Make sure, all [in]dependencies refer to existing random switch blocks
                if (not isinstance(dep_list[i], expression.SpriteGroupRef)
                    ) or len(dep_list[i].param_list) > 0:
                    raise generic.ScriptError(
                        "Value for (in)dependent should be an identifier",
                        dep_list[i].pos)
                spritegroup = action2.resolve_spritegroup(dep_list[i].name)
                if not isinstance(spritegroup, RandomSwitch):
                    raise generic.ScriptError(
                        "Value of (in)dependent '{}' should refer to a random_switch."
                        .format(dep_list[i].name.value), dep_list[i].pos)

        self.triggers = self.triggers.reduce_constant(
            global_constants.const_list)
        if not (0 <= self.triggers.value <= 255):
            raise generic.ScriptError(
                "random_switch parameter 4 'triggers' out of range 0..255, encountered "
                + str(self.triggers.value), self.triggers.pos)

        switch_base_class.pre_process(self)
示例#2
0
    def resolve_spritegroup_ref(self, sg_ref):
        """
        Resolve a reference to a (sprite/palette) sprite group

        @param sg_ref: Reference to a sprite group
        @type sg_ref: L{SpriteGroupRef}

        @return: Sprite number (index of action1 set) to use
        @rtype: L{Expression}
        """
        spriteset = action2.resolve_spritegroup(sg_ref.name)

        if len(sg_ref.param_list) == 0:
            offset = None
        elif len(sg_ref.param_list) == 1:
            id_dicts = [(spriteset.labels, lambda val, pos: expression.ConstantNumeric(val, pos))]
            offset = action2var.reduce_varaction2_expr(sg_ref.param_list[0], self.feature, self.extra_dicts + id_dicts)
            if isinstance(offset, expression.ConstantNumeric):
                generic.check_range(offset.value, 0, len(real_sprite.parse_sprite_data(spriteset)) - 1, "offset within spriteset", sg_ref.pos)
        else:
            raise generic.ScriptError("Expected 0 or 1 parameter, got " + str(len(sg_ref.param_list)), sg_ref.pos)

        num = action1.get_action1_index(spriteset)
        generic.check_range(num, 0, (1 << 14) - 1, "sprite", sg_ref.pos)
        return expression.ConstantNumeric(num), offset
示例#3
0
    def pre_process(self):
        feature = next(iter(self.feature_set))
        # var_feature is really weird for type=BACKWARD/FORWARD.
        # Expressions in cases will still refer to the origin vehicle.
        var_feature = action2var_variables.varact2parent_scope[feature] if self.type.value == "PARENT" else feature

        for choice in self.choices:
            choice.reduce_expressions(var_feature)

        for dep_list in (self.dependent, self.independent):
            for i, dep in enumerate(dep_list[:]):
                if dep.is_return:
                    raise generic.ScriptError(
                        "Expected a random_switch identifier after (in)dependent, not a return.", dep.pos
                    )
                dep_list[i] = dep.value.reduce(global_constants.const_list)
                # Make sure, all [in]dependencies refer to existing random switch blocks
                if (not isinstance(dep_list[i], expression.SpriteGroupRef)) or len(dep_list[i].param_list) > 0:
                    raise generic.ScriptError("Value for (in)dependent should be an identifier", dep_list[i].pos)
                spritegroup = action2.resolve_spritegroup(dep_list[i].name)
                if not isinstance(spritegroup, RandomSwitch):
                    raise generic.ScriptError(
                        "Value of (in)dependent '{}' should refer to a random_switch.".format(dep_list[i].name.value),
                        dep_list[i].pos,
                    )

        self.triggers = self.triggers.reduce_constant(global_constants.const_list)
        if not (0 <= self.triggers.value <= 255):
            raise generic.ScriptError(
                "random_switch parameter 4 'triggers' out of range 0..255, encountered " + str(self.triggers.value),
                self.triggers.pos,
            )

        switch_base_class.pre_process(self)
示例#4
0
 def __init__(self, sg_ref):
     if not isinstance(action2.resolve_spritegroup(sg_ref.name), (switch.Switch, switch.RandomSwitch)):
         raise generic.ScriptError("Block with name '{}' is not a valid procedure".format(sg_ref.name), sg_ref.pos)
     if not sg_ref.is_procedure:
         raise generic.ScriptError("Unexpected identifier encountered: '{}'".format(sg_ref.name), sg_ref.pos)
     VarAction2Var.__init__(self, 0x7E, 0, 0, comment=str(sg_ref))
     # Reference to the called action2
     self.sg_ref = sg_ref
示例#5
0
 def pre_process(self):
     self.spriteset_list = [
         x.reduce(global_constants.const_list) for x in self.spriteset_list
     ]
     for sg_ref in self.spriteset_list:
         if not (isinstance(sg_ref, expression.SpriteGroupRef) and
                 action2.resolve_spritegroup(sg_ref.name).is_spriteset()):
             raise generic.ScriptError("Expected a sprite set reference",
                                       sg_ref.pos)
         if len(sg_ref.param_list) != 0:
             raise generic.ScriptError(
                 "Spritesets referenced from a spritegroup may not have parameters.",
                 sg_ref.pos)
示例#6
0
def lookup_random_action2(sg_ref):
    """
    Lookup a sprite group reference to find the corresponding random action2

    @param sg_ref: Reference to random action2
    @type sg_ref: L{SpriteGroupRef}

    @return: Random action2 corresponding to this sprite group, or None if N/A
    @rtype: L{Action2Random} or C{None}
    """
    spritegroup = action2.resolve_spritegroup(sg_ref.name)
    act2 = spritegroup.random_act2
    assert act2 is None or isinstance(act2, Action2Random)
    return act2
示例#7
0
def get_real_action2s(spritegroup, feature):
    loaded_list = []
    loading_list = []
    actions = []

    if feature not in action2.features_sprite_group:
        raise generic.ScriptError("Sprite groups that combine sprite sets are not supported for feature '{}'.".format(general.feature_name(feature)), spritegroup.pos)

    # First make sure that all referenced real sprites are put in a single action1
    spriteset_list = []
    for view in spritegroup.spriteview_list:
        spriteset_list.extend([action2.resolve_spritegroup(sg_ref.name) for sg_ref in view.spriteset_list])
    actions.extend(action1.add_to_action1(spriteset_list, feature, spritegroup.pos))

    view_names = sorted([view.name.value for view in spritegroup.spriteview_list])
    if feature in (0x00, 0x01, 0x02, 0x03):
        if view_names != sorted(['loading', 'loaded']):
            raise generic.ScriptError("Expected a 'loading' and a 'loaded' (list of) sprite set(s).", spritegroup.pos)
    elif feature in (0x05, 0x0B, 0x0D, 0x10):
        msg = "Sprite groups for feature {:02X} will not be supported in the future, as they are no longer needed. Directly refer to sprite sets instead."
        msg = msg.format(feature)
        generic.print_warning(msg, spritegroup.pos)
        if view_names != ['default']:
            raise generic.ScriptError("Expected only a 'default' (list of) sprite set(s).", spritegroup.pos)

    for view in spritegroup.spriteview_list:
        if len(view.spriteset_list) == 0:
            raise generic.ScriptError("Expected at least one sprite set, encountered 0.", view.pos)
        for set_ref in view.spriteset_list:
            spriteset = action2.resolve_spritegroup(set_ref.name)
            action1_index = action1.get_action1_index(spriteset)
            if view.name.value == 'loading': loading_list.append(action1_index)
            else: loaded_list.append(action1_index)

    actions.append(Action2Real(feature, spritegroup.name.value + " - feature {:02X}".format(feature), spritegroup.pos, loaded_list, loading_list))
    spritegroup.set_action2(actions[-1], feature)
    return actions
示例#8
0
    def parse_proc_call(self, expr):
        assert isinstance(expr, expression.SpriteGroupRef)
        var_access = VarAction2ProcCallVar(expr)
        target = action2.resolve_spritegroup(expr.name)
        refs = expr.collect_references()

        # Fill param registers for the call
        tmp_vars = []
        for i, param in enumerate(expr.param_list):
            if i > 0:  # No operator before first param as per advanced VarAct2 syntax
                self.var_list.append(nmlop.VAL2)
                self.var_list_size += 1
            if refs != [expr]:
                # For f(x, g(y)), x can be overwritten by y if f and g share the same param registers
                # Use temporary variables as an intermediate step
                store_tmp = VarAction2StoreTempVar()
                tmp_vars.append((
                    VarAction2LoadTempVar(store_tmp),
                    VarAction2StoreCallParam(
                        target.register_map[self.action_feature][i]),
                ))
            else:
                store_tmp = VarAction2StoreCallParam(
                    target.register_map[self.action_feature][i])
            self.parse_expr(reduce_varaction2_expr(param, self.var_scope))
            self.var_list.append(nmlop.STO_TMP)
            self.var_list.append(store_tmp)
            self.var_list_size += store_tmp.get_size(
            ) + 1  # Add 1 for operator

        # Fill param registers with temporary variables if needed
        for (src, dest) in tmp_vars:
            self.var_list.append(nmlop.VAL2)
            self.var_list.append(src)
            self.var_list.append(nmlop.STO_TMP)
            self.var_list.append(dest)
            self.var_list_size += src.get_size() + dest.get_size(
            ) + 2  # Add 2 for operators

        if expr.param_list:
            self.var_list.append(nmlop.VAL2)
            self.var_list_size += 1
        self.var_list.append(var_access)
        self.var_list_size += var_access.get_size()
        self.proc_call_list.append(expr)
示例#9
0
    def get_action2_id(self, feature):
        """
        Get the action2 set-ID that this reference maps to

        @param feature: Feature of the action2
        @type feature: C{int}

        @return: The set ID
        @rtype: C{int}
        """
        if self.act2 is not None: return self.act2.id
        if self.name.value == 'CB_FAILED': return 0 # 0 serves as a failed CB result because it is never used
        try:
            spritegroup = action2.resolve_spritegroup(self.name)
        except generic.ScriptError:
            raise AssertionError("Illegal action2 reference '{}' encountered.".format(self.name.value))

        return spritegroup.get_action2(feature).id
示例#10
0
def get_layout_action2s(spritelayout, feature, spr_pos):
    """
    @param spr_pos: Position information of the sprite view.
    @type  spr_pos: L{Position}
    """
    ground_sprite = None
    building_sprites = []
    actions = []

    if feature not in action2.features_sprite_layout:
        raise generic.ScriptError(
            "Sprite layouts are not supported for feature '{}'.".format(
                general.feature_name(feature)))

    # Allocate registers
    param_map = {}
    param_registers = []
    for param in spritelayout.param_list:
        reg = action2var.VarAction2CallParam(param.value)
        param_registers.append(reg)
        param_map[param.value] = reg
    param_map = (param_map, lambda name, value, pos: action2var.
                 VarAction2LoadCallParam(value, name))
    spritelayout.register_map[feature] = param_registers

    # Reduce all expressions, can't do that earlier as feature is not known
    all_sprite_sets = []
    layout_sprite_list = []  # Create a new structure
    for layout_sprite in spritelayout.layout_sprite_list:
        param_list = []
        layout_sprite_list.append(
            (layout_sprite.type, layout_sprite.pos, param_list))
        for param in layout_sprite.param_list:
            param_val = action2var.reduce_varaction2_expr(
                param.value, action2var.get_scope(feature), [param_map])
            param_list.append((param.name, param_val))
            if isinstance(param_val, expression.SpriteGroupRef):
                spriteset = action2.resolve_spritegroup(param_val.name)
                if not spriteset.is_spriteset():
                    raise generic.ScriptError(
                        "Expected a reference to a spriteset.", param_val.pos)
                all_sprite_sets.append(spriteset)
    actions.extend(
        action1.add_to_action1(all_sprite_sets, feature, spritelayout.pos))

    temp_registers = []
    for type, pos, param_list in layout_sprite_list:
        if type.value not in layout_sprite_types:
            raise generic.ScriptError(
                "Invalid sprite type '{}' encountered. Expected 'ground', 'building', or 'childsprite'."
                .format(type.value),
                type.pos,
            )
        sprite = Action2LayoutSprite(feature, layout_sprite_types[type.value],
                                     pos, [param_map])
        for name, value in param_list:
            sprite.set_param(name, value)
        temp_registers.extend(sprite.get_all_registers())
        if sprite.type == Action2LayoutSpriteType.GROUND:
            if ground_sprite is not None:
                raise generic.ScriptError(
                    "Sprite layout can have no more than one ground sprite",
                    spritelayout.pos)
            ground_sprite = sprite
        else:
            building_sprites.append(sprite)

    if ground_sprite is None:
        if len(building_sprites) == 0:
            # no sprites defined at all, that's not very much.
            raise generic.ScriptError(
                "Sprite layout requires at least one sprite", spr_pos)
        # set to 0 for no ground sprite
        ground_sprite = Action2LayoutSprite(feature,
                                            Action2LayoutSpriteType.GROUND)
        ground_sprite.set_param(expression.Identifier("sprite"),
                                expression.ConstantNumeric(0))

    action6.free_parameters.save()
    act6 = action6.Action6()

    advanced = any(x.is_advanced_sprite()
                   for x in building_sprites + [ground_sprite])

    offset = 4
    sprite_num = ground_sprite.get_sprite_number()
    sprite_num, offset = actionD.write_action_value(sprite_num, actions, act6,
                                                    offset, 4)
    if advanced:
        offset += ground_sprite.get_registers_size()

    for sprite in building_sprites:
        sprite_num = sprite.get_sprite_number()
        sprite_num, offset = actionD.write_action_value(
            sprite_num, actions, act6, offset, 4)
        if advanced:
            offset += sprite.get_registers_size()
        offset += 3 if sprite.type == Action2LayoutSpriteType.CHILD else 6

    if len(act6.modifications) > 0:
        actions.append(act6)

    layout_action = Action2Layout(
        feature,
        spritelayout.name.value + " - feature {:02X}".format(feature),
        spritelayout.pos,
        ground_sprite,
        building_sprites,
        param_registers,
    )
    actions.append(layout_action)

    if temp_registers:
        varact2parser = action2var.Varaction2Parser(
            feature, action2var.get_scope(feature))
        for register_info in temp_registers:
            reg, expr = register_info[1], register_info[2]
            if reg is None:
                continue
            varact2parser.parse_expr(expr)
            varact2parser.var_list.append(nmlop.STO_TMP)
            varact2parser.var_list.append(reg)
            varact2parser.var_list.append(nmlop.VAL2)
            varact2parser.var_list_size += reg.get_size() + 2

    # Only continue if we actually needed any new registers
    if temp_registers and varact2parser.var_list:  # lgtm[py/uninitialized-local-variable]
        # Remove the last VAL2 operator
        varact2parser.var_list.pop()
        varact2parser.var_list_size -= 1

        actions.extend(varact2parser.extra_actions)
        extra_act6 = action6.Action6()
        for mod in varact2parser.mods:
            extra_act6.modify_bytes(mod.param, mod.size, mod.offset + 4)
        if len(extra_act6.modifications) > 0:
            actions.append(extra_act6)

        varaction2 = action2var.Action2Var(
            feature,
            "{}@registers - feature {:02X}".format(spritelayout.name.value,
                                                   feature), spritelayout.pos,
            0x89)
        varaction2.var_list = varact2parser.var_list
        ref = expression.SpriteGroupRef(spritelayout.name, [], None,
                                        layout_action)
        varaction2.ranges.append(
            action2var.VarAction2Range(expression.ConstantNumeric(0),
                                       expression.ConstantNumeric(0), ref, ""))
        varaction2.default_result = ref
        varaction2.default_comment = ""

        # Add two references (default + range)
        # Make sure that registers allocated here are not used by the spritelayout
        action2.add_ref(ref, varaction2, True)
        action2.add_ref(ref, varaction2, True)
        spritelayout.set_action2(varaction2, feature)
        actions.append(varaction2)
    else:
        spritelayout.set_action2(layout_action, feature)

    action6.free_parameters.restore()
    return actions
示例#11
0
def parse_sg_ref_result(result, action_list, parent_action, var_range):
    """
    Parse a result that is a sprite group reference.

    @param result: Result to parse
    @type result: L{SpriteGroupRef}

    @param action_list: List to append any extra actions to
    @type action_list: C{list} of L{BaseAction}

    @param parent_action: Reference to the action of which this is a result
    @type parent_action: L{BaseAction}

    @param var_range: Variable range to use for variables in the expression
    @type var_range: C{int}

    @return: Result to use in the calling varaction2
    @rtype: L{SpriteGroupRef}
    """
    if result.name.value == "CB_FAILED":
        return get_failed_cb_result(parent_action.feature, action_list,
                                    parent_action, result.pos)

    if len(result.param_list) == 0:
        action2.add_ref(result, parent_action)
        return result

    # Result is parametrized
    # Insert an intermediate varaction2 to store expressions in registers
    var_scope = get_scope(parent_action.feature, var_range)
    varact2parser = Varaction2Parser(parent_action.feature, var_scope)
    layout = action2.resolve_spritegroup(result.name)
    for i, param in enumerate(result.param_list):
        if i > 0:
            varact2parser.var_list.append(nmlop.VAL2)
            varact2parser.var_list_size += 1
        varact2parser.parse_expr(reduce_varaction2_expr(param, var_scope))
        varact2parser.var_list.append(nmlop.STO_TMP)
        store_tmp = VarAction2StoreCallParam(
            layout.register_map[parent_action.feature][i])
        varact2parser.var_list.append(store_tmp)
        varact2parser.var_list_size += store_tmp.get_size(
        ) + 1  # Add 1 for operator

    action_list.extend(varact2parser.extra_actions)
    extra_act6 = action6.Action6()
    for mod in varact2parser.mods:
        extra_act6.modify_bytes(mod.param, mod.size, mod.offset + 4)
    if len(extra_act6.modifications) > 0:
        action_list.append(extra_act6)

    global return_action_id
    name = "@return_action_{:d}".format(return_action_id)
    varaction2 = Action2Var(parent_action.feature, name, result.pos, var_range)
    return_action_id += 1
    varaction2.var_list = varact2parser.var_list
    ref = expression.SpriteGroupRef(result.name, [], result.pos)
    varaction2.ranges.append(
        VarAction2Range(expression.ConstantNumeric(0),
                        expression.ConstantNumeric(0), ref, result.name.value))
    varaction2.default_result = ref
    varaction2.default_comment = result.name.value
    # Add the references as procs, to make sure, that any intermediate registers
    # are freed at the spritelayout and thus not selected to pass parameters
    # Reference is used twice (range + default) so call add_ref twice
    action2.add_ref(ref, varaction2, True)
    action2.add_ref(ref, varaction2, True)

    ref = expression.SpriteGroupRef(expression.Identifier(name), [], None,
                                    varaction2)
    action_list.append(varaction2)
    action2.add_ref(ref, parent_action)

    return ref
示例#12
0
 def is_read_only(self):
     if self.name.value != "CB_FAILED":
         spritegroup = action2.resolve_spritegroup(self.name)
         return spritegroup.is_read_only()
     return True
示例#13
0
 def reduce(self, id_dicts=None, unknown_id_fatal=True):
     if self.name.value != "CB_FAILED" and not self.is_procedure:
         spritegroup = action2.resolve_spritegroup(self.name)
         if spritegroup.optimise():
             return spritegroup.optimised
     return self