using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using GameNetcodeStuff; using HarmonyLib; using LethalNetworkAPI; using LethalPursuer.Abilities; using LethalPursuer.Config; using LethalPursuer.Core; using LethalPursuer.Net; using LethalPursuer.Pursuer; using LethalPursuer.UI; using Microsoft.CodeAnalysis; using Unity.Netcode; using UnityEngine; using UnityEngine.AI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("AmazingAssets.TerrainToMesh")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: IgnoresAccessChecksTo("ClientNetworkTransform")] [assembly: IgnoresAccessChecksTo("com.olegknyazev.softmask")] [assembly: IgnoresAccessChecksTo("DissonanceVoip")] [assembly: IgnoresAccessChecksTo("DunGen")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.ASPP")] [assembly: IgnoresAccessChecksTo("DunGen.Integration.UnityNav")] [assembly: IgnoresAccessChecksTo("EasyTextEffects")] [assembly: IgnoresAccessChecksTo("Facepunch Transport for Netcode for GameObjects")] [assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")] [assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging")] [assembly: IgnoresAccessChecksTo("Unity.Animation.Rigging.DocCodeExamples")] [assembly: IgnoresAccessChecksTo("Unity.Burst")] [assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")] [assembly: IgnoresAccessChecksTo("Unity.Collections")] [assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem")] [assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")] [assembly: IgnoresAccessChecksTo("Unity.Jobs")] [assembly: IgnoresAccessChecksTo("Unity.Mathematics")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.Common")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.MetricTypes")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStats")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Component")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsMonitor.Implementation")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetStatsReporting")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkProfiler.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Multiplayer.Tools.NetworkSolutionInterface")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Components")] [assembly: IgnoresAccessChecksTo("Unity.Netcode.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.Networking.Transport")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Csg")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.KdTree")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Poly2Tri")] [assembly: IgnoresAccessChecksTo("Unity.ProBuilder.Stl")] [assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Config.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.HighDefinition.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")] [assembly: IgnoresAccessChecksTo("Unity.Services.Authentication")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Analytics")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Components")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Configuration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Device")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Environments.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Internal")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Networking")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Registration")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Scheduler")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Telemetry")] [assembly: IgnoresAccessChecksTo("Unity.Services.Core.Threading")] [assembly: IgnoresAccessChecksTo("Unity.Services.QoS")] [assembly: IgnoresAccessChecksTo("Unity.Services.Relay")] [assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")] [assembly: IgnoresAccessChecksTo("Unity.Timeline")] [assembly: IgnoresAccessChecksTo("Unity.VisualEffectGraph.Runtime")] [assembly: IgnoresAccessChecksTo("Unity.XR.CoreUtils")] [assembly: IgnoresAccessChecksTo("Unity.XR.Management")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.ConformanceAutomation")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MetaQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.MockRuntime")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.OculusQuestSupport")] [assembly: IgnoresAccessChecksTo("Unity.XR.OpenXR.Features.RuntimeDebugger")] [assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")] [assembly: IgnoresAccessChecksTo("UnityEngine.SpatialTracking")] [assembly: IgnoresAccessChecksTo("UnityEngine.UI")] [assembly: IgnoresAccessChecksTo("UnityEngine.XR.LegacyInputHelpers")] [assembly: AssemblyCompany("SharedNetUtils")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+4d00f8934e29e8ca2a944f82b5cc060c9833ba62")] [assembly: AssemblyProduct("SharedNetUtils")] [assembly: AssemblyTitle("SharedNetUtils")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace LethalPursuer { [BepInPlugin("SharedNetUtils", "SharedNetUtils", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { private readonly Harmony _harmony = new Harmony("SharedNetUtils"); public static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; PursuerConfig.Init(((BaseUnityPlugin)this).Config); WarnOnInvalidBinds(); NetworkHandler.Initialize(); _harmony.PatchAll(); Log.LogInfo((object)"SharedNetUtils 1.0.0 loaded."); } private static void WarnOnInvalidBinds() { Check("ghost_toggle_key", PursuerConfig.GhostToggle); Check("phantom_state_key", PursuerConfig.Phantom); Check("monster_swap_key", PursuerConfig.MonsterSwap); Check("sound_emitter_key", PursuerConfig.SoundEmitter); Check("light_flicker_key", PursuerConfig.LightFlicker); Check("door_lock_key", PursuerConfig.DoorLock); Check("fake_blip_key", PursuerConfig.FakeBlip); Check("ghost_voice_key", PursuerConfig.GhostVoice); Check("masked_cycle_item_key", PursuerConfig.MaskedCycle); Check("leash_key", PursuerConfig.Leash); Check("breaker_key", PursuerConfig.Breaker); Check("vent_key", PursuerConfig.Vent); Check("item_curse_key", PursuerConfig.ItemCurse); Check("puppet_waypoint_button", PursuerConfig.PuppetWaypoint); Check("director_mode_key", PursuerConfig.DirectorMode); Check("countdown_toggle_key", PursuerConfig.CountdownToggle); static void Check(string name, KeyBind b) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (!b.IsValid) { Log.LogWarning((object)$"Keybind '{name}' was unparseable; using fallback {b.Key}."); } } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "SharedNetUtils"; public const string PLUGIN_NAME = "SharedNetUtils"; public const string PLUGIN_VERSION = "1.0.0"; } } namespace LethalPursuer.UI { public sealed class PursuerHud : MonoBehaviour { private static PursuerHud? _instance; private static bool IsHost { get { if ((Object)(object)StartOfRound.Instance != (Object)null) { return ((NetworkBehaviour)StartOfRound.Instance).IsHost; } return false; } } public static PursuerHud GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("LethalPursuer_PursuerHud"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); return _instance; } private void OnGUI() { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) try { if (!IsHost) { return; } ModeManager instance = ModeManager.Instance; if (!((Object)(object)instance == (Object)null) && instance.Mode != PursuerMode.Player) { string[] array = BuildLines(instance); float num = 8f + (float)array.Length * 18f; GUI.Box(new Rect(12f, 48f, 190f, num), string.Empty); GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 13, alignment = (TextAnchor)3 }; for (int i = 0; i < array.Length; i++) { GUI.Label(new Rect(20f, 54f + (float)i * 18f, 174f, 18f), array[i], val); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"PursuerHud.OnGUI: {arg}"); } } private static string[] BuildLines(ModeManager mode) { DirectorModeAbility instance = DirectorModeAbility.Instance; bool flag = instance?.CurrentBudget.HasValue ?? false; string[] array = new string[2 + (flag ? 2 : 0)]; array[0] = $"Mode: {mode.Mode}"; array[1] = "Phantom: " + (mode.IsPhantom ? "ON" : "off"); if (flag) { array[2] = $"Director: {instance.CurrentBudget}/{instance.MaxBudget} spawns"; array[3] = "Selected: " + (instance.SelectedEnemyName ?? "--"); } return array; } } } namespace LethalPursuer.Pursuer { [HarmonyPatch] internal static class EnemySuppressionPatches { [HarmonyPatch(typeof(EnemyAI), "DoAIInterval")] [HarmonyPrefix] private static bool SuppressPossessedAI(EnemyAI __instance) { try { EnemyAI val = (((Object)(object)PossessionController.Instance != (Object)null) ? PossessionController.Instance.Possessed : null); return (Object)(object)val == (Object)null || val != __instance; } catch (Exception arg) { Plugin.Log.LogError((object)$"EnemySuppressionPatches.SuppressPossessedAI: {arg}"); return true; } } } public sealed class MonsterCamera : MonoBehaviour { private const float FollowDistance = 4.5f; private const float FollowHeight = 2f; private const float PositionLerpSpeed = 12f; private static MonsterCamera? _instance; private Camera _cam; private Transform? _target; private bool _active; public Transform CameraTransform => ((Component)_cam).transform; public static MonsterCamera GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("LethalPursuer_MonsterCamera"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); _instance.BuildCamera(); return _instance; } private void BuildCamera() { _cam = ((Component)this).gameObject.AddComponent(); _cam.cullingMask = -1; _cam.nearClipPlane = 0.05f; ((Behaviour)_cam).enabled = false; } public void Activate(Transform target) { _target = target; _active = true; ((Behaviour)_cam).enabled = true; PlayerControllerB val = StartOfRound.Instance?.localPlayerController; if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) { ((Behaviour)val.gameplayCamera).enabled = false; } SnapToTarget(); } public void Deactivate() { _active = false; ((Behaviour)_cam).enabled = false; _target = null; PlayerControllerB val = StartOfRound.Instance?.localPlayerController; if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) { ((Behaviour)val.gameplayCamera).enabled = true; } } public void Pause() { if (_active) { ((Behaviour)_cam).enabled = false; } } public void Resume() { if (_active && !((Object)(object)_target == (Object)null)) { ((Behaviour)_cam).enabled = true; SnapToTarget(); } } public void Tick() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) if (_active && !((Object)(object)_target == (Object)null)) { PossessionController instance = PossessionController.Instance; float num = (((Object)(object)instance != (Object)null) ? instance.Yaw : _target.eulerAngles.y); Quaternion val = Quaternion.Euler(((Object)(object)instance != (Object)null) ? instance.Pitch : 0f, num, 0f); Vector3 val2 = _target.position + Vector3.up * 2f - val * Vector3.forward * 4.5f; RaycastHit val3 = default(RaycastHit); if (Physics.Linecast(_target.position + Vector3.up * 2f, val2, ref val3, ((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.collidersAndRoomMaskAndDefault : (-1), (QueryTriggerInteraction)1)) { Vector3 point = ((RaycastHit)(ref val3)).point; Vector3 val4 = val2 - ((RaycastHit)(ref val3)).point; val2 = point + ((Vector3)(ref val4)).normalized * 0.3f; } ((Component)_cam).transform.position = Vector3.Lerp(((Component)_cam).transform.position, val2, 12f * Time.deltaTime); ((Component)_cam).transform.rotation = val; } } private void SnapToTarget() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_target == (Object)null)) { PossessionController instance = PossessionController.Instance; float num = (((Object)(object)instance != (Object)null) ? instance.Yaw : _target.eulerAngles.y); Quaternion val = Quaternion.Euler(0f, num, 0f); ((Component)_cam).transform.position = _target.position + Vector3.up * 2f - val * Vector3.forward * 4.5f; ((Component)_cam).transform.rotation = val; } } } public sealed class PossessionController : MonoBehaviour { private const float DriveTurnDegreesPerSecond = 220f; private const float MouseLookSensitivity = 2f; private const float MinAgentSpeed = 0.01f; private MonsterCamera _monsterCam; private float _yaw; private float _pitch; private float _defaultAgentSpeed = 1f; private float _defaultAgentAngularSpeed = 1f; private bool _defaultAgentUpdateRotation = true; public static PossessionController? Instance { get; private set; } public EnemyAI? Possessed { get; private set; } public string? PositionOverrideOwner { get; private set; } public bool PositionOverrideActive => PositionOverrideOwner != null; public bool FollowCamSuppressed { get; private set; } private static bool IsHost { get { if ((Object)(object)StartOfRound.Instance != (Object)null) { return ((NetworkBehaviour)StartOfRound.Instance).IsHost; } return false; } } public bool IsPossessing => (Object)(object)Possessed != (Object)null; public float Yaw => _yaw; public float Pitch => _pitch; public Transform CameraTransform => _monsterCam.CameraTransform; public static Transform ActiveAimTransform { get { if (!((Object)(object)Instance != (Object)null) || !Instance.IsPossessing) { return GhostCamera.GetOrCreate().CameraTransform; } return Instance.CameraTransform; } } public event Action? Released; public bool TryClaimPositionOverride(string owner) { if (PositionOverrideOwner != null && PositionOverrideOwner != owner) { return false; } PositionOverrideOwner = owner; return true; } public void ReleasePositionOverride(string owner) { if (PositionOverrideOwner == owner) { PositionOverrideOwner = null; } } public static PossessionController GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)Instance != (Object)null) { return Instance; } GameObject val = new GameObject("LethalPursuer_PossessionController"); Object.DontDestroyOnLoad((Object)val); Instance = val.AddComponent(); Instance._monsterCam = MonsterCamera.GetOrCreate(); return Instance; } public bool TryPossess(EnemyAI enemy) { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (!IsHost || (Object)(object)enemy == (Object)null) { return false; } try { if ((Object)(object)Possessed != (Object)null) { ReleaseCurrent(); } if ((Object)(object)enemy.agent == (Object)null) { enemy.agent = ((Component)enemy).GetComponentInChildren(); } if ((Object)(object)enemy.agent == (Object)null) { Plugin.Log.LogWarning((object)("PossessionController.TryPossess: " + ((Object)enemy).name + " has no NavMeshAgent, aborting.")); return false; } Possessed = enemy; enemy.movingTowardsTargetPlayer = false; enemy.targetPlayer = null; enemy.moveTowardsDestination = false; _defaultAgentSpeed = enemy.agent.speed; _defaultAgentAngularSpeed = enemy.agent.angularSpeed; _defaultAgentUpdateRotation = enemy.agent.updateRotation; enemy.agent.updateRotation = false; _yaw = ((Component)enemy).transform.eulerAngles.y; _pitch = 0f; PositionOverrideOwner = null; FollowCamSuppressed = false; _monsterCam.Activate(((Component)enemy).transform); return true; } catch (Exception arg) { Plugin.Log.LogError((object)$"PossessionController.TryPossess: {arg}"); return false; } } public void ReleaseCurrent() { try { this.Released?.Invoke(); } catch (Exception arg) { Plugin.Log.LogError((object)$"PossessionController.ReleaseCurrent Released subscriber: {arg}"); } try { _monsterCam.Deactivate(); PositionOverrideOwner = null; FollowCamSuppressed = false; EnemyAI possessed = Possessed; Possessed = null; if ((Object)(object)possessed == (Object)null) { return; } if ((Object)(object)possessed.agent != (Object)null) { ((Behaviour)possessed.agent).enabled = true; possessed.agent.speed = _defaultAgentSpeed; possessed.agent.angularSpeed = _defaultAgentAngularSpeed; possessed.agent.updateRotation = _defaultAgentUpdateRotation; if (possessed.agent.isOnNavMesh) { possessed.agent.ResetPath(); } } possessed.inSpecialAnimation = false; if ((Object)(object)possessed.creatureAnimator != (Object)null) { possessed.creatureAnimator.SetBool("inSpawningAnimation", false); } possessed.moveTowardsDestination = true; possessed.movingTowardsTargetPlayer = false; possessed.targetPlayer = null; } catch (Exception arg2) { Plugin.Log.LogError((object)$"PossessionController.ReleaseCurrent: {arg2}"); } } private void Update() { try { if (!IsHost || (Object)(object)Possessed == (Object)null) { return; } EnemyAI possessed = Possessed; if ((Object)(object)possessed == (Object)null || possessed.isEnemyDead) { ReleaseCurrent(); return; } DriveFromInput(possessed); if (!FollowCamSuppressed) { _monsterCam.Tick(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"PossessionController.Update: {arg}"); } } public void SuppressFollowCam() { FollowCamSuppressed = true; _monsterCam.Pause(); } public void ResumeFollowCam() { FollowCamSuppressed = false; _monsterCam.Resume(); } private void DriveFromInput(EnemyAI enemy) { //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)enemy.agent == (Object)null || !((Behaviour)enemy.agent).enabled) { return; } _yaw += Input.GetAxis("Mouse X") * 2f * 60f * Time.deltaTime; _pitch = Mathf.Clamp(_pitch - Input.GetAxis("Mouse Y") * 2f * 60f * Time.deltaTime, -40f, 40f); if (!PositionOverrideActive && enemy.agent.isOnNavMesh) { Vector3 val = Quaternion.Euler(0f, _yaw, 0f) * Vector3.forward; Vector3 val2 = Quaternion.Euler(0f, _yaw, 0f) * Vector3.right; Vector3 val3 = Vector3.zero; if (Input.GetKey((KeyCode)119)) { val3 += val; } if (Input.GetKey((KeyCode)115)) { val3 -= val; } if (Input.GetKey((KeyCode)100)) { val3 += val2; } if (Input.GetKey((KeyCode)97)) { val3 -= val2; } float num = Mathf.Max(_defaultAgentSpeed, 0.01f); if (((Vector3)(ref val3)).sqrMagnitude > 0.0001f) { ((Vector3)(ref val3)).Normalize(); enemy.agent.Move(val3 * num * Time.deltaTime); Quaternion val4 = Quaternion.LookRotation(val3, Vector3.up); ((Component)enemy).transform.rotation = Quaternion.RotateTowards(((Component)enemy).transform.rotation, val4, 220f * Time.deltaTime); } else { ((Component)enemy).transform.rotation = Quaternion.RotateTowards(((Component)enemy).transform.rotation, Quaternion.Euler(0f, _yaw, 0f), 220f * Time.deltaTime); } enemy.SyncPositionToClients(); } } } } namespace LethalPursuer.Phantom { [HarmonyPatch] internal static class PhantomPatches { [HarmonyPatch(typeof(PlayerControllerB), "Update")] [HarmonyPostfix] private static void HideGhostingBody(PlayerControllerB __instance) { try { bool flag = PresenceRegistry.Instance.IsGhosting(__instance.actualClientId); if ((Object)(object)__instance.thisPlayerModel != (Object)null && ((Renderer)__instance.thisPlayerModel).enabled == flag) { ((Renderer)__instance.thisPlayerModel).enabled = !flag; } if ((Object)(object)__instance.thisPlayerModelLOD1 != (Object)null) { ((Renderer)__instance.thisPlayerModelLOD1).enabled = !flag; } if ((Object)(object)__instance.thisPlayerModelLOD2 != (Object)null) { ((Renderer)__instance.thisPlayerModelLOD2).enabled = !flag; } if ((Object)(object)__instance.thisPlayerModelArms != (Object)null) { ((Renderer)__instance.thisPlayerModelArms).enabled = !flag; } } catch (Exception arg) { Plugin.Log.LogError((object)$"HideGhostingBody: {arg}"); } } [HarmonyPatch(typeof(ManualCameraRenderer), "Update")] [HarmonyPostfix] private static void ScrubPhantomRadarTargets(ManualCameraRenderer __instance) { try { List radarTargets = __instance.radarTargets; if (radarTargets == null) { return; } for (int num = radarTargets.Count - 1; num >= 0; num--) { PlayerControllerB val = (((Object)(object)radarTargets[num].transform != (Object)null) ? ((Component)radarTargets[num].transform).GetComponent() : null); if ((Object)(object)val != (Object)null && PresenceRegistry.Instance.IsPhantom(val.actualClientId)) { radarTargets.RemoveAt(num); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"ScrubPhantomRadarTargets: {arg}"); } } [HarmonyPatch(typeof(PlayerControllerB), "DamagePlayer")] [HarmonyPrefix] private static bool BlockDamageWhileGhosting(PlayerControllerB __instance) { try { return !PresenceRegistry.Instance.IsGhosting(__instance.actualClientId); } catch (Exception arg) { Plugin.Log.LogError((object)$"BlockDamageWhileGhosting: {arg}"); return true; } } [HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")] [HarmonyPrefix] private static bool BlockKillWhileGhosting(PlayerControllerB __instance) { try { return !PresenceRegistry.Instance.IsGhosting(__instance.actualClientId); } catch (Exception arg) { Plugin.Log.LogError((object)$"BlockKillWhileGhosting: {arg}"); return true; } } } } namespace LethalPursuer.Net { public static class NetworkHandler { private static LNetworkMessage? _presence; public static void Initialize() { _presence = LNetworkMessage.Connect("LethalPursuer.Presence", (Action)null, (Action)null, (Action)null); _presence.OnClientReceived += delegate(PresenceMessage msg) { PresenceRegistry.Instance.SetGhosting(msg.ClientId, msg.Ghosting); PresenceRegistry.Instance.SetPhantom(msg.ClientId, msg.Phantom); }; } public static void BroadcastPresence(ulong clientId, bool ghosting, bool phantom) { PresenceMessage presenceMessage = new PresenceMessage { ClientId = clientId, Ghosting = ghosting, Phantom = phantom }; PresenceRegistry.Instance.SetGhosting(clientId, ghosting); PresenceRegistry.Instance.SetPhantom(clientId, phantom); _presence?.SendClients(presenceMessage); } } public struct PresenceMessage { public ulong ClientId; public bool Ghosting; public bool Phantom; } } namespace LethalPursuer.Lifecycle { [HarmonyPatch] internal static class RoundLifecyclePatches { [HarmonyPatch(typeof(StartOfRound), "StartGame")] [HarmonyPostfix] private static void OnRoundStart() { try { ModeManager.Instance?.ForceReset(); ResetDirectorAndCountdown(); if ((Object)(object)StartOfRound.Instance != (Object)null && ((NetworkBehaviour)StartOfRound.Instance).IsHost && (Object)(object)ModeManager.Instance != (Object)null) { NetworkHandler.BroadcastPresence(ModeManager.Instance.LocalId, ghosting: false, phantom: false); } } catch (Exception arg) { Plugin.Log.LogError((object)$"OnRoundStart: {arg}"); } } [HarmonyPatch(typeof(StartOfRound), "ShipHasLeft")] [HarmonyPostfix] private static void OnRoundEnd() { try { ModeManager.Instance?.ForceReset(); ResetDirectorAndCountdown(); } catch (Exception arg) { Plugin.Log.LogError((object)$"OnRoundEnd: {arg}"); } } private static void ResetDirectorAndCountdown() { DirectorModeAbility.Instance?.ForceReset(); CountdownAbility.Instance?.ForceStop(); } [HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")] [HarmonyPostfix] private static void EnsureManagers() { try { if ((Object)(object)StartOfRound.Instance != (Object)null && ((NetworkBehaviour)StartOfRound.Instance).IsHost) { ModeManager.GetOrCreate(); PursuerHud.GetOrCreate(); } AbilityBootstrap.RegisterAll(InputRouter.GetOrCreate()); } catch (Exception arg) { Plugin.Log.LogError((object)$"EnsureManagers: {arg}"); } } } } namespace LethalPursuer.Core { public abstract class AbilityBase : IPursuerAbility { protected static bool IsHostActive { get { if ((Object)(object)StartOfRound.Instance != (Object)null) { return ((NetworkBehaviour)StartOfRound.Instance).IsHost; } return false; } } protected static ModeManager? Mode => ModeManager.Instance; protected static bool InGhostOrPursuer { get { if ((Object)(object)Mode != (Object)null) { return Mode.Mode != PursuerMode.Player; } return false; } } protected static Vector3 GhostPosition => GhostCamera.GetOrCreate().CameraTransform.position; protected static ulong LocalId { get { if (!((Object)(object)Mode != (Object)null)) { return 0uL; } return Mode.LocalId; } } public abstract void Tick(); } public static class AbilityBootstrap { private static bool _registered; public static void RegisterAll(InputRouter router) { if (_registered) { return; } _registered = true; try { foreach (Type item in from t in Assembly.GetExecutingAssembly().GetTypes() where (object)t != null && t.IsClass && !t.IsAbstract && typeof(IPursuerAbility).IsAssignableFrom(t) && t.GetConstructor(Type.EmptyTypes) != null select t) { try { if (Activator.CreateInstance(item) is IPursuerAbility ability) { router.Register(ability); } } catch (Exception arg) { Plugin.Log.LogError((object)$"AbilityBootstrap: failed to register {item.FullName}: {arg}"); } } } catch (Exception arg2) { Plugin.Log.LogError((object)$"AbilityBootstrap.RegisterAll: {arg2}"); } } } public sealed class GhostCamera : MonoBehaviour { private static GhostCamera? _instance; private Camera _cam; private Light _nightVision; private float _pitch; private float _yaw; private bool _active; public Transform CameraTransform => ((Component)_cam).transform; public static GhostCamera GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("LethalPursuer_GhostCamera"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); _instance.BuildCamera(); return _instance; } private void BuildCamera() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown _cam = ((Component)this).gameObject.AddComponent(); _cam.cullingMask = -1; _cam.nearClipPlane = 0.05f; ((Behaviour)_cam).enabled = false; GameObject val = new GameObject("LethalPursuer_NightVision"); val.transform.SetParent(((Component)_cam).transform, false); _nightVision = val.AddComponent(); _nightVision.type = (LightType)1; _nightVision.intensity = 1.2f; _nightVision.range = 1000f; ((Behaviour)_nightVision).enabled = false; } public void Activate(Vector3 startPos) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) _active = true; ((Component)_cam).transform.position = startPos; _yaw = ((Component)_cam).transform.eulerAngles.y; _pitch = 0f; ((Behaviour)_cam).enabled = true; ((Behaviour)_nightVision).enabled = true; PlayerControllerB val = StartOfRound.Instance?.localPlayerController; if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) { ((Behaviour)val.gameplayCamera).enabled = false; } } public void Deactivate() { _active = false; ((Behaviour)_cam).enabled = false; ((Behaviour)_nightVision).enabled = false; PlayerControllerB val = StartOfRound.Instance?.localPlayerController; if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) { ((Behaviour)val.gameplayCamera).enabled = true; } } private void Update() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) if (_active) { _yaw += Input.GetAxis("Mouse X") * 2f; _pitch = Mathf.Clamp(_pitch - Input.GetAxis("Mouse Y") * 2f, -89f, 89f); ((Component)_cam).transform.rotation = Quaternion.Euler(_pitch, _yaw, 0f); float num = PursuerConfig.GhostFlySpeed * (Input.GetKey((KeyCode)304) ? 3f : 1f); Vector3 val = Vector3.zero; if (Input.GetKey((KeyCode)119)) { val += ((Component)_cam).transform.forward; } if (Input.GetKey((KeyCode)115)) { val -= ((Component)_cam).transform.forward; } if (Input.GetKey((KeyCode)100)) { val += ((Component)_cam).transform.right; } if (Input.GetKey((KeyCode)97)) { val -= ((Component)_cam).transform.right; } if (Input.GetKey((KeyCode)32)) { val += Vector3.up; } if (Input.GetKey((KeyCode)306)) { val += Vector3.down; } Transform transform = ((Component)_cam).transform; transform.position += ((Vector3)(ref val)).normalized * num * Time.deltaTime; } } } public enum HoldResult { None, Tap, HoldTriggered } public sealed class HoldTracker { private readonly float _holdSeconds; private bool _isDown; private float _downAt; private bool _holdFired; public HoldTracker(float holdSeconds) { _holdSeconds = holdSeconds; } public HoldResult Update(bool isDown, float now) { if (isDown && !_isDown) { _isDown = true; _downAt = now; _holdFired = false; return HoldResult.None; } if (isDown && _isDown) { if (!_holdFired && now - _downAt >= _holdSeconds) { _holdFired = true; return HoldResult.HoldTriggered; } return HoldResult.None; } if (!isDown && _isDown) { _isDown = false; bool num = !_holdFired; _holdFired = false; if (!num) { return HoldResult.None; } return HoldResult.Tap; } return HoldResult.None; } } public sealed class InputRouter : MonoBehaviour { private static InputRouter? _instance; private readonly List _abilities = new List(); private static bool IsHost { get { if ((Object)(object)StartOfRound.Instance != (Object)null) { return ((NetworkBehaviour)StartOfRound.Instance).IsHost; } return false; } } public static InputRouter GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("LethalPursuer_InputRouter"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); return _instance; } public void Register(IPursuerAbility ability) { _abilities.Add(ability); } private void Update() { if (!IsHost) { return; } ModeManager orCreate = ModeManager.GetOrCreate(); if (PursuerConfig.GhostToggle.Pressed()) { orCreate.EnterGhostOrExit(); } if (PursuerConfig.Phantom.Pressed()) { orCreate.TogglePhantom(); } if (PursuerConfig.MonsterSwap.Pressed()) { orCreate.SwapMonster(); } foreach (IPursuerAbility ability in _abilities) { ability.Tick(); } } } public interface IPursuerAbility { void Tick(); } public sealed class ModeManager : MonoBehaviour { private readonly ModeStateMachine _sm = new ModeStateMachine(); private GhostCamera _ghostCam; private bool _bodyHidden; public static ModeManager? Instance { get; private set; } public PursuerMode Mode => _sm.Mode; public bool IsPhantom => _sm.IsPhantom; public ulong LocalId { get { if (!((Object)(object)StartOfRound.Instance?.localPlayerController != (Object)null)) { return 0uL; } return StartOfRound.Instance.localPlayerController.actualClientId; } } private static bool IsHost { get { if ((Object)(object)StartOfRound.Instance != (Object)null) { return ((NetworkBehaviour)StartOfRound.Instance).IsHost; } return false; } } public static ModeManager GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)Instance != (Object)null) { return Instance; } GameObject val = new GameObject("LethalPursuer_ModeManager"); Object.DontDestroyOnLoad((Object)val); Instance = val.AddComponent(); Instance._ghostCam = GhostCamera.GetOrCreate(); return Instance; } private void Update() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) if (IsHost && _sm.Mode == PursuerMode.Pursuer) { PossessionController instance = PossessionController.Instance; if ((!((Object)(object)instance != (Object)null) || !instance.IsPossessing) && _sm.TrySwapToGhost()) { instance?.ReleaseCurrent(); PlayerControllerB val = StartOfRound.Instance?.localPlayerController; _ghostCam.Activate(((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) ? ((Component)val.gameplayCamera).transform.position : (((Object)(object)val != (Object)null) ? ((Component)val).transform.position : Vector3.zero)); Plugin.Log.LogWarning((object)"ModeManager: possession ended unexpectedly (likely the possessed enemy died); recovered to Ghost mode."); } } } public void EnterGhostOrExit() { if (!IsHost) { return; } if (_sm.Mode == PursuerMode.Player) { if (_sm.TryEnterGhost()) { OnEnteredGhost(); } } else if (_sm.TryExitToPlayer()) { OnReturnedToPlayer(); } } public void TogglePhantom() { if (IsHost && _sm.TryTogglePhantom()) { NetworkHandler.BroadcastPresence(LocalId, ghosting: true, _sm.IsPhantom); } } public bool EnterPursuer(EnemyAI enemy) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (!IsHost || Mode != PursuerMode.Ghost || (Object)(object)enemy == (Object)null) { return false; } PossessionController orCreate = PossessionController.GetOrCreate(); _ghostCam.Deactivate(); if (!orCreate.TryPossess(enemy)) { _ghostCam.Activate(GetGhostCamFallbackPosition()); return false; } if (!_sm.TryEnterPursuer()) { orCreate.ReleaseCurrent(); _ghostCam.Activate(GetGhostCamFallbackPosition()); return false; } return true; } private static Vector3 GetGhostCamFallbackPosition() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB val = StartOfRound.Instance?.localPlayerController; if ((Object)(object)val == (Object)null) { return Vector3.zero; } if (!((Object)(object)val.gameplayCamera != (Object)null)) { return ((Component)val).transform.position; } return ((Component)val.gameplayCamera).transform.position; } public void SwapMonster() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) if (IsHost && _sm.TrySwapToGhost()) { PossessionController.GetOrCreate().ReleaseCurrent(); PlayerControllerB val = StartOfRound.Instance?.localPlayerController; _ghostCam.Activate(((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) ? ((Component)val.gameplayCamera).transform.position : (((Object)(object)val != (Object)null) ? ((Component)val).transform.position : Vector3.zero)); } } private void OnEnteredGhost() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB localPlayerController = StartOfRound.Instance.localPlayerController; _ghostCam.Activate(((Object)(object)localPlayerController.gameplayCamera != (Object)null) ? ((Component)localPlayerController.gameplayCamera).transform.position : ((Component)localPlayerController).transform.position); SetLocalBodyHidden(hidden: true); NetworkHandler.BroadcastPresence(LocalId, ghosting: true, phantom: false); } private void OnReturnedToPlayer() { PossessionController.GetOrCreate().ReleaseCurrent(); _ghostCam.Deactivate(); SetLocalBodyHidden(hidden: false); NetworkHandler.BroadcastPresence(LocalId, ghosting: false, phantom: false); } private void SetLocalBodyHidden(bool hidden) { _bodyHidden = hidden; PlayerControllerB val = StartOfRound.Instance?.localPlayerController; if (!((Object)(object)val == (Object)null)) { val.disableMoveInput = hidden; } } public void ForceReset() { PossessionController.GetOrCreate().ReleaseCurrent(); if (_sm.Mode != PursuerMode.Player) { _ghostCam.Deactivate(); SetLocalBodyHidden(hidden: false); } _sm.ResetToPlayer(); PresenceRegistry.Instance.Clear(); } } public sealed class ModeStateMachine { public PursuerMode Mode { get; private set; } public bool IsPhantom { get; private set; } public bool TryEnterGhost() { if (Mode != PursuerMode.Player) { return false; } Mode = PursuerMode.Ghost; return true; } public bool TryEnterPursuer() { if (Mode != PursuerMode.Ghost) { return false; } Mode = PursuerMode.Pursuer; return true; } public bool TrySwapToGhost() { if (Mode != PursuerMode.Pursuer) { return false; } Mode = PursuerMode.Ghost; return true; } public bool TryExitToPlayer() { if (Mode == PursuerMode.Player) { return false; } Mode = PursuerMode.Player; IsPhantom = false; return true; } public bool TryTogglePhantom() { if (Mode == PursuerMode.Player) { return false; } IsPhantom = !IsPhantom; return true; } public void ResetToPlayer() { Mode = PursuerMode.Player; IsPhantom = false; } } public sealed class PresenceRegistry { private readonly HashSet _ghosting = new HashSet(); private readonly HashSet _phantom = new HashSet(); public static PresenceRegistry Instance { get; } = new PresenceRegistry(); public bool IsGhosting(ulong clientId) { return _ghosting.Contains(clientId); } public bool IsPhantom(ulong clientId) { return _phantom.Contains(clientId); } public void SetGhosting(ulong clientId, bool on) { if (on) { _ghosting.Add(clientId); } else { _ghosting.Remove(clientId); } } public void SetPhantom(ulong clientId, bool on) { if (on) { _phantom.Add(clientId); } else { _phantom.Remove(clientId); } } public void Clear() { _ghosting.Clear(); _phantom.Clear(); } } public enum PursuerMode { Player, Ghost, Pursuer } public sealed class SpawnBudget { private readonly int _max; private readonly float _rechargeIntervalSeconds; private int _current; private float _lastRechargeAt; public int Max => _max; public SpawnBudget(int max, float rechargeIntervalSeconds, float now) { _max = Math.Max(1, max); _rechargeIntervalSeconds = Math.Max(1f, rechargeIntervalSeconds); _current = _max; _lastRechargeAt = now; } public int Current(float now) { ApplyRecharge(now); return _current; } public bool TrySpend(float now) { ApplyRecharge(now); if (_current <= 0) { return false; } _current--; return true; } public void Reset(float now) { _current = _max; _lastRechargeAt = now; } private void ApplyRecharge(float now) { if (_current >= _max) { _lastRechargeAt = now; return; } float num = now - _lastRechargeAt; if (!(num < _rechargeIntervalSeconds)) { int num2 = (int)(num / _rechargeIntervalSeconds); _current = Math.Min(_max, _current + num2); _lastRechargeAt += (float)num2 * _rechargeIntervalSeconds; } } } } namespace LethalPursuer.Config { public readonly struct KeyBind { public KeyCode Key { get; } public bool IsValid { get; } private KeyBind(KeyCode key, bool isValid) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) Key = key; IsValid = isValid; } public static KeyBind Parse(string raw, KeyCode fallback) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (Enum.TryParse((raw ?? string.Empty).Trim(), ignoreCase: true, out KeyCode result) && Enum.IsDefined(typeof(KeyCode), result)) { return new KeyBind(result, isValid: true); } return new KeyBind(fallback, isValid: false); } public bool Pressed() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Input.GetKeyDown(Key); } public bool Held() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return Input.GetKey(Key); } } public static class PursuerConfig { public static KeyBind GhostToggle; public static KeyBind Phantom; public static KeyBind MonsterSwap; public static KeyBind SoundEmitter; public static KeyBind LightFlicker; public static KeyBind DoorLock; public static KeyBind FakeBlip; public static KeyBind GhostVoice; public static KeyBind MaskedCycle; public static KeyBind Leash; public static KeyBind Breaker; public static KeyBind Vent; public static KeyBind ItemCurse; public static KeyBind PuppetWaypoint; public static KeyBind DirectorMode; public static KeyBind CountdownToggle; public static bool GhostVoiceEnabled; public static bool GhostVoiceDistortion; public static bool AsymmetricMode; public static bool BlackoutRequiresBreakerReset; public static int GhostPlayerIndex; public static int CountdownDuration; public static int DoorLockDuration; public static int FakeBlipDuration; public static int VentSealDuration; public static int ItemCurseAggroDuration; public static int SpawnBudgetMax; public static int SpawnBudgetRechargeInterval; public static int MaxSoundEmitters; public static int MaxFakeBlips; public static float SoundEmitterRadius; public static float GhostFlySpeed; private static string _keyGhostToggle = "F1"; private static string _keyPhantom = "F5"; private static string _keyMonsterSwap = "F2"; private static string _keySound = "Z"; private static string _keyFlicker = "X"; private static string _keyDoor = "C"; private static string _keyBlip = "V"; private static string _keyVoice = "T"; private static string _keyMasked = "G"; private static string _keyLeash = "L"; private static string _keyBreaker = "B"; private static string _keyVent = "N"; private static string _keyCurse = "I"; private static string _keyPuppet = "Mouse1"; private static string _keyDirector = "F3"; private static string _keyCountdown = "F4"; public static void Init(ConfigFile cfg) { _keyGhostToggle = cfg.Bind("Keybinds", "ghost_toggle_key", "F1", "Toggle Ghost mode.").Value; _keyPhantom = cfg.Bind("Keybinds", "phantom_state_key", "F5", "Toggle Phantom State.").Value; _keyMonsterSwap = cfg.Bind("Keybinds", "monster_swap_key", "F2", "Exit current monster back to Ghost.").Value; _keySound = cfg.Bind("Keybinds", "sound_emitter_key", "Z", "Sound emitter radial menu.").Value; _keyFlicker = cfg.Bind("Keybinds", "light_flicker_key", "X", "Light flicker (tap) / blackout (hold).").Value; _keyDoor = cfg.Bind("Keybinds", "door_lock_key", "C", "Door lock.").Value; _keyBlip = cfg.Bind("Keybinds", "fake_blip_key", "V", "Fake radar blip.").Value; _keyVoice = cfg.Bind("Keybinds", "ghost_voice_key", "T", "Ghost voice push-to-talk.").Value; _keyMasked = cfg.Bind("Keybinds", "masked_cycle_item_key", "G", "Cycle Masked held-item cosmetic.").Value; _keyLeash = cfg.Bind("Keybinds", "leash_key", "L", "Leash mode.").Value; _keyBreaker = cfg.Bind("Keybinds", "breaker_key", "B", "Breaker panel remote.").Value; _keyVent = cfg.Bind("Keybinds", "vent_key", "N", "Vent cover (tap) / vent hide (hold).").Value; _keyCurse = cfg.Bind("Keybinds", "item_curse_key", "I", "Item curse.").Value; _keyPuppet = cfg.Bind("Keybinds", "puppet_waypoint_button", "Mouse1", "AI puppet waypoint button.").Value; _keyDirector = cfg.Bind("Keybinds", "director_mode_key", "F3", "Director mode (deferred).").Value; _keyCountdown = cfg.Bind("Keybinds", "countdown_toggle_key", "F4", "Countdown toggle (deferred).").Value; GhostVoiceEnabled = cfg.Bind("Toggles", "ghost_voice_enabled", true, "Enable ghost voice.").Value; GhostVoiceDistortion = cfg.Bind("Toggles", "ghost_voice_distortion", true, "Distort ghost voice.").Value; AsymmetricMode = cfg.Bind("Toggles", "asymmetric_mode", false, "Designate a non-host as ghost (deferred).").Value; BlackoutRequiresBreakerReset = cfg.Bind("Toggles", "blackout_requires_breaker_reset", true, "Blackout stays until breaker reset.").Value; GhostPlayerIndex = cfg.Bind("Ghost", "ghost_player_index", 0, "Ghost player index (deferred).").Value; GhostFlySpeed = cfg.Bind("Ghost", "ghost_fly_speed", 12f, "Ghost free-fly camera speed (m/s).").Value; CountdownDuration = cfg.Bind("Durations", "countdown_duration", 240, "Countdown seconds (deferred).").Value; DoorLockDuration = cfg.Bind("Durations", "door_lock_duration", 15, "Door lock seconds.").Value; FakeBlipDuration = cfg.Bind("Durations", "fake_blip_duration", 30, "Fake blip seconds.").Value; VentSealDuration = cfg.Bind("Durations", "vent_seal_duration", 20, "Vent seal seconds.").Value; ItemCurseAggroDuration = cfg.Bind("Durations", "item_curse_aggro_duration", 5, "Item curse aggro seconds.").Value; SpawnBudgetRechargeInterval = cfg.Bind("Durations", "spawn_budget_recharge_interval", 60, "Spawn budget recharge seconds (deferred).").Value; SpawnBudgetMax = cfg.Bind("Limits", "spawn_budget_max", 5, "Max spawn budget (deferred).").Value; SoundEmitterRadius = cfg.Bind("Limits", "sound_emitter_radius", 10f, "Sound emitter 3D falloff radius (m).").Value; MaxSoundEmitters = cfg.Bind("Limits", "max_sound_emitters", 3, "Max active sound emitters.").Value; MaxFakeBlips = cfg.Bind("Limits", "max_fake_blips", 2, "Max active fake blips.").Value; GhostToggle = KeyBind.Parse(_keyGhostToggle, (KeyCode)282); Phantom = KeyBind.Parse(_keyPhantom, (KeyCode)286); MonsterSwap = KeyBind.Parse(_keyMonsterSwap, (KeyCode)283); SoundEmitter = KeyBind.Parse(_keySound, (KeyCode)122); LightFlicker = KeyBind.Parse(_keyFlicker, (KeyCode)120); DoorLock = KeyBind.Parse(_keyDoor, (KeyCode)99); FakeBlip = KeyBind.Parse(_keyBlip, (KeyCode)118); GhostVoice = KeyBind.Parse(_keyVoice, (KeyCode)116); MaskedCycle = KeyBind.Parse(_keyMasked, (KeyCode)103); Leash = KeyBind.Parse(_keyLeash, (KeyCode)108); Breaker = KeyBind.Parse(_keyBreaker, (KeyCode)98); Vent = KeyBind.Parse(_keyVent, (KeyCode)110); ItemCurse = KeyBind.Parse(_keyCurse, (KeyCode)105); PuppetWaypoint = KeyBind.Parse(_keyPuppet, (KeyCode)324); DirectorMode = KeyBind.Parse(_keyDirector, (KeyCode)284); CountdownToggle = KeyBind.Parse(_keyCountdown, (KeyCode)285); } } } namespace LethalPursuer.Abilities { public sealed class BlackoutAbility : AbilityBase { private const float HoldThresholdSeconds = 2f; private const float AutoRestoreDelaySeconds = 5f; private readonly HoldTracker _tracker = new HoldTracker(2f); private bool _autoRestorePending; private float _autoRestoreAt; public override void Tick() { try { if (!AbilityBase.IsHostActive || !AbilityBase.InGhostOrPursuer) { _autoRestorePending = false; return; } if (_tracker.Update(PursuerConfig.LightFlicker.Held(), Time.time) == HoldResult.HoldTriggered) { RoundManager instance = RoundManager.Instance; if (instance != null) { instance.SwitchPower(false); } if (!PursuerConfig.BlackoutRequiresBreakerReset) { _autoRestorePending = true; _autoRestoreAt = Time.time + 5f; } } if (_autoRestorePending && Time.time >= _autoRestoreAt) { _autoRestorePending = false; RoundManager instance2 = RoundManager.Instance; if (instance2 != null) { instance2.SwitchPower(true); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"BlackoutAbility.Tick: {arg}"); } } } public sealed class BreakerAbility : AbilityBase { private const float CooldownSeconds = 10f; private float _cooldownUntil; public override void Tick() { try { if (AbilityBase.IsHostActive && AbilityBase.InGhostOrPursuer && PursuerConfig.Breaker.Pressed() && !(Time.time < _cooldownUntil)) { _cooldownUntil = Time.time + 10f; RoundManager instance = RoundManager.Instance; if (instance != null) { instance.SwitchPower(false); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"BreakerAbility.Tick: {arg}"); } } } public sealed class CountdownAbility : AbilityBase { public struct CountdownMessage { public bool On; public float StartTime; public int Duration; } private static readonly LNetworkMessage Msg; private bool _hostToggledOn; public static CountdownAbility? Instance { get; private set; } public CountdownAbility() { Instance = this; } static CountdownAbility() { Msg = LNetworkMessage.Connect("LethalPursuer.Countdown", (Action)null, (Action)null, (Action)null); try { Msg.OnClientReceived += delegate(CountdownMessage msg) { if (msg.On) { CountdownOverlay.GetOrCreate().Show(msg.StartTime, msg.Duration); } else { CountdownOverlay.GetOrCreate().Hide(); } }; } catch (Exception arg) { Plugin.Log.LogError((object)$"CountdownAbility static init: {arg}"); } } public override void Tick() { try { if (!AbilityBase.IsHostActive) { if (_hostToggledOn) { _hostToggledOn = false; } } else if (PursuerConfig.CountdownToggle.Pressed()) { Toggle(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"CountdownAbility.Tick: {arg}"); } } private void Toggle() { _hostToggledOn = !_hostToggledOn; if (_hostToggledOn) { float time = Time.time; int duration = Math.Max(1, PursuerConfig.CountdownDuration); CountdownOverlay.GetOrCreate().Show(time, duration); Msg.SendClients(new CountdownMessage { On = true, StartTime = time, Duration = duration }); } else { CountdownOverlay.GetOrCreate().Hide(); Msg.SendClients(new CountdownMessage { On = false }); } } public void ForceStop() { if (_hostToggledOn) { _hostToggledOn = false; CountdownOverlay.GetOrCreate().Hide(); Msg.SendClients(new CountdownMessage { On = false }); } } } public sealed class CountdownOverlay : MonoBehaviour { private static CountdownOverlay? _instance; private bool _visible; private float _startTime; private int _duration; public static CountdownOverlay GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("LethalPursuer_CountdownOverlay"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); return _instance; } public void Show(float startTime, int duration) { _startTime = startTime; _duration = Math.Max(1, duration); _visible = true; } public void Hide() { _visible = false; } private void OnGUI() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Expected O, but got Unknown //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) if (!_visible) { return; } try { float num = Mathf.Max(0f, (float)_duration - (Time.time - _startTime)); int num2 = Mathf.CeilToInt(num); int num3 = num2 / 60; string text = string.Format(arg1: num2 % 60, format: "{0:00}:{1:00}", arg0: num3); float num4 = Mathf.Max(10f, (float)_duration * 0.1f); Color textColor = ((num <= num4) ? new Color(0.85f, 0.1f, 0.1f) : new Color(0.9f, 0.85f, 0.75f)); GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = 54, fontStyle = (FontStyle)1, alignment = (TextAnchor)1 }; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((float)Screen.width - 300f) * 0.5f, 18f, 300f, 70f); GUIStyle val3 = new GUIStyle(val); val3.normal.textColor = Color.black; GUIStyle val4 = val3; GUI.Label(new Rect(((Rect)(ref val2)).x + 2f, ((Rect)(ref val2)).y + 2f, ((Rect)(ref val2)).width, ((Rect)(ref val2)).height), text, val4); val.normal.textColor = textColor; GUI.Label(val2, text, val); } catch (Exception arg) { Plugin.Log.LogError((object)$"CountdownOverlay.OnGUI: {arg}"); } } } public sealed class DirectorModeAbility : AbilityBase { private const float RaycastDistance = 120f; private readonly List _cycle = new List(); private bool _active; private int _selectedIndex; private SpawnBudget? _budget; public static bool IsActive { get; private set; } public static DirectorModeAbility? Instance { get; private set; } public int? CurrentBudget { get { if (!_active || _budget == null) { return null; } return _budget.Current(Time.time); } } public int? MaxBudget { get { if (!_active || _budget == null) { return null; } return _budget.Max; } } public string? SelectedEnemyName { get { if (!_active || _cycle.Count <= 0) { return null; } return _cycle[Mathf.Clamp(_selectedIndex, 0, _cycle.Count - 1)].enemyName; } } public DirectorModeAbility() { Instance = this; } public override void Tick() { try { if (!AbilityBase.IsHostActive) { if (_active) { Deactivate(); } return; } if (PursuerConfig.DirectorMode.Pressed()) { if (_active) { Deactivate(); } else { TryActivate(); } } if (_active && ((Object)(object)AbilityBase.Mode == (Object)null || AbilityBase.Mode.Mode != PursuerMode.Ghost)) { Deactivate(); } else if (_active) { HandleCycleInput(); if (Input.GetMouseButtonDown(0)) { TrySpawn(); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"DirectorModeAbility.Tick: {arg}"); } } private void TryActivate() { if ((Object)(object)AbilityBase.Mode == (Object)null || AbilityBase.Mode.Mode != PursuerMode.Ghost) { Plugin.Log.LogInfo((object)"DirectorModeAbility: Director mode only available while ghosting."); return; } RebuildCycle(); if (_cycle.Count == 0) { Plugin.Log.LogWarning((object)"DirectorModeAbility: current level has no spawnable enemy types; Director mode not activated."); return; } _selectedIndex = Mathf.Clamp(_selectedIndex, 0, _cycle.Count - 1); if (_budget == null) { _budget = new SpawnBudget(PursuerConfig.SpawnBudgetMax, PursuerConfig.SpawnBudgetRechargeInterval, Time.time); } _active = true; IsActive = true; } private void Deactivate() { _active = false; IsActive = false; } public void ForceReset() { Deactivate(); _budget = null; } private void RebuildCycle() { _cycle.Clear(); SelectableLevel val = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.currentLevel : null); if ((Object)(object)val == (Object)null || val.Enemies == null) { return; } foreach (SpawnableEnemyWithRarity enemy in val.Enemies) { EnemyType val2 = enemy?.enemyType; if (!((Object)(object)val2 == (Object)null) && !val2.spawningDisabled && !((Object)(object)val2.enemyPrefab == (Object)null)) { _cycle.Add(val2); } } } private void HandleCycleInput() { if (_cycle.Count != 0) { float axis = Input.GetAxis("Mouse ScrollWheel"); int num = ((axis > 0f) ? 1 : ((axis < 0f) ? (-1) : 0)); if (Input.GetKeyDown((KeyCode)93)) { num = 1; } else if (Input.GetKeyDown((KeyCode)91)) { num = -1; } if (num != 0) { _selectedIndex = ((_selectedIndex + num) % _cycle.Count + _cycle.Count) % _cycle.Count; } } } private void TrySpawn() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) if (_cycle.Count == 0 || _budget == null || (Object)(object)RoundManager.Instance == (Object)null) { return; } if (!_budget.TrySpend(Time.time)) { Plugin.Log.LogInfo((object)"DirectorModeAbility: spawn budget empty."); return; } Vector3? val = FindFloorPoint(); if (val.HasValue) { Vector3 valueOrDefault = val.GetValueOrDefault(); EnemyType val2 = _cycle[_selectedIndex]; try { RoundManager.Instance.SpawnEnemyGameObject(valueOrDefault, 0f, -1, val2); Plugin.Log.LogInfo((object)$"DirectorModeAbility: spawned {val2.enemyName} at {valueOrDefault}."); return; } catch (Exception arg) { Plugin.Log.LogError((object)$"DirectorModeAbility.TrySpawn: {arg}"); RefundLastSpend(); return; } } Plugin.Log.LogInfo((object)"DirectorModeAbility: no floor point under aim; spawn cancelled."); RefundLastSpend(); } private static void RefundLastSpend() { } private static Vector3? FindFloorPoint() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) Transform cameraTransform = GhostCamera.GetOrCreate().CameraTransform; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(cameraTransform.position, cameraTransform.forward, ref val, 120f, -1, (QueryTriggerInteraction)1)) { return null; } return ((RaycastHit)(ref val)).point; } } public sealed class DoorLockAbility : AbilityBase { private const float RaycastDistance = 5f; private static readonly LNetworkMessage Msg; private readonly Dictionary _expiryByDoorId = new Dictionary(); static DoorLockAbility() { Msg = LNetworkMessage.Connect("LethalPursuer.DoorLock", (Action)null, (Action)null, (Action)null); try { Msg.OnClientReceived += delegate(ulong doorNetworkObjectId) { ApplyLock(doorNetworkObjectId); }; } catch (Exception arg) { Plugin.Log.LogError((object)$"DoorLockAbility static init: {arg}"); } } public override void Tick() { try { if (AbilityBase.IsHostActive && AbilityBase.InGhostOrPursuer) { if (PursuerConfig.DoorLock.Pressed()) { TryLockDoorAhead(); } TickExpiry(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"DoorLockAbility.Tick: {arg}"); } } private void TryLockDoorAhead() { DoorLock val = FindDoorAhead(); if (!((Object)(object)val == (Object)null) && val.canBeLocked && !val.isLocked) { ulong networkObjectId = ((NetworkBehaviour)val).NetworkObjectId; ApplyLock(networkObjectId); Msg.SendClients(networkObjectId); _expiryByDoorId[networkObjectId] = Time.time + (float)PursuerConfig.DoorLockDuration; } } private void TickExpiry() { if (_expiryByDoorId.Count == 0) { return; } List list = null; foreach (KeyValuePair item in _expiryByDoorId) { if (!(Time.time < item.Value)) { (list ?? (list = new List())).Add(item.Key); } } if (list == null) { return; } foreach (ulong item2 in list) { _expiryByDoorId.Remove(item2); UnlockDoorById(item2); } } private static DoorLock? FindDoorAhead() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) Transform cameraTransform = GhostCamera.GetOrCreate().CameraTransform; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(cameraTransform.position, cameraTransform.forward, ref val, 5f, -1, (QueryTriggerInteraction)2)) { return null; } return ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent(); } private static void ApplyLock(ulong doorNetworkObjectId) { try { DoorLock val = ResolveDoor(doorNetworkObjectId); if (!((Object)(object)val == (Object)null) && val.canBeLocked) { val.LockDoor((float)PursuerConfig.DoorLockDuration); } } catch (Exception arg) { Plugin.Log.LogError((object)$"DoorLockAbility.ApplyLock: {arg}"); } } private static void UnlockDoorById(ulong doorNetworkObjectId) { try { DoorLock val = ResolveDoor(doorNetworkObjectId); if ((Object)(object)val != (Object)null && val.isLocked) { val.UnlockDoorSyncWithServer(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"DoorLockAbility.UnlockDoorById: {arg}"); } } private static DoorLock? ResolveDoor(ulong doorNetworkObjectId) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.SpawnManager == null) { return null; } if (!singleton.SpawnManager.SpawnedObjects.TryGetValue(doorNetworkObjectId, out var value) || (Object)(object)value == (Object)null) { return null; } return ((Component)value).GetComponent(); } } public sealed class FakeRadarBlipAbility : AbilityBase { public struct BlipMessage { public ulong BlipId; public bool Add; public Vector3 Position; public string Label; } private static readonly string[] FakePlayerLabels; private static readonly LNetworkMessage Msg; private static readonly Dictionary ActiveBlipObjects; private readonly Dictionary _expiryById = new Dictionary(); private readonly List _activeOrder = new List(); private bool _toggledOn; private ulong _nextId = 1uL; static FakeRadarBlipAbility() { FakePlayerLabels = new string[6] { "??? #1", "??? #2", "??? #3", "SIGNAL LOST", "UNKNOWN", "static" }; Msg = LNetworkMessage.Connect("LethalPursuer.FakeBlip", (Action)null, (Action)null, (Action)null); ActiveBlipObjects = new Dictionary(); try { Msg.OnClientReceived += delegate(BlipMessage msg) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (msg.Add) { ApplyAdd(msg.BlipId, msg.Position, msg.Label); } else { ApplyRemove(msg.BlipId); } }; } catch (Exception arg) { Plugin.Log.LogError((object)$"FakeRadarBlipAbility static init: {arg}"); } } public override void Tick() { try { if (!AbilityBase.IsHostActive || !AbilityBase.InGhostOrPursuer) { if (_toggledOn) { RemoveAllOwnedByHost(); } return; } if (PursuerConfig.FakeBlip.Pressed()) { _toggledOn = !_toggledOn; if (_toggledOn) { SpawnOne(); } else { RemoveAllOwnedByHost(); } } TickExpiry(); } catch (Exception arg) { Plugin.Log.LogError((object)$"FakeRadarBlipAbility.Tick: {arg}"); } } private void SpawnOne() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) while (_activeOrder.Count >= Math.Max(1, PursuerConfig.MaxFakeBlips)) { ulong num = _activeOrder[0]; _activeOrder.RemoveAt(0); _expiryById.Remove(num); ApplyRemove(num); Msg.SendClients(new BlipMessage { BlipId = num, Add = false }); } ulong num2 = _nextId++; Vector3 position = PlausibleSpawnPosition(); string label = FakePlayerLabels[Random.Range(0, FakePlayerLabels.Length)]; _activeOrder.Add(num2); _expiryById[num2] = Time.time + (float)Math.Max(1, PursuerConfig.FakeBlipDuration); ApplyAdd(num2, position, label); Msg.SendClients(new BlipMessage { BlipId = num2, Add = true, Position = position, Label = label }); } private static Vector3 PlausibleSpawnPosition() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return AbilityBase.GhostPosition; } private void TickExpiry() { if (_expiryById.Count == 0) { return; } List list = null; foreach (KeyValuePair item in _expiryById) { if (!(Time.time < item.Value)) { (list ?? (list = new List())).Add(item.Key); } } if (list == null) { return; } foreach (ulong item2 in list) { _expiryById.Remove(item2); _activeOrder.Remove(item2); ApplyRemove(item2); Msg.SendClients(new BlipMessage { BlipId = item2, Add = false }); } if (_activeOrder.Count == 0) { _toggledOn = false; } } private void RemoveAllOwnedByHost() { if (_activeOrder.Count == 0) { _toggledOn = false; return; } foreach (ulong item in _activeOrder) { _expiryById.Remove(item); ApplyRemove(item); Msg.SendClients(new BlipMessage { BlipId = item, Add = false }); } _activeOrder.Clear(); _toggledOn = false; } private static void ApplyAdd(ulong blipId, Vector3 position, string label) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown try { if (!ActiveBlipObjects.ContainsKey(blipId)) { ManualCameraRenderer val = Object.FindObjectOfType(); if (!((Object)(object)val == (Object)null) && val.radarTargets != null) { GameObject val2 = new GameObject($"LethalPursuer_FakeBlip_{blipId}"); Object.DontDestroyOnLoad((Object)(object)val2); val2.transform.position = position; val.radarTargets.Add(new TransformAndName(val2.transform, label, true)); ActiveBlipObjects[blipId] = val2; } } } catch (Exception arg) { Plugin.Log.LogError((object)$"FakeRadarBlipAbility.ApplyAdd: {arg}"); } } private static void ApplyRemove(ulong blipId) { try { if (!ActiveBlipObjects.TryGetValue(blipId, out GameObject value)) { return; } ActiveBlipObjects.Remove(blipId); ManualCameraRenderer val = Object.FindObjectOfType(); if ((Object)(object)val != (Object)null && val.radarTargets != null && (Object)(object)value != (Object)null) { for (int num = val.radarTargets.Count - 1; num >= 0; num--) { if ((Object)(object)val.radarTargets[num].transform == (Object)(object)value.transform) { val.radarTargets.RemoveAt(num); } } } if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } catch (Exception arg) { Plugin.Log.LogError((object)$"FakeRadarBlipAbility.ApplyRemove: {arg}"); } } } public sealed class GhostVoiceAbility : AbilityBase { public struct VoiceStateMessage { public ulong HostClientId; public bool Active; public Vector3 GhostPosition; public bool Distortion; } private static readonly LNetworkMessage Msg; private bool _wasHeld; static GhostVoiceAbility() { Msg = LNetworkMessage.Connect("LethalPursuer.GhostVoice", (Action)null, (Action)null, (Action)null); try { Msg.OnClientReceived += delegate(VoiceStateMessage msg) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (msg.Active) { GhostVoiceRelocator.GetOrCreate().BeginRelocate(msg.HostClientId, msg.GhostPosition, msg.Distortion); } else { GhostVoiceRelocator.GetOrCreate().EndRelocate(msg.HostClientId); } }; } catch (Exception arg) { Plugin.Log.LogError((object)$"GhostVoiceAbility static init: {arg}"); } } public override void Tick() { try { if (!AbilityBase.IsHostActive || !AbilityBase.InGhostOrPursuer) { if (_wasHeld) { StopBroadcast(); } return; } if (!PursuerConfig.GhostVoiceEnabled) { if (_wasHeld) { StopBroadcast(); } return; } bool flag = PursuerConfig.GhostVoice.Held(); if (flag && !_wasHeld) { StartBroadcast(); } else if (!flag && _wasHeld) { StopBroadcast(); } else if (flag) { UpdateBroadcastPosition(); } _wasHeld = flag; } catch (Exception arg) { Plugin.Log.LogError((object)$"GhostVoiceAbility.Tick: {arg}"); } } private void StartBroadcast() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) VoiceStateMessage voiceStateMessage = new VoiceStateMessage { HostClientId = AbilityBase.LocalId, Active = true, GhostPosition = AbilityBase.GhostPosition, Distortion = PursuerConfig.GhostVoiceDistortion }; Msg.SendClients(voiceStateMessage); } private void UpdateBroadcastPosition() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) Msg.SendClients(new VoiceStateMessage { HostClientId = AbilityBase.LocalId, Active = true, GhostPosition = AbilityBase.GhostPosition, Distortion = PursuerConfig.GhostVoiceDistortion }); } private void StopBroadcast() { _wasHeld = false; Msg.SendClients(new VoiceStateMessage { HostClientId = AbilityBase.LocalId, Active = false }); } } public sealed class GhostVoiceRelocator : MonoBehaviour { private static GhostVoiceRelocator? _instance; private bool _active; private ulong _hostClientId; private Vector3 _targetPosition; private bool _distortion; private AudioSource? _voiceAudio; private AudioLowPassFilter? _lowPass; private float _originalSpatialBlend; private float _originalLowPassCutoff; private bool _originalLowPassEnabled; private bool _capturedOriginal; public static GhostVoiceRelocator GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("LethalPursuer_GhostVoiceRelocator"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); return _instance; } public void BeginRelocate(ulong hostClientId, Vector3 ghostPosition, bool distortion) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) try { _hostClientId = hostClientId; _targetPosition = ghostPosition; _distortion = distortion; if (!_active) { _active = true; ResolveVoiceSource(); CaptureOriginalIfNeeded(); ApplyActiveState(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"GhostVoiceRelocator.BeginRelocate: {arg}"); } } public void EndRelocate(ulong hostClientId) { try { if (_active && hostClientId == _hostClientId) { _active = false; RestoreOriginal(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"GhostVoiceRelocator.EndRelocate: {arg}"); } } private void ResolveVoiceSource() { _voiceAudio = null; _lowPass = null; PlayerControllerB[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.allPlayerScripts : null); if (array == null) { return; } PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if (!((Object)(object)val == (Object)null) && val.actualClientId == _hostClientId) { _voiceAudio = val.currentVoiceChatAudioSource; break; } } if ((Object)(object)_voiceAudio != (Object)null) { _lowPass = ((Component)_voiceAudio).GetComponent(); } } private void CaptureOriginalIfNeeded() { if (!_capturedOriginal && !((Object)(object)_voiceAudio == (Object)null)) { _originalSpatialBlend = _voiceAudio.spatialBlend; if ((Object)(object)_lowPass != (Object)null) { _originalLowPassEnabled = ((Behaviour)_lowPass).enabled; _originalLowPassCutoff = _lowPass.cutoffFrequency; } _capturedOriginal = true; } } private void ApplyActiveState() { if (!((Object)(object)_voiceAudio == (Object)null)) { _voiceAudio.spatialBlend = 1f; if (_distortion && (Object)(object)_lowPass != (Object)null) { ((Behaviour)_lowPass).enabled = true; _lowPass.cutoffFrequency = 800f; } } } private void RestoreOriginal() { if ((Object)(object)_voiceAudio != (Object)null && _capturedOriginal) { _voiceAudio.spatialBlend = _originalSpatialBlend; } if ((Object)(object)_lowPass != (Object)null && _capturedOriginal) { ((Behaviour)_lowPass).enabled = _originalLowPassEnabled; _lowPass.cutoffFrequency = _originalLowPassCutoff; } _capturedOriginal = false; _voiceAudio = null; _lowPass = null; } private void LateUpdate() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!_active) { return; } try { if ((Object)(object)_voiceAudio == (Object)null) { ResolveVoiceSource(); } if (!((Object)(object)_voiceAudio == (Object)null)) { ((Component)_voiceAudio).transform.position = _targetPosition; } } catch (Exception arg) { Plugin.Log.LogError((object)$"GhostVoiceRelocator.LateUpdate: {arg}"); } } } public sealed class ItemCurseAbility : AbilityBase { public struct CueMessage { public ulong VictimClientId; } private const float RaycastDistance = 6f; private const float AggroRadius = 60f; internal static readonly LNetworkMessage GrabReportMsg; private static readonly LNetworkMessage CueMsg; internal static ulong? CursedItemNetworkObjectId; private static readonly Dictionary ActiveAggro; private static readonly Dictionary AggroVictim; static ItemCurseAbility() { GrabReportMsg = LNetworkMessage.Connect("LethalPursuer.ItemCurse.GrabReport", (Action)null, (Action)null, (Action)null); CueMsg = LNetworkMessage.Connect("LethalPursuer.ItemCurse.Cue", (Action)null, (Action)null, (Action)null); ActiveAggro = new Dictionary(); AggroVictim = new Dictionary(); try { GrabReportMsg.OnServerReceived += delegate(ulong grabbedNetworkObjectId, ulong grabberClientId) { OnGrabReported(grabbedNetworkObjectId, grabberClientId); }; CueMsg.OnClientReceived += delegate(CueMessage msg) { PlayCue(msg.VictimClientId); }; } catch (Exception arg) { Plugin.Log.LogError((object)$"ItemCurseAbility static init: {arg}"); } } public override void Tick() { try { if (AbilityBase.IsHostActive && AbilityBase.InGhostOrPursuer) { if (PursuerConfig.ItemCurse.Pressed()) { TryCurseItemAhead(); } TickAggro(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"ItemCurseAbility.Tick: {arg}"); } } private void TryCurseItemAhead() { GrabbableObject val = FindGrabbableAhead(); if (!((Object)(object)val == (Object)null)) { CursedItemNetworkObjectId = ((NetworkBehaviour)val).NetworkObjectId; } } private static GrabbableObject? FindGrabbableAhead() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) Transform cameraTransform = GhostCamera.GetOrCreate().CameraTransform; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(cameraTransform.position, cameraTransform.forward, ref val, 6f, -1, (QueryTriggerInteraction)2)) { return null; } return ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent(); } private static void OnGrabReported(ulong grabbedNetworkObjectId, ulong grabberClientId) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) try { ulong? cursedItemNetworkObjectId = CursedItemNetworkObjectId; if (!cursedItemNetworkObjectId.HasValue) { return; } ulong valueOrDefault = cursedItemNetworkObjectId.GetValueOrDefault(); if (valueOrDefault != grabbedNetworkObjectId) { return; } CursedItemNetworkObjectId = null; PlayerControllerB val = ResolvePlayerByClientId(grabberClientId); if ((Object)(object)val == (Object)null) { return; } float value = Time.time + (float)Math.Max(1, PursuerConfig.ItemCurseAggroDuration); List list = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.SpawnedEnemies : null); if (list != null) { foreach (EnemyAI item in list) { if (!((Object)(object)item == (Object)null) && !item.isEnemyDead && !(Vector3.Distance(((Component)item).transform.position, ((Component)val).transform.position) > 60f)) { item.SetMovingTowardsTargetPlayer(val); ActiveAggro[item] = value; AggroVictim[item] = val; } } } CueMsg.SendClients(new CueMessage { VictimClientId = val.actualClientId }); } catch (Exception arg) { Plugin.Log.LogError((object)$"ItemCurseAbility.OnGrabReported: {arg}"); } } private static void TickAggro() { //IL_0097: Unknown result type (might be due to invalid IL or missing references) if (ActiveAggro.Count == 0) { return; } List list = null; foreach (KeyValuePair item in ActiveAggro) { EnemyAI key = item.Key; PlayerControllerB value; if ((Object)(object)key == (Object)null || key.isEnemyDead) { (list ?? (list = new List())).Add(key); } else if (Time.time >= item.Value) { (list ?? (list = new List())).Add(key); } else if (AggroVictim.TryGetValue(key, out value) && (Object)(object)value != (Object)null) { key.SetDestinationToPosition(((Component)value).transform.position, true); } } if (list == null) { return; } foreach (EnemyAI item2 in list) { ActiveAggro.Remove(item2); AggroVictim.Remove(item2); } } private static PlayerControllerB? ResolvePlayerByClientId(ulong clientId) { PlayerControllerB[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.allPlayerScripts : null); if (array == null) { return null; } PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if ((Object)(object)val != (Object)null && val.actualClientId == clientId) { return val; } } return null; } private static void PlayCue(ulong victimClientId) { try { PlayerControllerB val = ResolvePlayerByClientId(victimClientId); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.itemAudio == (Object)null)) { AudioClip[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.playerGrabSFX : null); if (array != null && array.Length != 0) { AudioClip val2 = array[Random.Range(0, array.Length)]; val.itemAudio.PlayOneShot(val2, 1f); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"ItemCurseAbility.PlayCue: {arg}"); } } } [HarmonyPatch] internal static class ItemCursePatches { [HarmonyPatch(typeof(GrabbableObject), "GrabItemOnClient")] [HarmonyPostfix] private static void OnGrabItemOnClient(GrabbableObject __instance) { try { if (!((Object)(object)__instance == (Object)null)) { ItemCurseAbility.GrabReportMsg.SendServer(((NetworkBehaviour)__instance).NetworkObjectId); } } catch (Exception arg) { Plugin.Log.LogError((object)$"ItemCursePatches.OnGrabItemOnClient: {arg}"); } } } public sealed class LeashAbility : AbilityBase { private const float RaycastDistance = 60f; private const float FollowOffset = 3f; private const float RepathInterval = 0.25f; private EnemyAI? _first; private EnemyAI? _second; private float _repathTimer; public bool IsLeashActive { get { if ((Object)(object)_first != (Object)null) { return (Object)(object)_second != (Object)null; } return false; } } public override void Tick() { try { if (AbilityBase.IsHostActive && AbilityBase.InGhostOrPursuer) { if (PursuerConfig.Leash.Pressed()) { HandleClick(); } TickFollow(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"LeashAbility.Tick: {arg}"); } } private void HandleClick() { EnemyAI val = FindEnemyAhead(); if (!((Object)(object)val == (Object)null)) { if ((Object)(object)_first == (Object)null) { _first = val; _second = null; } else if (val != _first) { _second = val; LeashGuiIcon.GetOrCreate().SetActive(active: true); } } } private void TickFollow() { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_first == (Object)null || (Object)(object)_second == (Object)null) { return; } if (IsGone(_first) || IsGone(_second)) { _first = null; _second = null; LeashGuiIcon.GetOrCreate().SetActive(active: false); return; } _repathTimer -= Time.deltaTime; if (!(_repathTimer > 0f)) { _repathTimer = 0.25f; Vector3 position = ((Component)_first).transform.position; Vector3 val = position - ((Component)_second).transform.position; if (!(((Vector3)(ref val)).sqrMagnitude < 0.0001f)) { ((Vector3)(ref val)).Normalize(); Vector3 val2 = position - val * 3f; _second.SetDestinationToPosition(val2, false); } } } private static bool IsGone(EnemyAI enemy) { if (!((Object)(object)enemy == (Object)null)) { return enemy.isEnemyDead; } return true; } private static EnemyAI? FindEnemyAhead() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) Transform activeAimTransform = PossessionController.ActiveAimTransform; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(activeAimTransform.position, activeAimTransform.forward, ref val, 60f, -1, (QueryTriggerInteraction)2)) { return null; } EnemyAI componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null || componentInParent.isEnemyDead) { return null; } return componentInParent; } } public sealed class LeashGuiIcon : MonoBehaviour { private static LeashGuiIcon? _instance; private bool _active; public static LeashGuiIcon GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("LethalPursuer_LeashGuiIcon"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); return _instance; } public void SetActive(bool active) { _active = active; } private void OnGUI() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!_active) { return; } try { GUI.Box(new Rect(12f, 12f, 28f, 28f), string.Empty); GUI.Label(new Rect(12f, 12f, 28f, 28f), "L"); } catch (Exception arg) { Plugin.Log.LogError((object)$"LeashGuiIcon.OnGUI: {arg}"); } } } public sealed class LightFlickerAbility : AbilityBase { public struct FlickerSignal { public byte Unused; } private const float HoldThresholdSeconds = 0.3f; private const float CooldownSeconds = 5f; private static readonly LNetworkMessage Msg; private readonly HoldTracker _tracker = new HoldTracker(0.3f); private float _cooldownUntil; static LightFlickerAbility() { Msg = LNetworkMessage.Connect("LethalPursuer.LightFlicker", (Action)null, (Action)null, (Action)null); try { Msg.OnClientReceived += delegate { RunFlicker(); }; } catch (Exception arg) { Plugin.Log.LogError((object)$"LightFlickerAbility static init: {arg}"); } } public override void Tick() { try { if (AbilityBase.IsHostActive && AbilityBase.InGhostOrPursuer && _tracker.Update(PursuerConfig.LightFlicker.Held(), Time.time) == HoldResult.Tap && !(Time.time < _cooldownUntil)) { _cooldownUntil = Time.time + 5f; RunFlicker(); Msg.SendClients(default(FlickerSignal)); } } catch (Exception arg) { Plugin.Log.LogError((object)$"LightFlickerAbility.Tick: {arg}"); } } private static void RunFlicker() { try { RoundManager instance = RoundManager.Instance; if (instance != null) { instance.FlickerLights(false, false); } } catch (Exception arg) { Plugin.Log.LogError((object)$"LightFlickerAbility.RunFlicker: {arg}"); } } } public sealed class MaskedDisguiseAbility : AbilityBase { public enum CosmeticSlot : byte { Flashlight, Walkie, Empty } public struct CosmeticMessage { public ulong TargetNetworkObjectId; public CosmeticSlot Slot; } private const float RaycastDistance = 40f; private const float NearestPlayerSearchRadius = 30f; private const float PropLocalYOffset = 1.1f; private const float PropLocalZOffset = 0.25f; private static readonly LNetworkMessage Msg; private static readonly Dictionary SlotByTarget; private static readonly Dictionary AttachedProps; static MaskedDisguiseAbility() { Msg = LNetworkMessage.Connect("LethalPursuer.MaskedDisguise", (Action)null, (Action)null, (Action)null); SlotByTarget = new Dictionary(); AttachedProps = new Dictionary(); try { Msg.OnClientReceived += delegate(CosmeticMessage msg) { ApplyCosmetic(msg.TargetNetworkObjectId, msg.Slot); }; } catch (Exception arg) { Plugin.Log.LogError((object)$"MaskedDisguiseAbility static init: {arg}"); } } public override void Tick() { try { if (AbilityBase.IsHostActive && AbilityBase.InGhostOrPursuer) { if (PursuerConfig.MaskedCycle.Pressed()) { CycleTargetAhead(); } TickCadenceSync(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"MaskedDisguiseAbility.Tick: {arg}"); } } private void CycleTargetAhead() { MaskedPlayerEnemy val = FindMaskedAhead(); if (!((Object)(object)val == (Object)null)) { ulong networkObjectId = ((NetworkBehaviour)val).NetworkObjectId; CosmeticSlot value; CosmeticSlot cosmeticSlot = (SlotByTarget.TryGetValue(networkObjectId, out value) ? Next(value) : CosmeticSlot.Flashlight); SlotByTarget[networkObjectId] = cosmeticSlot; ApplyCosmetic(networkObjectId, cosmeticSlot); Msg.SendClients(new CosmeticMessage { TargetNetworkObjectId = networkObjectId, Slot = cosmeticSlot }); } } private static CosmeticSlot Next(CosmeticSlot s) { return s switch { CosmeticSlot.Flashlight => CosmeticSlot.Walkie, CosmeticSlot.Walkie => CosmeticSlot.Empty, _ => CosmeticSlot.Flashlight, }; } private static MaskedPlayerEnemy? FindMaskedAhead() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) Transform activeAimTransform = PossessionController.ActiveAimTransform; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(activeAimTransform.position, activeAimTransform.forward, ref val, 40f, -1, (QueryTriggerInteraction)2)) { return null; } MaskedPlayerEnemy componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent(); if (!((Object)(object)componentInParent != (Object)null) || ((EnemyAI)componentInParent).isEnemyDead) { return null; } return componentInParent; } private static void ApplyCosmetic(ulong targetNetworkObjectId, CosmeticSlot slot) { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) try { MaskedPlayerEnemy val = ResolveMasked(targetNetworkObjectId); if (!((Object)(object)val == (Object)null)) { if (AttachedProps.TryGetValue(targetNetworkObjectId, out GameObject value) && (Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } AttachedProps.Remove(targetNetworkObjectId); object nameContains; switch (slot) { case CosmeticSlot.Empty: return; default: nameContains = "Walkie-talkie"; break; case CosmeticSlot.Flashlight: nameContains = "Flashlight"; break; } GameObject val2 = FindItemPrefab((string)nameContains); if ((Object)(object)val2 == (Object)null) { Plugin.Log.LogWarning((object)$"MaskedDisguiseAbility.ApplyCosmetic: no item prefab found for slot {slot}."); return; } Transform val3 = (((Object)(object)val.rendererLOD0 != (Object)null) ? ((Component)val.rendererLOD0).transform : ((Component)val).transform); GameObject val4 = Object.Instantiate(val2, val3); ((Object)val4).name = $"LethalPursuer_MaskedCosmetic_{slot}"; val4.transform.localPosition = new Vector3(0f, 1.1f, 0.25f); val4.transform.localRotation = Quaternion.identity; val4.transform.localScale = Vector3.one; StripToVisualOnly(val4); AttachedProps[targetNetworkObjectId] = val4; } } catch (Exception arg) { Plugin.Log.LogError((object)$"MaskedDisguiseAbility.ApplyCosmetic: {arg}"); } } private static void StripToVisualOnly(GameObject prop) { GrabbableObject[] componentsInChildren = prop.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.Destroy((Object)(object)componentsInChildren[i]); } Collider[] componentsInChildren2 = prop.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.Destroy((Object)(object)componentsInChildren2[i]); } Rigidbody[] componentsInChildren3 = prop.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.Destroy((Object)(object)componentsInChildren3[i]); } NetworkObject component = prop.GetComponent(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } } private static GameObject? FindItemPrefab(string nameContains) { List list = (((Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.allItemsList != (Object)null) ? StartOfRound.Instance.allItemsList.itemsList : null); if (list == null) { return null; } foreach (Item item in list) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.spawnPrefab == (Object)null) && !string.IsNullOrEmpty(item.itemName) && item.itemName.IndexOf(nameContains, StringComparison.OrdinalIgnoreCase) >= 0) { return item.spawnPrefab; } } return null; } private static MaskedPlayerEnemy? ResolveMasked(ulong networkObjectId) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.SpawnManager == null) { return null; } if (!singleton.SpawnManager.SpawnedObjects.TryGetValue(networkObjectId, out var value) || (Object)(object)value == (Object)null) { return null; } return ((Component)value).GetComponent(); } private static void TickCadenceSync() { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) List list = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.SpawnedEnemies : null); if (list == null) { return; } foreach (EnemyAI item in list) { MaskedPlayerEnemy val = (MaskedPlayerEnemy)(object)((item is MaskedPlayerEnemy) ? item : null); if (val != null && !((EnemyAI)val).isEnemyDead && !((Object)(object)((EnemyAI)val).creatureAnimator == (Object)null)) { PlayerControllerB val2 = FindNearestPlayer(((Component)val).transform.position); if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.thisController == (Object)null)) { Vector3 velocity = val2.thisController.velocity; float speed = Mathf.Clamp(((Vector3)(ref velocity)).magnitude / 4f, 0.5f, 2f); ((EnemyAI)val).creatureAnimator.speed = speed; } } } } private static PlayerControllerB? FindNearestPlayer(Vector3 position) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) PlayerControllerB[] array = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.allPlayerScripts : null); if (array == null) { return null; } PlayerControllerB result = null; float num = 900f; PlayerControllerB[] array2 = array; foreach (PlayerControllerB val in array2) { if (!((Object)(object)val == (Object)null) && !val.isPlayerDead) { Vector3 val2 = ((Component)val).transform.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude <= num) { num = sqrMagnitude; result = val; } } } return result; } } public sealed class PossessAbility : AbilityBase { private const float PossessRange = 40f; public override void Tick() { try { if (AbilityBase.IsHostActive && !((Object)(object)AbilityBase.Mode == (Object)null) && AbilityBase.Mode.Mode == PursuerMode.Ghost && !DirectorModeAbility.IsActive && Input.GetMouseButtonDown(0)) { EnemyAI val = FindEnemyAhead(); if (!((Object)(object)val == (Object)null) && !AbilityBase.Mode.EnterPursuer(val)) { Plugin.Log.LogWarning((object)("PossessAbility: failed to possess " + ((Object)val).name + " (missing NavMeshAgent or state mismatch).")); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"PossessAbility.Tick: {arg}"); } } private static EnemyAI? FindEnemyAhead() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) Transform cameraTransform = GhostCamera.GetOrCreate().CameraTransform; RaycastHit val = default(RaycastHit); if (!Physics.Raycast(cameraTransform.position, cameraTransform.forward, ref val, 40f, -1, (QueryTriggerInteraction)2)) { return null; } EnemyAI componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent(); if ((Object)(object)componentInParent == (Object)null || componentInParent.isEnemyDead) { return null; } return componentInParent; } } public sealed class PuppetWaypointAbility : AbilityBase { private const float RaycastDistance = 60f; private const float NavMeshSampleRadius = 4f; private const float ArrivalThreshold = 0.75f; private const string ClaimOwner = "Puppet"; private Vector3? _waypoint; public override void Tick() { try { if (!AbilityBase.IsHostActive) { return; } PossessionController instance = PossessionController.Instance; if ((Object)(object)instance == (Object)null || !instance.IsPossessing || (Object)(object)AbilityBase.Mode == (Object)null || AbilityBase.Mode.Mode != PursuerMode.Pursuer) { ClearWaypoint(instance); return; } if (Input.GetKeyDown((KeyCode)27)) { ClearWaypoint(instance); return; } if (PursuerConfig.PuppetWaypoint.Held() && Input.GetMouseButtonDown(0)) { TrySetWaypoint(instance); } DriveTowardsWaypoint(instance); } catch (Exception arg) { Plugin.Log.LogError((object)$"PuppetWaypointAbility.Tick: {arg}"); } } private void TrySetWaypoint(PossessionController pc) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (!pc.TryClaimPositionOverride("Puppet")) { return; } Transform cameraTransform = pc.CameraTransform; RaycastHit val = default(RaycastHit); if (Physics.Raycast(cameraTransform.position, cameraTransform.forward, ref val, 60f, -1, (QueryTriggerInteraction)1)) { NavMeshHit val2 = default(NavMeshHit); if (NavMesh.SamplePosition(((RaycastHit)(ref val)).point, ref val2, 4f, -1)) { _waypoint = ((NavMeshHit)(ref val2)).position; return; } Plugin.Log.LogWarning((object)"PuppetWaypointAbility: raycast hit point had no nearby NavMesh; waypoint not set."); pc.ReleasePositionOverride("Puppet"); } } private void DriveTowardsWaypoint(PossessionController pc) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) Vector3? waypoint = _waypoint; if (waypoint.HasValue) { Vector3 valueOrDefault = waypoint.GetValueOrDefault(); EnemyAI possessed = pc.Possessed; if ((Object)(object)possessed == (Object)null || (Object)(object)possessed.agent == (Object)null || !((Behaviour)possessed.agent).enabled || !possessed.agent.isOnNavMesh) { _waypoint = null; pc.ReleasePositionOverride("Puppet"); } else if (Vector3.Distance(((Component)possessed).transform.position, valueOrDefault) <= 0.75f) { _waypoint = null; pc.ReleasePositionOverride("Puppet"); } else { possessed.agent.SetDestination(valueOrDefault); possessed.SyncPositionToClients(); } } else { pc.ReleasePositionOverride("Puppet"); } } private void ClearWaypoint(PossessionController? pc) { _waypoint = null; pc?.ReleasePositionOverride("Puppet"); } } public sealed class SoundEmitterAbility : AbilityBase { public enum SoundType : byte { Footsteps, Breathing, ItemPing, DoorCreak } public struct PlaceMessage { public ulong EmitterId; public bool Remove; public Vector3 Position; public SoundType Type; } private static readonly LNetworkMessage Msg; private static readonly Dictionary ActiveEmitters; private readonly List _activeOrder = new List(); private ulong _nextId = 1uL; private bool _menuOpen; static SoundEmitterAbility() { Msg = LNetworkMessage.Connect("LethalPursuer.SoundEmitter", (Action)null, (Action)null, (Action)null); ActiveEmitters = new Dictionary(); try { Msg.OnClientReceived += delegate(PlaceMessage msg) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (msg.Remove) { ApplyRemove(msg.EmitterId); } else { ApplyPlace(msg.EmitterId, msg.Position, msg.Type); } }; } catch (Exception arg) { Plugin.Log.LogError((object)$"SoundEmitterAbility static init: {arg}"); } } public override void Tick() { try { if (!AbilityBase.IsHostActive || !AbilityBase.InGhostOrPursuer) { if (_menuOpen) { CloseMenu(); } return; } bool flag = PursuerConfig.SoundEmitter.Held(); if (flag && !_menuOpen) { _menuOpen = true; SoundEmitterRadialMenu.GetOrCreate().Show(); } else if (!flag && _menuOpen) { CloseMenu(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"SoundEmitterAbility.Tick: {arg}"); } } private void CloseMenu() { _menuOpen = false; SoundType? soundType = SoundEmitterRadialMenu.GetOrCreate().Hide(); if (soundType.HasValue) { PlaceEmitter(soundType.Value); } } private void PlaceEmitter(SoundType type) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) while (_activeOrder.Count >= Math.Max(1, PursuerConfig.MaxSoundEmitters)) { ulong emitterId = _activeOrder[0]; _activeOrder.RemoveAt(0); ApplyRemove(emitterId); Msg.SendClients(new PlaceMessage { EmitterId = emitterId, Remove = true }); } ulong num = _nextId++; Vector3 ghostPosition = AbilityBase.GhostPosition; _activeOrder.Add(num); ApplyPlace(num, ghostPosition, type); Msg.SendClients(new PlaceMessage { EmitterId = num, Position = ghostPosition, Type = type }); } private static void ApplyPlace(ulong emitterId, Vector3 position, SoundType type) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) try { if (!ActiveEmitters.ContainsKey(emitterId)) { GameObject val = new GameObject($"LethalPursuer_SoundEmitter_{emitterId}"); Object.DontDestroyOnLoad((Object)(object)val); val.transform.position = position; AudioSource val2 = val.AddComponent(); val2.clip = ResolveClip(type); val2.loop = true; val2.playOnAwake = false; val2.spatialBlend = 1f; val2.rolloffMode = (AudioRolloffMode)1; val2.maxDistance = Mathf.Max(1f, PursuerConfig.SoundEmitterRadius); val2.volume = 1f; if ((Object)(object)val2.clip != (Object)null) { val2.Play(); } ActiveEmitters[emitterId] = val; } } catch (Exception arg) { Plugin.Log.LogError((object)$"SoundEmitterAbility.ApplyPlace: {arg}"); } } private static void ApplyRemove(ulong emitterId) { try { if (ActiveEmitters.TryGetValue(emitterId, out GameObject value)) { ActiveEmitters.Remove(emitterId); if ((Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } } } catch (Exception arg) { Plugin.Log.LogError((object)$"SoundEmitterAbility.ApplyRemove: {arg}"); } } private static AudioClip? ResolveClip(SoundType type) { try { switch (type) { case SoundType.Footsteps: { FootstepSurface[] array2 = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.footstepSurfaces : null); if (array2 != null && array2.Length != 0) { AudioClip[] array3 = array2[0]?.clips; if (array3 != null && array3.Length > 0) { return array3[Random.Range(0, array3.Length)]; } } return null; } case SoundType.Breathing: { object result; if (!((Object)(object)HUDManager.Instance != (Object)null)) { result = null; } else { AudioSource breathingUnderwaterAudio = HUDManager.Instance.breathingUnderwaterAudio; result = ((breathingUnderwaterAudio != null) ? breathingUnderwaterAudio.clip : null); } return (AudioClip?)result; } case SoundType.ItemPing: { AudioClip[] array4 = (((Object)(object)StartOfRound.Instance != (Object)null) ? StartOfRound.Instance.playerGrabSFX : null); if (array4 != null && array4.Length != 0) { return array4[Random.Range(0, array4.Length)]; } return null; } case SoundType.DoorCreak: { AudioClip[] array = (((Object)(object)SoundManager.Instance != (Object)null) ? SoundManager.Instance.steelDoorOpenSFX : null); if (array != null && array.Length != 0) { return array[Random.Range(0, array.Length)]; } return null; } default: return null; } } catch (Exception arg) { Plugin.Log.LogError((object)$"SoundEmitterAbility.ResolveClip({type}): {arg}"); return null; } } } public sealed class SoundEmitterRadialMenu : MonoBehaviour { private static SoundEmitterRadialMenu? _instance; private static readonly (SoundEmitterAbility.SoundType Type, string Label)[] Options = new(SoundEmitterAbility.SoundType, string)[4] { (SoundEmitterAbility.SoundType.Footsteps, "Footsteps"), (SoundEmitterAbility.SoundType.ItemPing, "Item ping"), (SoundEmitterAbility.SoundType.Breathing, "Breathing"), (SoundEmitterAbility.SoundType.DoorCreak, "Door creak") }; private bool _visible; private int _hoveredIndex = -1; public static SoundEmitterRadialMenu GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("LethalPursuer_SoundEmitterRadialMenu"); Object.DontDestroyOnLoad((Object)val); _instance = val.AddComponent(); return _instance; } public void Show() { _visible = true; _hoveredIndex = -1; } public SoundEmitterAbility.SoundType? Hide() { _visible = false; if (_hoveredIndex < 0 || _hoveredIndex >= Options.Length) { return null; } return Options[_hoveredIndex].Type; } private void OnGUI() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) if (!_visible) { return; } try { float num = (float)Screen.width * 0.5f; float num2 = (float)Screen.height * 0.5f; Vector2 mousePosition = Event.current.mousePosition; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(mousePosition.x - num, mousePosition.y - num2); _hoveredIndex = -1; if (((Vector2)(ref val)).magnitude > 24f) { float num3 = Mathf.Atan2(0f - val.y, val.x) * 57.29578f; float num4 = ((90f - num3) % 360f + 360f) % 360f; float num5 = 360f / (float)Options.Length; float num6 = (num4 + num5 * 0.5f) % 360f; _hoveredIndex = Mathf.FloorToInt(num6 / num5) % Options.Length; } GUIStyle val2 = new GUIStyle(GUI.skin.box) { alignment = (TextAnchor)4, fontSize = 14 }; GUI.Box(new Rect(num - 60f, num2 - 20f, 120f, 40f), "Sound Emitter"); for (int i = 0; i < Options.Length; i++) { float num7 = 360f / (float)Options.Length; float num8 = (90f - (float)i * num7) * (MathF.PI / 180f); float num9 = num + Mathf.Cos(num8) * 90f; float num10 = num2 - Mathf.Sin(num8) * 90f; Color color = GUI.color; GUI.color = ((i == _hoveredIndex) ? Color.yellow : Color.white); GUI.Box(new Rect(num9 - 65f, num10 - 16f, 130f, 32f), Options[i].Label, val2); GUI.color = color; } } catch (Exception arg) { Plugin.Log.LogError((object)$"SoundEmitterRadialMenu.OnGUI: {arg}"); } } } public sealed class VentCoverAbility : AbilityBase { private readonly struct SealState { public readonly float UnsealAt; public readonly bool OriginalOccupied; public readonly float OriginalSpawnTime; public readonly bool OriginalVentIsOpen; public SealState(float unsealAt, bool originalOccupied, float originalSpawnTime, bool originalVentIsOpen) { UnsealAt = unsealAt; OriginalOccupied = originalOccupied; OriginalSpawnTime = originalSpawnTime; OriginalVentIsOpen = originalVentIsOpen; } } public struct VentSealMessage { public ulong VentNetworkObjectId; public bool Sealed; public bool OriginalOccupied; public float OriginalSpawnTime; public bool OriginalVentIsOpen; } private const float HoldThresholdSeconds = 0.35f; private const float RaycastDistance = 4f; private static readonly LNetworkMessage Msg; private readonly HoldTracker _tracker = new HoldTracker(0.35f); private readonly Dictionary _sealed = new Dictionary(); static VentCoverAbility() { Msg = LNetworkMessage.Connect("LethalPursuer.VentCover", (Action)null, (Action)null, (Action)null); try { Msg.OnClientReceived += delegate(VentSealMessage msg) { if (msg.Sealed) { ApplySeal(msg.VentNetworkObjectId); } else { ApplyUnseal(msg.VentNetworkObjectId, msg.OriginalOccupied, msg.OriginalSpawnTime, msg.OriginalVentIsOpen); } }; } catch (Exception arg) { Plugin.Log.LogError((object)$"VentCoverAbility static init: {arg}"); } } public override void Tick() { try { if (AbilityBase.IsHostActive && AbilityBase.InGhostOrPursuer) { if (_tracker.Update(PursuerConfig.Vent.Held(), Time.time) == HoldResult.Tap) { ToggleNearestVent(); } TickExpiry(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"VentCoverAbility.Tick: {arg}"); } } private void ToggleNearestVent() { EnemyVent val = FindNearestVent(); if (!((Object)(object)val == (Object)null)) { ulong networkObjectId = ((NetworkBehaviour)val).NetworkObjectId; if (_sealed.TryGetValue(networkObjectId, out var value)) { _sealed.Remove(networkObjectId); ApplyUnseal(networkObjectId, value.OriginalOccupied, value.OriginalSpawnTime, value.OriginalVentIsOpen); Msg.SendClients(new VentSealMessage { VentNetworkObjectId = networkObjectId, Sealed = false, OriginalOccupied = value.OriginalOccupied, OriginalSpawnTime = value.OriginalSpawnTime, OriginalVentIsOpen = value.OriginalVentIsOpen }); } else { SealState value2 = new SealState(Time.time + (float)PursuerConfig.VentSealDuration, val.occupied, val.spawnTime, val.ventIsOpen); _sealed[networkObjectId] = value2; ApplySeal(networkObjectId); Msg.SendClients(new VentSealMessage { VentNetworkObjectId = networkObjectId, Sealed = true }); } } } private void TickExpiry() { if (_sealed.Count == 0) { return; } List list = null; foreach (KeyValuePair item in _sealed) { if (!(Time.time < item.Value.UnsealAt)) { (list ?? (list = new List())).Add(item.Key); } } if (list == null) { return; } foreach (ulong item2 in list) { SealState sealState = _sealed[item2]; _sealed.Remove(item2); ApplyUnseal(item2, sealState.OriginalOccupied, sealState.OriginalSpawnTime, sealState.OriginalVentIsOpen); Msg.SendClients(new VentSealMessage { VentNetworkObjectId = item2, Sealed = false, OriginalOccupied = sealState.OriginalOccupied, OriginalSpawnTime = sealState.OriginalSpawnTime, OriginalVentIsOpen = sealState.OriginalVentIsOpen }); } } private static EnemyVent? FindNearestVent() { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) EnemyVent[] array = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.allEnemyVents : null); if (array == null || array.Length == 0) { return null; } Vector3 ghostPosition = AbilityBase.GhostPosition; EnemyVent result = null; float num = 16f; EnemyVent[] array2 = array; foreach (EnemyVent val in array2) { if (!((Object)(object)val == (Object)null)) { Vector3 val2 = ((Component)val).transform.position - ghostPosition; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude <= num) { num = sqrMagnitude; result = val; } } } return result; } private static void ApplySeal(ulong ventNetworkObjectId) { try { EnemyVent val = ResolveVent(ventNetworkObjectId); if (!((Object)(object)val == (Object)null)) { RoundManager instance = RoundManager.Instance; float spawnTime = (((Object)(object)instance != (Object)null) ? (instance.timeScript.currentDayTime + instance.timeScript.totalTime) : float.MaxValue); val.ventIsOpen = true; val.occupied = true; val.spawnTime = spawnTime; } } catch (Exception arg) { Plugin.Log.LogError((object)$"VentCoverAbility.ApplySeal: {arg}"); } } private static void ApplyUnseal(ulong ventNetworkObjectId, bool originalOccupied, float originalSpawnTime, bool originalVentIsOpen) { try { EnemyVent val = ResolveVent(ventNetworkObjectId); if (!((Object)(object)val == (Object)null)) { val.occupied = originalOccupied; val.spawnTime = originalSpawnTime; val.ventIsOpen = originalVentIsOpen; } } catch (Exception arg) { Plugin.Log.LogError((object)$"VentCoverAbility.ApplyUnseal: {arg}"); } } private static EnemyVent? ResolveVent(ulong ventNetworkObjectId) { NetworkManager singleton = NetworkManager.Singleton; if ((Object)(object)singleton == (Object)null || singleton.SpawnManager == null) { return null; } if (!singleton.SpawnManager.SpawnedObjects.TryGetValue(ventNetworkObjectId, out var value) || (Object)(object)value == (Object)null) { return null; } return ((Component)value).GetComponent(); } } public sealed class VentHideAbility : AbilityBase { private const float HoldThresholdSeconds = 0.25f; private const float VentSearchRadius = 5f; private const float PeekDistance = 1.2f; private const float PeekLerpSpeed = 6f; private const string ClaimOwner = "VentHide"; private readonly HoldTracker _tracker = new HoldTracker(0.25f); private VentInteriorCamera? _cam; private bool _hidden; private EnemyVent? _vent; private bool _preHideAgentEnabled; private float _peekBlend; private PossessionController? _subscribedTo; public override void Tick() { try { if (!AbilityBase.IsHostActive) { return; } PossessionController instance = PossessionController.Instance; EnsureSubscribed(instance); if ((Object)(object)instance == (Object)null || !instance.IsPossessing || (Object)(object)AbilityBase.Mode == (Object)null || AbilityBase.Mode.Mode != PursuerMode.Pursuer) { if (_hidden) { BurstOut(instance); } return; } HoldResult holdResult = _tracker.Update(PursuerConfig.Vent.Held(), Time.time); if (!_hidden && holdResult == HoldResult.HoldTriggered) { TryEnterVent(instance); } else if (_hidden) { TickHidden(instance); } } catch (Exception arg) { Plugin.Log.LogError((object)$"VentHideAbility.Tick: {arg}"); } } private void EnsureSubscribed(PossessionController? pc) { if (!((Object)(object)pc == (Object)null) && _subscribedTo != pc) { pc.Released += CleanUpOnRelease; _subscribedTo = pc; } } private void CleanUpOnRelease() { if (!_hidden) { return; } try { _hidden = false; _cam?.Deactivate(); _vent = null; _peekBlend = 0f; } catch (Exception arg) { Plugin.Log.LogError((object)$"VentHideAbility.CleanUpOnRelease: {arg}"); } } private void TryEnterVent(PossessionController pc) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) EnemyAI possessed = pc.Possessed; if ((Object)(object)possessed == (Object)null) { return; } EnemyVent val = FindNearestVent(((Component)possessed).transform.position); if ((Object)(object)val == (Object)null || (Object)(object)val.floorNode == (Object)null || !pc.TryClaimPositionOverride("VentHide")) { return; } try { _vent = val; _preHideAgentEnabled = (Object)(object)possessed.agent != (Object)null && ((Behaviour)possessed.agent).enabled; if ((Object)(object)possessed.agent != (Object)null && ((Behaviour)possessed.agent).enabled) { ((Behaviour)possessed.agent).enabled = false; } ((Component)possessed).transform.position = val.floorNode.position; ((Component)possessed).transform.rotation = val.floorNode.rotation; possessed.inSpecialAnimation = true; if ((Object)(object)possessed.creatureAnimator != (Object)null) { possessed.creatureAnimator.SetBool("inSpawningAnimation", true); } pc.SuppressFollowCam(); _hidden = true; _peekBlend = 0f; if (_cam == null) { _cam = VentInteriorCamera.GetOrCreate(); } _cam.Activate(val.floorNode); possessed.SyncPositionToClients(); } catch (Exception arg) { Plugin.Log.LogError((object)$"VentHideAbility.TryEnterVent: {arg}"); pc.ReleasePositionOverride("VentHide"); _hidden = false; _vent = null; } } private void TickHidden(PossessionController pc) { if ((Object)(object)pc.Possessed == (Object)null || (Object)(object)_vent == (Object)null) { BurstOut(pc); return; } if (Input.GetMouseButtonDown(0)) { BurstOut(pc); return; } float num = (PursuerConfig.PuppetWaypoint.Held() ? 1f : 0f); _peekBlend = Mathf.MoveTowards(_peekBlend, num, 6f * Time.deltaTime); _cam?.SetPeek(_peekBlend, 1.2f); } private void BurstOut(PossessionController? pc) { try { _hidden = false; _cam?.Deactivate(); EnemyAI val = (((Object)(object)pc != (Object)null) ? pc.Possessed : null); if ((Object)(object)val != (Object)null) { val.inSpecialAnimation = false; if ((Object)(object)val.creatureAnimator != (Object)null) { val.creatureAnimator.SetBool("inSpawningAnimation", false); } if ((Object)(object)val.agent != (Object)null) { ((Behaviour)val.agent).enabled = _preHideAgentEnabled; } val.SyncPositionToClients(); } if ((Object)(object)pc != (Object)null) { pc.ReleasePositionOverride("VentHide"); pc.ResumeFollowCam(); } try { EnemyVent? vent = _vent; if (vent != null) { vent.OpenVentClientRpc(); } } catch (Exception arg) { Plugin.Log.LogError((object)$"VentHideAbility.BurstOut OpenVentClientRpc: {arg}"); } _vent = null; } catch (Exception arg2) { Plugin.Log.LogError((object)$"VentHideAbility.BurstOut: {arg2}"); } } private static EnemyVent? FindNearestVent(Vector3 position) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) EnemyVent[] array = (((Object)(object)RoundManager.Instance != (Object)null) ? RoundManager.Instance.allEnemyVents : null); if (array == null || array.Length == 0) { return null; } EnemyVent result = null; float num = 25f; EnemyVent[] array2 = array; foreach (EnemyVent val in array2) { if (!((Object)(object)val == (Object)null) && !((Object)(object)val.floorNode == (Object)null)) { Vector3 val2 = val.floorNode.position - position; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (sqrMagnitude <= num) { num = sqrMagnitude; result = val; } } } return result; } } public sealed class VentInteriorCamera : MonoBehaviour { private static VentInteriorCamera? _instance; private Camera _cam; private Transform? _anchor; private Vector3 _basePosition; private Quaternion _baseRotation; public static VentInteriorCamera GetOrCreate() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if ((Object)(object)_instance != (Object)null) { return _instance; } GameObject val = new GameObject("LethalPursuer_VentInteriorCamera"); Object.DontDestroyOnLoad((Object)(object)val); _instance = val.AddComponent(); _instance._cam = val.AddComponent(); _instance._cam.cullingMask = -1; _instance._cam.nearClipPlane = 0.05f; ((Behaviour)_instance._cam).enabled = false; return _instance; } public void Activate(Transform anchor) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) _anchor = anchor; _basePosition = anchor.position + Vector3.up * 0.5f; _baseRotation = anchor.rotation; ((Component)_cam).transform.SetPositionAndRotation(_basePosition, _baseRotation); ((Behaviour)_cam).enabled = true; PlayerControllerB val = StartOfRound.Instance?.localPlayerController; if ((Object)(object)val != (Object)null && (Object)(object)val.gameplayCamera != (Object)null) { ((Behaviour)val.gameplayCamera).enabled = false; } } public void Deactivate() { ((Behaviour)_cam).enabled = false; _anchor = null; } public void SetPeek(float blend01, float peekDistance) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_anchor == (Object)null)) { Vector3 val = Vector3.Lerp(_basePosition, _basePosition + _baseRotation * Vector3.forward * peekDistance, blend01); ((Component)_cam).transform.SetPositionAndRotation(val, _baseRotation); } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }