コード例 #1
0
ファイル: vactubes.py プロジェクト: GLiTcH2/BEE2.4
def join_markers(inst_a, inst_b, is_start=False):
    """Join two marker ents together with corners.

    This returns a list of solids used for the vphysics_motion trigger.
    """
    origin_a = Vec.from_str(inst_a['ent']['origin'])
    origin_b = Vec.from_str(inst_b['ent']['origin'])

    norm_a = Vec(-1, 0, 0).rotate_by_str(inst_a['ent']['angles'])
    norm_b = Vec(-1, 0, 0).rotate_by_str(inst_b['ent']['angles'])

    config = inst_a['conf']

    if norm_a == norm_b:
        # Either straight-line, or s-bend.
        dist = (origin_a - origin_b).mag()

        if origin_a + (norm_a * dist) == origin_b:
            make_straight(
                origin_a,
                norm_a,
                dist,
                config,
                is_start,
            )
        # else: S-bend, we don't do the geometry for this..
        return

    if norm_a == -norm_b:
        # U-shape bend..
        make_ubend(
            origin_a,
            origin_b,
            norm_a,
            config,
            max_size=inst_a['size'],
        )
        return

    try:
        corner_ang, flat_angle = CORNER_ANG[norm_a.as_tuple(), norm_b.as_tuple()]

        if origin_a[flat_angle] != origin_b[flat_angle]:
            # It needs to be flat in this angle!
            raise ValueError
    except ValueError:
        # The tubes need two corners to join together - abort for that.
        return
    else:
        make_bend(
            origin_a,
            origin_b,
            norm_a,
            norm_b,
            corner_ang,
            config,
            max_size=inst_a['size'],
        )
