示例#1
0
    def preprocess_storageop(self, expr):
        assert isinstance(expr, expression.StorageOp)
        if expr.info['perm'] and self.feature not in (0x08, 0x0A, 0x0D):
            raise generic.ScriptError(
                "Persistent storage is not supported for feature '{}'".format(
                    general.feature_name(self.feature)), 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.feature == 0x08:
            # 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 = expression.BinOp(nmlop.STO_TMP, grfid,
                                        expression.ConstantNumeric(0x100),
                                        expr.pos)
            ret = expression.BinOp(nmlop.VAL2, store_op, ret, expr.pos)
        elif expr.grfid is not None:
            raise generic.ScriptError(
                "Specifying a grfid is only possible for town persistent storage.",
                expr.pos)
        return ret
示例#2
0
    def parse(self, expr):
        #Preprocess the expression
        if isinstance(expr, expression.SpecialParameter):
            #do this first, since it may evaluate to a BinOp
            expr = expr.to_reading()

        if isinstance(expr, expression.BinOp):
            expr = self.preprocess_binop(expr)

        elif isinstance(expr, expression.Boolean):
            expr = expression.BinOp(nmlop.MINU, expr.expr,
                                    expression.ConstantNumeric(1))

        elif isinstance(expr, expression.BinNot):
            expr = expression.BinOp(nmlop.XOR, expr.expr,
                                    expression.ConstantNumeric(0xFFFFFFFF))

        elif isinstance(
                expr,
                expression.TernaryOp) and not expr.supported_by_actionD(False):
            expr = self.preprocess_ternaryop(expr)

        elif isinstance(expr, expression.StorageOp):
            expr = self.preprocess_storageop(expr)

        #Try to parse the expression to a list of variables+operators
        if isinstance(expr, expression.ConstantNumeric):
            self.parse_constant(expr)

        elif isinstance(expr, expression.Parameter) and isinstance(
                expr.num, expression.ConstantNumeric):
            self.parse_param(expr)

        elif isinstance(expr, expression.Variable):
            self.parse_variable(expr)

        elif expr.supported_by_actionD(False):
            self.parse_via_actionD(expr)

        elif isinstance(expr, expression.BinOp):
            self.parse_binop(expr)

        elif isinstance(expr, expression.Not):
            self.parse_not(expr)

        elif isinstance(expr, expression.String):
            self.parse_string(expr)

        elif isinstance(expr,
                        (VarAction2LoadTempVar, VarAction2LoadLayoutParam)):
            self.var_list.append(expr)
            self.var_list_size += expr.get_size()

        elif isinstance(expr, expression.SpriteGroupRef):
            self.parse_proc_call(expr)

        else:
            expr.supported_by_action2(True)
            assert False  #supported_by_action2 should have raised the correct error already
示例#3
0
def cargo_profit_value(value):
    # In NFO, calculation is (amount * price_factor * cb_result) / 8192
    # Units of the NML price factor differ by about 41.12, i.e. 1 NML unit = 41 NFO units
    # That'd make the formula (amount * price_factor * cb_result) / (8192 / 41)
    # This is almost (error 0.01%) equivalent to the following, which is what this calculation does
    # (amount * price_factor * (cb_result * 329 / 256)) / 256
    # This allows us to report a factor of 256 in the documentation, which makes a lot more sense than 199.804...
    # Not doing the division here would improve accuracy, but limits the range of the return value too much
    value = expression.BinOp(nmlop.MUL, value, expression.ConstantNumeric(329),
                             value.pos)
    return expression.BinOp(nmlop.DIV, value, expression.ConstantNumeric(256),
                            value.pos)
示例#4
0
def tile_offset(name, args, pos, info, min, max):
    if len(args) != 2:
        raise generic.ScriptError("'{}'() requires 2 arguments, encountered {:d}".format(name, len(args)), pos)
    for arg in args:
        if isinstance(arg, expression.ConstantNumeric):
            generic.check_range(arg.value, min, max, "Argument of '{}'".format(name), arg.pos)

    x = expression.BinOp(nmlop.AND, args[0], expression.ConstantNumeric(0xF), args[0].pos)
    y = expression.BinOp(nmlop.AND, args[1], expression.ConstantNumeric(0xF), args[1].pos)
    # Shift y left by four
    y = expression.BinOp(nmlop.SHIFT_LEFT, y, expression.ConstantNumeric(4), y.pos)
    param = expression.BinOp(nmlop.ADD, x, y, x.pos)
    #Make sure to reduce the result
    return ( param.reduce(), [] )
示例#5
0
    def get_sprite_number(self):
        # Layout of sprite number
        # bit  0 - 13: Sprite number
        # bit 14 - 15: Recolour mode (normal/transparent/remap)
        # bit 16 - 29: Palette sprite number
        # bit 30: Always draw sprite, even in transparent mode
        # bit 31: This is a custom sprite (from action1), not a TTD sprite
        if not self.is_set('sprite'):
            raise generic.ScriptError(
                "'sprite' must be set for this layout sprite", self.pos)

        # Make sure that recolouring is set correctly
        if self.get_param('recolour_mode') == 0 and self.is_set('palette'):
            raise generic.ScriptError(
                "'palette' may not be set when 'recolour_mode' is RECOLOUR_NONE."
            )
        elif self.get_param('recolour_mode') != 0 and not self.is_set(
                'palette'):
            raise generic.ScriptError(
                "'palette' must be set when 'recolour_mode' is not set to RECOLOUR_NONE."
            )

        # add the constant terms first
        sprite_num = self.get_param('recolour_mode') << 14
        if self.get_param('always_draw'):
            sprite_num |= 1 << 30
        if self.sprite_from_action1:
            sprite_num |= 1 << 31

        add_sprite = False
        sprite = self.get_param('sprite')
        if isinstance(sprite, expression.ConstantNumeric):
            sprite_num |= sprite.value
        else:
            add_sprite = True

        add_palette = False
        palette = self.get_param('palette')
        if isinstance(palette, expression.ConstantNumeric):
            sprite_num |= palette.value << 16
        else:
            add_palette = True

        expr = expression.ConstantNumeric(sprite_num, sprite.pos)
        if add_sprite:
            expr = expression.BinOp(nmlop.ADD, sprite, expr, sprite.pos)
        if add_palette:
            expr = expression.BinOp(nmlop.ADD, palette, expr, sprite.pos)
        return expr.reduce()
示例#6
0
def nearest_house_matching_criterion(name, args, pos, info):
    # nearest_house_matching_criterion(radius, criterion)
    # parameter is radius | (criterion << 6)
    if len(args) != 2:
        raise generic.ScriptError("{}() requires 2 arguments, encountered {:d}".format(name, len(args)), pos)
    if isinstance(args[0], expression.ConstantNumeric):
        generic.check_range(args[0].value, 1, 63, "{}()-parameter 1 'radius'".format(name), pos)
    if isinstance(args[1], expression.ConstantNumeric) and args[1].value not in (0, 1, 2):
        raise generic.ScriptError("Invalid value for {}()-parameter 2 'criterion'".format(name), pos)

    radius = expression.BinOp(nmlop.AND, args[0], expression.ConstantNumeric(0x3F, pos), pos)
    criterion = expression.BinOp(nmlop.AND, args[1], expression.ConstantNumeric(0x03, pos), pos)
    criterion = expression.BinOp(nmlop.MUL, criterion, expression.ConstantNumeric(0x40, pos), pos)
    retval = expression.BinOp(nmlop.OR, criterion, radius, pos).reduce()
    return (retval, [])
示例#7
0
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 = expression.BinOp(nmlop.VAL2, param, var, pos)

    if 'value_function' in info:
        res = info['value_function'](res, info)
    return res
示例#8
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
示例#9
0
文件: action3.py 项目: stormcone/nml
def parse_graphics_block(graphics_block, feature, id, size, is_livery_override = False):
    """
    Parse a graphics block (or livery override) into a list of actions, mainly action3

    @param graphics_block: Graphics-block to parse
    @type graphics_block: L{GraphicsBlock}

    @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 of the associated item (relevant for houses only)
    @type size: L{ConstantNumeric} or C{None}

    @param is_livery_override: Whether this is a livery override instead of a normal graphics block
    @type is_livery_override: C{bool}

    @return: The resulting list of actions
    @rtype: L{BaseAction}
    """
    action_list = action2real.create_spriteset_actions(graphics_block)
    if feature == 0x07:
        # Multi-tile houses need more work
        size_bit = size.value if size is not None else 0
        for i, tile in enumerate(house_tiles[size_bit]):
            tile_id = id if i == 0 else expression.BinOp(nmlop.ADD, id, expression.ConstantNumeric(i, id.pos), id.pos).reduce()
            action_list.extend(parse_graphics_block_single_id(graphics_block, feature, tile_id, is_livery_override, tile, id))
    else:
        action_list.extend(parse_graphics_block_single_id(graphics_block, feature, id, is_livery_override))
    return action_list
