コード例 #1
0
ファイル: instances.py プロジェクト: Coolasp1e/BEE2.4
def res_replace_instance(inst: VLib.Entity, res):
    """Replace an instance with another entity.

    'keys' and 'localkeys' defines the new keyvalues used.
    'targetname' and 'angles' are preset, and 'origin' will be used to offset
    the given amount from the current location.
    If 'keep_instance' is true, the instance entity will be kept instead of
    removed.
    """
    import vbsp

    origin = Vec.from_str(inst['origin'])
    angles = inst['angles']

    if not utils.conv_bool(res['keep_instance', '0'], False):
        inst.remove()  # Do this first to free the ent ID, so the new ent has
        # the same one.

    # We copy to allow us to still acess the $fixups and other values.
    new_ent = inst.copy(des_id=inst.id)
    new_ent.clear_keys()
    # Ensure there's a classname, just in case.
    new_ent['classname'] = 'info_null'

    vbsp.VMF.add_ent(new_ent)

    conditions.set_ent_keys(new_ent, inst, res)

    origin += Vec.from_str(new_ent['origin']).rotate_by_str(angles)
    new_ent['origin'] = origin
    new_ent['angles'] = angles
    new_ent['targetname'] = inst['targetname']
コード例 #2
0
ファイル: conditions.py プロジェクト: xDadiKx/BEE2.4
def flag_angles(inst, flag):
    """Check that a instance is pointed in a direction."""
    angle = inst["angles", "0 0 0"]

    if flag.has_children():
        targ_angle = flag["direction", "0 0 0"]
        from_dir = flag["from_dir", "0 0 1"]
        if from_dir.casefold() in DIRECTIONS:
            from_dir = Vec(DIRECTIONS[from_dir.casefold()])
        else:
            from_dir = Vec.from_str(from_dir, 0, 0, 1)
        allow_inverse = utils.conv_bool(flag["allow_inverse", "0"])
    else:
        targ_angle = flag.value
        from_dir = Vec(0, 0, 1)
        allow_inverse = False

    if angle == targ_angle:
        return True  # Check for exact match

    normal = DIRECTIONS.get(targ_angle.casefold(), None)
    if normal is None:
        return False  # If it's not a special angle,
        # so it failed the exact match

    angle = Vec.from_str(angle, 0, 0, 0)
    inst_normal = from_dir.rotate(angle.x, angle.y, angle.z)

    if normal == "WALL":
        # Special case - it's not on the floor or ceiling
        return not (inst_normal == (0, 0, 1) or inst_normal == (0, 0, -1))
    else:
        return inst_normal == normal or (allow_inverse and -inst_normal == normal)
コード例 #3
0
ファイル: positioning.py プロジェクト: goodDOS/BEE2.4
def flag_brush_at_loc(inst, flag):
    """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 brushes (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.
    """
    from conditions import VMF
    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 = Vec.from_str(flag['dir', '0 0 -1']).rotate_by_str(
        inst['angles', '0 0 0']
    )

    if utils.conv_bool(flag['gridpos', '0']):
        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 = utils.conv_bool(flag['RemoveBrush', False], False)
    des_type = flag['type', 'any'].casefold()

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

    if brush is None or brush.normal != 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
ファイル: conditions.py プロジェクト: xDadiKx/BEE2.4
def res_faith_mods(inst, res):
    """Modify the trigger_catrapult that is created for ItemFaithPlate items.

    """
    # Get data about the trigger this instance uses for flinging
    fixup_var = res["instvar", ""]
    offset = utils.conv_int(res["raise_trig", "0"])
    if offset:
        angle = Vec.from_str(inst["angles", "0 0 0"])
        offset = Vec(0, 0, offset).rotate(angle.x, angle.y, angle.z)
        ":type offset Vec"
    for trig in VMF.by_class["trigger_catapult"]:
        if inst["targetname"] in trig["targetname"]:
            if offset:  # Edit both the normal and the helper trigger
                trig["origin"] = (Vec.from_str(trig["origin"]) + offset).join(" ")
                for solid in trig.solids:
                    solid.translate(offset)

            for out in trig.outputs:
                if out.inst_in == "animate_angled_relay":
                    out.inst_in = res["angled_targ", "animate_angled_relay"]
                    out.input = res["angled_in", "Trigger"]
                    if fixup_var:
                        inst.fixup[fixup_var] = "angled"
                    break
                elif out.inst_in == "animate_straightup_relay":
                    out.inst_in = res["straight_targ", "animate_straightup_relay"]
                    out.input = res["straight_in", "Trigger"]
                    if fixup_var:
                        inst.fixup[fixup_var] = "straight"
                    break
コード例 #5
0
ファイル: conditions.py プロジェクト: NotFirestorm348/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 = round(Vec(0, 0, 1).rotate(angles.x, angles.y, angles.z))
    ':type direction: utils.Vec'
    print(end_name, direction)

    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(' '),
            )
コード例 #6
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'],
        )
コード例 #7
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(" "),
            )
コード例 #8
0
ファイル: scaffold.py プロジェクト: goodDOS/BEE2.4
def res_unst_scaffold_setup(res):
    group = res["group", "DEFAULT_GROUP"]

    if group not in SCAFFOLD_CONFIGS:
        # Store our values in the CONFIGS dictionary
        targ_inst, links = SCAFFOLD_CONFIGS[group] = {}, {}
    else:
        # Grab the already-filled values, and add to them
        targ_inst, links = SCAFFOLD_CONFIGS[group]

    for block in res.find_all("Instance"):
        conf = {
            # If set, adjusts the offset appropriately
            "is_piston": utils.conv_bool(block["isPiston", "0"]),
            "rotate_logic": utils.conv_bool(block["AlterAng", "1"], True),
            "off_floor": Vec.from_str(block["FloorOff", "0 0 0"]),
            "off_wall": Vec.from_str(block["WallOff", "0 0 0"]),
            "logic_start": block["startlogic", ""],
            "logic_end": block["endLogic", ""],
            "logic_mid": block["midLogic", ""],
            "logic_start_rev": block["StartLogicRev", None],
            "logic_end_rev": block["EndLogicRev", None],
            "logic_mid_rev": block["EndLogicRev", None],
            "inst_wall": block["wallInst", ""],
            "inst_floor": block["floorInst", ""],
            "inst_offset": block["offsetInst", None],
            # Specially rotated to face the next track!
            "inst_end": block["endInst", None],
        }
        for logic_type in ("logic_start", "logic_mid", "logic_end"):
            if conf[logic_type + "_rev"] is None:
                conf[logic_type + "_rev"] = conf[logic_type]

        for inst in resolve_inst(block["file"]):
            targ_inst[inst] = conf

    # We need to provide vars to link the tracks and beams.
    for block in res.find_all("LinkEnt"):
        # The name for this set of entities.
        # It must be a '@' name, or the name will be fixed-up incorrectly!
        loc_name = block["name"]
        if not loc_name.startswith("@"):
            loc_name = "@" + loc_name
        links[block["nameVar"]] = {
            "name": loc_name,
            # The next entity (not set in end logic)
            "next": block["nextVar"],
            # A '*' name to reference all the ents (set on the start logic)
            "all": block["allVar", None],
        }

    return group  # We look up the group name to find the values.
