Decompiled source of Spectate v1.3.0

Spectate.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Threading.Tasks;
using AIGraph;
using Agents;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using CullingSystem;
using Globals;
using HarmonyLib;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using Microsoft.CodeAnalysis;
using Player;
using SNetwork;
using Spectate.Config;
using Spectate.UI;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("Spectate")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Spectate")]
[assembly: AssemblyTitle("Spectate")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.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;
		}
	}
}
namespace Spectate
{
	public class AgentTarget
	{
		public readonly PlayerAgent Agent;

		public Transform Transform => ((Component)Agent).transform;

		public FPSCamera? FPSCamera => Agent.FPSCamera;

		public FirstPersonItemHolder? FPHolder => Agent.FPItemHolder;

		public PlayerLocomotion Locomotion => Agent.Locomotion;

		public PLOC_State LocomotionStateEnum => Locomotion.m_currentStateEnum;

		public bool IsDowned => (int)LocomotionStateEnum == 7;

		public SNet_Player SAgent => Agent.Owner;

		public bool IsBot => SAgent.IsBot;

		public bool IsLocal => SAgent.IsLocal;

		public Dam_PlayerDamageBase Damage => Agent.Damage;

		public float Health => ((Dam_SyncedDamageBase)Damage).Health / ((Dam_SyncedDamageBase)Damage).HealthMax;

		public float HealthPercent => Health * 100f;

		public float Infection => Damage.Infection;

		public float InfectionPercent => Infection * 100f;

		public PlayerInventoryBase? Inventory => Agent.Inventory;

		public InventorySlot? ActiveItemSlot
		{
			get
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				PlayerInventoryBase? inventory = Inventory;
				if (inventory == null)
				{
					return null;
				}
				return inventory.WieldedSlot;
			}
		}

		public AIG_CourseNode? CourseNode => ((Agent)Agent).CourseNode;

		public AgentTarget(PlayerAgent agent)
		{
			Agent = agent;
		}

		public void SetRigActive(bool active)
		{
			Util.SetTargetActiveIfDiff(((Component)Agent.PlayerSyncModel).gameObject, active);
		}

		public static bool operator ==(AgentTarget? a, AgentTarget? b)
		{
			if ((object)a == b)
			{
				return true;
			}
			if ((object)a == null)
			{
				if ((object)b != null)
				{
					return (Object)(object)b.Agent == (Object)null;
				}
				return true;
			}
			if ((object)b == null)
			{
				return (Object)(object)a.Agent == (Object)null;
			}
			return (Object)(object)a.Agent == (Object)(object)b.Agent;
		}

		public static bool operator !=(AgentTarget? a, AgentTarget? b)
		{
			return !(a == b);
		}

		public override bool Equals(object? other)
		{
			if (other is AgentTarget agentTarget)
			{
				return this == agentTarget;
			}
			return false;
		}

