Esempio n. 1
0
File: action5.py Progetto: spnda/nml
def parse_action5(replaces):
    real_sprite_list = real_sprite.parse_sprite_data(replaces)
    num_sprites = len(real_sprite_list)

    if replaces.type.value not in action5_table:
        raise generic.ScriptError(replaces.type.value + " is not a valid sprite replacement type", replaces.type.pos)
    type_id, num_required, block_type = action5_table[replaces.type.value]
    offset = None

    if block_type == Action5BlockType.FIXED:
        if num_sprites < num_required:
            msg = "Invalid sprite count for sprite replacement type '{}', expected {:d}, got {:d}"
            msg = msg.format(replaces.type, num_required, num_sprites)
            raise generic.ScriptError(msg, replaces.pos)

        elif num_sprites > num_required:
            msg = (
                "Too many sprites specified for sprite replacement type '{}',"
                " expected {:d}, got {:d}, extra sprites may be ignored"
            ).format(replaces.type, num_required, num_sprites)
            generic.print_warning(generic.Warning.GENERIC, msg, replaces.pos)

        if replaces.offset != 0:
            msg = "replacenew parameter 'offset' must be zero for sprite replacement type '{}'".format(replaces.type)
            raise generic.ScriptError(msg, replaces.pos)

    elif block_type == Action5BlockType.ANY:
        if replaces.offset != 0:
            msg = "replacenew parameter 'offset' must be zero for sprite replacement type '{}'".format(replaces.type)
            raise generic.ScriptError(msg, replaces.pos)

    elif block_type == Action5BlockType.OFFSET:
        if num_sprites + replaces.offset > num_required:
            msg = "Exceeding the limit of {:d} sprites for sprite replacement type '{}', extra sprites may be ignored"
            msg = msg.format(num_required, replaces.type)
            generic.print_warning(generic.Warning.GENERIC, msg, replaces.pos)

        if replaces.offset != 0 or num_sprites != num_required:
            offset = replaces.offset
    else:
        assert 0

    return [Action5(type_id, num_sprites, offset)] + real_sprite_list
Esempio n. 2
0
def builtin_cargoexpr(name, args, pos):
    if len(args) < 1:
        raise generic.ScriptError(name + "() must have 1 or more parameters",
                                  pos)

    if not isinstance(args[0], StringLiteral
                      ) or args[0].value not in global_constants.cargo_numbers:
        raise generic.ScriptError(
            "First argument of " + name +
            "() must be a string literal that is also in your cargo table",
            pos)
    cargotype = global_constants.cargo_numbers[args[0].value]

    if name == "produce_cargo":
        return ProduceCargo(cargotype, args[1:], pos)
    elif name == "accept_cargo":
        return AcceptCargo(cargotype, args[1:], pos)
    else:
        raise AssertionError()
Esempio n. 3
0
    def resolve_townname_id(self):
        '''
        Resolve the reference number to a previous C{town_names} block.

        @return: Number of the referenced C{town_names} block.
        '''
        if isinstance(self.def_number, expression.Identifier):
            self.number = actionF.named_numbers.get(self.def_number.value)
            if self.number is None:
                raise generic.ScriptError(
                    'Town names name "{}" is not defined or points to a next town_names node'
                    .format(self.def_number.value), self.pos)
        else:
            self.number = self.def_number.value
            if self.number not in actionF.numbered_numbers:
                raise generic.ScriptError(
                    'Town names number "{}" is not defined or points to a next town_names node'
                    .format(self.number), self.pos)
        return self.number
Esempio n. 4
0
 def __init__(self, block_type, block_name):
     self.block_type = block_type
     self.block_name = block_name
     self.sprite_data = {}
     if block_name is not None:
         if block_name.value in SpriteContainer.sprite_blocks:
             raise generic.ScriptError(
                 "Block with name '{}' is already defined.".format(block_name.value), block_name.pos
             )
         SpriteContainer.sprite_blocks[block_name.value] = self