コード例 #9
0
ファイル: positioning.py プロジェクト: Coolasp1e/BEE2.4
def flag_goo_at_loc(inst, flag):
    """Check to see if a given location is submerged in goo.

    0 0 0 is the origin of the instance, values are in 128 increments.
    """
    pos = Vec.from_str(flag.value).rotate_by_str(inst['angles', '0 0 0'])
    pos *= 128
    pos += Vec.from_str(inst['origin'])

    # Round to 128 units, then offset to the center
    pos = pos // 128 * 128 + 64  # type: Vec
    val = pos.as_tuple() in GOO_LOCS
    return val
コード例 #10
0
ファイル: trackPlat.py プロジェクト: Coolasp1e/BEE2.4
def track_scan(
        tr_set,
        track_inst,
        start_track: VLib.Entity,
        middle_file: str,
        x_dir: int,
        ):
    """Build a set of track instances extending from a point.
    :param track_inst: A dictionary mapping origins to track instances
    :param start_track: The instance we start on
    :param middle_file: The file for the center track piece
    :param x_dir: The direction to look (-1 or 1)
    """
    track = start_track
    move_dir = Vec(x_dir*128, 0, 0).rotate_by_str(track['angles'])
    while track:
        tr_set.add(track)

        next_pos = Vec.from_str(track['origin']) + move_dir
        track = track_inst.get(next_pos.as_tuple(), None)
        if track is None:
            return
        if track['file'].casefold() != middle_file:
            # If the next piece is an end section, add it then quit
            tr_set.add(track)
            return
コード例 #11
0
ファイル: entities.py プロジェクト: Coolasp1e/BEE2.4
def res_import_template_setup(res):
    temp_id = res['id'].casefold()

    face = Vec.from_str(res['face_pos', '0 0 -64'])
    norm = Vec.from_str(res['normal', '0 0 1'])

    replace_tex = defaultdict(list)
    for prop in res.find_key('replace', []):
        replace_tex[prop.name].append(prop.value)

    offset = Vec.from_str(res['offset', '0 0 0'])

    return (
        temp_id,
        dict(replace_tex),
        face,
        norm,
        offset,
    )
コード例 #12
0
ファイル: cutoutTile.py プロジェクト: goodDOS/BEE2.4
def find_indicator_panels(inst):
    """We need to locate indicator panels, so they aren't overwritten.
    """
    if inst['file'].casefold() not in resolve_inst('[indpan]'):
        return
    loc = Vec(0, 0, -64).rotate_by_str(inst['angles'])
    loc += Vec.from_str(inst['origin'])

    # Sometimes (light bridges etc) a sign will be halfway between
    # tiles, so in that case we need to force 2 tiles.
    loc_min = (loc - (15, 15, 0)) // 32 * 32 + (16, 16, 0)
    loc_max = (loc + (15, 15, 0)) // 32 * 32 + (16, 16, 0)
    FORCE_LOCATIONS.add(loc_min.as_tuple())
    FORCE_LOCATIONS.add(loc_max.as_tuple())
コード例 #13
0
ファイル: positioning.py プロジェクト: Coolasp1e/BEE2.4
def res_translate_inst(inst, res):
    """Translate the instance locally by the given amount.

    The special values <piston>, <piston_bottom> and <piston_top> can be
    used to offset it based on the starting position, bottom or top position
    of a piston platform.
    """
    folded_val = res.value.casefold()
    if folded_val == '<piston>':
        folded_val = (
            '<piston_top>' if
            utils.conv_bool(inst.fixup['$start_up'])
            else '<piston_bottom>'
        )

    if folded_val == '<piston_top>':
        val = Vec(z=128 * utils.conv_int(inst.fixup['$top_level', '1'], 1))
    elif folded_val == '<piston_bottom>':
        val = Vec(z=128 * utils.conv_int(inst.fixup['$bottom_level', '0'], 0))
    else:
        val = Vec.from_str(res.value)

    offset = val.rotate_by_str(inst['angles'])
    inst['origin'] = (offset + Vec.from_str(inst['origin'])).join(' ')
コード例 #14
0
ファイル: custItems.py プロジェクト: goodDOS/BEE2.4
def res_faith_mods(inst, res):
    """Modify the trigger_catrapult that is created for ItemFaithPlate items.

    Values:
        - raise_trig: Raise or lower the trigger_catapults by this amount.
        - angled_targ, angled_in: Instance entity and input for angled plates
        - straight_targ, straight_in: Instance entity and input for
            straight plates
        - instvar: A $replace value to set to either 'angled' or '
            'straight'.
    """
    # Get data about the trigger this instance uses for flinging
    fixup_var = res['instvar', '']
    offset = utils.conv_int(res['raise_trig', '0'])
    if offset:
        offset = Vec(0, 0, offset).rotate_by_str(inst['angles', '0 0 0'])
        ':type offset Vec'
    for trig in vbsp.VMF.by_class['trigger_catapult']:
        if inst['targetname'] in trig['targetname']:
            if offset:  # Edit both the normal and the helper trigger
                trig['origin'] = (
                    Vec.from_str(trig['origin']) +
                    offset
                ).join(' ')
                for solid in trig.solids:
                    solid.translate(offset)

            # Inspect the outputs to determine the type.
            # We also change them if desired, since that's not possible
            # otherwise.

            for out in trig.outputs:
                if out.inst_in == 'animate_angled_relay':
                    out.inst_in = res['angled_targ', 'animate_angled_relay']
                    out.input = res['angled_in', 'Trigger']
                    if fixup_var:
                        inst.fixup[fixup_var] = 'angled'
                    break # There's only one output we want to look for...
                elif out.inst_in == 'animate_straightup_relay':
                    out.inst_in = res[
                        'straight_targ', 'animate_straightup_relay'
                    ]
                    out.input = res['straight_in', 'Trigger']
                    if fixup_var:
                        inst.fixup[fixup_var] = 'straight'
                    break