		public override int GetHashCode()
		{
			return ((Object)Agent).GetHashCode();
		}
	}
	public class InputHelper
	{
		public static int GetAlphaNumKeyDown()
		{
			int result = -1;
			for (int i = 0; i < 10; i++)
			{
				if (Input.GetKeyDown((KeyCode)(48 + i)))
				{
					result = i;
					break;
				}
			}
			return result;
		}

		public static bool OnlyModifies(KeyCode mod, KeyCode altMod = 0, KeyCode nonMod = 0)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Invalid comparison between Unknown and I4
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Invalid comparison between Unknown and I4
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Invalid comparison between Unknown and I4
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Invalid comparison between Unknown and I4
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between Unknown and I4
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Invalid comparison between Unknown and I4
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Invalid comparison between Unknown and I4
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Invalid comparison between Unknown and I4
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Invalid comparison between Unknown and I4
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Invalid comparison between Unknown and I4
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Invalid comparison between Unknown and I4
			bool num = Input.GetKey(mod) || Input.GetKey(altMod) || Input.GetKeyDown(nonMod);
			bool flag = false;
			if ((int)mod != 304 && (int)mod != 303 && (int)altMod != 304 && (int)altMod != 303)
			{
				flag |= Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
			}
			if ((int)mod != 306 && (int)mod != 305 && (int)altMod != 306 && (int)altMod != 305)
			{
				flag |= Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305);
			}
			if ((int)mod != 308 && (int)mod != 307 && (int)altMod != 308 && (int)altMod != 307)
			{
				flag |= Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307);
			}
			if (num)
			{
				return !flag;
			}
			return false;
		}

		public static Vector2 GetMouseDelta()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			return new Vector2(InputMapper.GetAxis.Invoke((InputAction)3, (eFocusState)0), InputMapper.GetAxis.Invoke((InputAction)4, (eFocusState)0));
		}
	}
	internal static class Logger
	{
		private static ManualLogSource _mLogSource;

		public static bool Ready => _mLogSource != null;

		public static void Setup()
		{
			_mLogSource = Logger.CreateLogSource("io.takina.gtfo.Spectate");
		}

		public static void SetupFromInit(ManualLogSource logSource)
		{
			_mLogSource = logSource;
		}

		private static string Format(object data)
		{
			return data.ToString();
		}

		public static void Debug(object msg)
		{
			if (ConfigMgr.Debug)
			{
				_mLogSource.LogInfo((object)("[DEBUG] " + Format(msg)));
			}
			else
			{
				_mLogSource.LogDebug((object)Format(msg));
			}
		}

		public static void Info(object msg)
		{
			_mLogSource.LogInfo((object)Format(msg));
		}

		public static void Warn(object msg)
		{
			_mLogSource.LogWarning((object)Format(msg));
		}

		public static void Error(object msg)
		{
			_mLogSource.LogError((object)Format(msg));
		}

		public static void Fatal(object msg)
		{
			_mLogSource.LogFatal((object)Format(msg));
		}
	}
	[HarmonyPatch]
	public class Patch
	{
		[HarmonyPatch(typeof(GS_ReadyToStopElevatorRide), "Enter")]
		[HarmonyPostfix]
		public static void GS_ReadyToStopElevatorRide_Enter(GS_ReadyToStopElevatorRide __instance)
		{
			SpectateCam.Instance?.Load();
		}

		[HarmonyPatch(typeof(LocalPlayerAgent), "get_CamPos")]
		[HarmonyPrefix]
		public static bool PlayerAgent_get_CamPos(ref Vector3 __result)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			SpectateCam? instance = SpectateCam.Instance;
			if (instance != null && instance.Active)
			{
				__result = SpectateCam.Instance.CameraPos;
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(RundownManager), "EndGameSession")]
		[HarmonyPrefix]
		private static void EndGameSession()
		{
			SpectateCam.Instance?.Unload();
		}

		[HarmonyPatch(typeof(SNet_SessionHub), "LeaveHub")]
		[HarmonyPrefix]
		private static void LeaveHub()
		{
			SpectateCam.Instance?.Unload();
		}

		[HarmonyPatch(typeof(PUI_LocalPlayerStatus), "SetDamageAnim")]
		[HarmonyPrefix]
		public static bool PUI_LocalPlayerStatus_SetDamageAnim(PUI_LocalPlayerStatus __instance)
		{
			SpectateCam? instance = SpectateCam.Instance;
			if (instance != null && instance.Active)
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(PlayerAgent), "GetDetectionMod")]
		[HarmonyPrefix]
		public static bool PlayerAgent_GetDetectionMod(PlayerAgent __instance, ref Vector3 dir, float distance)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			SpectateCam? instance = SpectateCam.Instance;
			if (instance != null && instance.Active)
			{
				dir = SpectateCam.Instance.LastCamDir;
			}
			return true;
		}

		[HarmonyPatch(typeof(PlayerSync), "SendLocomotion")]
		[HarmonyPrefix]
		public static bool PlayerSync_SendLocomotion(PlayerSync __instance, PLOC_State state, Vector3 pos, ref Vector3 lookDir, float velFwd, float velRight)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			SpectateCam? instance = SpectateCam.Instance;
			if (instance != null && instance.Active)
			{
				lookDir = SpectateCam.Instance.LastCamDir;
			}
			return true;
		}

		[HarmonyPatch(typeof(PLOC_Downed), "Enter")]
		[HarmonyPostfix]
		public static void PLOC_Downed_Enter(PLOC_Downed __instance)
		{
			if (((Agent)((PLOC_Base)__instance).m_owner).IsLocallyOwned && !((Object)(object)SpectateCam.Instance == (Object)null) && ConfigMgr.SwitchOnDeath && !SpectateCam.Instance.Active)
			{
				SpectateCam.Instance.Attach();
			}
		}

		[HarmonyPatch(typeof(PLOC_Downed), "Exit")]
		[HarmonyPostfix]
		public static void PLOC_Downed_Exit(PLOC_Downed __instance)
		{
			if (((Agent)((PLOC_Base)__instance).m_owner).IsLocallyOwned && !((Object)(object)SpectateCam.Instance == (Object)null) && SpectateCam.Instance.Active)
			{
				SpectateCam.Instance.Detach();
			}
		}
	}
	[BepInPlugin("io.takina.gtfo.Spectate", "Spectate", "1.3.0")]
	public class Plugin : BasePlugin
	{
		public const string NAME = "Spectate";

		public const string GUID = "io.takina.gtfo.Spectate";

		public const string VERSION = "1.3.0";

		public static GameObject? PluginObject;

		public event Action? OnManagersSetup;

		public override void Load()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			Logger.Setup();
			Logger.Info("Spectate [io.takina.gtfo.Spectate @ 1.3.0]");
			Logger.Info("Patching...");
			Harmony val = new Harmony("io.takina.gtfo.Spectate");
			ConfigMgr.Process();
			ClassInjector.RegisterTypeInIl2Cpp<SpectateCam>();
			ClassInjector.RegisterTypeInIl2Cpp<SpectateUI>();
			OnManagersSetup += Initialize;
			Global.OnAllManagersSetup += Action.op_Implicit(this.OnManagersSetup);
			val.PatchAll(typeof(Patch));
			Logger.Info("Finished Patching");
		}

		private void Initialize()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			Logger.Debug("Initializing Plugin GameObject...");
			PluginObject = new GameObject("io.takina.gtfo.Spectate");
			Object.DontDestroyOnLoad((Object)(object)PluginObject);
			PluginObject.AddComponent<SpectateCam>();
			PluginObject.AddComponent<SpectateUI>();
		}

		public override bool Unload()
		{
			ConfigMgr.WriteConfigIfDirty();
			return true;
		}
	}
	public class SpectateCam : MonoBehaviour
	{
		private bool _wasActive;

		private int _lastTargetPlayerIdx = -1;

		private bool _freecam = ConfigMgr.DefaultFreecamView;

		private bool _freecamFollow = ConfigMgr.AutoTransitionToFollowView;

		private float _freeLookReturnTimer;

		public Vector3 LastCamDir = Vector3.forward;

		private float _pitch = ConfigMgr.CameraPitchAngleDeg;

		private float _yaw;

		private float _pitchTarget = ConfigMgr.CameraPitchAngleDeg;

		private float _yawTarget;

		private float _eyeY;

		private float _eyeYTarget;

		private Vector3 _eyeXZ = Vector3.zero;

		private Vector3 _eyeXZTarget = Vector3.zero;

		private Vector3 _eyePosComputed = Vector3.zero;

		public const float DefaultCameraYPositionLerpGain = 11f;

		public const float DefaultCameraXZPositionLerpGain = 15f;

		public const float DefaultCameraLerpGain = 6f;

		public const float DefaultOrbitCenterVerticalOffset = 0.325f;

		public const float DefaultPitchAngleDeg = -18.75f;

		public const float DefaultDistanceFromEye = 0.625f;

		public const float DefaultScrollSensitivity = 0.5f;

		public const float DefaultFreecamSensitivity = 1f;

		public const float OrbitCenterVerticalOffsetMin = -1f;

		public const float OrbitCenterVerticalOffsetMax = 5f;

		public const float DistanceMin = 0.1f;

		public const float DistanceMax = 5f;

		public const float PitchAngleDegMin = -89f;

		public const float PitchAngleDegMax = 89f;

		private AgentTarget? _self;

		private AgentTarget? _target;

		public static SpectateCam? Instance { get; set; }

		public bool SelfReady
		{
			get
			{
				if (_self != null)
				{
					return (Object)(object)_self.FPSCamera != (Object)null;
				}
				return false;
			}
		}

		public bool TargetReady => _target != null;

		public bool Active { get; private set; }

		public bool Freecam => _freecam;

		public Vector3 CameraPos
		{
			get
			{
				//IL_0054: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_004d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				if (!Active)
				{
					AgentTarget? self = _self;
					Vector3? obj;
					if ((object)self == null)
					{
						obj = null;
					}
					else
					{
						FPSCamera? fPSCamera = self.FPSCamera;
						obj = ((fPSCamera != null) ? new Vector3?(fPSCamera.Position) : null);
					}
					return (Vector3)(((??)obj) ?? Vector3.zero);
				}
				return _eyePosComputed;
			}
		}

		public AgentTarget? Self
		{
			get
			{
				if (_self == null)
				{
					_self = new AgentTarget(PlayerManager.GetLocalPlayerAgent());
				}
				return _self;
			}
		}

		public AgentTarget? Target => _target;

		public event Action? OnActive;

		public SpectateCam(IntPtr ptr)
			: base(ptr)
		{
		}//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)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)


		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Instance = this;
			Object.DontDestroyOnLoad((Object)(object)this);
		}

		public bool Load()
		{
			PlayerAgent localPlayerAgent = PlayerManager.GetLocalPlayerAgent();
			if ((Object)(object)localPlayerAgent == (Object)null)
			{
				Logger.Error("SpectateCam: Failed to load - local player agent is null");
				return false;
			}
			_self = new AgentTarget(localPlayerAgent);
			return (Object)(object)_self.FPSCamera != (Object)null;
		}

		public bool Unload()
		{
			Active = false;
			_self = null;
			_target = null;
			return true;
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		public void SetTarget(PlayerAgent agent)
		{
			_target = new AgentTarget(agent);
		}

		public bool Attach()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			if (Active)
			{
				return true;
			}
			if (!SelfReady && !Load())
			{
				Logger.Error("SpectateCam: Attach failed - self not ready and cannot be loaded");
				return false;
			}
			if (!TargetReady && !TrySetAnyNonLocalTarget())
			{
				Logger.Warn("SpectateCam: Attach failed - no valid target available");
				return false;
			}
			LastCamDir = _self.FPSCamera.Forward;
			GuiManager.CrosshairLayer.ShowPrecisionDot();
			SpectateUI.Instance?.UpdateForAttach();
			SetRelatedActive(spectateActive: true);
			UpdateCull();
			SetActive(active: true);
			Logger.Debug("Attach");
			return true;
		}

		public bool Detach()
		{
			if (!Active)
			{
				return true;
			}
			if (!SelfReady && !Load())
			{
				Logger.Error("SpectateCam: Detach failed - self not ready and cannot be loaded");
				Logger.Info("SpectateCam: Detach falling back to Unload");
				Unload();
				return false;
			}
			CrosshairGuiLayer crosshairLayer = GuiManager.CrosshairLayer;
			if (crosshairLayer != null)
			{
				FirstPersonItemHolder? fPHolder = _self.FPHolder;
				float? obj;
				if (fPHolder == null)
				{
					obj = null;
				}
				else
				{
					ItemEquippable wieldedItem = fPHolder.WieldedItem;
					obj = ((wieldedItem != null) ? new float?(wieldedItem.HipFireCrosshairSize) : null);
				}
				crosshairLayer.ShowSpreadCircle(obj ?? 40f);
			}
			SpectateUI.Instance?.UpdateForDetach();
			SetRelatedActive(spectateActive: false);
			RevertCull();
			SetActive(active: false);
			Logger.Debug("Detach");
			return true;
		}

		private void SetActive(bool active)
		{
			if (active && !_wasActive)
			{
				this.OnActive?.Invoke();
			}
			_wasActive = Active;
			Active = active;
			if (!Active)
			{
				_yaw = 0f;
				_pitch = ConfigMgr.CameraPitchAngleDeg;
				_freeLookReturnTimer = 0f;
			}
		}

		private void SetRelatedActive(bool spectateActive)
		{
			if (!SelfReady)
			{
				Logger.Error("SpectateCam: SetRelatedActive failed - self not ready");
				return;
			}
			_self.SetRigActive(!spectateActive);
			_self.Agent.DeadDebugMode = spectateActive;
			Util.SetTargetActiveIfDiff((Behaviour?)(object)_self.Inventory, !spectateActive);
			PlayerInventoryBase? inventory = _self.Inventory;
			Util.SetTargetActiveIfDiff((inventory != null) ? ((Component)inventory.m_flashlight).gameObject : null, !spectateActive);
			FirstPersonItemHolder? fPHolder = _self.FPHolder;
			Util.SetTargetActiveIfDiff((fPHolder != null) ? ((Component)fPHolder).gameObject : null, !spectateActive);
			FirstPersonItemHolder? fPHolder2 = _self.FPHolder;
			if (fPHolder2 != null)
			{
				PlayerFPSBody fPSArms = fPHolder2.FPSArms;
				if (fPSArms != null)
				{
					fPSArms.SetVisible(!spectateActive && !_self.IsDowned);
				}
			}
			FPSCamera fPSCamera = _self.FPSCamera;
			if ((Object)(object)fPSCamera != (Object)null)
			{
				fPSCamera.MouseLookEnabled = !spectateActive;
				fPSCamera.PlayerAgentRotationEnabled = !spectateActive;
				fPSCamera.PlayerMoveEnabled = !spectateActive;
			}
		}

		private void Update()
		{
			if (!((Behaviour)this).enabled || !((Component)this).gameObject.activeInHierarchy)
			{
				return;
			}
			ProcessInput();
			UpdateTransitions();
			if (Active)
			{
				if (_target == null)
				{
					Detach();
					return;
				}
				UpdateLerp(_freecam);
				UpdateCamera();
				UpdateCull();
			}
		}

		private void UpdateTransitions()
		{
			if (SelfReady)
			{
				if (Active && !_self.IsDowned && !ConfigMgr.DevEnables(eDevOpts.AllowSpectatingAnytime))
				{
					Detach();
				}
				else if (Active && ConfigMgr.AutoTransitionToFollowView && _freeLookReturnTimer >= 0f)
				{
					_freeLookReturnTimer -= Time.deltaTime;
				}
			}
		}

		private void ProcessInput()
		{
			//IL_0032: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: 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_00e7: 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_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			if (!InputMapper.Current.FocusStateFilterPass((eFocusState)4))
			{
				return;
			}
			if ((ConfigMgr.DevEnables(eDevOpts.AllowSpectatingAnytime) || (SelfReady && _self.IsDowned)) && Input.GetKeyDown(ConfigMgr.GetKeybind(SpectateInputAction.ToggleSpectate)))
			{
				if (Active)
				{
					if (!Detach())
					{
						Logger.Warn("SpectateCam: Failed to detach SpecCam");
					}
				}
				else if (!Attach())
				{
					Logger.Warn("SpectateCam: Failed to attach SpecCam");
				}
			}
			if (!Active)
			{
				return;
			}
			if (Input.GetKeyDown(ConfigMgr.GetKeybind(SpectateInputAction.ToggleFreecam)))
			{
				if (_freecam)
				{
					_freecam = false;
					OnFree2Follow();
				}
				else
				{
					_freecam = true;
					OnFollow2Free();
				}
			}
			if (_freecam && Input.GetKeyDown(ConfigMgr.GetKeybind(SpectateInputAction.ToggleAutoFollow)))
			{
				ConfigMgr.AutoTransitionToFollowView = !ConfigMgr.AutoTransitionToFollowView;
				SpectateUI.Instance?.MarkUIDirty();
			}
			Vector2 mouseDelta = InputHelper.GetMouseDelta();
			if (_freecam && mouseDelta != Vector2.zero)
			{
				AdjustYaw(mouseDelta.x * ConfigMgr.FreecamSensitivity);
				AdjustPitch(mouseDelta.y * ConfigMgr.FreecamSensitivity);
				if (ConfigMgr.AutoTransitionToFollowView)
				{
					_freecamFollow = false;
					_freeLookReturnTimer = ConfigMgr.AutoTransitionDelay;
				}
			}
			if (_freecam)
			{
				if (Input.GetKey((KeyCode)273))
				{
					AdjustPitch(ConfigMgr.FreecamSensitivity);
				}
				if (Input.GetKey((KeyCode)274))
				{
					AdjustPitch(0f - ConfigMgr.FreecamSensitivity);
				}
				if (Input.GetKey((KeyCode)276))
				{
					AdjustYaw(0f - ConfigMgr.FreecamSensitivity);
				}
				if (Input.GetKey((KeyCode)275))
				{
					AdjustYaw(ConfigMgr.FreecamSensitivity);
				}
			}
			if (Input.GetKeyDown((KeyCode)323))
			{
				TrySetNextTarget();
			}
			if (Input.GetKeyDown((KeyCode)324))
			{
				TrySetPreviousTarget();
			}
			int alphaNumKeyDown = InputHelper.GetAlphaNumKeyDown();
			if (alphaNumKeyDown > 0)
			{
				TrySetTargetByIdx(alphaNumKeyDown - 1);
			}
			float num = Input.mouseScrollDelta.y * ConfigMgr.ScrollSensitivity;
			if (InputMapper.Current.FocusStateFilterPass((eFocusState)6) || !(Mathf.Abs(num) > 0f))
			{
				return;
			}
			if (InputHelper.OnlyModifies((KeyCode)304, (KeyCode)303, (KeyCode)0))
			{
				if (_freecam)
				{
					SpectateUI.Instance?.WarnFreecamNoAdjustPitch();
				}
				else
				{
					ConfigMgr.CameraPitchAngleDeg -= 0.5f * num;
				}
			}
			else if (InputHelper.OnlyModifies((KeyCode)306, (KeyCode)305, (KeyCode)0))
			{
				ConfigMgr.CameraOrbitVerticalOffset = Mathf.Clamp(ConfigMgr.CameraOrbitVerticalOffset + 0.05f * num, -1f, 5f);
			}
			else
			{
				ConfigMgr.CameraDistance = Mathf.Clamp(ConfigMgr.CameraDistance - 0.05f * num, 0.1f, 5f);
			}
		}

		private void UpdateCamera()
		{
			//IL_004f: 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_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: 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_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: 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_012b: 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_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			if (!SelfReady || !TargetReady)
			{
				Logger.Error("SpectateCam: UpdateCull failed - target or self not ready");
				return;
			}
			if (ConfigMgr.AutoTransitionToFollowView && _freecam)
			{
				if (_freeLookReturnTimer < 0.001f)
				{
					_freecamFollow = true;
				}
				if (_freecamFollow)
				{
					UpdateYawPitchWithFollowView(instant: false);
				}
			}
			SetEye(GetTargetOrbitCenter());
			Vector3 val = _eyeXZ + Vector3.up * _eyeY;
			Vector3 forward = ((Agent)_target.Agent).Forward;
			Vector3 val2 = ((Vector3)(ref forward)).normalized;
			if (_freecam)
			{
				val2 = Quaternion.Euler(0f, _yaw, 0f) * Vector3.forward;
			}
			float num = (float)Math.PI / 180f * (_freecam ? _pitch : ConfigMgr.CameraPitchAngleDeg);
			Vector3 val3 = val2 + Vector3.up * Mathf.Tan(num);
			((Vector3)(ref val3)).Normalize();
			_eyePosComputed = val - val3 * ConfigMgr.CameraDistance;
			RaycastHit val4 = default(RaycastHit);
			if (Physics.Raycast(val, -val3, ref val4, ConfigMgr.CameraDistance, LayerManager.MASK_WORLD))
			{
				_eyePosComputed = val4.m_Point + val3 * 0.1f;
			}
			_self.FPSCamera.OverridePositionAndRotation(_eyePosComputed, Quaternion.LookRotation(val3));
			_self.FPSCamera.OverrideFieldOfView((float)CellSettingsManager.GetIntValue((eCellSettingID)52));
		}

		private void UpdateLerp(bool freecamEnabled)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			_eyeXZ = Vector3.Lerp(_eyeXZ, _eyeXZTarget, Time.deltaTime * ConfigMgr.CameraXZLerpGain);
			_eyeY = Mathf.Lerp(_eyeY, _eyeYTarget, Time.deltaTime * ConfigMgr.CameraYLerpGain);
			if (freecamEnabled)
			{
				_yaw = Mathf.LerpAngle(_yaw, _yawTarget, Time.deltaTime * ConfigMgr.FreecamLerpGain);
				float num = _yawTarget - _yaw;
				_yaw = Mathf.Repeat(_yaw, 360f);
				_yawTarget = _yaw + num;
				_pitch = Mathf.LerpAngle(_pitch, _pitchTarget, Time.deltaTime * ConfigMgr.FreecamLerpGain);
			}
		}

		private void UpdateCull()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//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_0045: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!SelfReady || !TargetReady)
			{
				Logger.Error("SpectateCam: UpdateCull failed - target or self not ready");
				return;
			}
			Vector3 val = ((Agent)_target.Agent).Position;
			RaycastHit val2 = default(RaycastHit);
			if (Physics.Raycast(val, Vector3.down, ref val2, 64f, LayerManager.MASK_WORLD))
			{
				val = val2.m_Point;
			}
			CameraManager.CullingPosition = val;
			CameraManager.CullingDirection = _self.FPSCamera.Forward;
			_self.Agent.m_movingCuller.UpdatePosition(((Agent)_self.Agent).m_dimensionIndex, val);
			C_Node currentNode = _self.Agent.m_movingCuller.CurrentNode;
			AIG_CourseNode? courseNode = _target.CourseNode;
			C_Node val3 = ((courseNode != null) ? courseNode.m_cullNode : null);
			if (val3 != null)
			{
				if (((Il2CppObjectBase)currentNode).Pointer != ((Il2CppObjectBase)val3).Pointer)
				{
					_self.Agent.m_movingCuller.SetCurrentNode(val3);
				}
			}
			else
			{
				Logger.Warn("SpectateCam: UpdateCull - failed to sync cull nodes, target node is null");
			}
		}

		private void RevertCull()
		{
			//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)
			//IL_0024: 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)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (!SelfReady)
			{
				Logger.Error("SpectateCam: RevertCull failed - self is not ready");
				return;
			}
			Vector3 val = ((Agent)_self.Agent).Position;
			RaycastHit val2 = default(RaycastHit);
			if (Physics.Raycast(val, Vector3.down, ref val2, 64f, LayerManager.MASK_WORLD))
			{
				val = val2.m_Point;
			}
			CameraManager.CullingPosition = val;
			CameraManager.CullingDirection = _self.FPSCamera.Forward;
			_self.Agent.m_movingCuller.UpdatePosition(((Agent)_self.Agent).m_dimensionIndex, val);
			C_Node currentNode = _self.Agent.m_movingCuller.CurrentNode;
			AIG_CourseNode? courseNode = _self.CourseNode;
			C_Node val3 = ((courseNode != null) ? courseNode.m_cullNode : null);
			if (val3 != null)
			{
				if (((currentNode != null) ? new IntPtr?(((Il2CppObjectBase)currentNode).Pointer) : null) != ((val3 != null) ? new IntPtr?(((Il2CppObjectBase)val3).Pointer) : null))
				{
					_self.Agent.m_movingCuller.SetCurrentNode(val3);
				}
			}
			else
			{
				Logger.Warn("SpectateCam: RevertCull - failed to sync cull nodes self or target node is null");
			}
		}

		private void OnApplicationQuit()
		{
			ConfigMgr.WriteConfigIfDirty();
		}

		private bool TrySetAnyNonLocalTarget()
		{
			SNet_PlayerSlotManager slots = SNet.Slots;
			List<SNet_Player> val = ((slots != null) ? slots.SlottedPlayers : null);
			if (val == null || val.Count == 0)
			{
				return false;
			}
			_lastTargetPlayerIdx = 0;
			for (int i = 0; i < val.Count; i++)
			{
				if (TrySetTargetByIdx(i))
				{
					return true;
				}
			}
			return false;
		}

		private bool TrySetNextTarget()
		{
			SNet_PlayerSlotManager slots = SNet.Slots;
			int num = ((slots == null) ? null : slots.SlottedPlayers?.Count) ?? (-1);
			if (num <= 0)
			{
				return false;
			}
			for (int i = 1; i <= num; i++)
			{
				int playerIdx = (_lastTargetPlayerIdx + i) % num;
				if (TrySetTargetByIdx(playerIdx))
				{
					return true;
				}
			}
			return false;
		}

		private bool TrySetPreviousTarget()
		{
			SNet_PlayerSlotManager slots = SNet.Slots;
			int num = ((slots == null) ? null : slots.SlottedPlayers?.Count) ?? (-1);
			if (num <= 0)
			{
				return false;
			}
			for (int i = 1; i <= num; i++)
			{
				int playerIdx = (_lastTargetPlayerIdx - i + num) % num;
				if (TrySetTargetByIdx(playerIdx))
				{
					return true;
				}
			}
			return false;
		}

		private bool TrySetTargetByIdx(int playerIdx)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			SNet_PlayerSlotManager slots = SNet.Slots;
			List<SNet_Player> val = ((slots != null) ? slots.SlottedPlayers : null);
			if (val == null || val.Count == 0)
			{
				return false;
			}
			if (playerIdx >= 0 && playerIdx < val.Count && !val[playerIdx].IsLocal)
			{
				SetTarget(((Il2CppObjectBase)val[playerIdx].PlayerAgent).Cast<PlayerAgent>());
				if ((ConfigMgr.NoPosLerpOnSwitchTarget && playerIdx != _lastTargetPlayerIdx) || !_freecam)
				{
					SetEye(GetTargetOrbitCenter(), instant: true);
				}
				_lastTargetPlayerIdx = playerIdx;
				return true;
			}
			return false;
		}

		private Vector3 GetTargetOrbitCenter()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			AgentTarget? target = _target;
			Vector3 result = (((object)target != null) ? target.Agent.m_eyePosition : Vector3.zero);
			result.y += ConfigMgr.CameraOrbitVerticalOffset;
			return result;
		}

		private void OnFollow2Free()
		{
			if (!TargetReady)
			{
				Logger.Error("SpectateCam: OnTransitionToFreecam failed - target not ready");
				return;
			}
			UpdateYawPitchWithFollowView(instant: true);
			SpectateUI.Instance?.MarkUIDirty();
		}

		private void OnFree2Follow()
		{
			if (!TargetReady)
			{
				Logger.Error("SpectateCam: OnTransitionToFollow failed - target not ready");
				return;
			}
			UpdateYawPitchWithFollowView(instant: true);
			SpectateUI.Instance?.MarkUIDirty();
		}

		private void UpdateYawPitchWithFollowView(bool instant)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			SetYaw(Vector3.SignedAngle(Vector3.forward, ((Agent)_target.Agent).Forward, Vector3.up), instant);
			SetPitch(ConfigMgr.CameraPitchAngleDeg, instant);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void AdjustPitch(float deltaPitch, bool instant = false)
		{
			SetPitch(_pitchTarget + deltaPitch, instant);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void AdjustYaw(float deltaYaw, bool instant = false)
		{
			SetYaw(_yawTarget + deltaYaw, instant);
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void SetPitch(float pitch, bool instant = false)
		{
			pitch = Mathf.Clamp(pitch, -89f, 89f);
			_pitchTarget = pitch;
			if (instant)
			{
				_pitch = pitch;
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void SetYaw(float yaw, bool instant = false)
		{
			_yawTarget = yaw;
			if (instant)
			{
				_yaw = yaw;
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void SetEyeXZ(Vector3 eyeXZ, bool instant = false)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			eyeXZ.y = 0f;
			_eyeXZTarget = eyeXZ;
			if (instant)
			{
				_eyeXZ = eyeXZ;
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void SetEyeY(float eyeY, bool instant = false)
		{
			_eyeYTarget = eyeY;
			if (instant)
			{
				_eyeY = eyeY;
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void SetEye(Vector3 pos, bool instant = false)
		{
			//IL_0001: 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)
			SetEyeXZ(pos, instant);
			SetEyeY(pos.y, instant);
		}
	}
	public static class Util
	{
		public const float GOOD_ENOUGH_DEG_EPS = 0.2f;

		public static bool GoodEnoughDeg(float a, float b)
		{
			return NearlyEqual(a, b, 0.2f);
		}

		public static bool NearlyEqual(float a, float b, float eps = 0.0001f)
		{
			return Mathf.Abs(a - b) < eps;
		}

		public static void SetTargetActiveIfDiff(GameObject? obj, bool active)
		{
			if (!((Object)(object)obj == (Object)null) && obj.activeSelf != active)
			{
				obj.SetActive(active);
			}
		}

		public static void SetTargetActiveIfDiff(Behaviour? beh, bool active)
		{
			if (!((Object)(object)beh == (Object)null) && beh.enabled != active)
			{
				beh.enabled = active;
			}
		}
	}
}
namespace Spectate.UI
{
	public enum eSpectateUIComp
	{
		SpectateTarget,
		Title,
		ViewMode,
		Menu
	}
	public enum eSpectateMenuItem
	{
		HideMenu,
		ShowMenu,
		EnterSpectate,
		ExitSpectate,
		ToggleFreecam,
		EnableFreecamAutoTransition,
		DisableFreecamAutoTransition,
		SwitchPlayer,
		SelectPlayer,
		AdjustDistance,
		AdjustOrbitCenterHeight,
		AdjustFollowPitch
	}
	public enum eSpectateUIState
	{
		FPDowned,
		FPNotDowned,
		HideMenu,
		ShowMenu
	}
	public struct MenuKeybindEntry
	{
		private string _keyName;

		private bool _isAssociated;

		private SpectateInputAction _associatedAction;

		public MenuKeybindEntry(string keyName)
		{
			_isAssociated = false;
			_associatedAction = SpectateInputAction.None;
			_keyName = keyName;
		}

		public MenuKeybindEntry(SpectateInputAction action)
		{
			_isAssociated = true;
			_associatedAction = action;
			_keyName = "";
		}

		public override string ToString()
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (_isAssociated)
			{
				return CellSettingGlobals.GetLocalizedKeyCode(ConfigMgr.GetKeybind(_associatedAction));
			}
			return _keyName;
		}
	}
	public class SpectateUI : MonoBehaviour
	{
		private bool _wasUIActive;

		private bool _isUIDirty = true;

		private eSpectateUIState _uiState = eSpectateUIState.FPNotDowned;

		private eSpectateUIState _uiStatePrev = eSpectateUIState.FPNotDowned;

		private eSpectateUIState _uiStateRendered = eSpectateUIState.ShowMenu;

		private readonly List<GameObject> _uiRoots = new List<GameObject>();

		private readonly Dictionary<eSpectateUIComp, bool> _uiCompState = new Dictionary<eSpectateUIComp, bool>();

		private const float MenuCenterOffsetX = 300f;

		private const float MenuLeftPadding = 50f;

		private const float MenuElementWidth = 250f;

		private const float MenuCenterDivideSpacing = 5f;

		private const int MaxMenuItems = 10;

		private const float MenuItemSpacing = 27f;

		private const float MenuOptionHeight = 22f;

		private const float MenuKeybindHeight = 16f;

		private const string SpecTargetTextColor = "FFFFFF";

		private const string StateHighlightColor = "04B065";

		private TextMeshPro? _specTargetTmp;

		private GameObject? _menuObj;

		private TextMeshPro? _menuTitleTmp;

		private TextMeshPro? _menuViewModeTmp;

		private GameObject? _menuListObj;

		private List<(TextMeshPro?, TextMeshPro?)> _menuItemsTmp = new List<(TextMeshPro, TextMeshPro)>(10);

		private SpriteRenderer? _menuBackground;

		private string _specTargetStr = "";

		private readonly string _menuTitleStr = "SPECTATE";

		private string _menuViewModeStr = "";

		private readonly List<(string, string)> _menuItemsStr = new List<(string, string)>();

		private readonly Dictionary<eSpectateMenuItem, (string, MenuKeybindEntry)> _menuItems = new Dictionary<eSpectateMenuItem, (string, MenuKeybindEntry)>
		{
			[eSpectateMenuItem.ShowMenu] = ("Show Menu", new MenuKeybindEntry(SpectateInputAction.ToggleMenu)),
			[eSpectateMenuItem.HideMenu] = ("Hide Menu", new MenuKeybindEntry(SpectateInputAction.ToggleMenu)),
			[eSpectateMenuItem.EnterSpectate] = ("Enter Spectate", new MenuKeybindEntry(SpectateInputAction.ToggleSpectate)),
			[eSpectateMenuItem.ExitSpectate] = ("Exit Spectate", new MenuKeybindEntry(SpectateInputAction.ToggleSpectate)),
			[eSpectateMenuItem.ToggleFreecam] = ("Toggle Free-Look", new MenuKeybindEntry(SpectateInputAction.ToggleFreecam)),
			[eSpectateMenuItem.EnableFreecamAutoTransition] = ("Enable Auto-Follow", new MenuKeybindEntry(SpectateInputAction.ToggleAutoFollow)),
			[eSpectateMenuItem.DisableFreecamAutoTransition] = ("Disable Auto-Follow", new MenuKeybindEntry(SpectateInputAction.ToggleAutoFollow)),
			[eSpectateMenuItem.SwitchPlayer] = ("Switch Player", new MenuKeybindEntry("LMB / RMB")),
			[eSpectateMenuItem.SelectPlayer] = ("Select Player", new MenuKeybindEntry("1 - 8")),
			[eSpectateMenuItem.AdjustDistance] = ("Camera Distance", new MenuKeybindEntry("Scroll")),
			[eSpectateMenuItem.AdjustOrbitCenterHeight] = ("Camera Vertical Offset", new MenuKeybindEntry("Ctrl + Scroll")),
			[eSpectateMenuItem.AdjustFollowPitch] = ("Camera Pitch", new MenuKeybindEntry("Shift + Scroll"))
		};

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

		public eSpectateUIState UIState => _uiState;

		public SpectateUI(IntPtr ptr)
			: base(ptr)
		{
		}

		private void Awake()
		{
			if ((Object)(object)Instance != (Object)null && (Object)(object)Instance != (Object)(object)this)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
			else
			{
				Instance = this;
			}
		}

		private bool AllTMPExist()
		{
			if ((Object)(object)_menuObj != (Object)null)
			{
				return (Object)(object)_specTargetTmp != (Object)null;
			}
			return false;
		}

		private void Update()
		{
			if ((!(SpectateCam.Instance?.Self != null) || !SpectateCam.Instance.Self.IsDowned) && !ConfigMgr.DevEnables(eDevOpts.AllowSpectatingAnytime))
			{
				SetUIActive(active: false);
			}
			else if (CheckOrCreateTMP())
			{
				ProcessInput();
				SpectateCam? instance = SpectateCam.Instance;
				if (instance != null && instance.Active)
				{
					UpdatePlayerStatusUI(SpectateCam.Instance.Target);
				}
				SetUIActive(active: true);
				if (_isUIDirty || UIState != _uiStateRendered)
				{
					RefreshUI();
				}
			}
		}

		public void UpdateForAttach()
		{
			if (_uiStatePrev == eSpectateUIState.HideMenu || _uiStatePrev == eSpectateUIState.ShowMenu)
			{
				SetUIState(_uiStatePrev);
			}
			else
			{
				SetUIState(eSpectateUIState.HideMenu);
			}
		}

		public void UpdateForDetach()
		{
			bool valueOrDefault = (SpectateCam.Instance?.Self?.IsDowned).GetValueOrDefault();
			SetUIState((!valueOrDefault) ? eSpectateUIState.FPNotDowned : eSpectateUIState.FPDowned);
			UpdatePlayerStatusUI(SpectateCam.Instance?.Self);
		}

		private void ProcessInput()
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (InputMapper.Current.FocusStateFilterPass((eFocusState)4) && Input.GetKeyDown(ConfigMgr.GetKeybind(SpectateInputAction.ToggleMenu)))
			{
				switch (UIState)
				{
				case eSpectateUIState.ShowMenu:
					SetUIState(eSpectateUIState.HideMenu);
					break;
				case eSpectateUIState.HideMenu:
					SetUIState(eSpectateUIState.ShowMenu);
					break;
				}
			}
		}

		private bool CheckOrCreateTMP()
		{
			//IL_008a: 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)
			//IL_00cd: 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)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			//IL_0148: 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_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fb: Expected O, but got Unknown
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_0329: Unknown result type (might be due to invalid IL or missing references)
			//IL_033f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0355: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_0409: Unknown result type (might be due to invalid IL or missing references)
			//IL_0424: Unknown result type (might be due to invalid IL or missing references)
			//IL_044f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0485: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0598: Unknown result type (might be due to invalid IL or missing references)
			if (AllTMPExist())
			{
				return true;
			}
			RemoveAllUIRoots();
			PlayerGuiLayer playerLayer = GuiManager.PlayerLayer;
			PUI_LocalPlayerStatus val = ((playerLayer != null) ? playerLayer.m_playerStatus : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Transform parent = ((Component)val).transform.parent;
			TextMeshPro healthText = val.m_healthText;
			_specTargetTmp = CreateTMPFrom(healthText, "io.takina.gtfo.Spectate_SpectateTarget");
			RegisterUIRoot(((Component)_specTargetTmp).gameObject);
			((Transform)((TMP_Text)_specTargetTmp).rectTransform).SetParent(parent, false);
			((TMP_Text)_specTargetTmp).rectTransform.anchorMin = new Vector2(0.5f, 1f);
			((TMP_Text)_specTargetTmp).rectTransform.anchorMax = new Vector2(0.5f, 1f);
			((TMP_Text)_specTargetTmp).rectTransform.anchoredPosition3D = new Vector3(0f, -200f, 0f);
			((TMP_Text)_specTargetTmp).rectTransform.sizeDelta = new Vector2(300f, 100f);
			((TMP_Text)_specTargetTmp).alignment = (TextAlignmentOptions)514;
			((Component)_specTargetTmp).gameObject.SetActive(false);
			_menuObj = new GameObject("io.takina.gtfo.Spectate_SpectateMenu");
			RegisterUIRoot(_menuObj);
			RectTransform val2 = _menuObj.AddComponent<RectTransform>();
			((Transform)val2).SetParent(parent, false);
			((Transform)val2).localScale = Vector3.one;
			val2.anchorMin = new Vector2(0f, 0.5f);
			val2.anchorMax = new Vector2(0f, 0.5f);
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.anchoredPosition3D = new Vector3(300f, 0f, 0f);
			_menuTitleTmp = CreateTMPFrom(healthText, "io.takina.gtfo.Spectate_MenuTitle");
			((Transform)((TMP_Text)_menuTitleTmp).rectTransform).SetParent(_menuObj.transform, false);
			((TMP_Text)_menuTitleTmp).rectTransform.pivot = new Vector2(1f, 0f);
			((TMP_Text)_menuTitleTmp).rectTransform.anchoredPosition3D = new Vector3(0f, 55f, 0f);
			((TMP_Text)_menuTitleTmp).rectTransform.sizeDelta = new Vector2(250f, 45f);
			((TMP_Text)_menuTitleTmp).alignment = (TextAlignmentOptions)516;
			_menuViewModeTmp = CreateTMPFrom(healthText, "io.takina.gtfo.Spectate_ViewMode");
			((Transform)((TMP_Text)_menuViewModeTmp).rectTransform).SetParent(_menuObj.transform, false);
			((TMP_Text)_menuViewModeTmp).rectTransform.pivot = new Vector2(1f, 0f);
			((TMP_Text)_menuViewModeTmp).rectTransform.anchoredPosition3D = new Vector3(0f, 13f, 0f);
			((TMP_Text)_menuViewModeTmp).rectTransform.sizeDelta = new Vector2(250f, 40f);
			((TMP_Text)_menuViewModeTmp).alignment = (TextAlignmentOptions)516;
			_menuListObj = new GameObject("io.takina.gtfo.Spectate_SpectateMenuList");
			RectTransform val3 = _menuListObj.AddComponent<RectTransform>();
			((Transform)val3).SetParent((Transform)(object)val2, false);
			((Transform)val3).localScale = Vector3.one;
			val3.anchorMin = new Vector2(0.5f, 0.5f);
			val3.anchorMax = new Vector2(0.5f, 0.5f);
			val3.pivot = new Vector2(0.5f, 0.5f);
			val3.anchoredPosition3D = Vector3.zero;
			for (int i = 0; i < 10; i++)
			{
				TextMeshPro val4 = CreateTMPFrom(healthText, $"{"io.takina.gtfo.Spectate"}_MenuOption_{i}");
				TextMeshPro val5 = CreateTMPFrom(healthText, $"{"io.takina.gtfo.Spectate"}_MenuKeybind_{i}");
				((Transform)((TMP_Text)val4).rectTransform).SetParent((Transform)(object)val3, false);
				((TMP_Text)val4).rectTransform.pivot = new Vector2(1f, 0.5f);
				((TMP_Text)val4).rectTransform.sizeDelta = new Vector2(250f, 22f);
				((TMP_Text)val4).rectTransform.anchoredPosition3D = new Vector3(0f, 0f - ((float)i + 0.5f) * 27f, 0f);
				((TMP_Text)val4).alignment = (TextAlignmentOptions)516;
				((Transform)((TMP_Text)val5).rectTransform).SetParent((Transform)(object)val3, false);
				((TMP_Text)val5).rectTransform.pivot = new Vector2(0f, 0.5f);
				((TMP_Text)val5).rectTransform.sizeDelta = new Vector2(250f, 16f);
				((TMP_Text)val5).rectTransform.anchoredPosition3D = new Vector3(5f, 0f - ((float)i + 0.5f) * 27f, 0f);
				((TMP_Text)val5).alignment = (TextAlignmentOptions)513;
				_menuItemsTmp.Add((val4, val5));
			}
			RefreshUI();
			Transform val6 = parent.Find("PUI_CommunicationMenu(Clone)/Root/Backround");
			if ((Object)(object)val6 == (Object)null)
			{
				Logger.Warn("SpectateUI: Can't find bg transform in CommunicationMenu! Using \"" + ((Object)parent).name + "\"");
				return true;
			}
			GameObject val7 = Object.Instantiate<GameObject>(((Component)val6).gameObject);
			((Object)val7).name = "io.takina.gtfo.Spectate_MenuBackground";
			_menuBackground = val7.GetComponent<SpriteRenderer>();
			((Renderer)_menuBackground).sortingOrder = -20;
			RectTransform component = val7.GetComponent<RectTransform>();
			((Transform)component).SetParent(_menuObj.transform);
			component.anchoredPosition3D = new Vector3(-25f, -55f, 0f);
			return true;
		}

		private TextMeshPro CreateTMPFrom(TextMeshPro from, string name)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			TextMeshPro obj = Object.Instantiate<TextMeshPro>(from);
			((Object)obj).name = name;
			((Graphic)obj).color = new Color(1f, 1f, 1f, 0.8f);
			((TMP_Text)obj).enableAutoSizing = true;
			((TMP_Text)obj).autoSizeTextContainer = false;
			((TMP_Text)obj).enableWordWrapping = false;
			((TMP_Text)obj).overflowMode = (TextOverflowModes)1;
			((TMP_Text)obj).margin = Vector4.zero;
			((TMP_Text)obj).fontSize = 26f;
			((TMP_Text)obj).fontSizeMin = 0f;
			((TMP_Text)obj).fontSizeMax = 69f;
			((TMP_Text)obj).rectTransform.anchorMin = new Vector2(0.5f, 0.5f);
			((TMP_Text)obj).rectTransform.anchorMax = new Vector2(0.5f, 0.5f);
			((TMP_Text)obj).rectTransform.pivot = new Vector2(0.5f, 0.5f);
			return obj;
		}

		public void UpdatePlayerStatusUI(AgentTarget? target)
		{
			if (target == null || (Object)(object)target.Agent == (Object)null)
			{
				Logger.Error("SpectateUI: Could not update player status text: no target!");
				return;
			}
			PlayerGuiLayer playerLayer = GuiManager.PlayerLayer;
			PUI_LocalPlayerStatus obj = ((playerLayer != null) ? playerLayer.m_playerStatus : null);
			if (obj != null)
			{
				obj.UpdateHealth(target.Health, false);
			}
			if (obj != null)
			{
				obj.UpdateInfection(target.Infection, 0f);
			}
		}

		private void AddMenuItem(eSpectateMenuItem item, bool enableUI = true)
		{
			if (_menuItemsStr.Count >= 10)
			{
				Logger.Warn("SpectateUI: AddMenuItem (" + item.ToString() + ") but menu is full!");
				return;
			}
			if (_menuItems.TryGetValue(item, out (string, MenuKeybindEntry) value))
			{
				_menuItemsStr.Add((value.Item1, value.Item2.ToString()));
			}
			else
			{
				Logger.Warn("SpectateUI: Tried to add unknown menu item!");
			}
			if (enableUI)
			{
				EnableUI(eSpectateUIComp.Menu);
			}
		}

		private void UpdateViewMode(bool freecam, bool enableUI = true)
		{
			string text = (freecam ? "<#04B065FF>FREE-LOOK</color>" : "<#FFFFFF60>FREE-LOOK</color>");
			string text2 = ((!freecam) ? "<#04B065FF>FOLLOW</color>" : "<#FFFFFF60>FOLLOW</color>");
			_menuViewModeStr = text + " / " + text2;
			if (enableUI)
			{
				EnableUI(eSpectateUIComp.ViewMode);
			}
		}

		private void UpdateSpectateTargetText(bool enableUI = true)
		{
			SpectateCam? instance = SpectateCam.Instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				AgentTarget? target = instance.Target;
				obj = (((object)target != null) ? target.Agent.InteractionName : null);
			}
			if (obj == null)
			{
				obj = "<#FFDE21>Unknown</color>";
			}
			string value = (string)obj;
			_specTargetStr = $"<#{"FFFFFF"}>Spectating\n<size=36>{value}</size></color>";
			if (enableUI)
			{
				EnableUI(eSpectateUIComp.SpectateTarget);
			}
		}

		public void SetUIState(eSpectateUIState state)
		{
			_uiStatePrev = _uiState;
			_uiState = state;
		}

		public void MarkUIDirty()
		{
			_isUIDirty = true;
		}

		private void RefreshUI()
		{
			RefreshUI(SpectateCam.Instance?.Freecam ?? false);
		}

		private void RefreshUI(bool freecam)
		{
			_isUIDirty = false;
			_uiStateRendered = _uiState;
			ClearUI();
			switch (_uiState)
			{
			case eSpectateUIState.ShowMenu:
				EnableUI(eSpectateUIComp.Title);
				UpdateViewMode(freecam);
				AddMenuItem(eSpectateMenuItem.ExitSpectate);
				AddMenuItem(eSpectateMenuItem.HideMenu);
				AddMenuItem(eSpectateMenuItem.ToggleFreecam);
				AddMenuItem(eSpectateMenuItem.SwitchPlayer);
				AddMenuItem(eSpectateMenuItem.SelectPlayer);
				AddMenuItem(eSpectateMenuItem.AdjustDistance);
				AddMenuItem(eSpectateMenuItem.AdjustOrbitCenterHeight);
				if (freecam)
				{
					if (ConfigMgr.AutoTransitionToFollowView)
					{
						AddMenuItem(eSpectateMenuItem.DisableFreecamAutoTransition);
					}
					else
					{
						AddMenuItem(eSpectateMenuItem.EnableFreecamAutoTransition);
					}
				}
				else
				{
					AddMenuItem(eSpectateMenuItem.AdjustFollowPitch);
				}
				break;
			case eSpectateUIState.HideMenu:
				EnableUI(eSpectateUIComp.Title);
				UpdateViewMode(freecam);
				AddMenuItem(eSpectateMenuItem.ExitSpectate);
				AddMenuItem(eSpectateMenuItem.ShowMenu);
				break;
			case eSpectateUIState.FPDowned:
			case eSpectateUIState.FPNotDowned:
				AddMenuItem(eSpectateMenuItem.EnterSpectate);
				break;
			}
			RenderUI();
		}

		private void ClearUI()
		{
			_menuViewModeStr = "";
			_menuItemsStr.Clear();
			eSpectateUIComp[] values = Enum.GetValues<eSpectateUIComp>();
			foreach (eSpectateUIComp key in values)
			{
				_uiCompState[key] = false;
			}
		}

		private void RemoveAllUIRoots()
		{
			foreach (GameObject uiRoot in _uiRoots)
			{
				Object.Destroy((Object)(object)uiRoot);
			}
			_specTargetTmp = null;
			_menuObj = null;
			_menuTitleTmp = null;
			_menuViewModeTmp = null;
			_menuListObj = null;
			_menuBackground = null;
			_menuItemsTmp.Clear();
			_uiRoots.Clear();
		}

		private void RegisterUIRoot(GameObject root)
		{
			if (!_uiRoots.Contains(root))
			{
				_uiRoots.Add(root);
			}
		}

		[MethodImpl(MethodImplOptions.AggressiveInlining)]
		private void EnableUI(eSpectateUIComp comp)
		{
			_uiCompState[comp] = true;
		}

		private void RenderUI()
		{
			foreach (var (eSpectateUIComp3, flag2) in _uiCompState)
			{
				switch (eSpectateUIComp3)
				{
				case eSpectateUIComp.SpectateTarget:
					if (flag2)
					{
						UpdateText(_specTargetTmp, _specTargetStr);
					}
					Util.SetTargetActiveIfDiff((Behaviour?)(object)_specTargetTmp, flag2);
					break;
				case eSpectateUIComp.Title:
					if (flag2)
					{
						UpdateText(_menuTitleTmp, _menuTitleStr);
					}
					Util.SetTargetActiveIfDiff((Behaviour?)(object)_menuTitleTmp, flag2);
					break;
				case eSpectateUIComp.ViewMode:
					if (flag2)
					{
						UpdateText(_menuViewModeTmp, _menuViewModeStr);
					}
					Util.SetTargetActiveIfDiff((Behaviour?)(object)_menuViewModeTmp, flag2);
					break;
				case eSpectateUIComp.Menu:
					if (flag2)
					{
						int count = _menuItemsStr.Count;
						for (int i = 0; i < 10; i++)
						{
							var (val, val2) = _menuItemsTmp[i];
							if (i < count)
							{
								var (text, text2) = _menuItemsStr[i];
								UpdateText(val, "<allcaps>" + text + "</allcaps>");
								UpdateText(val2, "<color=orange>[" + text2 + "]</color>");
								Util.SetTargetActiveIfDiff((Behaviour?)(object)val, active: true);
								Util.SetTargetActiveIfDiff((Behaviour?)(object)val2, active: true);
							}
							else
							{
								Util.SetTargetActiveIfDiff((Behaviour?)(object)val, active: false);
								Util.SetTargetActiveIfDiff((Behaviour?)(object)val2, active: false);
							}
						}
					}
					Util.SetTargetActiveIfDiff(_menuListObj, flag2);
					break;
				}
			}
		}

		private void SetUIActive(bool active)
		{
			if (_wasUIActive == active)
			{
				return;
			}
			foreach (GameObject uiRoot in _uiRoots)
			{
				Util.SetTargetActiveIfDiff(uiRoot, active);
			}
			_wasUIActive = active;
		}

		private void UpdateText(TextMeshPro? tmp, string newText)
		{
			if (!((Object)(object)tmp == (Object)null) && !(((TMP_Text)tmp).text == newText) && newText != ((TMP_Text)tmp).text)
			{
				((TMP_Text)tmp).text = newText;
				ForceTMPUpdate(tmp);
			}
		}

		private void ForceTMPUpdate(TextMeshPro? tmp)
		{
			if (!((Object)(object)tmp == (Object)null))
			{
				((Graphic)tmp).SetAllDirty();
				((TMP_Text)tmp).ForceMeshUpdate(false, false);
			}
		}

		public void WarnFreecamNoAdjustPitch()
		{
		}
	}
}
namespace Spectate.Config
{
	public enum ConfigEntryRule
	{
		Min,
		Max
	}
	public class ConfigEntryExtended<T>
	{
		private Dictionary<ConfigEntryRule, T> _rules = new Dictionary<ConfigEntryRule, T>();

		private ConfigEntry<T> _entry;

		public T Value
		{
			get
			{
				return _entry.Value;
			}
			set
			{
				_entry.Value = Enforce(value);
			}
		}

		public object BoxedValue
		{
			get
			{
				return ((ConfigEntryBase)_entry).BoxedValue;
			}
			set
			{
				((ConfigEntryBase)_entry).BoxedValue = Enforce((T)value);
			}
		}

		public ConfigEntryExtended(ConfigEntry<T> entry)
		{
			_entry = entry;
		}

		public bool AddRule(ConfigEntryRule rule, T ruleValue)
		{
			if (_rules.TryAdd(rule, ruleValue))
			{
				Enforce(rule);
				return true;
			}
			return false;
		}

		private void Enforce()
		{
			T val = Enforce(_entry.Value);
			if (val != null && !val.Equals(_entry.Value))
			{
				_entry.Value = val;
			}
		}

		private T Enforce(T val)
		{
			foreach (var (configEntryRule2, val3) in _rules)
			{
				switch (configEntryRule2)
				{
				case ConfigEntryRule.Min:
					if (Comparer<T>.Default.Compare(val, val3) < 0)
					{
						val = val3;
					}
					break;
				case ConfigEntryRule.Max:
					if (Comparer<T>.Default.Compare(val, val3) > 0)
					{
						val = val3;
					}
					break;
				}
			}
			return val;
		}

		private void Enforce(ConfigEntryRule rule)
		{
			T value = _entry.Value;
			value = Enforce(value, rule);
			if (value != null && !value.Equals(_entry.Value))
			{
				_entry.Value = value;
			}
		}

		private T Enforce(T val, ConfigEntryRule rule)
		{
			if (!_rules.TryGetValue(rule, out var value))
			{
				return val;
			}
			switch (rule)
			{
			case ConfigEntryRule.Min:
				if (Comparer<T>.Default.Compare(val, value) < 0)
				{
					val = value;
				}
				break;
			case ConfigEntryRule.Max:
				if (Comparer<T>.Default.Compare(val, value) > 0)
				{
					val = value;
				}
				break;
			}
			return val;
		}

		public static implicit operator ConfigEntryExtended<T>(ConfigEntry<T> entry)
		{
			return new ConfigEntryExtended<T>(entry);
		}
	}
	internal static class ConfigMgr
	{
		private static readonly ConfigFile Conf;

		private static bool _configDirty;

		private static readonly FileSystemWatcher? ConfigWatcher;

		private static readonly Dictionary<string, eDevOpts> DevOptsStr2Enum;

		private static readonly ConfigEntryExtended<bool> DebugConf;

		private static readonly ConfigEntryExtended<string> DevOptsConf;

		private static eDevOpts _devOptsVal;

		private static readonly ConfigEntryExtended<bool> SwitchOnDeathConf;

		private static readonly ConfigEntryExtended<bool> DefaultFreecamViewConf;

		private static readonly ConfigEntryExtended<bool> AutoTransitionToFollowViewConf;

		private static readonly ConfigEntryExtended<bool> NoPosLerpOnSwitchTargetConf;

		private static readonly ConfigEntryExtended<float> AutoTransitionDelayConf;

		private static bool _autoTransitionToFollowViewCache;

		private static readonly ConfigEntryExtended<float> ScrollSensitivityConf;

		private static readonly ConfigEntryExtended<float> FreecamSensitivityConf;

		private static readonly ConfigEntryExtended<float> FreecamLerpGainConf;

		private static readonly ConfigEntryExtended<float> CameraXZLerpGainConf;

		private static readonly ConfigEntryExtended<float> CameraYLerpGainConf;

		private static readonly ConfigEntryExtended<float> CameraDistanceConf;

		private static readonly ConfigEntryExtended<float> CameraOrbitVerticalOffsetConf;

		private static readonly ConfigEntryExtended<float> CameraPitchAngleDegConf;

		private static float _cameraDistanceCache;

		private static float _cameraOrbitVerticalOffsetCache;

		private static float _cameraPitchAngleDegCache;

		private static readonly Dictionary<SpectateInputAction, ConfigEntryExtended<KeyCode>> KeybindConfs;

		private static readonly Dictionary<SpectateInputAction, KeybindSetting> KeybindSettingConfs;

		public static bool Debug => DebugConf.Value;

		public static bool SwitchOnDeath => SwitchOnDeathConf.Value;

		public static bool DefaultFreecamView => DefaultFreecamViewConf.Value;

		public static bool AutoTransitionToFollowView
		{
			get
			{
				return _autoTransitionToFollowViewCache;
			}
			set
			{
				_autoTransitionToFollowViewCache = value;
				_configDirty = true;
			}
		}

		public static bool NoPosLerpOnSwitchTarget => NoPosLerpOnSwitchTargetConf.Value;

		public static float AutoTransitionDelay => AutoTransitionDelayConf.Value;

		public static float ScrollSensitivity => ScrollSensitivityConf.Value;

		public static float FreecamSensitivity => FreecamSensitivityConf.Value;

		public static float FreecamLerpGain => FreecamLerpGainConf.Value;

		public static float CameraXZLerpGain => CameraXZLerpGainConf.Value;

		public static float CameraYLerpGain => CameraYLerpGainConf.Value;

		public static float CameraDistance
		{
			get
			{
				return _cameraDistanceCache;
			}
			set
			{
				_cameraDistanceCache = value;
				_configDirty = true;
			}
		}

		public static float CameraOrbitVerticalOffset
		{
			get
			{
				return _cameraOrbitVerticalOffsetCache;
			}
			set
			{
				_cameraOrbitVerticalOffsetCache = value;
				_configDirty = true;
			}
		}

		public static float CameraPitchAngleDeg
		{
			get
			{
				return _cameraPitchAngleDegCache;
			}
			set
			{
				_cameraPitchAngleDegCache = value;
				_configDirty = true;
			}
		}

		public static bool DevEnables(eDevOpts opt)
		{
			return _devOptsVal.HasFlag(opt);
		}

		public static KeyCode GetKeybind(SpectateInputAction action)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (KeybindConfs.TryGetValue(action, out ConfigEntryExtended<KeyCode> value))
			{
				return value.Value;
			}
			Logger.Error($"GetKeybind: no config found for action {action}");
			return (KeyCode)0;
		}

		public static void Process()
		{
			Logger.Info($"debug={Debug}");
			_cameraDistanceCache = CameraDistanceConf.Value;
			_cameraOrbitVerticalOffsetCache = CameraOrbitVerticalOffsetConf.Value;
			_cameraPitchAngleDegCache = CameraPitchAngleDegConf.Value;
			_autoTransitionToFollowViewCache = AutoTransitionToFollowViewConf.Value;
			_devOptsVal = eDevOpts.None;
			string[] array = DevOptsConf.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				if (DevOptsStr2Enum.TryGetValue(text.Trim().ToLower(), out var value))
				{
					_devOptsVal |= value;
				}
			}
			SpectateUI.Instance?.MarkUIDirty();
		}

		static ConfigMgr()
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: 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_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			//IL_065d: Unknown result type (might be due to invalid IL or missing references)
			_configDirty = false;
			DevOptsStr2Enum = new Dictionary<string, eDevOpts>
			{
				["--any"] = eDevOpts.AllowSpectatingAnytime,
				["--exp"] = eDevOpts.ExperimentalFeatures
			};
			_devOptsVal = eDevOpts.None;
			_autoTransitionToFollowViewCache = false;
			_cameraDistanceCache = float.NaN;
			_cameraOrbitVerticalOffsetCache = float.NaN;
			_cameraPitchAngleDegCache = float.NaN;
			KeybindConfs = new Dictionary<SpectateInputAction, ConfigEntryExtended<KeyCode>>();
			Dictionary<SpectateInputAction, KeybindSetting> obj = new Dictionary<SpectateInputAction, KeybindSetting>
			{
				[SpectateInputAction.ToggleSpectate] = new KeybindSetting
				{
					Name = "Toggle Spectate",
					Description = "toggle spectate active/inactive",
					DefaultKey = (KeyCode)118
				},
				[SpectateInputAction.ToggleFreecam] = new KeybindSetting
				{
					Name = "Toggle Free-Look View",
					Description = "toggle free-look spectate view on/off",
					DefaultKey = (KeyCode)102
				},
				[SpectateInputAction.ToggleAutoFollow] = new KeybindSetting
				{
					Name = "Toggle Auto Follow View",
					Description = "toggle automatic transition to temporary follow view on/off",
					DefaultKey = (KeyCode)116
				}
			};
			KeybindSetting value = new KeybindSetting
			{
				Name = "Toggle Spectate Menu",
				Description = "toggle spectate action menu on/off",
				DefaultKey = (KeyCode)92
			};
			obj[SpectateInputAction.ToggleMenu] = value;
			KeybindSettingConfs = obj;
			string text = "Spectate.cfg";
			string text2 = Path.Combine(Paths.ConfigPath, text);
			Logger.Info("cfgPath = " + text2);
			Conf = new ConfigFile(text2, true);
			ConfigWatcher = new FileSystemWatcher(Paths.ConfigPath, text)
			{
				NotifyFilter = NotifyFilters.LastWrite,
				EnableRaisingEvents = true
			};
			ConfigWatcher.Changed += async delegate
			{
				ConfigWatcher.EnableRaisingEvents = false;
				await Task.Delay(500);
				Logger.Debug("Reloading config file due to external change...");
				Conf.Reload();
				await Task.Delay(250);
				Process();
				await Task.Delay(250);
				ConfigWatcher.EnableRaisingEvents = true;
			};
			int num = 1;
			string text3 = $"({num++}) Behavior";
			SwitchOnDeathConf = Conf.Bind<bool>(text3, "Switch on Death", false, "automatically switch to spectate upon death");
			DefaultFreecamViewConf = Conf.Bind<bool>(text3, "Default to Free-Look View", true, "start game sessions with free-look enabled by default");
			AutoTransitionToFollowViewConf = Conf.Bind<bool>(text3, "Auto Transition to Temporary Follow View", true, "automatically transition to temporary follow view after idling in free-look");
			NoPosLerpOnSwitchTargetConf = Conf.Bind<bool>(text3, "No Position Smoothing on Target Switch", false, "disable camera position smoothing when switching spectate targets in free-look");
			AutoTransitionDelayConf = Conf.Bind<float>(text3, "Auto Transition Delay", 3f, "delay after no mouse input in seconds before automatically transitioning to temporary follow view (min: 0.2)");
			AutoTransitionDelayConf.AddRule(ConfigEntryRule.Min, 0.2f);
			text3 = $"({num++}) Sensitivity";
			ScrollSensitivityConf = Conf.Bind<float>(text3, "Scroll Sensitivity", 0.5f, "sensitivity for scroll wheel based adjustments (camera distance, camera pitch, orbit center offset) (min: 0.01)");
			ScrollSensitivityConf.AddRule(ConfigEntryRule.Min, 0.01f);
			FreecamSensitivityConf = Conf.Bind<float>(text3, "Free-Look Sensitivity", 1f, "sensitivity for free-look mouse movement (min: 0.01)");
			FreecamSensitivityConf.AddRule(ConfigEntryRule.Min, 0.01f);
			FreecamLerpGainConf = Conf.Bind<float>(text3, "Free-Look Smoothing Rate", 6f, "rate at which camera snaps to target: higher is snappier, lower is smoother (min: 1.0)");
			FreecamLerpGainConf.AddRule(ConfigEntryRule.Min, 1f);
			CameraXZLerpGainConf = Conf.Bind<float>(text3, "Camera XZ-Position Smoothing Rate", 15f, "rate at which camera horizontal position snaps to target: higher is snappier, lower is smoother (min: 7.0)");
			CameraXZLerpGainConf.AddRule(ConfigEntryRule.Min, 7f);
			CameraYLerpGainConf = Conf.Bind<float>(text3, "Camera Y-Position Smoothing Rate", 11f, "rate at which camera vertical position snaps to target: higher is snappier, lower is smoother (min: 7.0)");
			CameraYLerpGainConf.AddRule(ConfigEntryRule.Min, 7f);
			text3 = $"({num++}) Camera Settings <SYNCED>";
			CameraOrbitVerticalOffsetConf = Conf.Bind<float>(text3, "Camera Orbit Vertical Offset", 0.325f, $"vertical offset of the camera orbit center/point from the player's position (min: {-1f:F2}, max: {5f:F2})");
			CameraOrbitVerticalOffsetConf.AddRule(ConfigEntryRule.Min, -1f);
			CameraOrbitVerticalOffsetConf.AddRule(ConfigEntryRule.Max, 5f);
			CameraDistanceConf = Conf.Bind<float>(text3, "Camera Distance", 0.625f, $"distance of the camera from the orbit point (min: {0.1f:F2}, max: {5f:F2})");
			CameraDistanceConf.AddRule(ConfigEntryRule.Min, 0.1f);
			CameraDistanceConf.AddRule(ConfigEntryRule.Max, 5f);
			CameraPitchAngleDegConf = Conf.Bind<float>(text3, "Camera Pitch Angle (deg)", -18.75f, $"pitch angle of the camera in degrees (min: {-89f:F2}, max: {89f:F2})");
			CameraPitchAngleDegConf.AddRule(ConfigEntryRule.Min, -89f);
			CameraPitchAngleDegConf.AddRule(ConfigEntryRule.Max, 89f);
			text3 = $"({num++}) Keybinds";
			foreach (KeyValuePair<SpectateInputAction, KeybindSetting> keybindSettingConf in KeybindSettingConfs)
			{
				keybindSettingConf.Deconstruct(out var key, out value);
				SpectateInputAction key2 = key;
				KeybindSetting keybindSetting = value;
				KeybindConfs[key2] = Conf.Bind<KeyCode>(text3, keybindSetting.Name, keybindSetting.DefaultKey, keybindSetting.Description);
			}
			text3 = "(Z) Dev";
			DebugConf = Conf.Bind<bool>(text3, "Enable Debug Logs", false, "debug logging");
			DevOptsConf = Conf.Bind<string>(text3, "Dev Options", "", "if you know you know");
		}

		internal static void WriteConfigIfDirty()
		{
			if (_configDirty)
			{
				CameraDistanceConf.Value = _cameraDistanceCache;
				CameraOrbitVerticalOffsetConf.Value = _cameraOrbitVerticalOffsetCache;
				CameraPitchAngleDegConf.Value = _cameraPitchAngleDegCache;
				AutoTransitionToFollowViewConf.Value = _autoTransitionToFollowViewCache;
				Conf.Save();
				_configDirty = false;
			}
		}
	}
	[Flags]
	public enum eDevOpts
	{
		None = 0,
		AllowSpectatingAnytime = 1,
		ExperimentalFeatures = 2
	}
	public struct KeybindSetting
	{
		public string Name;

		public string Description;

		public KeyCode DefaultKey;
	}
	public enum SpectateInputAction
	{
		None,
		ToggleSpectate,
		ToggleFreecam,
		ToggleAutoFollow,
		ToggleMenu
	}
}