Decompiled source of DeadPossession v0.7.24

DeadPossession.dll

Decompiled 7 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using REPOLib.Modules;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.InputSystem;

[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("DeadPossession")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.7.24.0")]
[assembly: AssemblyInformationalVersion("0.7.24+de4485b5cf72e35f8232bae2215441ec021813bc")]
[assembly: AssemblyProduct("DeadPossession")]
[assembly: AssemblyTitle("DeadPossession")]
[assembly: AssemblyVersion("0.7.24.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 DeadPossession
{
	public static class ConfigSettings
	{
		public static ConfigEntry<float> MaxPossessionDuration { get; private set; }

		public static ConfigEntry<float> PossessionCooldown { get; private set; }

		public static ConfigEntry<float> SenseBlurIntensity { get; private set; }

		public static ConfigEntry<KeyCode> PossessionKey { get; private set; }

		public static ConfigEntry<float> MouseSensitivity { get; private set; }

		public static ConfigEntry<float> PuppetFloorBump { get; private set; }

		public static ConfigEntry<bool> DebugPossessWhileAlive { get; private set; }

		public static ConfigEntry<bool> DebugKillOnLevelStart { get; private set; }

		public static void Bind(ConfigFile config)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected O, but got Unknown
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Expected O, but got Unknown
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Expected O, but got Unknown
			MaxPossessionDuration = config.Bind<float>("Possession", "MaxPossessionDuration", 60f, new ConfigDescription("Maximum possession duration in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>()));
			PossessionCooldown = config.Bind<float>("Possession", "PossessionCooldown", 120f, new ConfigDescription("Cooldown before the dead player can possess again, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
			SenseBlurIntensity = config.Bind<float>("Possession", "SenseBlurIntensity", 0f, new ConfigDescription("Amount of vision blurring while possessing (0 = none, 1 = fully blind).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			PossessionKey = config.Bind<KeyCode>("Possession", "PossessionKey", (KeyCode)102, "Key to start/end possession (Unity KeyCode name).");
			MouseSensitivity = config.Bind<float>("Possession", "MouseSensitivity", 0.1f, new ConfigDescription("Mouse look multiplier while possessing (vanilla mouse feel is around 0.25 for this input scale).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>()));
			PuppetFloorBump = config.Bind<float>("Possession", "PuppetFloorBump", 0f, new ConfigDescription("Vertical bias of the possessed model anchor, in meters. The mod anchors the model's origin to the floor under the player body; shift up/down here if it floats or sinks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), Array.Empty<object>()));
			DebugPossessWhileAlive = config.Bind<bool>("Debug", "DebugPossessWhileAlive", false, "TESTING ONLY: allow starting possession while alive (no need to die). Use with the PossessionKey.");
			DebugKillOnLevelStart = config.Bind<bool>("Debug", "DebugKillOnLevelStart", false, "TESTING ONLY: vanilla-kill the local player a few seconds after entering a level and disable the all-players-dead run end. Gives the real dead/spectate flow in singleplayer.");
		}
	}
	public static class HostConfig
	{
		private static float[] hostValues;

		private static bool AppliesLocal
		{
			get
			{
				if (GameManager.Multiplayer())
				{
					return PhotonNetwork.IsMasterClient;
				}
				return true;
			}
		}

		public static float MaxPossessionDuration => Get(0, ConfigSettings.MaxPossessionDuration.Value);

		public static float PossessionCooldown => Get(1, ConfigSettings.PossessionCooldown.Value);

		public static float SenseBlurIntensity => Get(2, ConfigSettings.SenseBlurIntensity.Value);

		private static float Get(int index, float localValue)
		{
			if (AppliesLocal || hostValues == null || hostValues.Length < 3)
			{
				return localValue;
			}
			return hostValues[index];
		}

		public static void Accept(float[] values)
		{
			if (values != null && values.Length == 3)
			{
				hostValues = values;
				Plugin.Log($"Host config accepted (duration={values[0]:F0}, cooldown={values[1]:F0}, blur={values[2]:F2})");
			}
		}
	}
	[BepInPlugin("yo.DeadPossession", "DeadPossession", "0.7.24")]
	[BepInProcess("REPO.exe")]
	public class Plugin : BaseUnityPlugin
	{
		private static ManualLogSource logSource;

		public static Plugin Instance { get; private set; }

		private void Awake()
		{
			//IL_0025: 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)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			ConfigSettings.Bind(((BaseUnityPlugin)this).Config);
			logSource = Logger.CreateLogSource("DeadPossession");
			new Harmony("yo.DeadPossession").PatchAll();
			GameObject val = new GameObject("DeadPossession");
			Object.DontDestroyOnLoad((Object)val);
			((Object)val).hideFlags = (HideFlags)61;
			val.AddComponent<PossessionBehavior>();
			val.AddComponent<PossessionNetworking>();
			Log(string.Format("{0} v{1} loaded | DebugKillOnLevelStart={2} | DebugPossessWhileAlive={3}", "DeadPossession", "0.7.24", ConfigSettings.DebugKillOnLevelStart.Value, ConfigSettings.DebugPossessWhileAlive.Value));
		}

		public static void Log(string message)
		{
			logSource.LogInfo((object)message);
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "yo.DeadPossession";

		public const string PLUGIN_NAME = "DeadPossession";

		public const string PLUGIN_VERSION = "0.7.24";
	}
	[DefaultExecutionOrder(32766)]
	public class PossessionBehavior : MonoBehaviour
	{
		private const float SearchRadius = 20f;

		private const float PuppetScale = 1f;

		private const float CameraEyeHeight = 0.95f;

		private float lastTimeNotifiedMissing;

		private bool flashlightWasOn;

		private float lookYaw;

		private float lookPitch;

		private float levelEnterTime = -999f;

		private bool previousInLevel;

		private bool killedThisLevel;

		private float lastKillAttempt;

		private bool debugWasInMainMenu;

		private GameObject possessedPuppet;

		private GameObject hiddenPlayerVisuals;

		private bool debugEnergyWas;

		private float attackDamageCooldown;

		private Light possessionLight;

		private float nextMoveBroadcast;

		private const float PuppetDriftFreeze = 0.05f;

		private Dictionary<Transform, Vector3> puppetSnapshot;

		private static bool possessionStartedDead;

		private bool forcedMainState;

		private gameState savedGameState;

		private int inputClearLogs;

		private bool avatarWasActive;

		private string noVisionSteamId;

		private Vector3 aliveBodyReturnPos;

		private bool aliveReturnPlanned;

		private float? savedGrabRange;

		private Behaviour parkedLocalCamera;

		public static PlayerAvatar LocalAvatar { get; set; }

		public static EnemyGnome PossessedGnome { get; private set; }

		public static bool SpectateOwnedByMod { get; private set; }

		public static Transform PossessedRoot { get; private set; }

		public static bool AttackWindowOpen { get; private set; }

		private void Awake()
		{
			LocalAvatar = null;
			PossessedGnome = null;
			PossessedRoot = null;
			SpectateOwnedByMod = false;
		}

		private void Start()
		{
			ResolveLocalAvatar();
		}

		private static bool ResolveLocalAvatar()
		{
			if ((Object)(object)LocalAvatar != (Object)null)
			{
				return true;
			}
			LocalAvatar = PlayerAvatar.instance;
			return (Object)(object)LocalAvatar != (Object)null;
		}

		private bool EnsureBodyAwake()
		{
			Transform val = PlayerBodyRoot();
			object? obj = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
			PlayerController val2 = (PlayerController)((obj is PlayerController) ? obj : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return true;
			}
			if (!((Behaviour)val2).enabled)
			{
				((Behaviour)val2).enabled = true;
				Plugin.Log("PlayerController component was DISABLED — re-enabled for possession");
			}
			Transform val3 = val;
			while ((Object)(object)val3 != (Object)null)
			{
				if (!((Component)val3).gameObject.activeSelf)
				{
					((Component)val3).gameObject.SetActive(true);
					Plugin.Log("Body ancestor '" + ((Object)((Component)val3).gameObject).name + "' was INACTIVE — re-activated for possession");
				}
				val3 = val3.parent;
			}
			return ((Component)val).gameObject.activeInHierarchy;
		}

		private void FixedUpdate()
		{
			if (PossessionController.IsPossessing)
			{
				EnsureBodyAwake();
				PinPuppetToBody();
			}
		}

		private void PinPuppetToBody()
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: 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_0155: 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_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			Transform val = PlayerBodyRoot();
			if ((Object)(object)val == (Object)null || (Object)(object)possessedPuppet == (Object)null)
			{
				return;
			}
			if ((Object)(object)possessedPuppet.transform.parent != (Object)(object)val)
			{
				string text = (((Object)(object)possessedPuppet.transform.parent != (Object)null) ? ((Object)((Component)possessedPuppet.transform.parent).gameObject).name : "(none)");
				possessedPuppet.transform.SetParent(val, false);
				Plugin.Log("Puppet re-parented (was under " + text + ")");
			}
			Vector3 position = val.position;
			float num = GroundBelow(position, val) - position.y + ConfigSettings.PuppetFloorBump.Value;
			Vector3 localPosition = possessedPuppet.transform.localPosition;
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(0f, num, 0f);
			Vector3 value = localPosition - val2;
			if (((Vector3)(ref value)).sqrMagnitude > 0.0001f)
			{
				possessedPuppet.transform.localPosition = val2;
			}
			if (puppetSnapshot == null)
			{
				return;
			}
			float num2 = 0.0025000002f;
			foreach (KeyValuePair<Transform, Vector3> item in puppetSnapshot)
			{
				item.Deconstruct(out var key, out value);
				Transform val3 = key;
				Vector3 val4 = value;
				if (!((Object)(object)val3 == (Object)null))
				{
					Vector3 val5 = val3.localPosition - val4;
					if (((Vector3)(ref val5)).sqrMagnitude > num2)
					{
						val3.localPosition = val4;
						Plugin.Log("Puppet child drift frozen: " + ((Object)((Component)val3).gameObject).name + " (was " + ((Vector3)(ref val5)).ToString("F2") + ")");
					}
				}
			}
		}

		private void CapturePuppetSnapshot()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)possessedPuppet == (Object)null))
			{
				puppetSnapshot = new Dictionary<Transform, Vector3>();
				Transform[] componentsInChildren = possessedPuppet.GetComponentsInChildren<Transform>(true);
				foreach (Transform val in componentsInChildren)
				{
					puppetSnapshot[val] = val.localPosition;
				}
				Plugin.Log("Puppet snapshot captured (" + puppetSnapshot.Count + " nodes)");
			}
		}

		private float GroundBelow(Vector3 from, Transform bodyOf)
		{
			//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)
			//IL_000b: 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)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			RaycastHit[] array = Physics.RaycastAll(from + Vector3.up * 2f, Vector3.down, 25f);
			Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
			RaycastHit[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				RaycastHit val = array2[num];
				if (!((Object)(object)((RaycastHit)(ref val)).collider == (Object)null))
				{
					Transform root = ((Component)((RaycastHit)(ref val)).collider).transform.root;
					if ((!((Object)(object)possessedPuppet != (Object)null) || !((Component)((RaycastHit)(ref val)).collider).transform.IsChildOf(possessedPuppet.transform)) && !((Object)(object)root == (Object)(object)bodyOf.root))
					{
						return ((RaycastHit)(ref val)).point.y;
					}
				}
			}
			return from.y - 1.8f;
		}

		private void DrivePuppetWalk()
		{
			//IL_0035: 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_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)possessedPuppet == (Object)null)
			{
				return;
			}
			Animator componentInChildren = possessedPuppet.GetComponentInChildren<Animator>();
			Rigidbody val = PlayerBodyRb();
			if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)val == (Object)null))
			{
				Vector3 velocity = val.velocity;
				Vector3 val2 = new Vector3(velocity.x, 0f, velocity.z);
				int num;
				if (!(((Vector3)(ref val2)).magnitude > 0.2f))
				{
					val2 = val.angularVelocity;
					num = ((((Vector3)(ref val2)).magnitude > 0.5f) ? 1 : 0);
				}
				else
				{
					num = 1;
				}
				bool flag = (byte)num != 0;
				componentInChildren.SetBool("Moving", flag);
			}
		}

		private static Rigidbody PlayerBodyRb()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			object? obj = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
			PlayerController val = (PlayerController)((obj is PlayerController) ? obj : null);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return (Rigidbody)(AccessTools.Field(typeof(PlayerController), "rb")?.GetValue(val));
		}

		private static bool LocalAvatarDead()
		{
			if (PossessionController.IsPossessing)
			{
				return possessionStartedDead;
			}
			if ((Object)(object)LocalAvatar != (Object)null)
			{
				return (bool)AccessTools.Field(typeof(PlayerAvatar), "deadSet").GetValue(LocalAvatar);
			}
			return false;
		}

		private unsafe void ForceMainGameState(bool deadPossession)
		{
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Invalid comparison between Unknown and I4
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			FieldInfo fieldInfo = AccessTools.Field(typeof(GameDirector), "currentState");
			GameDirector instance = GameDirector.instance;
			if (fieldInfo == null || (Object)(object)instance == (Object)null || !deadPossession)
			{
				if (deadPossession && (Object)(object)instance == (Object)null)
				{
					Plugin.Log("Main-state force skipped: no GameDirector.instance");
				}
				return;
			}
			FieldInfo fieldInfo2 = AccessTools.Field(typeof(GameDirector), "outroStart");
			if (fieldInfo2 != null && (bool)fieldInfo2.GetValue(instance))
			{
				fieldInfo2.SetValue(instance, false);
			}
			if (instance.DisableInput)
			{
				instance.DisableInput = false;
			}
			object? obj = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
			PlayerController val = (PlayerController)((obj is PlayerController) ? obj : null);
			if ((Object)(object)val != (Object)null)
			{
				FieldInfo fieldInfo3 = AccessTools.Field(typeof(PlayerController), "InputDisableTimer");
				if (fieldInfo3 != null)
				{
					float num = (float)fieldInfo3.GetValue(val);
					if (num > 0f)
					{
						if (inputClearLogs < 5)
						{
							inputClearLogs++;
							Plugin.Log("InputDisable timer was " + num.ToString("0.000") + " — cleared for possession");
						}
						fieldInfo3.SetValue(val, 0f);
					}
				}
			}
			gameState val2 = (gameState)fieldInfo.GetValue(instance);
			if ((int)val2 != 2)
			{
				savedGameState = val2;
				forcedMainState = true;
				fieldInfo.SetValue(instance, (object)(gameState)2);
				Plugin.Log("GameDirector state " + ((object)(*(gameState*)(&val2))/*cast due to .constrained prefix*/).ToString() + " -> Main (player body physics awake)");
			}
		}

		private void RestoreMainGameState()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if (!forcedMainState)
			{
				return;
			}
			forcedMainState = false;
			if ((int)savedGameState == 3)
			{
				Plugin.Log("Release: kept Main state (saved state was Outro)");
				savedGameState = (gameState)2;
				return;
			}
			FieldInfo fieldInfo = AccessTools.Field(typeof(GameDirector), "currentState");
			GameDirector instance = GameDirector.instance;
			if (!(fieldInfo == null) && !((Object)(object)instance == (Object)null))
			{
				fieldInfo.SetValue(instance, savedGameState);
				Plugin.Log("GameDirector state restored to " + ((object)Unsafe.As<gameState, gameState>(ref savedGameState)/*cast due to .constrained prefix*/).ToString());
			}
		}

		public static bool IsEnemyOwned(Component component)
		{
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			if (PossessionController.IsPossessing && (Object)(object)PossessedRoot != (Object)null && (Object)(object)component.transform.root == (Object)(object)PossessedRoot.root)
			{
				return true;
			}
			return PossessionNetworking.IsRootPossessed(component.transform.root);
		}

		private bool CanPossess(bool isDead)
		{
			if (isDead)
			{
				return true;
			}
			if (ConfigSettings.DebugKillOnLevelStart.Value)
			{
				return false;
			}
			return ConfigSettings.DebugPossessWhileAlive.Value;
		}

		private static bool InLevel()
		{
			if ((Object)(object)LevelGenerator.Instance == (Object)null || (Object)(object)RunManager.instance == (Object)null || !(bool)AccessTools.Field(typeof(RunManager), "runStarted").GetValue(RunManager.instance))
			{
				return false;
			}
			Level levelCurrent = RunManager.instance.levelCurrent;
			if ((Object)(object)levelCurrent != (Object)null)
			{
				return levelCurrent.HasEnemies;
			}
			return false;
		}

		private void UpdateDebugAutoRestart()
		{
			if (!ConfigSettings.DebugKillOnLevelStart.Value)
			{
				return;
			}
			bool flag = SemiFunc.IsMainMenu();
			if (flag && !debugWasInMainMenu && Time.time > 2f)
			{
				if (AccessTools.Field(typeof(MainMenuOpen), "instance")?.GetValue(null) != null && !((Object)(object)RunManager.instance == (Object)null))
				{
					debugWasInMainMenu = true;
					SemiFunc.MainMenuSetSingleplayer();
					SemiFunc.MenuActionSingleplayerGame((string)null, (List<string>)null);
					Plugin.Log("Debug: main menu entered — starting fresh singleplayer run");
				}
			}
			else if (!flag)
			{
				debugWasInMainMenu = false;
			}
		}

		private void Update()
		{
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			PossessionController.Tick();
			UpdateDebugAutoRestart();
			if (!ResolveLocalAvatar())
			{
				return;
			}
			bool flag = InLevel();
			if (!flag)
			{
				previousInLevel = false;
				killedThisLevel = false;
				SpectateOwnedByMod = false;
				if (PossessionController.IsPossessing)
				{
					EndPossession();
				}
				return;
			}
			if (!previousInLevel)
			{
				levelEnterTime = Time.time;
			}
			previousInLevel = flag;
			bool flag2 = LocalAvatarDead();
			SpectateOwnedByMod = (ConfigSettings.DebugKillOnLevelStart.Value && flag2) || PossessionController.IsPossessing;
			if (PossessionController.IsPossessing)
			{
				if (!CanPossess(flag2) || (Object)(object)PossessedGnome == (Object)null)
				{
					EndPossession();
					return;
				}
				ForceMainGameState(flag2);
				EnsureVanillaGrabUsable();
				ReadLook();
				AttackPossessed();
				DrivePuppetWalk();
				if (attackDamageCooldown > 0f)
				{
					attackDamageCooldown -= Time.deltaTime;
				}
				EnergyRefill();
				EnsurePossessionVision();
				if ((Object)(object)PossessedGnome != (Object)null && Time.time > nextMoveBroadcast)
				{
					nextMoveBroadcast = Time.time + 0.1f;
					Transform val = PlayerBodyRoot();
					PhotonView val2 = PossessionNetworking.ViewIdOf(PossessedGnome);
					if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null)
					{
						PossessionNetworking.BroadcastMove(val2.ViewID, val.position);
					}
				}
			}
			if (ConfigSettings.DebugKillOnLevelStart.Value)
			{
				UpdateLevelKill(flag2);
			}
			if (CanPossess(flag2) && Input.GetKeyDown(ConfigSettings.PossessionKey.Value))
			{
				if (PossessionController.IsPossessing)
				{
					EndPossession();
				}
				else if (PossessionController.IsEligible())
				{
					TryStartPossession();
				}
			}
		}

		private void UpdateLevelKill(bool isDead)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Invalid comparison between Unknown and I4
			if (isDead)
			{
				if (killedThisLevel)
				{
					Plugin.Log("Debug: local player confirmed dead");
				}
				killedThisLevel = false;
			}
			else if (ConfigSettings.DebugKillOnLevelStart.Value && !(Time.time - levelEnterTime < 3f) && !((Object)(object)GameDirector.instance == (Object)null) && (int)GameDirector.instance.currentState == 2)
			{
				if (!killedThisLevel)
				{
					killedThisLevel = true;
					Plugin.Log("Debug: level entered, executing vanilla debug kill");
				}
				if (!(Time.time - lastKillAttempt < 2f))
				{
					lastKillAttempt = Time.time;
					KillLocalPlayerForTesting();
				}
			}
		}

		private static void KillLocalPlayerForTesting()
		{
			if ((Object)(object)LocalAvatar == (Object)null)
			{
				Plugin.Log("Debug kill: no local avatar yet");
				return;
			}
			object obj = AccessTools.Field(typeof(RunManager), "instance")?.GetValue(null);
			if (obj != null)
			{
				AccessTools.Field(typeof(RunManager), "allPlayersDeadCheckDisabled")?.SetValue(obj, true);
				Plugin.Log("Debug: allPlayersDeadCheckDisabled = true (run end disabled)");
			}
			else
			{
				Plugin.Log("Debug kill: RunManager.instance not found");
			}
			MethodInfo methodInfo = AccessTools.Method(typeof(PlayerAvatar), "PlayerDeath", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Log("Debug kill: PlayerDeath method not found");
				return;
			}
			try
			{
				methodInfo.Invoke(LocalAvatar, new object[1] { -1 });
				Plugin.Log("Debug: vanilla kill executed (waiting deadSet confirmation)");
			}
			catch (Exception ex)
			{
				Plugin.Log("Debug kill: PlayerDeath invoke failed: " + ex.Message);
			}
		}

		public static Transform RootOf(EnemyGnome gnome)
		{
			Enemy val = (((Object)(object)gnome != (Object)null) ? gnome.enemy : null);
			EnemyParent val2 = (((Object)(object)val == (Object)null) ? null : Traverse.Create((object)val).Field("EnemyParent").GetValue<EnemyParent>());
			if (!((Object)(object)val2 != (Object)null))
			{
				return ((Component)gnome).transform.root;
			}
			return ((Component)val2).transform;
		}

		public static void StripComponents(EnemyGnome gnome, Transform root)
		{
			if ((Object)(object)gnome == (Object)null || (Object)(object)root == (Object)null)
			{
				return;
			}
			HashSet<Type> hashSet = new HashSet<Type>
			{
				typeof(EnemyGnome),
				typeof(EnemyGnomeAnim),
				typeof(EnemyHealth),
				typeof(EnemyParent),
				typeof(Enemy),
				typeof(EnemyRigidbody),
				typeof(EnemyNavMeshAgent),
				typeof(EnemyGrounded),
				typeof(EnemyJump),
				typeof(PhysGrabObjectCollider),
				typeof(PhysGrabObjectCapsuleCollider),
				typeof(PhysGrabObjectMeshCollider),
				typeof(PhysGrabObjectSphereCollider),
				typeof(PhysGrabObjectBoxCollider)
			};
			List<(MonoBehaviour, Type)> list = new List<(MonoBehaviour, Type)>();
			MonoBehaviour[] componentsInChildren = ((Component)root).GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null))
				{
					Type type = ((object)val).GetType();
					if (!hashSet.Contains(type))
					{
						list.Add((val, type));
					}
				}
			}
			DestroyImmediateTopological(root, list);
		}

		private static void DestroyImmediateTopological(Transform root, List<(MonoBehaviour component, Type type)> candidates)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			HashSet<Object> hashSet = new HashSet<Object>();
			foreach (var (val, type) in candidates)
			{
				if ((Object)(object)val == (Object)null || hashSet.Contains((Object)(object)val))
				{
					continue;
				}
				bool flag = false;
				if ((Object)(object)root != (Object)null)
				{
					foreach (var candidate in candidates)
					{
						if ((Object)(object)candidate.component == (Object)null || (Object)(object)candidate.component == (Object)(object)val)
						{
							continue;
						}
						object[] customAttributes = ((object)candidate.component).GetType().GetCustomAttributes(typeof(RequireComponent), inherit: true);
						for (int i = 0; i < customAttributes.Length; i++)
						{
							RequireComponent val2 = (RequireComponent)customAttributes[i];
							if (val2.m_Type0 == type || val2.m_Type1 == type || val2.m_Type2 == type)
							{
								flag = true;
							}
						}
					}
				}
				if (!flag)
				{
					Object.DestroyImmediate((Object)(object)val);
					hashSet.Add((Object)(object)val);
				}
			}
			foreach (var candidate2 in candidates)
			{
				MonoBehaviour item = candidate2.component;
				if ((Object)(object)item != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)item);
				}
			}
		}

		private void ActivatePossession(EnemyGnome gnome)
		{
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			PossessedGnome = gnome;
			PossessedRoot = RootOf(gnome);
			AttackWindowOpen = false;
			attackDamageCooldown = 0f;
			bool flag = (Object)(object)LocalAvatar != (Object)null && (bool)AccessTools.Field(typeof(PlayerAvatar), "deadSet").GetValue(LocalAvatar);
			possessionStartedDead = flag;
			PossessionController.StartPossession();
			if (!flag)
			{
				SetLocalFlashlight(on: false);
			}
			ForceMainGameState(flag);
			Player localPlayer = PhotonNetwork.LocalPlayer;
			PossessionNetworking.BroadcastPossession((localPlayer != null) ? localPlayer.ActorNumber : (-1), gnome);
			Plugin.Log("Possession activated" + (flag ? " (dead flow)" : " (debug alive)"));
			((Component)PossessedRoot).gameObject.SetActive(false);
			Plugin.Log("Original enemy hidden: " + ((Object)((Component)PossessedRoot).gameObject).name);
			if (!flag)
			{
				object? obj = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
				PlayerController val = (PlayerController)((obj is PlayerController) ? obj : null);
				if ((Object)(object)val != (Object)null)
				{
					aliveBodyReturnPos = ((Component)val).transform.position;
					aliveReturnPlanned = true;
				}
			}
			TeleportPlayer(PossessedRoot.position + Vector3.up * 0.3f);
			ParkLocalCamera();
			BuildPuppet();
			object obj2 = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
			if (obj2 != null)
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(PlayerController), "DebugEnergy");
				if (fieldInfo != null)
				{
					try
					{
						debugEnergyWas = (bool)fieldInfo.GetValue(obj2);
						fieldInfo.SetValue(obj2, true);
						Plugin.Log("PlayerController.DebugEnergy = true (no stamina)");
					}
					catch (Exception ex)
					{
						Plugin.Log("DebugEnergy set failed: " + ex.Message);
					}
				}
			}
			Camera main = Camera.main;
			if ((Object)(object)main != (Object)null)
			{
				lookYaw = ((Component)main).transform.eulerAngles.y;
				lookPitch = 0f;
			}
			ApplyLookToVanillaRig();
			AddPossessionVisionLight();
			EnsurePossessionVision();
		}

		private static Transform PlayerBodyRoot()
		{
			object obj = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
			if (obj == null)
			{
				return null;
			}
			return ((Component)((obj is PlayerController) ? obj : null)).transform;
		}

		private unsafe static void TeleportPlayer(Vector3 destination)
		{
			//IL_0071: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			object obj = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
			if (obj == null)
			{
				Plugin.Log("Teleport: no PlayerController.instance");
				return;
			}
			Transform transform = ((Component)((obj is PlayerController) ? obj : null)).transform;
			object? obj2 = AccessTools.Field(typeof(PlayerController), "rb")?.GetValue(obj);
			Rigidbody val = (Rigidbody)((obj2 is Rigidbody) ? obj2 : null);
			if ((Object)(object)val != (Object)null)
			{
				((Component)val).transform.position = destination;
				val.velocity = Vector3.zero;
				val.angularVelocity = Vector3.zero;
			}
			else
			{
				Plugin.Log("Teleport: PlayerController rb not found");
			}
			transform.position = destination;
			object obj3 = AccessTools.Field(typeof(CameraPosition), "instance")?.GetValue(null);
			if (obj3 != null)
			{
				((Component)((obj3 is CameraPosition) ? obj3 : null)).transform.position = destination;
			}
			Vector3 val2 = destination;
			Plugin.Log("Player teleported to " + ((object)(*(Vector3*)(&val2))/*cast due to .constrained prefix*/).ToString());
		}

		private void BuildPuppet()
		{
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)possessedPuppet != (Object)null)
			{
				Object.Destroy((Object)(object)possessedPuppet);
				possessedPuppet = null;
			}
			if ((Object)(object)PossessedRoot == (Object)null || (Object)(object)PlayerBodyRoot() == (Object)null)
			{
				return;
			}
			_ = ((Component)PossessedRoot).gameObject.activeInHierarchy;
			((Component)PossessedRoot).gameObject.SetActive(true);
			GameObject val = Object.Instantiate<GameObject>(((Component)PossessedRoot).gameObject);
			((Component)PossessedRoot).gameObject.SetActive(false);
			MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val2 in componentsInChildren)
			{
				if ((Object)(object)val2 != (Object)null)
				{
					((Behaviour)val2).enabled = false;
				}
			}
			NavMeshAgent[] componentsInChildren2 = val.GetComponentsInChildren<NavMeshAgent>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				((Behaviour)componentsInChildren2[i]).enabled = false;
			}
			EnemyRigidbody[] componentsInChildren3 = val.GetComponentsInChildren<EnemyRigidbody>(true);
			for (int i = 0; i < componentsInChildren3.Length; i++)
			{
				Object.Destroy((Object)(object)componentsInChildren3[i]);
			}
			EnemyParent[] componentsInChildren4 = val.GetComponentsInChildren<EnemyParent>(true);
			for (int i = 0; i < componentsInChildren4.Length; i++)
			{
				Object.Destroy((Object)(object)componentsInChildren4[i]);
			}
			PhotonView[] componentsInChildren5 = val.GetComponentsInChildren<PhotonView>(true);
			for (int i = 0; i < componentsInChildren5.Length; i++)
			{
				Object.Destroy((Object)(object)componentsInChildren5[i]);
			}
			Rigidbody[] componentsInChildren6 = val.GetComponentsInChildren<Rigidbody>(true);
			for (int i = 0; i < componentsInChildren6.Length; i++)
			{
				componentsInChildren6[i].isKinematic = true;
			}
			Collider[] componentsInChildren7 = val.GetComponentsInChildren<Collider>(true);
			for (int i = 0; i < componentsInChildren7.Length; i++)
			{
				componentsInChildren7[i].enabled = false;
			}
			Transform val3 = PlayerBodyRoot();
			((Object)val).name = "PossessionPuppet";
			val.transform.SetParent(val3, false);
			val.transform.localPosition = Vector3.zero;
			val.transform.localRotation = Quaternion.identity;
			val.transform.localScale = Vector3.one * 1f;
			possessedPuppet = val;
			if (!val.activeSelf)
			{
				val.SetActive(true);
			}
			Animator componentInChildren = val.GetComponentInChildren<Animator>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.applyRootMotion = false;
				componentInChildren.cullingMode = (AnimatorCullingMode)0;
				Plugin.Log("Puppet animator: root motion off, culling always-animate");
			}
			object? obj = AccessTools.Field(typeof(PlayerAvatar), "playerAvatarVisuals")?.GetValue(LocalAvatar);
			PlayerAvatarVisuals val4 = (PlayerAvatarVisuals)((obj is PlayerAvatarVisuals) ? obj : null);
			if ((Object)(object)val4 != (Object)null)
			{
				hiddenPlayerVisuals = ((Component)val4).gameObject;
				hiddenPlayerVisuals.SetActive(false);
			}
			avatarWasActive = ((Component)LocalAvatar).gameObject.activeSelf;
			if (!avatarWasActive)
			{
				((Component)LocalAvatar).gameObject.SetActive(true);
				Plugin.Log("Local avatar GO re-activated (vanilla PhysGrabber)");
			}
			CapturePuppetSnapshot();
			Plugin.Log("Puppet built (scale " + 1f + ") under " + ((Object)val3).name + ", player model hidden");
		}

		private void ReadLook()
		{
			//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_0027: 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)
			float value = ConfigSettings.MouseSensitivity.Value;
			Mouse current = Mouse.current;
			if (current != null)
			{
				Vector2 val = ((InputControl<Vector2>)(object)((Pointer)current).delta).ReadValue();
				lookYaw += val.x * value;
				lookPitch = Mathf.Clamp(lookPitch - val.y * value, -80f, 80f);
			}
			else
			{
				lookYaw += Input.GetAxis("Mouse X") * value;
				lookPitch = Mathf.Clamp(lookPitch - Input.GetAxis("Mouse Y") * value, -80f, 80f);
			}
			ApplyLookToVanillaRig();
		}

		private void ApplyLookToVanillaRig()
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			CameraAim instance = CameraAim.Instance;
			Quaternion val = Quaternion.Euler(0f, lookYaw, 0f);
			if ((Object)(object)instance != (Object)null)
			{
				instance.SetPlayerAim(val, true);
				return;
			}
			object? obj = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
			PlayerController val2 = (PlayerController)((obj is PlayerController) ? obj : null);
			GameObject val3 = val2?.cameraGameObject ?? val2?.cameraGameObjectLocal;
			if (!((Object)(object)val3 == (Object)null))
			{
				val3.transform.localRotation = val;
			}
		}

		private void AttackPossessed()
		{
			if (Input.GetMouseButtonDown(1) && !(attackDamageCooldown > 0f))
			{
				PlayAttackAnimation();
				ManualAttackDamage();
			}
		}

		private void PlayAttackAnimation()
		{
			Animator val = (((Object)(object)possessedPuppet != (Object)null) ? possessedPuppet.GetComponentInChildren<Animator>() : null);
			if ((Object)(object)val != (Object)null)
			{
				val.SetTrigger("Attack");
			}
			else
			{
				Plugin.Log("Attack: no animator found on puppet");
			}
		}

		private void ManualAttackDamage()
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0037: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			Transform val = PlayerBodyRoot();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			attackDamageCooldown = 0.75f;
			Vector3 position = val.position;
			Vector3 val2 = Quaternion.Euler(0f, lookYaw, 0f) * Vector3.forward;
			Enemy[] array = Object.FindObjectsOfType<Enemy>();
			foreach (Enemy val3 in array)
			{
				if (((Object)(object)PossessedGnome != (Object)null && (Object)(object)val3 == (Object)(object)PossessedGnome.enemy) || Vector3.Distance(((Component)val3).transform.position, position) > 1.5f)
				{
					continue;
				}
				Vector3 val4 = ((Component)val3).transform.position - position;
				Vector3 normalized = ((Vector3)(ref val4)).normalized;
				if (!(Vector3.Angle(val2, normalized) > 50f))
				{
					EnemyHealth value = Traverse.Create((object)val3).Field("Health").GetValue<EnemyHealth>();
					if (!((Object)(object)value == (Object)null))
					{
						value.Hurt(1, normalized);
					}
				}
			}
		}

		private void EnsureVanillaGrabUsable()
		{
			if ((Object)(object)LocalAvatar == (Object)null)
			{
				return;
			}
			AccessTools.Field(typeof(PlayerAvatar), "isDisabled")?.SetValue(LocalAvatar, false);
			AccessTools.Field(typeof(PlayerAvatar), "deadSet")?.SetValue(LocalAvatar, false);
			SetLocalFlashlight(on: false);
			if ((Object)(object)LocalAvatar.playerHealth != (Object)null)
			{
				LocalAvatar.playerHealth.InvincibleSet(1000f);
			}
			EnemyDirector instance = EnemyDirector.instance;
			string text = (string)AccessTools.Field(typeof(PlayerAvatar), "steamID")?.GetValue(LocalAvatar);
			if ((Object)(object)instance != (Object)null && text != null)
			{
				List<string> list = (List<string>)AccessTools.Field(typeof(EnemyDirector), "debugNoVision").GetValue(instance);
				if (list != null && !list.Contains(text))
				{
					list.Add(text);
					noVisionSteamId = text;
				}
			}
			GameObject val = (((Object)(object)Aim.instance != (Object)null) ? ((Component)Aim.instance).gameObject : null);
			if ((Object)(object)val != (Object)null && !val.activeInHierarchy)
			{
				Transform val2 = val.transform;
				List<string> list2 = new List<string>();
				while ((Object)(object)val2 != (Object)null && !((Component)val2).gameObject.activeSelf)
				{
					((Component)val2).gameObject.SetActive(true);
					list2.Add(((Object)((Component)val2).gameObject).name);
					val2 = val2.parent;
				}
				Plugin.Log("Aim UI re-activated: " + ((Object)val).name + " ancestors=[" + string.Join(", ", list2) + "]");
			}
			if (!((Component)LocalAvatar).gameObject.activeSelf)
			{
				((Component)LocalAvatar).gameObject.SetActive(true);
				Plugin.Log("Local avatar GO re-asserted active");
			}
			PhysGrabber physGrabber = LocalAvatar.physGrabber;
			if ((Object)(object)physGrabber != (Object)null)
			{
				if (!savedGrabRange.HasValue)
				{
					savedGrabRange = physGrabber.grabRange;
				}
				physGrabber.grabRange = savedGrabRange.Value + 2.42f;
			}
		}

		private void ReleaseVanillaGrab()
		{
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			PhysGrabber val = (((Object)(object)LocalAvatar != (Object)null) ? LocalAvatar.physGrabber : null);
			if ((Object)(object)val != (Object)null && val.grabbed)
			{
				val.ReleaseObject(-1, 0.1f);
				Plugin.Log("Vanilla grab released on end possession");
			}
			if ((Object)(object)val != (Object)null && savedGrabRange.HasValue)
			{
				val.grabRange = savedGrabRange.Value;
				savedGrabRange = null;
			}
			if ((Object)(object)LocalAvatar != (Object)null && !avatarWasActive)
			{
				((Component)LocalAvatar).gameObject.SetActive(false);
				Plugin.Log("Local avatar GO de-activated again (vanilla death state)");
			}
			avatarWasActive = false;
			if ((Object)(object)LocalAvatar != (Object)null && (Object)(object)LocalAvatar.playerHealth != (Object)null)
			{
				LocalAvatar.playerHealth.InvincibleSet(0.1f);
			}
			if (noVisionSteamId != null)
			{
				EnemyDirector instance = EnemyDirector.instance;
				(((Object)(object)instance != (Object)null) ? ((List<string>)AccessTools.Field(typeof(EnemyDirector), "debugNoVision").GetValue(instance)) : null)?.Remove(noVisionSteamId);
				noVisionSteamId = null;
			}
			if (!possessionStartedDead || !((Object)(object)LocalAvatar != (Object)null))
			{
				return;
			}
			AccessTools.Field(typeof(PlayerAvatar), "isDisabled")?.SetValue(LocalAvatar, true);
			AccessTools.Field(typeof(PlayerAvatar), "deadSet")?.SetValue(LocalAvatar, true);
			object? obj = AccessTools.Field(typeof(PlayerAvatar), "playerDeathHead")?.GetValue(LocalAvatar);
			Component val2 = (Component)((obj is Component) ? obj : null);
			if ((Object)(object)val2 != (Object)null)
			{
				TeleportPlayer(val2.transform.position + Vector3.up * 0.3f);
				object? obj2 = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
				PlayerController val3 = (PlayerController)((obj2 is PlayerController) ? obj2 : null);
				if ((Object)(object)val3 != (Object)null)
				{
					AccessTools.Field(typeof(PlayerController), "InputDisableTimer")?.SetValue(val3, 1f);
				}
				Plugin.Log("Body teleported back to death spot");
			}
			SpectateCamera instance2 = SpectateCamera.instance;
			if ((Object)(object)instance2 != (Object)null)
			{
				Traverse.Create((object)instance2).Method("UpdateState", new object[1] { (object)(State)2 }).GetValue();
				Plugin.Log("Spectate re-pinned to death head (vanilla dead state restored)");
			}
		}

		private void EnergyRefill()
		{
			object obj = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
			if (obj != null)
			{
				object value = AccessTools.Field(typeof(PlayerController), "EnergyStart")?.GetValue(obj);
				AccessTools.Field(typeof(PlayerController), "EnergyCurrent")?.SetValue(obj, value);
			}
		}

		private void EndPossession()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			ReleaseVanillaGrab();
			if (aliveReturnPlanned && !possessionStartedDead)
			{
				TeleportPlayer(aliveBodyReturnPos);
				aliveReturnPlanned = false;
				Plugin.Log("Debug alive release: body returned to pre-possession spot");
			}
			Player localPlayer = PhotonNetwork.LocalPlayer;
			PossessionNetworking.BroadcastPossession((localPlayer != null) ? localPlayer.ActorNumber : (-1), null);
			PossessionController.EndPossession();
			AttackWindowOpen = false;
			RestoreMainGameState();
			if ((Object)(object)possessedPuppet != (Object)null)
			{
				Object.Destroy((Object)(object)possessedPuppet);
				possessedPuppet = null;
			}
			if ((Object)(object)hiddenPlayerVisuals != (Object)null)
			{
				hiddenPlayerVisuals.SetActive(!LocalAvatarDead());
				hiddenPlayerVisuals = null;
			}
			RestoreLocalCamera();
			if ((Object)(object)possessionLight != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)possessionLight).gameObject);
				possessionLight = null;
			}
			RemovePossessionVision();
			if ((Object)(object)PossessedRoot != (Object)null)
			{
				((Component)PossessedRoot).gameObject.SetActive(true);
				Plugin.Log("Original enemy restored: " + ((Object)((Component)PossessedRoot).gameObject).name);
			}
			PossessedGnome = null;
			PossessedRoot = null;
			object obj = AccessTools.Field(typeof(PlayerController), "instance")?.GetValue(null);
			if (obj != null && AccessTools.Field(typeof(PlayerController), "DebugEnergy") != null)
			{
				AccessTools.Field(typeof(PlayerController), "DebugEnergy")?.SetValue(obj, debugEnergyWas);
			}
			if (!LocalAvatarDead())
			{
				SetLocalFlashlight(on: true);
			}
			else
			{
				SetLocalFlashlight(on: false);
			}
		}

		private void AddPossessionVisionLight()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_003d: 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)
			Transform val = PlayerBodyRoot();
			if (!((Object)(object)val == (Object)null))
			{
				GameObject val2 = new GameObject("PossessionVisionLight");
				val2.transform.SetParent(val, false);
				val2.transform.localPosition = new Vector3(0f, 2f, 0f);
				possessionLight = val2.AddComponent<Light>();
				possessionLight.type = (LightType)1;
				possessionLight.intensity = 0.25f;
				possessionLight.shadows = (LightShadows)0;
				possessionLight.color = new Color(0.35f, 0.4f, 0.75f, 1f);
			}
		}

		private void LateUpdate()
		{
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: 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_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Unknown result type (might be due to invalid IL or missing references)
			bool flag = PossessionController.IsPossessing && (Object)(object)PossessedRoot != (Object)null;
			if (!SpectateOwnedByMod && !flag)
			{
				return;
			}
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return;
			}
			Transform val = PlayerBodyRoot();
			if (flag && (Object)(object)val != (Object)null)
			{
				Quaternion rotation = val.rotation;
				float y = ((Quaternion)(ref rotation)).eulerAngles.y;
				Vector3 val2 = val.position + Vector3.up * 0.95f;
				Vector3 val3 = Quaternion.Euler(0f, y, 0f) * (Vector3.back * 2.2f + Vector3.up * 1f);
				Vector3 position = val2 + val3;
				RaycastHit val4 = default(RaycastHit);
				if (Physics.Raycast(val2, ((Vector3)(ref val3)).normalized, ref val4, ((Vector3)(ref val3)).magnitude, LayerMask.GetMask(new string[1] { "Default" }), (QueryTriggerInteraction)1))
				{
					position = ((RaycastHit)(ref val4)).point + ((RaycastHit)(ref val4)).normal * 0.2f;
				}
				((Component)main).transform.position = position;
				((Component)main).transform.rotation = Quaternion.Euler(lookPitch * 0.4f, y, 0f);
				if ((Object)(object)possessedPuppet != (Object)null)
				{
					possessedPuppet.transform.localRotation = Quaternion.identity;
				}
			}
			else if ((Object)(object)val != (Object)null)
			{
				lookYaw += Input.GetAxis("Mouse X");
				lookPitch = Mathf.Clamp(lookPitch - Input.GetAxis("Mouse Y"), -80f, 80f);
				((Component)main).transform.position = val.position + Vector3.up * 2.5f;
				((Component)main).transform.rotation = Quaternion.Euler(lookPitch, lookYaw, 0f);
			}
			main.nearClipPlane = 0.05f;
			main.farClipPlane = 500f;
			if (flag && (Object)(object)val != (Object)null)
			{
				if ((Object)(object)parkedLocalCamera != (Object)null)
				{
					((Component)parkedLocalCamera).transform.SetPositionAndRotation(((Component)main).transform.position, ((Component)main).transform.rotation);
				}
				PlayerVisionTarget playerVisionTarget = LocalAvatar.PlayerVisionTarget;
				if ((Object)(object)playerVisionTarget != (Object)null && (Object)(object)playerVisionTarget.VisionTransform != (Object)null)
				{
					playerVisionTarget.VisionTransform.SetPositionAndRotation(((Component)main).transform.position, ((Component)main).transform.rotation);
				}
			}
		}

		private void TryStartPossession()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_008d: 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)
			EnemyGnome val = null;
			float num = 20f;
			float y = ((Component)LocalAvatar).transform.position.y;
			bool flag = false;
			EnemyGnome[] array = Object.FindObjectsOfType<EnemyGnome>();
			foreach (EnemyGnome val2 in array)
			{
				if ((Object)(object)val2.enemy == (Object)null || PossessionController.IsPossessing || IsEnemyOwned((Component)(object)val2))
				{
					continue;
				}
				Vector3 position = RootOf(val2).position;
				if (position.y > y + 3f || position.y < y - 6f)
				{
					flag = true;
					continue;
				}
				float num2 = Vector3.Distance(((Component)val2).transform.position, ((Component)LocalAvatar).transform.position);
				if (num2 < num)
				{
					val = val2;
					num = num2;
				}
			}
			if (flag && (Object)(object)val == (Object)null)
			{
				Plugin.Log("Possession: gnomes found but drifting over/under the plane — spawning fresh");
			}
			if ((Object)(object)val == (Object)null)
			{
				TrySpawnGnome();
			}
			else
			{
				ActivatePossession(val);
			}
		}

		private void ParkLocalCamera()
		{
			PlayerLocalCamera val = (((Object)(object)LocalAvatar != (Object)null) ? LocalAvatar.localCamera : null);
			if ((Object)(object)val != (Object)null)
			{
				((Behaviour)val).enabled = false;
				parkedLocalCamera = (Behaviour)(object)val;
				Plugin.Log("Vanilla local camera parked");
			}
			else
			{
				Plugin.Log("Local camera park skipped (no localCamera)");
			}
		}

		private void RestoreLocalCamera()
		{
			if ((Object)(object)parkedLocalCamera != (Object)null)
			{
				parkedLocalCamera.enabled = true;
				parkedLocalCamera = null;
				Plugin.Log("Vanilla local camera restored");
			}
		}

		private void SetLocalFlashlight(bool on)
		{
			FlashlightController val = LocalAvatar?.flashlightController;
			if (!((Object)(object)val == (Object)null))
			{
				if (!on)
				{
					FieldInfo fieldInfo = AccessTools.Field(typeof(FlashlightController), "active");
					flashlightWasOn = fieldInfo != null && (bool)fieldInfo.GetValue(val);
					AccessTools.Method(typeof(FlashlightController), "LightOff", (Type[])null, (Type[])null)?.Invoke(val, null);
				}
				else if (flashlightWasOn)
				{
					AccessTools.Method(typeof(FlashlightController), "LightOn", (Type[])null, (Type[])null)?.Invoke(val, null);
					flashlightWasOn = false;
				}
			}
		}

		private void TrySpawnGnome()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (PhotonNetwork.CurrentRoom == null || PhotonNetwork.IsMasterClient || PhotonNetwork.CurrentRoom.PlayerCount <= 1)
			{
				Plugin.Log("No gnome nearby — spawning one (host)");
				EnemySetup val = FindGnomeSetup();
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log("Gnome EnemySetup not found");
					return;
				}
				LevelPoint val2 = SemiFunc.LevelPointGet(((Component)LocalAvatar).transform.position, 15f, 40f) ?? SemiFunc.LevelPointGetFurthestFromPlayer(((Component)LocalAvatar).transform.position, 20f) ?? SemiFunc.LevelPointGetFurthestFromPlayer(((Component)LocalAvatar).transform.position, 5f);
				Vector3 val3 = ((Component)LocalAvatar).transform.position + Quaternion.Euler(0f, Random.Range(0f, 360f), 0f) * Vector3.forward * 3f;
				if ((Object)(object)val2 != (Object)null)
				{
					val3 = ((Component)val2).transform.position;
					Plugin.Log($"Spawn point picked: {val3} ({((Object)val2).name})");
				}
				val3 += Vector3.up * 1.5f;
				List<EnemyParent> list = Enemies.SpawnEnemy(val, val3, Quaternion.identity, false);
				if (list != null)
				{
					Plugin.Log($"SpawnEnemy returned {list.Count} parents");
					foreach (EnemyParent item in list)
					{
						EnemyGnome componentInChildren = ((Component)item).gameObject.GetComponentInChildren<EnemyGnome>();
						if ((Object)(object)PossessedGnome == (Object)null && (Object)(object)componentInChildren != (Object)null)
						{
							PossessedGnome = componentInChildren;
							Plugin.Log("Spawn: possessing " + ((Object)componentInChildren).name + " under parent " + ((Object)item).name);
						}
						else
						{
							Plugin.Log("Spawn: removing extra " + (((componentInChildren != null) ? ((Object)componentInChildren).name : null) ?? "(no gnome)") + " under parent " + ((Object)item).name);
							RemoveExtra(item);
						}
					}
				}
				if ((Object)(object)PossessedGnome != (Object)null)
				{
					ActivatePossession(PossessedGnome);
				}
				else
				{
					Plugin.Log("Spawn succeeded but no EnemyGnome component found");
				}
			}
			else if (Time.time - lastTimeNotifiedMissing > 3f)
			{
				lastTimeNotifiedMissing = Time.time;
				Plugin.Log("No gnome nearby and you are not the host — ask the host");
			}
		}

		private static void RemoveExtra(EnemyParent parent)
		{
			if (!((Object)(object)parent == (Object)null))
			{
				PhotonView value = Traverse.Create((object)parent).Field("photonView").GetValue<PhotonView>();
				if ((Object)(object)value != (Object)null && PhotonNetwork.CurrentRoom != null && PhotonNetwork.IsMasterClient)
				{
					PhotonNetwork.Destroy(value);
				}
				else
				{
					Object.Destroy((Object)(object)((Component)parent).gameObject);
				}
			}
		}

		private static EnemySetup FindGnomeSetup()
		{
			foreach (EnemySetup allEnemy in Enemies.AllEnemies)
			{
				if (((Object)allEnemy).name != null && ((Object)allEnemy).name.ToLower().Contains("gnome"))
				{
					return allEnemy;
				}
			}
			return null;
		}

		private void EnsurePossessionVision()
		{
			Camera main = Camera.main;
			if (!((Object)(object)main == (Object)null))
			{
				PossessionVision possessionVision = ((Component)main).GetComponent<PossessionVision>();
				if ((Object)(object)possessionVision == (Object)null)
				{
					possessionVision = ((Component)main).gameObject.AddComponent<PossessionVision>();
				}
				possessionVision.pixelation = HostConfig.SenseBlurIntensity;
			}
		}

		private static void RemovePossessionVision()
		{
			Camera main = Camera.main;
			if (!((Object)(object)main == (Object)null))
			{
				PossessionVision component = ((Component)main).GetComponent<PossessionVision>();
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
			}
		}

		private void OnGUI()
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0035: 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_00b9: 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_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			if (ResolveLocalAvatar() && InLevel() && CanPossess(LocalAvatarDead()))
			{
				GUIStyle val = new GUIStyle(GUI.skin.label)
				{
					fontSize = 22
				};
				GUI.color = Color.white;
				if (PossessionController.IsPossessing)
				{
					GUI.Label(new Rect(20f, (float)(Screen.height - 60), 700f, 30f), "[F] End possession  ·  [RMB] Attack  ·  [LMB] Grab / [Wheel] push-pull", val);
				}
				else if (PossessionController.IsEligible())
				{
					GUI.Label(new Rect(20f, (float)(Screen.height - 60), 700f, 30f), "[F] Possess a nearby gnome", val);
				}
				else
				{
					GUI.Label(new Rect(20f, (float)(Screen.height - 60), 700f, 30f), "Possession on cooldown", val);
				}
				if (PossessionController.IsPossessing && HostConfig.SenseBlurIntensity > 0f)
				{
					GUI.color = new Color(0f, 0f, 0f, HostConfig.SenseBlurIntensity);
					GUI.DrawTexture(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), (Texture)(object)Texture2D.whiteTexture);
					GUI.color = Color.white;
				}
			}
		}
	}
	public static class PossessionController
	{
		private static readonly Stopwatch cooldownStopwatch = new Stopwatch();

		private static float possessionEndTime;

		public static bool IsPossessing { get; private set; }

		public static bool IsEligible()
		{
			if (IsPossessing)
			{
				return false;
			}
			if (ConfigSettings.DebugPossessWhileAlive.Value || ConfigSettings.DebugKillOnLevelStart.Value)
			{
				return true;
			}
			if (cooldownStopwatch.IsRunning)
			{
				return cooldownStopwatch.Elapsed.TotalSeconds >= (double)HostConfig.PossessionCooldown;
			}
			return true;
		}

		public static void StartPossession()
		{
			IsPossessing = true;
			bool flag = ConfigSettings.DebugPossessWhileAlive.Value || ConfigSettings.DebugKillOnLevelStart.Value;
			possessionEndTime = (flag ? float.MaxValue : (Time.time + HostConfig.MaxPossessionDuration));
			Plugin.Log("Possession started" + (flag ? " (debug: no time limit)" : ""));
		}

		public static void EndPossession()
		{
			IsPossessing = false;
			cooldownStopwatch.Restart();
			Plugin.Log("Possession ended");
		}

		public static void Tick()
		{
			if (IsPossessing && Time.time >= possessionEndTime)
			{
				EndPossession();
			}
		}
	}
	public class PossessionNetworking : MonoBehaviour, IOnEventCallback
	{
		private class PossessedPlayer
		{
			public int ActorNumber;

			public int GnomeViewId;

			public Vector3 Position;

			public bool Active;

			public bool RenderersHidden;

			public bool Stripped;

			public Transform Root;

			public readonly List<Renderer> HiddenRenderers = new List<Renderer>();
		}

		private const byte PossessionStateEvent = 71;

		private const byte PossessionMoveEvent = 72;

		private const byte PossessionConfigEvent = 73;

		private const float CacheRefreshInterval = 2f;

		private const float ConfigBroadcastInterval = 10f;

		private static float nextConfigBroadcast;

		private readonly Dictionary<int, PossessedPlayer> possessed = new Dictionary<int, PossessedPlayer>();

		private readonly Dictionary<int, EnemyGnome> gnomeByViewId = new Dictionary<int, EnemyGnome>();

		private readonly Dictionary<int, PlayerVoiceChat> voiceByActor = new Dictionary<int, PlayerVoiceChat>();

		private float nextCacheRefresh;

		private bool registered;

		public static PossessionNetworking Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		private void OnDisable()
		{
			Unregister();
			foreach (PossessedPlayer value in possessed.Values)
			{
				HideActorMeshes(value, hide: false);
			}
			possessed.Clear();
		}

		private static bool GameReady()
		{
			if ((Object)(object)RunManager.instance != (Object)null && (Object)(object)GameDirector.instance != (Object)null)
			{
				return SemiFunc.RunIsLevel();
			}
			return false;
		}

		private void EnsureRegistered()
		{
			if (!registered && PhotonNetwork.InRoom)
			{
				PhotonNetwork.AddCallbackTarget((object)this);
				registered = true;
			}
		}

		private void Unregister()
		{
			if (registered)
			{
				PhotonNetwork.RemoveCallbackTarget((object)this);
				registered = false;
			}
		}

		private void Update()
		{
			if (!GameReady())
			{
				Unregister();
				return;
			}
			EnsureRegistered();
			if (Time.time < nextCacheRefresh)
			{
				return;
			}
			nextCacheRefresh = Time.time + 2f;
			CacheVoiceChats();
			CacheGnomes();
			BroadcastHostConfigIfNeeded();
			foreach (PossessedPlayer value in possessed.Values)
			{
				if (value.Active && !value.Stripped)
				{
					resolveRoot(value);
					TryStrip(value);
				}
				if (value.Active && !value.RenderersHidden)
				{
					HideActorMeshes(value, hide: true);
				}
			}
		}

		private void TryStrip(PossessedPlayer entry)
		{
			if (entry.Stripped || entry.GnomeViewId <= 0 || (Object)(object)entry.Root == (Object)null)
			{
				return;
			}
			gnomeByViewId.TryGetValue(entry.GnomeViewId, out var value);
			if (!((Object)(object)value == (Object)null))
			{
				int actorNumber = entry.ActorNumber;
				Player localPlayer = PhotonNetwork.LocalPlayer;
				if (actorNumber == ((localPlayer != null) ? new int?(localPlayer.ActorNumber) : ((int?)null)))
				{
					entry.Stripped = true;
					return;
				}
				PossessionBehavior.StripComponents(value, entry.Root);
				entry.Stripped = true;
				Plugin.Log($"Networked strip applied (actor {entry.ActorNumber})");
			}
		}

		private void LateUpdate()
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			if (possessed.Count == 0)
			{
				return;
			}
			int num = LocalActor();
			foreach (PossessedPlayer value2 in possessed.Values)
			{
				if (value2.Active)
				{
					if (value2.ActorNumber != num && (Object)(object)resolveRoot(value2) != (Object)null)
					{
						resolveRoot(value2).position = value2.Position;
					}
					if (value2.ActorNumber != num && voiceByActor.TryGetValue(value2.ActorNumber, out var value) && (Object)(object)value != (Object)null)
					{
						value.OverridePosition(value2.Position, 1.5f);
					}
				}
			}
		}

		public static PhotonView ViewIdOf(EnemyGnome gnome)
		{
			if (!((Object)(object)gnome == (Object)null))
			{
				return Traverse.Create((object)gnome).Field("photonView").GetValue<PhotonView>();
			}
			return null;
		}

		private static PhotonView VoiceView(PlayerVoiceChat voice)
		{
			if (!((Object)(object)voice == (Object)null))
			{
				return Traverse.Create((object)voice).Field("photonView").GetValue<PhotonView>();
			}
			return null;
		}

		private static PlayerAvatar VoiceAvatar(PlayerVoiceChat voice)
		{
			if (!((Object)(object)voice == (Object)null))
			{
				return Traverse.Create((object)voice).Field("playerAvatar").GetValue<PlayerAvatar>();
			}
			return null;
		}

		public void OnEvent(EventData eventData)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (eventData.Code == 71 && eventData.CustomData is int[] array && array.Length >= 2)
			{
				HandleState(array[0], array[1]);
			}
			else if (eventData.Code == 72 && eventData.CustomData is float[] array2 && array2.Length >= 4)
			{
				ApplyMove((int)array2[0], new Vector3(array2[1], array2[2], array2[3]));
			}
			else if (eventData.Code == 73 && eventData.CustomData is float[] array3 && array3.Length == 3)
			{
				HostConfig.Accept(array3);
			}
		}

		public static void BroadcastPossession(int actorNumber, EnemyGnome gnome)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			if (ShouldNetwork())
			{
				int[] array = new int[2]
				{
					actorNumber,
					((Object)(object)gnome != (Object)null && (Object)(object)ViewIdOf(gnome) != (Object)null) ? ViewIdOf(gnome).ViewID : 0
				};
				PhotonNetwork.RaiseEvent((byte)71, (object)array, new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)1
				}, SendOptions.SendReliable);
			}
		}

		public static void BroadcastMove(int viewId, Vector3 position)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_0037: 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: Expected O, but got Unknown
			if (ShouldNetwork())
			{
				float[] array = new float[4] { viewId, position.x, position.y, position.z };
				PhotonNetwork.RaiseEvent((byte)72, (object)array, new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				}, SendOptions.SendUnreliable);
			}
		}

		public static bool IsGnomePossessed(EnemyGnome gnome)
		{
			if ((Object)(object)Instance == (Object)null || (Object)(object)gnome == (Object)null || Instance.possessed.Count == 0)
			{
				return false;
			}
			PhotonView val = ViewIdOf(gnome);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			int viewID = val.ViewID;
			foreach (PossessedPlayer value in Instance.possessed.Values)
			{
				if (value.Active && value.GnomeViewId == viewID)
				{
					return true;
				}
			}
			return false;
		}

		private Transform resolveRoot(PossessedPlayer entry)
		{
			if ((Object)(object)entry.Root == (Object)null)
			{
				gnomeByViewId.TryGetValue(entry.GnomeViewId, out var value);
				entry.Root = (((Object)(object)value != (Object)null) ? PossessionBehavior.RootOf(value) : null);
			}
			return entry.Root;
		}

		public static bool IsRootPossessed(Transform root)
		{
			if ((Object)(object)Instance == (Object)null || (Object)(object)root == (Object)null || Instance.possessed.Count == 0)
			{
				return false;
			}
			foreach (PossessedPlayer value in Instance.possessed.Values)
			{
				if (value.Active && (Object)(object)value.Root == (Object)(object)root)
				{
					return true;
				}
			}
			return false;
		}

		private static bool ShouldNetwork()
		{
			Room currentRoom = PhotonNetwork.CurrentRoom;
			if (currentRoom != null)
			{
				return currentRoom.PlayerCount > 1;
			}
			return false;
		}

		private static void BroadcastHostConfigIfNeeded()
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_0072: Expected O, but got Unknown
			if (ShouldNetwork() && PhotonNetwork.IsMasterClient && !(Time.time < nextConfigBroadcast))
			{
				nextConfigBroadcast = Time.time + 10f;
				float[] array = new float[3]
				{
					ConfigSettings.MaxPossessionDuration.Value,
					ConfigSettings.PossessionCooldown.Value,
					ConfigSettings.SenseBlurIntensity.Value
				};
				PhotonNetwork.RaiseEvent((byte)73, (object)array, new RaiseEventOptions
				{
					Receivers = (ReceiverGroup)0
				}, SendOptions.SendReliable);
			}
		}

		private static int LocalActor()
		{
			Player localPlayer = PhotonNetwork.LocalPlayer;
			if (localPlayer == null)
			{
				return -1;
			}
			return localPlayer.ActorNumber;
		}

		private void HandleState(int actorNumber, int viewId)
		{
			PossessedPlayer value3;
			if (viewId > 0)
			{
				if (!possessed.TryGetValue(actorNumber, out var value))
				{
					value = new PossessedPlayer
					{
						ActorNumber = actorNumber
					};
					possessed[actorNumber] = value;
				}
				value.GnomeViewId = viewId;
				value.Active = true;
				gnomeByViewId.TryGetValue(viewId, out var value2);
				value.Root = (((Object)(object)value2 != (Object)null) ? PossessionBehavior.RootOf(value2) : null);
				TryStrip(value);
				HideActorMeshes(value, hide: true);
			}
			else if (possessed.TryGetValue(actorNumber, out value3))
			{
				value3.Active = false;
				value3.Root = null;
				HideActorMeshes(value3, hide: false);
			}
		}

		private void ApplyMove(int viewId, Vector3 position)
		{
			//IL_002d: 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)
			foreach (PossessedPlayer value in possessed.Values)
			{
				if (value.Active && value.GnomeViewId == viewId)
				{
					value.Position = position;
					break;
				}
			}
		}

		private void CacheVoiceChats()
		{
			voiceByActor.Clear();
			PlayerVoiceChat[] array = Object.FindObjectsOfType<PlayerVoiceChat>();
			foreach (PlayerVoiceChat val in array)
			{
				PhotonView val2 = VoiceView(val);
				if (!((Object)(object)val2 == (Object)null))
				{
					int num = ((val2.Owner != null) ? val2.Owner.ActorNumber : (-1));
					if (num > 0 && !voiceByActor.ContainsKey(num))
					{
						voiceByActor[num] = val;
					}
				}
			}
		}

		private void CacheGnomes()
		{
			gnomeByViewId.Clear();
			EnemyGnome[] array = Object.FindObjectsOfType<EnemyGnome>();
			foreach (EnemyGnome val in array)
			{
				PhotonView val2 = ViewIdOf(val);
				if (!((Object)(object)val2 == (Object)null))
				{
					gnomeByViewId[val2.ViewID] = val;
				}
			}
		}

		private void HideActorMeshes(PossessedPlayer entry, bool hide)
		{
			voiceByActor.TryGetValue(entry.ActorNumber, out var value);
			PlayerAvatar val = VoiceAvatar(value);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			if (hide)
			{
				entry.HiddenRenderers.Clear();
				Renderer[] componentsInChildren = ((Component)val).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val2 in componentsInChildren)
				{
					if (!((Object)(object)val2 == (Object)null) && val2.enabled)
					{
						entry.HiddenRenderers.Add(val2);
						val2.enabled = false;
					}
				}
				entry.RenderersHidden = true;
			}
			else
			{
				if (!entry.RenderersHidden)
				{
					return;
				}
				foreach (Renderer hiddenRenderer in entry.HiddenRenderers)
				{
					if ((Object)(object)hiddenRenderer != (Object)null)
					{
						hiddenRenderer.enabled = true;
					}
				}
				entry.HiddenRenderers.Clear();
				entry.RenderersHidden = false;
			}
		}
	}
	public class PossessionVision : MonoBehaviour
	{
		public float pixelation;

		private void OnRenderImage(RenderTexture source, RenderTexture destination)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			if (pixelation <= 0f)
			{
				Graphics.Blit((Texture)(object)source, destination);
				return;
			}
			int num = Mathf.Max(1, (int)(2f + pixelation * 18f));
			RenderTexture temporary = RenderTexture.GetTemporary(Mathf.Max(16, ((Texture)source).width / num), Mathf.Max(16, ((Texture)source).height / num), 0, source.format);
			((Texture)temporary).filterMode = (FilterMode)0;
			Graphics.Blit((Texture)(object)source, temporary);
			Graphics.Blit((Texture)(object)temporary, destination);
			RenderTexture.ReleaseTemporary(temporary);
		}
	}
}
namespace DeadPossession.Patches
{
	[HarmonyPatch(typeof(Enemy), "Update")]
	[HarmonyPatch(typeof(EnemyStateRoaming), "Update")]
	[HarmonyPatch(typeof(EnemyStateInvestigate), "Update")]
	[HarmonyPatch(typeof(EnemyStateChase), "Update")]
	[HarmonyPatch(typeof(EnemyStateChaseBegin), "Update")]
	[HarmonyPatch(typeof(EnemyStateChaseEnd), "Update")]
	[HarmonyPatch(typeof(EnemyStateChaseSlow), "Update")]
	[HarmonyPatch(typeof(EnemyStateLookUnder), "Update")]
	[HarmonyPatch(typeof(EnemyStateSneak), "Update")]
	[HarmonyPatch(typeof(EnemyStateSpawn), "Update")]
	[HarmonyPatch(typeof(EnemyStateStunned), "Update")]
	[HarmonyPatch(typeof(EnemyStateDespawn), "Update")]
	[HarmonyPatch(typeof(EnemyNavMeshAgent), "Update")]
	[HarmonyPatch(typeof(EnemyRigidbody), "Update")]
	[HarmonyPatch(typeof(EnemyRigidbody), "FixedUpdate")]
	[HarmonyPatch(typeof(EnemyParent), "Update")]
	[HarmonyPatch(typeof(EnemyGrounded), "Update")]
	[HarmonyPatch(typeof(EnemyGnomeAnim), "Update")]
	[HarmonyPatch(typeof(PhysGrabObject), "Update")]
	[HarmonyPatch(typeof(PhysGrabObject), "FixedUpdate")]
	public static class EnemyAiPatch
	{
		[HarmonyPrefix]
		public static bool SkipWhenPossessed(object __instance)
		{
			Component val = (Component)((__instance is Component) ? __instance : null);
			if (!((Object)(object)val == (Object)null))
			{
				return !PossessionBehavior.IsEnemyOwned(val);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(EnemyGnome), "Update")]
	[HarmonyPatch(typeof(EnemyGnome), "FixedUpdate")]
	public static class EnemyGnomeUpdatePatch
	{
		[HarmonyPrefix]
		public static bool SkipWhilePossessed(object __instance)
		{
			EnemyGnome val = (EnemyGnome)((__instance is EnemyGnome) ? __instance : null);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			if ((Object)(object)val == (Object)(object)PossessionBehavior.PossessedGnome)
			{
				return PossessionBehavior.AttackWindowOpen;
			}
			return !PossessionBehavior.IsEnemyOwned((Component)(object)val);
		}
	}
	[HarmonyPatch]
	public static class PlayerAvatarPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(PlayerAvatar), "Revive", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(PlayerAvatar), "ReviveRPC", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(PlayerTumble), "TumbleRequest", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(PlayerTumble), "TumbleRequestRPC", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(PlayerTumble), "TumbleSet", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(PlayerTumble), "TumbleSetRPC", (Type[])null, (Type[])null);
		}

		private static bool Prefix(MethodBase __originalMethod)
		{
			if (PossessionController.IsPossessing)
			{
				Plugin.Log("Blocked vanilla body call while possessing: " + __originalMethod.Name);
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(SpectateCamera), "Update")]
	[HarmonyPatch(typeof(SpectateCamera), "LateUpdate")]
	public static class SpectateCameraPatch
	{
		private static bool Prefix()
		{
			return !PossessionBehavior.SpectateOwnedByMod;
		}
	}
	[HarmonyPatch(typeof(SpectateHeadUI), "Update")]
	public static class SpectateHeadUIPatch
	{
		private static bool Prefix()
		{
			if (!ConfigSettings.DebugKillOnLevelStart.Value)
			{
				return !ConfigSettings.DebugPossessWhileAlive.Value;
			}
			return false;
		}
	}
}