示例#1
0
def res_rand_vec(inst: Entity, res: Property) -> None:
    """A modification to RandomNum which generates a random vector instead.

    `decimal`, `seed` and `ResultVar` work like RandomNum. `min_x`, `max_y` etc
    are used to define the boundaries. If the min and max are equal that number
    will be always used instead.
    """
    is_float = srctools.conv_bool(res['decimal'])
    var = res['resultvar', '$random']

    set_random_seed(inst, 'e' + res['seed', 'random'])

    if is_float:
        func = random.uniform
    else:
        func = random.randint

    value = Vec()

    for axis in 'xyz':
        max_val = srctools.conv_float(res['max_' + axis, 0.0])
        min_val = srctools.conv_float(res['min_' + axis, 0.0])
        if min_val == max_val:
            value[axis] = min_val
        else:
            value[axis] = func(min_val, max_val)

    inst.fixup[var] = value.join(' ')
示例#2
0
def flag_random(inst: Entity, res: Property) -> bool:
    """Randomly is either true or false."""
    if res.has_children():
        chance = res['chance', '100']
        seed = 'a' + res['seed', '']
    else:
        chance = res.value
        seed = 'a'

    # Allow ending with '%' sign
    chance = srctools.conv_int(chance.rstrip('%'), 100)

    set_random_seed(inst, seed)
    return random.randrange(100) < chance
示例#3
0
def res_add_variant(inst: Entity, res: Property) -> None:
    """This allows using a random instance from a weighted group.

    A suffix will be added in the form `_var4`.
    Two properties should be given:

    - `Number`: The number of random instances.
    - `Weights`: A comma-separated list of weights for each instance.

    Any variant has a chance of weight/sum(weights) of being chosen:
    A weight of `2, 1, 1` means the first instance has a 2/4 chance of
    being chosen, and the other 2 have a 1/4 chance of being chosen.  
    The chosen variant depends on the position, direction and name of
    the instance.

    Alternatively, you can use `"variant" "number"` to choose from equally-weighted
    options.
    """
    set_random_seed(inst, 'variant')
    conditions.add_suffix(inst, "_var" + str(random.choice(res.value) + 1))
示例#4
0
def res_random(inst: Entity, res: Property) -> None:
    """Randomly choose one of the sub-results to execute.

    The `chance` value defines the percentage chance for any result to be
    chosen. `weights` defines the weighting for each result. Both are
    comma-separated, matching up with the results following. Wrap a set of
    results in a `group` property block to treat them as a single result to be
    executed in order.
    """
    # Note: 'global' results like "Has" won't delete themselves!
    # Instead they're replaced by 'dummy' results that don't execute.
    # Otherwise the chances would be messed up.
    seed, chance, weight, results = res.value  # type: str, float, List[int], List[Property]

    set_random_seed(inst, seed)
    if random.randrange(100) > chance:
        return

    ind = random.choice(weight)
    choice = results[ind]
    if choice.name == 'nop':
        pass
    elif choice.name == 'group':
        for sub_res in choice:
            should_del = Condition.test_result(
                inst,
                sub_res,
            )
            if should_del is RES_EXHAUSTED:
                # This Result doesn't do anything!
                sub_res.name = 'nop'
                sub_res.value = None
    else:
        should_del = Condition.test_result(
            inst,
            choice,
        )
        if should_del is RES_EXHAUSTED:
            choice.name = 'nop'
            choice.value = None
示例#5
0
def res_rand_num(inst: Entity, res: Property) -> None:
    """Generate a random number and save in a fixup value.

    If 'decimal' is true, the value will contain decimals. 'max' and 'min' are
    inclusive. 'ResultVar' is the variable the result will be saved in.
    If 'seed' is set, it will be used to keep the value constant across
    map recompiles. This should be unique.
    """
    is_float = srctools.conv_bool(res['decimal'])
    max_val = srctools.conv_float(res['max', 1.0])
    min_val = srctools.conv_float(res['min', 0.0])
    var = res['resultvar', '$random']
    seed = 'd' + res['seed', 'random']

    set_random_seed(inst, seed)

    if is_float:
        func = random.uniform
    else:
        func = random.randint

    inst.fixup[var] = str(func(min_val, max_val))
