예제 #1
0
def fix_base_brush(vmf: VMF, solid: Solid, face: Side):
    """Retexture the brush forming the bottom of a pit."""
    if SETTINGS['skybox'] != '':
        face.mat = 'tools/toolsskybox'
    else:
        # We have a pit shell, we don't want a bottom.
        vmf.remove_brush(solid)
예제 #2
0
def fix_base_brush(vmf: VMF, solid: Solid, face: Side):
    """Retexture the brush forming the bottom of a pit."""
    if SETTINGS['skybox'] != '':
        face.mat = 'tools/toolsskybox'
        vbsp.IGNORED_FACES.add(face)
    else:
        # We have a pit shell, we don't want a bottom.
        vmf.remove_brush(solid)
예제 #3
0
def flag_brush_at_loc(vmf: VMF, inst: Entity, flag: Property):
    """Checks to see if a wall is present at the given location.

    - `Pos` is the position of the brush, where `0 0 0` is the floor-position
       of the brush.
    - `Dir` is the normal the face is pointing. `(0 0 -1)` is up.
    - `Type` defines the type the brush must be:
      - `Any` requires either a black or white brush.
      - `None` means that no brush must be present.
      - `White` requires a portalable surface.
      - `Black` requires a non-portalable surface.
    - `SetVar` defines an instvar which will be given a value of `black`,
      `white` or `none` to allow the result to be reused.
    - If `gridPos` is true, the position will be snapped so it aligns with
      the 128 grid (Useful with fizzler/light strip items).
    - `RemoveBrush`: If set to `1`, the brush will be removed if found.
      Only do this to `EmbedFace` brushes, since it will remove the other
      sides as well.
    """
    pos = Vec.from_str(flag['pos', '0 0 0'])
    pos.z -= 64  # Subtract so origin is the floor-position
    pos = pos.rotate_by_str(inst['angles', '0 0 0'])

    # Relative to the instance origin
    pos += Vec.from_str(inst['origin', '0 0 0'])

    norm = flag['dir', None]
    if norm is not None:
        norm = Vec.from_str(norm).rotate_by_str(inst['angles', '0 0 0'], )

    if srctools.conv_bool(flag['gridpos', '0']) and norm is not None:
        for axis in 'xyz':
            # Don't realign things in the normal's axis -
            # those are already fine.
            if norm[axis] == 0:
                pos[axis] = pos[axis] // 128 * 128 + 64

    result_var = flag['setVar', '']
    should_remove = srctools.conv_bool(flag['RemoveBrush', False], False)
    des_type = flag['type', 'any'].casefold()

    brush = SOLIDS.get(pos.as_tuple(), None)

    if brush is None or (norm is not None and abs(brush.normal) != abs(norm)):
        br_type = 'none'
    else:
        br_type = str(brush.color)
        if should_remove:
            vmf.remove_brush(brush.solid, )

    if result_var:
        inst.fixup[result_var] = br_type

    if des_type == 'any' and br_type != 'none':
        return True

    return des_type == br_type
