diff --git a/SecretAPI/Enums/RoomSafetyFailReason.cs b/SecretAPI/Enums/RoomSafetyFailReason.cs new file mode 100644 index 0000000..a5cc293 --- /dev/null +++ b/SecretAPI/Enums/RoomSafetyFailReason.cs @@ -0,0 +1,44 @@ +namespace SecretAPI.Enums +{ + using System; + using MapGeneration; + using SecretAPI.Extensions; + using UnityEngine; + + /// + /// Reasons why should fail. + /// + [Flags] + public enum RoomSafetyFailReason + { + /// + /// No fail. + /// + None = 0, + + /// + /// Room safety check will fail if warhead has gone off and room is not part of . + /// + Warhead = 1, + + /// + /// Room safety check will fail if decontamination has gone off and room is part of . + /// + Decontamination = 2, + + /// + /// Room safety check will fail if room has . + /// + Tesla = 4, + + /// + /// Room safety check will fail if the listed room fails a check. + /// + MissingFloor = 8, + + /// + /// Room safety check will fail if the listed room is part of . + /// + KnownBad = 16, + } +} \ No newline at end of file diff --git a/SecretAPI/Extensions/RoomExtensions.cs b/SecretAPI/Extensions/RoomExtensions.cs index 41629af..ab87169 100644 --- a/SecretAPI/Extensions/RoomExtensions.cs +++ b/SecretAPI/Extensions/RoomExtensions.cs @@ -1,8 +1,12 @@ namespace SecretAPI.Extensions { using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; using LabApi.Features.Wrappers; using MapGeneration; + using PlayerRoles.FirstPersonControl; + using PlayerRoles.PlayableScps.Scp106; + using SecretAPI.Enums; using UnityEngine; /// @@ -10,9 +14,13 @@ /// public static class RoomExtensions { - private static readonly List KnownUnsafeRooms = + private const float RaycastDistance = 2; + + /// + /// Gets a list of that will be denied by . + /// + public static List KnownUnsafeRooms { get; } = [ - RoomName.HczTesla, // Instant death RoomName.EzEvacShelter, // Stuck permanently RoomName.EzCollapsedTunnel, // Stuck permanently RoomName.HczWaysideIncinerator, // Death @@ -23,19 +31,67 @@ public static class RoomExtensions /// Gets whether a room is safe to teleport to. Will consider decontamination, warhead, teslas and void rooms. /// /// The room to check. + /// Reasons why the safety check should fail. /// Whether the room is safe to teleport to. - public static bool IsSafeToTeleport(this Room room) + public static bool IsSafeToTeleport(this Room room, RoomSafetyFailReason failReasons) { - if (Warhead.IsDetonated && room.Zone != FacilityZone.Surface) + if (failReasons.HasFlag(RoomSafetyFailReason.Warhead) && Warhead.IsDetonated && room.Zone != FacilityZone.Surface) return false; - if (Decontamination.IsDecontaminating && room.Zone == FacilityZone.LightContainment) + if (failReasons.HasFlag(RoomSafetyFailReason.Decontamination) && Decontamination.IsDecontaminating && room.Zone == FacilityZone.LightContainment) + return false; + + if (failReasons.HasFlag(RoomSafetyFailReason.Tesla) && room.Name == RoomName.HczTesla) return false; if (KnownUnsafeRooms.Contains(room.Name)) return false; - return Physics.Raycast(room.Position, Vector3.down, out _, 2); + if (failReasons.HasFlag(RoomSafetyFailReason.MissingFloor) && !Physics.Raycast(room.Position, Vector3.down, out _, RaycastDistance, FpcStateProcessor.Mask)) + return false; + + return true; + } + + /// + /// Tries to get a location to teleport a to. + /// + /// The player to attempt to get a teleport position from. + /// The position found if any, otherwise null. + /// If set to anything other than will only attempt to find in that zone. + /// The default radius allowed nea the found spot. + /// Whether a valid teleport position was correctly found. + public static bool TryGetTeleportLocation(this Player player, [NotNullWhen(true)] out Vector3? position, FacilityZone zone = FacilityZone.None, float defaultRadius = Scp106PocketExitFinder.RaycastRange) + { + position = null; + return player.RoleBase is IFpcRole fpc && TryGetTeleportLocation(fpc, out position, zone); + } + + /// + /// Tries to get a location to teleport a to. + /// + /// The to attempt to get a teleport position from. + /// The position found if any, otherwise null. + /// If set to anything other than will only attempt to find in that zone. + /// The default radius allowed nea the found spot. + /// Whether a valid teleport position was correctly found. + public static bool TryGetTeleportLocation(this IFpcRole fpc, [NotNullWhen(true)] out Vector3? position, FacilityZone zone = FacilityZone.None, float defaultRadius = Scp106PocketExitFinder.RaycastRange) + { + position = null; + + IEnumerable poses = zone == FacilityZone.None + ? SafeLocationFinder.GetLocations(null, null) + : Scp106PocketExitFinder.GetPosesForZone(zone); + + if (!poses.TryGetRandomValue(out Pose pose)) + return false; + + float radius = defaultRadius; + if (Room.TryGetRoomAtPosition(pose.position, out Room? room)) + radius = Scp106PocketExitFinder.GetRaycastRange(room.Zone); + + position = SafeLocationFinder.GetSafePosition(pose.position, pose.forward, radius, fpc.FpcModule.CharController); + return true; } } } \ No newline at end of file