Decompiled source of LethalPursuer v1.0.0

LethalPursuer.dll

Decompiled 6 hours ago
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("LethalPursuer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+96bc9575a36f3de16bb18576f9e796cc4bf9a404")]
[assembly: AssemblyProduct("LethalPursuer")]
[assembly: AssemblyTitle("LethalPursuer")]
[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("LethalPursuer", "LethalPursuer", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("LethalPursuer");

		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)"LethalPursuer 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 = "LethalPursuer";

		public const string PLUGIN_NAME = "LethalPursuer";

		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<PursuerHud>();
			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<MonsterCamera>();
			_instance.BuildCamera();
			return _instance;
		}

		private void BuildCamera()
		{
			_cam = ((Component)this).gameObject.AddComponent<Camera>();
			_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<PossessionController>();
			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<NavMeshAgent>();
				}
				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<TransformAndName> 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<PlayerControllerB>() : 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<PresenceMessage>? _presence;

		public static void Initialize()
		{
			_presence = LNetworkMessage<PresenceMessage>.Connect("LethalPursuer.Presence", (Action<PresenceMessage, ulong>)null, (Action<PresenceMessage>)null, (Action<PresenceMessage, ulong>)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<GhostCamera>();
			_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<Camera>();
			_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<Light>();
			_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<IPursuerAbility> _abilities = new List<IPursuerAbility>();

		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<InputRouter>();
			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<ModeManager>();
			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<ulong> _ghosting = new HashSet<ulong>();

		private readonly HashSet<ulong> _phantom = new HashSet<ulong>();

		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<KeyCode>((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<string>("Keybinds", "ghost_toggle_key", "F1", "Toggle Ghost mode.").Value;
			_keyPhantom = cfg.Bind<string>("Keybinds", "phantom_state_key", "F5", "Toggle Phantom State.").Value;
			_keyMonsterSwap = cfg.Bind<string>("Keybinds", "monster_swap_key", "F2", "Exit current monster back to Ghost.").Value;
			_keySound = cfg.Bind<string>("Keybinds", "sound_emitter_key", "Z", "Sound emitter radial menu.").Value;
			_keyFlicker = cfg.Bind<string>("Keybinds", "light_flicker_key", "X", "Light flicker (tap) / blackout (hold).").Value;
			_keyDoor = cfg.Bind<string>("Keybinds", "door_lock_key", "C", "Door lock.").Value;
			_keyBlip = cfg.Bind<string>("Keybinds", "fake_blip_key", "V", "Fake radar blip.").Value;
			_keyVoice = cfg.Bind<string>("Keybinds", "ghost_voice_key", "T", "Ghost voice push-to-talk.").Value;
			_keyMasked = cfg.Bind<string>("Keybinds", "masked_cycle_item_key", "G", "Cycle Masked held-item cosmetic.").Value;
			_keyLeash = cfg.Bind<string>("Keybinds", "leash_key", "L", "Leash mode.").Value;
			_keyBreaker = cfg.Bind<string>("Keybinds", "breaker_key", "B", "Breaker panel remote.").Value;
			_keyVent = cfg.Bind<string>("Keybinds", "vent_key", "N", "Vent cover (tap) / vent hide (hold).").Value;
			_keyCurse = cfg.Bind<string>("Keybinds", "item_curse_key", "I", "Item curse.").Value;
			_keyPuppet = cfg.Bind<string>("Keybinds", "puppet_waypoint_button", "Mouse1", "AI puppet waypoint button.").Value;
			_keyDirector = cfg.Bind<string>("Keybinds", "director_mode_key", "F3", "Director mode (deferred).").Value;
			_keyCountdown = cfg.Bind<string>("Keybinds", "countdown_toggle_key", "F4", "Countdown toggle (deferred).").Value;
			GhostVoiceEnabled = cfg.Bind<bool>("Toggles", "ghost_voice_enabled", true, "Enable ghost voice.").Value;
			GhostVoiceDistortion = cfg.Bind<bool>("Toggles", "ghost_voice_distortion", true, "Distort ghost voice.").Value;
			AsymmetricMode = cfg.Bind<bool>("Toggles", "asymmetric_mode", false, "Designate a non-host as ghost (deferred).").Value;
			BlackoutRequiresBreakerReset = cfg.Bind<bool>("Toggles", "blackout_requires_breaker_reset", true, "Blackout stays until breaker reset.").Value;
			GhostPlayerIndex = cfg.Bind<int>("Ghost", "ghost_player_index", 0, "Ghost player index (deferred).").Value;
			GhostFlySpeed = cfg.Bind<float>("Ghost", "ghost_fly_speed", 12f, "Ghost free-fly camera speed (m/s).").Value;
			CountdownDuration = cfg.Bind<int>("Durations", "countdown_duration", 240, "Countdown seconds (deferred).").Value;
			DoorLockDuration = cfg.Bind<int>("Durations", "door_lock_duration", 15, "Door lock seconds.").Value;
			FakeBlipDuration = cfg.Bind<int>("Durations", "fake_blip_duration", 30, "Fake blip seconds.").Value;
			VentSealDuration = cfg.Bind<int>("Durations", "vent_seal_duration", 20, "Vent seal seconds.").Value;
			ItemCurseAggroDuration = cfg.Bind<int>("Durations", "item_curse_aggro_duration", 5, "Item curse aggro seconds.").Value;
			SpawnBudgetRechargeInterval = cfg.Bind<int>("Durations", "spawn_budget_recharge_interval", 60, "Spawn budget recharge seconds (deferred).").Value;
			SpawnBudgetMax = cfg.Bind<int>("Limits", "spawn_budget_max", 5, "Max spawn budget (deferred).").Value;
			SoundEmitterRadius = cfg.Bind<float>("Limits", "sound_emitter_radius", 10f, "Sound emitter 3D falloff radius (m).").Value;
			MaxSoundEmitters = cfg.Bind<int>("Limits", "max_sound_emitters", 3, "Max active sound emitters.").Value;
			MaxFakeBlips = cfg.Bind<int>("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<CountdownMessage> Msg;

		private bool _hostToggledOn;

		public static CountdownAbility? Instance { get; private set; }

		public CountdownAbility()
		{
			Instance = this;
		}

		static CountdownAbility()
		{
			Msg = LNetworkMessage<CountdownMessage>.Connect("LethalPursuer.Countdown", (Action<CountdownMessage, ulong>)null, (Action<CountdownMessage>)null, (Action<CountdownMessage, ulong>)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<CountdownOverlay>();
			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<EnemyType> _cycle = new List<EnemyType>();

		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<ulong> Msg;

		private readonly Dictionary<ulong, float> _expiryByDoorId = new Dictionary<ulong, float>();

		static DoorLockAbility()
		{
			Msg = LNetworkMessage<ulong>.Connect("LethalPursuer.DoorLock", (Action<ulong, ulong>)null, (Action<ulong>)null, (Action<ulong, ulong>)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<ulong> list = null;
			foreach (KeyValuePair<ulong, float> item in _expiryByDoorId)
			{
				if (!(Time.time < item.Value))
				{
					(list ?? (list = new List<ulong>())).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<DoorLock>();
		}

		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<DoorLock>();
		}
	}
	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<BlipMessage> Msg;

		private static readonly Dictionary<ulong, GameObject> ActiveBlipObjects;

		private readonly Dictionary<ulong, float> _expiryById = new Dictionary<ulong, float>();

		private readonly List<ulong> _activeOrder = new List<ulong>();

		private bool _toggledOn;

		private ulong _nextId = 1uL;

		static FakeRadarBlipAbility()
		{
			FakePlayerLabels = new string[6] { "??? #1", "??? #2", "??? #3", "SIGNAL LOST", "UNKNOWN", "static" };
			Msg = LNetworkMessage<BlipMessage>.Connect("LethalPursuer.FakeBlip", (Action<BlipMessage, ulong>)null, (Action<BlipMessage>)null, (Action<BlipMessage, ulong>)null);
			ActiveBlipObjects = new Dictionary<ulong, GameObject>();
			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<ulong> list = null;
			foreach (KeyValuePair<ulong, float> item in _expiryById)
			{
				if (!(Time.time < item.Value))
				{
					(list ?? (list = new List<ulong>())).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<ManualCameraRenderer>();
					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<ManualCameraRenderer>();
				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<VoiceStateMessage> Msg;

		private bool _wasHeld;

		static GhostVoiceAbility()
		{
			Msg = LNetworkMessage<VoiceStateMessage>.Connect("LethalPursuer.GhostVoice", (Action<VoiceStateMessage, ulong>)null, (Action<VoiceStateMessage>)null, (Action<VoiceStateMessage, ulong>)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<GhostVoiceRelocator>();
			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<AudioLowPassFilter>();
			}
		}

		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<ulong> GrabReportMsg;

		private static readonly LNetworkMessage<CueMessage> CueMsg;

		internal static ulong? CursedItemNetworkObjectId;

		private static readonly Dictionary<EnemyAI, float> ActiveAggro;

		private static readonly Dictionary<EnemyAI, PlayerControllerB> AggroVictim;

		static ItemCurseAbility()
		{
			GrabReportMsg = LNetworkMessage<ulong>.Connect("LethalPursuer.ItemCurse.GrabReport", (Action<ulong, ulong>)null, (Action<ulong>)null, (Action<ulong, ulong>)null);
			CueMsg = LNetworkMessage<CueMessage>.Connect("LethalPursuer.ItemCurse.Cue", (Action<CueMessage, ulong>)null, (Action<CueMessage>)null, (Action<CueMessage, ulong>)null);
			ActiveAggro = new Dictionary<EnemyAI, float>();
			AggroVictim = new Dictionary<EnemyAI, PlayerControllerB>();
			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<GrabbableObject>();
		}

		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<EnemyAI> 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<EnemyAI> list = null;
			foreach (KeyValuePair<EnemyAI, float> item in ActiveAggro)
			{
				EnemyAI key = item.Key;
				PlayerControllerB value;
				if ((Object)(object)key == (Object)null || key.isEnemyDead)
				{
					(list ?? (list = new List<EnemyAI>())).Add(key);
				}
				else if (Time.time >= item.Value)
				{
					(list ?? (list = new List<EnemyAI>())).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