コード例 #15
0
ファイル: brushes.py プロジェクト: goodDOS/BEE2.4
def res_import_template_setup(res):
    temp_id = res['id'].casefold()

    force = res['force', ''].casefold().split()
    if 'white' in force:
        force_colour = MAT_TYPES.white
    elif 'black' in force:
        force_colour = MAT_TYPES.black
    elif 'invert' in force:
        force_colour = 'INVERT'
    else:
        force_colour = None

    if 'world' in force:
        force_type = TEMP_TYPES.world
    elif 'detail' in force:
        force_type = TEMP_TYPES.detail
    else:
        force_type = TEMP_TYPES.default

    for size in ('2x2', '4x4', 'wall', 'special'):
        if size in force:
            force_grid = size
            break
    else:
        force_grid = None

    replace_tex = defaultdict(list)
    for prop in res.find_key('replace', []):
        replace_tex[prop.name].append(prop.value)

    replace_brush = res['replaceBrush', None]
    if replace_brush:
        replace_brush_pos = Vec.from_str(replace_brush)
        replace_brush_pos.z -= 64  # 0 0 0 defaults to the floor.
    else:
        replace_brush_pos = None

    return (
        temp_id,
        dict(replace_tex),
        force_colour,
        force_grid,
        force_type,
        replace_brush_pos,
    )
コード例 #16
0
ファイル: positioning.py プロジェクト: goodDOS/BEE2.4
def flag_angles(inst, flag):
    """Check that a instance is pointed in a direction.

    The value should be either just the angle to check, or a block of
    options:
    - Angle: A unit vector (XYZ value) pointing in a direction, or some
        keywords: +z, -y, N/S/E/W, up/down, floor/ceiling, or walls
    - From_dir: The direction the unrotated instance is pointed in.
        This lets the flag check multiple directions
    - Allow_inverse: If true, this also returns True if the instance is
        pointed the opposite direction .
    """
    angle = inst['angles', '0 0 0']

    if flag.has_children():
        targ_angle = flag['direction', '0 0 0']
        from_dir = flag['from_dir', '0 0 1']
        if from_dir.casefold() in DIRECTIONS:
            from_dir = Vec(DIRECTIONS[from_dir.casefold()])
        else:
            from_dir = Vec.from_str(from_dir, 0, 0, 1)
        allow_inverse = utils.conv_bool(flag['allow_inverse', '0'])
    else:
        targ_angle = flag.value
        from_dir = Vec(0, 0, 1)
        allow_inverse = False

    if angle == targ_angle:
        return True  # Check for exact match

    normal = DIRECTIONS.get(targ_angle.casefold(), None)
    if normal is None:
        return False  # If it's not a special angle,
        # so it failed the exact match

    inst_normal = from_dir.rotate_by_str(angle)

    if normal == 'WALL':
        # Special case - it's not on the floor or ceiling
        return not (inst_normal == (0, 0, 1) or inst_normal == (0, 0, -1))
    else:
        return inst_normal == normal or (
            allow_inverse and -inst_normal == normal
        )
コード例 #17
0
ファイル: vactubes.py プロジェクト: GLiTcH2/BEE2.4
def make_corner(origin, angle, size, config):
    vbsp.VMF.create_ent(
        classname='func_instance',
        origin=origin,
        angles=angle,
        file=config['corner', size],
    )

    temp = config['corner_temp', size]
    if temp:
        temp_solids = import_template(
            temp,
            origin=origin,
            angles=Vec.from_str(angle),
            force_type=TEMP_TYPES.world,
        ).world
        for solid in temp_solids:
            vbsp.VMF.remove_brush(solid)
        motion_trigger(*temp_solids)
コード例 #18
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])
    )
コード例 #19
0
ファイル: vactubes.py プロジェクト: GLiTcH2/BEE2.4
def make_vac_track(start, all_markers):
    """Create a vactube path section.

    """

    start_normal = Vec(-1, 0, 0).rotate_by_str(start['ent']['angles'])

    # First create the start section..
    start_logic = start['ent'].copy()
    vbsp.VMF.add_ent(start_logic)

    start_logic['file'] = start['conf']['entry', (
        'ceiling' if (start_normal.z > 0) else
        'floor' if (start_normal.z < 0) else
        'wall'
    )]

    end = start

    for inst, end in follow_vac_path(all_markers, start):
        join_markers(inst, end, inst is start)

    end_loc = Vec.from_str(end['ent']['origin'])
    end_norm = Vec(-1, 0, 0).rotate_by_str(end['ent']['angles'])

    # join_markers creates straight parts up-to the marker, but not at it's
    # location - create the last one.
    make_straight(
        end_loc,
        end_norm,
        128,
        end['conf'],
    )

    # If the end is placed in goo, don't add logic - it isn't visible, and
    # the object is on a one-way trip anyway.
    if end_loc.as_tuple() not in GOO_LOCS:
        end_logic = end['ent'].copy()
        vbsp.VMF.add_ent(end_logic)
        end_logic['file'] = end['conf']['exit']
