예제 #1
0
파일: action3.py 프로젝트: Xaroth/nml
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(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:
                        assert False
                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
예제 #2
0
파일: action3.py 프로젝트: Xaroth/nml
def create_intermediate_varaction2(feature, varact2parser, mapping, default,
                                   pos):
    """
    Create a varaction2 based on a parsed expression and a value mapping

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

    @param varact2parser: Parser containing a parsed expression
    @type varact2parser: L{Varaction2Parser}

    @param mapping: Mapping of various values to sprite groups / return values,
                    with a possible extra function to apply to the return value
    @type mapping: C{dict} that maps C{int} to C{tuple} of (L{SpriteGroupRef}, C{function}, or C{None})

    @param default: Default sprite group if no value matches
    @type default: L{SpriteGroupRef}

    @param pos: Positional context.
    @type  pos: L{Position}

    @return: A tuple containing the action list and a reference to the created action2
    @rtype: C{tuple} of (C{list} of L{BaseAction}, L{SpriteGroupRef})
    """
    global action2_id

    action_list = varact2parser.extra_actions
    act6 = action6.Action6()
    for mod in varact2parser.mods:
        act6.modify_bytes(mod.param, mod.size, mod.offset + 4)

    name = expression.Identifier("@action3_{:d}".format(action2_id))
    action2_id += 1
    varaction2 = action2var.Action2Var(feature, name.value, pos, 0x89)
    varaction2.var_list = varact2parser.var_list
    offset = 5 + varact2parser.var_list_size
    for proc in varact2parser.proc_call_list:
        action2.add_ref(proc, varaction2, True)

    for switch_value in sorted(mapping):
        return_value, ret_value_function = mapping[switch_value]
        if ret_value_function is None:
            result, comment = action2var.parse_result(return_value,
                                                      action_list, act6,
                                                      offset, varaction2, None,
                                                      0x89)
        else:
            if isinstance(return_value, expression.SpriteGroupRef):
                # We need to execute the callback via a procedure call
                # then return CB_FAILED if the CB failed,
                # or the CB result (with ret_value_function applied) if successful
                if return_value.name.value == "CB_FAILED":
                    result, comment = action2var.parse_result(
                        return_value, action_list, act6, offset, varaction2,
                        None, 0x89)
                else:
                    extra_actions, result, comment = create_proc_call_varaction2(
                        feature, return_value, ret_value_function, pos)
                    action_list.extend(extra_actions)
            else:
                return_value = ret_value_function(return_value).reduce()
                result, comment = action2var.parse_result(
                    return_value, action_list, act6, offset, varaction2, None,
                    0x89)

        varaction2.ranges.append(
            action2var.VarAction2Range(
                expression.ConstantNumeric(switch_value),
                expression.ConstantNumeric(switch_value), result, comment))
        offset += 10
    result, comment = action2var.parse_result(default, action_list, act6,
                                              offset, varaction2, None, 0x89)
    varaction2.default_result = result
    varaction2.default_comment = comment

    return_ref = expression.SpriteGroupRef(name, [], None, varaction2)
    if len(act6.modifications) > 0:
        action_list.append(act6)
    action_list.append(varaction2)
    return (action_list, return_ref)
예제 #3
0
def parse_actionD(assignment):
    assignment.value.supported_by_actionD(True)

    if isinstance(assignment.param, expression.SpecialParameter):
        assignment.param, assignment.value = assignment.param.to_assignment(assignment.value)
    elif isinstance(assignment.param, expression.Identifier):
        if global_constants.identifier_refcount[assignment.param.value] == 0:
            # Named parameter is not referenced, ignoring
            return []
        assignment.param = expression.Parameter(
            expression.ConstantNumeric(global_constants.named_parameters[assignment.param.value]), assignment.param.pos
        )
    assert isinstance(assignment.param, expression.Parameter)

    if isinstance(assignment.value, expression.SpecialParameter):
        assignment.value = assignment.value.to_reading()

    if isinstance(assignment.value, expression.TernaryOp):
        return parse_ternary_op(assignment)

    if isinstance(assignment.value, expression.SpecialCheck):
        return parse_special_check(assignment)

    if isinstance(assignment.value, expression.GRMOp):
        return parse_grm(assignment)

    if isinstance(assignment.value, expression.BinOp):
        op = assignment.value.op
        if op == nmlop.HASBIT or op == nmlop.NOTHASBIT:
            return parse_hasbit(assignment)
        elif op == nmlop.MIN or op == nmlop.MAX:
            return parse_min_max(assignment)

    if isinstance(assignment.value, expression.Boolean):
        return parse_boolean(assignment)

    if isinstance(assignment.value, expression.Not):
        expr = nmlop.SUB(1, assignment.value.expr)
        assignment = ParameterAssignment(assignment.param, expr)

    if isinstance(assignment.value, expression.BinNot):
        expr = nmlop.SUB(0xFFFFFFFF, assignment.value.expr)
        assignment = ParameterAssignment(assignment.param, expr)

    action6.free_parameters.save()
    action_list = []
    act6 = action6.Action6()
    assert isinstance(assignment.param, expression.Parameter)
    target = assignment.param.num
    if isinstance(target, expression.Parameter) and isinstance(target.num, expression.ConstantNumeric):
        act6.modify_bytes(target.num.value, 1, 1)
        target = expression.ConstantNumeric(0)
    elif not isinstance(target, expression.ConstantNumeric):
        tmp_param, tmp_param_actions = get_tmp_parameter(target)
        act6.modify_bytes(tmp_param, 1, 1)
        target = expression.ConstantNumeric(0)
        action_list.extend(tmp_param_actions)

    data = None
    # print assignment.value
    if isinstance(assignment.value, expression.ConstantNumeric):
        op = nmlop.ASSIGN
        param1 = expression.ConstantNumeric(0xFF)
        param2 = expression.ConstantNumeric(0)
        data = assignment.value
    elif isinstance(assignment.value, expression.Parameter):
        if isinstance(assignment.value.num, expression.ConstantNumeric):
            op = nmlop.ASSIGN
            param1 = assignment.value.num
        else:
            tmp_param, tmp_param_actions = get_tmp_parameter(assignment.value.num)
            act6.modify_bytes(tmp_param, 1, 3)
            action_list.extend(tmp_param_actions)
            op = nmlop.ASSIGN
            param1 = expression.ConstantNumeric(0)
        param2 = expression.ConstantNumeric(0)
    elif isinstance(assignment.value, expression.OtherGRFParameter):
        op = nmlop.ASSIGN
        if isinstance(assignment.value.num, expression.ConstantNumeric):
            param1 = assignment.value.num
        else:
            tmp_param, tmp_param_actions = get_tmp_parameter(assignment.value.num)
            act6.modify_bytes(tmp_param, 1, 3)
            action_list.extend(tmp_param_actions)
            param1 = expression.ConstantNumeric(0)
        param2 = expression.ConstantNumeric(0xFE)
        data = expression.ConstantNumeric(expression.parse_string_to_dword(assignment.value.grfid))
    elif isinstance(assignment.value, expression.PatchVariable):
        op = nmlop.ASSIGN
        param1 = expression.ConstantNumeric(assignment.value.num)
        param2 = expression.ConstantNumeric(0xFE)
        data = expression.ConstantNumeric(0xFFFF)
    elif isinstance(assignment.value, expression.BinOp):
        op, expr1, expr2, extra_actions = transform_bin_op(assignment)
        action_list.extend(extra_actions)

        if isinstance(expr1, expression.ConstantNumeric):
            param1 = expression.ConstantNumeric(0xFF)
            data = expr1
        elif isinstance(expr1, expression.Parameter) and isinstance(expr1.num, expression.ConstantNumeric):
            param1 = expr1.num
        else:
            tmp_param, tmp_param_actions = get_tmp_parameter(expr1)
            action_list.extend(tmp_param_actions)
            param1 = expression.ConstantNumeric(tmp_param)

        # We can use the data only for one for the parameters.
        # If the first parameter uses "data" we need a temp parameter for this one
        if isinstance(expr2, expression.ConstantNumeric) and data is None:
            param2 = expression.ConstantNumeric(0xFF)
            data = expr2
        elif isinstance(expr2, expression.Parameter) and isinstance(expr2.num, expression.ConstantNumeric):
            param2 = expr2.num
        else:
            tmp_param, tmp_param_actions = get_tmp_parameter(expr2)
            action_list.extend(tmp_param_actions)
            param2 = expression.ConstantNumeric(tmp_param)

    else:
        raise generic.ScriptError("Invalid expression in argument assignment", assignment.value.pos)

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

    action_list.append(ActionD(target, param1, op, param2, data))
    action6.free_parameters.restore()
    return action_list
예제 #4
0
def parse_property_block(prop_list, feature, id, size):
    """
    Parse a property block to an action0 (with possibly various other actions)

    @param prop_list: List of properties to parse
    @type prop_list: C{list} of L{Property}

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

    @param id: ID of the associated item
    @type id: L{Expression}

    @param size: Size (for houses only)
    @type size: L{ConstantNumeric} or C{None}

    @return: List of resulting actions
    @rtype: C{list} of L{BaseAction}
    """
    action6.free_parameters.save()
    action_list = []
    action_list_append = []
    act6 = action6.Action6()

    action0, offset = create_action0(feature, id, act6, action_list)
    if feature == 0x07:
        size_bit = size.value if size is not None else 0
        action0.num_ids = house_sizes[size_bit]
    else:
        size_bit = None
        action0.num_ids = 1

    prop_info_list = []
    value_list_list = []
    pos_list = []
    for prop in prop_list:
        new_prop_info_list = get_property_info_list(feature, prop.name)
        prop_info_list.extend(new_prop_info_list)
        value_list_list.extend(
            parse_property_value(prop_info, prop.value, prop.unit, size_bit) for prop_info in new_prop_info_list
        )
        pos_list.extend(prop.name.pos for i in prop_info_list)

    validate_prop_info_list(prop_info_list, pos_list, feature)

    for prop_info, value_list in zip(prop_info_list, value_list_list):
        if "test_function" in prop_info and not prop_info["test_function"](*value_list):
            continue
        props, extra_actions, mods, extra_append_actions = parse_property(prop_info, value_list, feature, id)
        action_list.extend(extra_actions)
        action_list_append.extend(extra_append_actions)
        for mod in mods:
            act6.modify_bytes(mod[0], mod[1], mod[2] + offset)
        for p in props:
            offset += p.get_size()
        action0.prop_list.extend(props)

    if len(act6.modifications) > 0:
        action_list.append(act6)
    if len(action0.prop_list) != 0:
        action_list.append(action0)

    action_list.extend(action_list_append)

    action6.free_parameters.restore()
    return action_list
예제 #5
0
파일: action0.py 프로젝트: stormcone/nml
def get_snowlinetable_action(snowline_table):
    assert len(snowline_table) == 12 * 32

    action6.free_parameters.save()
    action_list = []
    tmp_param_map = {}  #Cache for tmp parameters
    act6 = action6.Action6()

    act0, offset = create_action0(0x08, expression.ConstantNumeric(0), act6,
                                  action_list)
    act0.num_ids = 1
    offset += 1  # Skip property number

    data_table = []
    idx = 0
    while idx < len(snowline_table):
        val = snowline_table[idx]
        if isinstance(val, expression.ConstantNumeric):
            data_table.append(val.value)
            idx += 1
            continue

        if idx + 3 >= len(snowline_table):
            tmp_param, tmp_param_actions = actionD.get_tmp_parameter(val)
            tmp_param_map[val] = tmp_param
            act6.modify_bytes(tmp_param, 1, offset + idx)
            action_list.extend(tmp_param_actions)
            data_table.append(0)
            idx += 1
            continue

        # Merge the next 4 values together in a single parameter.
        val2 = expression.BinOp(nmlop.SHIFT_LEFT, snowline_table[idx + 1],
                                expression.ConstantNumeric(8))
        val3 = expression.BinOp(nmlop.SHIFT_LEFT, snowline_table[idx + 2],
                                expression.ConstantNumeric(16))
        val4 = expression.BinOp(nmlop.SHIFT_LEFT, snowline_table[idx + 3],
                                expression.ConstantNumeric(24))
        expr = expression.BinOp(nmlop.OR, val, val2)
        expr = expression.BinOp(nmlop.OR, expr, val3)
        expr = expression.BinOp(nmlop.OR, expr, val4)
        expr = expr.reduce()

        #Cache lookup, saves some ActionDs
        if expr in tmp_param_map:
            tmp_param, tmp_param_actions = tmp_param_map[expr], []
        else:
            tmp_param, tmp_param_actions = actionD.get_tmp_parameter(expr)
            tmp_param_map[expr] = tmp_param

        act6.modify_bytes(tmp_param, 4, offset + idx)
        action_list.extend(tmp_param_actions)
        data_table.extend([0, 0, 0, 0])
        idx += 4

    act0.prop_list.append(
        ByteListProp(0x10, ''.join([chr(x) for x in data_table])))
    if len(act6.modifications) > 0: action_list.append(act6)
    action_list.append(act0)
    action6.free_parameters.restore()
    return action_list
예제 #6
0
def parse_randomswitch(random_switch):
    """
    Parse a randomswitch block into actions

    @param random_switch: RandomSwitch block to parse
    @type random_switch: L{RandomSwitch}

    @return: List of actions
    @rtype: C{list} of L{BaseAction}
    """
    action_list = action2real.create_spriteset_actions(random_switch)
    feature = next(iter(random_switch.feature_set))
    type_byte, count, count_expr, start_bit, bits_available = parse_randomswitch_type(
        random_switch)

    total_prob = sum(
        [choice.probability.value for choice in random_switch.choices])
    assert total_prob > 0
    nrand = 1
    while nrand < total_prob:
        nrand <<= 1

    # Verify that enough random data is available
    if min(1 << bits_available, 0x80) < nrand:
        msg = "The maximum sum of all random_switch probabilities is {:d}, encountered {:d}."
        msg = msg.format(min(1 << bits_available, 0x80), total_prob)
        raise generic.ScriptError(msg, random_switch.pos)

    randbit, nrand = parse_randomswitch_dependencies(random_switch, start_bit,
                                                     bits_available, nrand)

    random_action2 = Action2Random(feature, random_switch.name.value,
                                   random_switch.pos, type_byte, count,
                                   random_switch.triggers.value, randbit,
                                   nrand)
    random_switch.random_act2 = random_action2

    action6.free_parameters.save()
    act6 = action6.Action6()
    offset = 8 if count is not None else 7

    #divide the 'extra' probabilities in an even manner
    i = 0
    resulting_prob = dict(
        (c, c.probability.value) for c in random_switch.choices)
    while i < (nrand - total_prob):
        best_choice = None
        best_ratio = 0
        for choice in random_switch.choices:
            #float division, so 9 / 10 = 0.9
            ratio = choice.probability.value / float(resulting_prob[choice] +
                                                     1)
            if ratio > best_ratio:
                best_ratio = ratio
                best_choice = choice
        assert best_choice is not None
        resulting_prob[best_choice] += 1
        i += 1

    for choice in random_switch.choices:
        res_prob = resulting_prob[choice]
        result, comment = action2var.parse_result(choice.result.value,
                                                  action_list, act6, offset,
                                                  random_action2, None, 0x89,
                                                  res_prob)
        offset += res_prob * 2
        comment = "({:d}/{:d}) -> ({:d}/{:d}): ".format(
            choice.probability.value, total_prob, res_prob, nrand) + comment
        random_action2.choices.append(
            RandomAction2Choice(result, res_prob, comment))

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

    action_list.append(random_action2)
    if count_expr is None:
        random_switch.set_action2(random_action2, feature)
    else:
        # Create intermediate varaction2
        varaction2 = action2var.Action2Var(
            feature, '{}@registers'.format(random_switch.name.value),
            random_switch.pos, 0x89)
        varact2parser = action2var.Varaction2Parser(feature)
        varact2parser.parse_expr(count_expr)
        varaction2.var_list = varact2parser.var_list
        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)
        ref = expression.SpriteGroupRef(random_switch.name, [], None,
                                        random_action2)
        varaction2.ranges.append(
            action2var.VarAction2Range(expression.ConstantNumeric(0),
                                       expression.ConstantNumeric(0), ref, ''))
        varaction2.default_result = ref
        varaction2.default_comment = ''

        # Add two references (default + range)
        action2.add_ref(ref, varaction2)
        action2.add_ref(ref, varaction2)
        random_switch.set_action2(varaction2, feature)
        action_list.append(varaction2)

    action6.free_parameters.restore()
    return action_list