示例#10
0
def cargo_accepted_nearby(name, args, pos, info):
    # cargo_accepted_nearby(cargo[, xoffset, yoffset])
    if len(args) not in (1, 3):
        raise generic.ScriptError("{}() requires 1 or 3 arguments, encountered {:d}".format(name, len(args)), pos)

    if len(args) > 1:
        offsets = args[1:3]
        for i, offs in enumerate(offsets[:]):
            if isinstance(offs, expression.ConstantNumeric):
                generic.check_range(offs.value, -128, 127, "{}-parameter {:d} '{}offset'".format(name, i + 1, "x" if i == 0 else "y"), pos)
            offsets[i] = expression.BinOp(nmlop.AND, offs, expression.ConstantNumeric(0xFF, pos), pos).reduce()
        # Register 0x100 should be set to xoffset | (yoffset << 8)
        reg100 = expression.BinOp(nmlop.OR, expression.BinOp(nmlop.MUL, offsets[1], expression.ConstantNumeric(256, pos), pos), offsets[0], pos).reduce()
    else:
        reg100 = expression.ConstantNumeric(0, pos)

    return (args[0], [(0x100, reg100)])
示例#11
0
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 expression.BinOp(nmlop.VAL2, north_tile, var61, var.pos)
示例#12
0
def signed_byte_parameter(name, args, pos, info):
    # Convert to a signed byte by AND-ing with 0xFF
    if len(args) != 1:
        raise generic.ScriptError("{}() requires one argument, encountered {:d}".format(name, len(args)), pos)
    if isinstance(args[0], expression.ConstantNumeric):

        generic.check_range(args[0].value, -128, 127, "parameter of {}()".format(name), pos)
    ret = expression.BinOp(nmlop.AND, args[0], expression.ConstantNumeric(0xFF, pos), pos).reduce()
    return (ret, [])
示例#13
0
def industry_layout_count(name, args, pos, info):
    if len(args) < 2 or len(args) > 3:
        raise generic.ScriptError("'{}'() requires between 2 and 3 argument(s), encountered {:d}".format(name, len(args)), pos)

    grfid = expression.ConstantNumeric(0xFFFFFFFF) if len(args) == 2 else args[2]

    extra_params = []
    extra_params.append( (0x100, grfid) )
    extra_params.append( (0x101, expression.BinOp(nmlop.AND, args[1], expression.ConstantNumeric(0xFF)).reduce()) )
    return (args[0], extra_params)
示例#14
0
文件: actionD.py 项目: stormcone/nml
def parse_boolean(assignment):
    assert isinstance(assignment.value, expression.Boolean)
    actions = parse_actionD(
        ParameterAssignment(assignment.param, expression.ConstantNumeric(0)))
    expr = expression.BinOp(nmlop.CMP_NEQ, assignment.value.expr,
                            expression.ConstantNumeric(0))
    cond_block = nml.ast.conditional.Conditional(
        expr,
        [ParameterAssignment(assignment.param, expression.ConstantNumeric(1))],
        None)
    actions.extend(
        nml.ast.conditional.ConditionalList([cond_block]).get_action_list())
    return actions