Esempio n. 5
0
def validate_func_float(expr1, expr2, pos):
    if expr1.type() not in (Type.INTEGER,
                            Type.FLOAT) or expr2.type() not in (Type.INTEGER,
                                                                Type.FLOAT):
        if expr1.type() == Type.SPRITEGROUP_REF:
            raise generic.ProcCallSyntaxError(expr1.name, expr1.pos)
        if expr2.type() == Type.SPRITEGROUP_REF:
            raise generic.ProcCallSyntaxError(expr2.name, expr2.pos)
        raise generic.ScriptError(
            "Binary operator requires both operands to be integers or floats.",
            pos)
    # If one is a float, the other must be constant since we can't handle floats at runtime
    if (expr1.type() == Type.FLOAT
            and not isinstance(expr2, (ConstantNumeric, ConstantFloat))) or (
                expr2.type() == Type.FLOAT
                and not isinstance(expr1, (ConstantNumeric, ConstantFloat))):
        raise generic.ScriptError(
            "Floating-point operations are only possible when both operands are compile-time constants.",
            pos)
Esempio n. 6
0
def validate_string(string):
    """
    Check if a given string refers to a string that is translated in the language
    files and raise an error otherwise.

    @param string: The string to validate.
    @type  string: L{expression.String}
    """
    if string.name.value not in default_lang.strings:
        raise generic.ScriptError('Unknown string "{}"'.format(string.name.value), string.pos)
Esempio n. 7
0
def builtin_tramtype_available(name, args, pos):
    """
    tramtype_available(tramtype_label) builtin function.

    @return 1 if the roadtype label is available, 0 otherwise.
    """
    if len(args) != 1:
        raise generic.ScriptError(name + "() must have exactly 1 parameter", pos)
    label = args[0].reduce()
    return SpecialCheck((0x11, None), 0, (0, 1), parse_string_to_dword(label), "{}({})".format(name, str(label)), pos = args[0].pos)
Esempio n. 8
0
 def supported_by_action2(self, raise_error):
     if not self.op.act2_supports:
         token = " '{}'".format(self.op.token) if self.op.token else ""
         if raise_error:
             raise generic.ScriptError(
                 "Operator{} not supported in a switch-block".format(token),
                 self.pos)
         return False
     return self.expr1.supported_by_action2(
         raise_error) and self.expr2.supported_by_action2(raise_error)
Esempio n. 9
0
def parse_file(filename, default):
    """
    Read and parse a single language file.

    @param filename: The filename of the file to parse.
    @type  filename: C{str}

    @param default: True iff this is the default language.
    @type  default: C{bool}
    """
    lang = Language(False)
    try:
        with open(generic.find_file(filename), "r", encoding="utf-8") as fh:
            for idx, line in enumerate(fh):
                pos = generic.LinePosition(filename, idx + 1)
                line = line.rstrip("\n\r").lstrip("\uFEFF")
                # The default language is processed twice here. Once as fallback langauge
                # and once as normal language.
                if default:
                    default_lang.handle_string(line, pos)
                lang.handle_string(line, pos)
    except UnicodeDecodeError:
        pos = generic.LanguageFilePosition(filename)
        if default:
            raise generic.ScriptError("The default language file contains non-utf8 characters.", pos)
        generic.print_warning("Language file contains non-utf8 characters. Ignoring (part of) the contents.", pos)
    except generic.ScriptError as err:
        if default:
            raise
        generic.print_warning(err.value, err.pos)
    else:
        if lang.langid is None:
            generic.print_warning(
                "Language file does not contain a ##grflangid pragma", generic.LanguageFilePosition(filename)
            )
        else:
            for lng in langs:
                if lng[0] == lang.langid:
                    msg = "Language file has the same ##grflangid (with number {:d}) as another language file".format(
                        lang.langid
                    )
                    raise generic.ScriptError(msg, generic.LanguageFilePosition(filename))
            langs.append((lang.langid, lang))
Esempio n. 10
0
File: font.py Progetto: werbfred/nml
 def __init__(self, param_list, sprite_list, name, pos):
     base_statement.BaseStatement.__init__(self, "font_glyph-block", pos)
     sprite_container.SpriteContainer.__init__(self, "font_glyph-block",
                                               name)
     if not (2 <= len(param_list) <= 3):
         raise generic.ScriptError(
             "font_glyph-block requires 2 or 3 parameters, encountered " +
             str(len(param_list)), pos)
     self.font_size = param_list[0]
     self.base_char = param_list[1]
     self.image_file = param_list[2].reduce(
     ) if len(param_list) >= 3 else None
     if self.image_file is not None and not isinstance(
             self.image_file, expression.StringLiteral):
         raise generic.ScriptError(
             "font_glyph-block parameter 3 'file' must be a string literal",
             self.image_file.pos)
     self.sprite_list = sprite_list
     self.add_sprite_data(self.sprite_list, self.image_file, pos)