예제 #4
0
    def read_from_map(self, vmf: VMF, has_attr: Dict[str, bool]) -> None:
        """Given the map file, set blocks."""
        search_locs = []

        for ent in vmf.entities:
            pos = ent['origin', None]
            if pos is None:
                continue
            pos = world_to_grid(Vec.from_str(pos))

            # Exclude entities outside the main area - elevators mainly.
            # The border should never be set to air!
            if (0, 0, 0) <= pos <= (25, 25, 25):
                search_locs.append(pos)

        can_have_pit = bottomlessPit.pits_allowed()

        for brush in vmf.brushes[:]:
            tex = {face.mat.casefold() for face in brush.sides}

            bbox_min, bbox_max = brush.get_bbox()

            if ('nature/toxicslime_a2_bridge_intro' in tex or
                'nature/toxicslime_puzzlemaker_cheap' in tex):
                # It's goo!

                x = bbox_min.x + 64
                y = bbox_min.y + 64

                g_x = x // 128
                g_y = y // 128

                is_pit = can_have_pit and bottomlessPit.is_pit(bbox_min, bbox_max)

                # If goo is multi-level, we want to record all pos!
                z_pos = range(int(bbox_min.z) + 64, int(bbox_max.z), 128)
                top_ind = len(z_pos) - 1
                for ind, z in enumerate(z_pos):
                    g_z = z // 128
                    self[g_x, g_y, g_z] = Block.from_pitgoo_attr(
                        is_pit,
                        is_top=(ind == top_ind),
                        is_bottom=(ind == 0),
                    )
                    # If goo has totally submerged tunnels, they are not filled.
                    # Add each horizontal neighbour to the search list.
                    # If not found they'll be ignored.
                    if ind != top_ind: # Don't bother on the top level..
                        search_locs.extend([
                            (g_x - 1, g_y, g_z),
                            (g_x + 1, g_y, g_z),
                            (g_x, g_y + 1, g_z),
                            (g_x, g_y - 1, g_z),
                        ])

                # Bottomless pits don't use goo, so remove the water..
                if is_pit:
                    vmf.remove_brush(brush)

                # Indicate that this map contains goo/pits
                if is_pit:
                    has_attr[VOICE_ATTR_PIT] = True
                else:
                    has_attr[VOICE_ATTR_GOO] = True

                continue

            pos = world_to_grid(brush.get_origin(bbox_min, bbox_max))

            if bbox_max - bbox_min == (128, 128, 128):
                # Full block..
                self[pos] = Block.SOLID
            else:
                # Must be an embbedvoxel block
                self[pos] = Block.EMBED

        LOGGER.info(
            'Analysed map, filling air... ({} starting positions..)',
            len(search_locs)
        )
        self.fill_air(search_locs)
        LOGGER.info('Air filled!')
예제 #5
0
def flag_brush_at_loc(vmf: VMF, inst: Entity, flag: Property):
    """Checks to see if a wall is present at the given location.

    - `Pos` is the position of the brush, where `0 0 0` is the floor-position
       of the brush.
    - `Dir` is the normal the face is pointing. `(0 0 -1)` is up.
    - `Type` defines the type the brush must be:
      - `Any` requires either a black or white brush.
      - `None` means that no brush must be present.
      - `White` requires a portalable surface.
      - `Black` requires a non-portalable surface.
    - `SetVar` defines an instvar which will be given a value of `black`,
      `white` or `none` to allow the result to be reused.
    - If `gridPos` is true, the position will be snapped so it aligns with
      the 128 grid (Useful with fizzler/light strip items).
    - `RemoveBrush`: If set to `1`, the brush will be removed if found.
      Only do this to `EmbedFace` brushes, since it will remove the other
      sides as well.
    """
    pos = Vec.from_str(flag['pos', '0 0 0'])
    pos.z -= 64  # Subtract so origin is the floor-position
    pos = pos.rotate_by_str(inst['angles', '0 0 0'])

    # Relative to the instance origin
    pos += Vec.from_str(inst['origin', '0 0 0'])

    norm = flag['dir', None]
    if norm is not None:
        norm = Vec.from_str(norm).rotate_by_str(
            inst['angles', '0 0 0'],
        )

    if srctools.conv_bool(flag['gridpos', '0']) and norm is not None:
        for axis in 'xyz':
            # Don't realign things in the normal's axis -
            # those are already fine.
            if norm[axis] == 0:
                pos[axis] = pos[axis] // 128 * 128 + 64

    result_var = flag['setVar', '']
    should_remove = srctools.conv_bool(flag['RemoveBrush', False], False)
    des_type = flag['type', 'any'].casefold()

    brush = SOLIDS.get(pos.as_tuple(), None)

    if brush is None or (norm is not None and abs(brush.normal) != abs(norm)):
        br_type = 'none'
    else:
        br_type = str(brush.color)
        if should_remove:
            vmf.remove_brush(
                brush.solid,
            )

    if result_var:
        inst.fixup[result_var] = br_type

    if des_type == 'any' and br_type != 'none':
        return True

    return des_type == br_type