コード例 #20
0
ファイル: conditions.py プロジェクト: xDadiKx/BEE2.4
def res_track_plat(_, res):
    """Logic specific to Track Platforms.

    This allows switching the instances used depending on if the track
    is horizontal or vertical and sets the track
    targetnames to a useful value.
    """
    # Get the instances from editoritems
    (inst_bot_grate, inst_bottom, inst_middle, inst_top, inst_plat, inst_plat_oscil, inst_single) = resolve_inst(
        res["orig_item"]
    )
    single_plat_inst = res["single_plat", ""]
    track_targets = res["track_name", ""]

    track_files = [inst_bottom, inst_middle, inst_top, inst_single]
    platforms = [inst_plat, inst_plat_oscil]

    # All the track_set in the map, indexed by origin
    track_instances = {
        Vec.from_str(inst["origin"]).as_tuple(): inst
        for inst in VMF.by_class["func_instance"]
        if inst["file"].casefold() in track_files
    }
    utils.con_log("Track instances:")
    utils.con_log("\n".join("{!s}: {}".format(k, v["file"]) for k, v in track_instances.items()))

    # Now we loop through all platforms in the map, and then locate their
    # track_set
    for plat_inst in VMF.by_class["func_instance"]:
        if plat_inst["file"].casefold() not in platforms:
            continue  # Not a platform!

        utils.con_log('Modifying "' + plat_inst["targetname"] + '"!')

        plat_loc = Vec.from_str(plat_inst["origin"])
        angles = Vec.from_str(plat_inst["angles"])
        # The direction away from the wall/floor/ceil
        normal = Vec(0, 0, 1).rotate(angles.x, angles.y, angles.z)

        for tr_origin, first_track in track_instances.items():
            if plat_loc == tr_origin:
                # Check direction

                if normal == Vec(0, 0, 1).rotate(*Vec.from_str(first_track["angles"])):
                    break
        else:
            raise Exception('Platform "{}" has no track!'.format(plat_inst["targetname"]))

        track_type = first_track["file"].casefold()
        if track_type == inst_single:
            # Track is one block long, use a single-only instance and
            # remove track!
            plat_inst["file"] = single_plat_inst
            first_track.remove()
            continue  # Next platform

        track_set = set()
        if track_type == inst_top or track_type == inst_middle:
            # search left
            track_scan(track_set, track_instances, first_track, middle_file=inst_middle, x_dir=-1)
        if track_type == inst_bottom or track_type == inst_middle:
            # search right
            track_scan(track_set, track_instances, first_track, middle_file=inst_middle, x_dir=+1)

        # Give every track a targetname matching the platform
        for ind, track in enumerate(track_set, start=1):
            if track_targets == "":
                track["targetname"] = plat_inst["targetname"]
            else:
                track["targetname"] = plat_inst["targetname"] + "-" + track_targets + str(ind)

        # Now figure out which way the track faces:

        # The direction horizontal track is offset
        side_dir = Vec(0, 1, 0).rotate(*Vec.from_str(first_track["angles"]))

        # The direction of the platform surface
        facing = Vec(-1, 0, 0).rotate(angles.x, angles.y, angles.z)
        if side_dir == facing:
            track_facing = "HORIZ"
        elif side_dir == -facing:
            track_facing = "HORIZ_MIRR"
        else:
            track_facing = "VERT"
        # Now add the suffixes
        if track_facing == "VERT":
            if utils.conv_bool(res["vert_suffix", ""]):
                for inst in track_set:
                    add_suffix(inst, "_vert")
                if utils.conv_bool(res["plat_suffix", ""]):
                    add_suffix(plat_inst, "_vert")
        elif track_facing == "HORIZ_MIRR":
            if utils.conv_bool(res["horiz_suffix", ""]):
                for inst in track_set:
                    add_suffix(inst, "_horiz_mirrored")
                if utils.conv_bool(res["plat_suffix", ""]):
                    add_suffix(plat_inst, "_horiz")
        else:  # == 'HORIZ'
            if utils.conv_bool(res["horiz_suffix", ""]):
                for inst in track_set:
                    add_suffix(inst, "_horiz")
                if utils.conv_bool(res["plat_suffix", ""]):
                    add_suffix(plat_inst, "_horiz")
    return True  # Only run once!
コード例 #21
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
コード例 #22
0
ファイル: brushes.py プロジェクト: goodDOS/BEE2.4
def res_fix_rotation_axis(ent, res):
    """Generate a `func_rotating`, `func_door_rotating` or any similar entity.

    This uses the orientation of the instance to detemine the correct
    spawnflags to make it rotate in the correct direction. The brush
    will be 2x2x2 units large, and always set to be non-solid.
    - `Pos` and `name` are local to the
      instance, and will set the `origin` and `targetname` respectively.
    - `Keys` are any other keyvalues to be be set.
    - `Flags` sets additional spawnflags. Multiple values may be
       separated by '+', and will be added together.
    - `Classname` specifies which entity will be created, as well as
       which other values will be set to specify the correct orientation.
    - `AddOut` is used to add outputs to the generated entity. It takes
       the options `Output`, `Target`, `Input`, `Param` and `Delay`. If
       `Inst_targ` is defined, it will be used with the input to construct
       an instance proxy input. If `OnceOnly` is set, the output will be
       deleted when fired.

    Permitted entities:
     * `func_rotating`
     * `func_door_rotating`
     * `func_rot_button`
     * `func_platrot`
    """
    des_axis = res['axis', 'z'].casefold()
    reverse = utils.conv_bool(res['reversed', '0'])
    door_type = res['classname', 'func_door_rotating']

    # Extra stuff to apply to the flags (USE, toggle, etc)
    flags = sum(map(
        # Add together multiple values
        utils.conv_int,
        res['flags', '0'].split('+')
    ))

    name = res['name', '']
    if not name.startswith('@'):
        # If a local name is given, add it to the instance targetname.
        # It the name given is '', set to the instance's name.
        # If it has an @, don't change it!
        name = ent['targetname', ''] + (('-' + name) if name else '')

    axis = Vec(
        x=int(des_axis == 'x'),
        y=int(des_axis == 'y'),
        z=int(des_axis == 'z'),
    ).rotate_by_str(ent['angles', '0 0 0'])

    pos = Vec.from_str(
        res['Pos', '0 0 0']
    ).rotate_by_str(ent['angles', '0 0 0'])
    pos += Vec.from_str(ent['origin', '0 0 0'])

    door_ent = vbsp.VMF.create_ent(
        classname=door_type,
        targetname=name,
        origin=pos.join(' '),
    )

    conditions.set_ent_keys(door_ent, ent, res)

    for output in res.find_all('AddOut'):
        door_ent.add_out(VLib.Output(
            out=output['Output', 'OnUse'],
            inp=output['Input', 'Use'],
            targ=output['Target', ''],
            inst_in=output['Inst_targ', None],
            param=output['Param', ''],
            delay=utils.conv_float(output['Delay', '']),
            times=(
                1 if
                utils.conv_bool(output['OnceOnly', False])
                else -1),
        ))

    # Generate brush
    door_ent.solids = [vbsp.VMF.make_prism(pos - 1, pos + 1).solid]

    if axis.x > 0 or axis.y > 0 or axis.z > 0:
        # If it points forward, we need to reverse the rotating door
        reverse = not reverse

    flag_values = FLAG_ROTATING[door_type]
    # Make the door always non-solid!
    flags |= flag_values.get('solid_flags', 0)
    # Add or remove flags as needed.
    # flags |= bit sets it to 1.
    # flags |= ~bit sets it to 0.
    if axis.x != 0:
        flags |= flag_values.get('x', 0)
    else:
        flags &= ~flag_values.get('x', 0)

    if axis.y != 0:
        flags |= flag_values.get('y', 0)
    else:
        flags &= ~flag_values.get('y', 0)

    if axis.z != 0:
        flags |= flag_values.get('z', 0)
    else:
        flags &= ~flag_values.get('z', 0)

    if door_type == 'momentary_rot_button':
        door_ent['startdirection'] = '1' if reverse else '-1'
    else:
        if reverse:
            flags |= flag_values.get('rev', 0)
        else:
            flags &= ~flag_values.get('rev', 0)
    door_ent['spawnflags'] = str(flags)
コード例 #23
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,
        })
コード例 #24
0
ファイル: trackPlat.py プロジェクト: Coolasp1e/BEE2.4
def res_track_plat(_, res):
    """Logic specific to Track Platforms.

    This allows switching the instances used depending on if the track
    is horizontal or vertical and sets the track
    targetnames to a useful value.
    Values:
        - Orig_item: The "<ITEM_ID>" for the track platform, with angle brackets
        - Single_plat: An instance used for platform with 1 rail
        - Track_name: The name to give to the tracks.
        - Vert_suffix: Add suffixes to vertical tracks
            (_vert)
        - Horiz_suffix: Add suffixes to horizontal tracks
            (_horiz, _horiz_mirrored)
        - plat_suffix: Also add the above _vert or _horiz suffixes to
            the platform.
        - plat_var: If set, save the orientation to the given $fixup variable
    """
    # Get the instances from editoritems
    (
        inst_bot_grate, inst_bottom, inst_middle,
        inst_top, inst_plat, inst_plat_oscil, inst_single
    ) = resolve_inst(res['orig_item'])
    single_plat_inst = res['single_plat', '']
    track_targets = res['track_name', '']

    track_files = [inst_bottom, inst_middle, inst_top, inst_single]
    platforms = [inst_plat, inst_plat_oscil]

    # All the track_set in the map, indexed by origin
    track_instances = {
        Vec.from_str(inst['origin']).as_tuple(): inst
        for inst in
        vbsp.VMF.by_class['func_instance']
        if inst['file'].casefold() in track_files
    }

    LOGGER.debug('Track instances:')
    LOGGER.debug('\n'.join(
        '{!s}: {}'.format(k, v['file'])
        for k, v in
        track_instances.items()
    ))

    if not track_instances:
        return RES_EXHAUSTED

    # Now we loop through all platforms in the map, and then locate their
    # track_set
    for plat_inst in vbsp.VMF.by_class['func_instance']:
        if plat_inst['file'].casefold() not in platforms:
            continue  # Not a platform!

        LOGGER.debug('Modifying "' + plat_inst['targetname'] + '"!')

        plat_loc = Vec.from_str(plat_inst['origin'])
        # The direction away from the wall/floor/ceil
        normal = Vec(0, 0, 1).rotate_by_str(
            plat_inst['angles']
        )

        for tr_origin, first_track in track_instances.items():
            if plat_loc == tr_origin:
                # Check direction

                if normal == Vec(0, 0, 1).rotate(
                        *Vec.from_str(first_track['angles'])
                        ):
                    break
        else:
            raise Exception('Platform "{}" has no track!'.format(
                plat_inst['targetname']
            ))

        track_type = first_track['file'].casefold()
        if track_type == inst_single:
            # Track is one block long, use a single-only instance and
            # remove track!
            plat_inst['file'] = single_plat_inst
            first_track.remove()
            continue  # Next platform

        track_set = set()
        if track_type == inst_top or track_type == inst_middle:
            # search left
            track_scan(
                track_set,
                track_instances,
                first_track,
                middle_file=inst_middle,
                x_dir=-1,
            )
        if track_type == inst_bottom or track_type == inst_middle:
            # search right
            track_scan(
                track_set,
                track_instances,
                first_track,
                middle_file=inst_middle,
                x_dir=+1,
            )

        # Give every track a targetname matching the platform
        for ind, track in enumerate(track_set, start=1):
            if track_targets == '':
                track['targetname'] = plat_inst['targetname']
            else:
                track['targetname'] = (
                    plat_inst['targetname'] +
                    '-' +
                    track_targets + str(ind)
                )

        # Now figure out which way the track faces:

        # The direction horizontal track is offset
        side_dir = Vec(0, 1, 0).rotate_by_str(first_track['angles'])

        # The direction of the platform surface
        facing = Vec(-1, 0, 0).rotate_by_str(plat_inst['angles'])
        if side_dir == facing:
            track_facing = 'HORIZ'
        elif side_dir == -facing:
            track_facing = 'HORIZ_MIRR'
        else:
            track_facing = 'VERT'
        # Now add the suffixes
        if track_facing == 'VERT':
            if utils.conv_bool(res['vert_suffix', '']):
                for inst in track_set:
                    conditions.add_suffix(inst, '_vert')
                if utils.conv_bool(res['plat_suffix', '']):
                    conditions.add_suffix(plat_inst, '_vert')
        elif track_facing == 'HORIZ_MIRR':
            if utils.conv_bool(res['horiz_suffix', '']):
                for inst in track_set:
                    conditions.add_suffix(inst, '_horiz_mirrored')
                if utils.conv_bool(res['plat_suffix', '']):
                    conditions.add_suffix(plat_inst, '_horiz')
        else:  # == 'HORIZ'
            if utils.conv_bool(res['horiz_suffix', '']):
                for inst in track_set:
                    conditions.add_suffix(inst, '_horiz')
                if utils.conv_bool(res['plat_suffix', '']):
                    conditions.add_suffix(plat_inst, '_horiz')

        plat_var = res['plat_var', '']
        if plat_var != '':
            # Skip the '_mirrored' section if needed
            plat_inst.fixup[plat_var] = track_facing[:5].lower()

    return RES_EXHAUSTED # Don't re-run