Esempio n. 11
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)
Esempio n. 12
0
def builtin_hasbit(name, args, pos):
    """
    hasbit(value, bit_num) builtin function.

    @return C{1} if and only if C{value} has bit C{bit_num} set, C{0} otherwise.
    """
    if len(args) != 2:
        raise generic.ScriptError(name + "() must have exactly two parameters",
                                  pos)
    return BinOp(nmlop.HASBIT, args[0], args[1], pos)
Esempio n. 13
0
 def resolve_tmp_storage(self):
     for reg in self.param_registers:
         if not self.tmp_locations:
             raise generic.ScriptError(
                 "There are not enough registers available " +
                 "to perform all required computations in switch blocks. " +
                 "Please reduce the complexity of your code.", self.pos)
         location = self.tmp_locations[0]
         self.remove_tmp_location(location, False)
         reg.set_register(location)
Esempio n. 14
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)
Esempio n. 15
0
def industry_town_count(name, args, pos, info):
    if len(args) < 1 or len(args) > 2:
        raise generic.ScriptError("'{}'() requires between 1 and 2 argument(s), encountered {:d}".format(name, len(args)), pos)

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

    extra_params = []
    extra_params.append( (0x100, grfid) )
    extra_params.append( (0x101, expression.ConstantNumeric(0x0100)) )
    return (args[0], extra_params)
Esempio n. 16
0
def get_feature(switch_block):
    feature = next(iter(switch_block.feature_set))
    if switch_block.var_range == 0x8A:
        feature = action2var_variables.varact2parent_scope[feature]
        if feature is None:
            raise generic.ScriptError(
                "Parent scope for this feature not available, feature: " +
                str(feature), switch_block.pos)

    return feature
Esempio n. 17
0
 def reduce(self, id_dicts=None, unknown_id_fatal=True):
     expr = self.expr.reduce(id_dicts)
     if expr.type() != Type.INTEGER:
         raise generic.ScriptError(
             "Not-operator (~) requires an integer argument.", expr.pos)
     if isinstance(expr, ConstantNumeric):
         return ConstantNumeric(0xFFFFFFFF ^ expr.value)
     if isinstance(expr, BinNot):
         return expr.expr
     return BinNot(expr)
Esempio n. 18
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)
Esempio n. 19
0
    def __init__(self, param_list, sprite_list, pos):
        base_statement.BaseStatement.__init__(self, "alt_sprites-block", pos)
        if not (3 <= len(param_list) <= 5):
            raise generic.ScriptError(
                "alternative_sprites-block requires 3 or 4 parameters, encountered "
                + str(len(param_list)), pos)

        self.name = param_list[0]
        if not isinstance(self.name, expression.Identifier):
            raise generic.ScriptError(
                "alternative_sprites parameter 1 'name' must be an identifier",
                self.name.pos)

        if isinstance(
                param_list[1],
                expression.Identifier) and param_list[1].value in zoom_levels:
            self.zoom_level = zoom_levels[param_list[1].value]
        else:
            raise generic.ScriptError(
                "value for alternative_sprites parameter 2 'zoom level' is not a valid zoom level",
                param_list[1].pos)

        if isinstance(
                param_list[2],
                expression.Identifier) and param_list[2].value in bit_depths:
            self.bit_depth = bit_depths[param_list[2].value]
        else:
            raise generic.ScriptError(
                "value for alternative_sprites parameter 3 'bit depth' is not a valid bit depthl",
                param_list[2].pos)
        global any_32bpp_sprites
        if self.bit_depth == 32:
            any_32bpp_sprites = True

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

        if len(param_list) >= 5:
            self.mask_file = param_list[4].reduce()
            if not isinstance(self.mask_file, expression.StringLiteral):
                raise generic.ScriptError(
                    "alternative_sprites-block parameter 5 'mask_file' must be a string literal",
                    self.mask_file.pos)
            if not self.bit_depth == 32:
                raise generic.ScriptError(
                    "A mask file may only be specified for 32 bpp sprites.",
                    self.mask_file.pos)
        else:
            self.mask_file = None

        self.sprite_list = sprite_list