예제 #6
0
    def read_from_map(self, vmf: VMF, has_attr: Dict[str, bool]) -> None:
        """Given the map file, set blocks."""
        from conditions import EMBED_OFFSETS
        from instance_traits import get_item_id

        # Starting points to fill air and goo.
        # We want to fill goo first...
        air_search_locs: List[Tuple[Vec, bool]] = []
        goo_search_locs: List[Tuple[Vec, bool]] = []

        for ent in vmf.entities:
            str_pos = ent['origin', None]
            if str_pos is None:
                continue

            pos = world_to_grid(Vec.from_str(str_pos))

            # Exclude entities outside the main area - elevators mainly.
            # The border should never be set to air!
            if (0, 0, 0) <= pos <= (25, 25, 25):
                air_search_locs.append((Vec(pos.x, pos.y, pos.z), False))

            # We need to manually set EmbeddedVoxel locations.
            # These might not be detected for items where there's a block
            # which is entirely empty - corridors and obs rooms for example.
            item_id = get_item_id(ent)
            if item_id:
                try:
                    embed_locs = EMBED_OFFSETS[item_id]
                except KeyError:
                    continue
                angles = Vec.from_str(ent['angles'])
                for local_pos in embed_locs:
                    world_pos = local_pos - (0, 0, 1)
                    world_pos.localise(pos, angles)
                    self[world_pos] = Block.EMBED

        can_have_pit = bottomlessPit.pits_allowed()

        for brush in vmf.brushes[:]:
            tex = {face.mat.casefold() for face in brush.sides}

            bbox_min, bbox_max = brush.get_bbox()

            if (
                'nature/toxicslime_a2_bridge_intro' in tex or
                'nature/toxicslime_puzzlemaker_cheap' in tex
            ):
                # It's goo!

                x = bbox_min.x + 64
                y = bbox_min.y + 64

                g_x = x // 128
                g_y = y // 128

                is_pit = can_have_pit and bottomlessPit.is_pit(bbox_min, bbox_max)

                # If goo is multi-level, we want to record all pos!
                z_pos = range(int(bbox_min.z) + 64, int(bbox_max.z), 128)
                top_ind = len(z_pos) - 1
                for ind, z in enumerate(z_pos):
                    g_z = z // 128
                    self[g_x, g_y, g_z] = Block.from_pitgoo_attr(
                        is_pit,
                        is_top=(ind == top_ind),
                        is_bottom=(ind == 0),
                    )
                    # If goo has totally submerged tunnels, they are not filled.
                    # Add each horizontal neighbour to the search list.
                    # If not found they'll be ignored.
                    goo_search_locs += [
                        (Vec(g_x - 1, g_y, g_z), True),
                        (Vec(g_x + 1, g_y, g_z), True),
                        (Vec(g_x, g_y + 1, g_z), True),
                        (Vec(g_x, g_y - 1, g_z), True),
                    ]

                # Remove the brush, since we're not going to use it.
                vmf.remove_brush(brush)

                # Indicate that this map contains goo/pits
                if is_pit:
                    has_attr[VOICE_ATTR_PIT] = True
                else:
                    has_attr[VOICE_ATTR_GOO] = True

                continue

            pos = world_to_grid(brush.get_origin(bbox_min, bbox_max))

            if bbox_max - bbox_min == (128, 128, 128):
                # Full block..
                self[pos] = Block.SOLID
            else:
                # Must be an embbedvoxel block
                self[pos] = Block.EMBED

        LOGGER.info(
            'Analysed map, filling air... ({} starting positions..)',
            len(air_search_locs)
        )
        self.fill_air(goo_search_locs + air_search_locs)
        LOGGER.info('Air filled!')
