Decompiled source of SpectatorMode v1.0.0

SpectatorMode.dll

Decompiled 16 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Collections;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SpectatorMode")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("SpectatorMode")]
[assembly: AssemblyTitle("SpectatorMode")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SpectatorMode
{
	internal class AutoTestDriver : MonoBehaviour
	{
		private readonly List<string> errors = new List<string>();

		private string dir;

		private string logFile;

		private void Log(string s)
		{
			Plugin.Log.LogInfo((object)("[AutoTest] " + s));
			try
			{
				File.AppendAllText(logFile, s + Environment.NewLine);
			}
			catch
			{
			}
		}

		private void OnEnable()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			Application.logMessageReceived += new LogCallback(OnLog);
		}

		private void OnDisable()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			Application.logMessageReceived -= new LogCallback(OnLog);
		}

		private void OnLog(string m, string st, LogType t)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if ((int)t == 4 || (int)t == 0)
			{
				errors.Add(m.Split('\n')[0] + " @ " + (st ?? "").Split('\n')[0]);
			}
		}

		private void Check(bool ok, string what)
		{
			Log((ok ? "OK    : " : "ÉCHEC : ") + what);
			if (!ok)
			{
				errors.Add("check: " + what);
			}
		}

		private IEnumerator Start()
		{
			dir = Path.Combine(Paths.ConfigPath, "SpectatorMode", "autotest");
			Directory.CreateDirectory(dir);
			string[] files = Directory.GetFiles(dir);
			for (int i = 0; i < files.Length; i++)
			{
				File.Delete(files[i]);
			}
			logFile = Path.Combine(dir, "log.txt");
			Log("Démarrage du test automatique.");
			yield return WaitFor(() => (Object)(object)Object.FindObjectOfType<PreInitSceneScript>() != (Object)null || (Object)(object)MainMenu() != (Object)null, 90f);
			PreInitSceneScript pre = Object.FindObjectOfType<PreInitSceneScript>();
			if ((Object)(object)pre != (Object)null)
			{
				yield return (object)new WaitForSeconds(2f);
				pre.ChooseLaunchOption(false);
			}
			MenuManager menu = null;
			yield return WaitFor(() => (Object)(object)(menu = MainMenu()) != (Object)null, 120f);
			if ((Object)(object)menu == (Object)null)
			{
				Finish("menu introuvable");
				yield break;
			}
			yield return (object)new WaitForSeconds(3f);
			GameNetworkManager.Instance.currentSaveFileName = "LCSpectatorAutoTest";
			menu.ClickHostButton();
			yield return (object)new WaitForSeconds(1f);
			menu.lobbyNameInputField.text = "SpectatorAutoTest";
			menu.ConfirmHostButton();
			yield return WaitFor(() => (Object)(object)StartOfRound.Instance != (Object)null && (Object)(object)StartOfRound.Instance.localPlayerController != (Object)null && StartOfRound.Instance.inShipPhase && StartOfRound.Instance.fullyLoadedPlayers.Count > 0, 120f);
			StartOfRound sor = StartOfRound.Instance;
			if ((Object)(object)sor == (Object)null)
			{
				Finish("partie non chargée");
				yield break;
			}
			yield return (object)new WaitForSeconds(4f);
			if (sor.currentLevelID != 0)
			{
				sor.ChangeLevel(0);
				sor.ArriveAtLevel();
				yield return (object)new WaitForSeconds(2f);
			}
			sor.currentLevel.currentWeather = (LevelWeatherType)(-1);
			StartMatchLever obj = Object.FindObjectOfType<StartMatchLever>();
			obj.singlePlayerEnabled = true;
			obj.leverHasBeenPulled = true;
			obj.StartGame();
			yield return WaitFor(() => sor.shipHasLanded && RoundManager.Instance.dungeonCompletedGenerating, 240f);
			if (!sor.shipHasLanded)
			{
				Finish("pas d'atterrissage");
				yield break;
			}
			yield return (object)new WaitForSeconds(3f);
			PlayerControllerB me = sor.localPlayerController;
			int idx = (int)me.playerClientId;
			Vector3 bodyPos = ((Component)me).transform.position;
			yield return PressKey(Settings.ToggleKey.Value);
			yield return (object)new WaitForSeconds(1f);
			Camera cam = sor.freeCinematicCamera;
			Check(Spectator.Active && Net.IsSpectator(idx), "touche F6 : mode spectateur activé");
			Check((Object)(object)sor.activeCamera == (Object)(object)cam && ((Behaviour)cam).enabled && !((Behaviour)me.gameplayCamera).enabled, "caméra libre active, caméra du joueur coupée");
			Check((Object)(object)sor.audioListener != (Object)null && (Object)(object)((Component)sor.audioListener).transform.parent == (Object)(object)((Component)cam).transform, "le son suit la caméra libre");
			Check(!((Renderer)me.thisPlayerModel).enabled, "corps caché");
			Check(HUDManager.Instance.hudHidden, "interface du jeu cachée");
			yield return Capture("01_entree");
			Vector3 p0 = ((Component)cam).transform.position;
			yield return HoldKey((Key)37, 1f);
			Vector3 p1 = ((Component)cam).transform.position;
			Check(Vector3.Distance(p0, p1) > 5f, $"vol avec W : {Vector3.Distance(p0, p1):0.0} m");
			Check(Vector3.Distance(((Component)me).transform.position, bodyPos) < 0.5f, "le corps n'a pas bougé");
			yield return HoldKey((Key)1, 1f);
			Check(((Component)cam).transform.position.y - p1.y > 5f, $"monter avec Espace : +{((Component)cam).transform.position.y - p1.y:0.0} m");
			((Component)cam).transform.position = sor.middleOfShipNode.position + Vector3.up * 1f;
			yield return (object)new WaitForSeconds(0.3f);
			yield return Capture("02_dans_le_vaisseau");
			SetCam(cam, sor.elevatorTransform.position + new Vector3(-25f, 30f, -25f), sor.elevatorTransform.position);
			yield return (object)new WaitForSeconds(0.5f);
			yield return Capture("03_vue_aerienne");
			yield return PressKey(Settings.LightKey.Value);
			Light val = ((IEnumerable<Light>)((Component)cam).GetComponentsInChildren<Light>(true)).FirstOrDefault((Func<Light, bool>)((Light l) => ((Object)l).name == "SpectatorLight"));
			Check((Object)(object)val != (Object)null && ((Component)val).gameObject.activeInHierarchy, "touche L : lumière allumée");
			int hp = me.health;
			me.DamagePlayer(60, true, true, (CauseOfDeath)1, 0, false, default(Vector3));
			yield return (object)new WaitForSeconds(0.3f);
			Check(me.health == hp && !me.isPlayerDead, $"invulnérable ({me.health} PV)");
			Vector3 val2 = sor.elevatorTransform.position + new Vector3(-20f, 3f, 0f);
			SetCam(cam, val2, val2 + Vector3.forward);
			Check(Spectator.TryFindSpot(((Component)cam).transform.position, out var spot), $"endroit libre trouvé sous la caméra ({spot})");
			yield return PressKey(Settings.ToggleKey.Value);
			yield return (object)new WaitForSeconds(1f);
			Check(!Spectator.Active && !Net.IsSpectator(idx), "touche F6 : sortie du mode spectateur");
			Check(Vector3.Distance(((Component)me).transform.position, spot) < 1.5f, $"réapparition sur place ({((Component)me).transform.position})");
			Check((Object)(object)sor.activeCamera == (Object)(object)me.gameplayCamera && ((Behaviour)me.gameplayCamera).enabled && !((Behaviour)cam).enabled, "caméra du joueur rendue");
			Check(!HUDManager.Instance.hudHidden, "interface du jeu rendue");
			Check(!me.disableMoveInput && !me.disableLookInput && !me.disableInteract, "contrôles rendus");
			yield return Capture("04_sortie");
			Vector3 body2 = ((Component)me).transform.position;
			yield return PressKey(Settings.ToggleKey.Value);
			yield return (object)new WaitForSeconds(0.8f);
			SetCam(cam, new Vector3(0f, 400f, 0f), Vector3.zero);
			yield return PressKey(Settings.ToggleKey.Value);
			yield return (object)new WaitForSeconds(0.8f);
			Check(!Spectator.Active && Vector3.Distance(((Component)me).transform.position, body2) < 1f, "endroit impossible : retour au corps");
			Finish(null);
		}

		private static void SetCam(Camera cam, Vector3 pos, Vector3 look)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			((Component)cam).transform.position = pos;
			Vector3 val = look - pos;
			Quaternion val2 = Quaternion.LookRotation(((Vector3)(ref val)).normalized);
			Vector3 eulerAngles = ((Quaternion)(ref val2)).eulerAngles;
			FieldInfo field = typeof(Spectator).GetField("_yaw", BindingFlags.Static | BindingFlags.NonPublic);
			FieldInfo? field2 = typeof(Spectator).GetField("_pitch", BindingFlags.Static | BindingFlags.NonPublic);
			field.SetValue(null, eulerAngles.y);
			field2.SetValue(null, (eulerAngles.x > 180f) ? (eulerAngles.x - 360f) : eulerAngles.x);
		}

		private static IEnumerator PressKey(Key key)
		{
			//IL_0007: 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)
			Keyboard kb = Keyboard.current;
			InputSystem.QueueStateEvent<KeyboardState>((InputDevice)(object)kb, new KeyboardState((Key[])(object)new Key[1] { (Key)(int)key }), -1.0);
			yield return null;
			yield return null;
			InputSystem.QueueStateEvent<KeyboardState>((InputDevice)(object)kb, default(KeyboardState), -1.0);
			yield return null;
		}

		private static IEnumerator HoldKey(Key key, float seconds)
		{
			//IL_0007: 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)
			Keyboard kb = Keyboard.current;
			float t = 0f;
			while (t < seconds)
			{
				InputSystem.QueueStateEvent<KeyboardState>((InputDevice)(object)kb, new KeyboardState((Key[])(object)new Key[1] { (Key)(int)key }), -1.0);
				t += Time.unscaledDeltaTime;
				yield return null;
			}
			InputSystem.QueueStateEvent<KeyboardState>((InputDevice)(object)kb, default(KeyboardState), -1.0);
			yield return null;
		}

		private void Finish(string fail)
		{
			if (fail != null)
			{
				Log("ÉCHEC : " + fail);
			}
			Log("===== RÉSUMÉ =====");
			Log((errors.Count == 0 && fail == null) ? "Aucune erreur." : string.Format("{0} erreur(s) : {1}", errors.Count, string.Join(" | ", errors.Distinct().Take(20))));
			Application.Quit();
		}

		private IEnumerator Capture(string name)
		{
			yield return (object)new WaitForEndOfFrame();
			ScreenCapture.CaptureScreenshot(Path.Combine(dir, name + ".png"));
			Log("Capture : " + name);
			yield return (object)new WaitForSeconds(0.4f);
		}

		private static MenuManager MainMenu()
		{
			return ((IEnumerable<MenuManager>)Object.FindObjectsOfType<MenuManager>()).FirstOrDefault((Func<MenuManager, bool>)((MenuManager m) => !m.isInitScene && (Object)(object)m.lobbyNameInputField != (Object)null));
		}

		private static IEnumerator WaitFor(Func<bool> cond, float timeout)
		{
			float t = 0f;
			while (t < timeout)
			{
				bool flag = false;
				try
				{
					flag = cond();
				}
				catch
				{
				}
				if (flag)
				{
					break;
				}
				t += 0.25f;
				yield return (object)new WaitForSeconds(0.25f);
			}
		}
	}
	internal static class Net
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static HandleNamedMessageDelegate <0>__OnRequest;

			public static HandleNamedMessageDelegate <1>__OnRequestSync;

			public static HandleNamedMessageDelegate <2>__OnState;

			public static HandleNamedMessageDelegate <3>__OnConfig;

			public static HandleNamedMessageDelegate <4>__OnDenied;
		}

		private const string P = "SpectatorMode_";

		private const string MsgRequest = "SpectatorMode_Request";

		private const string MsgRequestSync = "SpectatorMode_Sync";

		private const string MsgState = "SpectatorMode_State";

		private const string MsgConfig = "SpectatorMode_Config";

		private const string MsgDenied = "SpectatorMode_Denied";

		public static readonly HashSet<int> Spectators = new HashSet<int>();

		private static CustomMessagingManager _registeredOn;

		private static NetworkManager NM => NetworkManager.Singleton;

		public static bool IsServer
		{
			get
			{
				if ((Object)(object)NM != (Object)null)
				{
					return NM.IsServer;
				}
				return false;
			}
		}

		public static int LocalIndex
		{
			get
			{
				StartOfRound instance = StartOfRound.Instance;
				if (!((Object)(object)instance != (Object)null) || !((Object)(object)instance.localPlayerController != (Object)null))
				{
					return -1;
				}
				return (int)instance.localPlayerController.playerClientId;
			}
		}

		public static void EnsureRegistered()
		{
			//IL_006a: 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_0075: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Expected O, but got Unknown
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			if (!((Object)(object)NM == (Object)null) && NM.CustomMessagingManager != null && _registeredOn != NM.CustomMessagingManager)
			{
				_registeredOn = NM.CustomMessagingManager;
				Spectators.Clear();
				Settings.ClearHostValues();
				CustomMessagingManager customMessagingManager = NM.CustomMessagingManager;
				object obj = <>O.<0>__OnRequest;
				if (obj == null)
				{
					HandleNamedMessageDelegate val = OnRequest;
					<>O.<0>__OnRequest = val;
					obj = (object)val;
				}
				customMessagingManager.RegisterNamedMessageHandler("SpectatorMode_Request", (HandleNamedMessageDelegate)obj);
				object obj2 = <>O.<1>__OnRequestSync;
				if (obj2 == null)
				{
					HandleNamedMessageDelegate val2 = OnRequestSync;
					<>O.<1>__OnRequestSync = val2;
					obj2 = (object)val2;
				}
				customMessagingManager.RegisterNamedMessageHandler("SpectatorMode_Sync", (HandleNamedMessageDelegate)obj2);
				object obj3 = <>O.<2>__OnState;
				if (obj3 == null)
				{
					HandleNamedMessageDelegate val3 = OnState;
					<>O.<2>__OnState = val3;
					obj3 = (object)val3;
				}
				customMessagingManager.RegisterNamedMessageHandler("SpectatorMode_State", (HandleNamedMessageDelegate)obj3);
				object obj4 = <>O.<3>__OnConfig;
				if (obj4 == null)
				{
					HandleNamedMessageDelegate val4 = OnConfig;
					<>O.<3>__OnConfig = val4;
					obj4 = (object)val4;
				}
				customMessagingManager.RegisterNamedMessageHandler("SpectatorMode_Config", (HandleNamedMessageDelegate)obj4);
				object obj5 = <>O.<4>__OnDenied;
				if (obj5 == null)
				{
					HandleNamedMessageDelegate val5 = OnDenied;
					<>O.<4>__OnDenied = val5;
					obj5 = (object)val5;
				}
				customMessagingManager.RegisterNamedMessageHandler("SpectatorMode_Denied", (HandleNamedMessageDelegate)obj5);
			}
		}

		public static bool IsSpectator(int idx)
		{
			return Spectators.Contains(idx);
		}

		public static bool IsSpectator(PlayerControllerB p)
		{
			if ((Object)(object)p != (Object)null)
			{
				return Spectators.Contains((int)p.playerClientId);
			}
			return false;
		}

		public static void RequestSpectator(bool on)
		{
			int localIndex = LocalIndex;
			if (localIndex < 0)
			{
				return;
			}
			if (IsServer)
			{
				ServerSet(localIndex, on, 0uL);
				return;
			}
			SendTo(0uL, "SpectatorMode_Request", 8, delegate(FastBufferWriter w)
			{
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				((FastBufferWriter)(ref w)).WriteValueSafe<bool>(ref on, default(ForPrimitives));
			});
		}

		public static void RequestSync()
		{
			if (!IsServer)
			{
				SendTo(0uL, "SpectatorMode_Sync", 8, delegate
				{
				});
			}
		}

		public static string Refusal(int idx)
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || idx < 0 || idx >= instance.allPlayerScripts.Length)
			{
				return "joueur inconnu";
			}
			switch ((Who)Settings.AllowedWho.Value)
			{
			case Who.HoteSeulement:
				if (idx != 0)
				{
					return "Seul l'hôte peut passer en spectateur.";
				}
				return null;
			case Who.MortsSeulement:
				if (!instance.allPlayerScripts[idx].isPlayerDead)
				{
					return "Seuls les joueurs morts peuvent passer en spectateur.";
				}
				return null;
			default:
				return null;
			}
		}

		private static void ServerSet(int idx, bool on, ulong requester)
		{
			if (on)
			{
				string text = Refusal(idx);
				if (text != null)
				{
					Deny(requester, text);
					return;
				}
				if (!Spectators.Add(idx))
				{
					return;
				}
			}
			else if (!Spectators.Remove(idx))
			{
				return;
			}
			Spectator.OnStateChanged();
			BroadcastState();
		}

		public static void ServerForget(int idx)
		{
			if (Spectators.Remove(idx))
			{
				Spectator.OnStateChanged();
				BroadcastState();
			}
		}

		public static void BroadcastState()
		{
			if (!IsServer)
			{
				return;
			}
			foreach (ulong connectedClientsId in NM.ConnectedClientsIds)
			{
				if (connectedClientsId != 0L)
				{
					SendState(connectedClientsId);
				}
			}
		}

		public static void BroadcastConfig()
		{
			if (!IsServer || _registeredOn == null)
			{
				return;
			}
			foreach (ulong connectedClientsId in NM.ConnectedClientsIds)
			{
				if (connectedClientsId != 0L)
				{
					SendConfig(connectedClientsId);
				}
			}
		}

		private static void SendState(ulong clientId)
		{
			List<int> list = new List<int>(Spectators);
			SendTo(clientId, "SpectatorMode_State", 8 + list.Count * 4, delegate(FastBufferWriter w)
			{
				//IL_0012: 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_003a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0040: Unknown result type (might be due to invalid IL or missing references)
				int count = list.Count;
				((FastBufferWriter)(ref w)).WriteValueSafe<int>(ref count, default(ForPrimitives));
				foreach (int item in list)
				{
					int current = item;
					((FastBufferWriter)(ref w)).WriteValueSafe<int>(ref current, default(ForPrimitives));
				}
			});
		}

		private static void SendConfig(ulong clientId)
		{
			SendTo(clientId, "SpectatorMode_Config", 8 + Settings.All.Count * 4, delegate(FastBufferWriter w)
			{
				//IL_0011: 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_0040: 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)
				int count = Settings.All.Count;
				((FastBufferWriter)(ref w)).WriteValueSafe<int>(ref count, default(ForPrimitives));
				foreach (ISynced item in Settings.All)
				{
					float local = item.Local;
					((FastBufferWriter)(ref w)).WriteValueSafe<float>(ref local, default(ForPrimitives));
				}
			});
		}

		private static void Deny(ulong clientId, string why)
		{
			if (clientId == 0L)
			{
				Spectator.OnDenied(why);
				return;
			}
			SendTo(clientId, "SpectatorMode_Denied", 64 + why.Length * 4, delegate(FastBufferWriter w)
			{
				((FastBufferWriter)(ref w)).WriteValueSafe(why, false);
			});
		}

		private static void OnRequest(ulong sender, FastBufferReader r)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (IsServer)
			{
				bool flag = default(bool);
				((FastBufferReader)(ref r)).ReadValueSafe<bool>(ref flag, default(ForPrimitives));
				int num = PlayerIndexOf(sender);
				if (num >= 0)
				{
					ServerSet(num, flag, sender);
				}
			}
		}

		private static void OnRequestSync(ulong sender, FastBufferReader r)
		{
			if (IsServer)
			{
				SendConfig(sender);
				SendState(sender);
			}
		}

		private static void OnState(ulong sender, FastBufferReader r)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (!IsServer)
			{
				int num = default(int);
				((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num, default(ForPrimitives));
				Spectators.Clear();
				int item = default(int);
				for (int i = 0; i < num; i++)
				{
					((FastBufferReader)(ref r)).ReadValueSafe<int>(ref item, default(ForPrimitives));
					Spectators.Add(item);
				}
				Spectator.OnStateChanged();
			}
		}

		private static void OnConfig(ulong sender, FastBufferReader r)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			if (IsServer)
			{
				return;
			}
			int num = default(int);
			((FastBufferReader)(ref r)).ReadValueSafe<int>(ref num, default(ForPrimitives));
			float host = default(float);
			for (int i = 0; i < num; i++)
			{
				((FastBufferReader)(ref r)).ReadValueSafe<float>(ref host, default(ForPrimitives));
				if (i < Settings.All.Count)
				{
					Settings.All[i].SetHost(host);
				}
			}
		}

		private static void OnDenied(ulong sender, FastBufferReader r)
		{
			string why = default(string);
			((FastBufferReader)(ref r)).ReadValueSafe(ref why, false);
			Spectator.OnDenied(why);
		}

		private static int PlayerIndexOf(ulong clientId)
		{
			StartOfRound instance = StartOfRound.Instance;
			if (!((Object)(object)instance != (Object)null) || !instance.ClientPlayerList.TryGetValue(clientId, out var value))
			{
				return -1;
			}
			return value;
		}

		private unsafe static void SendTo(ulong clientId, string name, int size, Action<FastBufferWriter> write)
		{
			//IL_0030: 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)
			if ((Object)(object)NM == (Object)null || NM.CustomMessagingManager == null)
			{
				return;
			}
			FastBufferWriter val = default(FastBufferWriter);
			((FastBufferWriter)(ref val))..ctor(Math.Max(size, 64), (Allocator)2, 8000);
			try
			{
				write(val);
				NM.CustomMessagingManager.SendNamedMessage(name, clientId, val, (NetworkDelivery)3);
			}
			finally
			{
				((IDisposable)(*(FastBufferWriter*)(&val))/*cast due to .constrained prefix*/).Dispose();
			}
		}
	}
	[BepInPlugin("emeric.spectatormode", "SpectatorMode", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "emeric.spectatormode";

		public const string Name = "SpectatorMode";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings.Init(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				Net.BroadcastConfig();
			};
			new Harmony("emeric.spectatormode").PatchAll();
			GameObject val = new GameObject("SpectatorModeRunner")
			{
				hideFlags = (HideFlags)61
			};
			Object.DontDestroyOnLoad((Object)(object)val);
			val.AddComponent<Runner>();
			if (Settings.AutoTest.Value)
			{
				val.AddComponent<AutoTestDriver>();
			}
			Log.LogInfo((object)string.Format("{0} {1} chargé. [{2}] pour passer en spectateur.", "SpectatorMode", "1.0.0", Settings.ToggleKey.Value));
		}
	}
	internal class Runner : MonoBehaviour
	{
		private float _inGameSince = -1f;

		private bool _autoDone;

		private void LateUpdate()
		{
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				NetworkManager singleton = NetworkManager.Singleton;
				StartOfRound instance = StartOfRound.Instance;
				if ((Object)(object)singleton == (Object)null || !singleton.IsListening || (Object)(object)instance == (Object)null || (Object)(object)instance.localPlayerController == (Object)null || !instance.localPlayerController.isPlayerControlled)
				{
					_inGameSince = -1f;
					_autoDone = false;
					return;
				}
				PlayerControllerB localPlayerController = instance.localPlayerController;
				if (_inGameSince < 0f)
				{
					_inGameSince = Time.unscaledTime;
				}
				if (!_autoDone && Settings.StartAsSpectator.Value && Time.unscaledTime - _inGameSince > 3f)
				{
					_autoDone = true;
					if (!Spectator.Active)
					{
						Spectator.Toggle();
					}
				}
				Keyboard current = Keyboard.current;
				bool flag = localPlayerController.isTypingChat || localPlayerController.inTerminalMenu || ((Object)(object)localPlayerController.quickMenuManager != (Object)null && localPlayerController.quickMenuManager.isMenuOpen);
				if (current != null && !flag && ((ButtonControl)current[Settings.ToggleKey.Value]).wasPressedThisFrame)
				{
					Spectator.Toggle();
				}
				Spectator.Tick();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("SpectatorMode : " + ex));
			}
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		private static bool LocalSpectating(PlayerControllerB p)
		{
			if (Spectator.Active)
			{
				return (Object)(object)p == (Object)(object)StartOfRound.Instance?.localPlayerController;
			}
			return false;
		}

		[HarmonyPatch(typeof(StartOfRound), "Start")]
		[HarmonyPostfix]
		private static void SorStart()
		{
			Net.EnsureRegistered();
		}

		[HarmonyPatch(typeof(PlayerControllerB), "ConnectClientToPlayerObject")]
		[HarmonyPostfix]
		private static void Connect()
		{
			Net.EnsureRegistered();
			Net.RequestSync();
		}

		[HarmonyPatch(typeof(StartOfRound), "OnPlayerDC")]
		[HarmonyPostfix]
		private static void PlayerLeft(int playerObjectNumber)
		{
			if (Net.IsServer)
			{
				Net.ServerForget(playerObjectNumber);
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "OnLocalDisconnect")]
		[HarmonyPrefix]
		private static void LocalDisconnect()
		{
			Spectator.ForceReset();
			Visibility.ShowAll();
			Net.Spectators.Clear();
		}

		[HarmonyPatch(typeof(ElevatorAnimationEvents), "ElevatorFullyRunning")]
		[HarmonyPrefix]
		private static void ShipLeaving()
		{
			PlayerControllerB val = StartOfRound.Instance?.localPlayerController;
			if (!((Object)(object)val == (Object)null) && Spectator.Active)
			{
				Spectator.Exit(val, teleport: false);
				Net.RequestSpectator(on: false);
				if (!val.isPlayerDead)
				{
					val.isInElevator = true;
					val.isInHangarShipRoom = false;
				}
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "ReviveDeadPlayers")]
		[HarmonyPrefix]
		private static void NewDay()
		{
			PlayerControllerB val = StartOfRound.Instance?.localPlayerController;
			if (!((Object)(object)val == (Object)null) && Spectator.Active)
			{
				Spectator.Exit(val, teleport: false);
				Net.RequestSpectator(on: false);
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Jump_performed")]
		[HarmonyPrefix]
		private static bool Jump(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Crouch_performed")]
		[HarmonyPrefix]
		private static bool Crouch(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "ScrollMouse_performed")]
		[HarmonyPrefix]
		private static bool Scroll(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "ActivateItem_performed")]
		[HarmonyPrefix]
		private static bool Use(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "ItemSecondaryUse_performed")]
		[HarmonyPrefix]
		private static bool Use2(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "ItemTertiaryUse_performed")]
		[HarmonyPrefix]
		private static bool Use3(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Discard_performed")]
		[HarmonyPrefix]
		private static bool Drop(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "InspectItem_performed")]
		[HarmonyPrefix]
		private static bool Inspect(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Emote1_performed")]
		[HarmonyPrefix]
		private static bool Emote1(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Emote2_performed")]
		[HarmonyPrefix]
		private static bool Emote2(PlayerControllerB __instance)
		{
			return !LocalSpectating(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "DamagePlayer")]
		[HarmonyPrefix]
		private static bool Damage(PlayerControllerB __instance)
		{
			return !Net.IsSpectator(__instance);
		}

		[HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")]
		[HarmonyPrefix]
		private static bool Kill(PlayerControllerB __instance, CauseOfDeath causeOfDeath)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			if (Net.IsSpectator(__instance))
			{
				return (int)causeOfDeath == 10;
			}
			return true;
		}

		[HarmonyPatch(typeof(EnemyAI), "PlayerIsTargetable")]
		[HarmonyPostfix]
		private static void Targetable(PlayerControllerB playerScript, ref bool __result)
		{
			if (__result && Net.IsSpectator(playerScript))
			{
				__result = false;
			}
		}

		[HarmonyPatch(typeof(EnemyAI), "MeetsStandardPlayerCollisionConditions")]
		[HarmonyPostfix]
		private static void Touch(ref PlayerControllerB __result)
		{
			if ((Object)(object)__result != (Object)null && Net.IsSpectator(__result))
			{
				__result = null;
			}
		}

		[HarmonyPatch(typeof(HUDManager), "SubmitChat_performed")]
		[HarmonyPrefix]
		private static void Chat(HUDManager __instance)
		{
			string text = (__instance.chatTextField.text ?? "").Trim();
			if (!text.StartsWith("/gamemode", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("/gm ", StringComparison.OrdinalIgnoreCase))
			{
				return;
			}
			__instance.chatTextField.text = "";
			string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			string text2 = ((array.Length > 1) ? array[1].ToLowerInvariant() : "");
			bool? flag = null;
			if (text2 != null)
			{
				switch (text2.Length)
				{
				case 1:
				{
					char c = text2[0];
					if (c != '0')
					{
						if (c == '3')
						{
							goto IL_0133;
						}
						if (c != 's')
						{
							break;
						}
					}
					goto IL_013d;
				}
				case 9:
					if (!(text2 == "spectator"))
					{
						break;
					}
					goto IL_0133;
				case 10:
					if (!(text2 == "spectateur"))
					{
						break;
					}
					goto IL_0133;
				case 4:
					if (!(text2 == "spec"))
					{
						break;
					}
					goto IL_0133;
				case 2:
					if (!(text2 == "sp"))
					{
						break;
					}
					goto IL_0133;
				case 8:
					if (!(text2 == "survival"))
					{
						break;
					}
					goto IL_013d;
				case 6:
					{
						if (!(text2 == "survie"))
						{
							break;
						}
						goto IL_013d;
					}
					IL_0133:
					flag = true;
					break;
					IL_013d:
					flag = false;
					break;
				}
			}
			if (!flag.HasValue)
			{
				Spectator.Tip("Commande", "/gamemode spectator ou /gamemode survival", warning: false);
			}
			else if (flag.Value != (Spectator.Active || Net.IsSpectator(Net.LocalIndex)))
			{
				Spectator.Toggle();
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "UpdatePlayerVoiceEffects")]
		[HarmonyPostfix]
		private static void Voices(StartOfRound __instance)
		{
			if (!Settings.MuteForLiving.Value)
			{
				return;
			}
			PlayerControllerB localPlayerController = __instance.localPlayerController;
			if ((Object)(object)localPlayerController == (Object)null || localPlayerController.isPlayerDead || Spectator.Active)
			{
				return;
			}
			PlayerControllerB[] allPlayerScripts = __instance.allPlayerScripts;
			foreach (PlayerControllerB val in allPlayerScripts)
			{
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val == (Object)(object)localPlayerController) && Net.IsSpectator(val) && val.voicePlayerState != null)
				{
					val.voicePlayerState.Volume = 0f;
				}
			}
		}
	}
	internal interface ISynced
	{
		float Local { get; }

		void SetHost(float value);

		void ClearHost();
	}
	internal sealed class Synced<T> : ISynced where T : IConvertible
	{
		private readonly ConfigEntry<T> _entry;

		private T _host;

		private bool _hasHost;

		public T Value
		{
			get
			{
				if (!_hasHost)
				{
					return _entry.Value;
				}
				return _host;
			}
		}

		public float Local => Convert.ToSingle(_entry.Value);

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

		public void SetHost(float value)
		{
			_host = (T)Convert.ChangeType(value, typeof(T));
			_hasHost = true;
		}

		public void ClearHost()
		{
			_hasHost = false;
		}

		public static implicit operator T(Synced<T> s)
		{
			return s.Value;
		}
	}
	internal enum Who
	{
		Tous,
		HoteSeulement,
		MortsSeulement
	}
	internal static class Settings
	{
		public static readonly List<ISynced> All = new List<ISynced>();

		public static ConfigEntry<Key> ToggleKey;

		public static ConfigEntry<Key> LightKey;

		public static ConfigEntry<float> StartSpeed;

		public static ConfigEntry<bool> ShowHelp;

		public static ConfigEntry<bool> HideGameHud;

		public static ConfigEntry<bool> StartAsSpectator;

		public static Synced<int> AllowedWho;

		public static Synced<bool> StayWhereYouAre;

		public static Synced<bool> MuteForLiving;

		public static ConfigEntry<bool> AutoTest;

		public static void Init(ConfigFile cfg)
		{
			ToggleKey = cfg.Bind<Key>("Touches", "Spectateur", (Key)99, "Entrer / sortir du mode spectateur.");
			LightKey = cfg.Bind<Key>("Touches", "Vision", (Key)26, "En spectateur : allumer / éteindre la lumière (toi seul la vois).");
			StartSpeed = cfg.Bind<float>("Spectateur", "Vitesse", 10f, "Vitesse de vol de départ (m/s). La molette la change en jeu.");
			ShowHelp = cfg.Bind<bool>("Spectateur", "AfficherAide", true, "Affiche les commandes en haut de l'écran en mode spectateur.");
			StartAsSpectator = cfg.Bind<bool>("Spectateur", "DemarrerEnSpectateur", false, "Passe automatiquement en spectateur en arrivant dans une partie.");
			HideGameHud = cfg.Bind<bool>("Spectateur", "CacherInterfaceDuJeu", true, "Cache l'interface du jeu (vie, inventaire…) en mode spectateur.");
			AllowedWho = S<int>(cfg.Bind<int>("Partie", "QuiPeut", 0, "[Hôte] Qui peut passer en spectateur : 0 = tout le monde, 1 = l'hôte seulement, 2 = seulement les joueurs morts."));
			StayWhereYouAre = S<bool>(cfg.Bind<bool>("Partie", "SortieSurPlace", true, "[Hôte] En quittant le mode spectateur, le joueur réapparaît là où il est (si l'endroit est libre), sinon il revient à son corps."));
			MuteForLiving = S<bool>(cfg.Bind<bool>("Partie", "VoixCacheeAuxVivants", true, "[Hôte] Les joueurs vivants n'entendent pas la voix des spectateurs."));
			AutoTest = cfg.Bind<bool>("Test", "AutoTest", false, "Développeur : lance tout seul une partie de test.");
		}

		private static Synced<T> S<T>(ConfigEntry<T> e) where T : IConvertible
		{
			Synced<T> synced = new Synced<T>(e);
			All.Add(synced);
			return synced;
		}

		public static void ClearHostValues()
		{
			foreach (ISynced item in All)
			{
				item.ClearHost();
			}
		}
	}
	internal static class Spectator
	{
		public static bool Active;

		private static Camera _cam;

		private static float _yaw;

		private static float _pitch;

		private static float _speed;

		private static GameObject _light;

		private static bool _lightOn;

		private static bool _savedInside;

		private static bool _requested;

		private static bool _hidHud;

		private static float _nextVisibility;

		private const int SolidMask = 318769409;

		private static GameObject _hud;

		private static TextMeshProUGUI _hudText;

		private static PlayerControllerB Local
		{
			get
			{
				if (!((Object)(object)StartOfRound.Instance != (Object)null))
				{
					return null;
				}
				return StartOfRound.Instance.localPlayerController;
			}
		}

		public static void Toggle()
		{
			PlayerControllerB local = Local;
			if (!((Object)(object)local == (Object)null))
			{
				if (Active || Net.IsSpectator(local))
				{
					Net.RequestSpectator(on: false);
					return;
				}
				_requested = true;
				Net.RequestSpectator(on: true);
			}
		}

		public static void OnDenied(string why)
		{
			_requested = false;
			Tip("Mode spectateur", why, warning: true);
		}

		public static void OnStateChanged()
		{
			PlayerControllerB local = Local;
			if ((Object)(object)local != (Object)null)
			{
				bool flag = Net.IsSpectator(local);
				if (flag && !Active)
				{
					Enter(local);
				}
				else if (!flag && Active)
				{
					Exit(local, teleport: true);
				}
			}
			_nextVisibility = 0f;
			try
			{
				StartOfRound instance = StartOfRound.Instance;
				if (instance != null)
				{
					instance.UpdatePlayerVoiceEffects();
				}
			}
			catch
			{
			}
		}

		private static void Enter(PlayerControllerB p)
		{
			//IL_00a9: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			_cam = instance.freeCinematicCamera;
			if ((Object)(object)_cam == (Object)null)
			{
				Plugin.Log.LogError((object)"Caméra libre introuvable : mode spectateur impossible.");
				Net.RequestSpectator(on: false);
				return;
			}
			Active = true;
			_requested = false;
			_speed = Mathf.Clamp(Settings.StartSpeed.Value, 1f, 80f);
			Transform val = ((p.isPlayerDead && (Object)(object)instance.spectateCamera != (Object)null) ? ((Component)instance.spectateCamera).transform : ((Component)p.gameplayCamera).transform);
			((Component)_cam).transform.SetParent((Transform)null, true);
			((Component)_cam).transform.SetPositionAndRotation(val.position, Quaternion.Euler(val.eulerAngles.x, val.eulerAngles.y, 0f));
			_yaw = val.eulerAngles.y;
			_pitch = ((val.eulerAngles.x > 180f) ? (val.eulerAngles.x - 360f) : val.eulerAngles.x);
			_savedInside = p.isInsideFactory;
			UseCamera(p);
			Lock(p, on: true);
			if ((Object)(object)p.currentlyHeldObjectServer != (Object)null && !p.isPlayerDead)
			{
				p.currentlyHeldObjectServer.EnableItemMeshes(false);
			}
			_hidHud = false;
			if (Settings.HideGameHud.Value && (Object)(object)HUDManager.Instance != (Object)null && !HUDManager.Instance.hudHidden)
			{
				HUDManager.Instance.HideHUD(true);
				_hidHud = true;
			}
			Plugin.Log.LogInfo((object)"Mode spectateur activé.");
		}

		private static void UseCamera(PlayerControllerB p)
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)p.gameplayCamera != (Object)null)
			{
				((Behaviour)p.gameplayCamera).enabled = false;
			}
			instance.SwitchCamera(_cam);
			((Behaviour)_cam).enabled = true;
			p.ChangeAudioListenerToObject(((Component)_cam).gameObject);
		}

		public static void Exit(PlayerControllerB p, bool teleport)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_0121: 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_013c: 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_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			if (!Active)
			{
				return;
			}
			Active = false;
			StartOfRound instance = StartOfRound.Instance;
			Vector3 val = (((Object)(object)_cam != (Object)null) ? ((Component)_cam).transform.position : ((Component)p).transform.position);
			float yaw = _yaw;
			if ((Object)(object)_light != (Object)null)
			{
				_light.SetActive(false);
			}
			_lightOn = false;
			Lock(p, on: false);
			if ((Object)(object)_cam != (Object)null)
			{
				((Behaviour)_cam).enabled = false;
			}
			if (_hidHud && (Object)(object)HUDManager.Instance != (Object)null)
			{
				HUDManager.Instance.HideHUD(false);
			}
			_hidHud = false;
			if (p.isPlayerDead)
			{
				if ((Object)(object)instance.spectateCamera != (Object)null)
				{
					instance.SwitchCamera(instance.spectateCamera);
					p.ChangeAudioListenerToObject(((Component)instance.spectateCamera).gameObject);
				}
			}
			else
			{
				((Behaviour)p.gameplayCamera).enabled = true;
				instance.SwitchCamera(p.gameplayCamera);
				p.ChangeAudioListenerToObject(((Component)p.gameplayCamera).gameObject);
				bool flag = false;
				if (teleport && Settings.StayWhereYouAre.Value && TryFindSpot(val, out var spot))
				{
					bool inside = spot.y < -80f;
					p.TeleportPlayer(spot, false, 0f, false, true);
					Bounds bounds = instance.shipBounds.bounds;
					p.isInElevator = ((Bounds)(ref bounds)).Contains(spot);
					bounds = instance.shipInnerRoomBounds.bounds;
					p.isInHangarShipRoom = ((Bounds)(ref bounds)).Contains(spot);
					SetInside(p, inside);
					p.thisPlayerBody.rotation = Quaternion.Euler(0f, yaw, 0f);
					flag = true;
				}
				if (!flag)
				{
					SetInside(p, _savedInside);
				}
				if ((Object)(object)p.currentlyHeldObjectServer != (Object)null)
				{
					p.currentlyHeldObjectServer.EnableItemMeshes(true);
				}
				if (teleport && !flag && Settings.StayWhereYouAre.Value)
				{
					Tip("Mode spectateur", "Endroit bloqué : retour à ton corps.", warning: false);
				}
			}
			Plugin.Log.LogInfo((object)"Mode spectateur désactivé.");
		}

		public static void ForceReset()
		{
			PlayerControllerB local = Local;
			if (Active && (Object)(object)local != (Object)null)
			{
				Exit(local, teleport: false);
			}
			Active = false;
			_requested = false;
		}

		private static void SetInside(PlayerControllerB p, bool inside)
		{
			if (p.isInsideFactory == inside)
			{
				return;
			}
			p.isInsideFactory = inside;
			try
			{
				StartOfRound.Instance.SwitchIsInsideFactoryRpc((int)p.playerClientId, inside);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Synchro intérieur/extérieur : " + ex.Message));
			}
		}

		private static void Lock(PlayerControllerB p, bool on)
		{
			p.disableMoveInput = on;
			p.disableLookInput = on;
			p.disableInteract = on;
			if (on)
			{
				((TMP_Text)p.cursorTip).text = "";
				((Behaviour)p.cursorIcon).enabled = false;
			}
		}

		public static bool TryFindSpot(Vector3 from, out Vector3 spot)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			spot = from;
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(from + Vector3.up * 0.2f, Vector3.down, ref val, 60f, 318769409, (QueryTriggerInteraction)1))
			{
				return false;
			}
			Vector3 val2 = ((RaycastHit)(ref val)).point + Vector3.up * 0.05f;
			if (Physics.CheckCapsule(val2 + Vector3.up * 0.45f, val2 + Vector3.up * 1.95f, 0.35f, 318769409, (QueryTriggerInteraction)1))
			{
				return false;
			}
			NavMeshHit val3 = default(NavMeshHit);
			if (!NavMesh.SamplePosition(val2, ref val3, 2.5f, -1))
			{
				return false;
			}
			if (Mathf.Abs(((NavMeshHit)(ref val3)).position.y - val2.y) > 1.5f)
			{
				return false;
			}
			spot = val2;
			return true;
		}

		public static void Tick()
		{
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			StartOfRound instance = StartOfRound.Instance;
			PlayerControllerB local = Local;
			if ((Object)(object)local == (Object)null)
			{
				return;
			}
			if (Time.unscaledTime >= _nextVisibility)
			{
				_nextVisibility = Time.unscaledTime + 0.5f;
				Visibility.Refresh();
			}
			if (Active && !Net.IsSpectator(local) && !_requested)
			{
				Exit(local, teleport: true);
			}
			if (!Active)
			{
				Hud(show: false);
				return;
			}
			if ((Object)(object)instance.activeCamera != (Object)(object)_cam || !((Behaviour)_cam).enabled)
			{
				UseCamera(local);
			}
			if (((Behaviour)local.gameplayCamera).enabled)
			{
				((Behaviour)local.gameplayCamera).enabled = false;
			}
			Lock(local, on: true);
			local.ResetFallGravity();
			if ((!((Object)(object)local.quickMenuManager != (Object)null) || !local.quickMenuManager.isMenuOpen) && !local.isTypingChat && !local.inTerminalMenu)
			{
				Fly(local);
			}
			Hud(Settings.ShowHelp.Value);
			bool flag = ((Component)_cam).transform.position.y < -80f;
			if (local.isInsideFactory != flag && !local.isPlayerDead)
			{
				local.isInsideFactory = flag;
			}
		}

		private static void Fly(PlayerControllerB p)
		{
			//IL_0033: 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_0043: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: 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_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: 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_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: 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_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_01e7: 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_020e: 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_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			Keyboard current = Keyboard.current;
			Mouse current2 = Mouse.current;
			float num = (((Object)(object)IngamePlayerSettings.Instance != (Object)null) ? IngamePlayerSettings.Instance.settings.lookSensitivity : 10);
			Vector2 val = ReadAction("Look") * 0.008f * num;
			if ((Object)(object)IngamePlayerSettings.Instance != (Object)null && IngamePlayerSettings.Instance.settings.invertYAxis)
			{
				val.y = 0f - val.y;
			}
			_yaw += val.x;
			_pitch = Mathf.Clamp(_pitch - val.y, -89f, 89f);
			((Component)_cam).transform.rotation = Quaternion.Euler(_pitch, _yaw, 0f);
			if (current2 != null)
			{
				float y = ((InputControl<Vector2>)(object)current2.scroll).ReadValue().y;
				if (y > 0.1f)
				{
					_speed = Mathf.Min(80f, _speed * 1.2f);
				}
				else if (y < -0.1f)
				{
					_speed = Mathf.Max(1f, _speed / 1.2f);
				}
			}
			Vector2 val2 = ReadAction("Move");
			float num2 = 0f;
			if (current != null)
			{
				if (((ButtonControl)current.spaceKey).isPressed)
				{
					num2 += 1f;
				}
				if (((ButtonControl)current.leftCtrlKey).isPressed || ((ButtonControl)current.cKey).isPressed)
				{
					num2 -= 1f;
				}
			}
			float num3 = _speed * ((current != null && ((ButtonControl)current.leftShiftKey).isPressed) ? 3f : 1f);
			Vector3 val3 = ((Component)_cam).transform.forward * val2.y + ((Component)_cam).transform.right * val2.x + Vector3.up * num2;
			if (((Vector3)(ref val3)).sqrMagnitude > 1f)
			{
				((Vector3)(ref val3)).Normalize();
			}
			Transform transform = ((Component)_cam).transform;
			transform.position += val3 * num3 * Time.unscaledDeltaTime;
			if (current != null && ((ButtonControl)current[Settings.LightKey.Value]).wasPressedThisFrame)
			{
				ToggleLight(p);
			}
		}

		private static Vector2 ReadAction(string name)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: 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)
			try
			{
				InputAction val = (((Object)(object)InputSystem.actions != (Object)null) ? InputSystem.actions.FindAction(name, false) : null);
				return (val != null) ? val.ReadValue<Vector2>() : Vector2.zero;
			}
			catch
			{
				return Vector2.zero;
			}
		}

		private static void ToggleLight(PlayerControllerB p)
		{
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_light == (Object)null)
			{
				Light val = ((p.allHelmetLights != null && p.allHelmetLights.Length != 0) ? p.allHelmetLights[0] : p.helmetLight);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				_light = Object.Instantiate<GameObject>(((Component)val).gameObject);
				((Object)_light).name = "SpectatorLight";
				foreach (Transform item in _light.transform)
				{
					Object.Destroy((Object)(object)((Component)item).gameObject);
				}
				Light component = _light.GetComponent<Light>();
				((Behaviour)component).enabled = true;
				component.range = 70f;
				component.spotAngle = 110f;
				component.innerSpotAngle = 70f;
				component.intensity *= 2.5f;
				component.shadows = (LightShadows)0;
			}
			_lightOn = !_lightOn;
			_light.transform.SetParent(((Component)_cam).transform, false);
			_light.transform.localPosition = Vector3.zero;
			_light.transform.localRotation = Quaternion.identity;
			_light.SetActive(_lightOn);
		}

		private static void Hud(bool show)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if (!show)
			{
				if ((Object)(object)_hud != (Object)null && _hud.activeSelf)
				{
					_hud.SetActive(false);
				}
				return;
			}
			if ((Object)(object)_hud == (Object)null)
			{
				BuildHud();
			}
			if (!((Object)(object)_hud == (Object)null))
			{
				if (!_hud.activeSelf)
				{
					_hud.SetActive(true);
				}
				string text = ((object)Settings.ToggleKey.Value/*cast due to .constrained prefix*/).ToString();
				((TMP_Text)_hudText).text = "<color=#ff8c26><b>SPECTATEUR</b></color>   [" + text + "] quitter   ·   ZQSD voler   ·   Espace / Ctrl : monter / descendre   ·   Maj : plus vite\n" + string.Format("<size=85%>Molette : vitesse <b>{0:0}</b> m/s   ·   [{1}] lumière {2}   ·   Invisible, invulnérable, ignoré des monstres</size>", _speed, Settings.LightKey.Value, _lightOn ? "allumée" : "éteinte");
			}
		}

		private static void BuildHud()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: 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_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			_hud = new GameObject("SpectatorMode_Hud");
			Object.DontDestroyOnLoad((Object)(object)_hud);
			Canvas obj = _hud.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 45;
			CanvasScaler obj2 = _hud.AddComponent<CanvasScaler>();
			obj2.uiScaleMode = (ScaleMode)1;
			obj2.referenceResolution = new Vector2(1920f, 1080f);
			obj2.matchWidthOrHeight = 0.5f;
			GameObject val = new GameObject("Text", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(_hud.transform, false);
			_hudText = val.AddComponent<TextMeshProUGUI>();
			TMP_FontAsset val2 = ((IEnumerable<TMP_FontAsset>)Resources.FindObjectsOfTypeAll<TMP_FontAsset>()).FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset f) => ((Object)f).name == "LiberationSans SDF"));
			if ((Object)(object)val2 != (Object)null)
			{
				((TMP_Text)_hudText).font = val2;
			}
			((TMP_Text)_hudText).fontSize = 22f;
			((TMP_Text)_hudText).alignment = (TextAlignmentOptions)258;
			((TMP_Text)_hudText).enableWordWrapping = false;
			((TMP_Text)_hudText).richText = true;
			((Graphic)_hudText).raycastTarget = false;
			((TMP_Text)_hudText).outlineWidth = 0.18f;
			((TMP_Text)_hudText).outlineColor = new Color32((byte)0, (byte)0, (byte)0, (byte)220);
			RectTransform rectTransform = ((TMP_Text)_hudText).rectTransform;
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 1f);
			rectTransform.pivot = val3;
			Vector2 anchorMin = (rectTransform.anchorMax = val3);
			rectTransform.anchorMin = anchorMin;
			rectTransform.anchoredPosition = new Vector2(0f, -16f);
			rectTransform.sizeDelta = new Vector2(1800f, 70f);
		}

		public static void Tip(string header, string body, bool warning)
		{
			try
			{
				HUDManager instance = HUDManager.Instance;
				if (instance != null)
				{
					instance.DisplayTip(Plain(header), Plain(body), warning, false, "");
				}
			}
			catch
			{
			}
		}

		private static string Plain(string s)
		{
			string text = (s ?? "").Normalize(NormalizationForm.FormD);
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			string text2 = text;
			foreach (char c in text2)
			{
				if (CharUnicodeInfo.GetUnicodeCategory(c) != UnicodeCategory.NonSpacingMark)
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}
	}
	internal static class Visibility
	{
		private static readonly Dictionary<int, List<Renderer>> Hidden = new Dictionary<int, List<Renderer>>();

		public static void Refresh()
		{
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			for (int i = 0; i < instance.allPlayerScripts.Length; i++)
			{
				PlayerControllerB val = instance.allPlayerScripts[i];
				if (!((Object)(object)val == (Object)null))
				{
					if (Net.IsSpectator(i) && !val.isPlayerDead)
					{
						Hide(i, val);
					}
					else if (Hidden.ContainsKey(i))
					{
						Show(i, val);
					}
				}
			}
		}

		private static void Hide(int i, PlayerControllerB p)
		{
			if (!Hidden.TryGetValue(i, out var value))
			{
				value = (Hidden[i] = new List<Renderer>());
			}
			Renderer[] componentsInChildren = ((Component)p).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && val.enabled)
				{
					val.enabled = false;
					if (!value.Contains(val))
					{
						value.Add(val);
					}
				}
			}
			if ((Object)(object)p.usernameCanvas != (Object)null)
			{
				((Behaviour)p.usernameCanvas).enabled = false;
			}
			if ((Object)(object)p.currentlyHeldObjectServer != (Object)null)
			{
				p.currentlyHeldObjectServer.EnableItemMeshes(false);
			}
		}

		private static void Show(int i, PlayerControllerB p)
		{
			foreach (Renderer item in Hidden[i])
			{
				if ((Object)(object)item != (Object)null)
				{
					item.enabled = true;
				}
			}
			Hidden.Remove(i);
			if ((Object)(object)p.usernameCanvas != (Object)null)
			{
				((Behaviour)p.usernameCanvas).enabled = true;
			}
			if ((Object)(object)p.currentlyHeldObjectServer != (Object)null && !p.isPlayerDead)
			{
				p.currentlyHeldObjectServer.EnableItemMeshes(true);
			}
		}

		public static void ShowAll()
		{
			StartOfRound instance = StartOfRound.Instance;
			foreach (int item in Hidden.Keys.ToList())
			{
				if ((Object)(object)instance != (Object)null && item < instance.allPlayerScripts.Length)
				{
					Show(item, instance.allPlayerScripts[item]);
				}
				else
				{
					Hidden.Remove(item);
				}
			}
		}
	}
}