예제 #7
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_feature = parent_action.feature if var_range == 0x89 else action2var_variables.varact2parent_scope[
        parent_action.feature]
    varact2parser = Varaction2Parser(var_feature)
    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_feature))
        varact2parser.var_list.append(nmlop.STO_TMP)
        store_tmp = VarAction2StoreLayoutParam(
            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
예제 #8
0
def parse_varaction2(switch_block):
    global return_action_id
    return_action_id = 0

    action6.free_parameters.save()
    act6 = action6.Action6()
    action_list = action2real.create_spriteset_actions(switch_block)

    feature = next(iter(switch_block.feature_set))
    varaction2 = Action2Var(feature, switch_block.name.value, switch_block.pos,
                            switch_block.var_range)

    expr = reduce_varaction2_expr(switch_block.expr, get_feature(switch_block))

    offset = 4  #first var

    parser = Varaction2Parser(get_feature(switch_block))
    parser.parse_expr(expr)
    action_list.extend(parser.extra_actions)
    for mod in parser.mods:
        act6.modify_bytes(mod.param, mod.size, mod.offset + offset)
    varaction2.var_list = parser.var_list
    offset += parser.var_list_size + 1  # +1 for the byte num-ranges
    for proc in parser.proc_call_list:
        action2.add_ref(proc, varaction2, True)

    none_result = None
    if any(x is not None and x.value is None
           for x in [r.result for r in switch_block.body.ranges] +
           [switch_block.body.default]):
        # Computed result is returned in at least one result
        if len(switch_block.body.ranges) == 0:
            # There is only a default, which is 'return computed result', so we're fine
            none_result = expression.ConstantNumeric(
                0)  # Return value does not matter
        else:
            # Add an extra action to return the computed value
            extra_actions, none_result = create_return_action(
                expression.Variable(expression.ConstantNumeric(0x1C)), feature,
                switch_block.name.value + "@return", 0x89)
            action_list.extend(extra_actions)

    used_ranges = []
    for r in switch_block.body.ranges:
        comment = str(r.min) + " .. " + str(r.max) + ": "

        range_result, range_comment = parse_result(r.result.value, action_list,
                                                   act6, offset, varaction2,
                                                   none_result,
                                                   switch_block.var_range)
        comment += range_comment
        offset += 2  # size of result

        range_min, offset, check_min = parse_minmax(r.min, r.unit, action_list,
                                                    act6, offset)
        range_max, offset, check_max = parse_minmax(r.max, r.unit, action_list,
                                                    act6, offset)

        range_overlap = False
        if check_min and check_max:
            for existing_range in used_ranges:
                if existing_range[
                        0] <= range_min.value and range_max.value <= existing_range[
                            1]:
                    generic.print_warning(
                        "Range overlaps with existing ranges so it'll never be reached",
                        r.min.pos)
                    range_overlap = True
                    break
            if not range_overlap:
                used_ranges.append([range_min.value, range_max.value])
                used_ranges.sort()
                i = 0
                while i + 1 < len(used_ranges):
                    if used_ranges[i + 1][0] <= used_ranges[i][1] + 1:
                        used_ranges[i][1] = max(used_ranges[i][1],
                                                used_ranges[i + 1][1])
                        used_ranges.pop(i + 1)
                    else:
                        i += 1

        if not range_overlap:
            varaction2.ranges.append(
                VarAction2Range(range_min, range_max, range_result, comment))

    if len(switch_block.body.ranges) == 0 and \
            (switch_block.body.default is None or switch_block.body.default.value is not None):
        # Computed result is not returned, but there are no ranges
        # Add one range, to avoid the nvar == 0 bear trap
        offset += 10
        varaction2.ranges.append(
            VarAction2Range(expression.ConstantNumeric(1),
                            expression.ConstantNumeric(0),
                            expression.ConstantNumeric(0),
                            "Bogus range to avoid nvar == 0"))

    # Handle default result
    if switch_block.body.default is not None:
        # there is a default value
        default_result = switch_block.body.default.value
    else:
        # Default to CB_FAILED
        default_result = expression.SpriteGroupRef(
            expression.Identifier('CB_FAILED', None), [], None)

    default, default_comment = parse_result(default_result, action_list, act6,
                                            offset, varaction2, none_result,
                                            switch_block.var_range)
    varaction2.default_result = default
    if switch_block.body.default is None:
        varaction2.default_comment = 'No default specified -> fail callback'
    elif switch_block.body.default.value is None:
        varaction2.default_comment = 'Return computed value'
    else:
        varaction2.default_comment = 'default: ' + default_comment

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

    action_list.append(varaction2)
    switch_block.set_action2(varaction2, feature)

    action6.free_parameters.restore()
    return action_list
예제 #9
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 i, param in enumerate(spritelayout.param_list):
        reg = action2var.VarAction2LayoutParam()
        param_registers.append(reg)
        param_map[param.value] = reg
    param_map = (
        param_map,
        lambda value, pos: action2var.VarAction2LoadLayoutParam(value))
    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, 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()

    offset = 4
    sprite_num = ground_sprite.get_sprite_number()
    sprite_num, offset = actionD.write_action_value(sprite_num, actions, act6,
                                                    offset, 4)
    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)
        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)
        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:
        #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
예제 #10
0
def get_production_actions(produce):
    """
    Get the action list that implements the given produce-block in nfo.

    @param produce: Produce-block to parse.
    @type produce: L{Produce}
    """
    action_list = []
    act6 = action6.Action6()
    action6.free_parameters.save()

    result_list = []
    varact2parser = action2var.Varaction2Parser(0x0A)
    if all(x.supported_by_actionD(False) for x in produce.param_list):
        version = 0
        offset = 4
        for i, param in enumerate(produce.param_list):
            result, offset = actionD.write_action_value(
                param, action_list, act6, offset, 2 if i < 5 else 1)
            result_list.append(result.value)
    else:
        version = 1
        for i, param in enumerate(produce.param_list):
            if isinstance(param, expression.StorageOp) and param.name == 'LOAD_TEMP' and \
                    isinstance(param.register, expression.ConstantNumeric):
                # We can load a register directly
                result_list.append(
                    action2var.VarAction2Var(0x7D, 0, 0xFFFFFFFF,
                                             param.register.value))
            else:
                if len(varact2parser.var_list) != 0:
                    varact2parser.var_list.append(nmlop.VAL2)
                    varact2parser.var_list_size += 1
                varact2parser.parse_expr(
                    action2var.reduce_varaction2_expr(param, 0x0A))
                store_tmp = action2var.VarAction2StoreTempVar()
                result_list.append(action2var.VarAction2LoadTempVar(store_tmp))
                varact2parser.var_list.append(nmlop.STO_TMP)
                varact2parser.var_list.append(store_tmp)
                varact2parser.var_list_size += store_tmp.get_size(
                ) + 1  # Add 1 for operator

    if len(act6.modifications) > 0: action_list.append(act6)
    prod_action = Action2Production(produce.name.value, produce.pos, version,
                                    result_list[0:3], result_list[3:5],
                                    result_list[5])
    action_list.append(prod_action)

    if len(varact2parser.var_list) == 0:
        produce.set_action2(prod_action, 0x0A)
    else:
        # Create intermediate varaction2
        varaction2 = action2var.Action2Var(
            0x0A, '{}@registers'.format(produce.name.value), produce.pos, 0x89)
        varaction2.var_list = varact2parser.var_list
        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)
        ref = expression.SpriteGroupRef(produce.name, [], None, prod_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)
        action2.add_ref(ref, varaction2)
        action2.add_ref(ref, varaction2)
        produce.set_action2(varaction2, 0x0A)
        action_list.append(varaction2)

    action6.free_parameters.restore()

    return action_list