예제 #7
0
    def read_from_map(self, vmf: VMF, has_attr: dict[str, bool],
                      items: dict[str, editoritems.Item]) -> None:
        """Given the map file, set blocks."""
        from precomp.instance_traits import get_item_id
        from precomp import bottomlessPit

        # Starting points to fill air and goo.
        # We want to fill goo first...
        air_search_locs: list[tuple[Vec, bool]] = []
        goo_search_locs: list[tuple[Vec, bool]] = []

        for ent in vmf.entities:
            str_pos = ent['origin', None]
            if str_pos is None:
                continue

            pos = world_to_grid(Vec.from_str(str_pos))

            # Exclude entities outside the main area - elevators mainly.
            # The border should never be set to air!
            if not ((0, 0, 0) <= pos <= (25, 25, 25)):
                continue

            # We need to manually set EmbeddedVoxel locations.
            # These might not be detected for items where there's a block
            # which is entirely empty - corridors and obs rooms for example.
            # We also need to check occupy locations, so that it can seed search
            # locs.
            item_id = get_item_id(ent)
            seeded = False
            if item_id:
                try:
                    item = items[item_id.casefold()]
                except KeyError:
                    pass
                else:
                    orient = Matrix.from_angle(Angle.from_str(ent['angles']))
                    for local_pos in item.embed_voxels:
                        # Offset down because 0 0 0 is the floor voxel.
                        world_pos = (Vec(local_pos) - (0, 0, 1)) @ orient + pos
                        self[round(world_pos, 0)] = Block.EMBED
                    for occu in item.occupy_voxels:
                        world_pos = Vec(occu.pos) @ orient + pos
                        air_search_locs.append((round(world_pos, 0), False))
                        seeded = True
            if not seeded:
                # Assume origin is its location.
                air_search_locs.append((pos.copy(), False))

        can_have_pit = bottomlessPit.pits_allowed()

        for brush in vmf.brushes[:]:
            tex = {face.mat.casefold() for face in brush.sides}

            bbox_min, bbox_max = brush.get_bbox()

            if ('nature/toxicslime_a2_bridge_intro' in tex
                    or 'nature/toxicslime_puzzlemaker_cheap' in tex):
                # It's goo!

                x = bbox_min.x + 64
                y = bbox_min.y + 64

                g_x = x // 128
                g_y = y // 128

                is_pit = can_have_pit and bottomlessPit.is_pit(
                    bbox_min, bbox_max)

                # If goo is multi-level, we want to record all pos!
                z_pos = range(int(bbox_min.z) + 64, int(bbox_max.z), 128)
                top_ind = len(z_pos) - 1
                for ind, z in enumerate(z_pos):
                    g_z = z // 128
                    self[g_x, g_y, g_z] = Block.from_pitgoo_attr(
                        is_pit,
                        is_top=(ind == top_ind),
                        is_bottom=(ind == 0),
                    )
                    # If goo has totally submerged tunnels, they are not filled.
                    # Add each horizontal neighbour to the search list.
                    # If not found they'll be ignored.
                    goo_search_locs += [
                        (Vec(g_x - 1, g_y, g_z), True),
                        (Vec(g_x + 1, g_y, g_z), True),
                        (Vec(g_x, g_y + 1, g_z), True),
                        (Vec(g_x, g_y - 1, g_z), True),
                    ]

                # Remove the brush, since we're not going to use it.
                vmf.remove_brush(brush)

                # Indicate that this map contains goo/pits
                if is_pit:
                    has_attr[VOICE_ATTR_PIT] = True
                else:
                    has_attr[VOICE_ATTR_GOO] = True

                continue

            pos = world_to_grid(brush.get_origin(bbox_min, bbox_max))

            if bbox_max - bbox_min == (128, 128, 128):
                # Full block..
                self[pos] = Block.SOLID
            else:
                # Must be an embbedvoxel block
                self[pos] = Block.EMBED

        LOGGER.info('Analysed map, filling air... ({} starting positions..)',
                    len(air_search_locs))
        self.fill_air(goo_search_locs + air_search_locs)
        LOGGER.info('Air filled!')