Esempio n. 20
0
    def __init__(self, param_list, sprite_list, name, pos):
        base_statement.BaseStatement.__init__(self, "replace-block", pos)
        sprite_container.SpriteContainer.__init__(self, "replace-block", name)

        num_params = len(param_list)
        if not (1 <= num_params <= 2):
            raise generic.ScriptError(
                "replace-block requires 1 or 2 parameters, encountered " +
                str(num_params), pos)
        self.start_id = param_list[0]
        if num_params >= 2:
            self.image_file = param_list[1].reduce()
            if not isinstance(self.image_file, expression.StringLiteral):
                raise generic.ScriptError(
                    "replace-block parameter 2 'file' must be a string literal",
                    self.image_file.pos)
        else:
            self.image_file = None
        self.sprite_list = sprite_list
        self.add_sprite_data(self.sprite_list, self.image_file, pos)
Esempio n. 21
0
    def handle_grflangid(self, data, pos):
        """
        Handle a 'grflangid' pragma.

        @param data: Data of the pragma.
        @type  data: C{str}
        """
        if self.langid is not None:
            raise generic.ScriptError("grflangid already set", pos)
        lang_text = data.strip()
        value = LANG_NAMES.get(lang_text)
        if value is None:
            try:
                value = int(lang_text, 16)
            except ValueError:
                raise generic.ScriptError("Invalid grflangid {!r}".format(lang_text), pos)
        if value < 0 or value >= 0x7F:
            raise generic.ScriptError("Invalid grflangid", pos)
        self.langid = value
        self.check_expected_plural(None)
Esempio n. 22
0
    def pre_process(self):
        if len(self.args) not in (1, 2):
            raise generic.ScriptError(
                "engine_override expects 1 or 2 parameters", self.pos)

        if len(self.args) == 1:
            try:
                self.source_grfid = expression.Identifier("GRFID").reduce(
                    global_constants.const_list).value
                assert isinstance(self.source_grfid, int)
            except generic.ScriptError:
                raise generic.ScriptError(
                    "GRFID of this grf is required, but no grf-block is defined.",
                    self.pos)
        else:
            self.source_grfid = expression.parse_string_to_dword(
                self.args[0].reduce(global_constants.const_list))

        self.grfid = expression.parse_string_to_dword(self.args[-1].reduce(
            global_constants.const_list))
Esempio n. 23
0
def builtin_industry_type(name, args, pos):
    """
    industry_type(IND_TYPE_OLD | IND_TYPE_NEW, id) builtin function

    @return The industry type in the format used by grfs (industry prop 0x16 and var 0x64)
    """
    if len(args) != 2:
        raise generic.ScriptError(name + "() must have 2 parameters", pos)

    from nml import global_constants
    type = args[0].reduce_constant(global_constants.const_list).value
    if type not in (0, 1):
        raise generic.ScriptError("First argument of industry_type() must be IND_TYPE_OLD or IND_TYPE_NEW", pos)

    # Industry ID uses 7 bits (0 .. 6), bit 7 is for old/new
    id = args[1].reduce_constant(global_constants.const_list).value
    if not 0 <= id <= 127:
        raise generic.ScriptError("Second argument 'id' of industry_type() must be in range 0..127", pos)

    return ConstantNumeric(type << 7 | id)
Esempio n. 24
0
 def __init__(self, name, subtract_in, add_out, again, pos):
     if not isinstance(name, expression.Identifier):
         raise generic.ScriptError(
             "produce parameter 1 'name' should be an identifier.",
             name.pos)
     base_statement.BaseStatement.__init__(self, "produce-block", pos,
                                           False, False)
     self.initialize(name, 0x0A)
     self.subtract_in = subtract_in
     self.add_out = add_out
     self.again = again
