Beispiel #1
0
    def __init__(self, param_list, sprite_list, name, pos):
        base_statement.BaseStatement.__init__(self, "replacenew-block", pos)
        sprite_container.SpriteContainer.__init__(self, "replacenew-block",
                                                  name)
        num_params = len(param_list)
        if not (1 <= num_params <= 3):
            raise generic.ScriptError(
                "replacenew-block requires 1 to 3 parameters, encountered " +
                str(num_params), pos)

        self.type = param_list[0]
        if not isinstance(self.type, expression.Identifier):
            raise generic.ScriptError(
                "replacenew parameter 'type' must be an identifier of a sprite replacement type",
                self.type.pos)

        if num_params >= 2:
            self.image_file = param_list[1].reduce()
            if not isinstance(self.image_file, expression.StringLiteral):
                raise generic.ScriptError(
                    "replacenew-block parameter 2 'file' must be a string literal",
                    self.image_file.pos)
        else:
            self.image_file = None

        if num_params >= 3:
            self.offset = param_list[2].reduce_constant().value
            generic.check_range(self.offset, 0, 0xFFFF,
                                "replacenew-block parameter 3 'offset'",
                                param_list[2].pos)
        else:
            self.offset = 0

        self.sprite_list = sprite_list
        self.add_sprite_data(self.sprite_list, self.image_file, pos)
