コード例 #1
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 = nmlop.AND(nmlop.CMP_LE(args[0], 14, pos),
                     nmlop.CMP_GE(args[0], 0, pos))
    expr = nmlop.MUL(expr, args[0])
    # 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 = nmlop.MUL(nmlop.CMP_EQ(args[0], slope, pos), offset)
        expr = nmlop.ADD(expr, to_add)
    return expr
コード例 #2
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 = nmlop.MUL(expr.expr1, VarAction2LoadTempVar(guard_var))
         expr2 = nmlop.MUL(expr.expr2, VarAction2LoadTempVar(inverted_guard_var))
         return nmlop.ADD(expr1, expr2)
コード例 #3
0
ファイル: action3_callbacks.py プロジェクト: rexxars/nml
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 = nmlop.MUL(value, 329)
    return nmlop.DIV(value, 256)
コード例 #4
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, [])
コード例 #5
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)
    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 = nmlop.MUL(year, 365)
        part2 = nmlop.DIV(year, 4)
        part3 = nmlop.DIV(year, 100)
        part4 = nmlop.DIV(year, 400)
        res = nmlop.ADD(part1, part2)
        res = nmlop.SUB(res, part3)
        res = 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)
コード例 #6
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)])
コード例 #7
0
ファイル: functioncall.py プロジェクト: Xaroth/nml
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)
コード例 #8
0
ファイル: functioncall.py プロジェクト: Xaroth/nml
def builtin_num_corners_raised(name, args, pos):
    """
    num_corners_raised(slope) builtin function.
    slope is a 5-bit value

    @return Number of raised corners in a slope (4 for steep slopes)
    """
    if len(args) != 1:
        raise generic.ScriptError(name + "() must have 1 parameter", pos)

    slope = args[0]
    # The returned value is ((slope x 0x8421) & 0x11111) % 0xF
    # Explanation in steps: (numbers in binary)
    # - Masking constrains the slope to 5 bits, just to be sure (a|bcde)
    # - Multiplication creates 4 copies of those bits (abcd|eabc|deab|cdea|bcde)
    # - And-masking leaves only the lowest bit in each nibble (000d|000c|000b|000a|000e)
    # - The modulus operation adds one to the output for each set bit
    # - We now have the count of bits in the slope, which is wat we want. yay!
    slope = nmlop.AND(slope, 0x1F, pos)
    slope = nmlop.MUL(slope, 0x8421)
    slope = nmlop.AND(slope, 0x11111)
    return nmlop.MOD(slope, 0xF)
コード例 #9
0
ファイル: action0.py プロジェクト: spnda/nml
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 "unit_type" not 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 = nmlop.MUL(value, mul)
            value = nmlop.ADD(value, int(div / 2))
            value = nmlop.DIV(value, div)

    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]
コード例 #10
0
def value_mul_div(mul, div):
    return lambda var, info: nmlop.DIV(nmlop.MUL(var, mul), div)
コード例 #11
0
ファイル: snowline.py プロジェクト: werbfred/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 = nmlop.MUL(height, mul)
                height = nmlop.ADD(height, int(div / 2))
                height = nmlop.DIV(height, div)

        # 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