Esempio n. 25
0
def convert_palette(pal):
    ret = 256 * [0]
    for idx, colour in enumerate(pal):
        if 0xD7 <= idx <= 0xE2:
            if idx != colour:
                raise generic.ScriptError(
                    "Indices 0xD7..0xE2 are not allowed in recolour sprites when the output is in the WIN palette"
                )
            continue
        ret[palmap_d2w[idx]] = palmap_d2w[colour]
    return ret
Esempio n. 26
0
    def supported_by_actionD(self, raise_error):
        if not self.op.actd_supports:
            if raise_error:
                if self.op == nmlop.STO_PERM:
                    raise generic.ScriptError(
                        "STORE_PERM is only available in switch-blocks.",
                        self.pos)
                elif self.op == nmlop.STO_TMP:
                    raise generic.ScriptError(
                        "STORE_TEMP is only available in switch-blocks.",
                        self.pos)

                #default case
                token = " '{}'".format(self.op.token) if self.op.token else ""
                raise generic.ScriptError(
                    "Operator{} not supported in parameter assignment".format(
                        token), self.pos)
            return False
        return self.expr1.supported_by_actionD(
            raise_error) and self.expr2.supported_by_actionD(raise_error)
Esempio n. 27
0
 def reduce_expressions(self, var_feature):
     for r in self.ranges[:]:
         if r.min is r.max and isinstance(r.min, expression.Identifier) and r.min.value == 'default':
             if self.default is not None:
                 raise generic.ScriptError("Switch-block has more than one default, which is impossible.", r.result.pos)
             self.default = r.result
             self.ranges.remove(r)
         else:
             r.reduce_expressions(var_feature)
     if self.default is not None and self.default.value is not None:
         self.default.value = action2var.reduce_varaction2_expr(self.default.value, var_feature)
Esempio n. 28
0
    def pre_process(self):
        if None in (self.name, self.desc, self.grfid, self.version,
                    self.min_compatible_version):
            raise generic.ScriptError(
                "A GRF-block requires the 'name', 'desc', 'grfid', 'version' and 'min_compatible_version' properties to be set.",
                self.pos)

        self.grfid = self.grfid.reduce()
        global_constants.constant_numbers[
            'GRFID'] = expression.parse_string_to_dword(self.grfid)
        self.name = self.name.reduce()
        if not isinstance(self.name, expression.String):
            raise generic.ScriptError("GRF-name must be a string",
                                      self.name.pos)
        grfstrings.validate_string(self.name)
        self.desc = self.desc.reduce()
        if not isinstance(self.desc, expression.String):
            raise generic.ScriptError("GRF-description must be a string",
                                      self.desc.pos)
        grfstrings.validate_string(self.desc)
        if self.url is not None:
            self.url = self.url.reduce()
            if not isinstance(self.url, expression.String):
                raise generic.ScriptError("URL must be a string", self.url.pos)
            grfstrings.validate_string(self.url)
        self.version = self.version.reduce_constant()
        self.min_compatible_version = self.min_compatible_version.reduce_constant(
        )

        global param_stats

        param_num = 0
        for param in self.params:
            param.pre_process(expression.ConstantNumeric(param_num))
            param_num = param.num.value + 1
            if param_num > param_stats[1]:
                raise generic.ScriptError(
                    "No free parameters available. Consider assigning <num> manually and combine multiple bool parameters into a single bitmask parameter using <bit>.",
                    self.pos)
            if param_num > param_stats[0]:
                param_stats[0] = param_num
Esempio n. 29
0
    def pre_process(self):
        feature = next(iter(self.feature_set))
        # var_feature is really weird for type=BACKWARD/FORWARD.
        # Expressions in cases will still refer to the origin vehicle.
        var_feature = action2var_variables.varact2parent_scope[
            feature] if self.type.value == "PARENT" else feature

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

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

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

        switch_base_class.pre_process(self)
Esempio n. 30
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

        # Prepare the return value
        expr = expression.ConstantNumeric(sprite_num, self.pos)
        # Add the sprite
        expr = expression.BinOp(nmlop.ADD, self.get_param('sprite'), expr,
                                self.pos)
        # Add the palette
        expr = expression.BinOp(
            nmlop.ADD,
            expression.BinOp(nmlop.SHIFT_LEFT, self.get_param('palette'),
                             expression.ConstantNumeric(16, self.pos),
                             self.pos), expr, self.pos)
        return expr.reduce()