示例#6
0
def res_rand_inst_shift(inst: Entity, res: Property) -> None:
    """Randomly shift a instance by the given amounts.

    The positions are local to the instance.
    """
    (
        min_x, max_x,
        min_y, max_y,
        min_z, max_z,
        seed,
    ) = res.value  # type: float, float, float, float, float, float, str

    set_random_seed(inst, seed)

    offset = Vec(
        random.uniform(min_x, max_x),
        random.uniform(min_y, max_y),
        random.uniform(min_z, max_z),
    )

    offset.rotate_by_str(inst['angles'])
    origin = Vec.from_str(inst['origin'])
    origin += offset
    inst['origin'] = origin
示例#7
0
def res_set_tile(inst: Entity, res: Property) -> None:
    """Set 4x4 parts of a tile to the given values.

    `Offset` defines the position of the upper-left tile in the grid.
    Each `Tile` section defines a row of the positions to edit like so:
        "Tile" "bbbb"
        "Tile" "b..b"
        "Tile" "b..b"
        "Tile" "bbbb"
    If `Force` is true, the specified tiles will override any existing ones
    and create the tile if necessary.
    Otherwise they will be merged in - white/black tiles will not replace
    tiles set to nodraw or void for example.
    `chance`, if specified allows producing irregular tiles by randomly not
    changing the tile.

    If you need less regular placement (other orientation, precise positions)
    use a bee2_template_tilesetter in a template.

    Allowed tile characters:
    - `W`: White tile.
    - `w`: White 4x4 only tile.
    - `B`: Black tile.
    - `b`: Black 4x4 only tile.
    - `g`: The side/bottom of goo pits.
    - `n`: Nodraw surface.
    - `i`: Invert the tile surface, if black/white.
    - `1`: Convert to a 1x1 only tile, if a black/white tile.
    - `4`: Convert to a 4x4 only tile, if a black/white tile.
    - `.`: Void (remove the tile in this position).
    - `_` or ` `: Placeholder (don't modify this space).
    - `x`: Cutout Tile (Broken)
    - `o`: Cutout Tile (Partial)
    """
    origin = Vec.from_str(inst['origin'])
    angles = Vec.from_str(inst['angles'])

    offset = (res.vec('offset', -48, 48) - (0, 0, 64)).rotate(*angles)
    offset += origin

    norm = Vec(0, 0, 1).rotate(*angles)

    force_tile = res.bool('force')

    tiles: List[str] = [
        row.value
        for row in res
        if row.name in ('tile', 'tiles')
    ]
    if not tiles:
        raise ValueError('No "tile" parameters in SetTile!')

    chance = srctools.conv_float(res['chance', '100'].rstrip('%'), 100.0)
    if chance < 100.0:
        conditions.set_random_seed(inst, 'tile' + res['seed', ''])

    for y, row in enumerate(tiles):
        for x, val in enumerate(row):
            if val in '_ ':
                continue

            if chance < 100.0 and random.uniform(0, 100) > chance:
                continue

            pos = Vec(32 * x, -32 * y, 0).rotate(*angles) + offset

            if val == '4':
                size = tiling.TileSize.TILE_4x4
            elif val == '1':
                size = tiling.TileSize.TILE_1x1
            elif val == 'i':
                size = None
            else:
                try:
                    new_tile = tiling.TILETYPE_FROM_CHAR[val]  # type: tiling.TileType
                except KeyError:
                    LOGGER.warning('Unknown tiletype "{}"!', val)
                else:
                    tiling.edit_quarter_tile(pos, norm, new_tile, force_tile)
                continue

            # Edit the existing tile.
            try:
                tile, u, v = tiling.find_tile(pos, norm, force_tile)
            except KeyError:
                LOGGER.warning(
                    'Expected tile, but none found: {}, {}',
                    pos,
                    norm,
                )
                continue

            if size is None:
                # Invert the tile.
                tile[u, v] = tile[u, v].inverted
                continue

            # Unless forcing is enabled don't alter the size of GOO_SIDE.
            if tile[u, v].is_tile and tile[u, v] is not tiling.TileType.GOO_SIDE:
                tile[u, v] = tiling.TileType.with_color_and_size(
                    size,
                    tile[u, v].color
                )
            elif force_tile:
                # If forcing, make it black. Otherwise no need to change.
                tile[u, v] = tiling.TileType.with_color_and_size(
                    size,
                    tiling.Portalable.BLACK
                )