コード例 #25
0
ファイル: brushes.py プロジェクト: goodDOS/BEE2.4
def res_add_brush(inst, res):
    """Spawn in a brush at the indicated points.

    - point1 and point2 are locations local to the instance, with '0 0 0'
      as the floor-position.
    - type is either 'black' or 'white'.
    - detail should be set to True/False. If true the brush will be a
      func_detail instead of a world brush.

    The sides will be textured with 1x1, 2x2 or 4x4 wall, ceiling and floor
    textures as needed.
    """
    import vbsp

    point1 = Vec.from_str(res['point1'])
    point2 = Vec.from_str(res['point2'])

    point1.z -= 64  # Offset to the location of the floor
    point2.z -= 64

    # Rotate to match the instance
    point1.rotate_by_str(inst['angles'])
    point2.rotate_by_str(inst['angles'])

    origin = Vec.from_str(inst['origin'])
    point1 += origin  # Then offset to the location of the instance
    point2 += origin

    tex_type = res['type', None]
    if tex_type not in ('white', 'black'):
        LOGGER.warning(
            'AddBrush: "{}" is not a valid brush '
            'color! (white or black)',
            tex_type,
        )
        tex_type = 'black'

    dim = point2 - point1
    dim.max(-dim)

    # Figure out what grid size and scale is needed
    # Check the dimensions in two axes to figure out the largest
    # tile size that can fit in it.
    x_maxsize = min(dim.y, dim.z)
    y_maxsize = min(dim.x, dim.z)
    if x_maxsize <= 32:
        x_grid = '4x4'
    elif x_maxsize <= 64:
        x_grid = '2x2'
    else:
        x_grid = 'wall'

    if y_maxsize <= 32:
        y_grid = '4x4'
    elif y_maxsize <= 64:
        y_grid = '2x2'
    else:
        y_grid = 'wall'

    grid_offset = origin // 128  # type: Vec

    # All brushes in each grid have the same textures for each side.
    random.seed(grid_offset.join(' ') + '-partial_block')

    solids = vbsp.VMF.make_prism(point1, point2)
    ':type solids: VLib.PrismFace'

    # Ensure the faces aren't re-textured later
    vbsp.IGNORED_FACES.update(solids.solid.sides)

    solids.north.mat = vbsp.get_tex(tex_type + '.' + y_grid)
    solids.south.mat = vbsp.get_tex(tex_type + '.' + y_grid)
    solids.east.mat = vbsp.get_tex(tex_type + '.' + x_grid)
    solids.west.mat = vbsp.get_tex(tex_type + '.' + x_grid)
    solids.top.mat = vbsp.get_tex(tex_type + '.floor')
    solids.bottom.mat = vbsp.get_tex(tex_type + '.ceiling')

    if utils.conv_bool(res['detail', False], False):
        # Add the brush to a func_detail entity
        vbsp.VMF.create_ent(
            classname='func_detail'
        ).solids = [
            solids.solid
        ]
    else:
        # Add to the world
        vbsp.VMF.add_brush(solids.solid)
コード例 #26
0
ファイル: brushes.py プロジェクト: goodDOS/BEE2.4
def res_set_texture(inst, res):
    """Set the brush face at a location to a particular texture.

    pos is the position, relative to the instance
      (0 0 0 is the floor-surface).
    dir is the normal of the texture.
    If gridPos is true, the position will be snapped so it aligns with
     the 128 brushes (Useful with fizzler/light strip items).

    tex is the texture used.
    If tex begins and ends with '<>', certain
    textures will be used based on style:
    - If tex is '<special>', the brush will be given a special texture
      like angled and clear panels.
    - '<white>' and '<black>' will use the regular textures for the
      given color.
    - '<white-2x2>', '<white-4x4>', '<black-2x2>', '<black-4x4'> will use
      the given wall-sizes. If on floors or ceilings these always use 4x4.
    - '<2x2>' or '<4x4>' will force to the given wall-size, keeping color.
    - '<special-white>' and '<special-black>' will use a special texture
       of the given color.
    If tex begins and ends with '[]', it is an option in the 'Textures' list.
    These are composed of a group and texture, separated by '.'. 'white.wall'
    are the white wall textures; 'special.goo' is the goo texture.
    """
    import vbsp
    pos = Vec.from_str(res['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 = Vec.from_str(res['dir', '0 0 -1']).rotate_by_str(
        inst['angles', '0 0 0']
    )

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

    brush = SOLIDS.get(pos.as_tuple(), None)
    ':type brush: solidGroup'

    if not brush or brush.normal != norm:
        return

    tex = res['tex']

    if tex.startswith('[') and tex.endswith(']'):
        brush.face.mat = vbsp.get_tex(tex[1:-1])
        brush.face.mat = tex
    elif tex.startswith('<') and tex.endswith('>'):
        # Special texture names!
        tex = tex[1:-1].casefold()
        if tex == 'white':
            brush.face.mat = 'tile/white_wall_tile003a'
        elif tex == 'black':
            brush.face.mat = 'metal/black_wall_metal_002c'

        if tex == 'black' or tex == 'white':
            # For these two, run the regular logic to apply textures
            # correctly.
            vbsp.alter_mat(
                brush.face,
                vbsp.face_seed(brush.face),
                vbsp.get_bool_opt('tile_texture_lock', True),
            )

        if tex == 'special':
            vbsp.set_special_mat(brush.face, str(brush.color))
        elif tex == 'special-white':
            vbsp.set_special_mat(brush.face, 'white')
            return
        elif tex == 'special-black':
            vbsp.set_special_mat(brush.face, 'black')

        # Do <4x4>, <white-2x4>, etc
        color = str(brush.color)
        if tex.startswith('black') or tex.endswith('white'):
            # Override the color used for 2x2/4x4 brushes
            color = tex[:5]
        if tex.endswith('2x2') or tex.endswith('4x4'):
            # 4x4 and 2x2 instructions are ignored on floors and ceilings.
            orient = vbsp.get_face_orient(brush.face)
            if orient == vbsp.ORIENT.wall:
                brush.face.mat = vbsp.get_tex(
                    color + '.' + tex[-3:]
                )
            else:
                brush.face.mat = vbsp.get_tex(
                    color + '.' + str(orient)
                )
    else:
        brush.face.mat = tex

    # Don't allow this to get overwritten later.
    vbsp.IGNORED_FACES.add(brush.face)
コード例 #27
0
ファイル: scaffold.py プロジェクト: goodDOS/BEE2.4
def res_unst_scaffold(_, res):
    """The condition to generate Unstationary Scaffolds.

    This is executed once to modify all instances.
    """
    # The instance types we're modifying
    if res.value not in SCAFFOLD_CONFIGS:
        # We've already executed this config group
        return RES_EXHAUSTED

    LOGGER.info("Running Scaffold Generator ({})...", res.value)
    TARG_INST, LINKS = SCAFFOLD_CONFIGS[res.value]
    del SCAFFOLD_CONFIGS[res.value]  # Don't let this run twice

    instances = {}
    # Find all the instances we're wanting to change, and map them to
    # targetnames
    for ent in vbsp.VMF.by_class["func_instance"]:
        file = ent["file"].casefold()
        targ = ent["targetname"]
        if file not in TARG_INST:
            continue
        config = TARG_INST[file]
        next_inst = set(out.target for out in ent.outputs)
        # Destroy these outputs, they're useless now!
        ent.outputs.clear()
        instances[targ] = {"ent": ent, "conf": config, "next": next_inst, "prev": None}

    # Now link each instance to its in and outputs
    for targ, inst in instances.items():
        scaff_targs = 0
        for ent_targ in inst["next"]:
            if ent_targ in instances:
                instances[ent_targ]["prev"] = targ
                inst["next"] = ent_targ
                scaff_targs += 1
            else:
                # If it's not a scaffold, it's probably an indicator_toggle.
                # We want to remove any them as well as the assoicated
                # antlines!
                for toggle in vbsp.VMF.by_target[ent_targ]:
                    conditions.remove_ant_toggle(toggle)
        if scaff_targs > 1:
            raise Exception("A scaffold item has multiple destinations!")
        elif scaff_targs == 0:
            inst["next"] = None  # End instance

    starting_inst = []
    # We need to find the start instances, so we can set everything up
    for inst in instances.values():
        if inst["prev"] is None and inst["next"] is None:
            # Static item!
            continue
        elif inst["prev"] is None:
            starting_inst.append(inst)

    # We need to make the link entities unique for each scaffold set,
    # otherwise the AllVar property won't work.
    group_counter = 0

    # Set all the instances and properties
    for start_inst in starting_inst:
        group_counter += 1
        ent = start_inst["ent"]
        for vals in LINKS.values():
            if vals["all"] is not None:
                ent.fixup[vals["all"]] = SCAFF_PATTERN.format(name=vals["name"], group=group_counter, index="*")

        should_reverse = utils.conv_bool(ent.fixup["$start_reversed"])

        # Now set each instance in the chain, including first and last
        for index, inst in enumerate(scaff_scan(instances, start_inst)):
            ent, conf = inst["ent"], inst["conf"]
            orient = "floor" if Vec(0, 0, 1).rotate_by_str(ent["angles"]) == (0, 0, 1) else "wall"

            # Find the offset used for the logic ents
            offset = (conf["off_" + orient]).copy()
            if conf["is_piston"]:
                # Adjust based on the piston position
                offset.z += 128 * utils.conv_int(
                    ent.fixup["$top_level" if ent.fixup["$start_up"] == "1" else "$bottom_level"]
                )
            offset.rotate_by_str(ent["angles"])
            offset += Vec.from_str(ent["origin"])

            if inst["prev"] is None:
                link_type = "start"
            elif inst["next"] is None:
                link_type = "end"
            else:
                link_type = "mid"

            if orient == "floor" and link_type != "mid" and conf["inst_end"] is not None:
                # Add an extra instance pointing in the direction
                # of the connected track. This would be the endcap
                # model.
                other_ent = instances[inst["next" if link_type == "start" else "prev"]]["ent"]

                other_pos = Vec.from_str(other_ent["origin"])
                our_pos = Vec.from_str(ent["origin"])
                link_dir = other_pos - our_pos
                link_ang = math.degrees(math.atan2(link_dir.y, link_dir.x))
                # Round to nearest 90 degrees
                # Add 45 so the switchover point is at the diagonals
                link_ang = (link_ang + 45) // 90 * 90
                vbsp.VMF.create_ent(
                    classname="func_instance",
                    targetname=ent["targetname"],
                    file=conf["inst_end"],
                    origin=offset.join(" "),
                    angles="0 {:.0f} 0".format(link_ang),
                )
                # Don't place the offset instance, this replaces that!
            elif conf["inst_offset"] is not None:
                # Add an additional rotated entity at the offset.
                # This is useful for the piston item.
                vbsp.VMF.create_ent(
                    classname="func_instance",
                    targetname=ent["targetname"],
                    file=conf["inst_offset"],
                    origin=offset.join(" "),
                    angles=ent["angles"],
                )

            logic_inst = vbsp.VMF.create_ent(
                classname="func_instance",
                targetname=ent["targetname"],
                file=conf.get("logic_" + link_type + ("_rev" if should_reverse else ""), ""),
                origin=offset.join(" "),
                angles=("0 0 0" if conf["rotate_logic"] else ent["angles"]),
            )
            for key, val in ent.fixup.items():
                # Copy over fixup values
                logic_inst.fixup[key] = val

            # Add the link-values
            for linkVar, link in LINKS.items():
                logic_inst.fixup[linkVar] = SCAFF_PATTERN.format(name=link["name"], group=group_counter, index=index)
                if inst["next"] is not None:
                    logic_inst.fixup[link["next"]] = SCAFF_PATTERN.format(
                        name=link["name"], group=group_counter, index=index + 1
                    )

            new_file = conf.get("inst_" + orient, "")
            if new_file != "":
                ent["file"] = new_file

    LOGGER.info("Finished Scaffold generation!")
    return RES_EXHAUSTED
コード例 #28
0
ファイル: voiceLine.py プロジェクト: GLiTcH2/BEE2.4
def add_voice(
        has_items: dict,
        style_vars_: dict,
        vmf_file: vmfLib.VMF,
        map_seed: str,
        use_priority=True,
):
    """Add a voice line to the map."""
    global ALLOW_MID_VOICES, VMF, map_attr, style_vars
    LOGGER.info('Adding Voice Lines!')

    VMF = vmf_file
    map_attr = has_items
    style_vars = style_vars_

    norm_config = ConfigFile('voice.cfg', root='bee2')
    mid_config = ConfigFile('mid_voice.cfg', root='bee2')

    quote_base = QUOTE_DATA['base', False]
    quote_loc = Vec.from_str(QUOTE_DATA['quote_loc', '-10000 0 0'], x=-10000)
    if quote_base:
        LOGGER.info('Adding Base instance!')
        VMF.create_ent(
            classname='func_instance',
            targetname='voice',
            file=INST_PREFIX + quote_base,
            angles='0 0 0',
            origin=quote_loc,
            fixup_style='0',
        )

    # Always add a box around the lines - it may be needed for quoteEvents.
    VMF.add_brushes(VMF.make_hollow(
        quote_loc - 64,
        quote_loc + 64,
        thick=32,
    ))

    ALLOW_MID_VOICES = not style_vars.get('nomidvoices', False)

    mid_quotes = []

    # Enable using the beep before and after choreo lines.
    allow_dings = utils.conv_bool(QUOTE_DATA['use_dings', '0'])
    if allow_dings:
        VMF.create_ent(
            classname='logic_choreographed_scene',
            targetname='@ding_on',
            origin=quote_loc + (-8, -16, 0),
            scenefile='scenes/npc/glados_manual/ding_on.vcd',
            busyactor="1",  # Wait for actor to stop talking
            onplayerdeath='0',
        )
        VMF.create_ent(
            classname='logic_choreographed_scene',
            targetname='@ding_off',
            origin=quote_loc + (8, -16, 0),
            scenefile='scenes/npc/glados_manual/ding_off.vcd',
            busyactor="1",  # Wait for actor to stop talking
            onplayerdeath='0',
        )

    # QuoteEvents allows specifiying an instance for particular items,
    # so a voice line can be played at a certain time. It's only active
    # in certain styles, but uses the default if not set.
    for event in QUOTE_DATA.find_all('QuoteEvents', 'Event'):
        event_id = event['id', ''].casefold()
        # We ignore the config if no result was executed.
        if event_id and event_id in QUOTE_EVENTS:
            # Instances from the voiceline config are in this subfolder,
            # but not the default item - that's set from the conditions
            QUOTE_EVENTS[event_id] = INST_PREFIX + event['file']

    LOGGER.info('Quote events: {}', list(QUOTE_EVENTS.keys()))

    if has_responses():
        LOGGER.info('Generating responses data..')
        with open(RESP_LOC, 'w') as f:
            generate_resp_script(f, allow_dings)
    else:
        LOGGER.info('No responses data..')
        try:
            os.remove(RESP_LOC)
        except FileNotFoundError:
            pass

    for ind, file in enumerate(QUOTE_EVENTS.values()):
        VMF.create_ent(
            classname='func_instance',
            targetname='voice_event_' + str(ind),
            file=file,
            angles='0 0 0',
            origin=quote_loc,
            fixup_style='0',
        )

    # For each group, locate the voice lines.
    for group in itertools.chain(
            QUOTE_DATA.find_all('group'),
            QUOTE_DATA.find_all('midchamber'),
            ):

        quote_targetname = group['Choreo_Name', '@choreo']
        use_dings = utils.conv_bool(group['use_dings', ''], allow_dings)

        possible_quotes = sorted(
            find_group_quotes(
                group,
                mid_quotes,
                use_dings,
                conf=mid_config if group.name == 'midchamber' else norm_config,
                mid_name=quote_targetname,
            ),
            key=sort_func,
            reverse=True,
        )

        if possible_quotes:

            choreo_loc = Vec.from_str(group['choreo_loc', quote_loc])

            if use_priority:
                chosen = possible_quotes[0].lines
            else:
                # Chose one of the quote blocks..
                random.seed('{}-VOICE_QUOTE_{}'.format(
                    map_seed,
                    len(possible_quotes),
                ))
                chosen = random.choice(possible_quotes).lines

            # Join the IDs for
            # the voice lines to the map seed,
            # so each quote block will chose different lines.
            random.seed(map_seed + '-VOICE_LINE_' + '|'.join(
                prop['id', 'ID']
                for prop in
                chosen
            ))

            # Add one of the associated quotes
            add_quote(
                random.choice(chosen),
                quote_targetname,
                choreo_loc,
                use_dings,
            )

    if ADDED_BULLSEYES or utils.conv_bool(QUOTE_DATA['UseMicrophones', '']):
        # Add microphones that broadcast audio directly at players.
        # This ensures it is heard regardless of location.
        # This is used for Cave and core Wheatley.
        if vbsp.GAME_MODE == 'SP':
            VMF.create_ent(
                classname='env_microphone',
                targetname='player_speaker_sp',
                speakername='!player',
                maxRange='96',
                origin=quote_loc,
            )
        else:
            VMF.create_ent(
                classname='env_microphone',
                targetname='player_speaker_blue',
                speakername='!player_blue',
                maxRange='96',
                origin=quote_loc,
            )
            VMF.create_ent(
                classname='env_microphone',
                targetname='player_speaker_orange',
                speakername='!player_orange',
                maxRange='96',
                origin=quote_loc,
            )

    LOGGER.info('{} Mid quotes', len(mid_quotes))
    for mid_item, use_ding, mid_name in mid_quotes:
        # Add all the mid quotes
        add_quote(mid_item, mid_name, quote_loc, use_ding)

    LOGGER.info('Done!')
コード例 #29
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(' '),
            )
コード例 #30
0
ファイル: custItems.py プロジェクト: Coolasp1e/BEE2.4
def res_faith_mods(inst: VLib.Entity, res: Property):
    """Modify the trigger_catrapult that is created for ItemFaithPlate items.

    Values:
        - raise_trig: Raise or lower the trigger_catapults by this amount.
        - angled_targ, angled_in: Instance entity and input for angled plates
        - straight_targ, straight_in: Instance entity and input for
            straight plates
        - instvar: A $replace value to set to either 'angled' or '
            'straight'.
        - enabledVar: A $replace value which will be copied to the main
            trigger's Start Disabled value (and inverted).
        - trig_temp: An ID for a template brush to add. This will be offset by
            the trigger's position (in the case of the 'helper' trigger).
    """
    # Get data about the trigger this instance uses for flinging
    fixup_var = res['instvar', '']
    trig_enabled = res['enabledVar', None]
    trig_temp = res['trig_temp', '']
    offset = utils.conv_int(res['raise_trig', '0'])
    if offset:
        offset = Vec(0, 0, offset).rotate_by_str(inst['angles', '0 0 0'])
    else:
        offset = Vec()

    if trig_enabled is not None:
        trig_enabled = utils.conv_bool(inst.fixup[trig_enabled])
    else:
        trig_enabled = None

    for trig in vbsp.VMF.by_class['trigger_catapult']:
        if inst['targetname'] not in trig['targetname']:
            continue

        # Edit both the normal and the helper trigger..
        trig_origin = trig['origin'] = Vec.from_str(trig['origin']) + offset

        if offset and not trig_temp:
            # No template, shift the current brushes.
            for solid in trig.solids:
                solid.translate(offset)
        elif trig_temp:
            trig.solids = conditions.import_template(
                temp_name=trig_temp,
                origin=trig_origin,
                angles=Vec.from_str(inst['angles']),
                force_type=conditions.TEMP_TYPES.world,
            ).world
            # Remove the trigger solids from worldspawn..
            for solid in trig.solids:
                vbsp.VMF.remove_brush(solid)

        if trig_enabled is not None and 'helper' not in trig['targetname']:
            trig['startdisabled'] = utils.bool_as_int(not trig_enabled)

        # Inspect the outputs to determine the type.
        # We also change them if desired, since that's not possible
        # otherwise.

        for out in trig.outputs:
            if out.inst_in == 'animate_angled_relay':
                # Instead of an instance: output, use local names.
                # This allows us to strip the proxy, as well as use
                # overlay instances.
                out.inst_in = None
                out.target = conditions.local_name(
                    inst,
                    res['angled_targ', 'animate_angled_relay']
                )
                out.input = res['angled_in', 'Trigger']
                if fixup_var:
                    inst.fixup[fixup_var] = 'angled'
                break  # There's only one output we want to look for...

            elif out.inst_in == 'animate_straightup_relay':
                out.inst_in = None
                out.target = conditions.local_name(
                    inst,
                    res[
                        'straight_targ',
                        'animate_straightup_relay'
                    ],
                )
                out.input = res['straight_in', 'Trigger']

                if fixup_var:
                    inst.fixup[fixup_var] = 'straight'
                break