Decompiled source of DeathSpectator v1.0.0

DeathSpectator.dll

Decompiled 9 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.Rendering.HighDefinition;

[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("DeathSpectator")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("DeathSpectator")]
[assembly: AssemblyTitle("DeathSpectator")]
[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 DeathSpectator
{
	internal class AutoTestDriver : MonoBehaviour
	{
		private readonly List<string> errors = new List<string>();

		private string shotDir;

		private string logFile;

		private PlayerControllerB me;

		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 message, string stack, LogType type)
		{
			//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)type == 4 || (int)type == 0) && ((stack ?? "").Contains("DeathSpectator") || message.Contains("DeathSpectator")))
			{
				errors.Add(message.Split('\n')[0]);
			}
		}

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

		private IEnumerator Start()
		{
			shotDir = Path.Combine(Paths.ConfigPath, "DeathSpectator", "autotest");
			Directory.CreateDirectory(shotDir);
			string[] files = Directory.GetFiles(shotDir);
			for (int i = 0; i < files.Length; i++)
			{
				File.Delete(files[i]);
			}
			logFile = Path.Combine(shotDir, "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(4f);
			GameNetworkManager.Instance.currentSaveFileName = "LCDeathSpectatorAutoTest";
			menu.ClickHostButton();
			yield return (object)new WaitForSeconds(1f);
			menu.lobbyNameInputField.text = "DeathSpectatorAutoTest";
			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("vaisseau non chargé");
				yield break;
			}
			yield return (object)new WaitForSeconds(6f);
			StartMatchLever obj = Object.FindObjectOfType<StartMatchLever>();
			obj.singlePlayerEnabled = true;
			obj.leverHasBeenPulled = true;
			obj.StartGame();
			Log("Levier tiré, lune : " + sor.currentLevel.PlanetName + ".");
			yield return WaitFor(() => sor.shipHasLanded && RoundManager.Instance.dungeonCompletedGenerating, 240f);
			if (!sor.shipHasLanded)
			{
				Finish("le vaisseau n'a pas atterri");
				yield break;
			}
			me = sor.localPlayerController;
			yield return (object)new WaitForSeconds(4f);
			me.TeleportPlayer(((Component)me).transform.position + ((Component)me).transform.forward * 4f, false, 0f, false, true);
			yield return (object)new WaitForSeconds(1f);
			Vector3 eye = ((Component)me.gameplayCamera).transform.position;
			StartOfRound obj2 = sor;
			obj2.livingPlayers++;
			me.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 0, default(Vector3), false);
			yield return (object)new WaitForSeconds(0.5f);
			Check(me.isPlayerDead, "joueur mort");
			Check(!sor.allPlayersDead && !sor.overrideSpectateCamera, "partie toujours en cours (vivant fictif)");
			Check(SpectatorCam.Active, "vol libre activé automatiquement à la mort");
			Transform cam = ((Component)sor.spectateCamera).transform;
			Check(Vector3.Distance(cam.position, eye) < 0.5f, $"caméra au point de mort ({Vector3.Distance(cam.position, eye):0.00} m des yeux)");
			Check((Object)(object)sor.activeCamera == (Object)(object)sor.spectateCamera, "caméra active = caméra spectateur");
			Check(((Component)StartOfRound.Instance.audioListener).transform.IsChildOf(cam) || Vector3.Distance(((Component)sor.audioListener).transform.position, cam.position) < 0.1f, "le son suit la caméra");
			string text = ((TMP_Text)HUDManager.Instance.spectatingPlayerText).text;
			Check(text.Contains("Vol libre"), "texte spectateur : " + text.Split('\n')[0]);
			yield return Capture("01_mort_vol_libre");
			Vector3 p0 = cam.position;
			float speed = SpectatorCam.CurrentSpeed;
			yield return HoldKey((Key)37, 1f);
			float num = Vector3.Distance(p0, cam.position);
			Check(num > speed * 0.6f && num < speed * 1.5f, $"avance de {num:0.0} m en 1 s à {speed} m/s");
			p0 = cam.position;
			yield return HoldKey((Key)1, 1f);
			Check(cam.position.y - p0.y > speed * 0.6f, $"monte de {cam.position.y - p0.y:0.0} m avec Espace");
			p0 = cam.position;
			yield return HoldKeys((Key[])(object)new Key[2]
			{
				(Key)37,
				(Key)51
			}, 0.5f);
			Check(Vector3.Distance(p0, cam.position) > speed * Settings.SprintMult.Value * 0.3f, $"sprint : {Vector3.Distance(p0, cam.position):0.0} m en 0,5 s");
			yield return Capture("02_apres_vol");
			yield return Scroll(120f);
			Check(SpectatorCam.CurrentSpeed > speed, $"molette vers le haut : {speed} → {SpectatorCam.CurrentSpeed:0.0} m/s");
			yield return Scroll(-120f);
			Check(Mathf.Abs(SpectatorCam.CurrentSpeed - speed) < 0.01f, "molette vers le bas : vitesse revenue");
			bool l0 = SpectatorCam.LightOn;
			yield return PressKey(Settings.LightKey.Value);
			Check(SpectatorCam.LightOn != l0, "touche lumière : bascule");
			Transform val = cam.Find("DeathSpectatorLight");
			Check((Object)(object)val != (Object)null && ((Behaviour)((Component)val).GetComponent<Light>()).enabled == SpectatorCam.LightOn, "lumière accrochée à la caméra et dans le bon état");
			if (!SpectatorCam.LightOn)
			{
				yield return PressKey(Settings.LightKey.Value);
			}
			EntranceTeleport val2 = ((IEnumerable<EntranceTeleport>)Object.FindObjectsOfType<EntranceTeleport>()).FirstOrDefault((Func<EntranceTeleport, bool>)((EntranceTeleport e) => e.entranceId == 0 && !e.isEntranceToBuilding));
			if ((Object)(object)val2 != (Object)null)
			{
				SetPose(val2.entrancePoint.position + Vector3.up * 1.5f);
				yield return (object)new WaitForSeconds(1.5f);
				LocalVolumetricFog indoorFog = RoundManager.Instance.indoorFog;
				Check((Object)(object)indoorFog == (Object)null || !((Component)indoorFog).gameObject.activeSelf || ((Behaviour)indoorFog).enabled, "brouillard intérieur activé dans le bâtiment");
				Check(!((Behaviour)TimeOfDay.Instance.sunDirect).enabled, "soleil coupé dans le bâtiment");
				yield return Capture("03_interieur_lumiere");
				yield return PressKey(Settings.LightKey.Value);
				yield return (object)new WaitForSeconds(0.3f);
				yield return Capture("04_interieur_sans_lumiere");
				yield return PressKey(Settings.LightKey.Value);
				SetPose(sor.elevatorTransform.position + Vector3.up * 8f);
				yield return (object)new WaitForSeconds(1.5f);
				Check(((Behaviour)TimeOfDay.Instance.sunDirect).enabled, "soleil rallumé dehors");
			}
			else
			{
				Check(ok: false, "entrée intérieure introuvable");
			}
			yield return PressKey(Settings.ModeKey.Value);
			Check(SpectatorCam.Active, "touche mode sans joueur vivant : reste en vol libre");
			sor.ReviveDeadPlayers();
			yield return (object)new WaitForSeconds(1f);
			Check(!me.isPlayerDead && !SpectatorCam.Active, "réanimé : vol libre terminé");
			Check((Object)(object)cam.Find("DeathSpectatorLight") == (Object)null, "lumière supprimée à la réanimation");
			Check((Object)(object)sor.activeCamera == (Object)(object)me.gameplayCamera, "caméra du joueur rendue");
			yield return Capture("05_reanime");
			yield return (object)new WaitForSeconds(1f);
			me.KillPlayer(Vector3.zero, true, (CauseOfDeath)0, 0, default(Vector3), false);
			yield return WaitFor(() => sor.overrideSpectateCamera, 5f);
			yield return (object)new WaitForSeconds(0.5f);
			Check(sor.allPlayersDead && sor.overrideSpectateCamera, "tous morts : fin de partie du jeu");
			Check(!SpectatorCam.Active, "vol libre rendu au jeu pendant la fin de partie");
			yield return Capture("06_fin_de_partie");
			yield return WaitFor(() => sor.inShipPhase && !me.isPlayerDead, 90f);
			Check(!me.isPlayerDead && !SpectatorCam.Active, "de retour en orbite, vivant, sans vol libre");
			Finish(null);
		}

		private static void SetPose(Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			SpectatorCam.SetPositionForTest(p);
		}

		private static IEnumerator HoldKey(Key key, float seconds)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected I4, but got Unknown
			return HoldKeys((Key[])(object)new Key[1] { (Key)(int)key }, seconds);
		}

		private static IEnumerator HoldKeys(Key[] keys, float seconds)
		{
			Keyboard kb = Keyboard.current;
			InputSystem.QueueStateEvent<KeyboardState>((InputDevice)(object)kb, new KeyboardState(keys), -1.0);
			yield return (object)new WaitForSeconds(seconds);
			InputSystem.QueueStateEvent<KeyboardState>((InputDevice)(object)kb, default(KeyboardState), -1.0);
			yield return null;
			yield return null;
		}

		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 Scroll(float y)
		{
			InputSystem.QueueStateEvent<MouseState>((InputDevice)(object)Mouse.current, new MouseState
			{
				scroll = new Vector2(0f, y)
			}, -1.0);
			yield return null;
			yield return null;
			InputSystem.QueueStateEvent<MouseState>((InputDevice)(object)Mouse.current, default(MouseState), -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(shotDir, 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);
			}
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		[HarmonyPatch(typeof(PlayerControllerB), "KillPlayer")]
		[HarmonyPrefix]
		private static void KillPlayerPrefix(PlayerControllerB __instance)
		{
			if (((NetworkBehaviour)__instance).IsOwner && !__instance.isPlayerDead && (Object)(object)__instance == (Object)(object)GameNetworkManager.Instance?.localPlayerController)
			{
				SpectatorCam.RememberDeathPose(__instance);
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")]
		[HarmonyPostfix]
		private static void LateUpdatePostfix(PlayerControllerB __instance)
		{
			if (!((Object)(object)GameNetworkManager.Instance == (Object)null) && !((Object)(object)__instance != (Object)(object)GameNetworkManager.Instance.localPlayerController))
			{
				SpectatorCam.Tick(__instance);
			}
		}

		[HarmonyPatch(typeof(PlayerControllerB), "ActivateItem_performed")]
		[HarmonyPrefix]
		private static bool ActivateItemPrefix(PlayerControllerB __instance, CallbackContext context)
		{
			if (!SpectatorCam.IsLocalFlying(__instance))
			{
				return true;
			}
			if (((CallbackContext)(ref context)).performed)
			{
				SpectatorCam.OnActivatePressed(__instance);
			}
			return false;
		}

		[HarmonyPatch(typeof(PlayerControllerB), "Interact_performed")]
		[HarmonyPrefix]
		private static bool InteractPrefix(PlayerControllerB __instance)
		{
			return !SpectatorCam.IsLocalFlying(__instance);
		}

		[HarmonyPatch(typeof(HUDManager), "SetSpectatingTextToPlayer")]
		[HarmonyPostfix]
		private static void SpectatingTextPostfix()
		{
			SpectatorCam.AfterSpectatingText();
		}

		[HarmonyPatch(typeof(TimeOfDay), "SetInsideLightingDimness")]
		[HarmonyPostfix]
		private static void SunPostfix(TimeOfDay __instance)
		{
			SpectatorCam.FixSun(__instance);
		}

		[HarmonyPatch(typeof(StartOfRound), "UpdateOcclusionCuller")]
		[HarmonyPostfix]
		private static void OcclusionPostfix(StartOfRound __instance)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (SpectatorCam.Active && !(SpectatorCam.Position.y < -80f))
			{
				ManualCameraRenderer mapScreen = __instance.mapScreen;
				if (!((Object)(object)mapScreen == (Object)null) && !((Object)(object)mapScreen.mapCamera == (Object)null))
				{
					__instance.SetOcclusionCullerToPosition(((Component)mapScreen.mapCamera).transform.position - new Vector3(0f, 3.636f, 0f));
				}
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "OnLocalDisconnect")]
		[HarmonyPostfix]
		private static void Disconnect()
		{
			SpectatorCam.ResetAll();
		}
	}
	[BepInPlugin("emeric.deathspectator", "DeathSpectator", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "emeric.deathspectator";

		public const string Name = "DeathSpectator";

		public const string Version = "1.0.0";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0021: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Settings.Init(((BaseUnityPlugin)this).Config);
			new Harmony("emeric.deathspectator").PatchAll();
			if (Settings.AutoTest.Value)
			{
				GameObject val = new GameObject("DeathSpectatorAutoTest")
				{
					hideFlags = (HideFlags)61
				};
				Object.DontDestroyOnLoad((Object)val);
				val.AddComponent<AutoTestDriver>();
			}
			Log.LogInfo((object)"DeathSpectator 1.0.0 chargé.");
		}
	}
	internal static class Settings
	{
		public const float MinSpeed = 1f;

		public const float MaxSpeed = 80f;

		public static ConfigEntry<bool> FreeOnDeath;

		public static ConfigEntry<float> Speed;

		public static ConfigEntry<float> SprintMult;

		public static ConfigEntry<bool> LightOnStart;

		public static ConfigEntry<bool> FollowWhenShipLeaves;

		public static ConfigEntry<bool> ShowHelp;

		public static ConfigEntry<Key> ModeKey;

		public static ConfigEntry<Key> LightKey;

		public static ConfigEntry<bool> AutoTest;

		public static void Init(ConfigFile cfg)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			FreeOnDeath = cfg.Bind<bool>("Spectateur", "VolLibreALaMort", true, "À ta mort, tu passes directement en vol libre (sinon tu suis un joueur comme dans le jeu de base, et la touche Mode passe en vol libre).");
			Speed = cfg.Bind<float>("Spectateur", "Vitesse", 10f, new ConfigDescription("Vitesse de vol de départ (m/s). La molette la change en jeu.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 80f), Array.Empty<object>()));
			SprintMult = cfg.Bind<float>("Spectateur", "MultiplicateurSprint", 3f, new ConfigDescription("Vitesse multipliée en maintenant la touche de sprint.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			LightOnStart = cfg.Bind<bool>("Spectateur", "LumiereAuDepart", true, "La lumière (que toi seul vois) est allumée en passant en vol libre.");
			FollowWhenShipLeaves = cfg.Bind<bool>("Spectateur", "SuivreAuDecollage", true, "Quand le vaisseau décolle, repasse automatiquement en suivi d'un joueur pour partir avec lui.");
			ShowHelp = cfg.Bind<bool>("Spectateur", "AfficherAide", true, "Affiche les commandes en bas de l'écran en vol libre.");
			ModeKey = cfg.Bind<Key>("Touches", "Mode", (Key)36, "Mort : passer du vol libre au suivi d'un joueur, et inversement.");
			LightKey = cfg.Bind<Key>("Touches", "Lumiere", (Key)26, "Vol libre : allumer / éteindre la lumière (toi seul la vois).");
			AutoTest = cfg.Bind<bool>("Test", "AutoTest", false, "Développeur : lance tout seul une partie de test.");
		}
	}
	internal static class SpectatorCam
	{
		private const float InsideY = -80f;

		private const float AimAngle = 20f;

		private static bool wasDead;

		private static Vector3 pos;

		private static float yaw;

		private static float pitch;

		private static float speed = 10f;

		private static bool lightOn;

		private static Light light;

		private static bool hasDeathPose;

		private static Vector3 deathPos;

		private static Quaternion deathRot;

		private static string label;

		private static float labelSpeed = -1f;

		private static bool labelLight;

		public static bool Active { get; private set; }

		public static float CurrentSpeed => speed;

		public static Vector3 Position => pos;

		public static bool LightOn => lightOn;

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

		public static void RememberDeathPose(PlayerControllerB me)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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 (!((Object)(object)me.gameplayCamera == (Object)null))
			{
				deathPos = ((Component)me.gameplayCamera).transform.position;
				deathRot = ((Component)me.gameplayCamera).transform.rotation;
				hasDeathPose = true;
			}
		}

		public static void Tick(PlayerControllerB me)
		{
			//IL_00b0: 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)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: 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)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			if (!me.isPlayerDead)
			{
				if (wasDead)
				{
					OnRevived();
				}
				return;
			}
			StartOfRound instance = StartOfRound.Instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.spectateCamera == (Object)null)
			{
				return;
			}
			if (!wasDead)
			{
				wasDead = true;
				speed = Mathf.Clamp(Settings.Speed.Value, 1f, 80f);
				lightOn = Settings.LightOnStart.Value;
				Plugin.Log.LogInfo((object)"Joueur local mort : mode spectateur.");
				if (Settings.FreeOnDeath.Value)
				{
					if (hasDeathPose)
					{
						Enter(me, deathPos, deathRot);
					}
					else
					{
						Enter(me, ((Component)instance.spectateCamera).transform.position, ((Component)instance.spectateCamera).transform.rotation);
					}
				}
			}
			if (instance.overrideSpectateCamera)
			{
				if (Active)
				{
					Exit(me, "séquence de fin de partie");
				}
				return;
			}
			bool flag = instance.shipIsLeaving && Settings.FollowWhenShipLeaves.Value;
			if (Active && flag)
			{
				Plugin.Log.LogInfo((object)"Le vaisseau décolle : retour au suivi d'un joueur.");
				if (!FollowPlayer(me, Target(me, ((Component)instance.spectateCamera).transform)))
				{
					Exit(me, "décollage");
				}
			}
			if (!InputBlocked(me))
			{
				Keyboard current = Keyboard.current;
				bool flag2 = current != null && (int)Settings.ModeKey.Value != 0 && ((ButtonControl)current[Settings.ModeKey.Value]).wasPressedThisFrame;
				if (Active)
				{
					if (flag2)
					{
						FollowPlayer(me, Target(me, ((Component)instance.spectateCamera).transform));
					}
					else if (current != null && (int)Settings.LightKey.Value != 0 && ((ButtonControl)current[Settings.LightKey.Value]).wasPressedThisFrame)
					{
						lightOn = !lightOn;
					}
				}
				else if (!flag)
				{
					if (!flag2)
					{
						MovementActions movement = me.playerActions.Movement;
						if (!((MovementActions)(ref movement)).Crouch.WasPressedThisFrame())
						{
							goto IL_020b;
						}
					}
					Transform transform = ((Component)instance.spectateCamera).transform;
					Enter(me, transform.position, transform.rotation);
				}
				goto IL_020b;
			}
			goto IL_0218;
			IL_020b:
			if (Active)
			{
				Fly(me);
			}
			goto IL_0218;
			IL_0218:
			if (Active)
			{
				Apply(me, instance);
			}
		}

		public static void OnActivatePressed(PlayerControllerB me)
		{
			StartOfRound instance = StartOfRound.Instance;
			if (Active && !((Object)(object)instance == (Object)null) && !((Object)(object)instance.spectateCamera == (Object)null) && !InputBlocked(me))
			{
				FollowPlayer(me, Target(me, ((Component)instance.spectateCamera).transform));
			}
		}

		public static void ResetAll()
		{
			Active = false;
			wasDead = false;
			hasDeathPose = false;
			label = null;
			if ((Object)(object)light != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)light).gameObject);
			}
			light = null;
		}

		private static void OnRevived()
		{
			Plugin.Log.LogInfo((object)"Joueur local de nouveau en vie : fin du mode spectateur.");
			ResetAll();
		}

		private static void Enter(PlayerControllerB me, Vector3 position, Quaternion rotation)
		{
			//IL_0006: 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_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_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_0059: Unknown result type (might be due to invalid IL or missing references)
			Active = true;
			pos = position;
			Vector3 eulerAngles = ((Quaternion)(ref rotation)).eulerAngles;
			yaw = eulerAngles.y;
			pitch = Mathf.Clamp(Mathf.DeltaAngle(0f, eulerAngles.x), -89f, 89f);
			label = null;
			EnsureLight(me);
			Plugin.Log.LogInfo((object)$"Vol libre à {pos}.");
		}

		private static void Exit(PlayerControllerB me, string why)
		{
			Active = false;
			if ((Object)(object)light != (Object)null)
			{
				((Behaviour)light).enabled = false;
			}
			HUDManager instance = HUDManager.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.spectatingPlayerText != (Object)null)
			{
				((TMP_Text)instance.spectatingPlayerText).text = (((Object)(object)me.spectatedPlayerScript != (Object)null) ? ("(Spectating: " + me.spectatedPlayerScript.playerUsername + ")") : "");
			}
			Plugin.Log.LogInfo((object)("Fin du vol libre (" + why + ")."));
		}

		private static bool FollowPlayer(PlayerControllerB me, PlayerControllerB target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return false;
			}
			me.spectatedPlayerScript = target;
			StartOfRound.Instance.SetPlayerSafeInShip();
			me.SetSpectatedPlayerEffects(false);
			Exit(me, "suivi de " + target.playerUsername);
			return true;
		}

		private static PlayerControllerB Target(PlayerControllerB me, Transform cam)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: 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)
			PlayerControllerB val = null;
			PlayerControllerB result = null;
			float num = 20f;
			float num2 = float.MaxValue;
			PlayerControllerB[] allPlayerScripts = StartOfRound.Instance.allPlayerScripts;
			foreach (PlayerControllerB val2 in allPlayerScripts)
			{
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)me) && !val2.isPlayerDead && val2.isPlayerControlled)
				{
					Vector3 val3 = ((Component)val2).transform.position + Vector3.up - cam.position;
					float num3 = Vector3.Angle(cam.forward, val3);
					if (num3 < num)
					{
						num = num3;
						val = val2;
					}
					if (((Vector3)(ref val3)).sqrMagnitude < num2)
					{
						num2 = ((Vector3)(ref val3)).sqrMagnitude;
						result = val2;
					}
				}
			}
			if (!((Object)(object)val != (Object)null))
			{
				return result;
			}
			return val;
		}

		private static bool InputBlocked(PlayerControllerB me)
		{
			if (!me.isTypingChat && !me.inTerminalMenu && !me.disableLookInput)
			{
				if ((Object)(object)me.quickMenuManager != (Object)null)
				{
					return me.quickMenuManager.isMenuOpen;
				}
				return false;
			}
			return true;
		}

		private static void Fly(PlayerControllerB me)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: 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_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: 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_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			MovementActions movement = me.playerActions.Movement;
			Vector2 val = ((MovementActions)(ref movement)).Look.ReadValue<Vector2>() * 0.008f;
			IngamePlayerSettings instance = IngamePlayerSettings.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				val *= (float)instance.settings.lookSensitivity;
				if (instance.flipCamera)
				{
					val.x = 0f - val.x;
				}
				if (instance.settings.invertYAxis)
				{
					val.y = 0f - val.y;
				}
			}
			yaw += val.x;
			pitch = Mathf.Clamp(pitch - val.y, -89f, 89f);
			Mouse current = Mouse.current;
			if (current != null)
			{
				float y = ((InputControl<Vector2>)(object)current.scroll).ReadValue().y;
				if (y > 0f)
				{
					speed = Mathf.Min(speed * 1.25f, 80f);
				}
				else if (y < 0f)
				{
					speed = Mathf.Max(speed / 1.25f, 1f);
				}
			}
			Vector2 val2 = ((MovementActions)(ref movement)).Move.ReadValue<Vector2>();
			float num = (((MovementActions)(ref movement)).Jump.IsPressed() ? 1f : 0f) - (((MovementActions)(ref movement)).Crouch.IsPressed() ? 1f : 0f);
			Vector3 val3 = Quaternion.Euler(pitch, yaw, 0f) * new Vector3(val2.x, 0f, val2.y) + Vector3.up * num;
			if (((Vector3)(ref val3)).sqrMagnitude > 1f)
			{
				((Vector3)(ref val3)).Normalize();
			}
			float num2 = speed * (((MovementActions)(ref movement)).Sprint.IsPressed() ? Settings.SprintMult.Value : 1f);
			pos += val3 * (num2 * Time.deltaTime);
		}

		private static void Apply(PlayerControllerB me, StartOfRound sor)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			((Component)sor.spectateCamera).transform.SetPositionAndRotation(pos, Quaternion.Euler(pitch, yaw, 0f));
			bool enabled = pos.y < -80f;
			RoundManager instance = RoundManager.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)instance.indoorFog != (Object)null && ((Component)instance.indoorFog).gameObject.activeSelf)
			{
				((Behaviour)instance.indoorFog).enabled = enabled;
			}
			if ((Object)(object)light == (Object)null)
			{
				EnsureLight(me);
			}
			if ((Object)(object)light != (Object)null && ((Behaviour)light).enabled != lightOn)
			{
				((Behaviour)light).enabled = lightOn;
			}
			UpdateLabel();
		}

		public static void FixSun(TimeOfDay tod)
		{
			if (Active && !((Object)(object)tod.sunDirect == (Object)null) && !((Object)(object)tod.sunIndirect == (Object)null) && !((Object)(object)StartOfRound.Instance == (Object)null) && !StartOfRound.Instance.allPlayersDead)
			{
				bool enabled = pos.y >= -80f;
				((Behaviour)tod.sunDirect).enabled = enabled;
				((Behaviour)tod.sunIndirect).enabled = enabled;
			}
		}

		private static void EnsureLight(PlayerControllerB me)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: 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)
			if (!((Object)(object)light != (Object)null) && !((Object)(object)me.nightVision == (Object)null) && !((Object)(object)StartOfRound.Instance == (Object)null) && !((Object)(object)StartOfRound.Instance.spectateCamera == (Object)null))
			{
				Light nightVision = me.nightVision;
				GameObject val = new GameObject("DeathSpectatorLight");
				val.SetActive(false);
				val.transform.SetParent(((Component)StartOfRound.Instance.spectateCamera).transform, false);
				light = val.AddComponent<Light>();
				light.type = nightVision.type;
				light.color = nightVision.color;
				light.intensity = nightVision.intensity;
				light.range = nightVision.range;
				light.spotAngle = nightVision.spotAngle;
				light.cullingMask = nightVision.cullingMask;
				light.shadows = (LightShadows)0;
				HDAdditionalLightData val2 = val.AddComponent<HDAdditionalLightData>();
				HDAdditionalLightData component = ((Component)nightVision).GetComponent<HDAdditionalLightData>();
				if ((Object)(object)component != (Object)null)
				{
					component.CopyTo(val2);
				}
				val2.affectsVolumetric = false;
				((Behaviour)light).enabled = false;
				val.SetActive(true);
				Plugin.Log.LogInfo((object)string.Format("Lumière spectateur créée ({0}, portée {1}, intensité {2}, HDRP {3}).", nightVision.type, nightVision.range, nightVision.intensity, ((Object)(object)component != (Object)null) ? "copié" : "par défaut"));
			}
		}

		private static void UpdateLabel()
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			HUDManager instance = HUDManager.Instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.spectatingPlayerText == (Object)null))
			{
				if (label == null || labelSpeed != speed || labelLight != lightOn)
				{
					labelSpeed = speed;
					labelLight = lightOn;
					label = (Settings.ShowHelp.Value ? string.Format("(Vol libre · {0:0.#} m/s)\n<size=50%>ZQSD voler · Espace/Ctrl haut/bas · Maj vite · Molette vitesse\nClic suivre · {1} mode · {2} lumière ({3})</size>", speed, Settings.ModeKey.Value, Settings.LightKey.Value, lightOn ? "on" : "off") : $"(Vol libre · {speed:0.#} m/s)");
				}
				if (((TMP_Text)instance.spectatingPlayerText).text != label)
				{
					((TMP_Text)instance.spectatingPlayerText).text = label;
				}
			}
		}

		public static void AfterSpectatingText()
		{
			if (Active)
			{
				label = null;
				UpdateLabel();
			}
		}

		internal static void SetPositionForTest(Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			pos = p;
		}

		public static bool IsLocalFlying(PlayerControllerB p)
		{
			if (Active && (Object)(object)p != (Object)null)
			{
				return (Object)(object)p == (Object)(object)Local;
			}
			return false;
		}
	}
}