Decompiled source of FirstPersonView ModelReplacementCompat v1.1.0

FirstPersonViewModelReplacementCompat.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FirstPersonView;
using GameNetcodeStuff;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using ModelReplacement;
using ModelReplacement.Monobehaviors;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("FirstPersonView")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("v0id")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Compatibility patch between FirstPersonView and ModelReplacementAPI")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("FirstPersonViewModelReplacementCompat")]
[assembly: AssemblyTitle("FirstPersonViewModelReplacementCompat")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[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 FirstPersonViewModelReplacementCompat
{
	public enum HeadPositionMode
	{
		Disabled,
		Auto,
		IndividualSuits
	}
	public static class ModConfig
	{
		private static readonly Dictionary<string, float> ParsedSuitOffsets = new Dictionary<string, float>(StringComparer.OrdinalIgnoreCase);

		public static ConfigEntry<HeadPositionMode> HeadPositionAdjustmentMode { get; private set; } = null;

		public static ConfigEntry<float> ManualHeadOffset { get; private set; } = null;

		public static ConfigEntry<string> SuitHeadOffsets { get; private set; } = null;

		public static void Init(ConfigFile config)
		{
			HeadPositionAdjustmentMode = config.Bind<HeadPositionMode>("HeadPosition", "AdjustmentMode", HeadPositionMode.Auto, "Determines how the first-person camera head position is adjusted for custom models. Options: Disabled, Auto, IndividualSuits.");
			ManualHeadOffset = config.Bind<float>("HeadPosition", "ManualHeadOffset", 0f, "Manual vertical offset in meters added to camera height. Applies in Auto mode or as a default fallback.");
			SuitHeadOffsets = config.Bind<string>("HeadPosition", "SuitHeadOffsets", "Ashley: 0.10, Default: 0.0", "Custom per-suit vertical head offsets in meters used when AdjustmentMode is set to IndividualSuits. Format: SuitName: Offset, SuitName2: Offset");
			SuitHeadOffsets.SettingChanged += delegate
			{
				ParseOffsets();
			};
			ParseOffsets();
		}

		private static void ParseOffsets()
		{
			ParsedSuitOffsets.Clear();
			if (string.IsNullOrWhiteSpace(SuitHeadOffsets.Value))
			{
				return;
			}
			string[] array = SuitHeadOffsets.Value.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				int num = text.IndexOf(':');
				if (num > 0 && num < text.Length - 1)
				{
					string key = text.Substring(0, num).Trim();
					if (float.TryParse(text.Substring(num + 1).Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						ParsedSuitOffsets[key] = result;
					}
				}
			}
		}

		public static float GetSuitOffset(string suitName)
		{
			if (string.IsNullOrEmpty(suitName))
			{
				return ManualHeadOffset.Value;
			}
			if (ParsedSuitOffsets.TryGetValue(suitName, out var value))
			{
				return value;
			}
			foreach (KeyValuePair<string, float> parsedSuitOffset in ParsedSuitOffsets)
			{
				if (suitName.IndexOf(parsedSuitOffset.Key, StringComparison.OrdinalIgnoreCase) >= 0 || parsedSuitOffset.Key.IndexOf(suitName, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return parsedSuitOffset.Value;
				}
			}
			return ManualHeadOffset.Value;
		}
	}
	public static class ModelReplacementCompat
	{
		private sealed class TrackedMesh
		{
			public SkinnedMeshRenderer Renderer;

			public Mesh OriginalMesh;

			public Mesh? HeadlessMesh;

			public Mesh? HeadlessArmlessMesh;

			public bool OriginalUpdateWhenOffscreen;
		}

		private sealed class TrackedHeadRenderer
		{
			public Renderer Renderer;

			public bool OriginalEnabled;
		}

		private static bool _initialized;

		private static bool _renderCallbacksHooked;

		private static readonly List<TrackedMesh> TrackedMeshes = new List<TrackedMesh>();

		private static readonly List<TrackedHeadRenderer> TrackedHeadRenderers = new List<TrackedHeadRenderer>();

		private static int _trackedModelInstanceId;

		private static Camera? _firstPersonCamera;

		private static bool _useVanillaArms;

		private static bool _isFirstPersonActive;

		private static float _smoothedHeadOffset;

		private static bool _hasCalculatedInitialHeadOffset;

		public static bool IsModelReplacementActive { get; private set; }

		public static void Initialize()
		{
			if (!_initialized)
			{
				_initialized = true;
				EnsureRenderCallbacks();
			}
		}

		public static void Dispose()
		{
			Reset();
			if (_renderCallbacksHooked)
			{
				RenderPipelineManager.beginCameraRendering -= OnBeginCameraRendering;
				RenderPipelineManager.endCameraRendering -= OnEndCameraRendering;
				_renderCallbacksHooked = false;
			}
			_initialized = false;
		}

		public static void Reset()
		{
			RestoreAll();
			TrackedMeshes.Clear();
			TrackedHeadRenderers.Clear();
			_trackedModelInstanceId = 0;
			_firstPersonCamera = null;
			_useVanillaArms = false;
			_isFirstPersonActive = false;
			_smoothedHeadOffset = 0f;
			_hasCalculatedInitialHeadOffset = false;
			IsModelReplacementActive = false;
		}

		private static void EnsureRenderCallbacks()
		{
			if (!_renderCallbacksHooked)
			{
				_renderCallbacksHooked = true;
				RenderPipelineManager.beginCameraRendering += OnBeginCameraRendering;
				RenderPipelineManager.endCameraRendering += OnEndCameraRendering;
			}
		}

		public static bool CheckLocalPlayerReplacement(PlayerControllerB player, out BodyReplacementBase? replacement)
		{
			replacement = null;
			if ((Object)(object)player == (Object)null || !Players.IsLocal(player))
			{
				return false;
			}
			replacement = ((Component)player).GetComponent<BodyReplacementBase>();
			if ((Object)(object)replacement != (Object)null && replacement.IsActive)
			{
				return (Object)(object)replacement.replacementModel != (Object)null;
			}
			return false;
		}

		public static float GetHeadPositionOffset(PlayerControllerB player, BodyReplacementBase replacement)
		{
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			switch (ModConfig.HeadPositionAdjustmentMode.Value)
			{
			case HeadPositionMode.Disabled:
				return 0f;
			case HeadPositionMode.IndividualSuits:
			{
				string text = replacement.suitName;
				if (string.IsNullOrEmpty(text) && StartOfRound.Instance?.unlockablesList?.unlockables != null)
				{
					int currentSuitID = player.currentSuitID;
					if (currentSuitID >= 0 && currentSuitID < StartOfRound.Instance.unlockablesList.unlockables.Count)
					{
						text = StartOfRound.Instance.unlockablesList.unlockables[currentSuitID].unlockableName;
					}
				}
				if (string.IsNullOrEmpty(text) && (Object)(object)replacement.replacementModel != (Object)null)
				{
					text = ((Object)replacement.replacementModel).name;
				}
				return ModConfig.GetSuitOffset(text);
			}
			default:
			{
				GameObject replacementModel = replacement.replacementModel;
				if ((Object)(object)replacementModel == (Object)null)
				{
					return ModConfig.ManualHeadOffset.Value;
				}
				Transform val = ResolveHeadBone(replacementModel.GetComponentInChildren<Animator>(), replacement, replacementModel);
				if ((Object)(object)val == (Object)null)
				{
					return ModConfig.ManualHeadOffset.Value;
				}
				Transform val2 = ResolveVanillaHeadBone(player);
				Vector3 up = ((Component)player).transform.up;
				float num = Vector3.Dot(val.position - ((Component)player).transform.position, up);
				float num2 = (((Object)(object)val2 != (Object)null) ? Vector3.Dot(val2.position - ((Component)player).transform.position, up) : 1.45f);
				float num3 = Mathf.Clamp(num - num2, -1.2f, 1.2f) + ModConfig.ManualHeadOffset.Value;
				if (!_hasCalculatedInitialHeadOffset)
				{
					_smoothedHeadOffset = num3;
					_hasCalculatedInitialHeadOffset = true;
				}
				else
				{
					_smoothedHeadOffset = Mathf.Lerp(_smoothedHeadOffset, num3, Time.deltaTime * 12f);
				}
				return _smoothedHeadOffset;
			}
			}
		}

		public static void OnLateUpdate(PlayerControllerB player)
		{
			if (!CheckLocalPlayerReplacement(player, out BodyReplacementBase replacement) || (Object)(object)replacement == (Object)null)
			{
				if (IsModelReplacementActive)
				{
					Reset();
				}
				return;
			}
			IsModelReplacementActive = true;
			_firstPersonCamera = player.gameplayCamera;
			if ((_isFirstPersonActive = LocalBodyViewController.LocalBodyShown) && (Object)(object)_firstPersonCamera != (Object)null)
			{
				int num = (1 << ViewStateManager.modelLayer) | (1 << ViewStateManager.NoPostModelLayer);
				if ((_firstPersonCamera.cullingMask & num) != num)
				{
					Camera? firstPersonCamera = _firstPersonCamera;
					firstPersonCamera.cullingMask |= num;
				}
			}
			if ((Object)(object)replacement.replacementViewModel == (Object)null)
			{
				_useVanillaArms = false;
				if ((Object)(object)player.thisPlayerModelArms != (Object)null && ((Renderer)player.thisPlayerModelArms).enabled)
				{
					((Renderer)player.thisPlayerModelArms).enabled = false;
				}
			}
			else
			{
				_useVanillaArms = ResolveUseVanillaArms(player);
				SyncArmsVisibility(player, replacement, _useVanillaArms);
			}
			GameObject replacementModel = replacement.replacementModel;
			if (((Object)replacementModel).GetInstanceID() != _trackedModelInstanceId)
			{
				_hasCalculatedInitialHeadOffset = false;
				RebuildModelTracking(replacement, replacementModel);
			}
		}

		private static bool ResolveUseVanillaArms(PlayerControllerB player)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			if (!LocalBodyViewController.LocalBodyShown)
			{
				return true;
			}
			if ((int)ConfigManager.Hands.Value == 1)
			{
				return false;
			}
			bool flag = Players.IsActivelyHolding(player);
			return !player.inSpecialInteractAnimation && flag;
		}

		private static void SyncArmsVisibility(PlayerControllerB player, BodyReplacementBase replacement, bool useVanillaArms)
		{
			GameObject replacementViewModel = replacement.replacementViewModel;
			if ((Object)(object)replacementViewModel != (Object)null)
			{
				if ((Object)(object)player.thisPlayerModelArms != (Object)null && ((Renderer)player.thisPlayerModelArms).enabled)
				{
					((Renderer)player.thisPlayerModelArms).enabled = false;
				}
				Renderer[] componentsInChildren = replacementViewModel.GetComponentsInChildren<Renderer>(true);
				for (int i = 0; i < componentsInChildren.Length; i++)
				{
					if ((Object)(object)componentsInChildren[i] != (Object)null && componentsInChildren[i].enabled != useVanillaArms)
					{
						componentsInChildren[i].enabled = useVanillaArms;
					}
				}
			}
			else if ((Object)(object)player.thisPlayerModelArms != (Object)null && ((Renderer)player.thisPlayerModelArms).enabled)
			{
				((Renderer)player.thisPlayerModelArms).enabled = false;
			}
		}

		private static void RebuildModelTracking(BodyReplacementBase replacement, GameObject model)
		{
			RestoreAll();
			TrackedMeshes.Clear();
			TrackedHeadRenderers.Clear();
			_trackedModelInstanceId = ((Object)model).GetInstanceID();
			Animator componentInChildren = model.GetComponentInChildren<Animator>();
			Transform val = ResolveHeadBone(componentInChildren, replacement, model);
			Transform val2 = ResolveBone(componentInChildren, replacement, (HumanBodyBones)13, "arm.L_upper");
			Transform val3 = ResolveBone(componentInChildren, replacement, (HumanBodyBones)14, "arm.R_upper");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Could not find head bone on replacement model; head mesh surgery cannot be performed.");
				return;
			}
			SkinnedMeshRenderer[] componentsInChildren = model.GetComponentsInChildren<SkinnedMeshRenderer>(true);
			foreach (SkinnedMeshRenderer val4 in componentsInChildren)
			{
				if ((Object)(object)val4 == (Object)null || (Object)(object)val4.sharedMesh == (Object)null)
				{
					continue;
				}
				Mesh sharedMesh = val4.sharedMesh;
				if (MeshSurgery.IsGenerated(((Object)sharedMesh).GetInstanceID()))
				{
					continue;
				}
				Mesh orCreateHeadlessMesh = MeshSurgery.GetOrCreateHeadlessMesh(sharedMesh, val4, val);
				if ((Object)(object)orCreateHeadlessMesh != (Object)null && RemovesGeometry(sharedMesh, orCreateHeadlessMesh))
				{
					Mesh val5 = MeshSurgery.GetOrCreateHeadlessArmlessMesh(sharedMesh, val4, val, val2, val3);
					if ((Object)(object)val5 == (Object)null)
					{
						val5 = BuildFallbackHeadlessArmlessMesh(sharedMesh, val4, val, val2, val3);
					}
					TrackedMeshes.Add(new TrackedMesh
					{
						Renderer = val4,
						OriginalMesh = sharedMesh,
						HeadlessMesh = orCreateHeadlessMesh,
						HeadlessArmlessMesh = val5,
						OriginalUpdateWhenOffscreen = val4.updateWhenOffscreen
					});
					val4.updateWhenOffscreen = true;
				}
			}
			Renderer[] componentsInChildren2 = model.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val6 in componentsInChildren2)
			{
				if (!((Object)(object)val6 == (Object)null) && !(val6 is SkinnedMeshRenderer) && ((Object)(object)((Component)val6).transform == (Object)(object)val || ((Component)val6).transform.IsChildOf(val)))
				{
					TrackedHeadRenderers.Add(new TrackedHeadRenderer
					{
						Renderer = val6,
						OriginalEnabled = val6.enabled
					});
				}
			}
			Plugin.Log.LogInfo((object)$"[FPV-MRCompat] Tracked {TrackedMeshes.Count} skinned mesh(es) and {TrackedHeadRenderers.Count} head accessory renderer(s) for '{((Object)model).name}'.");
		}

		private static Mesh? BuildFallbackHeadlessArmlessMesh(Mesh sourceMesh, SkinnedMeshRenderer smr, Transform headBone, Transform? leftArm, Transform? rightArm)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (smr.bones == null || sourceMesh.boneWeights == null || sourceMesh.boneWeights.Length == 0)
				{
					return null;
				}
				HashSet<int> boneAndChildrenIndices = GetBoneAndChildrenIndices(smr.bones, headBone);
				HashSet<int> hashSet = new HashSet<int>();
				if ((Object)(object)leftArm != (Object)null)
				{
					hashSet.UnionWith(GetBoneAndChildrenIndices(smr.bones, leftArm));
				}
				if ((Object)(object)rightArm != (Object)null)
				{
					hashSet.UnionWith(GetBoneAndChildrenIndices(smr.bones, rightArm));
				}
				if (hashSet.Count == 0)
				{
					return null;
				}
				BoneWeight[] boneWeights = sourceMesh.boneWeights;
				Vector3[] array = (Vector3[])sourceMesh.vertices.Clone();
				bool[] array2 = new bool[array.Length];
				for (int i = 0; i < array.Length; i++)
				{
					BoneWeight val = boneWeights[i];
					if (hashSet.Contains(((BoneWeight)(ref val)).boneIndex0) || hashSet.Contains(((BoneWeight)(ref val)).boneIndex1) || boneAndChildrenIndices.Contains(((BoneWeight)(ref val)).boneIndex0) || boneAndChildrenIndices.Contains(((BoneWeight)(ref val)).boneIndex1))
					{
						array2[i] = true;
						array[i] = Vector3.zero;
					}
				}
				Mesh val2 = Object.Instantiate<Mesh>(sourceMesh);
				((Object)val2).name = ((Object)sourceMesh).name + "_FPVHeadlessArmlessFallback";
				val2.vertices = array;
				for (int j = 0; j < val2.subMeshCount; j++)
				{
					int[] triangles = sourceMesh.GetTriangles(j);
					List<int> list = new List<int>(triangles.Length);
					for (int k = 0; k < triangles.Length; k += 3)
					{
						int num = triangles[k];
						int num2 = triangles[k + 1];
						int num3 = triangles[k + 2];
						if (!array2[num] && !array2[num2] && !array2[num3])
						{
							list.Add(num);
							list.Add(num2);
							list.Add(num3);
						}
					}
					val2.SetTriangles(list, j);
				}
				val2.RecalculateBounds();
				return val2;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not build fallback headless armless mesh: " + ex.Message));
				return null;
			}
		}

		private static HashSet<int> GetBoneAndChildrenIndices(Transform[] bones, Transform root)
		{
			HashSet<int> hashSet = new HashSet<int>();
			for (int i = 0; i < bones.Length; i++)
			{
				Transform val = bones[i];
				if ((Object)(object)val != (Object)null && ((Object)(object)val == (Object)(object)root || val.IsChildOf(root)))
				{
					hashSet.Add(i);
				}
			}
			return hashSet;
		}

		private static Transform? ResolveVanillaHeadBone(PlayerControllerB player)
		{
			if ((Object)(object)player.thisPlayerModel != (Object)null && player.thisPlayerModel.bones != null)
			{
				Transform[] bones = player.thisPlayerModel.bones;
				foreach (Transform val in bones)
				{
					if ((Object)(object)val != (Object)null && ((Object)val).name.IndexOf("spine.004", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return val;
					}
				}
			}
			return null;
		}

		private static Transform? ResolveHeadBone(Animator? animator, BodyReplacementBase replacement, GameObject model)
		{
			Transform val = null;
			if ((Object)(object)animator != (Object)null && animator.isHuman)
			{
				val = animator.GetBoneTransform((HumanBodyBones)10);
			}
			if ((Object)(object)val == (Object)null && replacement.avatar != null)
			{
				val = replacement.avatar.GetAvatarTransformFromBoneName("spine.004");
			}
			if ((Object)(object)val == (Object)null)
			{
				val = FindFirstByNameContains(model.transform, "head");
			}
			return val;
		}

		private static Transform? ResolveBone(Animator? animator, BodyReplacementBase replacement, HumanBodyBones humanBone, string mrapiName)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			Transform val = null;
			if ((Object)(object)animator != (Object)null && animator.isHuman)
			{
				val = animator.GetBoneTransform(humanBone);
			}
			if ((Object)(object)val == (Object)null && replacement.avatar != null)
			{
				val = replacement.avatar.GetAvatarTransformFromBoneName(mrapiName);
			}
			return val;
		}

		private static Transform? FindFirstByNameContains(Transform root, string token)
		{
			Queue<Transform> queue = new Queue<Transform>();
			queue.Enqueue(root);
			while (queue.Count > 0)
			{
				Transform val = queue.Dequeue();
				if (((Object)val).name.IndexOf(token, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return val;
				}
				for (int i = 0; i < val.childCount; i++)
				{
					queue.Enqueue(val.GetChild(i));
				}
			}
			return null;
		}

		private static bool RemovesGeometry(Mesh source, Mesh headless)
		{
			if (headless.subMeshCount != source.subMeshCount)
			{
				return true;
			}
			for (int i = 0; i < source.subMeshCount; i++)
			{
				if (headless.GetIndexCount(i) != source.GetIndexCount(i))
				{
					return true;
				}
			}
			return false;
		}

		private static void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera)
		{
			if (!IsModelReplacementActive || (TrackedMeshes.Count == 0 && TrackedHeadRenderers.Count == 0))
			{
				return;
			}
			bool flag = (Object)(object)camera == (Object)(object)_firstPersonCamera && _isFirstPersonActive;
			for (int i = 0; i < TrackedMeshes.Count; i++)
			{
				TrackedMesh trackedMesh = TrackedMeshes[i];
				if ((Object)(object)trackedMesh.Renderer == (Object)null)
				{
					continue;
				}
				if (flag)
				{
					Mesh val = ((_useVanillaArms && (Object)(object)trackedMesh.HeadlessArmlessMesh != (Object)null) ? trackedMesh.HeadlessArmlessMesh : trackedMesh.HeadlessMesh);
					if ((Object)(object)val != (Object)null && (Object)(object)trackedMesh.Renderer.sharedMesh != (Object)(object)val)
					{
						trackedMesh.Renderer.sharedMesh = val;
					}
				}
				else if ((Object)(object)trackedMesh.OriginalMesh != (Object)null && (Object)(object)trackedMesh.Renderer.sharedMesh != (Object)(object)trackedMesh.OriginalMesh)
				{
					trackedMesh.Renderer.sharedMesh = trackedMesh.OriginalMesh;
				}
			}
			for (int j = 0; j < TrackedHeadRenderers.Count; j++)
			{
				TrackedHeadRenderer trackedHeadRenderer = TrackedHeadRenderers[j];
				if (!((Object)(object)trackedHeadRenderer.Renderer == (Object)null))
				{
					trackedHeadRenderer.Renderer.enabled = !flag && trackedHeadRenderer.OriginalEnabled;
				}
			}
		}

		private static void OnEndCameraRendering(ScriptableRenderContext context, Camera camera)
		{
			if (!IsModelReplacementActive || (Object)(object)camera != (Object)(object)_firstPersonCamera)
			{
				return;
			}
			for (int i = 0; i < TrackedMeshes.Count; i++)
			{
				TrackedMesh trackedMesh = TrackedMeshes[i];
				if ((Object)(object)trackedMesh.Renderer != (Object)null && (Object)(object)trackedMesh.OriginalMesh != (Object)null && (Object)(object)trackedMesh.Renderer.sharedMesh != (Object)(object)trackedMesh.OriginalMesh)
				{
					trackedMesh.Renderer.sharedMesh = trackedMesh.OriginalMesh;
				}
			}
			for (int j = 0; j < TrackedHeadRenderers.Count; j++)
			{
				TrackedHeadRenderer trackedHeadRenderer = TrackedHeadRenderers[j];
				if ((Object)(object)trackedHeadRenderer.Renderer != (Object)null)
				{
					trackedHeadRenderer.Renderer.enabled = trackedHeadRenderer.OriginalEnabled;
				}
			}
		}

		private static void RestoreAll()
		{
			for (int i = 0; i < TrackedMeshes.Count; i++)
			{
				TrackedMesh trackedMesh = TrackedMeshes[i];
				if ((Object)(object)trackedMesh.Renderer != (Object)null)
				{
					if ((Object)(object)trackedMesh.OriginalMesh != (Object)null && (Object)(object)trackedMesh.Renderer.sharedMesh != (Object)(object)trackedMesh.OriginalMesh)
					{
						trackedMesh.Renderer.sharedMesh = trackedMesh.OriginalMesh;
					}
					trackedMesh.Renderer.updateWhenOffscreen = trackedMesh.OriginalUpdateWhenOffscreen;
				}
			}
			for (int j = 0; j < TrackedHeadRenderers.Count; j++)
			{
				TrackedHeadRenderer trackedHeadRenderer = TrackedHeadRenderers[j];
				if ((Object)(object)trackedHeadRenderer.Renderer != (Object)null)
				{
					trackedHeadRenderer.Renderer.enabled = trackedHeadRenderer.OriginalEnabled;
				}
			}
		}
	}
	public static class PluginInfo
	{
		public const string GUID = "com.v0id.firstpersonview.modelreplacementcompat";

		public const string NAME = "LC FirstPersonView ModelReplacement Compat";

		public const string VERSION = "1.1.0";
	}
	[BepInPlugin("com.v0id.firstpersonview.modelreplacementcompat", "LC FirstPersonView ModelReplacement Compat", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private readonly Harmony _harmony = new Harmony("com.v0id.firstpersonview.modelreplacementcompat");

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Log.LogInfo((object)"Initializing LC FirstPersonView ModelReplacement Compat v1.1.0");
			ModConfig.Init(((BaseUnityPlugin)this).Config);
			ModelReplacementCompat.Initialize();
			_harmony.PatchAll();
			Log.LogInfo((object)"LC FirstPersonView ModelReplacement Compat loaded successfully.");
		}

		private void OnDestroy()
		{
			ModelReplacementCompat.Dispose();
			_harmony.UnpatchSelf();
		}
	}
}
namespace FirstPersonViewModelReplacementCompat.Patches
{
	[HarmonyPatch(typeof(CameraRig))]
	internal static class CameraRigPatches
	{
		[HarmonyPatch("ApplyOffset")]
		[HarmonyPostfix]
		private static void ApplyOffset_Postfix(LocalBodyState state, PlayerControllerB player)
		{
			//IL_0065: 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_006a: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: 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_0095: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if (state != null && !((Object)(object)state.GameplayCamera == (Object)null) && state.CameraOffsetApplied && ModelReplacementCompat.CheckLocalPlayerReplacement(player, out BodyReplacementBase replacement) && (Object)(object)replacement != (Object)null)
			{
				float headPositionOffset = ModelReplacementCompat.GetHeadPositionOffset(player, replacement);
				if (Mathf.Abs(headPositionOffset) > 0.0001f)
				{
					Transform transform = ((Component)state.GameplayCamera).transform;
					Transform parent = transform.parent;
					Vector3 val = (((Object)(object)parent != (Object)null) ? parent.up : Vector3.up);
					Vector3 val2 = (transform.position += val * headPositionOffset);
					state.LastCameraTargetLocal = (((Object)(object)parent != (Object)null) ? parent.InverseTransformPoint(val2) : val2);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class FirstPersonBodyPatches
	{
		[HarmonyPatch(typeof(FirstPersonBody), "WarnIfBodyRendererDisabled")]
		[HarmonyPrefix]
		private static bool WarnIfBodyRendererDisabled_Prefix(SkinnedMeshRenderer body)
		{
			PlayerControllerB val = StartOfRound.Instance?.localPlayerController;
			if ((Object)(object)val != (Object)null && ModelReplacementCompat.CheckLocalPlayerReplacement(val, out BodyReplacementBase _))
			{
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(FirstPersonBody), "SetFpArmsEnabled")]
		[HarmonyPrefix]
		private static void SetFpArmsEnabled_Prefix(PlayerControllerB player, ref bool enabled)
		{
			if ((Object)(object)player != (Object)null && ModelReplacementCompat.CheckLocalPlayerReplacement(player, out BodyReplacementBase replacement) && ((Object)(object)replacement == (Object)null || (Object)(object)replacement.replacementViewModel == (Object)null))
			{
				enabled = false;
			}
		}

		[HarmonyPatch(typeof(HeldItemView), "ApplyHolder")]
		[HarmonyPrefix]
		private static void HeldItemView_ApplyHolder_Prefix(LocalBodyState state, PlayerControllerB player, bool showBody, ref bool useVanillaArms)
		{
			if ((Object)(object)player != (Object)null && ModelReplacementCompat.CheckLocalPlayerReplacement(player, out BodyReplacementBase replacement) && ((Object)(object)replacement == (Object)null || (Object)(object)replacement.replacementViewModel == (Object)null))
			{
				useVanillaArms = false;
			}
		}

		[HarmonyPatch(typeof(CameraRig), "ApplyArmAnchor")]
		[HarmonyPrefix]
		private static bool CameraRig_ApplyArmAnchor_Prefix(PlayerControllerB player, ref bool useVanillaArms)
		{
			if ((Object)(object)player != (Object)null && ModelReplacementCompat.CheckLocalPlayerReplacement(player, out BodyReplacementBase replacement) && ((Object)(object)replacement == (Object)null || (Object)(object)replacement.replacementViewModel == (Object)null))
			{
				useVanillaArms = false;
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(PlayerControllerB), "LateUpdate")]
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void PlayerControllerB_LateUpdate_Postfix(PlayerControllerB __instance)
		{
			if (Players.IsLocal(__instance))
			{
				ModelReplacementCompat.OnLateUpdate(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(ViewStateManager))]
	internal static class ViewStateManagerPatches
	{
		[HarmonyPatch("UpdateModelReplacement")]
		[HarmonyPostfix]
		private static void UpdateModelReplacement_Postfix(ViewStateManager __instance)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			object? obj = AccessTools.Field(typeof(ManagerBase), "controller")?.GetValue(__instance);
			PlayerControllerB val = (PlayerControllerB)((obj is PlayerControllerB) ? obj : null);
			if (!((Object)(object)val == (Object)null) && Players.IsLocal(val) && (int)__instance.GetViewState() == 2 && LocalBodyViewController.LocalBodyShown)
			{
				int num = (1 << ViewStateManager.modelLayer) | (1 << ViewStateManager.NoPostModelLayer);
				if ((Object)(object)val.gameplayCamera != (Object)null && (val.gameplayCamera.cullingMask & num) != num)
				{
					Camera gameplayCamera = val.gameplayCamera;
					gameplayCamera.cullingMask |= num;
				}
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}