Decompiled source of SpectatorPlus v1.0.1

Bepinex/plugins/SpectatorPlus/SpectatorPlus.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet.Object;
using HarmonyLib;
using Steamworks;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("SpectatorPlus")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SpectatorPlus")]
[assembly: AssemblyCopyright("Copyright ©  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("98cf1544-b8e1-4464-a01a-376394e5ef86")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace SpectatorPlus
{
	[BepInPlugin("com.spectatorplus.mod", "SpectatorPlus", "1.0.0")]
	[BepInProcess("MageArena.exe")]
	public class SpectatorPlus : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(MainMenuManager), "ActuallyStartGameActually")]
		internal static class KillSpectatorOnGameStartPatch
		{
			private static void Postfix(MainMenuManager __instance)
			{
				ManualLogSource modLogger = ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)"ActuallyStartGameActually called - Game started, checking for spectator player");
				}
				if ((Object)(object)__instance.pm != (Object)null)
				{
					ManualLogSource modLogger2 = ModLogger;
					if (modLogger2 != null)
					{
						modLogger2.LogInfo((object)$"Found pm: {__instance.pm.playername}, IsOwner: {((NetworkBehaviour)__instance.pm).IsOwner}");
					}
					if (IsSpectatorPlayer(__instance.pm))
					{
						ManualLogSource modLogger3 = ModLogger;
						if (modLogger3 != null)
						{
							modLogger3.LogInfo((object)"Local player detected as spectator! Killing immediately");
						}
						((MonoBehaviour)__instance).StartCoroutine(KillSpectatorCoroutine(__instance.pm));
					}
					else
					{
						ManualLogSource modLogger4 = ModLogger;
						if (modLogger4 != null)
						{
							modLogger4.LogInfo((object)"Player is not spectator (not owner)");
						}
					}
				}
				else
				{
					ManualLogSource modLogger5 = ModLogger;
					if (modLogger5 != null)
					{
						modLogger5.LogWarning((object)"pm is null in ActuallyStartGameActually");
					}
				}
			}
		}

		[HarmonyPatch(typeof(PlayerRespawnManager), "RespawnRoutine")]
		internal static class DisableRespawnPatch
		{
			private static bool Prefix(PlayerRespawnManager __instance, ref IEnumerator __result)
			{
				ManualLogSource modLogger = ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)"RespawnRoutine called");
				}
				if (IsSpectatorPlayer(__instance.pmv))
				{
					ManualLogSource modLogger2 = ModLogger;
					if (modLogger2 != null)
					{
						modLogger2.LogInfo((object)"Replacing respawn routine with no-op for spectator player");
					}
					__result = NoOpCoroutine();
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(PlayerRespawnManager), "ColiRespawnRoutine")]
		internal static class DisableColiRespawnPatch
		{
			private static bool Prefix(PlayerRespawnManager __instance, ref IEnumerator __result)
			{
				ManualLogSource modLogger = ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)"ColiRespawnRoutine called");
				}
				if (IsSpectatorPlayer(__instance.pmv))
				{
					ManualLogSource modLogger2 = ModLogger;
					if (modLogger2 != null)
					{
						modLogger2.LogInfo((object)"Replacing coli respawn routine with no-op for spectator player");
					}
					__result = NoOpCoroutine();
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(PlayerRespawnManager), "SpectateRoutine")]
		internal static class CustomSpectateRoutinePatch
		{
			private static bool Prefix(PlayerRespawnManager __instance)
			{
				ManualLogSource modLogger = ModLogger;
				if (modLogger != null)
				{
					modLogger.LogInfo((object)"SpectateRoutine called");
				}
				if (IsSpectatorPlayer(__instance.pmv))
				{
					ManualLogSource modLogger2 = ModLogger;
					if (modLogger2 != null)
					{
						modLogger2.LogInfo((object)"Starting custom spectate routine for spectator");
					}
					((MonoBehaviour)__instance).StartCoroutine(CustomSpectateCoroutine(__instance));
					return false;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(PlayerRespawnManager), "EndGame")]
		internal static class EndGamePatch
		{
			private static void Prefix()
			{
				try
				{
					ManualLogSource modLogger = ModLogger;
					if (modLogger != null)
					{
						modLogger.LogInfo((object)"Game ended - clearing all spectator states and resetting camera");
					}
					ResetAllStates();
				}
				catch (Exception ex)
				{
					ManualLogSource modLogger2 = ModLogger;
					if (modLogger2 != null)
					{
						modLogger2.LogError((object)("Error in EndGame patch: " + ex.Message));
					}
				}
			}
		}

		[CompilerGenerated]
		private sealed class <CustomSpectateCoroutine>d__55 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public PlayerRespawnManager manager;

			private GameObject[] <allPlayers>5__1;

			private PlayerMovement <firstAlivePlayer>5__2;

			private int <i>5__3;

			private PlayerMovement <pm>5__4;

			private int <i>5__5;

			private PlayerMovement <pm>5__6;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <CustomSpectateCoroutine>d__55(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<allPlayers>5__1 = null;
				<firstAlivePlayer>5__2 = null;
				<pm>5__4 = null;
				<pm>5__6 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_0071: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					ModLogger.LogInfo((object)"Starting custom spectate routine");
					if ((Object)(object)manager == (Object)null)
					{
						ModLogger.LogError((object)"PlayerRespawnManager is null in CustomSpectateCoroutine");
						return false;
					}
					<>2__current = (object)new WaitForSeconds(0.5f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<allPlayers>5__1 = GameObject.FindGameObjectsWithTag("Player");
					if (<allPlayers>5__1 == null)
					{
						ModLogger.LogWarning((object)"No players found for spectating (null array)");
						return false;
					}
					ModLogger.LogInfo((object)$"Found {<allPlayers>5__1.Length} total players");
					if (<allPlayers>5__1.Length == 0)
					{
						ModLogger.LogWarning((object)"No players found for spectating");
						return false;
					}
					<i>5__3 = 0;
					while (<i>5__3 < <allPlayers>5__1.Length)
					{
						if ((Object)(object)<allPlayers>5__1[<i>5__3] != (Object)null)
						{
							<pm>5__4 = <allPlayers>5__1[<i>5__3].GetComponent<PlayerMovement>();
							if ((Object)(object)<pm>5__4 != (Object)null)
							{
								ModLogger.LogInfo((object)$"Player {<i>5__3}: {<pm>5__4.playername}, IsOwner: {((NetworkBehaviour)<pm>5__4).IsOwner}, IsAlive: {IsPlayerAlive(<pm>5__4)}");
							}
							<pm>5__4 = null;
						}
						<i>5__3++;
					}
					<firstAlivePlayer>5__2 = null;
					<i>5__5 = 0;
					while (<i>5__5 < <allPlayers>5__1.Length)
					{
						if ((Object)(object)<allPlayers>5__1[<i>5__5] != (Object)null)
						{
							<pm>5__6 = <allPlayers>5__1[<i>5__5].GetComponent<PlayerMovement>();
							if ((Object)(object)<pm>5__6 != (Object)null && IsPlayerAlive(<pm>5__6) && !IsSpectatorPlayer(<pm>5__6))
							{
								<firstAlivePlayer>5__2 = <pm>5__6;
								currentPlayerIndex = <i>5__5;
								ModLogger.LogInfo((object)$"Selected player {<i>5__5}: {<pm>5__6.playername} for spectating");
								break;
							}
							<pm>5__6 = null;
						}
						<i>5__5++;
					}
					if ((Object)(object)<firstAlivePlayer>5__2 == (Object)null)
					{
						ModLogger.LogWarning((object)"No alive players found for spectating");
						return false;
					}
					ModLogger.LogInfo((object)("Starting to spectate " + <firstAlivePlayer>5__2.playername));
					StartSpectating(<firstAlivePlayer>5__2);
					break;
				case 2:
					<>1__state = -1;
					UpdateSpectateCamera();
					if ((Object)(object)spectateTarget == (Object)null || !IsPlayerAlive(spectateTarget))
					{
						ModLogger.LogInfo((object)"Current spectate target is no longer valid, cycling to next player");
						CycleToNextPlayer();
					}
					break;
				}
				if (isSpectator)
				{
					<>2__current = null;
					<>1__state = 2;
					return true;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <KillSpectatorCoroutine>d__54 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public PlayerMovement pm;

			private PlayerRespawnManager <prm>5__1;

			private Vector3 <currentPos>5__2;

			private Vector3 <newPos>5__3;

			private GameObject <netItemManager>5__4;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <KillSpectatorCoroutine>d__54(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<prm>5__1 = null;
				<netItemManager>5__4 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0067: Unknown result type (might be due to invalid IL or missing references)
				//IL_0071: Expected O, but got Unknown
				//IL_00de: 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_0110: 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_0126: Unknown result type (might be due to invalid IL or missing references)
				//IL_013c: 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)
				//IL_01e0: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					ModLogger.LogInfo((object)"KillSpectatorCoroutine started");
					if ((Object)(object)pm == (Object)null)
					{
						ModLogger.LogError((object)"PlayerMovement is null in KillSpectatorCoroutine");
						return false;
					}
					<>2__current = (object)new WaitForSeconds(2f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					ModLogger.LogInfo((object)("Attempting to kill spectator player: " + pm?.playername));
					if ((Object)(object)pm != (Object)null && (Object)(object)((Component)pm).transform != (Object)null)
					{
						<currentPos>5__2 = ((Component)pm).transform.position;
						<newPos>5__3 = new Vector3(<currentPos>5__2.x, <currentPos>5__2.y - 100f, <currentPos>5__2.z);
						((Component)pm).transform.position = <newPos>5__3;
						ModLogger.LogInfo((object)$"Teleported player to {<newPos>5__3} (100 units below original position)");
					}
					if (IsPlayerAlive(pm))
					{
						ModLogger.LogInfo((object)"Player is alive, setting health to 0 and marking as dead");
						SetPlayerHealth(pm, 0f);
						pm.isDead = true;
						ModLogger.LogInfo((object)"Player killed - letting game systems handle death naturally");
					}
					else
					{
						ModLogger.LogInfo((object)"Player is already dead");
					}
					HideUIComponents();
					isSpectator = true;
					ModLogger.LogInfo((object)"Spectator marked and killed");
					<>2__current = (object)new WaitForSeconds(1f);
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					<prm>5__1 = GetPlayerRespawnManager(pm);
					if ((Object)(object)<prm>5__1 == (Object)null)
					{
						<netItemManager>5__4 = GameObject.FindGameObjectWithTag("NetItemManager");
						if ((Object)(object)<netItemManager>5__4 != (Object)null)
						{
							<prm>5__1 = <netItemManager>5__4.GetComponent<PlayerRespawnManager>();
						}
						<netItemManager>5__4 = null;
					}
					if ((Object)(object)<prm>5__1 != (Object)null)
					{
						ModLogger.LogInfo((object)"Manually starting custom spectate routine");
						((MonoBehaviour)<prm>5__1).StartCoroutine(CustomSpectateCoroutine(<prm>5__1));
					}
					else
					{
						ModLogger.LogError((object)"Could not find PlayerRespawnManager to start spectating!");
					}
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <NoOpCoroutine>d__49 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <NoOpCoroutine>d__49(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				if (<>1__state != 0)
				{
					return false;
				}
				<>1__state = -1;
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		internal static ManualLogSource ModLogger;

		private static Harmony harmony;

		private static bool isSpectator = false;

		private static bool isFreecamMode = false;

		private static PlayerMovement spectateTarget = null;

		private static int currentPlayerIndex = 0;

		private static float spectateDistance = 5f;

		private static float minSpectateDistance = 2f;

		private static float maxSpectateDistance = 15f;

		private static float spectateHeight = 3f;

		private static bool invertMouseX = false;

		private static bool invertMouseY = false;

		private static float cameraSmoothing = 5f;

		private static Vector3 targetCameraPosition;

		private static Quaternion targetCameraRotation;

		private static Vector3 freecamPosition;

		private static float freecamYaw = 0f;

		private static float freecamPitch = 0f;

		private static float freecamSpeed = 10f;

		private static float freecamSensitivity = 2f;

		private static Vector3 originalCameraPosition;

		private static Quaternion originalCameraRotation;

		private static Transform originalCameraParent;

		private static ConfigEntry<bool> configInvertMouseX;

		private static ConfigEntry<bool> configInvertMouseY;

		private static ConfigEntry<float> configCameraSmoothing;

		private static ConfigEntry<float> configMouseSensitivity;

		private static ConfigEntry<float> configMinZoomDistance;

		private static ConfigEntry<float> configMaxZoomDistance;

		private static ConfigEntry<float> configDefaultZoomDistance;

		private static ConfigEntry<float> configZoomSpeed;

		private static ConfigEntry<float> configSpectateHeight;

		private static ConfigEntry<float> configFreecamSpeed;

		private static ConfigEntry<KeyCode> configFreecamToggleKey;

		private static ConfigEntry<KeyCode> configPlayerCycleKey;

		private void Awake()
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			ModLogger = Logger.CreateLogSource("SpectatorPlus");
			ModLogger.LogInfo((object)"SpectatorPlus mod loaded!");
			InitializeConfig();
			harmony = new Harmony("com.spectatorplus.mod");
			harmony.PatchAll(typeof(SpectatorPlus).Assembly);
			ModLogger.LogInfo((object)"Harmony patches applied!");
		}

		private void InitializeConfig()
		{
			configInvertMouseX = ((BaseUnityPlugin)this).Config.Bind<bool>("Camera", "InvertMouseX", false, "Invert horizontal mouse movement");
			configInvertMouseY = ((BaseUnityPlugin)this).Config.Bind<bool>("Camera", "InvertMouseY", false, "Invert vertical mouse movement");
			configCameraSmoothing = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "CameraSmoothing", 8f, "Camera smoothing speed for spectating (higher = smoother)");
			configMouseSensitivity = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "MouseSensitivity", 2f, "Mouse sensitivity");
			configMinZoomDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Spectating", "MinZoomDistance", 2f, "Minimum zoom distance when spectating");
			configMaxZoomDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Spectating", "MaxZoomDistance", 15f, "Maximum zoom distance when spectating");
			configDefaultZoomDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Spectating", "DefaultZoomDistance", 5f, "Default zoom distance when spectating");
			configZoomSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Spectating", "ZoomSpeed", 5f, "Zoom speed when using mouse wheel");
			configSpectateHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Spectating", "SpectateHeight", 3f, "Height offset when spectating players");
			configFreecamSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Freecam", "FreecamSpeed", 10f, "Movement speed in freecam mode");
			configFreecamToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "FreecamToggleKey", (KeyCode)286, "Key to toggle freecam mode on/off");
			configPlayerCycleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "PlayerCycleKey", (KeyCode)323, "Key to cycle between players when spectating");
			ApplyConfig();
			ValidateAndFixConfig();
			ModLogger.LogInfo((object)"BepInEx configuration initialized");
		}

		internal static void ApplyConfig()
		{
			invertMouseX = configInvertMouseX.Value;
			invertMouseY = configInvertMouseY.Value;
			cameraSmoothing = configCameraSmoothing.Value;
			freecamSensitivity = configMouseSensitivity.Value;
			minSpectateDistance = configMinZoomDistance.Value;
			maxSpectateDistance = configMaxZoomDistance.Value;
			spectateDistance = configDefaultZoomDistance.Value;
			spectateHeight = configSpectateHeight.Value;
			freecamSpeed = configFreecamSpeed.Value;
			ModLogger.LogInfo((object)$"Config applied - InvertX: {invertMouseX}, InvertY: {invertMouseY}, Smoothing: {cameraSmoothing}, Sensitivity: {freecamSensitivity}, FreecamSpeed: {freecamSpeed}");
		}

		internal static void ValidateAndFixConfig()
		{
			ModLogger.LogInfo((object)"Validating configuration values...");
			if (configCameraSmoothing.Value < 0.1f)
			{
				configCameraSmoothing.Value = 0.1f;
				ModLogger.LogWarning((object)"Camera smoothing was too low, set to minimum value 0.1");
			}
			if (configMouseSensitivity.Value < 0.1f)
			{
				configMouseSensitivity.Value = 0.1f;
				ModLogger.LogWarning((object)"Mouse sensitivity was too low, set to minimum value 0.1");
			}
			if (configMinZoomDistance.Value < 0.5f)
			{
				configMinZoomDistance.Value = 0.5f;
				ModLogger.LogWarning((object)"Min zoom distance was too low, set to minimum value 0.5");
			}
			if (configMaxZoomDistance.Value < configMinZoomDistance.Value)
			{
				configMaxZoomDistance.Value = configMinZoomDistance.Value + 5f;
				ModLogger.LogWarning((object)"Max zoom distance was lower than min, adjusted automatically");
			}
			if (configDefaultZoomDistance.Value < configMinZoomDistance.Value || configDefaultZoomDistance.Value > configMaxZoomDistance.Value)
			{
				configDefaultZoomDistance.Value = (configMinZoomDistance.Value + configMaxZoomDistance.Value) / 2f;
				ModLogger.LogWarning((object)"Default zoom distance was out of range, set to middle value");
			}
			if (configZoomSpeed.Value < 0.1f)
			{
				configZoomSpeed.Value = 0.1f;
				ModLogger.LogWarning((object)"Zoom speed was too low, set to minimum value 0.1");
			}
			if (configFreecamSpeed.Value < 0.1f)
			{
				configFreecamSpeed.Value = 0.1f;
				ModLogger.LogWarning((object)"Freecam speed was too low, set to minimum value 0.1");
			}
			ModLogger.LogInfo((object)"Configuration validation complete");
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		private void Update()
		{
			//IL_0023: 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)
			if (IsCurrentPlayerHost() && isSpectator)
			{
				if (Input.GetKeyDown(configFreecamToggleKey.Value))
				{
					ToggleFreecam();
				}
				if (isFreecamMode)
				{
					UpdateFreecam();
				}
				if (Input.GetKeyDown(configPlayerCycleKey.Value) && !isFreecamMode)
				{
					CycleToNextPlayer();
				}
			}
		}

		internal static bool IsPlayerAlive(PlayerMovement pm)
		{
			if ((Object)(object)pm == (Object)null)
			{
				return false;
			}
			if (pm.isDead)
			{
				return false;
			}
			if (pm.playerHealth <= 0f)
			{
				return false;
			}
			return true;
		}

		internal static void SetPlayerHealth(PlayerMovement pm, float health)
		{
			if (!((Object)(object)pm == (Object)null))
			{
				pm.playerHealth = health;
			}
		}

		internal static PlayerRespawnManager GetPlayerRespawnManager(PlayerMovement pm)
		{
			if ((Object)(object)pm == (Object)null)
			{
				return null;
			}
			return pm.prm;
		}

		[IteratorStateMachine(typeof(<NoOpCoroutine>d__49))]
		internal static IEnumerator NoOpCoroutine()
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <NoOpCoroutine>d__49(0);
		}

		internal static bool IsSpectatorPlayer(PlayerMovement pm)
		{
			if ((Object)(object)pm == (Object)null)
			{
				return false;
			}
			if (!IsCurrentPlayerHost())
			{
				return false;
			}
			return ((NetworkBehaviour)pm).IsOwner;
		}

		internal static bool IsCurrentPlayerHost()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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_0048: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: 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)
			try
			{
				if ((Object)(object)BootstrapManager.instance == (Object)null)
				{
					return false;
				}
				if (BootstrapManager.CurrentLobbyID == 0)
				{
					return false;
				}
				CSteamID val = default(CSteamID);
				((CSteamID)(ref val))..ctor(BootstrapManager.CurrentLobbyID);
				CSteamID lobbyOwner = SteamMatchmaking.GetLobbyOwner(val);
				CSteamID steamID = SteamUser.GetSteamID();
				if (lobbyOwner == CSteamID.Nil || steamID == CSteamID.Nil)
				{
					return false;
				}
				return lobbyOwner == steamID;
			}
			catch (Exception ex)
			{
				ManualLogSource modLogger = ModLogger;
				if (modLogger != null)
				{
					modLogger.LogError((object)("Error checking host status: " + ex.Message));
				}
				return false;
			}
		}

		internal static void HideUIComponents()
		{
			//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)
			if (!IsCurrentPlayerHost())
			{
				return;
			}
			ModLogger.LogInfo((object)"Hiding UI components for spectator mode");
			if (SceneManager.sceneCount == 0)
			{
				ModLogger.LogWarning((object)"No scenes loaded, cannot hide UI components");
				return;
			}
			Scene sceneByName = SceneManager.GetSceneByName("GameScene");
			if (((Scene)(ref sceneByName)).isLoaded)
			{
				ModLogger.LogInfo((object)"Found GameScene, hiding inventory and level up UI");
				GameObject[] rootGameObjects = ((Scene)(ref sceneByName)).GetRootGameObjects();
				if (rootGameObjects == null)
				{
					return;
				}
				GameObject[] array = rootGameObjects;
				foreach (GameObject val in array)
				{
					if ((Object)(object)val != (Object)null && ((Object)val).name == "Canvas")
					{
						Transform val2 = val.transform.Find("INVUI");
						if ((Object)(object)val2 != (Object)null)
						{
							((Component)val2).gameObject.SetActive(false);
							ModLogger.LogInfo((object)"Hidden Canvas/INVUI");
						}
						Transform val3 = val.transform.Find("lvluptext");
						if ((Object)(object)val3 != (Object)null)
						{
							((Component)val3).gameObject.SetActive(false);
							ModLogger.LogInfo((object)"Hidden Canvas/lvluptext");
						}
						break;
					}
				}
			}
			else
			{
				ModLogger.LogWarning((object)"GameScene not found or not loaded");
			}
		}

		internal static void RestoreUIComponents()
		{
			//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)
			if (!IsCurrentPlayerHost())
			{
				return;
			}
			ModLogger.LogInfo((object)"Restoring UI components");
			if (SceneManager.sceneCount == 0)
			{
				ModLogger.LogWarning((object)"No scenes loaded, cannot restore UI components");
				return;
			}
			Scene sceneByName = SceneManager.GetSceneByName("GameScene");
			if (!((Scene)(ref sceneByName)).isLoaded)
			{
				return;
			}
			GameObject[] rootGameObjects = ((Scene)(ref sceneByName)).GetRootGameObjects();
			if (rootGameObjects == null)
			{
				return;
			}
			GameObject[] array = rootGameObjects;
			foreach (GameObject val in array)
			{
				if ((Object)(object)val != (Object)null && ((Object)val).name == "Canvas")
				{
					Transform val2 = val.transform.Find("INVUI");
					if ((Object)(object)val2 != (Object)null)
					{
						((Component)val2).gameObject.SetActive(true);
						ModLogger.LogInfo((object)"Restored Canvas/INVUI");
					}
					Transform val3 = val.transform.Find("lvluptext");
					if ((Object)(object)val3 != (Object)null)
					{
						((Component)val3).gameObject.SetActive(true);
						ModLogger.LogInfo((object)"Restored Canvas/lvluptext");
					}
					break;
				}
			}
		}

		[IteratorStateMachine(typeof(<KillSpectatorCoroutine>d__54))]
		internal static IEnumerator KillSpectatorCoroutine(PlayerMovement pm)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <KillSpectatorCoroutine>d__54(0)
			{
				pm = pm
			};
		}

		[IteratorStateMachine(typeof(<CustomSpectateCoroutine>d__55))]
		internal static IEnumerator CustomSpectateCoroutine(PlayerRespawnManager manager)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CustomSpectateCoroutine>d__55(0)
			{
				manager = manager
			};
		}

		internal static void StartSpectating(PlayerMovement targetPlayer)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)targetPlayer == (Object)null)
			{
				ModLogger.LogError((object)"Target player is null in StartSpectating");
				return;
			}
			if ((Object)(object)((Component)targetPlayer).transform == (Object)null)
			{
				ModLogger.LogError((object)"Target player transform is null in StartSpectating");
				return;
			}
			ModLogger.LogInfo((object)("Starting to spectate " + targetPlayer.playername));
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null && (Object)(object)spectateTarget == (Object)null)
			{
				ModLogger.LogInfo((object)("Main camera found: " + ((Object)main).name));
				originalCameraPosition = ((Component)main).transform.position;
				originalCameraRotation = ((Component)main).transform.rotation;
				originalCameraParent = ((Component)main).transform.parent;
				ManualLogSource modLogger = ModLogger;
				object arg = originalCameraPosition;
				object arg2 = originalCameraRotation;
				Transform obj = originalCameraParent;
				modLogger.LogInfo((object)string.Format("Stored original camera state - Pos: {0}, Rot: {1}, Parent: {2}", arg, arg2, ((obj != null) ? ((Object)obj).name : null) ?? "None"));
				((Component)main).transform.SetParent((Transform)null);
				freecamYaw = 0f;
				freecamPitch = 0f;
			}
			else if ((Object)(object)main == (Object)null)
			{
				ModLogger.LogError((object)"Main camera not found!");
				return;
			}
			spectateTarget = targetPlayer;
			ModLogger.LogInfo((object)("Set spectate target to " + targetPlayer.playername));
			if (isFreecamMode)
			{
				ModLogger.LogInfo((object)"Disabling freecam before starting spectating");
				DisableFreecam();
			}
			freecamYaw = 0f;
			freecamPitch = 0f;
			spectateDistance = configDefaultZoomDistance.Value;
			ModLogger.LogInfo((object)("Reset camera angles and zoom for " + targetPlayer.playername));
		}

		internal static void ReloadConfig()
		{
			ModLogger.LogInfo((object)"Reloading configuration...");
			ApplyConfig();
			ModLogger.LogInfo((object)"Configuration reloaded successfully");
		}

		internal static void UpdateSpectateCamera()
		{
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0294: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)spectateTarget == (Object)null || isFreecamMode)
			{
				return;
			}
			if ((Object)(object)((Component)spectateTarget).transform == (Object)null)
			{
				ModLogger.LogWarning((object)"Spectate target transform is null, cannot update camera");
				return;
			}
			Camera main = Camera.main;
			if (!((Object)(object)main == (Object)null))
			{
				Transform val = spectateTarget.SpectatePoint;
				if ((Object)(object)val == (Object)null)
				{
					val = ((Component)spectateTarget).transform;
				}
				float num = Input.GetAxis("Mouse X") * freecamSensitivity;
				float num2 = Input.GetAxis("Mouse Y") * freecamSensitivity;
				if (invertMouseX)
				{
					num = 0f - num;
				}
				if (invertMouseY)
				{
					num2 = 0f - num2;
				}
				float axis = Input.GetAxis("Mouse ScrollWheel");
				if (axis != 0f)
				{
					spectateDistance -= axis * configZoomSpeed.Value;
					spectateDistance = Mathf.Clamp(spectateDistance, minSpectateDistance, maxSpectateDistance);
				}
				freecamYaw += num;
				freecamPitch -= num2 / 2f;
				freecamPitch = Mathf.Clamp(freecamPitch, -60f, 60f);
				float num3 = freecamYaw * ((float)Math.PI / 180f);
				float num4 = freecamPitch * ((float)Math.PI / 180f);
				Vector3 val2 = default(Vector3);
				((Vector3)(ref val2))..ctor(Mathf.Cos(num3) * spectateDistance, 0f, Mathf.Sin(num3) * spectateDistance);
				float num5 = spectateHeight + Mathf.Sin(num4) * spectateDistance;
				float num6 = Mathf.Cos(num4);
				val2 *= num6;
				targetCameraPosition = val.position + Vector3.up * num5 + val2;
				Vector3 val3 = val.position + Vector3.up * 1.5f;
				targetCameraRotation = Quaternion.LookRotation(val3 - targetCameraPosition);
				if (cameraSmoothing > 0f)
				{
					((Component)main).transform.position = Vector3.Lerp(((Component)main).transform.position, targetCameraPosition, Time.deltaTime * cameraSmoothing);
					((Component)main).transform.rotation = Quaternion.Lerp(((Component)main).transform.rotation, targetCameraRotation, Time.deltaTime * cameraSmoothing);
				}
				else
				{
					((Component)main).transform.position = targetCameraPosition;
					((Component)main).transform.rotation = targetCameraRotation;
				}
			}
		}

		internal static void StopSpectating()
		{
			//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)
			ModLogger.LogInfo((object)"Stopping spectating");
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				if ((Object)(object)((Component)main).transform == (Object)null)
				{
					ModLogger.LogError((object)"Main camera transform is null when stopping spectating");
					return;
				}
				((Component)main).transform.position = originalCameraPosition;
				((Component)main).transform.rotation = originalCameraRotation;
				if ((Object)(object)originalCameraParent != (Object)null)
				{
					((Component)main).transform.SetParent(originalCameraParent);
				}
			}
			spectateTarget = null;
			isSpectator = false;
		}

		internal static void CycleToNextPlayer()
		{
			GameObject[] array = GameObject.FindGameObjectsWithTag("Player");
			if (array == null || array.Length == 0)
			{
				return;
			}
			int num = 0;
			do
			{
				currentPlayerIndex = (currentPlayerIndex + 1) % array.Length;
				if ((Object)(object)array[currentPlayerIndex] != (Object)null)
				{
					PlayerMovement component = array[currentPlayerIndex].GetComponent<PlayerMovement>();
					if ((Object)(object)component != (Object)null && IsPlayerAlive(component) && !IsSpectatorPlayer(component))
					{
						StartSpectating(component);
						return;
					}
				}
				num++;
			}
			while (num < array.Length);
			ModLogger.LogWarning((object)"No alive players found to spectate");
		}

		internal static void ResetAllStates()
		{
			if (IsCurrentPlayerHost())
			{
				ModLogger.LogInfo((object)"Resetting all spectator and freecam states...");
				StopSpectating();
				DisableFreecam();
				RestoreUIComponents();
				isSpectator = false;
				isFreecamMode = false;
				spectateTarget = null;
				currentPlayerIndex = 0;
				ModLogger.LogInfo((object)"All states reset.");
			}
		}

		internal static void ToggleFreecam()
		{
			if (isFreecamMode)
			{
				DisableFreecam();
			}
			else
			{
				EnableFreecam();
			}
		}

		internal static void EnableFreecam()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			if (!isFreecamMode)
			{
				ModLogger.LogInfo((object)"Enabling freecam mode");
				Camera main = Camera.main;
				if ((Object)(object)main == (Object)null)
				{
					ModLogger.LogError((object)"Could not find main camera for freecam");
					return;
				}
				if ((Object)(object)((Component)main).transform == (Object)null)
				{
					ModLogger.LogError((object)"Main camera transform is null for freecam");
					return;
				}
				freecamPosition = ((Component)main).transform.position;
				freecamYaw = ((Component)main).transform.eulerAngles.y;
				freecamPitch = ((Component)main).transform.eulerAngles.x;
				((Component)main).transform.SetParent((Transform)null);
				isFreecamMode = true;
				ModLogger.LogInfo((object)$"Freecam enabled at position {freecamPosition}");
			}
		}

		internal static void DisableFreecam()
		{
			//IL_007c: 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)
			if (!isFreecamMode)
			{
				return;
			}
			ModLogger.LogInfo((object)"Disabling freecam mode");
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				if ((Object)(object)((Component)main).transform == (Object)null)
				{
					ModLogger.LogError((object)"Main camera transform is null when disabling freecam");
					return;
				}
				if ((Object)(object)spectateTarget != (Object)null)
				{
					UpdateSpectateCamera();
				}
				else
				{
					((Component)main).transform.position = originalCameraPosition;
					((Component)main).transform.rotation = originalCameraRotation;
					if ((Object)(object)originalCameraParent != (Object)null)
					{
						((Component)main).transform.SetParent(originalCameraParent);
					}
				}
			}
			isFreecamMode = false;
			ModLogger.LogInfo((object)"Freecam disabled");
		}

		internal static void UpdateFreecam()
		{
			//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_00f5: 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_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: 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_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: 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_014d: 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_0161: 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_0168: 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_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			if (!isFreecamMode)
			{
				return;
			}
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return;
			}
			if ((Object)(object)((Component)main).transform == (Object)null)
			{
				ModLogger.LogError((object)"Main camera transform is null in UpdateFreecam");
				return;
			}
			float num = Input.GetAxis("Mouse X") * freecamSensitivity;
			float num2 = Input.GetAxis("Mouse Y") * freecamSensitivity;
			freecamYaw += num;
			freecamPitch -= num2;
			freecamPitch = Mathf.Clamp(freecamPitch, -80f, 80f);
			float num3 = freecamSpeed;
			if (Input.GetKey((KeyCode)304))
			{
				num3 *= 2.5f;
			}
			if (Input.GetKey((KeyCode)306))
			{
				num3 *= 0.5f;
			}
			Vector3 val = Vector3.zero;
			if (Input.GetKey((KeyCode)119))
			{
				val += Vector3.forward;
			}
			if (Input.GetKey((KeyCode)115))
			{
				val += Vector3.back;
			}
			if (Input.GetKey((KeyCode)97))
			{
				val += Vector3.left;
			}
			if (Input.GetKey((KeyCode)100))
			{
				val += Vector3.right;
			}
			if (Input.GetKey((KeyCode)32))
			{
				val += Vector3.up;
			}
			if (Input.GetKey((KeyCode)308))
			{
				val += Vector3.down;
			}
			if (((Vector3)(ref val)).magnitude > 0f)
			{
				val = ((Component)main).transform.TransformDirection(((Vector3)(ref val)).normalized);
				freecamPosition += val * num3 * Time.deltaTime;
			}
			((Component)main).transform.position = freecamPosition;
			((Component)main).transform.rotation = Quaternion.Euler(freecamPitch, freecamYaw, 0f);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}