示例#15
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 = expression.BinOp(nmlop.STO_TMP, random_switch.type_count, expression.ConstantNumeric(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
示例#16
0
文件: actionD.py 项目: OpenTTD/nml
def parse_min_max(assignment):
    assert isinstance(assignment.value, expression.BinOp) and assignment.value.op in (nmlop.MIN, nmlop.MAX)
    # min(a, b) ==> a < b ? a : b.
    # max(a, b) ==> a > b ? a : b.
    action6.free_parameters.save()
    action_list = []
    expr1 = parse_subexpression(assignment.value.expr1, action_list)
    expr2 = parse_subexpression(assignment.value.expr2, action_list)
    guard = expression.BinOp(nmlop.CMP_LT if assignment.value.op == nmlop.MIN else nmlop.CMP_GT, expr1, expr2)
    action_list.extend(
        parse_actionD(ParameterAssignment(assignment.param, expression.TernaryOp(guard, expr1, expr2, None)))
    )
    action6.free_parameters.restore()
    return action_list
示例#17
0
 def preprocess_ternaryop(self, expr):
     assert isinstance(expr, expression.TernaryOp)
     guard = expression.Boolean(expr.guard).reduce()
     self.parse(guard)
     if isinstance(expr.expr1, expression.ConstantNumeric) and isinstance(
             expr.expr2, expression.ConstantNumeric):
         # This can be done more efficiently as (guard)*(expr1-expr2) + expr2
         self.var_list.append(nmlop.MUL)
         diff_var = VarAction2Var(0x1A, 0,
                                  expr.expr1.value - expr.expr2.value)
         diff_var.comment = "expr1 - expr2"
         self.var_list.append(diff_var)
         self.var_list.append(nmlop.ADD)
         # Add var sizes, +2 for the operators
         self.var_list_size += 2 + diff_var.get_size()
         return expr.expr2
     else:
         guard_var = VarAction2StoreTempVar()
         guard_var.comment = "guard"
         inverted_guard_var = VarAction2StoreTempVar()
         inverted_guard_var.comment = "!guard"
         self.var_list.append(nmlop.STO_TMP)
         self.var_list.append(guard_var)
         self.var_list.append(nmlop.XOR)
         var = VarAction2Var(0x1A, 0, 1)
         self.var_list.append(var)
         self.var_list.append(nmlop.STO_TMP)
         self.var_list.append(inverted_guard_var)
         self.var_list.append(nmlop.VAL2)
         # the +4 is for the 4 operators added above (STO_TMP, XOR, STO_TMP, VAL2)
         self.var_list_size += 4 + guard_var.get_size(
         ) + inverted_guard_var.get_size() + var.get_size()
         expr1 = expression.BinOp(nmlop.MUL, expr.expr1,
                                  VarAction2LoadTempVar(guard_var))
         expr2 = expression.BinOp(nmlop.MUL, expr.expr2,
                                  VarAction2LoadTempVar(inverted_guard_var))
         return expression.BinOp(nmlop.ADD, expr1, expr2)
示例#18
0
 def p_binop(self, t):
     """expression : expression PLUS expression
     | expression MINUS expression
     | expression TIMES expression
     | expression DIVIDE expression
     | expression MODULO expression
     | expression AND expression
     | expression OR expression
     | expression XOR expression
     | expression SHIFT_LEFT expression
     | expression SHIFT_RIGHT expression
     | expression SHIFTU_RIGHT expression
     | expression COMP_EQ expression
     | expression COMP_NEQ expression
     | expression COMP_LE expression
     | expression COMP_GE expression
     | expression COMP_LT expression
     | expression COMP_GT expression"""
     t[0] = expression.BinOp(self.code_to_op[t[2]], t[1], t[3], t[1].pos)
示例#19
0
文件: actionA.py 项目: stormcone/nml
def parse_actionA(replaces):
    """
    Parse replace-block to ActionA.

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

    real_sprite_list = real_sprite.parse_sprite_data(replaces)
    block_list = []
    total_sprites = len(real_sprite_list)
    offset = 2  # Skip 0A and <num-sets>
    sprite_offset = 0  # Number of sprites already covered by previous [<num-sprites> <first-sprite>]-pairs

    while total_sprites > 0:
        this_block = min(total_sprites, 255)  # number of sprites in this block
        total_sprites -= this_block
        offset += 1  # Skip <num-sprites>

        first_sprite = replaces.start_id  # number of first sprite
        if sprite_offset != 0:
            first_sprite = expression.BinOp(
                nmlop.ADD, first_sprite,
                expression.ConstantNumeric(sprite_offset, first_sprite.pos),
                first_sprite.pos).reduce()
        first_sprite, offset = actionD.write_action_value(
            first_sprite, action_list, act6, offset, 2)
        block_list.append((this_block, first_sprite.value))

        sprite_offset += this_block  # increase first-sprite for next block

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

    action_list.append(ActionA(block_list))
    action_list.extend(real_sprite_list)

    return action_list
示例#20
0
    def pre_process(self):
        new_costs = []

        for cost in self.costs:
            cost.value = cost.value.reduce(global_constants.const_list)
            if isinstance(cost.value, expression.ConstantNumeric):
                generic.check_range(cost.value.value, -8, 16,
                                    'Base cost value', cost.value.pos)
            cost.value = expression.BinOp(nmlop.ADD, cost.value,
                                          expression.ConstantNumeric(8),
                                          cost.value.pos).reduce()

            if isinstance(cost.name, expression.Identifier):
                if cost.name.value in base_cost_table:
                    cost.name = expression.ConstantNumeric(
                        base_cost_table[cost.name.value][0])
                    new_costs.append(cost)
                elif cost.name.value in generic_base_costs:
                    #create temporary list, so it can be sorted for efficiency
                    tmp_list = []
                    for num, type in list(base_cost_table.values()):
                        if type == cost.name.value:
                            tmp_list.append(
                                assignment.Assignment(
                                    expression.ConstantNumeric(num),
                                    cost.value, cost.name.pos))
                    tmp_list.sort(key=lambda x: x.name.value)
                    new_costs.extend(tmp_list)
                else:
                    raise generic.ScriptError(
                        "Unrecognized base cost identifier '{}' encountered".
                        format(cost.name.value), cost.name.pos)
            else:
                cost.name = cost.name.reduce()
                if isinstance(cost.name, expression.ConstantNumeric):
                    generic.check_range(cost.name.value, 0,
                                        len(base_cost_table),
                                        'Base cost number', cost.name.pos)
                new_costs.append(cost)
        self.costs = new_costs
示例#21
0
文件: snowline.py 项目: stormcone/nml
def compute_table(snowline):
    """
    Compute the table with snowline height for each day of the year.

    @param snowline: Snowline definition.
    @type  snowline: L{Snowline}

    @return: Table of 12*32 entries with snowline heights.
    @rtype:  C{str}
    """
    day_table = [None]*365 # Height at each day, starting at day 0
    for dh in snowline.date_heights:
        doy = dh.name.reduce()
        if not isinstance(doy, expression.ConstantNumeric):
            raise generic.ScriptError('Day of year is not a compile-time constant', doy.pos)
        if doy.value < 1 or doy.value > 365:
            raise generic.ScriptError('Day of the year must be between 1 and 365', doy.pos)

        height = dh.value.reduce()
        if isinstance(height, expression.ConstantNumeric) and height.value < 0:
            raise generic.ScriptError('Height must be at least 0', height.pos)
        if dh.unit is None:
            if isinstance(height, expression.ConstantNumeric) and height.value > 255:
                raise generic.ScriptError('Height must be at most 255', height.pos)
        else:
            unit = dh.unit
            if unit.type != 'snowline':
                raise generic.ScriptError('Expected a snowline percentage ("snow%")', height.pos)

            if isinstance(height, expression.ConstantNumeric) and height.value > 100:
                raise generic.ScriptError('Height must be at most 100 snow%', height.pos)


            mul, div = unit.convert, 1
            if isinstance(mul, tuple):
                mul, div = mul

            # Factor out common factors
            gcd = generic.greatest_common_divisor(mul, div)
            mul //= gcd
            div //= gcd

            if isinstance(height, (expression.ConstantNumeric, expression.ConstantFloat)):
                # Even if mul == div == 1, we have to round floats and adjust value
                height = expression.ConstantNumeric(int(float(height.value) * mul / div + 0.5), height.pos)
            elif mul != div:
                # Compute (value * mul + div/2) / div
                height = expression.BinOp(nmlop.MUL, height, expression.ConstantNumeric(mul, height.pos), height.pos)
                height = expression.BinOp(nmlop.ADD, height, expression.ConstantNumeric(int(div / 2), height.pos), height.pos)
                height = expression.BinOp(nmlop.DIV, height, expression.ConstantNumeric(div, height.pos), height.pos)

        # For 'linear' snow-line, only accept integer constants.
        if snowline.type != 'equal' and not isinstance(height, expression.ConstantNumeric):
            raise generic.ScriptError('Height is not a compile-time constant', height.pos)

        day_table[doy.value - 1] = height

    # Find first specified point.
    start = 0
    while start < 365 and day_table[start] is None:
        start = start + 1
    if start == 365:
        raise generic.ScriptError('No heights given for the snowline table', snowline.pos)

    first_point = start
    while True:
        # Find second point from start
        end = start + 1
        if end == 365:
            end = 0

        while end != first_point and day_table[end] is None:
            end = end + 1
            if end == 365:
                end = 0

        # Fill the days between start and end (exclusive both border values)
        startvalue = day_table[start]
        endvalue   = day_table[end]
        unwrapped_end = end
        if end < start: unwrapped_end += 365

        if snowline.type == 'equal':
            for day in range(start + 1, unwrapped_end):
                if day >= 365: day -= 365
                day_table[day] = startvalue
        else:
            assert snowline.type == 'linear'

            if start != end:
                dhd = float(endvalue.value - startvalue.value) / float(unwrapped_end - start)
            else:
                assert startvalue.value == endvalue.value
                dhd = 0

            for day in range(start + 1, unwrapped_end):
                uday = day
                if uday >= 365: uday -= 365
                height = startvalue.value + int(round(dhd * (day - start)))
                day_table[uday] = expression.ConstantNumeric(height)


        if end == first_point: # All days done
            break

        start = end

    table = [None] * (12*32)
    for dy in range(365):
        today = datetime.date.fromordinal(dy + 1)
        if day_table[dy]:
            expr = day_table[dy].reduce()
        else:
            expr = None
        table[(today.month - 1) * 32 + today.day - 1] = expr

    for idx, d in enumerate(table):
        if d is None:
            table[idx] = table[idx - 1]
    #Second loop is needed because we need make sure the first item is also set.
    for idx, d in enumerate(table):
        if d is None:
            table[idx] = table[idx - 1]


    return table
示例#22
0
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
示例#23
0
def parse_property_value(prop_info, value, unit=None, size_bit=None):
    """
    Parse a single property value / unit
    To determine the value that is to be used in nfo

    @param prop_info: A dictionary with property information
    @type prop_info: C{dict}

    @param value: Value of the property
    @type value: L{Expression}

    @param unit: Unit of the property value (e.g. km/h)
    @type unit: L{Unit} or C{None}

    @param size_bit: Bit that indicates the size of a multitile house
                     Set iff the item is a house
    @type size_bit: C{int} or C{None}

    @return: List of values to actually use (in nfo) for the property
    @rtype: L{Expression}
    """
    # Change value to use, except when the 'nfo' unit is used
    if unit is None or unit.type != 'nfo':
        # Save the original value to test conversion against it
        org_value = value

        # Multiply by property-specific conversion factor
        mul, div = 1, 1
        if 'unit_conversion' in prop_info:
            mul = prop_info['unit_conversion']
            if isinstance(mul, tuple):
                mul, div = mul

        # Divide by conversion factor specified by unit
        if unit is not None:
            if not 'unit_type' in prop_info or unit.type != prop_info[
                    'unit_type']:
                raise generic.ScriptError("Invalid unit for property",
                                          value.pos)
            unit_mul, unit_div = unit.convert, 1
            if isinstance(unit_mul, tuple):
                unit_mul, unit_div = unit_mul
            mul *= unit_div
            div *= unit_mul

        # Factor out common factors
        gcd = generic.greatest_common_divisor(mul, div)
        mul //= gcd
        div //= gcd

        if isinstance(value,
                      (expression.ConstantNumeric, expression.ConstantFloat)):
            # Even if mul == div == 1, we have to round floats and adjust value
            value = expression.ConstantNumeric(
                int(float(value.value) * mul / div + 0.5), value.pos)
            if unit is not None and 'adjust_value' in prop_info:
                value = adjust_value(value, org_value, unit,
                                     prop_info['adjust_value'])
        elif mul != div:
            # Compute (value * mul + div/2) / div
            value = expression.BinOp(
                nmlop.MUL, value, expression.ConstantNumeric(mul, value.pos),
                value.pos)
            value = expression.BinOp(
                nmlop.ADD, value,
                expression.ConstantNumeric(int(div / 2), value.pos), value.pos)
            value = expression.BinOp(
                nmlop.DIV, value, expression.ConstantNumeric(div, value.pos),
                value.pos)

    elif isinstance(value, expression.ConstantFloat):
        # Round floats to ints
        value = expression.ConstantNumeric(int(value.value + 0.5), value.pos)

    # Apply value_function if it exists
    if 'value_function' in prop_info:
        value = prop_info['value_function'](value)

    # Make multitile houses work
    if size_bit is not None:
        num_ids = house_sizes[size_bit]
        assert 'multitile_function' in prop_info
        ret = prop_info['multitile_function'](value, num_ids, size_bit)
        assert len(ret) == num_ids
        return ret
    else:
        return [value]
示例#24
0
文件: action3.py 项目: stormcone/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 = expression.BinOp(nmlop.SHIFT_LEFT, house_north_tile_id, expression.ConstantNumeric(24))
                register_FF = expression.BinOp(nmlop.OR, lowbytes, highbyte, lowbytes.pos).reduce()
                register_FF = expression.BinOp(nmlop.STO_TMP, register_FF, expression.ConstantNumeric(0xFF))
                expr = expression.BinOp(nmlop.VAL2, register_FF, expr, register_FF.pos)

                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)
    action6.free_parameters.restore()

    return prepend_action_list + action_list
示例#25
0
文件: actionD.py 项目: stormcone/nml
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):
        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 = expression.BinOp(nmlop.SUB, expression.ConstantNumeric(1),
                                assignment.value.expr)
        assignment = ParameterAssignment(assignment.param, expr)

    if isinstance(assignment.value, expression.BinNot):
        expr = expression.BinOp(nmlop.SUB,
                                expression.ConstantNumeric(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
示例#26
0
 def p_binop_logical(self, t):
     """expression : expression LOGICAL_AND expression
     | expression LOGICAL_OR expression"""
     t[0] = expression.BinOp(self.code_to_op[t[2]],
                             expression.Boolean(t[1]),
                             expression.Boolean(t[3]), t[1].pos)
示例#27
0
    def preprocess_binop(self, expr):
        """
        Several nml operators are not directly support by nfo so we have to work
        around that by implementing those operators in terms of others.

        @return: A pre-processed version of the expression.
        @rtype:  L{Expression}
        """
        assert isinstance(expr, expression.BinOp)
        if expr.op == nmlop.CMP_LT:
            #return value is 0, 1 or 2, we want to map 0 to 1 and the others to 0
            expr = expression.BinOp(nmlop.VACT2_CMP, expr.expr1, expr.expr2)
            #reduce the problem to 0/1
            expr = expression.BinOp(nmlop.MIN, expr,
                                    expression.ConstantNumeric(1))
            #and invert the result
            expr = expression.BinOp(nmlop.XOR, expr,
                                    expression.ConstantNumeric(1))
        elif expr.op == nmlop.CMP_GT:
            #return value is 0, 1 or 2, we want to map 2 to 1 and the others to 0
            expr = expression.BinOp(nmlop.VACT2_CMP, expr.expr1, expr.expr2)
            #subtract one
            expr = expression.BinOp(nmlop.SUB, expr,
                                    expression.ConstantNumeric(1))
            #map -1 and 0 to 0
            expr = expression.BinOp(nmlop.MAX, expr,
                                    expression.ConstantNumeric(0))
        elif expr.op == nmlop.CMP_LE:
            #return value is 0, 1 or 2, we want to map 2 to 0 and the others to 1
            expr = expression.BinOp(nmlop.VACT2_CMP, expr.expr1, expr.expr2)
            #swap 0 and 2
            expr = expression.BinOp(nmlop.XOR, expr,
                                    expression.ConstantNumeric(2))
            #map 1/2 to 1
            expr = expression.BinOp(nmlop.MIN, expr,
                                    expression.ConstantNumeric(1))
        elif expr.op == nmlop.CMP_GE:
            #return value is 0, 1 or 2, we want to map 1/2 to 1
            expr = expression.BinOp(nmlop.VACT2_CMP, expr.expr1, expr.expr2)
            expr = expression.BinOp(nmlop.MIN, expr,
                                    expression.ConstantNumeric(1))
        elif expr.op == nmlop.CMP_EQ:
            #return value is 0, 1 or 2, we want to map 1 to 1, other to 0
            expr = expression.BinOp(nmlop.VACT2_CMP, expr.expr1, expr.expr2)
            expr = expression.BinOp(nmlop.AND, expr,
                                    expression.ConstantNumeric(1))
        elif expr.op == nmlop.CMP_NEQ:
            #same as CMP_EQ but invert the result
            expr = expression.BinOp(nmlop.VACT2_CMP, expr.expr1, expr.expr2)
            expr = expression.BinOp(nmlop.AND, expr,
                                    expression.ConstantNumeric(1))
            expr = expression.BinOp(nmlop.XOR, expr,
                                    expression.ConstantNumeric(1))

        elif expr.op == nmlop.HASBIT:
            # hasbit(x, n) ==> (x >> n) & 1
            expr = expression.BinOp(nmlop.SHIFTU_RIGHT, expr.expr1, expr.expr2)
            expr = expression.BinOp(nmlop.AND, expr,
                                    expression.ConstantNumeric(1))
        elif expr.op == nmlop.NOTHASBIT:
            # !hasbit(x, n) ==> ((x >> n) & 1) ^ 1
            expr = expression.BinOp(nmlop.SHIFTU_RIGHT, expr.expr1, expr.expr2)
            expr = expression.BinOp(nmlop.AND, expr,
                                    expression.ConstantNumeric(1))
            expr = expression.BinOp(nmlop.XOR, expr,
                                    expression.ConstantNumeric(1))

        return expr.reduce()
示例#28
0
 def p_unary_minus(self, t):
     'expression : MINUS expression'
     t[0] = expression.BinOp(self.code_to_op[t[1]], expression.ConstantNumeric(0), t[2], t.lineno(1))
示例#29
0
def parse_result(value,
                 action_list,
                 act6,
                 offset,
                 parent_action,
                 none_result,
                 var_range,
                 repeat_result=1):
    """
    Parse a result (another switch or CB result) in a switch block.

    @param value: Value to parse
    @type value: L{Expression}

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

    @param act6: Action6 to add any modifications to
    @type act6: L{Action6}

    @param offset: Current offset to use for action6
    @type offset: C{int}

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

    @param none_result: Result to use to return the computed value
    @type none_result: L{Expression}

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

    @param repeat_result: Repeat any action6 modifying of the next sprite this many times.
    @type repeat_result: C{int}

    @return: A tuple of two values:
                - The value to use as return value
                - Comment to add to this value
    @rtype: C{tuple} of (L{ConstantNumeric} or L{SpriteGroupRef}), C{str}
    """
    if value is None:
        comment = "return;"
        assert none_result is not None
        if isinstance(none_result, expression.SpriteGroupRef):
            result = parse_sg_ref_result(none_result, action_list,
                                         parent_action, var_range)
        else:
            result = none_result
    elif isinstance(value, expression.SpriteGroupRef):
        result = parse_sg_ref_result(value, action_list, parent_action,
                                     var_range)
        comment = result.name.value + ';'
    elif isinstance(value, expression.ConstantNumeric):
        comment = "return {:d};".format(value.value)
        result = value
        if not (-16384 <= value.value <= 32767):
            msg = "Callback results are limited to -16384..16383 (when the result is a signed number) or 0..32767 (unsigned), encountered {:d}."
            msg = msg.format(value.value)
            raise generic.ScriptError(msg, value.pos)

    elif isinstance(value, expression.String):
        comment = "return {};".format(str(value))
        str_id, actions = action4.get_string_action4s(0, 0xD0, value)
        action_list.extend(actions)
        result = expression.ConstantNumeric(str_id - 0xD000 + 0x8000)
    elif value.supported_by_actionD(False):
        tmp_param, tmp_param_actions = actionD.get_tmp_parameter(
            expression.BinOp(nmlop.OR, value,
                             expression.ConstantNumeric(0x8000)).reduce())
        comment = "return param[{:d}];".format(tmp_param)
        action_list.extend(tmp_param_actions)
        for i in range(repeat_result):
            act6.modify_bytes(tmp_param, 2, offset + 2 * i)
        result = expression.ConstantNumeric(0)
    else:
        global return_action_id
        extra_actions, result = create_return_action(
            value, parent_action.feature,
            "@return_action_{:d}".format(return_action_id), var_range)
        return_action_id += 1
        action2.add_ref(result, parent_action)
        action_list.extend(extra_actions)
        comment = "return {}".format(value)
    return (result, comment)
示例#30
0
 def parse_not(self, expr):
     self.parse_binop(
         expression.BinOp(nmlop.XOR, expr.expr,
                          expression.ConstantNumeric(1)))