コード例 #2
0
ファイル: vactubes.py プロジェクト: GLiTcH2/BEE2.4
def make_straight(
        origin: Vec,
        normal: Vec,
        dist: int,
        config: dict,
        is_start=False,
    ):
    """Make a straight line of instances from one point to another."""

    # 32 added to the other directions, plus extended dist in the direction
    # of the normal - 1
    p1 = origin + (normal * ((dist // 128 * 128) - 96))
    # The starting brush needs to
    # stick out a bit further, to cover the
    # point_push entity.
    p2 = origin - (normal * (96 if is_start else 32))

    # bbox before +- 32 to ensure the above doesn't wipe it out
    p1, p2 = Vec.bbox(p1, p2)

    solid = vbsp.VMF.make_prism(
        # Expand to 64x64 in the other two directions
        p1 - 32, p2 + 32,
        mat='tools/toolstrigger',
    ).solid

    motion_trigger(solid.copy())

    push_trigger(origin, normal, [solid])

    angles = normal.to_angle()

    support_file = config['support']
    straight_file = config['straight']
    support_positions = (
        SUPPORT_POS[normal.as_tuple()]
        if support_file else
        []
    )

    for off in range(0, int(dist), 128):
        position = origin + off * normal
        vbsp.VMF.create_ent(
            classname='func_instance',
            origin=position,
            angles=angles,
            file=straight_file,
        )

        for supp_ang, supp_off in support_positions:
            if (position + supp_off).as_tuple() in SOLIDS:
                vbsp.VMF.create_ent(
                    classname='func_instance',
                    origin=position,
                    angles=supp_ang,
                    file=support_file,
                )
コード例 #3
0
ファイル: conditions.py プロジェクト: xDadiKx/BEE2.4
def res_fizzler_pair(begin_inst, res):
    """Modify the instance of a fizzler to link with its pair."""
    orig_target = begin_inst["targetname"]

    if "modelEnd" in orig_target:
        return  # We only execute starting from the start side.

    orig_target = orig_target[:-11]  # remove "_modelStart"
    end_name = orig_target + "_modelEnd"  # What we search for

    # The name all these instances get
    pair_name = orig_target + "-model" + str(begin_inst.id)

    orig_file = begin_inst["file"]

    begin_file = res["StartInst", orig_file]
    end_file = res["EndInst", orig_file]
    mid_file = res["MidInst", ""]

    begin_inst["file"] = begin_file
    begin_inst["targetname"] = pair_name

    angles = Vec.from_str(begin_inst["angles"])
    # We round it to get rid of 0.00001 inprecision from the calculations.
    direction = Vec(0, 0, 1).rotate(angles.x, angles.y, angles.z)
    ":type direction: utils.Vec"

    begin_pos = Vec.from_str(begin_inst["origin"])
    axis_1, axis_2, main_axis = PAIR_AXES[direction.as_tuple()]
    for end_inst in VMF.by_class["func_instance"]:
        if end_inst["targetname", ""] != end_name:
            # Only examine this barrier hazard's instances!
            continue
        end_pos = Vec.from_str(end_inst["origin"])
        if begin_pos[axis_1] == end_pos[axis_1] and begin_pos[axis_2] == end_pos[axis_2]:
            length = int(end_pos[main_axis] - begin_pos[main_axis])
            break
    else:
        utils.con_log("No matching pair for {}!!".format(orig_target))
        return
    end_inst["targetname"] = pair_name
    end_inst["file"] = end_file

    if mid_file != "":
        # Go 64 from each side, and always have at least 1 section
        # A 128 gap will have length = 0
        for dis in range(0, abs(length) + 1, 128):
            new_pos = begin_pos + direction * dis
            VMF.create_ent(
                classname="func_instance",
                targetname=pair_name,
                angles=begin_inst["angles"],
                file=mid_file,
                origin=new_pos.join(" "),
            )
コード例 #4
0
ファイル: img.py プロジェクト: Coolasp1e/BEE2.4
def color_square(color: utils.Vec, size=16):
    """Create a square image of the given size, with the given color."""
    key = color.x, color.y, color.z, size

    try:
        return cached_squares[key]
    except KeyError:
        img = Image.new(
            mode='RGB',
            size=(size, size),
            color=(int(color.x), int(color.y), int(color.z)),
        )
        tk_img = ImageTk.PhotoImage(image=img)
        cached_squares[color.as_tuple(), size] = tk_img

        return tk_img
コード例 #5
0
ファイル: brushes.py プロジェクト: goodDOS/BEE2.4
def res_hollow_brush(inst, res):
    """Hollow out the attached brush, as if EmbeddedVoxel was set.

    This just removes the surface if it's already an embeddedVoxel. This allows
    multiple items to embed thinly in the same block without affecting each
    other.
    """
    loc = Vec(0, 0, -64).rotate_by_str(inst['angles'])
    loc += Vec.from_str(inst['origin'])

    try:
        group = SOLIDS[loc.as_tuple()]
    except KeyError:
        LOGGER.warning('No brush for hollowing at ({})', loc)
        return  # No brush here?

    conditions.hollow_block(
        group,
        remove_orig_face=utils.conv_bool(res['RemoveFace', False])
    )
コード例 #6
0
ファイル: conditions.py プロジェクト: xDadiKx/BEE2.4
def res_make_catwalk(_, res):
    """Speciallised result to generate catwalks from markers.

    Only runs once, and then quits the condition list.
    """
    utils.con_log("Starting catwalk generator...")
    marker = resolve_inst(res["markerInst"])
    output_target = res["output_name", "MARKER"]

    instances = {
        name: resolve_inst(res[name, ""])[0]
        for name in (
            "straight_128",
            "straight_256",
            "straight_512",
            "corner",
            "tjunction",
            "crossjunction",
            "end",
            "stair",
            "end_wall",
            "support_wall",
            "support_ceil",
            "support_floor",
            "single_wall",
            "markerInst",
        )
    }
    # If there are no attachments remove a catwalk piece
    instances["NONE"] = ""
    if instances["end_wall"] == "":
        instances["end_wall"] = instances["end"]

    connections = {}  # The directions this instance is connected by (NSEW)
    markers = {}

    for inst in VMF.by_class["func_instance"]:
        if inst["file"].casefold() not in marker:
            continue
        #                   [North, South, East,  West ]
        connections[inst] = [False, False, False, False]
        markers[inst["targetname"]] = inst

    if not markers:
        return True  # No catwalks!

    utils.con_log("Conn:", connections)
    utils.con_log("Markers:", markers)

    # First loop through all the markers, adding connecting sections
    for inst in markers.values():
        for conn in inst.outputs:
            if conn.output != output_target or conn.input != output_target:
                # Indicator toggles or similar, delete these
                print("Removing ", conn.target)
                for del_inst in VMF.by_target[conn.target]:
                    del_inst.remove()
                continue

            inst2 = markers[conn.target]
            print(inst["targetname"], "<->", inst2["targetname"])
            origin1 = Vec.from_str(inst["origin"])
            origin2 = Vec.from_str(inst2["origin"])
            if origin1.x != origin2.x and origin1.y != origin2.y:
                utils.con_log("Instances not aligned!")
                continue

            y_dir = origin1.x == origin2.x  # Which way the connection is
            if y_dir:
                dist = abs(origin1.y - origin2.y)
            else:
                dist = abs(origin1.x - origin2.x)
            vert_dist = origin1.z - origin2.z

            utils.con_log("Dist =", dist, ", Vert =", vert_dist)

            if dist // 2 < vert_dist:
                # The stairs are 2 long, 1 high.
                utils.con_log("Not enough room for stairs!")
                continue

            if dist > 128:
                # add straight sections in between
                place_catwalk_connections(instances, origin1, origin2)

            # Update the lists based on the directions that were set
            conn_lst1 = connections[inst]
            conn_lst2 = connections[inst2]
            if origin1.x < origin2.x:
                conn_lst1[2] = True  # E
                conn_lst2[3] = True  # W
            elif origin2.x < origin1.x:
                conn_lst1[3] = True  # W
                conn_lst2[2] = True  # E

            if origin1.y < origin2.y:
                conn_lst1[0] = True  # N
                conn_lst2[1] = True  # S
            elif origin2.y < origin1.y:
                conn_lst1[1] = True  # S
                conn_lst2[0] = True  # N

        inst.outputs.clear()  # Remove the outputs now, they're useless

    for inst, dir_mask in connections.items():
        # Set the marker instances based on the attached walkways.
        print(inst["targetname"], dir_mask)
        angle = Vec.from_str(inst["angles"], 0, 0, 0)
        new_type, inst["angles"] = utils.CONN_LOOKUP[tuple(dir_mask)]
        inst["file"] = instances[CATWALK_TYPES[new_type]]

        normal = Vec(0, 0, 1).rotate(angle.x, angle.y, angle.z)
        ":type normal: Vec"

        if new_type is utils.CONN_TYPES.side:
            # If the end piece is pointing at a wall, switch the instance.
            if normal.z == 0:
                # Treat booleans as ints to get the direction the connection is
                # in - True == 1, False == 0
                conn_dir = Vec(x=dir_mask[2] - dir_mask[3], y=dir_mask[0] - dir_mask[1], z=0)  # +E, -W  # +N, -S,
                if normal == conn_dir:
                    inst["file"] = instances["end_wall"]
            continue  # We never have normal supports on end pieces
        elif new_type is utils.CONN_TYPES.none:
            # Unconnected catwalks on the wall switch to a special instance.
            # This lets players stand next to a portal surface on the wall.
            if normal.z == 0:
                inst["file"] = instances["single_wall"]
                inst["angles"] = INST_ANGLE[normal.as_tuple()]
            else:
                inst.remove()
            continue  # These don't get supports otherwise

        # Add regular supports
        if normal == (0, 0, 1):
            supp = instances["support_floor"]
        elif normal == (0, 0, -1):
            supp = instances["support_ceil"]
        else:
            supp = instances["support_wall"]

        if supp:
            VMF.create_ent(
                classname="func_instance", origin=inst["origin"], angles=INST_ANGLE[normal.as_tuple()], file=supp
            )

    utils.con_log("Finished catwalk generation!")
    return True  # Don't run this again
コード例 #7
0
ファイル: brushes.py プロジェクト: goodDOS/BEE2.4
def res_import_template(inst, res):
    """Import a template VMF file, retexturing it to match orientatation.

    It will be placed overlapping the given instance.
    Options:
    - ID: The ID of the template to be inserted.
    - force: a space-seperated list of overrides. If 'white' or 'black' is
             present, the colour of tiles will be overriden. If a tile size
            ('2x2', '4x4', 'wall', 'special') is included, all tiles will
            be switched to that size (if not a floor/ceiling). If 'world' or
            'detail' is present, the brush will be forced to that type.
    - replace: A block of template material -> replacement textures.
            This is case insensitive - any texture here will not be altered
            otherwise.
    - replaceBrush: The position of a brush to replace (0 0 0=the surface).
            This brush will be removed, and overlays will be fixed to use
            all faces with the same normal.
    """
    (
        temp_id,
        replace_tex,
        force_colour,
        force_grid,
        force_type,
        replace_brush_pos,
    ) = res.value

    if temp_id not in TEMPLATES:
        # The template map is read in after setup is performed, so
        # it must be checked here!
        # We don't want an error, just quit
        LOGGER.warning('"{}" not a valid template!', temp_id)
        return

    origin = Vec.from_str(inst['origin'])
    angles = Vec.from_str(inst['angles', '0 0 0'])
    temp_data = conditions.import_template(
        temp_id,
        origin,
        angles,
        targetname=inst['targetname', ''],
        force_type=force_type,
    )
    conditions.retexture_template(
        temp_data,
        origin,
        replace_tex,
        force_colour,
        force_grid,
    )

    # This is the original brush the template is replacing. We fix overlay
    # face IDs, so this brush is replaced by the faces in the template pointing
    # the same way.
    if replace_brush_pos is None:
        return

    pos = Vec(replace_brush_pos).rotate(angles.x, angles.y, angles.z)
    pos += origin

    try:
        brush_group = SOLIDS[pos.as_tuple()]
    except KeyError:
        return

    vbsp.VMF.remove_brush(brush_group.solid)
    new_ids = []

    all_brushes = temp_data.world
    if temp_data.detail is not None:
        for ent in temp_data.detail:
            all_brushes.extend(ent.solids)

    for brush in all_brushes:  # type: VLib.Solid
        for face in brush.sides:
            # Only faces pointing the same way!
            if face.normal() == brush_group.normal:
                # Skip tool brushes (nodraw, player clips..)
                if face.mat.casefold().startswith('tools/'):
                    continue
                new_ids.append(str(face.id))

    if new_ids:
        conditions.reallocate_overlays({
            str(brush_group.face.id): new_ids,
        })
コード例 #8
0
ファイル: fizzler.py プロジェクト: Coolasp1e/BEE2.4
def res_fizzler_pair(begin_inst, res):
    """Modify the instance of a fizzler to link with its pair.

    Each pair will be given a name along the lines of "fizz_name-model1334".
    Values:
        - StartInst, EndInst: The instances used for each end
        - MidInst: An instance placed every 128 units between emitters.
    """
    orig_target = begin_inst['targetname']

    if 'modelEnd' in orig_target:
        return  # We only execute starting from the start side.

    orig_target = orig_target[:-11]  # remove "_modelStart"
    end_name = orig_target + '_modelEnd'  # What we search for

    # The name all these instances get
    pair_name = orig_target + '-model' + str(begin_inst.id)

    orig_file = begin_inst['file']

    begin_file = res['StartInst', orig_file]
    end_file = res['EndInst', orig_file]
    mid_file = res['MidInst', '']

    begin_inst['file'] = begin_file
    begin_inst['targetname'] = pair_name

    direction = Vec(0, 0, 1).rotate_by_str(begin_inst['angles'])

    begin_pos = Vec.from_str(begin_inst['origin'])
    axis_1, axis_2, main_axis = PAIR_AXES[direction.as_tuple()]
    for end_inst in vbsp.VMF.by_class['func_instance']:
        if end_inst['targetname', ''] != end_name:
            # Only examine this barrier hazard's instances!
            continue
        end_pos = Vec.from_str(end_inst['origin'])
        if (
                begin_pos[axis_1] == end_pos[axis_1] and
                begin_pos[axis_2] == end_pos[axis_2]
                ):
            length = int(end_pos[main_axis] - begin_pos[main_axis])
            break
    else:
        LOGGER.warning('No matching pair for {}!!', orig_target)
        return
    end_inst['targetname'] = pair_name
    end_inst['file'] = end_file

    if mid_file != '':
        # Go 64 from each side, and always have at least 1 section
        # A 128 gap will have length = 0
        for dis in range(0, abs(length) + 1, 128):
            new_pos = begin_pos + direction*dis
            vbsp.VMF.create_ent(
                classname='func_instance',
                targetname=pair_name,
                angles=begin_inst['angles'],
                file=mid_file,
                origin=new_pos.join(' '),
            )
コード例 #9
0
ファイル: vactubes.py プロジェクト: GLiTcH2/BEE2.4
def make_ubend(
        origin_a: Vec,
        origin_b: Vec,
        normal: Vec,
        config,
        max_size: int,
        is_start=False,
):
    """Create u-shaped bends."""
    offset = origin_b - origin_a

    out_axis = normal.axis()
    out_off = offset[out_axis]
    offset[out_axis] = 0

    if len(offset) == 2:
        # Len counts the non-zero values..
        # If 2, the ubend is diagonal so it's ambigous where to put the bends.
        return []

    side_norm = offset.norm()

    for side_axis, side_dist in zip('xyz', offset):
        if side_dist:
            side_dist = abs(side_dist) + 128
            break
    else:
        # The two tube items are on top of another, that's
        # impossible to generate.
        return []

    # Calculate the size of the various parts.
    # first/second _size = size of the corners.
    # first/second _straight = length of straight sections
    # off_straight = length of straight in between corners
    if out_off == 0:
        # Both tubes are parallel to each other - use half the distance
        # for the bends.
        first_size = second_size = min(
            3,
            max_size,
            side_dist // (128 * 2),
        )
        first_straight = second_straight = 0
        side_straight = side_dist - 2 * 128 * first_size
    elif out_off > 0:
        # The second tube is further away than the first - the first bend
        # should be largest.
        # We need 1 spot for the second bend.
        first_size = min(
            3,
            max_size,
            side_dist // 128 - 1,
            out_off,
        )
        second_size = min(3, side_dist // 128 - first_size, max_size)

        first_straight = (out_off + 128) - 128 * second_size
        second_straight = (first_size - second_size) * 128

        side_straight = (side_dist / 128 - first_size - second_size) * 128

    elif out_off < 0:
        # The first tube is further away than the second - the second bend
        # should be largest.
        second_size = min(
            3,
            max_size,
            side_dist // 128 - 1,
            -out_off  # -out = abs()
        )
        first_size = min(3, side_dist // 128 - second_size, max_size)

        first_straight = (second_size - first_size) * 128
        second_straight = (-out_off + 128) - 128 * second_size

        side_straight = (side_dist / 128 - first_size - second_size) * 128
    else:
        return []  # Not possible..

    # We always have a straight segment at the first marker point - move
    # everything up slightly.

    first_straight += 128

    LOGGER.info(
        'Ubend {}: {}, c={}, {}, c={}, {}',
        out_off,
        first_straight,
        first_size,
        side_straight,
        second_size,
        second_straight,
    )

    make_straight(
        origin_a,
        normal,
        first_straight,
        config,
        is_start,
    )

    first_corner_loc = origin_a + (normal * first_straight)

    make_corner(
        first_corner_loc,
        CORNER_ANG[normal.as_tuple(), side_norm.as_tuple()].ang,
        first_size,
        config,
    )

    off_straight_loc = first_corner_loc + normal * (128 * (first_size - 1))
    off_straight_loc += side_norm * (128 * first_size)

    if side_straight > 0:
        make_straight(
            off_straight_loc,
            side_norm,
            side_straight,
            config,
        )

    sec_corner_loc = off_straight_loc + side_norm * side_straight

    make_corner(
        sec_corner_loc,
        CORNER_ANG[side_norm.as_tuple(), (-normal).as_tuple()].ang,
        second_size,
        config,
    )

    if second_straight > 0:
        make_straight(
            sec_corner_loc - normal * (128 * second_size),
            -normal,
            second_straight,
            config,
        )
コード例 #10
0
ファイル: entities.py プロジェクト: Coolasp1e/BEE2.4
def res_insert_overlay(inst: VLib.Entity, res: Property):
    """Use a template to insert one or more overlays on a surface.

    Options:
        - ID: The template ID. Brushes will be ignored.
        - Replace: old -> new material replacements
        - Face_pos: The offset of the brush face.
        - Normal: The direction of the brush face.
        - Offset: An offset to move the overlays by.
    """
    (
        temp_id,
        replace,
        face,
        norm,
        offset,
    ) = res.value

    if temp_id[:1] == '$':
        temp_id = inst.fixup[temp_id]

    origin = Vec.from_str(inst['origin'])  # type: Vec
    angles = Vec.from_str(inst['angles', '0 0 0'])

    face_pos = Vec(face).rotate(*angles)
    face_pos += origin
    normal = Vec(norm).rotate(*angles)

    # Don't make offset change the face_pos value..
    origin += offset.copy().rotate_by_str(
        inst['angles', '0 0 0']
    )

    for axis, norm in enumerate(normal):
        # Align to the center of the block grid. The normal direction is
        # already correct.
        if norm == 0:
            face_pos[axis] = face_pos[axis] // 128 * 128 + 64

    try:
        face_id = SOLIDS[face_pos.as_tuple()].face.id
    except KeyError:
        LOGGER.warning(
            'Overlay brush position is not valid: {}',
            face_pos,
        )
        return

    temp = conditions.import_template(
        temp_id,
        origin,
        angles,
        targetname=inst['targetname', ''],
        force_type=TEMP_TYPES.detail,
    )

    for over in temp.overlay:  # type: VLib.Entity
        random.seed('TEMP_OVERLAY_' + over['basisorigin'])
        mat = random.choice(replace.get(
            over['material'],
            (over['material'], ),
        ))
        if mat[:1] == '$':
            mat = inst.fixup[mat]
        over['material'] = mat
        over['sides'] = str(face_id)

    # Wipe the brushes from the map.
    if temp.detail is not None:
        temp.detail.remove()
        LOGGER.info(
            'Overlay template "{}" could set keep_brushes=0.',
            temp_id,
        )
コード例 #11
0
ファイル: brushes.py プロジェクト: SpyyZ158/BEE2.4
def res_import_template(inst, res):
    """Import a template VMF file, retexturing it to match orientatation.

    It will be placed overlapping the given instance.
    Options:
    - ID: The ID of the template to be inserted.
    - force: a space-seperated list of overrides. If 'white' or 'black' is
             present, the colour of tiles will be overriden. If 'invert' is
            added, white/black tiles will be swapped. If a tile size
            ('2x2', '4x4', 'wall', 'special') is included, all tiles will
            be switched to that size (if not a floor/ceiling). If 'world' or
            'detail' is present, the brush will be forced to that type.
    - replace: A block of template material -> replacement textures.
            This is case insensitive - any texture here will not be altered
            otherwise.
    - replaceBrush: The position of a brush to replace (0 0 0=the surface).
            This brush will be removed, and overlays will be fixed to use
            all faces with the same normal.
    - keys/localkeys: If set, a brush entity will instead be generated with
            these values. This overrides force world/detail. The origin is
            set automatically.
    - invertVar: If this fixup value is true, tile colour will be swapped to
            the opposite of the current force option. If it is set to
            'white' or 'black', that colour will be forced instead.
    """
    (
        temp_id,
        replace_tex,
        force_colour,
        force_grid,
        force_type,
        replace_brush_pos,
        invert_var,
        key_block,
    ) = res.value

    if temp_id not in TEMPLATES:
        # The template map is read in after setup is performed, so
        # it must be checked here!
        # We don't want an error, just quit
        LOGGER.warning('"{}" not a valid template!', temp_id)
        return

    if invert_var != '':
        invert_val = inst.fixup[invert_var].casefold()

        if invert_val == 'white':
            force_colour = conditions.MAT_TYPES.white
        elif invert_val == 'black':
            force_colour = conditions.MAT_TYPES.black
        elif utils.conv_bool(invert_val):
            force_colour = conditions.TEMP_COLOUR_INVERT[force_colour]

    origin = Vec.from_str(inst['origin'])
    angles = Vec.from_str(inst['angles', '0 0 0'])
    temp_data = conditions.import_template(
        temp_id,
        origin,
        angles,
        targetname=inst['targetname', ''],
        force_type=force_type,
    )
    conditions.retexture_template(
        temp_data,
        origin,
        replace_tex,
        force_colour,
        force_grid,
    )

    if key_block is not None:
        conditions.set_ent_keys(temp_data.detail, inst, key_block)
        br_origin = Vec.from_str(key_block.find_key('keys')['origin'])
        br_origin.localise(origin, angles)
        temp_data.detail['origin'] = br_origin
        # Add it to the list of ignored brushes, so vbsp.change_brush() doesn't
        # modify it.
        vbsp.IGNORED_BRUSH_ENTS.add(temp_data.detail)

    # This is the original brush the template is replacing. We fix overlay
    # face IDs, so this brush is replaced by the faces in the template pointing
    # the same way.
    if replace_brush_pos is None:
        return

    pos = Vec(replace_brush_pos).rotate(angles.x, angles.y, angles.z)
    pos += origin

    try:
        brush_group = SOLIDS[pos.as_tuple()]
    except KeyError:
        return

    vbsp.VMF.remove_brush(brush_group.solid)
    new_ids = []

    all_brushes = temp_data.world
    # Overlays can't be applied to entities (other than func_detail).
    if temp_data.detail is not None and key_block is None:
        all_brushes.extend(temp_data.detail.solids)

    for brush in all_brushes:  # type: VLib.Solid
        for face in brush.sides:
            # Only faces pointing the same way!
            if face.normal() == brush_group.normal:
                # Skip tool brushes (nodraw, player clips..)
                if face.mat.casefold().startswith('tools/'):
                    continue
                new_ids.append(str(face.id))

    if new_ids:
        conditions.reallocate_overlays({
            str(brush_group.face.id): new_ids,
        })
コード例 #12
0
ファイル: catwalks.py プロジェクト: Coolasp1e/BEE2.4
def res_make_catwalk(_, res):
    """Speciallised result to generate catwalks from markers.

    Only runs once, and then quits the condition list.
    Instances:
        MarkerInst: The instance set in editoritems.
        Straight_128/256/512: Straight sections. Extends East
        Corner: A corner piece. Connects on N and W sides.
        TJunction; A T-piece. Connects on all but the East side.
        CrossJunction: A X-piece. Connects on all sides.
        End: An end piece. Connects on the East side.
        Stair: A stair. Starts East and goes Up and West.
        End_wall: Connects a West wall to a East catwalk.
        Support_Wall: A support extending from the East wall.
        Support_Ceil: A support extending from the ceiling.
        Support_Floor: A support extending from the floor.
        Support_Goo: A floor support, designed for goo pits.
        Single_Wall: A section connecting to an East wall.
    """
    LOGGER.info("Starting catwalk generator...")
    marker = resolve_inst(res['markerInst'])
    output_target = res['output_name', 'MARKER']

    instances = {
        name: resolve_inst(res[name, ''])[0]
        for name in
        (
            'straight_128', 'straight_256', 'straight_512',
            'corner', 'tjunction', 'crossjunction', 'end', 'stair', 'end_wall',
            'support_wall', 'support_ceil', 'support_floor', 'support_goo',
            'single_wall',
            'markerInst',
        )
    }
    # If there are no attachments remove a catwalk piece
    instances['NONE'] = ''
    if instances['end_wall'] == '':
        instances['end_wall'] = instances['end']

    connections = {}  # The directions this instance is connected by (NSEW)
    markers = {}

    # Find all our markers, so we can look them up by targetname.
    for inst in vbsp.VMF.by_class['func_instance']:
        if inst['file'].casefold() not in marker:
            continue
        #                   [North, South, East,  West ]
        connections[inst] = [False, False, False, False]
        markers[inst['targetname']] = inst

        # Snap the markers to the grid. If on glass it can become offset...
        origin = Vec.from_str(inst['origin'])
        origin = origin // 128 * 128  # type: Vec
        origin += 64

        while origin.as_tuple() in conditions.GOO_LOCS:
            # The instance is in goo! Switch to floor orientation, and move
            # up until it's in air.
            inst['angles'] = '0 0 0'
            origin.z += 128

        inst['origin'] = str(origin)

    if not markers:
        return RES_EXHAUSTED

    LOGGER.info('Connections: {}', connections)
    LOGGER.info('Markers: {}', markers)

    # First loop through all the markers, adding connecting sections
    for inst in markers.values():
        for conn in inst.outputs:
            if conn.output != output_target or conn.input != output_target:
                # Indicator toggles or similar, delete these entities.
                # Find the associated overlays too.
                for del_inst in vbsp.VMF.by_target[conn.target]:
                    conditions.remove_ant_toggle(del_inst)
                continue

            inst2 = markers[conn.target]
            LOGGER.debug('{} <-> {}', inst['targetname'], inst2['targetname'])
            origin1 = Vec.from_str(inst['origin'])
            origin2 = Vec.from_str(inst2['origin'])
            if origin1.x != origin2.x and origin1.y != origin2.y:
                LOGGER.warning('Instances not aligned!')
                continue

            y_dir = origin1.x == origin2.x  # Which way the connection is
            if y_dir:
                dist = abs(origin1.y - origin2.y)
            else:
                dist = abs(origin1.x - origin2.x)
            vert_dist = origin1.z - origin2.z

            LOGGER.debug('Dist = {}, Vert = {}', dist, vert_dist)

            if (dist - 128) // 2 < abs(vert_dist):
                # The stairs are 2 long, 1 high. Check there's enough room
                # Subtract the last block though, since that's a corner.
                LOGGER.warning('Not enough room for stairs!')
                continue

            if dist > 128:
                # add straight sections in between
                place_catwalk_connections(instances, origin1, origin2)

            # Update the lists based on the directions that were set
            conn_lst1 = connections[inst]
            conn_lst2 = connections[inst2]
            if origin1.x < origin2.x:
                conn_lst1[2] = True  # E
                conn_lst2[3] = True  # W
            elif origin2.x < origin1.x:
                conn_lst1[3] = True  # W
                conn_lst2[2] = True  # E

            if origin1.y < origin2.y:
                conn_lst1[0] = True  # N
                conn_lst2[1] = True  # S
            elif origin2.y < origin1.y:
                conn_lst1[1] = True  # S
                conn_lst2[0] = True  # N

        inst.outputs.clear()  # Remove the outputs now, they're useless

    for inst, dir_mask in connections.items():
        # Set the marker instances based on the attached walkways.
        normal = Vec(0, 0, 1).rotate_by_str(inst['angles'])

        new_type, inst['angles'] = utils.CONN_LOOKUP[tuple(dir_mask)]
        inst['file'] = instances[CATWALK_TYPES[new_type]]

        if new_type is utils.CONN_TYPES.side:
            # If the end piece is pointing at a wall, switch the instance.
            if normal.z == 0:
                # Treat booleans as ints to get the direction the connection is
                # in - True == 1, False == 0
                conn_dir = Vec(
                    x=dir_mask[2] - dir_mask[3],  # +E, -W
                    y=dir_mask[0] - dir_mask[1],  # +N, -S,
                    z=0,
                )
                if normal == conn_dir:
                    inst['file'] = instances['end_wall']
            continue  # We never have normal supports on end pieces
        elif new_type is utils.CONN_TYPES.none:
            # Unconnected catwalks on the wall switch to a special instance.
            # This lets players stand next to a portal surface on the wall.
            if normal.z == 0:
                inst['file'] = instances['single_wall']
                inst['angles'] = INST_ANGLE[normal.as_tuple()]
            else:
                inst.remove()
            continue  # These don't get supports otherwise

        # Add regular supports
        if normal == (0, 0, 1):
            # If in goo, use different supports!
            origin = Vec.from_str(inst['origin'])
            origin.z -= 128
            if origin.as_tuple() in conditions.GOO_LOCS:
                supp = instances['support_goo']
            else:
                supp = instances['support_floor']
        elif normal == (0, 0, -1):
            supp = instances['support_ceil']
        else:
            supp = instances['support_wall']

        if supp:
            vbsp.VMF.create_ent(
                classname='func_instance',
                origin=inst['origin'],
                angles=INST_ANGLE[normal.as_tuple()],
                file=supp,
            )

    LOGGER.info('Finished catwalk generation!')
    return RES_EXHAUSTED
コード例 #13
0
ファイル: cutoutTile.py プロジェクト: GLiTcH2/BEE2.4
def convert_floor(
        loc: Vec,
        overlay_ids,
        mats,
        settings,
        signage_loc,
        detail,
        noise_weight,
        noise_func: SimplexNoise,
):
    """Cut out tiles at the specified location."""
    try:
        brush = conditions.SOLIDS[loc.as_tuple()]
    except KeyError:
        return False  # No tile here!


    if brush.normal == (0, 0, 1):
        # This is a pillar block - there isn't actually tiles here!
        # We need to generate a squarebeams brush to fill this gap.

        brush.face.mat = 'tools/toolsnodraw'  # It won't be visible
        temp_data = conditions.import_template(
            temp_name=FLOOR_TEMP_PILLAR,
            origin=loc,
        )
        conditions.retexture_template(
            temp_data,
            loc,
            # Switch to use the configured squarebeams texture
            replace_tex={
                'anim_wp/framework/squarebeams': random.choice(
                    MATS['squarebeams']
                ),
            }
        )
        return False

    # The new brush IDs overlays need to use
    # NOTE: strings, not ints!
    ant_locs = overlay_ids[str(brush.face.id)] = []

    # Move the floor brush down and switch to the floorbase texture.
    for plane in brush.face.planes:
        plane.z -= FLOOR_DEPTH
    brush.face.mat = random.choice(mats['floorbase'])

    loc.x -= 64
    loc.y -= 64

    for x, y in utils.iter_grid(max_x=4, max_y=4):
        tile_loc = loc + (x * 32 + 16, y * 32 + 16, 0)
        if tile_loc.as_tuple() in signage_loc:
            # Force the tile to be present under signage..
            should_make_tile = True
            rand = 100
            # We don't need to check this again in future!
            signage_loc.remove(tile_loc.as_tuple())
        else:
            # Create a number between 0-100
            rand = 100 * get_noise(tile_loc // 32, noise_func) + 10

            # Adjust based on the noise_weight value, so boundries have more tiles
            rand *= 0.1 + 0.9 * (1 - noise_weight)

            should_make_tile = rand < settings['floor_chance']
            if random.randint(0, 7) == 0:
                # Sometimes there'll be random holes/extra tiles
                should_make_tile = not should_make_tile

        if should_make_tile:
            # Full tile
            tile = make_tile(
                p1=tile_loc - (16, 16, 0),
                p2=tile_loc + (16, 16, -2),
                top_mat=vbsp.get_tex(str(brush.color) + '.floor'),
                bottom_mat='tools/toolsnodraw',
                beam_mat=random.choice(mats['squarebeams']),
            )
            detail.solids.append(tile.solid)
            ant_locs.append(str(tile.top.id))
        elif rand < settings['floor_glue_chance']:
            # 'Glue' tile - this chance should be higher, making these appear
            # bordering the full tiles.
            tile = make_tile(
                p1=tile_loc - (16, 16, 1),
                p2=tile_loc + (16, 16, -2),
                top_mat=random.choice(mats['tile_glue']),
                bottom_mat='tools/toolsnodraw',
                beam_mat=random.choice(mats['squarebeams']),
            )
            detail.solids.append(tile.solid)
        else:
            # No tile at this loc!
            pass

    return True