Decompiled source of I am a BigGhost v0.1.0

plugins/Thanks.I_am_a_BigGhost.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Peak;
using Photon.Pun;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Thanks.I_am_a_BigGhost")]
[assembly: AssemblyDescription("Client-side controllable big-ghost form for PEAK. Press B in any scene to become the big ghost that rises from the misty swamp.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Thanks")]
[assembly: AssemblyProduct("I'm a Ghost")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("3a7b9c51-4d2e-4f6a-8b10-2c3d4e5f6a7b")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.1.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 ImGhost
{
	[BepInPlugin("com.github.Thanks.ImGhost", "I'm a Ghost", "0.1.0")]
	public sealed class GhostPlugin : BaseUnityPlugin
	{
		public const string Id = "com.github.Thanks.ImGhost";

		public const string Name = "I'm a Ghost";

		public const string Version = "0.1.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<KeyCode> ToggleKey;

		internal static ConfigEntry<KeyCode> AttackKey;

		internal static ConfigEntry<KeyCode> UpKey;

		internal static ConfigEntry<KeyCode> DownKey;

		internal static ConfigEntry<KeyCode> SprintKey;

		internal static ConfigEntry<float> MovementSpeed;

		internal static ConfigEntry<float> VerticalSpeed;

		internal static ConfigEntry<float> SprintMultiplier;

		internal static ConfigEntry<float> AttackChargeSeconds;

		internal static ConfigEntry<float> AttackRevertSeconds;

		internal static ConfigEntry<float> AttackRadius;

		internal static ConfigEntry<float> KnockbackForce;

		internal static ConfigEntry<float> CameraDistance;

		internal static ConfigEntry<float> CameraHeight;

		internal static ConfigEntry<float> CameraFov;

		internal static ConfigEntry<bool> ShowGhostVisual;

		private readonly Harmony _harmony = new Harmony("com.github.Thanks.ImGhost");

		private GhostController _controller;

		private bool _lastTogglePressed;

		private void Awake()
		{
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Expected O, but got Unknown
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected O, but got Unknown
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Expected O, but got Unknown
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Expected O, but got Unknown
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Expected O, but got Unknown
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Expected O, but got Unknown
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Expected O, but got Unknown
			//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Expected O, but got Unknown
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ff: Expected O, but got Unknown
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony.PatchAll(typeof(GhostHarmonyPatches).Assembly);
			ConfigureOptionalCharacterPatches();
			ConfigureCameraFallbackPatch();
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "ToggleKey", (KeyCode)98, "Press to become the big ghost. Press again to return to normal.");
			AttackKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "AttackKey", (KeyCode)324, "Right-click while in ghost form to trigger the ghost explosion attack. The player reverts after the attack.");
			UpKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "UpKey", (KeyCode)32, "Hold Space to fly up while in ghost form.");
			DownKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "DownKey", (KeyCode)306, "Hold Ctrl to fly down while in ghost form.");
			SprintKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Controls", "SprintKey", (KeyCode)304, "Hold Shift to sprint (move faster) while in ghost form.");
			MovementSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Ghost", "MovementSpeed", 14f, new ConfigDescription("How fast the ghost glides with WASD.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 40f), Array.Empty<object>()));
			VerticalSpeed = ((BaseUnityPlugin)this).Config.Bind<float>("Ghost", "VerticalSpeed", 12f, new ConfigDescription("How fast the ghost rises and sinks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 30f), Array.Empty<object>()));
			SprintMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Ghost", "SprintMultiplier", 2f, new ConfigDescription("Speed multiplier while holding the sprint key.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 5f), Array.Empty<object>()));
			AttackChargeSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Ghost", "AttackChargeSeconds", 0.8f, new ConfigDescription("How long the ghost glows before exploding after the attack is triggered.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			AttackRevertSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Ghost", "AttackRevertSeconds", 1f, new ConfigDescription("Seconds after the explosion until the player automatically reverts to normal.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 6f), Array.Empty<object>()));
			AttackRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Ghost", "AttackRadius", 10f, new ConfigDescription("Radius in which nearby players are knocked down by the ghost explosion.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), Array.Empty<object>()));
			KnockbackForce = ((BaseUnityPlugin)this).Config.Bind<float>("Ghost", "KnockbackForce", 15f, new ConfigDescription("How strongly nearby players are launched by the ghost explosion.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 40f), Array.Empty<object>()));
			CameraDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "Distance", 16f, new ConfigDescription("Third-person ghost camera distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(8f, 30f), Array.Empty<object>()));
			CameraHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "Height", 5f, new ConfigDescription("Third-person ghost camera height above the player.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 14f), Array.Empty<object>()));
			CameraFov = ((BaseUnityPlugin)this).Config.Bind<float>("Camera", "Fov", 80f, new ConfigDescription("Field of view while in ghost form.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 110f), Array.Empty<object>()));
			ShowGhostVisual = ((BaseUnityPlugin)this).Config.Bind<bool>("Ghost", "ShowGhostVisual", true, "Show the big ghost visual while transformed.");
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[I'm a Ghost] Loaded version 0.1.0.");
		}

		private void ConfigureOptionalCharacterPatches()
		{
			TryPatchOptionalCharacterMethod("RPCA_Die", Type.EmptyTypes, "CharacterRpcDiePrefix", "Character.RPCA_Die()");
			TryPatchOptionalCharacterMethod("RPCA_SetDead", Type.EmptyTypes, "CharacterRpcSetDeadPrefix", "Character.RPCA_SetDead()");
			TryPatchOptionalCharacterMethod("RPCA_PassOut", Type.EmptyTypes, "CharacterRpcPassOutPrefix", "Character.RPCA_PassOut()");
			TryPatchOptionalCharacterMethod("DieInstantly", Type.EmptyTypes, "CharacterDieInstantlyPrefix", "Character.DieInstantly()");
			TryPatchOptionalCharacterMethod("HandleDeath", Type.EmptyTypes, "CharacterHandleDeathPrefix", "Character.HandleDeath()");
			TryPatchOptionalCharacterMethod("WarpPlayerRPC", new Type[2]
			{
				typeof(Vector3),
				typeof(bool)
			}, "CharacterWarpPlayerRpcPrefix", "Character.WarpPlayerRPC(Vector3,bool)");
			TryPatchOptionalCharacterMethod("WarpPlayer", new Type[2]
			{
				typeof(Vector3),
				typeof(bool)
			}, "CharacterWarpPlayerPrefix", "Character.WarpPlayer(Vector3,bool)");
			TryPatchOptionalCharacterMethod("Fall", new Type[2]
			{
				typeof(float),
				typeof(float)
			}, "CharacterFallPrefix", "Character.Fall(float,float)");
			TryPatchOptionalCharacterMethod("RPCA_Fall", new Type[2]
			{
				typeof(float),
				typeof(float)
			}, "CharacterRpcFallPrefix", "Character.RPCA_Fall(float,float)");
		}

		private void TryPatchOptionalCharacterMethod(string methodName, Type[] parameterTypes, string prefixName, string description)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Character), methodName, parameterTypes, (Type[])null);
				if (methodInfo == null)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("[I'm a Ghost] Optional " + description + " patch skipped; method is not present in this game build."));
					return;
				}
				MethodInfo methodInfo2 = AccessTools.Method(typeof(GhostHarmonyPatches), prefixName, (Type[])null, (Type[])null);
				if (methodInfo2 == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("[I'm a Ghost] Optional " + description + " patch skipped; prefix method was not found."));
				}
				else
				{
					_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[I'm a Ghost] Optional " + description + " patch failed: " + ex.Message));
			}
		}

		private void ConfigureCameraFallbackPatch()
		{
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName("MainCameraMovement");
				if (type == null)
				{
					foreach (Type item in AccessTools.AllTypes())
					{
						if (item.FullName == "MainCameraMovement")
						{
							type = item;
							break;
						}
					}
				}
				if (type == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"[I'm a Ghost] Camera fallback patch skipped; MainCameraMovement type not found.");
					return;
				}
				MethodInfo methodInfo = AccessTools.Method(type, "LateUpdate", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"[I'm a Ghost] Camera fallback patch skipped; MainCameraMovement.LateUpdate not found.");
					return;
				}
				MethodInfo methodInfo2 = AccessTools.Method(typeof(GhostHarmonyPatches), "MainCameraMovementLateUpdatePostfix", (Type[])null, (Type[])null);
				if (methodInfo2 == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"[I'm a Ghost] Camera fallback patch skipped; postfix method not found.");
					return;
				}
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				GhostController.CameraOverridePatchActive = true;
				((BaseUnityPlugin)this).Logger.LogInfo((object)"[I'm a Ghost] Camera Harmony fallback active (MainCameraMovement.LateUpdate postfix).");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("[I'm a Ghost] Camera fallback patch failed: " + ex.Message));
			}
		}

		private void Update()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((int)ToggleKey.Value != 0)
				{
					bool key = Input.GetKey(ToggleKey.Value);
					if (key && !_lastTogglePressed)
					{
						ToggleGhostForm();
					}
					_lastTogglePressed = key;
					if ((Object)(object)_controller != (Object)null && _controller.Active && !_controller.IsValid())
					{
						ForceExit();
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogError((object)("[I'm a Ghost] Plugin.Update: " + ex));
				}
			}
		}

		private void OnDestroy()
		{
			try
			{
				ForceExit();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("[I'm a Ghost] Exit during cleanup failed: " + ex.Message));
				}
			}
			try
			{
				_harmony.UnpatchSelf();
			}
			catch (Exception ex2)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[I'm a Ghost] Harmony unpatch failed (non-fatal): " + ex2.Message));
				}
			}
		}

		private void ToggleGhostForm()
		{
			if ((Object)(object)_controller != (Object)null && _controller.Active)
			{
				ExitGhost();
				return;
			}
			Character localCharacter = Character.localCharacter;
			if (CanTransform(localCharacter))
			{
				EnterGhost(localCharacter);
			}
		}

		private static bool CanTransform(Character character)
		{
			if ((Object)(object)character == (Object)null)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)"[I'm a Ghost] No local character found.");
				}
				return false;
			}
			if (GhostController.IsLocalGhostCharacter(character))
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)"[I'm a Ghost] Already in ghost form.");
				}
				return false;
			}
			if (character.isZombie || character.isBot || character.isScoutmaster)
			{
				ManualLogSource log3 = Log;
				if (log3 != null)
				{
					log3.LogWarning((object)"[I'm a Ghost] Cannot transform while in another special form.");
				}
				return false;
			}
			if ((Object)(object)character.data == (Object)null || character.refs == null || (Object)(object)((MonoBehaviourPun)character).photonView == (Object)null)
			{
				ManualLogSource log4 = Log;
				if (log4 != null)
				{
					log4.LogWarning((object)"[I'm a Ghost] Local character is not ready yet.");
				}
				return false;
			}
			if (character.data.dead || character.data.passedOut || character.data.fullyPassedOut || character.data.fallSeconds > 0f || character.data.isClimbing || character.data.isRopeClimbing || character.data.isVineClimbing)
			{
				ManualLogSource log5 = Log;
				if (log5 != null)
				{
					log5.LogWarning((object)"[I'm a Ghost] Local character is not in a valid state to transform.");
				}
				return false;
			}
			return true;
		}

		private void EnterGhost(Character character)
		{
			try
			{
				_controller = ((Component)character).gameObject.GetComponent<GhostController>();
				if ((Object)(object)_controller == (Object)null)
				{
					_controller = ((Component)character).gameObject.AddComponent<GhostController>();
				}
				_controller.EnterGhost(character);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogError((object)("[I'm a Ghost] Failed to enter ghost form:\n" + ex));
				}
				_controller = null;
			}
		}

		private void ExitGhost()
		{
			try
			{
				_controller?.ExitGhost();
				_controller = null;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogError((object)("[I'm a Ghost] Failed to exit ghost form:\n" + ex));
				}
			}
		}

		private void ForceExit()
		{
			if ((Object)(object)_controller == (Object)null)
			{
				return;
			}
			try
			{
				_controller.ExitGhost();
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("[I'm a Ghost] ForceExit failed: " + ex.Message));
				}
			}
			_controller = null;
		}
	}
	[DefaultExecutionOrder(600)]
	public sealed class GhostController : MonoBehaviour
	{
		private sealed class ExpressionState
		{
			public Animator Animator;

			public bool ValuesKnown;

			public bool Ready;

			public bool Fade;

			public bool Explode;

			public bool Sad;

			public bool Burning;
		}

		private const float GroundClearanceMin = 0.8f;

		private const float BallFaceMoveSharpness = 6f;

		private const float CameraLookAhead = 4f;

		private const float CameraSmoothTime = 0.08f;

		private const float CameraRotationSharpness = 12f;

		private const float DefaultCameraDistance = 16f;

		private const float DefaultCameraHeight = 5f;

		private const float DefaultCameraFov = 80f;

		private Character _character;

		private Vector3 _prevCenter;

		private Vector3 _cameraVelocity;

		private Vector3 _cameraSmoothedPosition;

		private Quaternion _cameraSmoothedRotation;

		private bool _cameraHasSmoothedPosition;

		private Vector3 _moveDirection;

		private GameObject _ghostVisualRoot;

		private GhostBall _ghostBall;

		private Rigidbody _ghostRigidbody;

		private bool _ghostIsNetworked;

		private bool _attacking;

		private float _attackStartedAt;

		private bool _attackExploded;

		internal const string NetworkVisualMarker = "ImGhost.Visual";

		private static readonly FieldInfo ReadyToExplodeField = typeof(GhostBall).GetField("_readyToExplode", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo ExplodingField = typeof(GhostBall).GetField("exploding", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldInfo TickUntilDespawnField = typeof(GhostBall).GetField("tickUntilDespawn", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly FieldRef<GhostBall, bool> ReadyToExplodeRef = MakeFieldRef<GhostBall, bool>("_readyToExplode");

		private static readonly FieldRef<GhostBall, bool> ExplodingRef = MakeFieldRef<GhostBall, bool>("exploding");

		private static int _anReady;

		private static int _anFade;

		private static int _anExplode;

		private static int _anSad;

		private static int _anBurning;

		private static bool _anParamsResolved;

		private static readonly ConditionalWeakTable<GhostBall, ExpressionState> ExpressionStates = new ConditionalWeakTable<GhostBall, ExpressionState>();

		private readonly Dictionary<GameObject, bool> _hiddenHudStates = new Dictionary<GameObject, bool>();

		private float _nextHudSearchTime;

		private readonly List<KeyValuePair<Rigidbody, RigidbodyInterpolation>> _savedInterpolations = new List<KeyValuePair<Rigidbody, RigidbodyInterpolation>>();

		public static GhostBall ActiveGhostBall { get; private set; }

		public static Character ActiveGhostCharacter { get; private set; }

		internal static bool CameraOverridePatchActive { get; set; }

		public bool Active { get; private set; }

		public Character CharacterRef => _character;

		private static FieldRef<T, F> MakeFieldRef<T, F>(string name) where T : class
		{
			try
			{
				return AccessTools.FieldRefAccess<T, F>(name);
			}
			catch
			{
				return null;
			}
		}

		public bool IsValid()
		{
			if (!Active || (Object)(object)_character == (Object)null || (Object)(object)_character.data == (Object)null)
			{
				return false;
			}
			if (!_character.data.dead)
			{
				return !_character.data.fullyPassedOut;
			}
			return false;
		}

		private static void LogInfo(string message)
		{
			ManualLogSource log = GhostPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("[I'm a Ghost] " + message));
			}
		}

		private static void LogError(string entryPoint, Exception ex)
		{
			ManualLogSource log = GhostPlugin.Log;
			if (log != null)
			{
				log.LogError((object)("[I'm a Ghost] " + entryPoint + ": " + ex));
			}
		}

		public static bool IsLocalGhostCharacter(Character character)
		{
			if ((Object)(object)character == (Object)null || !character.IsLocal)
			{
				return false;
			}
			GhostController component = ((Component)character).GetComponent<GhostController>();
			if ((Object)(object)component != (Object)null)
			{
				return component.Active;
			}
			return false;
		}

		public void EnterGhost(Character character)
		{
			//IL_0009: 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_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			_character = character;
			_prevCenter = character.Center;
			_cameraVelocity = Vector3.zero;
			_cameraHasSmoothedPosition = false;
			_attacking = false;
			_attackExploded = false;
			_attackStartedAt = 0f;
			_nextHudSearchTime = 0f;
			Active = true;
			ActiveGhostCharacter = character;
			((Behaviour)this).enabled = true;
			SetGhostNoClip(enable: true);
			BuildGhostVisual();
			HideHud();
			LogInfo("Entered ghost form.");
		}

		public void ExitGhost()
		{
			if (Active || !((Object)(object)_ghostVisualRoot == (Object)null))
			{
				Active = false;
				ActiveGhostCharacter = null;
				((Behaviour)this).enabled = false;
				_attacking = false;
				_attackExploded = false;
				_attackStartedAt = 0f;
				SetGhostNoClip(enable: false);
				DestroyGhostVisual();
				RestoreHud();
				LogInfo("Exited ghost form.");
			}
		}

		private void Update()
		{
			if (!Active || (Object)(object)_character == (Object)null)
			{
				return;
			}
			try
			{
				ClearNonMovementInput();
				KeepPlayerAlive();
				UpdateAttack();
				if (Active)
				{
					UpdateGhostVisual();
					HideHud();
				}
			}
			catch (Exception ex)
			{
				LogError("GhostController.Update", ex);
			}
		}

		private void FixedUpdate()
		{
			if (!Active || (Object)(object)_character == (Object)null || _character.refs == null)
			{
				return;
			}
			try
			{
				MoveGhostTransform();
			}
			catch (Exception ex)
			{
				LogError("GhostController.FixedUpdate", ex);
			}
		}

		private void LateUpdate()
		{
			if (!Active || (Object)(object)_character == (Object)null)
			{
				return;
			}
			try
			{
				SyncCharacterData();
				if (!CameraOverridePatchActive)
				{
					RefreshCamera();
				}
			}
			catch (Exception ex)
			{
				LogError("GhostController.LateUpdate", ex);
			}
		}

		private void OnDestroy()
		{
			if (Active)
			{
				Active = false;
				ActiveGhostCharacter = null;
				SetGhostNoClip(enable: false);
				DestroyGhostVisual();
				RestoreHud();
			}
		}

		private void HideHud()
		{
			if (_hiddenHudStates.Count < 2 && !(Time.unscaledTime < _nextHudSearchTime))
			{
				_nextHudSearchTime = Time.unscaledTime + 0.5f;
				TryHideCanvas("Canvas_HUD");
				TryHideCanvas("Canvas_Names");
			}
		}

		private void TryHideCanvas(string canvasName)
		{
			GameObject val = GameObject.Find(canvasName);
			if (!((Object)(object)val == (Object)null) && !_hiddenHudStates.ContainsKey(val))
			{
				_hiddenHudStates[val] = val.activeSelf;
				if (val.activeSelf)
				{
					val.SetActive(false);
				}
			}
		}

		private void RestoreHud()
		{
			if (_hiddenHudStates.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<GameObject, bool> hiddenHudState in _hiddenHudStates)
			{
				if ((Object)(object)hiddenHudState.Key != (Object)null)
				{
					hiddenHudState.Key.SetActive(hiddenHudState.Value);
				}
			}
			_hiddenHudStates.Clear();
		}

		private void ClearNonMovementInput()
		{
			if (!((Object)(object)_character == (Object)null) && !((Object)(object)_character.input == (Object)null))
			{
				_character.input.jumpWasPressed = false;
				_character.input.jumpIsPressed = false;
				_character.input.sprintIsPressed = false;
				_character.input.sprintWasPressed = false;
				_character.input.sprintToggleWasPressed = false;
				_character.input.usePrimaryWasPressed = false;
				_character.input.usePrimaryIsPressed = false;
				_character.input.useSecondaryWasPressed = false;
				_character.input.useSecondaryIsPressed = false;
				_character.input.crouchWasPressed = false;
				_character.input.crouchIsPressed = false;
				_character.input.crouchToggleWasPressed = false;
				_character.input.dropWasPressed = false;
				_character.input.dropIsPressed = false;
				_character.input.interactWasPressed = false;
				_character.input.interactIsPressed = false;
			}
		}

		private void KeepPlayerAlive()
		{
			if (!((Object)(object)_character == (Object)null) && !((Object)(object)_character.data == (Object)null))
			{
				_character.data.dead = false;
				_character.data.zombified = false;
				_character.data.passedOut = false;
				_character.data.fullyPassedOut = false;
				_character.data.fallSeconds = 0f;
				_character.data.isSprinting = false;
			}
		}

		private Vector2 GetMovementInput()
		{
			//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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_character == (Object)null || (Object)(object)_character.input == (Object)null)
			{
				return Vector2.zero;
			}
			Vector2 movementInput = _character.input.movementInput;
			movementInput.x = Mathf.Clamp(movementInput.x, -1f, 1f);
			movementInput.y = Mathf.Clamp(movementInput.y, -1f, 1f);
			return movementInput;
		}

		private void SetGhostNoClip(bool enable)
		{
			if ((Object)(object)_character == (Object)null || _character.refs == null || (Object)(object)_character.refs.ragdoll == (Object)null)
			{
				return;
			}
			CharacterRagdoll ragdoll = _character.refs.ragdoll;
			try
			{
				if (enable && (Object)(object)_character.data != (Object)null && _character.data.IsCarryingCharacter)
				{
					AccessTools.Method(typeof(Character), "DropCarriedCharacter", (Type[])null, (Type[])null)?.Invoke(_character, new object[1] { true });
				}
				ragdoll.ToggleKinematic(enable);
				ragdoll.ToggleCollision(!enable);
				if (ragdoll.partList != null)
				{
					foreach (Bodypart part in ragdoll.partList)
					{
						Rigidbody val = (((Object)(object)part != (Object)null) ? part.Rig : null);
						if (!((Object)(object)val == (Object)null))
						{
							val.useGravity = !enable;
						}
					}
				}
				SetRagdollInterpolation(enable);
				if (!enable)
				{
					ragdoll.HaltBodyVelocity(false);
				}
			}
			catch (Exception ex)
			{
				LogError("GhostController.SetGhostNoClip(" + enable + ")", ex);
			}
		}

		private void SetRagdollInterpolation(bool ghostEnabled)
		{
			//IL_0076: 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_00fa: Unknown result type (might be due to invalid IL or missing references)
			CharacterRagdoll val = (((Object)(object)_character != (Object)null && _character.refs != null) ? _character.refs.ragdoll : null);
			if ((Object)(object)val == (Object)null || val.partList == null)
			{
				return;
			}
			if (!ghostEnabled)
			{
				for (int i = 0; i < _savedInterpolations.Count; i++)
				{
					Rigidbody key = _savedInterpolations[i].Key;
					if ((Object)(object)key != (Object)null)
					{
						try
						{
							key.interpolation = _savedInterpolations[i].Value;
						}
						catch
						{
						}
					}
				}
				_savedInterpolations.Clear();
				return;
			}
			_savedInterpolations.Clear();
			foreach (Bodypart part in val.partList)
			{
				Rigidbody val2 = (((Object)(object)part != (Object)null) ? part.Rig : null);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				try
				{
					if ((int)val2.interpolation != 0)
					{
						_savedInterpolations.Add(new KeyValuePair<Rigidbody, RigidbodyInterpolation>(val2, val2.interpolation));
						val2.interpolation = (RigidbodyInterpolation)0;
					}
				}
				catch
				{
				}
			}
		}

		private void MoveGhostTransform()
		{
			//IL_0009: 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_0039: 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_003e: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_0077: 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_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_008d: 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_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_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: 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)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: 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_00bb: 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_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: 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_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: 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_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: 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)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			if (_attacking)
			{
				_moveDirection = Vector3.zero;
				return;
			}
			Vector3 val = (((Object)(object)_character.data != (Object)null) ? _character.data.lookDirection_Flat : Vector3.zero);
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val = Vector3.ProjectOnPlane(((Component)this).transform.forward, Vector3.up);
			}
			Vector3 val2 = Vector3.ProjectOnPlane(val, Vector3.up);
			val = ((Vector3)(ref val2)).normalized;
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val = Vector3.forward;
			}
			Vector2 movementInput = GetMovementInput();
			val2 = Vector3.Cross(Vector3.up, val);
			Vector3 normalized = ((Vector3)(ref val2)).normalized;
			Vector3 val3 = val * movementInput.y + normalized * movementInput.x;
			if (((Vector3)(ref val3)).sqrMagnitude > 1f)
			{
				((Vector3)(ref val3)).Normalize();
			}
			float num = 0f;
			if ((int)GhostPlugin.UpKey.Value != 0 && Input.GetKey(GhostPlugin.UpKey.Value))
			{
				num += 1f;
			}
			if ((int)GhostPlugin.DownKey.Value != 0 && Input.GetKey(GhostPlugin.DownKey.Value))
			{
				num -= 1f;
			}
			float num2 = 1f;
			if ((int)GhostPlugin.SprintKey.Value != 0 && Input.GetKey(GhostPlugin.SprintKey.Value))
			{
				num2 = Mathf.Max(1f, GhostPlugin.SprintMultiplier.Value);
			}
			Vector3 val4 = (val3 * GhostPlugin.MovementSpeed.Value + Vector3.up * (num * GhostPlugin.VerticalSpeed.Value)) * num2 * Time.fixedDeltaTime;
			if (val4.y < 0f)
			{
				float groundHeight = GetGroundHeight(_character.Center);
				if (!float.IsNaN(groundHeight) && _character.Center.y <= groundHeight + 0.8f)
				{
					val4.y = Mathf.Max(val4.y, 0f);
				}
			}
			_moveDirection = ((((Vector3)(ref val3)).sqrMagnitude > 0.0001f) ? val3 : Vector3.zero);
			if (((Vector3)(ref val4)).sqrMagnitude > 0f)
			{
				Transform transform = ((Component)this).transform;
				transform.position += val4;
			}
		}

		private float GetGroundHeight(Vector3 position)
		{
			//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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				RaycastHit groundPosRaycast = HelperFunctions.GetGroundPosRaycast(position + Vector3.up * 400f, (LayerType)2, 0f);
				if ((Object)(object)((RaycastHit)(ref groundPosRaycast)).transform != (Object)null)
				{
					return ((RaycastHit)(ref groundPosRaycast)).point.y;
				}
			}
			catch
			{
			}
			try
			{
				RaycastHit groundPosRaycast2 = HelperFunctions.GetGroundPosRaycast(position + Vector3.up * 400f, (LayerType)1, 0f);
				if ((Object)(object)((RaycastHit)(ref groundPosRaycast2)).transform != (Object)null)
				{
					return ((RaycastHit)(ref groundPosRaycast2)).point.y;
				}
			}
			catch
			{
			}
			return float.NaN;
		}

		private void SyncCharacterData()
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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_004e: 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_0040: 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)
			//IL_0069: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: 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_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_character == (Object)null) && !((Object)(object)_character.data == (Object)null))
			{
				Vector3 center = _character.Center;
				Vector3 avarageVelocity = ((((Vector3)(ref _prevCenter)).sqrMagnitude > 0f) ? ((center - _prevCenter) / Mathf.Max(Time.deltaTime, 0.0001f)) : Vector3.zero);
				_prevCenter = center;
				_character.data.avarageLastFrameVelocity = _character.data.avarageVelocity;
				_character.data.avarageVelocity = avarageVelocity;
				_character.data.worldMovementInput = (_character.data.worldMovementInput_Grounded = Vector3.zero);
				_character.data.sinceGrounded = Mathf.Min(_character.data.sinceGrounded, 0.1f);
			}
		}

		private void UpdateAttack()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!_attacking)
			{
				if ((int)GhostPlugin.AttackKey.Value != 0 && Input.GetKeyDown(GhostPlugin.AttackKey.Value))
				{
					StartAttack();
				}
				return;
			}
			float num = Time.unscaledTime - _attackStartedAt;
			if (!_attackExploded && num >= GhostPlugin.AttackChargeSeconds.Value)
			{
				_attackExploded = true;
				DoExplode();
			}
			if (num >= GhostPlugin.AttackChargeSeconds.Value + GhostPlugin.AttackRevertSeconds.Value)
			{
				ExitGhost();
			}
		}

		private void StartAttack()
		{
			_attacking = true;
			_attackExploded = false;
			_attackStartedAt = Time.unscaledTime;
			SetGhostValue(ReadyToExplodeField, true);
			LogInfo("Ghost attack charging...");
		}

		private void DoExplode()
		{
			//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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			LogInfo("Ghost attack exploded!");
			if ((Object)(object)_ghostBall != (Object)null && (Object)(object)_ghostBall.explosionPrefab != (Object)null)
			{
				GameObject explosionPrefab = _ghostBall.explosionPrefab;
				try
				{
					if (PhotonNetwork.InRoom && (Object)(object)explosionPrefab.GetComponent<PhotonView>() != (Object)null)
					{
						PhotonNetwork.Instantiate(((Object)explosionPrefab).name, _character.Center, Quaternion.identity, (byte)0, (object[])null);
					}
					else
					{
						Object.Instantiate<GameObject>(explosionPrefab, _character.Center, Quaternion.identity);
					}
				}
				catch (Exception ex)
				{
					try
					{
						Object.Instantiate<GameObject>(explosionPrefab, _character.Center, Quaternion.identity);
					}
					catch (Exception ex2)
					{
						LogError("GhostController.DoExplode (explosion prefab)", ex2);
					}
					LogError("GhostController.DoExplode (networked explosion)", ex);
				}
			}
			SetGhostValue(ExplodingField, true);
			KnockDownNearbyPlayers();
		}

		private void KnockDownNearbyPlayers()
		{
			//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_00a2: 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)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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_00d6: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			float value = GhostPlugin.AttackRadius.Value;
			float value2 = GhostPlugin.KnockbackForce.Value;
			Vector3 center = _character.Center;
			foreach (Character allCharacter in Character.AllCharacters)
			{
				if ((Object)(object)allCharacter == (Object)null || (Object)(object)allCharacter == (Object)(object)_character || (Object)(object)allCharacter.data == (Object)null || !allCharacter.data.fullyConscious || allCharacter.refs == null || (Object)(object)allCharacter.refs.view == (Object)null)
				{
					continue;
				}
				Vector3 val = allCharacter.Center - center;
				float magnitude = ((Vector3)(ref val)).magnitude;
				if (!(magnitude > value) && !(magnitude < 0.001f))
				{
					Vector3 val2 = val / magnitude * value2 + Vector3.up * (value2 * 0.55f);
					try
					{
						allCharacter.refs.view.RPC("RPCA_AddForceAtPosition", (RpcTarget)0, new object[3] { val2, center, value });
					}
					catch (Exception ex)
					{
						LogError("GhostController.KnockDownNearbyPlayers (force)", ex);
					}
					try
					{
						allCharacter.refs.view.RPC("RPCA_Fall", (RpcTarget)0, new object[2] { 1.2f, 0.25f });
					}
					catch (Exception ex2)
					{
						LogError("GhostController.KnockDownNearbyPlayers (fall)", ex2);
					}
				}
			}
		}

		private static void SetGhostValue(FieldInfo field, object value)
		{
			try
			{
				if (field != null && (Object)(object)ActiveGhostBall != (Object)null)
				{
					field.SetValue(ActiveGhostBall, value);
				}
			}
			catch
			{
			}
		}

		internal static void ApplyGhostBallExpression(GhostBall ghostBall)
		{
			if ((Object)(object)ghostBall == (Object)null)
			{
				return;
			}
			try
			{
				ExpressionState orCreateValue = ExpressionStates.GetOrCreateValue(ghostBall);
				if ((Object)(object)orCreateValue.Animator == (Object)null)
				{
					orCreateValue.Animator = ((Component)ghostBall).GetComponent<Animator>();
					orCreateValue.ValuesKnown = false;
				}
				Animator animator = orCreateValue.Animator;
				if (!((Object)(object)animator == (Object)null))
				{
					EnsureAnimatorParams(ghostBall);
					bool value = ReadGhostBool(ghostBall, ReadyToExplodeRef, ReadyToExplodeField);
					bool value2 = ReadGhostBool(ghostBall, ExplodingRef, ExplodingField);
					bool value3 = false;
					bool value4 = false;
					bool value5 = false;
					SetBoolIfChanged(animator, _anReady, value, orCreateValue.ValuesKnown, ref orCreateValue.Ready);
					SetBoolIfChanged(animator, _anFade, value3, orCreateValue.ValuesKnown, ref orCreateValue.Fade);
					SetBoolIfChanged(animator, _anExplode, value2, orCreateValue.ValuesKnown, ref orCreateValue.Explode);
					SetBoolIfChanged(animator, _anSad, value4, orCreateValue.ValuesKnown, ref orCreateValue.Sad);
					SetBoolIfChanged(animator, _anBurning, value5, orCreateValue.ValuesKnown, ref orCreateValue.Burning);
					orCreateValue.ValuesKnown = true;
				}
			}
			catch (Exception ex)
			{
				LogError("GhostController.ApplyGhostBallExpression", ex);
				ExpressionStates.Remove(ghostBall);
			}
		}

		private static void SetBoolIfChanged(Animator anim, int param, bool value, bool valuesKnown, ref bool lastValue)
		{
			if (!valuesKnown || lastValue != value)
			{
				anim.SetBool(param, value);
				lastValue = value;
			}
		}

		private static bool ReadGhostBool(GhostBall ghostBall, FieldRef<GhostBall, bool> fastRef, FieldInfo fallback)
		{
			if (fastRef == null)
			{
				return FieldAsBool(fallback, ghostBall);
			}
			return fastRef.Invoke(ghostBall);
		}

		private static void ClearExpressionCache(GhostBall ghostBall)
		{
			if ((Object)(object)ghostBall != (Object)null)
			{
				ExpressionStates.Remove(ghostBall);
			}
		}

		private static bool FieldAsBool(FieldInfo field, object instance)
		{
			try
			{
				bool flag = default(bool);
				int num;
				if (field != null)
				{
					object value = field.GetValue(instance);
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		private static void EnsureAnimatorParams(GhostBall ghostBall)
		{
			if (!_anParamsResolved)
			{
				_anReady = ResolveAnHash(ghostBall, "AN_READY", "ReadyToExplode");
				_anFade = ResolveAnHash(ghostBall, "AN_FADE", "FadeOut");
				_anExplode = ResolveAnHash(ghostBall, "AN_EXPLODE", "Exploded");
				_anSad = ResolveAnHash(ghostBall, "AN_SAD", "Sad");
				_anBurning = ResolveAnHash(ghostBall, "AN_BURNING", "Burning");
				_anParamsResolved = true;
			}
		}

		private static int ResolveAnHash(GhostBall ghostBall, string fieldName, string fallbackParam)
		{
			FieldInfo field = typeof(GhostBall).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null && field.GetValue(ghostBall) is int num && num != 0)
			{
				return num;
			}
			return Animator.StringToHash(fallbackParam);
		}

		private void BuildGhostVisual()
		{
			if ((Object)(object)_ghostVisualRoot != (Object)null || (Object)(object)_character == (Object)null)
			{
				return;
			}
			try
			{
				GameObject val = Resources.Load<GameObject>("GhostBall");
				if ((Object)(object)val == (Object)null)
				{
					LogInfo("Resources.Load(\"GhostBall\") returned null; no ghost visual will be shown.");
				}
				else if (PhotonNetwork.InRoom && (Object)(object)((MonoBehaviourPun)_character).photonView != (Object)null && ((MonoBehaviourPun)_character).photonView.ViewID > 0)
				{
					BuildNetworkedGhost(val);
				}
				else
				{
					BuildLocalGhost(val);
				}
			}
			catch (Exception ex)
			{
				LogError("GhostController.BuildGhostVisual", ex);
				DestroyGhostVisual();
			}
		}

		private void BuildNetworkedGhost(GameObject prefab)
		{
			//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)
			GameObject val = PhotonNetwork.Instantiate("GhostBall", _character.Center, Quaternion.identity, (byte)0, new object[2]
			{
				"ImGhost.Visual",
				((MonoBehaviourPun)_character).photonView.ViewID
			});
			((Object)val).name = "ImGhostNetworked";
			_ghostVisualRoot = val;
			_ghostIsNetworked = true;
			_ghostBall = val.GetComponent<GhostBall>();
			ActiveGhostBall = _ghostBall;
			_ghostRigidbody = val.GetComponent<Rigidbody>();
			if ((Object)(object)_ghostRigidbody != (Object)null)
			{
				_ghostRigidbody.isKinematic = true;
				_ghostRigidbody.interpolation = (RigidbodyInterpolation)0;
			}
			KeepGhostAliveIndefinitely();
			val.SetActive(GhostPlugin.ShowGhostVisual.Value);
			LogInfo("Spawned networked ghost visual with vanilla GhostBall prefab.");
		}

		private void BuildLocalGhost(GameObject prefab)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(prefab, _character.Center, Quaternion.identity);
			((Object)val).name = "ImGhostLocal";
			_ghostVisualRoot = val;
			_ghostIsNetworked = false;
			_ghostBall = val.GetComponent<GhostBall>();
			ActiveGhostBall = _ghostBall;
			_ghostRigidbody = val.GetComponent<Rigidbody>();
			if ((Object)(object)_ghostBall != (Object)null)
			{
				((Behaviour)_ghostBall).enabled = false;
			}
			if ((Object)(object)_ghostRigidbody != (Object)null)
			{
				_ghostRigidbody.isKinematic = true;
				_ghostRigidbody.interpolation = (RigidbodyInterpolation)0;
			}
			IgnoreCollisionWithOwnRagdoll(val);
			KeepGhostAliveIndefinitely();
			val.SetActive(GhostPlugin.ShowGhostVisual.Value);
			LogInfo("Spawned local-only ghost visual (offline / single-player).");
		}

		private void KeepGhostAliveIndefinitely()
		{
			SetGhostValue(TickUntilDespawnField, -1000f);
			if ((Object)(object)_ghostBall != (Object)null)
			{
				_ghostBall.lifetime = 9999f;
			}
		}

		private static void IgnoreCollisionWithOwnRagdoll(GameObject ghostRoot)
		{
			IgnoreCollisionWithCharacter(ghostRoot, ActiveGhostCharacter);
		}

		internal static void IgnoreCollisionWithCharacter(GameObject ghostRoot, Character character)
		{
			if ((Object)(object)ghostRoot == (Object)null || (Object)(object)character == (Object)null || character.refs == null || (Object)(object)character.refs.ragdoll == (Object)null)
			{
				return;
			}
			Collider[] componentsInChildren = ghostRoot.GetComponentsInChildren<Collider>(true);
			if (componentsInChildren.Length == 0)
			{
				return;
			}
			foreach (Bodypart part in character.refs.ragdoll.partList)
			{
				if ((Object)(object)part == (Object)null)
				{
					continue;
				}
				Collider[] componentsInChildren2 = ((Component)part).GetComponentsInChildren<Collider>(true);
				Collider[] array = componentsInChildren;
				foreach (Collider val in array)
				{
					Collider[] array2 = componentsInChildren2;
					foreach (Collider val2 in array2)
					{
						if ((Object)(object)val != (Object)null && (Object)(object)val2 != (Object)null && val.enabled && val2.enabled)
						{
							try
							{
								Physics.IgnoreCollision(val, val2, true);
							}
							catch
							{
							}
						}
					}
				}
			}
		}

		private void UpdateGhostVisual()
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_ghostVisualRoot == (Object)null) && !((Object)(object)_character == (Object)null))
			{
				if (_ghostVisualRoot.activeSelf != GhostPlugin.ShowGhostVisual.Value)
				{
					_ghostVisualRoot.SetActive(GhostPlugin.ShowGhostVisual.Value);
				}
				_ghostVisualRoot.transform.position = _character.Center;
				if (((Vector3)(ref _moveDirection)).sqrMagnitude > 0.01f)
				{
					Quaternion val = Quaternion.LookRotation(_moveDirection, Vector3.up);
					_ghostVisualRoot.transform.rotation = Quaternion.Slerp(_ghostVisualRoot.transform.rotation, val, Time.deltaTime * 6f);
				}
				ApplyGhostBallExpression(_ghostBall);
			}
		}

		private void DestroyGhostVisual()
		{
			ActiveGhostBall = null;
			if ((Object)(object)_ghostVisualRoot != (Object)null)
			{
				if (_ghostIsNetworked && PhotonNetwork.InRoom)
				{
					try
					{
						PhotonNetwork.Destroy(_ghostVisualRoot);
					}
					catch
					{
					}
				}
				else
				{
					Object.Destroy((Object)(object)_ghostVisualRoot);
				}
			}
			ClearExpressionCache(_ghostBall);
			_ghostVisualRoot = null;
			_ghostBall = null;
			_ghostRigidbody = null;
			_ghostIsNetworked = false;
		}

		internal static void ApplyCameraOverrideForLocalGhost()
		{
			Character activeGhostCharacter = ActiveGhostCharacter;
			if (!((Object)(object)activeGhostCharacter == (Object)null))
			{
				GhostController component = ((Component)activeGhostCharacter).GetComponent<GhostController>();
				if ((Object)(object)component != (Object)null && component.IsValid())
				{
					component.RefreshCamera();
				}
			}
		}

		private void RefreshCamera()
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0069: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_00a2: 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)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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)
			try
			{
				Camera main = Camera.main;
				if (!((Object)(object)main == (Object)null))
				{
					Vector3 thirdPersonCameraLookTarget = GetThirdPersonCameraLookTarget(_character);
					Vector3 thirdPersonCameraPosition = GetThirdPersonCameraPosition(_character);
					if (!_cameraHasSmoothedPosition)
					{
						_cameraSmoothedPosition = thirdPersonCameraPosition;
						_cameraSmoothedRotation = GetCameraRotation(_cameraSmoothedPosition, thirdPersonCameraLookTarget);
						_cameraHasSmoothedPosition = true;
					}
					else
					{
						_cameraSmoothedPosition = Vector3.SmoothDamp(_cameraSmoothedPosition, thirdPersonCameraPosition, ref _cameraVelocity, 0.08f);
						Quaternion cameraRotation = GetCameraRotation(_cameraSmoothedPosition, thirdPersonCameraLookTarget);
						float num = 1f - Mathf.Exp(-12f * Time.deltaTime);
						_cameraSmoothedRotation = Quaternion.Slerp(_cameraSmoothedRotation, cameraRotation, num);
					}
					((Component)main).transform.SetPositionAndRotation(_cameraSmoothedPosition, _cameraSmoothedRotation);
					main.fieldOfView = Mathf.Lerp(main.fieldOfView, GetCameraFov(), Time.deltaTime * 4f);
				}
			}
			catch (Exception ex)
			{
				LogError("GhostController.RefreshCamera", ex);
			}
		}

		private static Vector3 GetThirdPersonCameraPosition(Character character)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			Vector3 cameraForward = GetCameraForward(character);
			return character.Center + Vector3.up * (GetCameraHeight() + 2f) - cameraForward * GetCameraDistance();
		}

		private static Vector3 GetThirdPersonCameraLookTarget(Character character)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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)
			Vector3 cameraForward = GetCameraForward(character);
			float num = Mathf.Clamp((((Object)(object)character.data != (Object)null) ? ((Vector3)(ref character.data.lookDirection)).normalized : cameraForward).y, -0.35f, 0.65f);
			return character.Center + Vector3.up * (GetCameraHeight() + num * 3f) + cameraForward * 4f;
		}

		private static float GetCameraDistance()
		{
			return Mathf.Clamp((GhostPlugin.CameraDistance != null) ? GhostPlugin.CameraDistance.Value : 16f, 8f, 30f);
		}

		private static float GetCameraHeight()
		{
			return Mathf.Clamp((GhostPlugin.CameraHeight != null) ? GhostPlugin.CameraHeight.Value : 5f, 2f, 14f);
		}

		private static float GetCameraFov()
		{
			return Mathf.Clamp((GhostPlugin.CameraFov != null) ? GhostPlugin.CameraFov.Value : 80f, 60f, 110f);
		}

		private static Vector3 GetCameraForward(Character character)
		{
			//IL_001b: 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_0020: 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_0022: 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_002c: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: 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)
			//IL_0061: 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_0094: 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)
			Vector3 val = (((Object)(object)character.data != (Object)null) ? character.data.lookDirection_Flat : Vector3.zero);
			val = Vector3.ProjectOnPlane(val, Vector3.up);
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f && (Object)(object)Camera.main != (Object)null)
			{
				val = Vector3.ProjectOnPlane(((Component)Camera.main).transform.forward, Vector3.up);
			}
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val = Vector3.ProjectOnPlane(((Component)character).transform.forward, Vector3.up);
			}
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val = Vector3.forward;
			}
			return ((Vector3)(ref val)).normalized;
		}

		private static Quaternion GetCameraRotation(Vector3 cameraPosition, Vector3 lookTarget)
		{
			//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_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = lookTarget - cameraPosition;
			if (((Vector3)(ref val)).sqrMagnitude < 0.0001f)
			{
				val = Vector3.forward;
			}
			return Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up);
		}
	}
	[HarmonyPatch]
	public static class GhostHarmonyPatches
	{
		private static readonly HashSet<int> CollisionIgnoredInstances = new HashSet<int>();

		private static readonly ConditionalWeakTable<GhostBall, PhotonView> PhotonViewCache = new ConditionalWeakTable<GhostBall, PhotonView>();

		private static PhotonView GetPhotonView(GhostBall ghostBall)
		{
			if (PhotonViewCache.TryGetValue(ghostBall, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			value = ((Component)ghostBall).GetComponent<PhotonView>();
			PhotonViewCache.Remove(ghostBall);
			PhotonViewCache.Add(ghostBall, value);
			return value;
		}

		private static bool IsGhostVisual(GhostBall ghostBall, PhotonView view)
		{
			if ((Object)(object)ghostBall == (Object)null)
			{
				return false;
			}
			object[] array = (((Object)(object)view != (Object)null) ? view.InstantiationData : null);
			if (array != null && array.Length != 0 && array[0] is string text)
			{
				return text == "ImGhost.Visual";
			}
			return false;
		}

		private static bool IsGhost(Character character)
		{
			return GhostController.IsLocalGhostCharacter(character);
		}

		[HarmonyPatch(typeof(GhostBall), "Update")]
		[HarmonyPrefix]
		private static bool GhostBallUpdatePrefix(GhostBall __instance)
		{
			PhotonView photonView = GetPhotonView(__instance);
			if (!IsGhostVisual(__instance, photonView))
			{
				return true;
			}
			if ((Object)(object)photonView != (Object)null && !photonView.IsMine)
			{
				GhostController.ApplyGhostBallExpression(__instance);
			}
			return false;
		}

		[HarmonyPatch(typeof(GhostBall), "FixedUpdate")]
		[HarmonyPrefix]
		private static bool GhostBallFixedUpdatePrefix(GhostBall __instance)
		{
			if (!IsGhostVisual(__instance, GetPhotonView(__instance)))
			{
				return true;
			}
			TrySetupCollisionIgnore(__instance);
			return false;
		}

		private static void TrySetupCollisionIgnore(GhostBall ghostBall)
		{
			try
			{
				GameObject gameObject = ((Component)ghostBall).gameObject;
				int instanceID = ((Object)gameObject).GetInstanceID();
				if (CollisionIgnoredInstances.Contains(instanceID))
				{
					return;
				}
				PhotonView photonView = GetPhotonView(ghostBall);
				object[] array = (((Object)(object)photonView != (Object)null) ? photonView.InstantiationData : null);
				if (array != null && array.Length >= 2 && array[1] is int num)
				{
					PhotonView val = PhotonView.Find(num);
					Character val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<Character>() : null);
					if (!((Object)(object)val2 == (Object)null))
					{
						GhostController.IgnoreCollisionWithCharacter(gameObject, val2);
						CollisionIgnoredInstances.Add(instanceID);
					}
				}
			}
			catch
			{
			}
		}

		internal static bool CharacterRpcDiePrefix(Character __instance)
		{
			return !IsGhost(__instance);
		}

		internal static bool CharacterRpcSetDeadPrefix(Character __instance)
		{
			return !IsGhost(__instance);
		}

		internal static bool CharacterRpcPassOutPrefix(Character __instance)
		{
			return !IsGhost(__instance);
		}

		internal static bool CharacterDieInstantlyPrefix(Character __instance)
		{
			return !IsGhost(__instance);
		}

		internal static bool CharacterHandleDeathPrefix(Character __instance)
		{
			return !IsGhost(__instance);
		}

		internal static bool CharacterWarpPlayerRpcPrefix(Character __instance)
		{
			return !IsGhost(__instance);
		}

		internal static bool CharacterWarpPlayerPrefix(Character __instance)
		{
			return !IsGhost(__instance);
		}

		internal static bool CharacterFallPrefix(Character __instance)
		{
			return !IsGhost(__instance);
		}

		internal static bool CharacterRpcFallPrefix(Character __instance)
		{
			return !IsGhost(__instance);
		}

		internal static void MainCameraMovementLateUpdatePostfix()
		{
			GhostController.ApplyCameraOverrideForLocalGhost();
		}
	}
}