Decompiled source of ImmersiveFirstPerson v1.4.1

BepInEx/plugins/ImmersiveFirstPerson/ImmersiveFirstPerson.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BlacksmithTools;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 ImmersiveFirstPerson
{
	internal static class BlacksmithHeadHider
	{
		private sealed class BodypartEntrySnapshot
		{
			internal bool HadParts { get; set; }

			internal bool HadBones { get; set; }

			internal List<bodyPart>? Parts { get; set; }

			internal List<int>? Bones { get; set; }
		}

		private const string BodyPartControllerTypeName = "BlacksmithTools.BodyPartController";

		private static readonly List<int> EquippedHashes = new List<int>();

		private static readonly int[] HeadBoneIndexes = Util.BodyPartToBoneIndexes((bodyPart)0);

		private static readonly Dictionary<string, BodypartEntrySnapshot> EntrySnapshots = new Dictionary<string, BodypartEntrySnapshot>();

		private static Type? _bodyPartControllerType;

		private static MethodInfo? _setupMethod;

		private static MethodInfo? _fullUpdateMethod;

		private static VisEquipment? _activeVisEquipment;

		private static string? _activeKey;

		private static bool _active;

		private static bool _dirty = true;

		internal static void RequestRefresh()
		{
			_dirty = true;
		}

		internal static bool TryApply(Player player, out string failureReason)
		{
			failureReason = string.Empty;
			if (!PlayerEquipmentAccess.TryGetVisEquipment(player, out VisEquipment visEquipment))
			{
				return Fail("local VisEquipment was not found", out failureReason);
			}
			if ((Object)(object)_activeVisEquipment != (Object)null && (Object)(object)_activeVisEquipment != (Object)(object)visEquipment)
			{
				Restore();
			}
			if (!CanUseBlacksmith(visEquipment, out failureReason))
			{
				return false;
			}
			if (!TrySelectBodypartRuleKey(visEquipment, out string ruleKey, out failureReason))
			{
				return false;
			}
			if (_active && !_dirty && (Object)(object)_activeVisEquipment == (Object)(object)visEquipment && string.Equals(_activeKey, ruleKey, StringComparison.Ordinal))
			{
				return true;
			}
			if (_activeKey != null && !string.Equals(_activeKey, ruleKey, StringComparison.Ordinal))
			{
				RestoreBodypartEntries();
				_active = false;
				_activeKey = null;
			}
			RegisterHeadRule(ruleKey);
			if (!EnsureController(visEquipment, out Component controller, out failureReason))
			{
				RestoreAfterFailedApply(visEquipment);
				return false;
			}
			if ((Object)(object)controller == (Object)null || !TriggerBodyControllerUpdate(visEquipment, controller, out failureReason))
			{
				RestoreAfterFailedApply(visEquipment);
				return false;
			}
			if (!ValidateHeadHidden(visEquipment, out failureReason))
			{
				RestoreAfterFailedApply(visEquipment);
				return false;
			}
			_activeVisEquipment = visEquipment;
			_activeKey = ruleKey;
			_active = true;
			_dirty = false;
			Plugin.HeadHidingDebugLog("Blacksmith head hide active using rule key '" + ruleKey + "'.");
			return true;
		}

		internal static void Restore()
		{
			VisEquipment activeVisEquipment = _activeVisEquipment;
			RestoreBodypartEntries();
			if ((Object)(object)activeVisEquipment != (Object)null && TryGetController(activeVisEquipment, out Component controller) && (Object)(object)controller != (Object)null)
			{
				if (TriggerBodyControllerUpdate(activeVisEquipment, controller, out string failureReason))
				{
					Plugin.HeadHidingDebugLog("Restored Blacksmith body mesh after first-person exit.");
				}
				else
				{
					Plugin.HeadHidingDebugLog("Blacksmith restore update failed: " + failureReason + ".");
				}
			}
			_activeVisEquipment = null;
			_activeKey = null;
			_active = false;
			_dirty = true;
		}

		private static bool CanUseBlacksmith(VisEquipment visEquipment, out string failureReason)
		{
			failureReason = string.Empty;
			if (Main.bodyHidingEnabled == null || !Main.bodyHidingEnabled.Value)
			{
				return Fail("Blacksmith bodypart hiding is disabled", out failureReason);
			}
			if ((Object)(object)visEquipment.m_bodyModel == (Object)null)
			{
				return Fail("VisEquipment has no body model renderer", out failureReason);
			}
			Mesh sharedMesh = visEquipment.m_bodyModel.sharedMesh;
			if ((Object)(object)sharedMesh == (Object)null)
			{
				return Fail("body model has no shared mesh", out failureReason);
			}
			if (!sharedMesh.isReadable)
			{
				return Fail("body model mesh is unreadable", out failureReason);
			}
			return true;
		}

		private static bool TrySelectBodypartRuleKey(VisEquipment visEquipment, out string ruleKey, out string failureReason)
		{
			ruleKey = string.Empty;
			failureReason = string.Empty;
			int helmetHash = PlayerEquipmentAccess.GetHelmetHash(visEquipment);
			if (helmetHash != 0 && PlayerEquipmentAccess.TryGetPrefabName(helmetHash, out ruleKey))
			{
				return true;
			}
			PlayerEquipmentAccess.GetEquippedHashes(visEquipment, EquippedHashes);
			foreach (int equippedHash in EquippedHashes)
			{
				if (equippedHash == 0 || !PlayerEquipmentAccess.TryGetPrefabName(equippedHash, out ruleKey))
				{
					continue;
				}
				return true;
			}
			return Fail("no stable equipped item prefab name was available for Blacksmith body hiding", out failureReason);
		}

		private static void RegisterHeadRule(string ruleKey)
		{
			CaptureEntrySnapshot(ruleKey);
			List<bodyPart> value;
			List<bodyPart> list = (BodypartSystem.bodypartSettings.TryGetValue(ruleKey, out value) ? new List<bodyPart>(value) : new List<bodyPart>());
			if (!list.Contains((bodyPart)0))
			{
				list.Add((bodyPart)0);
			}
			BodypartSystem.bodypartSettings[ruleKey] = list;
			List<int> value2;
			List<int> list2 = (BodypartSystem.bodypartSettingsAsBones.TryGetValue(ruleKey, out value2) ? new List<int>(value2) : new List<int>());
			int[] headBoneIndexes = HeadBoneIndexes;
			foreach (int item in headBoneIndexes)
			{
				if (!list2.Contains(item))
				{
					list2.Add(item);
				}
			}
			BodypartSystem.bodypartSettingsAsBones[ruleKey] = list2;
			Plugin.HeadHidingDebugLog("Registered temporary Blacksmith Head rule for '" + ruleKey + "'.");
		}

		private static void CaptureEntrySnapshot(string ruleKey)
		{
			if (!EntrySnapshots.ContainsKey(ruleKey))
			{
				List<bodyPart> value;
				bool flag = BodypartSystem.bodypartSettings.TryGetValue(ruleKey, out value);
				List<int> value2;
				bool flag2 = BodypartSystem.bodypartSettingsAsBones.TryGetValue(ruleKey, out value2);
				EntrySnapshots[ruleKey] = new BodypartEntrySnapshot
				{
					HadParts = flag,
					HadBones = flag2,
					Parts = (flag ? new List<bodyPart>(value) : null),
					Bones = (flag2 ? new List<int>(value2) : null)
				};
			}
		}

		private static bool EnsureController(VisEquipment visEquipment, out Component? controller, out string failureReason)
		{
			controller = null;
			failureReason = string.Empty;
			if (!TryResolveBodyPartControllerType(out Type controllerType, out failureReason))
			{
				return false;
			}
			controller = ((Component)visEquipment).GetComponent(controllerType);
			if ((Object)(object)controller != (Object)null)
			{
				return true;
			}
			if (!CanUseBlacksmith(visEquipment, out failureReason))
			{
				return false;
			}
			try
			{
				controller = ((Component)visEquipment).gameObject.AddComponent(controllerType);
				MethodInfo setupMethod = GetSetupMethod(controllerType);
				if (setupMethod == null)
				{
					return Fail("Blacksmith BodyPartController.Setup method was not found", out failureReason);
				}
				setupMethod.Invoke(controller, new object[1] { visEquipment });
				Plugin.HeadHidingDebugLog("Attached missing Blacksmith BodyPartController to local VisEquipment.");
				return true;
			}
			catch (Exception ex)
			{
				return Fail("Blacksmith BodyPartController setup failed: " + ex.GetType().Name, out failureReason);
			}
		}

		private static bool TryGetController(VisEquipment visEquipment, out Component? controller)
		{
			controller = null;
			if (!TryResolveBodyPartControllerType(out Type controllerType, out string _))
			{
				return false;
			}
			controller = (((Object)(object)visEquipment != (Object)null) ? ((Component)visEquipment).GetComponent(controllerType) : null);
			return (Object)(object)controller != (Object)null;
		}

		private static bool TriggerBodyControllerUpdate(VisEquipment visEquipment, Component controller, out string failureReason)
		{
			failureReason = string.Empty;
			try
			{
				MethodInfo fullUpdateMethod = GetFullUpdateMethod(((object)controller).GetType());
				if (fullUpdateMethod == null)
				{
					return Fail("Blacksmith BodyPartController.FullUpdate method was not found", out failureReason);
				}
				fullUpdateMethod.Invoke(controller, Array.Empty<object>());
				ApplyCurrentModelMesh(visEquipment);
				return true;
			}
			catch (Exception ex)
			{
				return Fail("Blacksmith BodyPartController update failed: " + ex.GetType().Name, out failureReason);
			}
		}

		private static void ApplyCurrentModelMesh(VisEquipment visEquipment)
		{
			if ((Object)(object)visEquipment == (Object)null || (Object)(object)visEquipment.m_bodyModel == (Object)null || visEquipment.m_models == null)
			{
				return;
			}
			int modelIndex = visEquipment.GetModelIndex();
			if (modelIndex >= 0 && modelIndex < visEquipment.m_models.Length)
			{
				Mesh mesh = visEquipment.m_models[modelIndex].m_mesh;
				if ((Object)(object)mesh != (Object)null && (Object)(object)visEquipment.m_bodyModel.sharedMesh != (Object)(object)mesh)
				{
					visEquipment.m_bodyModel.sharedMesh = mesh;
				}
			}
		}

		private static bool ValidateHeadHidden(VisEquipment visEquipment, out string failureReason)
		{
			failureReason = string.Empty;
			if ((Object)(object)visEquipment.m_bodyModel == (Object)null)
			{
				return Fail("body model renderer disappeared during Blacksmith validation", out failureReason);
			}
			Mesh sharedMesh = visEquipment.m_bodyModel.sharedMesh;
			if ((Object)(object)sharedMesh == (Object)null)
			{
				return Fail("body model mesh disappeared during Blacksmith validation", out failureReason);
			}
			if (!sharedMesh.isReadable)
			{
				return Fail("body model mesh became unreadable during Blacksmith validation", out failureReason);
			}
			if (sharedMesh.boneWeights == null || sharedMesh.boneWeights.Length == 0)
			{
				return Fail("body model mesh has no bone weights for Blacksmith validation", out failureReason);
			}
			if (MeshContainsDominantHeadTriangles(sharedMesh))
			{
				return Fail("Blacksmith update left dominant head-weighted body triangles visible", out failureReason);
			}
			return true;
		}

		private static bool MeshContainsDominantHeadTriangles(Mesh mesh)
		{
			BoneWeight[] boneWeights = mesh.boneWeights;
			for (int i = 0; i < mesh.subMeshCount; i++)
			{
				int[] triangles = mesh.GetTriangles(i);
				for (int j = 0; j + 2 < triangles.Length; j += 3)
				{
					if (TriangleWouldBeRemovedByBlacksmith(boneWeights, triangles, j))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool TriangleWouldBeRemovedByBlacksmith(BoneWeight[] boneWeights, int[] triangles, int triangleIndex)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			for (int i = 0; i < 2; i++)
			{
				int num2 = triangles[triangleIndex + i];
				if (num2 >= 0 && num2 < boneWeights.Length && HasDominantHeadBone(boneWeights[num2]) && ++num == 1)
				{
					return true;
				}
			}
			return false;
		}

		private static bool HasDominantHeadBone(BoneWeight boneWeight)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Max(new float[4]
			{
				((BoneWeight)(ref boneWeight)).weight0,
				((BoneWeight)(ref boneWeight)).weight1,
				((BoneWeight)(ref boneWeight)).weight2,
				((BoneWeight)(ref boneWeight)).weight3
			});
			if (num <= 0f)
			{
				return false;
			}
			for (int i = 0; i < 4; i++)
			{
				int boneIndex = GetBoneIndex(boneWeight, i);
				if (HeadBoneIndexes.Contains(boneIndex))
				{
					float boneWeight2 = GetBoneWeight(boneWeight, i);
					if (boneWeight2 / num > 0.9f)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static int GetBoneIndex(BoneWeight boneWeight, int boneSlot)
		{
			if (1 == 0)
			{
			}
			int result = boneSlot switch
			{
				0 => ((BoneWeight)(ref boneWeight)).boneIndex0, 
				1 => ((BoneWeight)(ref boneWeight)).boneIndex1, 
				2 => ((BoneWeight)(ref boneWeight)).boneIndex2, 
				3 => ((BoneWeight)(ref boneWeight)).boneIndex3, 
				_ => -1, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private static float GetBoneWeight(BoneWeight boneWeight, int boneSlot)
		{
			if (1 == 0)
			{
			}
			float result = boneSlot switch
			{
				0 => ((BoneWeight)(ref boneWeight)).weight0, 
				1 => ((BoneWeight)(ref boneWeight)).weight1, 
				2 => ((BoneWeight)(ref boneWeight)).weight2, 
				3 => ((BoneWeight)(ref boneWeight)).weight3, 
				_ => 0f, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private static void RestoreAfterFailedApply(VisEquipment visEquipment)
		{
			RestoreBodypartEntries();
			if (TryGetController(visEquipment, out Component controller) && (Object)(object)controller != (Object)null)
			{
				TriggerBodyControllerUpdate(visEquipment, controller, out string _);
			}
			_activeVisEquipment = null;
			_activeKey = null;
			_active = false;
			_dirty = true;
		}

		private static void RestoreBodypartEntries()
		{
			foreach (KeyValuePair<string, BodypartEntrySnapshot> entrySnapshot in EntrySnapshots)
			{
				string key = entrySnapshot.Key;
				BodypartEntrySnapshot value = entrySnapshot.Value;
				if (value.HadParts && value.Parts != null)
				{
					BodypartSystem.bodypartSettings[key] = new List<bodyPart>(value.Parts);
				}
				else
				{
					BodypartSystem.bodypartSettings.Remove(key);
				}
				if (value.HadBones && value.Bones != null)
				{
					BodypartSystem.bodypartSettingsAsBones[key] = new List<int>(value.Bones);
				}
				else
				{
					BodypartSystem.bodypartSettingsAsBones.Remove(key);
				}
			}
			EntrySnapshots.Clear();
		}

		private static bool TryResolveBodyPartControllerType(out Type controllerType, out string failureReason)
		{
			failureReason = string.Empty;
			if ((object)_bodyPartControllerType == null)
			{
				_bodyPartControllerType = typeof(BodypartSystem).Assembly.GetType("BlacksmithTools.BodyPartController", throwOnError: false);
			}
			controllerType = _bodyPartControllerType;
			if (controllerType == null)
			{
				return Fail("Blacksmith BodyPartController type was not found", out failureReason);
			}
			return true;
		}

		private static MethodInfo? GetSetupMethod(Type controllerType)
		{
			if ((object)_setupMethod == null)
			{
				_setupMethod = controllerType.GetMethod("Setup", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			}
			return _setupMethod;
		}

		private static MethodInfo? GetFullUpdateMethod(Type controllerType)
		{
			if ((object)_fullUpdateMethod == null)
			{
				_fullUpdateMethod = controllerType.GetMethod("FullUpdate", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			}
			return _fullUpdateMethod;
		}

		private static bool Fail(string reason, out string failureReason)
		{
			failureReason = reason;
			Plugin.HeadHidingDebugLog("Blacksmith head hiding unavailable: " + reason + ".");
			return false;
		}
	}
	internal static class BodyVisibilityController
	{
		private static readonly Dictionary<Renderer, RendererStateSnapshot> OriginalRendererStates = new Dictionary<Renderer, RendererStateSnapshot>();

		private static readonly List<Renderer?> DeadRenderers = new List<Renderer>();

		private static Player? _cachedPlayer;

		private static bool _captured;

		private static readonly string[] HeldItemKeywords = new string[28]
		{
			"hand", "right", "left", "weapon", "sword", "axe", "mace", "hammer", "club", "knife",
			"bow", "arrow", "shield", "torch", "tool", "pickaxe", "hatchet", "battleaxe", "sledge", "spear",
			"atgeir", "crossbow", "buckler", "staff", "wand", "cultivator", "fishing", "itemstand"
		};

		private static readonly string[] HeadKeywords = new string[7] { "head", "hair", "face", "jaw", "eye", "helmet", "helm" };

		internal static void Update(Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if (!Plugin.EnableMod.Value || !FirstPersonState.Active || !Plugin.ForceBodyVisible.Value)
			{
				Reset();
				return;
			}
			if ((Object)(object)_cachedPlayer != (Object)null && (Object)(object)_cachedPlayer != (Object)(object)player)
			{
				Reset();
			}
			_cachedPlayer = player;
			if (!_captured)
			{
				CaptureVisibleBodyRenderers(player);
			}
			ForceCapturedBodyRenderersVisible();
		}

		internal static void Reset()
		{
			RestoreRendererStates();
			OriginalRendererStates.Clear();
			DeadRenderers.Clear();
			_cachedPlayer = null;
			_captured = false;
		}

		internal static void RequestRefresh()
		{
			RestoreRendererStates();
			OriginalRendererStates.Clear();
			DeadRenderers.Clear();
			_captured = false;
		}

		private static void CaptureVisibleBodyRenderers(Player player)
		{
			OriginalRendererStates.Clear();
			Renderer[] componentsInChildren = ((Component)player).GetComponentsInChildren<Renderer>(true);
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if (!((Object)(object)val == (Object)null) && LocalPlayerRendererOwnership.IsLocalPlayerRenderer(player, val) && val.enabled && !LooksLikeHeadRenderer(val))
				{
					OriginalRendererStates[val] = new RendererStateSnapshot(val);
				}
			}
			_captured = true;
			Plugin.DebugLog($"Captured {OriginalRendererStates.Count} visible body renderers for first-person body restore.");
		}

		private static void ForceCapturedBodyRenderersVisible()
		{
			RemoveDestroyedRenderers();
			foreach (Renderer key in OriginalRendererStates.Keys)
			{
				if (!((Object)(object)key == (Object)null) && !((Object)(object)_cachedPlayer == (Object)null) && LocalPlayerRendererOwnership.IsLocalPlayerRenderer(_cachedPlayer, key) && !key.enabled)
				{
					key.enabled = true;
				}
			}
		}

		private static bool LooksLikeHeadRenderer(Renderer renderer)
		{
			string text = (((Object)(object)((Component)renderer).transform != (Object)null) ? RendererScanner.GetPath(((Component)renderer).transform) : string.Empty);
			string value = (((Object)renderer).name + " " + text).ToLowerInvariant();
			if (ContainsAny(value, HeldItemKeywords))
			{
				return false;
			}
			return ContainsAnyHeadKeyword(value);
		}

		private static bool ContainsAny(string value, string[] keywords)
		{
			foreach (string value2 in keywords)
			{
				if (value.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static void RestoreRendererStates()
		{
			if (OriginalRendererStates.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<Renderer, RendererStateSnapshot> originalRendererState in OriginalRendererStates)
			{
				Renderer key = originalRendererState.Key;
				if (!((Object)(object)key == (Object)null))
				{
					originalRendererState.Value.Restore(key);
				}
			}
		}

		private static bool ContainsAnyHeadKeyword(string value)
		{
			string[] headKeywords = HeadKeywords;
			foreach (string keyword in headKeywords)
			{
				if (ContainsKeywordTokenOrSafePrefix(value, keyword))
				{
					return true;
				}
			}
			return false;
		}

		private static bool ContainsKeywordTokenOrSafePrefix(string value, string keyword)
		{
			int num = value.IndexOf(keyword, StringComparison.OrdinalIgnoreCase);
			while (num >= 0)
			{
				bool flag = num == 0 || !char.IsLetterOrDigit(value[num - 1]);
				int num2 = num + keyword.Length;
				bool flag2 = num2 >= value.Length || !char.IsLetterOrDigit(value[num2]);
				if (flag && flag2)
				{
					return true;
				}
				if (flag && keyword.Length >= 5)
				{
					return true;
				}
				num = value.IndexOf(keyword, num2, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static void RemoveDestroyedRenderers()
		{
			DeadRenderers.Clear();
			foreach (Renderer key in OriginalRendererStates.Keys)
			{
				if ((Object)(object)key == (Object)null)
				{
					DeadRenderers.Add(key);
				}
			}
			foreach (Renderer deadRenderer in DeadRenderers)
			{
				if (deadRenderer != null)
				{
					OriginalRendererStates.Remove(deadRenderer);
				}
			}
			DeadRenderers.Clear();
		}
	}
	internal static class DodgeDirectionController
	{
		private const float DodgeQueueBodyLockSuppressSeconds = 0.75f;

		private static Player? _suppressedPlayer;

		private static float _suppressBodyLockUntil;

		internal static void PrepareDodge(Player player, ref Vector3 dodgeDir)
		{
			//IL_002c: 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)
			if (!ShouldHandleFirstPersonDodge(player))
			{
				return;
			}
			if (Plugin.DodgeWhereYouLook.Value)
			{
				if (TryGetFlatLookDirection(player, out var lookDirection))
				{
					dodgeDir = lookDirection;
				}
			}
			else
			{
				_suppressedPlayer = player;
				_suppressBodyLockUntil = Time.unscaledTime + 0.75f;
			}
		}

		internal static bool ShouldSkipBodyYawLock(Player player)
		{
			if (!ShouldHandleFirstPersonDodge(player) || Plugin.DodgeWhereYouLook.Value)
			{
				return false;
			}
			if (((Character)player).InDodge())
			{
				return true;
			}
			if ((Object)(object)_suppressedPlayer == (Object)(object)player && Time.unscaledTime <= _suppressBodyLockUntil)
			{
				return true;
			}
			if ((Object)(object)_suppressedPlayer == (Object)(object)player)
			{
				ClearSuppression();
			}
			return false;
		}

		private static bool ShouldHandleFirstPersonDodge(Player player)
		{
			return (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && Plugin.EnableMod.Value && FirstPersonState.ShouldApplyCamera(player);
		}

		private static bool TryGetFlatLookDirection(Player player, out Vector3 lookDirection)
		{
			//IL_0003: 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_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)
			lookDirection = ((Character)player).GetLookDir();
			lookDirection.y = 0f;
			if (((Vector3)(ref lookDirection)).sqrMagnitude <= 0.0001f && (Object)(object)GameCamera.instance != (Object)null)
			{
				lookDirection = ((Component)GameCamera.instance).transform.forward;
				lookDirection.y = 0f;
			}
			if (((Vector3)(ref lookDirection)).sqrMagnitude <= 0.0001f)
			{
				return false;
			}
			((Vector3)(ref lookDirection)).Normalize();
			return true;
		}

		private static void ClearSuppression()
		{
			_suppressedPlayer = null;
			_suppressBodyLockUntil = 0f;
		}
	}
	[HarmonyPatch(typeof(Player), "Dodge")]
	internal static class PlayerDodgeDirectionPatch
	{
		private static void Prefix(Player __instance, ref Vector3 dodgeDir)
		{
			DodgeDirectionController.PrepareDodge(__instance, ref dodgeDir);
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "SetHelmetEquipped")]
	internal static class VisEquipmentSetHelmetEquippedPatch
	{
		private static void Prefix(VisEquipment __instance)
		{
			HelmetShadowController.CaptureHelmetChangeStart(__instance);
		}

		private static void Postfix(VisEquipment __instance, bool __result)
		{
			HelmetShadowController.CaptureHelmetChangeEnd(__instance, __result);
			if (__result && PlayerEquipmentAccess.IsLocalPlayerVisEquipment(__instance))
			{
				BodyVisibilityController.RequestRefresh();
				HeadVisibilityController.RequestRefresh();
			}
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "SetChestEquipped")]
	internal static class VisEquipmentSetChestEquippedPatch
	{
		private static void Postfix(VisEquipment __instance, bool __result)
		{
			if (__result && PlayerEquipmentAccess.IsLocalPlayerVisEquipment(__instance))
			{
				BodyVisibilityController.RequestRefresh();
				HeadVisibilityController.RequestRefresh();
			}
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "SetShoulderEquipped")]
	internal static class VisEquipmentSetShoulderEquippedPatch
	{
		private static void Postfix(VisEquipment __instance, bool __result)
		{
			if (__result && PlayerEquipmentAccess.IsLocalPlayerVisEquipment(__instance))
			{
				BodyVisibilityController.RequestRefresh();
				HeadVisibilityController.RequestRefresh();
			}
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "SetLegEquipped")]
	internal static class VisEquipmentSetLegEquippedPatch
	{
		private static void Postfix(VisEquipment __instance, bool __result)
		{
			if (__result && PlayerEquipmentAccess.IsLocalPlayerVisEquipment(__instance))
			{
				BodyVisibilityController.RequestRefresh();
				HeadVisibilityController.RequestRefresh();
			}
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "SetModel")]
	internal static class VisEquipmentSetModelPatch
	{
		private static void Postfix(VisEquipment __instance)
		{
			if (PlayerEquipmentAccess.IsLocalPlayerVisEquipment(__instance))
			{
				BodyVisibilityController.RequestRefresh();
				HeadVisibilityController.RequestRefresh();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDestroy")]
	internal static class PlayerOnDestroyVisibilityPatch
	{
		private static void Prefix(Player __instance)
		{
			if ((Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				FirstPersonState.ForceInactive();
			}
		}
	}
	[HarmonyPatch(typeof(GameCamera))]
	[HarmonyPatch("UpdateCamera")]
	internal static class GameCameraUpdatePatch
	{
		private static void Postfix(GameCamera __instance)
		{
			FirstPersonCamera.Update(__instance);
		}
	}
	internal static class FirstPersonCamera
	{
		private const float HeadBobFilterSpeed = 3f;

		private static readonly string[] CameraEffectTypeNameFragments = new string[8] { "PostProcessLayer", "PostProcessVolume", "Bloom", "DepthOfField", "MotionBlur", "ScreenSpaceAmbientOcclusion", "SunShafts", "Vignette" };

		private static readonly FieldInfo? CameraField = AccessTools.Field(typeof(GameCamera), "m_camera");

		private static readonly MethodInfo? IsCrouchingMethod = FindInstanceMethod(typeof(Player), "IsCrouching") ?? FindInstanceMethod(typeof(Character), "IsCrouching");

		private static readonly MethodInfo? InSneakMethod = FindInstanceMethod(typeof(Player), "InSneak") ?? FindInstanceMethod(typeof(Character), "InSneak");

		private static readonly FieldInfo? CrouchField = AccessTools.Field(typeof(Player), "m_crouchToggled") ?? AccessTools.Field(typeof(Player), "m_crouch") ?? AccessTools.Field(typeof(Character), "m_crouch");

		private static Camera? _lastCamera;

		private static Camera? _savedCamera;

		private static Player? _cachedAnchorPlayer;

		private static Transform? _cachedHeadAnchor;

		private static float _originalFov;

		private static float _originalNearClip;

		private static bool _originalUseOcclusionCulling;

		private static bool _savedOriginals;

		private static bool _loggedRenderingState;

		private static float _originalShadowDistance;

		private static int _originalShadowCascades;

		private static readonly Dictionary<Behaviour, bool> CameraEffectStates = new Dictionary<Behaviour, bool>();

		private static Player? _filteredHeadPlayer;

		private static Vector3 _filteredLocalHeadPosition;

		private static bool _hasFilteredHeadPosition;

		private static Player? _attachedCameraPlayer;

		private static Vector3 _attachedLocalCameraPosition;

		private static bool _hasAttachedCameraPosition;

		private static float _currentShoulderPeekOffset;

		private static int _toggledShoulderPeekDirection;

		internal static void Update(GameCamera gameCamera)
		{
			//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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)gameCamera == (Object)null)
			{
				ResetShoulderPeek();
				return;
			}
			Camera val = GetCamera(gameCamera) ?? Camera.main;
			if ((Object)(object)val == (Object)null)
			{
				ResetShoulderPeek();
				return;
			}
			_lastCamera = val;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !FirstPersonState.ShouldApplyCamera(localPlayer))
			{
				RestoreCamera(val);
				ResetHeadBobFilter();
				ResetAttachedCameraLock();
				ResetShoulderPeek();
				RestoreLocalVisibilityForSuppressedCamera();
				return;
			}
			Quaternion rotation = ((Component)gameCamera).transform.rotation;
			bool flag = ShouldLockAttachedCamera(localPlayer);
			if (!flag)
			{
				ResetAttachedCameraLock();
			}
			if (Plugin.LockBodyToCamera.Value && !flag && !DodgeDirectionController.ShouldSkipBodyYawLock(localPlayer))
			{
				LockBodyYawToCamera(localPlayer, rotation);
			}
			SaveOriginalCameraValues(val);
			ApplyFirstPersonCamera(gameCamera, val, localPlayer, rotation, flag);
			LocalPlayerVisibilityOverride.ForceVisible(localPlayer);
			BodyVisibilityController.Update(localPlayer);
			HeadVisibilityController.Update(localPlayer);
		}

		internal static void RestoreLastCamera()
		{
			if ((Object)(object)_lastCamera != (Object)null)
			{
				RestoreCamera(_lastCamera);
			}
			ResetHeadBobFilter();
			ResetAttachedCameraLock();
			ResetShoulderPeek();
			ResetAnchorCache();
		}

		private static Camera? GetCamera(GameCamera gameCamera)
		{
			if (CameraField == null)
			{
				return null;
			}
			object? value = CameraField.GetValue(gameCamera);
			return (Camera?)((value is Camera) ? value : null);
		}

		private static MethodInfo? FindInstanceMethod(Type type, string name)
		{
			return type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static void SaveOriginalCameraValues(Camera camera)
		{
			if (!_savedOriginals || !((Object)(object)_savedCamera == (Object)(object)camera))
			{
				if (_savedOriginals && (Object)(object)_savedCamera != (Object)null)
				{
					RestoreCamera(_savedCamera);
				}
				_savedCamera = camera;
				_originalFov = camera.fieldOfView;
				_originalNearClip = camera.nearClipPlane;
				_originalUseOcclusionCulling = camera.useOcclusionCulling;
				_originalShadowDistance = QualitySettings.shadowDistance;
				_originalShadowCascades = QualitySettings.shadowCascades;
				_savedOriginals = true;
				LogRenderingState();
			}
		}

		private static void LogRenderingState()
		{
			if (!_loggedRenderingState)
			{
				_loggedRenderingState = true;
				Plugin.Log.LogInfo((object)$"First-person rendering state: view distance unchanged; LOD unchanged; originalNearClip={_originalNearClip:0.###}; requestedNearClip={Plugin.NearClip.Value:0.###}; originalShadowDistance={_originalShadowDistance:0.###}; shadowDistanceCap={Plugin.FirstPersonShadowDistance.Value:0.###}; originalShadowCascades={_originalShadowCascades}; shadowCascadesCap={Plugin.FirstPersonShadowCascades.Value}; originalOcclusionCulling={_originalUseOcclusionCulling}; firstPersonOcclusionCulling=false; cameraEffectsDisabled={Plugin.DisableCameraEffects.Value}.");
			}
		}

		private static void RestoreLocalVisibilityForSuppressedCamera()
		{
			if (FirstPersonState.Active)
			{
				BodyVisibilityController.Reset();
				HeadVisibilityController.ForceVisible();
			}
		}

		private static bool ShouldLockAttachedCamera(Player player)
		{
			return Plugin.LockCameraWhileAttached.Value && ((Character)player).IsAttached();
		}

		private static void LockBodyYawToCamera(Player player, Quaternion cameraRotation)
		{
			//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_000c: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = cameraRotation * Vector3.forward;
			val.y = 0f;
			if (!(((Vector3)(ref val)).sqrMagnitude <= 0.0001f))
			{
				Quaternion val2 = Quaternion.LookRotation(((Vector3)(ref val)).normalized, Vector3.up);
				float num = Mathf.Max(0f, Plugin.BodyRotationFollowSpeed.Value);
				((Component)player).transform.rotation = ((num <= 0f) ? val2 : Quaternion.Slerp(((Component)player).transform.rotation, val2, 1f - Mathf.Exp((0f - num) * Time.unscaledDeltaTime)));
			}
		}

		private static void ApplyFirstPersonCamera(GameCamera gameCamera, Camera camera, Player player, Quaternion vanillaCameraRotation, bool lockAttachedCamera)
		{
			//IL_000d: 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_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: 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_00ba: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: 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_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: 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_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: 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)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: 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_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			if (lockAttachedCamera)
			{
				ApplyAttachedCamera(gameCamera, camera, player, vanillaCameraRotation);
				return;
			}
			Transform cameraAnchor = GetCameraAnchor(player);
			bool flag = (Object)(object)cameraAnchor != (Object)null && Plugin.UseHeadTrackedAnchor.Value && (Object)(object)cameraAnchor != (Object)(object)((Character)player).m_eye;
			bool flag2 = IsCrouchingOrSneaking(player);
			Vector3 headBobScaledAnchorPosition = GetHeadBobScaledAnchorPosition(player, cameraAnchor, flag);
			headBobScaledAnchorPosition += Vector3.up * Plugin.CameraVerticalOffset.Value;
			Vector3 val = vanillaCameraRotation * Vector3.forward;
			val.y = 0f;
			if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
			{
				headBobScaledAnchorPosition += ((Vector3)(ref val)).normalized * Plugin.CameraForwardOffset.Value;
			}
			float num = Mathf.Clamp01(Vector3.Dot(vanillaCameraRotation * Vector3.forward, Vector3.down));
			if (num > 0f)
			{
				if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
				{
					headBobScaledAnchorPosition += ((Vector3)(ref val)).normalized * Plugin.DownLookExtraForwardOffset.Value * num;
				}
				headBobScaledAnchorPosition += Vector3.up * Plugin.DownLookExtraVerticalOffset.Value * num;
			}
			if (!flag && flag2)
			{
				headBobScaledAnchorPosition += Vector3.up * Plugin.CrouchVerticalOffset.Value;
			}
			headBobScaledAnchorPosition = ApplyShoulderPeek(headBobScaledAnchorPosition, vanillaCameraRotation);
			ApplyCameraState(gameCamera, camera, headBobScaledAnchorPosition, vanillaCameraRotation);
		}

		private static void ApplyAttachedCamera(GameCamera gameCamera, Camera camera, Player player, Quaternion vanillaCameraRotation)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//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)
			ResetHeadBobFilter();
			Quaternion rotation = ((Component)player).transform.rotation;
			Quaternion limitedAttachedCameraRotation = GetLimitedAttachedCameraRotation(rotation, vanillaCameraRotation);
			Vector3 attachedCameraPosition = GetAttachedCameraPosition(player, rotation);
			attachedCameraPosition = ApplyShoulderPeek(attachedCameraPosition, limitedAttachedCameraRotation);
			ApplyCameraState(gameCamera, camera, attachedCameraPosition, limitedAttachedCameraRotation);
		}

		private static Vector3 ApplyShoulderPeek(Vector3 eyePosition, Quaternion cameraRotation)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: 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_0061: 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)
			if (!Plugin.EnableShoulderPeek.Value)
			{
				ResetShoulderPeek();
				return eyePosition;
			}
			int shoulderPeekDirection = GetShoulderPeekDirection();
			float targetOffset = (float)shoulderPeekDirection * Mathf.Max(0f, Plugin.ShoulderPeekOffset.Value);
			_currentShoulderPeekOffset = SmoothShoulderPeekOffset(targetOffset);
			if (Mathf.Abs(_currentShoulderPeekOffset) <= 0.0001f)
			{
				return eyePosition;
			}
			return eyePosition + cameraRotation * Vector3.right * _currentShoulderPeekOffset;
		}

		private static int GetShoulderPeekDirection()
		{
			//IL_0006: 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_0026: 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)
			bool keyDown = Input.GetKeyDown(Plugin.PeekLeftKey.Value);
			bool keyDown2 = Input.GetKeyDown(Plugin.PeekRightKey.Value);
			bool key = Input.GetKey(Plugin.PeekLeftKey.Value);
			bool key2 = Input.GetKey(Plugin.PeekRightKey.Value);
			if (Plugin.ShoulderPeekMode.Value == ShoulderPeekMode.Toggle)
			{
				if (keyDown)
				{
					_toggledShoulderPeekDirection = ((_toggledShoulderPeekDirection != -1) ? (-1) : 0);
				}
				if (keyDown2)
				{
					_toggledShoulderPeekDirection = ((_toggledShoulderPeekDirection != 1) ? 1 : 0);
				}
				return _toggledShoulderPeekDirection;
			}
			if (key && !key2)
			{
				return -1;
			}
			if (key2 && !key)
			{
				return 1;
			}
			return 0;
		}

		private static float SmoothShoulderPeekOffset(float targetOffset)
		{
			float num = Mathf.Max(0f, Plugin.ShoulderPeekSpeed.Value);
			if (num <= 0f)
			{
				return targetOffset;
			}
			float num2 = Mathf.Min(Time.unscaledDeltaTime, 0.05f);
			float num3 = 1f - Mathf.Exp((0f - num) * num2);
			return Mathf.Lerp(_currentShoulderPeekOffset, targetOffset, num3);
		}

		private static void ResetShoulderPeek()
		{
			_toggledShoulderPeekDirection = 0;
			_currentShoulderPeekOffset = 0f;
		}

		private static Vector3 GetAttachedCameraPosition(Player player, Quaternion bodyRotation)
		{
			//IL_0056: 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_005c: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: 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_0077: 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_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: 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_002a: 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_0030: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_attachedCameraPlayer != (Object)(object)player)
			{
				ResetAttachedCameraLock();
			}
			if (!_hasAttachedCameraPosition)
			{
				_attachedCameraPlayer = player;
				_attachedLocalCameraPosition = CaptureAttachedLocalCameraPosition(player, bodyRotation);
				_hasAttachedCameraPosition = true;
				Plugin.DebugLog($"Captured attached camera local position: {_attachedLocalCameraPosition}");
			}
			Vector3 attachedLocalCameraPosition = _attachedLocalCameraPosition;
			attachedLocalCameraPosition += Vector3.up * Plugin.AttachedCameraExtraVerticalOffset.Value;
			attachedLocalCameraPosition += Vector3.forward * Plugin.AttachedCameraExtraForwardOffset.Value;
			return ((Component)player).transform.TransformPoint(attachedLocalCameraPosition);
		}

		private static Vector3 CaptureAttachedLocalCameraPosition(Player player, Quaternion bodyRotation)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: 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_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_0067: 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_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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_004d: 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_0066: 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)
			Transform cameraAnchor = GetCameraAnchor(player);
			Vector3 val = (((Object)(object)cameraAnchor != (Object)null) ? cameraAnchor.position : GetFallbackEyePosition(player));
			Vector3 val2 = bodyRotation * Vector3.forward;
			val2.y = 0f;
			if (((Vector3)(ref val2)).sqrMagnitude > 0.0001f)
			{
				val += ((Vector3)(ref val2)).normalized * Plugin.CameraForwardOffset.Value;
			}
			val += Vector3.up * Plugin.CameraVerticalOffset.Value;
			return ((Component)player).transform.InverseTransformPoint(val);
		}

		private static Vector3 GetFallbackEyePosition(Player player)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)((Character)player).m_eye != (Object)null)
			{
				return ((Character)player).m_eye.position;
			}
			return ((Component)player).transform.position + Vector3.up * 1.6f;
		}

		private static Quaternion GetLimitedAttachedCameraRotation(Quaternion bodyRotation, Quaternion vanillaCameraRotation)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: 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_001a: 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_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			Quaternion val = Quaternion.Inverse(bodyRotation) * vanillaCameraRotation;
			Vector3 val2 = NormalizeEuler(((Quaternion)(ref val)).eulerAngles);
			float num = Mathf.Clamp(Plugin.AttachedCameraMaxYaw.Value, 0f, 180f);
			float num2 = Mathf.Clamp(Plugin.AttachedCameraMaxPitch.Value, 1f, 89f);
			float num3 = Mathf.Clamp(val2.y, 0f - num, num);
			float num4 = Mathf.Clamp(val2.x, 0f - num2, num2);
			return bodyRotation * Quaternion.Euler(num4, num3, 0f);
		}

		private static Vector3 NormalizeEuler(Vector3 euler)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			return new Vector3(NormalizeAngle(euler.x), NormalizeAngle(euler.y), NormalizeAngle(euler.z));
		}

		private static float NormalizeAngle(float angle)
		{
			angle %= 360f;
			if (angle > 180f)
			{
				angle -= 360f;
			}
			else if (angle < -180f)
			{
				angle += 360f;
			}
			return angle;
		}

		private static Vector3 GetHeadBobScaledAnchorPosition(Player player, Transform? anchor, bool hasHeadAnchor)
		{
			//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_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_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_010e: 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_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_00de: 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_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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_00da: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)anchor == (Object)null)
			{
				ResetHeadBobFilter();
				return GetFallbackEyePosition(player);
			}
			if (!hasHeadAnchor)
			{
				ResetHeadBobFilter();
				return anchor.position;
			}
			float num = Mathf.Clamp01(Plugin.HeadBobAmount.Value);
			Vector3 val = ((Component)player).transform.InverseTransformPoint(anchor.position);
			if ((Object)(object)_filteredHeadPlayer != (Object)(object)player)
			{
				ResetHeadBobFilter();
			}
			if (!_hasFilteredHeadPosition)
			{
				_filteredHeadPlayer = player;
				_filteredLocalHeadPosition = val;
				_hasFilteredHeadPosition = true;
			}
			float num2 = 1f - Mathf.Exp(-3f * Time.unscaledDeltaTime);
			_filteredLocalHeadPosition = Vector3.Lerp(_filteredLocalHeadPosition, val, num2);
			if (num >= 0.999f)
			{
				return anchor.position;
			}
			Vector3 val2 = val - _filteredLocalHeadPosition;
			Vector3 val3 = _filteredLocalHeadPosition + val2 * num;
			return ((Component)player).transform.TransformPoint(val3);
		}

		private static Transform GetCameraAnchor(Player player)
		{
			if (!Plugin.UseHeadTrackedAnchor.Value)
			{
				return ((Object)(object)((Character)player).m_eye != (Object)null) ? ((Character)player).m_eye : ((Component)player).transform;
			}
			if ((Object)(object)_cachedAnchorPlayer == (Object)(object)player && (Object)(object)_cachedHeadAnchor != (Object)null)
			{
				return _cachedHeadAnchor;
			}
			_cachedAnchorPlayer = player;
			_cachedHeadAnchor = FindBestHeadAnchor(player);
			if ((Object)(object)_cachedHeadAnchor != (Object)null)
			{
				Plugin.DebugLog("Using head-tracked camera anchor: " + RendererScanner.GetPath(_cachedHeadAnchor));
				return _cachedHeadAnchor;
			}
			return ((Object)(object)((Character)player).m_eye != (Object)null) ? ((Character)player).m_eye : ((Component)player).transform;
		}

		private static Transform? FindBestHeadAnchor(Player player)
		{
			Transform[] componentsInChildren = ((Component)player).GetComponentsInChildren<Transform>(true);
			Transform val = null;
			int num = int.MinValue;
			Transform[] array = componentsInChildren;
			foreach (Transform val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					int num2 = ScoreHeadAnchor(val2);
					if (num2 > num)
					{
						num = num2;
						val = val2;
					}
				}
			}
			return (num > 0) ? val : null;
		}

		private static int ScoreHeadAnchor(Transform transform)
		{
			string text = ((Object)transform).name.ToLowerInvariant();
			string text2 = RendererScanner.GetPath(transform).ToLowerInvariant();
			string text3 = text + " " + text2;
			if (!text3.Contains("head"))
			{
				return int.MinValue;
			}
			if (text3.Contains("helmet") || text3.Contains("helm") || text3.Contains("hair") || text3.Contains("beard"))
			{
				return int.MinValue;
			}
			int num = 10;
			if (text == "head")
			{
				num += 100;
			}
			if (text.Contains("head"))
			{
				num += 40;
			}
			if (text2.Contains("neck"))
			{
				num += 10;
			}
			if ((Object)(object)((Component)transform).GetComponent<Renderer>() != (Object)null)
			{
				num -= 50;
			}
			return num;
		}

		private static bool IsCrouchingOrSneaking(Player player)
		{
			if (TryInvokeBool(player, IsCrouchingMethod, out var value) && value)
			{
				return true;
			}
			if (TryInvokeBool(player, InSneakMethod, out var value2) && value2)
			{
				return true;
			}
			if (CrouchField != null && CrouchField.GetValue(player) is bool result)
			{
				return result;
			}
			return false;
		}

		private static bool TryInvokeBool(Player player, MethodInfo? method, out bool value)
		{
			value = false;
			if (method == null)
			{
				return false;
			}
			if (!(method.Invoke(player, null) is bool flag) || 1 == 0)
			{
				return false;
			}
			value = flag;
			return true;
		}

		private static void ApplyCameraState(GameCamera gameCamera, Camera camera, Vector3 desiredPosition, Quaternion desiredRotation)
		{
			//IL_0007: 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_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			((Component)gameCamera).transform.position = desiredPosition;
			((Component)gameCamera).transform.rotation = desiredRotation;
			if ((Object)(object)((Component)camera).transform != (Object)(object)((Component)gameCamera).transform)
			{
				((Component)camera).transform.position = desiredPosition;
				((Component)camera).transform.rotation = desiredRotation;
			}
			if (Plugin.UseCustomFov.Value)
			{
				camera.fieldOfView = Mathf.Clamp(Plugin.Fov.Value, 40f, 120f);
			}
			else
			{
				camera.fieldOfView = _originalFov;
			}
			ApplyFirstPersonRendering(camera);
		}

		private static void ApplyFirstPersonRendering(Camera camera)
		{
			camera.nearClipPlane = Mathf.Clamp(Plugin.NearClip.Value, 0.005f, 0.5f);
			camera.useOcclusionCulling = false;
			ApplyShadowOverrides();
			ApplyCameraEffectOverrides(camera);
		}

		private static void ApplyShadowOverrides()
		{
			if (Plugin.FirstPersonShadowDistance.Value >= 0f)
			{
				float num = Mathf.Max(0f, Plugin.FirstPersonShadowDistance.Value);
				QualitySettings.shadowDistance = Mathf.Min(_originalShadowDistance, num);
			}
			if (Plugin.FirstPersonShadowCascades.Value >= 0)
			{
				int num2 = NormalizeShadowCascades(Plugin.FirstPersonShadowCascades.Value);
				QualitySettings.shadowCascades = Mathf.Min(_originalShadowCascades, num2);
			}
		}

		private static int NormalizeShadowCascades(int value)
		{
			if (value <= 0)
			{
				return 0;
			}
			if (value <= 2)
			{
				return 2;
			}
			return 4;
		}

		private static void ApplyCameraEffectOverrides(Camera camera)
		{
			if (!Plugin.DisableCameraEffects.Value)
			{
				RestoreCameraEffectStates();
				return;
			}
			Behaviour[] components = ((Component)camera).GetComponents<Behaviour>();
			foreach (Behaviour val in components)
			{
				if (!((Object)(object)val == (Object)null) && IsCameraEffectComponent(val))
				{
					if (!CameraEffectStates.ContainsKey(val))
					{
						CameraEffectStates.Add(val, val.enabled);
					}
					val.enabled = false;
				}
			}
		}

		private static bool IsCameraEffectComponent(Behaviour component)
		{
			Type type = ((object)component).GetType();
			string text = type.FullName ?? type.Name;
			if (string.Equals(text, "UnityEngine.Rendering.Volume", StringComparison.Ordinal))
			{
				return true;
			}
			string[] cameraEffectTypeNameFragments = CameraEffectTypeNameFragments;
			foreach (string value in cameraEffectTypeNameFragments)
			{
				if (text.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static void RestoreCamera(Camera camera)
		{
			if (_savedOriginals)
			{
				Camera val = (Camera)(((Object)(object)_savedCamera != (Object)null) ? ((object)_savedCamera) : ((object)camera));
				if ((Object)(object)val != (Object)null)
				{
					val.fieldOfView = _originalFov;
					val.nearClipPlane = _originalNearClip;
					val.useOcclusionCulling = _originalUseOcclusionCulling;
				}
				QualitySettings.shadowDistance = _originalShadowDistance;
				QualitySettings.shadowCascades = _originalShadowCascades;
				RestoreCameraEffectStates();
				_savedCamera = null;
				_savedOriginals = false;
			}
		}

		private static void RestoreCameraEffectStates()
		{
			foreach (KeyValuePair<Behaviour, bool> cameraEffectState in CameraEffectStates)
			{
				if ((Object)(object)cameraEffectState.Key != (Object)null)
				{
					cameraEffectState.Key.enabled = cameraEffectState.Value;
				}
			}
			CameraEffectStates.Clear();
		}

		private static void ResetHeadBobFilter()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			_filteredHeadPlayer = null;
			_hasFilteredHeadPosition = false;
			_filteredLocalHeadPosition = Vector3.zero;
		}

		private static void ResetAttachedCameraLock()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			_attachedCameraPlayer = null;
			_hasAttachedCameraPosition = false;
			_attachedLocalCameraPosition = Vector3.zero;
		}

		private static void ResetAnchorCache()
		{
			_cachedAnchorPlayer = null;
			_cachedHeadAnchor = null;
		}
	}
	internal static class FirstPersonState
	{
		private static Player? _cachedPlayer;

		private static bool _defaultApplied;

		internal static bool Active { get; private set; }

		internal static void Update(Player player)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			if ((Object)(object)_cachedPlayer != (Object)null && (Object)(object)_cachedPlayer != (Object)(object)player)
			{
				ForceInactive();
				_defaultApplied = false;
			}
			_cachedPlayer = player;
			if (!Plugin.EnableMod.Value)
			{
				SetActive(active: false);
				return;
			}
			if (!IsSafePlayerState(player))
			{
				SetActive(active: false);
				return;
			}
			if (!_defaultApplied)
			{
				_defaultApplied = true;
				if (Plugin.DefaultToFirstPerson.Value)
				{
					SetActive(active: true);
				}
			}
			if (CanReadToggleInput() && Input.GetKeyDown(Plugin.ToggleFirstPersonKey.Value))
			{
				SetActive(!Active);
			}
			if (Active && Plugin.LogRendererNames.Value)
			{
				RendererScanner.LogRenderersOnce(player);
			}
		}

		internal static bool ShouldApplyCamera(Player player)
		{
			return Plugin.EnableMod.Value && Active && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && IsSafePlayerState(player) && CanOverrideCurrentCameraContext();
		}

		internal static void ForceInactive()
		{
			SetActive(active: false);
			_cachedPlayer = null;
			_defaultApplied = false;
		}

		private static void SetActive(bool active)
		{
			if (Active != active)
			{
				Active = active;
				if (!active)
				{
					HeadVisibilityController.ForceVisible();
					FirstPersonCamera.RestoreLastCamera();
					RendererScanner.ResetLogState();
				}
				Plugin.Log.LogInfo((object)(active ? "Immersive first person active." : "Immersive first person inactive."));
			}
		}

		private static bool CanReadToggleInput()
		{
			return !IsAnyBlockingUiVisible();
		}

		private static bool CanOverrideCurrentCameraContext()
		{
			if (!Plugin.OverrideForcedThirdPerson.Value)
			{
				return !IsAnyBlockingUiVisible() && !IsPlayerAttached();
			}
			if (IsHardCameraBlockVisible())
			{
				return false;
			}
			return true;
		}

		private static bool IsAnyBlockingUiVisible()
		{
			return IsInputBlockingUiVisible() || IsHardCameraBlockVisible();
		}

		private static bool IsInputBlockingUiVisible()
		{
			if (InventoryGui.IsVisible())
			{
				return true;
			}
			return false;
		}

		private static bool IsHardCameraBlockVisible()
		{
			if (Menu.IsVisible())
			{
				return true;
			}
			if (Minimap.IsOpen())
			{
				return true;
			}
			return false;
		}

		private static bool IsSafePlayerState(Player player)
		{
			if (((Character)player).IsDead())
			{
				return false;
			}
			return true;
		}

		private static bool IsPlayerAttached()
		{
			Player localPlayer = Player.m_localPlayer;
			return (Object)(object)localPlayer != (Object)null && ((Character)localPlayer).IsAttached();
		}
	}
	internal static class HeadVisibilityController
	{
		private readonly struct TransformState
		{
			internal readonly Vector3 LocalPosition;

			internal readonly Vector3 LocalScale;

			internal TransformState(Transform transform)
			{
				//IL_0003: 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_000f: 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)
				LocalPosition = transform.localPosition;
				LocalScale = transform.localScale;
			}
		}

		private const float HeadSlotClipRadius = 0.55f;

		private const float MaxHeadSlotExtent = 1.15f;

		private const float MinHeadSlotHeight = 0.9f;

		private const float MaxHeadSlotHeight = 2.4f;

		private const float MaxHeadSlotHorizontalDistance = 0.75f;

		private const float HeadShrinkScale = 0.001f;

		private const float HeadSlotCompensationScale = 999.99994f;

		private static readonly Vector3 HeadShrinkVector = Vector3.one * 0.001f;

		private static readonly Dictionary<Renderer, RendererStateSnapshot> OriginalRendererStates = new Dictionary<Renderer, RendererStateSnapshot>();

		private static readonly Dictionary<Transform, Vector3> OriginalBoneScales = new Dictionary<Transform, Vector3>();

		private static readonly Dictionary<Transform, TransformState> OriginalHeadSlotStates = new Dictionary<Transform, TransformState>();

		private static readonly HashSet<Renderer> DesiredRenderers = new HashSet<Renderer>();

		private static readonly List<Transform> CachedHeadBones = new List<Transform>();

		private static readonly List<Renderer> ManagedHelmetRenderers = new List<Renderer>();

		private static readonly List<Renderer?> RenderersToRemove = new List<Renderer>();

		private static readonly List<Transform?> BonesToRemove = new List<Transform>();

		private static readonly List<Transform?> HeadSlotTransformsToRemove = new List<Transform>();

		private static Player? _cachedPlayer;

		private static bool _active;

		private static bool _headBoneCacheInitialized;

		private static float _nextRefreshTime;

		private static string? _lastFallbackReason;

		private static readonly string[] HeadSlotKeywords = new string[18]
		{
			"head", "hair", "beard", "face", "jaw", "eye", "brow", "mouth", "nose", "teeth",
			"helmet", "helm", "hat", "hood", "circlet", "crown", "headgear", "padded"
		};

		private static readonly string[] HeadSlotHierarchyKeywords = new string[8] { "attach_helmet", "attach_helm", "attach_head", "helmet", "helm", "hair", "beard", "headgear" };

		private static readonly string[] HeadBoneRejectKeywords = new string[10] { "helmet", "helm", "hat", "hood", "hair", "beard", "circlet", "crown", "headgear", "padded" };

		private static readonly string[] EquipmentSkeletonKeywords = new string[6] { "attach_skin", "attach_armor", "attach_helmet", "attach_helm", "attach_shoulder", "attach_back" };

		private static readonly string[] HeldItemKeywords = new string[28]
		{
			"hand", "right", "left", "weapon", "sword", "axe", "mace", "hammer", "club", "knife",
			"bow", "arrow", "shield", "torch", "tool", "pickaxe", "hatchet", "battleaxe", "sledge", "spear",
			"atgeir", "crossbow", "buckler", "staff", "wand", "cultivator", "fishing", "itemstand"
		};

		private static readonly string[] BodyKeywords = new string[14]
		{
			"body", "torso", "chest", "arm", "leg", "foot", "feet", "hand", "player", "character",
			"human", "male", "female", "skin"
		};

		internal static void Update(Player player)
		{
			if (!IsLocalPlayer(player))
			{
				ForceVisible();
				return;
			}
			bool shouldHide = Plugin.EnableMod.Value && FirstPersonState.Active && Plugin.HideHead.Value;
			Apply(player, shouldHide);
		}

		internal static void ForceVisible()
		{
			BlacksmithHeadHider.Restore();
			HelmetShadowController.Restore();
			RestoreHeadSlotStates();
			RestoreBoneScales();
			RestoreRendererStates();
			ResetCache();
		}

		internal static void RequestRefresh()
		{
			_nextRefreshTime = 0f;
			_headBoneCacheInitialized = false;
			BlacksmithHeadHider.RequestRefresh();
			HelmetShadowController.RequestRefresh();
		}

		private static void Apply(Player player, bool shouldHide)
		{
			if (!IsLocalPlayer(player))
			{
				ForceVisible();
				return;
			}
			if ((Object)(object)_cachedPlayer != (Object)null && (Object)(object)_cachedPlayer != (Object)(object)player)
			{
				ForceVisible();
			}
			_cachedPlayer = player;
			if (!shouldHide)
			{
				ForceVisible();
				return;
			}
			if (!_active)
			{
				OriginalRendererStates.Clear();
				OriginalBoneScales.Clear();
				OriginalHeadSlotStates.Clear();
				DesiredRenderers.Clear();
				CachedHeadBones.Clear();
				_active = true;
				_nextRefreshTime = 0f;
			}
			HelmetShadowController.Update(player, shouldHide: true);
			bool flag = Time.unscaledTime >= _nextRefreshTime;
			if (flag)
			{
				RefreshRendererCache(player);
				_nextRefreshTime = Time.unscaledTime + Mathf.Clamp(Plugin.VisibilityRefreshInterval.Value, 0.1f, 10f);
			}
			HideCachedRenderersShadowsOnly();
			HelmetShadowController.Update(player, shouldHide: true);
			if (ShouldUseBlacksmith(player, out string fallbackReason))
			{
				RestoreHeadSlotStates();
				RestoreBoneScales();
				_lastFallbackReason = null;
				return;
			}
			if (!ShouldUseShrinkFallback(fallbackReason))
			{
				RestoreHeadSlotStates();
				RestoreBoneScales();
				return;
			}
			BlacksmithHeadHider.Restore();
			LogFallbackUse(fallbackReason);
			if (flag || !_headBoneCacheInitialized)
			{
				RefreshHeadBoneCache(player);
			}
			ApplyCachedHeadBoneScale();
			CompensateHeadSlotTransforms();
		}

		private static bool ShouldUseBlacksmith(Player player, out string fallbackReason)
		{
			fallbackReason = string.Empty;
			if (Plugin.HeadHidingMode.Value == HeadHidingMode.ShrinkFallback)
			{
				fallbackReason = "ShrinkFallback mode is selected";
				return false;
			}
			if (BlacksmithHeadHider.TryApply(player, out fallbackReason))
			{
				return true;
			}
			if (Plugin.HeadHidingMode.Value == HeadHidingMode.BlacksmithTools)
			{
				Plugin.HeadHidingDebugLog("BlacksmithTools mode did not hide the head: " + fallbackReason + ".");
				return false;
			}
			return false;
		}

		private static bool ShouldUseShrinkFallback(string fallbackReason)
		{
			return Plugin.HeadHidingMode.Value == HeadHidingMode.ShrinkFallback || (Plugin.HeadHidingMode.Value == HeadHidingMode.Auto && !string.IsNullOrEmpty(fallbackReason));
		}

		private static void LogFallbackUse(string fallbackReason)
		{
			string text = (string.IsNullOrEmpty(fallbackReason) ? "Blacksmith head hiding was unavailable" : fallbackReason);
			if (!string.Equals(_lastFallbackReason, text, StringComparison.Ordinal))
			{
				_lastFallbackReason = text;
				Plugin.Log.LogWarning((object)("Using first-person head shrink fallback because " + text + "."));
			}
		}

		private static void RefreshRendererCache(Player player)
		{
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			RestoreHeadSlotStates();
			RemoveDestroyedRenderers();
			RemoveDestroyedHeadSlotTransforms();
			DesiredRenderers.Clear();
			Renderer[] componentsInChildren = ((Component)player).GetComponentsInChildren<Renderer>(true);
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if (!((Object)(object)val == (Object)null) && IsRendererOwnedByPlayer(player, val) && !HelmetShadowController.IsManagedRenderer(val) && ShouldHideHeadSlotRenderer(player, val))
				{
					DesiredRenderers.Add(val);
				}
			}
			RenderersToRemove.Clear();
			foreach (KeyValuePair<Renderer, RendererStateSnapshot> originalRendererState in OriginalRendererStates)
			{
				Renderer key = originalRendererState.Key;
				if ((Object)(object)key == (Object)null || !DesiredRenderers.Contains(key))
				{
					RenderersToRemove.Add(key);
				}
			}
			foreach (Renderer item in RenderersToRemove)
			{
				if (item != null)
				{
					RestoreRenderer(item);
					OriginalRendererStates.Remove(item);
				}
			}
			RenderersToRemove.Clear();
			foreach (Renderer desiredRenderer in DesiredRenderers)
			{
				if (!OriginalRendererStates.ContainsKey(desiredRenderer))
				{
					OriginalRendererStates.Add(desiredRenderer, new RendererStateSnapshot(desiredRenderer));
					string arg = BuildRendererDescriptor(desiredRenderer);
					Bounds bounds = desiredRenderer.bounds;
					Plugin.DebugLog($"First-person head-slot visibility matched renderer: {arg} | bounds={((Bounds)(ref bounds)).size}");
				}
			}
		}

		private static bool ShouldHideHeadSlotRenderer(Player player, Renderer renderer)
		{
			if (!IsRendererOwnedByPlayer(player, renderer))
			{
				return false;
			}
			string text = BuildRendererDescriptor(renderer);
			if (LooksLikeHeldItem(text))
			{
				return false;
			}
			if (LooksLikeFullBodyRenderer(renderer, text))
			{
				return false;
			}
			bool flag = ContainsAnyHeadSlotKeyword(text);
			bool flag2 = LooksLikeHeadSlotHierarchy(renderer, text);
			bool flag3 = IsSmallRendererNearHeadOrCamera(player, renderer);
			if (flag2 && (flag || flag3))
			{
				return true;
			}
			return flag && flag3;
		}

		private static bool IsSmallRendererNearHeadOrCamera(Player player, Renderer renderer)
		{
			//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_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//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_0060: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: 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)
			Bounds bounds = renderer.bounds;
			Vector3 size = ((Bounds)(ref bounds)).size;
			float num = Mathf.Max(new float[3] { size.x, size.y, size.z });
			if (num > 1.15f)
			{
				return false;
			}
			Vector3 val = ((Component)player).transform.InverseTransformPoint(((Bounds)(ref bounds)).center);
			float y = val.y;
			if (y < 0.9f || y > 2.4f)
			{
				return false;
			}
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(val.x, val.z);
			if (((Vector2)(ref val2)).magnitude > 0.75f)
			{
				return false;
			}
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				return false;
			}
			Vector3 val3 = ((Bounds)(ref bounds)).ClosestPoint(((Component)main).transform.position);
			return Vector3.Distance(val3, ((Component)main).transform.position) <= 0.55f;
		}

		private static string BuildRendererDescriptor(Renderer renderer)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append(((Object)renderer).name).Append(' ');
			if ((Object)(object)((Component)renderer).transform != (Object)null)
			{
				stringBuilder.Append(RendererScanner.GetPath(((Component)renderer).transform)).Append(' ');
			}
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
			if (val != null)
			{
				if ((Object)(object)val.sharedMesh != (Object)null)
				{
					stringBuilder.Append(((Object)val.sharedMesh).name).Append(' ');
				}
				if ((Object)(object)val.rootBone != (Object)null)
				{
					stringBuilder.Append(RendererScanner.GetPath(val.rootBone)).Append(' ');
				}
			}
			Material[] sharedMaterials = renderer.sharedMaterials;
			if (sharedMaterials != null)
			{
				Material[] array = sharedMaterials;
				foreach (Material val2 in array)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						stringBuilder.Append(((Object)val2).name).Append(' ');
					}
				}
			}
			return stringBuilder.ToString().ToLowerInvariant();
		}

		private static bool LooksLikeHeadSlotHierarchy(Renderer renderer, string descriptor)
		{
			if ((Object)(object)renderer == (Object)null)
			{
				return false;
			}
			if (ContainsAny(descriptor, HeadSlotHierarchyKeywords))
			{
				return true;
			}
			if (IsTransformUnderLikelyHeadBone(((Component)renderer).transform, includeSelf: false))
			{
				return true;
			}
			SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((renderer is SkinnedMeshRenderer) ? renderer : null);
			if (val != null)
			{
				if (IsTransformUnderLikelyHeadBone(val.rootBone, includeSelf: true))
				{
					return true;
				}
				Transform[] bones = val.bones;
				if (bones != null)
				{
					Transform[] array = bones;
					foreach (Transform transform in array)
					{
						if (IsTransformUnderLikelyHeadBone(transform, includeSelf: true))
						{
							return true;
						}
					}
				}
			}
			return false;
		}

		private static bool IsTransformUnderLikelyHeadBone(Transform? transform, bool includeSelf)
		{
			Transform val = (Transform)(includeSelf ? ((object)transform) : ((object)((transform != null) ? transform.parent : null)));
			while ((Object)(object)val != (Object)null)
			{
				if (IsLikelyCharacterHeadBoneName(((Object)val).name))
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}

		private static bool IsLikelyCharacterHeadBoneName(string name)
		{
			if (name.Equals("head", StringComparison.OrdinalIgnoreCase) || name.Equals("bip_head", StringComparison.OrdinalIgnoreCase) || name.Equals("bip01 head", StringComparison.OrdinalIgnoreCase) || name.Equals("bip01head", StringComparison.OrdinalIgnoreCase) || name.Equals("bip001 head", StringComparison.OrdinalIgnoreCase) || name.Equals("bip001head", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			return name.Length > 4 && name.StartsWith("head", StringComparison.OrdinalIgnoreCase) && char.IsDigit(name[4]);
		}

		private static bool LooksLikeHeldItem(string descriptor)
		{
			return ContainsAny(descriptor, HeldItemKeywords);
		}

		private static bool LooksLikeFullBodyRenderer(Renderer renderer, string descriptor)
		{
			//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_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			Bounds bounds = renderer.bounds;
			Vector3 size = ((Bounds)(ref bounds)).size;
			float num = Mathf.Max(new float[3] { size.x, size.y, size.z });
			bool flag = ContainsAny(descriptor, BodyKeywords) && !ContainsAnyHeadSlotKeyword(descriptor);
			bool flag2 = size.y > 1.2f && num > 1.4f;
			return flag || flag2;
		}

		private static bool ContainsAny(string value, string[] keywords)
		{
			foreach (string value2 in keywords)
			{
				if (value.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static bool ContainsAnyHeadSlotKeyword(string value)
		{
			string[] headSlotKeywords = HeadSlotKeywords;
			foreach (string keyword in headSlotKeywords)
			{
				if (ContainsKeywordTokenOrSafePrefix(value, keyword))
				{
					return true;
				}
			}
			return false;
		}

		private static bool ContainsAnyHeadBoneRejectKeyword(string value)
		{
			string[] headBoneRejectKeywords = HeadBoneRejectKeywords;
			foreach (string keyword in headBoneRejectKeywords)
			{
				if (ContainsKeywordTokenOrSafePrefix(value, keyword))
				{
					return true;
				}
			}
			return false;
		}

		private static bool ContainsKeywordTokenOrSafePrefix(string value, string keyword)
		{
			int num = value.IndexOf(keyword, StringComparison.OrdinalIgnoreCase);
			while (num >= 0)
			{
				bool flag = num == 0 || !char.IsLetterOrDigit(value[num - 1]);
				int num2 = num + keyword.Length;
				bool flag2 = num2 >= value.Length || !char.IsLetterOrDigit(value[num2]);
				if (flag && flag2)
				{
					return true;
				}
				if (flag && keyword.Length >= 5)
				{
					return true;
				}
				num = value.IndexOf(keyword, num2, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static void HideCachedRenderersShadowsOnly()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Invalid comparison between Unknown and I4
			foreach (Renderer key in OriginalRendererStates.Keys)
			{
				if (!((Object)(object)key == (Object)null) && IsCachedLocalRenderer(key))
				{
					if (!key.enabled)
					{
						key.enabled = true;
					}
					if ((int)key.shadowCastingMode != 3)
					{
						key.shadowCastingMode = (ShadowCastingMode)3;
					}
				}
			}
		}

		private static void RefreshHeadBoneCache(Player player)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			RemoveDestroyedBones();
			_headBoneCacheInitialized = true;
			Transform[] componentsInChildren = ((Component)player).GetComponentsInChildren<Transform>(true);
			Transform[] array = componentsInChildren;
			foreach (Transform val in array)
			{
				if (!((Object)(object)val == (Object)null) && IsHeadBone(val) && IsTransformOwnedByPlayer(player, val) && !OriginalBoneScales.ContainsKey(val))
				{
					OriginalBoneScales.Add(val, val.localScale);
					CachedHeadBones.Add(val);
					Plugin.DebugLog("Shrinking local head bone for first-person view: " + RendererScanner.GetPath(val));
				}
			}
		}

		private static void ApplyCachedHeadBoneScale()
		{
			//IL_003b: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			RemoveDestroyedBones();
			foreach (Transform cachedHeadBone in CachedHeadBones)
			{
				if (!((Object)(object)cachedHeadBone == (Object)null) && IsCachedLocalTransform(cachedHeadBone) && cachedHeadBone.localScale != HeadShrinkVector)
				{
					cachedHeadBone.localScale = HeadShrinkVector;
				}
			}
		}

		private static bool IsHeadBone(Transform transform)
		{
			string value = (((Object)transform).name + " " + RendererScanner.GetPath(transform)).ToLowerInvariant();
			if (ContainsAny(value, EquipmentSkeletonKeywords))
			{
				return false;
			}
			if (ContainsAny(value, HeldItemKeywords))
			{
				return false;
			}
			if (ContainsAnyHeadBoneRejectKeyword(value))
			{
				return false;
			}
			string name = ((Object)transform).name;
			return IsLikelyCharacterHeadBoneName(name) || ContainsKeywordTokenOrSafePrefix(name, "head");
		}

		private static void CompensateHeadSlotTransforms()
		{
			foreach (Renderer key in OriginalRendererStates.Keys)
			{
				if (!((Object)(object)key == (Object)null) && IsCachedLocalRenderer(key))
				{
					CompensateTransformIfUnderShrunkHead(((Component)key).transform);
					SkinnedMeshRenderer val = (SkinnedMeshRenderer)(object)((key is SkinnedMeshRenderer) ? key : null);
					if (val != null && (Object)(object)val.rootBone != (Object)null)
					{
						CompensateTransformIfUnderShrunkHead(val.rootBone);
					}
				}
			}
			HelmetShadowController.CopyManagedRenderers(ManagedHelmetRenderers);
			foreach (Renderer managedHelmetRenderer in ManagedHelmetRenderers)
			{
				if (!((Object)(object)managedHelmetRenderer == (Object)null))
				{
					CompensateTransformIfUnderShrunkHead(((Component)managedHelmetRenderer).transform);
					SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((managedHelmetRenderer is SkinnedMeshRenderer) ? managedHelmetRenderer : null);
					if (val2 != null && (Object)(object)val2.rootBone != (Object)null)
					{
						CompensateTransformIfUnderShrunkHead(val2.rootBone);
					}
				}
			}
			RemoveDestroyedHeadSlotTransforms();
		}

		private static void CompensateTransformIfUnderShrunkHead(Transform transform)
		{
			if (IsCachedLocalTransform(transform))
			{
				Transform val = FindCompensationRootUnderShrunkHead(transform);
				if (!((Object)(object)val == (Object)null))
				{
					CompensateHeadSlotTransform(val, compensatePosition: true, "position and scale");
				}
			}
		}

		private static void CompensateHeadSlotTransform(Transform transform, bool compensatePosition, string mode)
		{
			//IL_006f: 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_0067: 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_0080: 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_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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_00a3: 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)
			if (IsCachedLocalTransform(transform))
			{
				if (!OriginalHeadSlotStates.ContainsKey(transform))
				{
					OriginalHeadSlotStates.Add(transform, new TransformState(transform));
					Plugin.DebugLog("Compensating head-slot transform " + mode + ": " + RendererScanner.GetPath(transform));
				}
				TransformState transformState = OriginalHeadSlotStates[transform];
				Vector3 val = (compensatePosition ? (transformState.LocalPosition * 999.99994f) : transformState.LocalPosition);
				Vector3 val2 = transformState.LocalScale * 999.99994f;
				if (transform.localPosition != val)
				{
					transform.localPosition = val;
				}
				if (transform.localScale != val2)
				{
					transform.localScale = val2;
				}
			}
		}

		private static Transform? FindCompensationRootUnderShrunkHead(Transform transform)
		{
			if ((Object)(object)transform == (Object)null || !IsCachedLocalTransform(transform) || OriginalBoneScales.ContainsKey(transform))
			{
				return null;
			}
			Transform val = transform;
			while ((Object)(object)val.parent != (Object)null)
			{
				if (OriginalBoneScales.ContainsKey(val.parent))
				{
					return val;
				}
				val = val.parent;
			}
			return null;
		}

		private static void RestoreRendererStates()
		{
			if (OriginalRendererStates.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<Renderer, RendererStateSnapshot> originalRendererState in OriginalRendererStates)
			{
				Renderer key = originalRendererState.Key;
				if (!((Object)(object)key == (Object)null))
				{
					originalRendererState.Value.Restore(key);
				}
			}
			OriginalRendererStates.Clear();
			RenderersToRemove.Clear();
			_nextRefreshTime = 0f;
		}

		private static void RestoreRenderer(Renderer renderer)
		{
			if (!((Object)(object)renderer == (Object)null) && OriginalRendererStates.TryGetValue(renderer, out var value))
			{
				value.Restore(renderer);
			}
		}

		private static void RestoreBoneScales()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (OriginalBoneScales.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<Transform, Vector3> originalBoneScale in OriginalBoneScales)
			{
				Transform key = originalBoneScale.Key;
				if (!((Object)(object)key == (Object)null))
				{
					key.localScale = originalBoneScale.Value;
				}
			}
			OriginalBoneScales.Clear();
			CachedHeadBones.Clear();
			BonesToRemove.Clear();
		}

		private static void RestoreHeadSlotStates()
		{
			//IL_004d: 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)
			if (OriginalHeadSlotStates.Count == 0)
			{
				return;
			}
			foreach (KeyValuePair<Transform, TransformState> originalHeadSlotState in OriginalHeadSlotStates)
			{
				Transform key = originalHeadSlotState.Key;
				if (!((Object)(object)key == (Object)null))
				{
					key.localPosition = originalHeadSlotState.Value.LocalPosition;
					key.localScale = originalHeadSlotState.Value.LocalScale;
				}
			}
			OriginalHeadSlotStates.Clear();
			HeadSlotTransformsToRemove.Clear();
		}

		private static void RemoveDestroyedRenderers()
		{
			RenderersToRemove.Clear();
			foreach (Renderer key in OriginalRendererStates.Keys)
			{
				if ((Object)(object)key == (Object)null)
				{
					RenderersToRemove.Add(key);
				}
			}
			foreach (Renderer item in RenderersToRemove)
			{
				if (item != null)
				{
					OriginalRendererStates.Remove(item);
				}
			}
			RenderersToRemove.Clear();
		}

		private static void RemoveDestroyedBones()
		{
			BonesToRemove.Clear();
			for (int num = CachedHeadBones.Count - 1; num >= 0; num--)
			{
				Transform val = CachedHeadBones[num];
				if (!((Object)(object)val != (Object)null))
				{
					CachedHeadBones.RemoveAt(num);
					if (val != null)
					{
						OriginalBoneScales.Remove(val);
					}
				}
			}
			foreach (Transform key in OriginalBoneScales.Keys)
			{
				if ((Object)(object)key == (Object)null)
				{
					BonesToRemove.Add(key);
				}
			}
			foreach (Transform item in BonesToRemove)
			{
				if (item != null)
				{
					OriginalBoneScales.Remove(item);
					CachedHeadBones.Remove(item);
				}
			}
			BonesToRemove.Clear();
		}

		private static void RemoveDestroyedHeadSlotTransforms()
		{
			HeadSlotTransformsToRemove.Clear();
			foreach (Transform key in OriginalHeadSlotStates.Keys)
			{
				if ((Object)(object)key == (Object)null)
				{
					HeadSlotTransformsToRemove.Add(key);
				}
			}
			foreach (Transform item in HeadSlotTransformsToRemove)
			{
				if (item != null)
				{
					OriginalHeadSlotStates.Remove(item);
				}
			}
			HeadSlotTransformsToRemove.Clear();
		}

		private static bool IsLocalPlayer(Player player)
		{
			return (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer;
		}

		private static bool IsRendererOwnedByPlayer(Player player, Renderer renderer)
		{
			return LocalPlayerRendererOwnership.IsLocalPlayerRenderer(player, renderer);
		}

		private static bool IsTransformOwnedByPlayer(Player player, Transform transform)
		{
			return LocalPlayerRendererOwnership.IsLocalPlayerTransform(player, transform);
		}

		private static bool IsCachedLocalRenderer(Renderer renderer)
		{
			return (Object)(object)_cachedPlayer != (Object)null && IsRendererOwnedByPlayer(_cachedPlayer, renderer);
		}

		private static bool IsCachedLocalTransform(Transform transform)
		{
			return (Object)(object)_cachedPlayer != (Object)null && IsTransformOwnedByPlayer(_cachedPlayer, transform);
		}

		private static void ResetCache()
		{
			_cachedPlayer = null;
			_active = false;
			_headBoneCacheInitialized = false;
			_nextRefreshTime = 0f;
			OriginalRendererStates.Clear();
			OriginalBoneScales.Clear();
			OriginalHeadSlotStates.Clear();
			DesiredRenderers.Clear();
			CachedHeadBones.Clear();
			ManagedHelmetRenderers.Clear();
			RenderersToRemove.Clear();
			BonesToRemove.Clear();
			HeadSlotTransformsToRemove.Clear();
			_lastFallbackReason = null;
		}
	}
	internal static class HelmetShadowController
	{
		private const float MaxHelmetRendererExtent = 1.35f;

		private const float MinHelmetRendererHeight = 0.85f;

		private const float MaxHelmetRendererHeight = 2.45f;

		private const float MaxHelmetHorizontalDistance = 0.9f;

		private static readonly FieldInfo? HelmetItemInstanceField = AccessTools.Field(typeof(VisEquipment), "m_helmetItemInstance");

		private static readonly Dictionary<Renderer, RendererStateSnapshot> OriginalRendererStates = new Dictionary<Renderer, RendererStateSnapshot>();

		private static readonly Dictionary<VisEquipment, HashSet<Renderer>> HelmetChangeSnapshots = new Dictionary<VisEquipment, HashSet<Renderer>>();

		private static readonly HashSet<Renderer> DesiredRenderers = new HashSet<Renderer>();

		private static readonly HashSet<Renderer> ObservedHelmetRenderers = new HashSet<Renderer>();

		private static readonly HashSet<Renderer> WarnedUnsafeRenderers = new HashSet<Renderer>();

		private static readonly HashSet<string> HelmetPrefabFingerprints = new HashSet<string>();

		private static readonly List<Renderer?> RenderersToRemove = new List<Renderer>();

		private static readonly List<VisEquipment?> SnapshotsToRemove = new List<VisEquipment>();

		private static Player? _cachedPlayer;

		private static bool _active;

		private static bool _dirty = true;

		private static float _nextRefreshTime;

		private static readonly string[] FullBodyKeywords = new string[14]
		{
			"body", "torso", "chest", "arm", "leg", "foot", "feet", "hand", "player", "character",
			"human", "male", "female", "skin"
		};

		private static readonly string[] HeadSlotKeywords = new string[10] { "head", "helmet", "helm", "hat", "hood", "hair", "beard", "circlet", "crown", "headgear" };

		internal static void RequestRefresh()
		{
			_dirty = true;
			_nextRefreshTime = 0f;
		}

		internal static void Update(Player player, bool shouldHide)
		{
			if (!shouldHide || (Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				Restore();
				return;
			}
			if ((Object)(object)_cachedPlayer != (Object)null && (Object)(object)_cachedPlayer != (Object)(object)player)
			{
				Restore();
			}
			_cachedPlayer = player;
			if (!_active)
			{
				OriginalRendererStates.Clear();
				DesiredRenderers.Clear();
				_active = true;
				_dirty = true;
				_nextRefreshTime = 0f;
			}
			if (_dirty || Time.unscaledTime >= _nextRefreshTime)
			{
				RefreshRendererCache(player);
				_dirty = false;
				_nextRefreshTime = Time.unscaledTime + Mathf.Clamp(Plugin.VisibilityRefreshInterval.Value, 0.1f, 10f);
			}
			ApplyShadowsOnly();
		}

		internal static void Restore()
		{
			RestoreRendererStates();
			DesiredRenderers.Clear();
			ObservedHelmetRenderers.Clear();
			HelmetChangeSnapshots.Clear();
			WarnedUnsafeRenderers.Clear();
			HelmetPrefabFingerprints.Clear();
			RenderersToRemove.Clear();
			SnapshotsToRemove.Clear();
			_cachedPlayer = null;
			_active = false;
			_dirty = true;
			_nextRefreshTime = 0f;
		}

		internal static bool IsManagedRenderer(Renderer renderer)
		{
			return (Object)(object)renderer != (Object)null && (DesiredRenderers.Contains(renderer) || OriginalRendererStates.ContainsKey(renderer));
		}

		internal static void CopyManagedRenderers(List<Renderer> target)
		{
			target.Clear();
			RemoveDestroyedRenderers();
			foreach (Renderer desiredRenderer in DesiredRenderers)
			{
				if ((Object)(object)desiredRenderer != (Object)null)
				{
					target.Add(desiredRenderer);
				}
			}
		}

		internal static void CaptureHelmetChangeStart(VisEquipment visEquipment)
		{
			if (ShouldObserveHelmetChange(visEquipment))
			{
				HashSet<Renderer> value = new HashSet<Renderer>(((Component)visEquipment).GetComponentsInChildren<Renderer>(true));
				HelmetChangeSnapshots[visEquipment] = value;
			}
		}

		internal static void CaptureHelmetChangeEnd(VisEquipment visEquipment, bool changed)
		{
			if ((Object)(object)visEquipment == (Object)null || !HelmetChangeSnapshots.TryGetValue(visEquipment, out HashSet<Renderer> value))
			{
				return;
			}
			HelmetChangeSnapshots.Remove(visEquipment);
			if (!changed || !PlayerEquipmentAccess.IsLocalPlayerVisEquipment(visEquipment))
			{
				RequestRefresh();
				return;
			}
			Renderer[] componentsInChildren = ((Component)visEquipment).GetComponentsInChildren<Renderer>(true);
			Renderer[] array = componentsInChildren;
			foreach (Renderer val in array)
			{
				if (!((Object)(object)val == (Object)null) && !v