Beispiel #2
0
    def resolve_spritegroup_ref(self, sg_ref):
        """
        Resolve a reference to a (sprite/palette) sprite group

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

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

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

        num = action1.get_action1_index(spriteset)
        generic.check_range(num, 0, (1 << 14) - 1, "sprite", sg_ref.pos)
        return expression.ConstantNumeric(num), offset
Beispiel #3
0
def builtin_visual_effect_and_powered(name, args, pos):
    """
    Builtin function, used in two forms:
    visual_effect_and_powered(effect, offset, powered)
    visual_effect(effect, offset)
    Use this to set the vehicle property visual_effect[_and_powered]
    and for the callback VEH_CB_VISUAL_EFFECT[_AND_POWERED]

    """
    arg_len = 2 if name == "visual_effect" else 3
    if len(args) != arg_len:
        raise generic.ScriptError(name + "() must have {:d} parameters".format(arg_len), pos)
    effect = args[0].reduce_constant(global_constants.const_list).value
    offset = nmlop.ADD(args[1], 8).reduce_constant().value
    generic.check_range(offset, 0, 0x0F, "offset in function " + name, pos)
    if arg_len == 3:
        powered = args[2].reduce_constant(global_constants.const_list).value
        if powered != 0 and powered != 0x80:
            raise generic.ScriptError(
                "3rd argument to visual_effect_and_powered (powered) must be"
                " either ENABLE_WAGON_POWER or DISABLE_WAGON_POWER",
                pos,
            )
    else:
        powered = 0
    return ConstantNumeric(effect | offset | powered)
Beispiel #4
0
def builtin_slope_to_sprite_offset(name, args, pos):
    """
    builtin function slope_to_sprite_offset(slope)

    @return sprite offset to use
    """
    if len(args) != 1:
        raise generic.ScriptError(name + "() must have 1 parameter", pos)

    if isinstance(args[0], ConstantNumeric):
        generic.check_range(args[0].value, 0, 15,
                            "Argument of '{}'".format(name), args[0].pos)

    # step 1: ((slope >= 0) & (slope <= 14)) * slope
    # This handles all non-steep slopes
    expr = BinOp(nmlop.AND,
                 BinOp(nmlop.CMP_LE, args[0], ConstantNumeric(14), pos),
                 BinOp(nmlop.CMP_GE, args[0], ConstantNumeric(0), pos), pos)
    expr = BinOp(nmlop.MUL, expr, args[0], pos)
    # Now handle the steep slopes separately
    # So add (slope == SLOPE_XX) * offset_of_SLOPE_XX for each steep slope
    steep_slopes = [(23, 16), (27, 17), (29, 15), (30, 18)]
    for slope, offset in steep_slopes:
        to_add = BinOp(
            nmlop.MUL, BinOp(nmlop.CMP_EQ, args[0], ConstantNumeric(slope),
                             pos), ConstantNumeric(offset), pos)
        expr = BinOp(nmlop.ADD, expr, to_add, pos)
    return expr
Beispiel #5
0
def builtin_sound_import(name, args, pos):
    from nml.actions import action11
    if len(args) not in (2, 3):
        raise generic.ScriptError(name + "() must have 2 or 3 parameters", pos)
    grfid = parse_string_to_dword(args[0].reduce())
    sound_num = args[1].reduce_constant().value
    volume = args[2].reduce_constant().value if len(args) >= 3 else 100
    generic.check_range(volume, 0, 100, "sound volume", pos)
    return ConstantNumeric(action11.add_sound( (grfid, sound_num, volume), pos), pos)
Beispiel #6
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 = nmlop.AND(args[0], 0xFF, pos).reduce()
    return (ret, [])
Beispiel #7
0
def builtin_sound_file(name, args, pos):
    from nml.actions import action11
    if len(args) not in (1, 2):
        raise generic.ScriptError(name + "() must have 1 or 2 parameters", pos)
    if not isinstance(args[0], StringLiteral):
        raise generic.ScriptError("Parameter for " + name + "() must be a string literal", pos)
    volume = args[1].reduce_constant().value if len(args) >= 2 else 100
    generic.check_range(volume, 0, 100, "sound volume", pos)
    return ConstantNumeric(action11.add_sound( (args[0].value, volume), pos), pos)
Beispiel #8
0
 def _validate_palette(self, name, value):
     if isinstance(value, expression.SpriteGroupRef):
         self.palette_from_action1 = True
         val, offset = self.resolve_spritegroup_ref(value)
         if offset is not None:
             self.create_register(name, offset)
         return val
     else:
         if isinstance(value, expression.ConstantNumeric):
             generic.check_range(value.value, 0, (1 << 14) - 1, "palette", value.pos)
         self.palette_from_action1 = False
         return value
Beispiel #9
0
def industry_input_multiplier(value, prop_num):
    if not isinstance(value, Array) or len(value.values) > 2:
        raise generic.ScriptError("Input multiplier must be an array of up to two values", value.pos)
    val1 = value.values[0].reduce() if len(value.values) > 0 else ConstantNumeric(0)
    val2 = value.values[1].reduce() if len(value.values) > 1 else ConstantNumeric(0)
    if not isinstance(val1, (ConstantNumeric, ConstantFloat)) or not isinstance(val2, (ConstantNumeric, ConstantFloat)):
        raise generic.ScriptError("Expected a compile-time constant", value.pos)
    generic.check_range(val1.value, 0, 256, "input_multiplier", val1.pos)
    generic.check_range(val2.value, 0, 256, "input_multiplier", val2.pos)
    mul1 = int(val1.value * 256)
    mul2 = int(val2.value * 256)
    return [Action0Property(prop_num, ConstantNumeric(mul1 | (mul2 << 16)), 4)]
Beispiel #10
0
def builtin_palette_1cc(name, args, pos):
    """
    palette_1cc(colour) builtin function.

    @return Recolour sprite to use
    """
    if len(args) != 1:
        raise generic.ScriptError(name + "() must have 1 parameter", pos)

    if isinstance(args[0], ConstantNumeric):
        generic.check_range(args[0].value, 0, 15, "Argument of '{}'".format(name), args[0].pos)

    return nmlop.ADD(args[0], 775, pos)
Beispiel #11
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 = nmlop.AND(args[0], 0xF)
    y = nmlop.AND(args[1], 0xF)
    # Shift y left by four
    y = nmlop.SHIFT_LEFT(y, 4)
    param = nmlop.ADD(x, y)
    #Make sure to reduce the result
    return ( param.reduce(), [] )
Beispiel #12
0
def builtin_create_effect(name, args, pos):
    """
    Builtin function:
    create_effect(effect_sprite, l_x_offset, t_y_offset, z_offset)
    Use this to set the values for temporary storages 100+x
    in the callback create_effect

    """
    if len(args) != 4:
        raise generic.ScriptError(name + "() must have 4 parameters", pos)
    from nml import global_constants

    sprite = args[0].reduce_constant(global_constants.const_list).value
    offset1 = args[1].reduce_constant().value
    offset2 = args[2].reduce_constant().value
    offset3 = args[3].reduce_constant().value

    generic.check_range(sprite, 0, 255, "effect_sprite in function " + name,
                        args[0].pos)
    generic.check_range(offset1, -128, 127, "l_x_offset in function " + name,
                        args[1].pos)
    generic.check_range(offset2, -128, 127, "t_y_offset in function " + name,
                        args[2].pos)
    generic.check_range(offset3, -128, 127, "z_offset in function " + name,
                        args[3].pos)

    return ConstantNumeric(sprite | (offset1 & 0xFF) << 8
                           | (offset2 & 0xFF) << 16 | (offset3 & 0xFF) << 24)
Beispiel #13
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 = nmlop.AND(args[0], 0x3F, pos)
    criterion = nmlop.AND(args[1], 0x03, pos)
    criterion = nmlop.MUL(criterion, 0x40)
    retval = nmlop.OR(criterion, radius).reduce()
    return (retval, [])
Beispiel #14
0
def builtin_vehicle_curv_info(name, args, pos):
    """
    vehicle_curv_info(prev_cur, cur_next) builtin function

    @return Value to use with vehicle var curv_info
    """
    if len(args) != 2:
        raise generic.ScriptError(name + "() must have 2 parameters", pos)

    for arg in args:
        if isinstance(arg, ConstantNumeric):
            generic.check_range(arg.value, -2, 2, "Argument of '{}'".format(name), arg.pos)

    args = [nmlop.AND(arg, 0xF, pos) for arg in args]
    cur_next = nmlop.SHIFT_LEFT(args[1], 8)
    return nmlop.OR(args[0], cur_next)
Beispiel #15
0
 def _validate_sprite(self, name, value):
     if isinstance(value, expression.SpriteGroupRef):
         self.sprite_from_action1 = True
         val, offset = self.resolve_spritegroup_ref(value)
         if offset is not None:
             self.create_register(name, offset)
         return val
     else:
         self.sprite_from_action1 = False
         if isinstance(value, expression.ConstantNumeric):
             generic.check_range(value.value, 0, (1 << 14) - 1, "sprite", value.pos)
             return value
         if value.supported_by_actionD(raise_error=False):
             return value
         self.create_register(name, value)
         return expression.ConstantNumeric(0)
Beispiel #16
0
 def check_sprite_size(self):
     generic.check_range(self.xpos.value, 0, 0x7FFFFFFF,
                         "Real sprite paramater 'xpos'", self.xpos.pos)
     generic.check_range(self.ypos.value, 0, 0x7FFFFFFF,
                         "Real sprite paramater 'ypos'", self.ypos.pos)
     generic.check_range(self.xsize.value, 1, 0xFFFF,
                         "Real sprite paramater 'xsize'", self.xsize.pos)
     generic.check_range(self.ysize.value, 1, 0xFFFF,
                         "Real sprite paramater 'ysize'", self.ysize.pos)
Beispiel #17
0
def house_random_colours(value):
    # User sets array with 4 values (range 0..15)
    # Output is a dword, each byte being a value from the array
    if not isinstance(value, Array) or len(value.values) != 4:
        raise generic.ScriptError("Random colours must be an array with exactly four values", value.pos)

    ret = None
    for i, colour in enumerate(value.values):
        if isinstance(colour, ConstantNumeric):
            generic.check_range(colour.value, 0, 15, "Random house colours", colour.pos)
        byte = BinOp(nmlop.AND, colour, ConstantNumeric(0xFF, colour.pos), colour.pos)
        if i == 0:
            ret = byte
        else:
            byte = BinOp(nmlop.SHIFT_LEFT, byte, ConstantNumeric(i * 8, colour.pos), colour.pos)
            ret = BinOp(nmlop.OR, ret, byte, colour.pos)
    return ret.reduce()
Beispiel #18
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] = nmlop.AND(offs, 0xFF, pos).reduce()
        # Register 0x100 should be set to xoffset | (yoffset << 8)
        reg100 = nmlop.OR(nmlop.MUL(offsets[1], 256, pos), offsets[0]).reduce()
    else:
        reg100 = expression.ConstantNumeric(0, pos)

    return (args[0], [(0x100, reg100)])
Beispiel #19
0
def builtin_palette_2cc(name, args, pos):
    """
    palette_2cc(colour1, colour2) builtin function.

    @return Recolour sprite to use
    """
    if len(args) != 2:
        raise generic.ScriptError(name + "() must have 2 parameters", pos)

    for i in range(0, 2):
        if isinstance(args[i], ConstantNumeric):
            generic.check_range(args[i].value, 0, 15, "Argument of '{}'".format(name), args[i].pos)

    col2 = nmlop.MUL(args[1], 16, pos)
    col12 = nmlop.ADD(col2, args[0])
    # Base sprite is not a constant
    base = global_constants.patch_variable("base_sprite_2cc", global_constants.patch_variables["base_sprite_2cc"], pos)

    return nmlop.ADD(col12, base)
Beispiel #20
0
def builtin_relative_coord(name, args, pos):
    """
    relative_coord(x, y) builtin function.

    @return Coordinates in 0xYYXX format.
    """
    if len(args) != 2:
        raise generic.ScriptError(name + "() must have x and y coordinates as parameters", pos)

    if isinstance(args[0], ConstantNumeric):
        generic.check_range(args[0].value, 0, 255, "Argument of '{}'".format(name), args[0].pos)
    if isinstance(args[1], ConstantNumeric):
        generic.check_range(args[1].value, 0, 255, "Argument of '{}'".format(name), args[1].pos)

    x_coord = nmlop.AND(args[0], 0xFF)
    y_coord = nmlop.AND(args[1], 0xFF)
    # Shift Y to its position.
    y_coord = nmlop.SHIFT_LEFT(y_coord, 8)

    return nmlop.OR(x_coord, y_coord, pos)
Beispiel #21
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
Beispiel #22
0
 def _validate_bounding_box(self, name, value):
     if self.type == Action2LayoutSpriteType.GROUND:
         raise generic.ScriptError(
             name + " can not be set for ground sprites", value.pos)
     elif self.type == Action2LayoutSpriteType.CHILD:
         if name not in ("xoffset", "yoffset"):
             raise generic.ScriptError(
                 name + " can not be set for child sprites", value.pos)
         if isinstance(value, expression.ConstantNumeric):
             generic.check_range(value.value, 0, 255, name, value.pos)
             return value
     else:
         assert self.type == Action2LayoutSpriteType.BUILDING
         if name in ("xoffset", "yoffset", "zoffset"):
             if isinstance(value, expression.ConstantNumeric):
                 generic.check_range(value.value, -128, 127, name,
                                     value.pos)
                 return value
         else:
             assert name in ("xextent", "yextent", "zextent")
             if not isinstance(value, expression.ConstantNumeric):
                 raise generic.ScriptError(
                     "Value of '{}' must be a compile-time constant number."
                     .format(name), value.pos)
             generic.check_range(value.value, 0, 255, name, value.pos)
             return value
     # Value must be written to a register
     self.create_register(name, value)
     if self.type == Action2LayoutSpriteType.BUILDING:
         # For building sprites, x and y registers are always written together
         if name == "xoffset" and self.get_register("yoffset") is None:
             self.create_register("yoffset", expression.ConstantNumeric(0))
         if name == "yoffset" and self.get_register("xoffset") is None:
             self.create_register("xoffset", expression.ConstantNumeric(0))
     return expression.ConstantNumeric(0)
Beispiel #23
0
def builtin_date(name, args, pos):
    """
    date(year, month, day) builtin function.

    @return Days since 1 jan 1 of the given date.
    """
    days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    if len(args) != 3:
        raise generic.ScriptError("date() requires exactly 3 arguments", pos)
    from nml import global_constants
    identifier.ignore_all_invalid_ids = True
    year = args[0].reduce(global_constants.const_list)
    identifier.ignore_all_invalid_ids = False
    try:
        month = args[1].reduce_constant().value
        day = args[2].reduce_constant().value
    except generic.ConstError:
        raise generic.ScriptError(
            "Month and day parameters of date() should be compile-time constants",
            pos)
    generic.check_range(month, 1, 12, "month", args[1].pos)
    generic.check_range(day, 1, days_in_month[month - 1], "day", args[2].pos)

    if not isinstance(year, ConstantNumeric):
        if month != 1 or day != 1:
            raise generic.ScriptError(
                "when the year parameter of date() is not a compile time constant month and day should be 1",
                pos)
        #num_days = year*365 + year/4 - year/100 + year/400
        part1 = BinOp(nmlop.MUL, year, ConstantNumeric(365))
        part2 = BinOp(nmlop.DIV, year, ConstantNumeric(4))
        part3 = BinOp(nmlop.DIV, year, ConstantNumeric(100))
        part4 = BinOp(nmlop.DIV, year, ConstantNumeric(400))
        res = BinOp(nmlop.ADD, part1, part2)
        res = BinOp(nmlop.SUB, res, part3)
        res = BinOp(nmlop.ADD, res, part4)
        return res

    generic.check_range(year.value, 0, 5000000, "year", year.pos)
    day_in_year = 0
    for i in range(month - 1):
        day_in_year += days_in_month[i]
    day_in_year += day
    if month >= 3 and (year.value % 4 == 0) and ((not year.value % 100 == 0) or
                                                 (year.value % 400 == 0)):
        day_in_year += 1
    return ConstantNumeric(
        year.value * 365 + calendar.leapdays(0, year.value) + day_in_year - 1,
        pos)
Beispiel #24
0
def parse_real_sprite(sprite, default_file, default_mask_file, poslist,
                      id_dict):
    # check the number of parameters
    num_param = len(sprite.param_list)
    if num_param == 0:
        sprite.is_empty = True
        return sprite
    elif not (2 <= num_param <= 9):
        raise generic.ScriptError(
            "Invalid number of arguments for real sprite. Expected 2..9.",
            sprite.param_list[0].pos)

    # create new sprite struct, needed for template expansion
    new_sprite = RealSprite(poslist=poslist + sprite.poslist)

    param_offset = 0

    if num_param >= 6:
        # xpos, ypos, xsize and ysize are all optional. If not specified they'll default
        # to 0, 0, image_width, image_height
        new_sprite.xpos = sprite.param_list[0].reduce_constant([id_dict])
        new_sprite.ypos = sprite.param_list[1].reduce_constant([id_dict])
        new_sprite.xsize = sprite.param_list[2].reduce_constant([id_dict])
        new_sprite.ysize = sprite.param_list[3].reduce_constant([id_dict])
        new_sprite.check_sprite_size()
        param_offset += 4

    new_sprite.xrel = sprite.param_list[param_offset].reduce_constant(
        [id_dict])
    new_sprite.yrel = sprite.param_list[param_offset + 1].reduce_constant(
        [id_dict])
    generic.check_range(
        new_sprite.xrel.value,
        -0x8000,
        0x7FFF,
        "Real sprite paramater {:d} 'xrel'".format(param_offset + 1),
        new_sprite.xrel.pos,
    )
    generic.check_range(
        new_sprite.yrel.value,
        -0x8000,
        0x7FFF,
        "Real sprite paramater {:d} 'yrel'".format(param_offset + 2),
        new_sprite.yrel.pos,
    )
    param_offset += 2

    # Next may follow any combination of (flags, filename, mask), but always in that order
    new_sprite.flags = expression.ConstantNumeric(0)
    if num_param > param_offset:
        try:
            new_sprite.flags = sprite.param_list[param_offset].reduce_constant(
                [real_sprite_flags, id_dict])
            param_offset += 1
        except generic.ConstError:
            # No flags
            pass

    new_sprite.file = default_file
    if num_param > param_offset and not isinstance(
            sprite.param_list[param_offset], expression.Array):
        new_sprite.file = sprite.param_list[param_offset].reduce([id_dict])
        param_offset += 1
        if not isinstance(new_sprite.file, expression.StringLiteral):
            raise generic.ScriptError(
                "Real sprite parameter {:d} 'file' should be a string literal".
                format(param_offset + 1),
                new_sprite.file.pos,
            )

    if new_sprite.file is None:
        raise generic.ScriptError("No image file specified for real sprite",
                                  sprite.param_list[0].pos)

    new_sprite.mask_file = default_mask_file
    new_sprite.mask_pos = None
    if num_param > param_offset:
        mask = sprite.param_list[param_offset]
        param_offset += 1
        # Mask may be either string (file only)
        #   or array (empty => no mask, 1 value => file only, 2 => offsets only, 3 => file + offsets)
        if isinstance(mask, expression.Array):
            if not (0 <= len(mask.values) <= 3):
                raise generic.ScriptError(
                    "Real sprite mask should be an array with 0 to 3 values, encountered {:d}"
                    .format(len(mask.values)),
                    mask.pos,
                )
            if len(mask.values) == 0:
                # disable any default mask
                new_sprite.mask_file = None
            else:
                if len(mask.values) & 1:
                    new_sprite.mask_file = mask.values[0].reduce([id_dict])
                    if not isinstance(new_sprite.mask_file,
                                      expression.StringLiteral):
                        raise generic.ScriptError(
                            "Real sprite parameter 'mask_file' should be a string literal",
                            new_sprite.file.pos)
                if len(mask.values) & 2:
                    new_sprite.mask_pos = tuple(
                        mask.values[i].reduce_constant([id_dict])
                        for i in range(-2, 0))
                    # Check that there is also a mask specified, else the offsets make no sense
                    if new_sprite.mask_file is None:
                        raise generic.ScriptError(
                            "Mask offsets are specified, but there is no mask file set.",
                            new_sprite.mask_pos[0].pos)
        else:
            new_sprite.mask_file = mask.reduce([id_dict])
            if not isinstance(new_sprite.mask_file, expression.StringLiteral):
                raise generic.ScriptError(
                    "Real sprite parameter {:d} 'mask' should be an array or string literal"
                    .format(param_offset + 1),
                    new_sprite.file.pos,
                )

    if num_param > param_offset:
        raise generic.ScriptError(
            "Real sprite has too many parameters, the last {:d} parameter(s) cannot be parsed."
            .format(num_param - param_offset),
            sprite.param_list[param_offset].pos,
        )

    return new_sprite
Beispiel #25
0
def vehicle_length(value):
    if isinstance(value, ConstantNumeric):
        generic.check_range(value.value, 1, 8, "vehicle length", value.pos)
    return BinOp(nmlop.SUB, ConstantNumeric(8, value.pos), value, value.pos).reduce()