예제 #11
0
파일: action4.py 프로젝트: Xaroth/nml
def get_string_action4s(feature, string_range, string, id=None):
    """
    Let a string from the lang files be used in the rest of NML.
    This may involve adding actions directly, but otherwise an ID is allocated and the string will be written later

    @param feature: Feature that uses the string
    @type feature: C{int}

    @param string_range: String range to use, either a value from L{string_ranges} or C{None} if N/A (item names)
    @type string_range: C{int} or C{None}

    @param string: String to parse
    @type string: L{expression.String}

    @param id: ID to use for this string, or C{None} if it will be allocated dynamically
               (random_id is true for the string range)
    @type id: L{Expression} or C{None}

    @return: A tuple of two values:
                - ID of the string (useful if allocated dynamically)
                - Resulting action list to be appended
    @rtype: C{tuple} of (C{int}, C{list} of L{BaseAction})
    """
    grfstrings.validate_string(string)
    write_action4s = True
    action6.free_parameters.save()
    actions = []

    mod = None
    if string_range is not None:
        size = 2
        if string_ranges[string_range]["random_id"]:
            # ID is allocated randomly, we will output the actions later
            write_action4s = False
            if (feature, string) in used_strings[string_range]:
                id_val = used_strings[string_range][(feature, string)]
            else:
                id_val = string_ranges[string_range]["ids"].pop()
                used_strings[string_range][(feature, string)] = id_val
        else:
            # ID must be supplied
            assert id is not None
            assert isinstance(id, expression.ConstantNumeric)
            id_val = id.value | (string_range << 8)
    else:
        # Not a string range, so we must have an id
        assert id is not None
        size = 3 if feature <= 3 else 1
        if isinstance(id, expression.ConstantNumeric):
            id_val = id.value
        else:
            id_val = 0
            tmp_param, tmp_param_actions = actionD.get_tmp_parameter(id)
            actions.extend(tmp_param_actions)
            # Apply ID via action4 later
            mod = (tmp_param, 2 if feature <= 3 else 1,
                   5 if feature <= 3 else 4)

    if write_action4s:
        strings = [(lang_id, grfstrings.get_translation(string, lang_id))
                   for lang_id in grfstrings.get_translations(string)]
        # Sort the strings for deterministic ordering and prepend the default language
        strings = [(0x7F, grfstrings.get_translation(string))] + sorted(
            strings, key=lambda lang_text: lang_text[0])

        for lang_id, text in strings:
            if mod is not None:
                act6 = action6.Action6()
                act6.modify_bytes(*mod)
                actions.append(act6)
            actions.append(Action4(feature, lang_id, size, id_val, [text]))

    action6.free_parameters.restore()

    return (id_val, actions)