Decompiled source of UniversalGrasp v0.3.1

plugins\UniversalGrasp.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("UniversalGrasp")]
[assembly: AssemblyDescription("Hold any inventory item visually in either hand.")]
[assembly: AssemblyCompany("bid")]
[assembly: AssemblyProduct("UniversalGrasp")]
[assembly: AssemblyCopyright("Copyright © 2021-2026 bid")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyVersion("0.3.1.0")]
namespace UniversalGrasp
{
	internal static class AttachSourceSelector
	{
		internal static GameObject Find(GameObject item)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Expected O, but got Unknown
			Transform obj = item.transform.Find("attach");
			GameObject val = ((obj != null) ? ((Component)obj).gameObject : null);
			if ((Object)(object)val != (Object)null && val.GetComponentsInChildren<Renderer>(true).Any())
			{
				return val;
			}
			GameObject val2 = null;
			bool flag = false;
			foreach (Transform item2 in item.transform)
			{
				Transform val3 = item2;
				if (((Component)val3).gameObject.layer == item.layer)
				{
					if ((Object)(object)val2 != (Object)null)
					{
						flag = true;
						break;
					}
					val2 = ((Component)val3).gameObject;
				}
			}
			if (!flag && (Object)(object)val2 != (Object)null)
			{
				return val2;
			}
			val2 = null;
			flag = false;
			foreach (Transform item3 in item.transform)
			{
				Transform val4 = item3;
				if ((Object)(object)val2 != (Object)null)
				{
					flag = true;
					break;
				}
				val2 = ((Component)val4).gameObject;
			}
			if (!flag && (Object)(object)val2 != (Object)null)
			{
				return val2;
			}
			return item;
		}
	}
	internal static class HandItemController
	{
		private static readonly FieldRef<Humanoid, ItemData> LeftItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_leftItem");

		private static readonly FieldRef<Humanoid, ItemData> RightItem = AccessTools.FieldRefAccess<Humanoid, ItemData>("m_rightItem");

		private static readonly FieldRef<Humanoid, VisEquipment> VisualEquipment = AccessTools.FieldRefAccess<Humanoid, VisEquipment>("m_visEquipment");

		private static readonly FieldRef<VisEquipment, int> VisualLeftItem = AccessTools.FieldRefAccess<VisEquipment, int>("m_leftItem");

		private static readonly FieldRef<VisEquipment, int> VisualRightItem = AccessTools.FieldRefAccess<VisEquipment, int>("m_rightItem");

		private static readonly FieldRef<Character, ZSyncAnimation> Animation = AccessTools.FieldRefAccess<Character, ZSyncAnimation>("m_zanim");

		internal static bool IsSelectionActive()
		{
			if (UniversalGraspPlugin.Enabled != null && UniversalGraspPlugin.Enabled.Value)
			{
				return UniversalGraspPlugin.IsHandSelectionModifierPressed();
			}
			return false;
		}

		internal static void SetHandItem(Player player, ItemData item, bool leftHand)
		{
			int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name);
			VisEquipment val = VisualEquipment.Invoke((Humanoid)(object)player);
			int num = CountItems(((Humanoid)player).GetInventory(), stableHashCode);
			if (leftHand)
			{
				ItemData val2 = LeftItem.Invoke((Humanoid)(object)player);
				if (val2 != null)
				{
					((Humanoid)player).UnequipItem(val2, false);
				}
				int num2 = ((VisualLeftItem.Invoke(val) != stableHashCode) ? stableHashCode : 0);
				if (num2 != 0 && VisualRightItem.Invoke(val) == stableHashCode && num < 2)
				{
					ClearHand(player, val, leftHand: false);
				}
				int num3 = ((num2 != 0) ? item.m_quality : 0);
				val.SetLeftItem(num2, -1, num3);
			}
			else
			{
				ItemData val3 = RightItem.Invoke((Humanoid)(object)player);
				if (val3 != null)
				{
					((Humanoid)player).UnequipItem(val3, false);
				}
				int num4 = ((VisualRightItem.Invoke(val) != stableHashCode) ? stableHashCode : 0);
				if (num4 != 0 && VisualLeftItem.Invoke(val) == stableHashCode && num < 2)
				{
					ClearHand(player, val, leftHand: true);
				}
				int num5 = ((num4 != 0) ? item.m_quality : 0);
				val.SetRightItem(num4, num5);
			}
			PlayEquipAnimation(player);
		}

		internal static void ReconcileWithInventory(Player player)
		{
			VisEquipment val = VisualEquipment.Invoke((Humanoid)(object)player);
			int num = ((LeftItem.Invoke((Humanoid)(object)player) == null) ? VisualLeftItem.Invoke(val) : 0);
			int num2 = ((RightItem.Invoke((Humanoid)(object)player) == null) ? VisualRightItem.Invoke(val) : 0);
			bool flag = false;
			if (num != 0 && num == num2)
			{
				int num3 = CountItems(((Humanoid)player).GetInventory(), num);
				if (num3 < 2)
				{
					ClearHand(player, val, leftHand: false);
					num2 = 0;
					flag = true;
				}
				if (num3 < 1)
				{
					ClearHand(player, val, leftHand: true);
					num = 0;
					flag = true;
				}
			}
			if (num != 0 && CountItems(((Humanoid)player).GetInventory(), num) == 0)
			{
				ClearHand(player, val, leftHand: true);
				flag = true;
			}
			if (num2 != 0 && CountItems(((Humanoid)player).GetInventory(), num2) == 0)
			{
				ClearHand(player, val, leftHand: false);
				flag = true;
			}
			if (flag)
			{
				PlayEquipAnimation(player);
			}
		}

		internal static bool ClearVisualOnlyItems(Player player)
		{
			VisEquipment val = VisualEquipment.Invoke((Humanoid)(object)player);
			bool result = false;
			if (RightItem.Invoke((Humanoid)(object)player) == null && VisualRightItem.Invoke(val) != 0)
			{
				val.SetRightItem(0, 0);
				result = true;
			}
			if (LeftItem.Invoke((Humanoid)(object)player) == null && VisualLeftItem.Invoke(val) != 0)
			{
				val.SetLeftItem(0, -1, 0);
				result = true;
			}
			return result;
		}

		internal static void PlayEquipAnimation(Player player)
		{
			Animation.Invoke((Character)(object)player).SetTrigger("equip_hip");
		}

		private static int CountItems(Inventory inventory, int prefabHash)
		{
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if ((Object)(object)allItem?.m_dropPrefab != (Object)null && StringExtensionMethods.GetStableHashCode(((Object)allItem.m_dropPrefab).name) == prefabHash)
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		private static void ClearHand(Player player, VisEquipment visualEquipment, bool leftHand)
		{
			ItemData val = (leftHand ? LeftItem.Invoke((Humanoid)(object)player) : RightItem.Invoke((Humanoid)(object)player));
			if (val != null)
			{
				((Humanoid)player).UnequipItem(val, false);
			}
			if (leftHand)
			{
				visualEquipment.SetLeftItem(0, -1, 0);
			}
			else
			{
				visualEquipment.SetRightItem(0, 0);
			}
		}
	}
	[BepInPlugin("org.bepinex.plugins.bid.universalgrasp", "UniversalGrasp", "0.3.1")]
	internal sealed class UniversalGraspPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "org.bepinex.plugins.bid.universalgrasp";

		public const string PluginName = "UniversalGrasp";

		public const string PluginVersion = "0.3.1";

		private readonly Harmony harmony = new Harmony("org.bepinex.plugins.bid.universalgrasp");

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<KeyboardShortcut> HandSelectionModifier { get; private set; }

		private void Awake()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Client config", "enabled", true, "Enables modifier-click hand selection in the player inventory.");
			HandSelectionModifier = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Client config", "Hand selection modifier", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), "Key held while clicking an inventory item to put it into a hand.");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"UniversalGrasp 0.3.1 loaded.");
		}

		private void OnDestroy()
		{
			harmony.UnpatchSelf();
		}

		internal static bool IsHandSelectionModifierPressed()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (HandSelectionModifier != null)
			{
				KeyboardShortcut value = HandSelectionModifier.Value;
				return ((KeyboardShortcut)(ref value)).IsPressed();
			}
			return false;
		}

		private void Update()
		{
			Player localPlayer = Player.m_localPlayer;
			if (ZInput.instance != null && ZInput.GetButtonDown("Hide") && (Object)(object)localPlayer != (Object)null && HandItemController.ClearVisualOnlyItems(localPlayer))
			{
				HandItemController.PlayEquipAnimation(localPlayer);
			}
		}
	}
	internal static class VisualCloneCleaner
	{
		internal sealed class PreparedSourceScope
		{
			private GameObject preparedSource;

			internal PreparedSourceScope Parent { get; }

			internal bool IsReleased { get; private set; }

			internal PreparedSourceScope(PreparedSourceScope parent)
			{
				Parent = parent;
			}

			internal void SetPreparedSource(GameObject source)
			{
				if (IsReleased)
				{
					throw new InvalidOperationException("Cannot prepare a source in a released scope.");
				}
				if ((Object)(object)preparedSource != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)preparedSource);
				}
				preparedSource = source;
			}

			internal void Release()
			{
				if (!IsReleased)
				{
					IsReleased = true;
					if ((Object)(object)preparedSource != (Object)null)
					{
						Object.DestroyImmediate((Object)(object)preparedSource);
						preparedSource = null;
					}
				}
			}
		}

		private static readonly Dictionary<Type, List<Type>> ComponentDependencies = new Dictionary<Type, List<Type>>();

		[ThreadStatic]
		private static PreparedSourceScope currentScope;

		internal static PreparedSourceScope BeginScope()
		{
			return currentScope = new PreparedSourceScope(currentScope);
		}

		internal static void ReleaseScope(PreparedSourceScope scope)
		{
			if (scope == null)
			{
				return;
			}
			scope.Release();
			if (currentScope == scope)
			{
				currentScope = scope.Parent;
				while (currentScope != null && currentScope.IsReleased)
				{
					currentScope = currentScope.Parent;
				}
			}
		}

		internal static bool TryPrepareSource(GameObject source, out GameObject result, out string error)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			result = null;
			error = null;
			PreparedSourceScope preparedSourceScope = currentScope;
			if (preparedSourceScope == null || preparedSourceScope.IsReleased)
			{
				error = "No active AttachItem prepared-source scope.";
				return false;
			}
			GameObject val = new GameObject("UniversalGrasp_VisualSource");
			val.SetActive(false);
			try
			{
				Vector3 localScale = source.transform.localScale;
				result = InstantiateWithoutZNetViewInitialization(source, val.transform);
				if (!RemoveComponentsRecursively(result.transform, out error))
				{
					result = null;
					return false;
				}
				((Object)result).name = ((Object)source).name;
				result.transform.localPosition = Vector3.zero;
				result.transform.localRotation = Quaternion.identity;
				result.transform.localScale = localScale;
				result.SetActive(false);
				result.transform.SetParent((Transform)null, false);
				preparedSourceScope.SetPreparedSource(result);
				return true;
			}
			catch (Exception ex)
			{
				error = ex.Message;
				result = null;
				return false;
			}
			finally
			{
				Object.DestroyImmediate((Object)(object)val);
			}
		}

		private static GameObject InstantiateWithoutZNetViewInitialization(GameObject source, Transform parent)
		{
			bool forceDisableInit = ZNetView.m_forceDisableInit;
			ZNetView.m_forceDisableInit = true;
			try
			{
				return Object.Instantiate<GameObject>(source, parent);
			}
			finally
			{
				ZNetView.m_forceDisableInit = forceDisableInit;
			}
		}

		private static bool RemoveComponentsRecursively(Transform parent, out string error)
		{
			for (int i = 0; i < parent.childCount; i++)
			{
				if (!RemoveComponentsRecursively(parent.GetChild(i), out error))
				{
					return false;
				}
			}
			List<Type> removalOrder = GetRemovalOrder(parent);
			if (removalOrder == null)
			{
				error = "Cyclic component dependencies detected on " + ((Object)parent).name + ".";
				return false;
			}
			foreach (Type item in removalOrder)
			{
				Component[] components = ((Component)parent).gameObject.GetComponents(item);
				foreach (Component val in components)
				{
					if (!IsVisualComponent(val))
					{
						try
						{
							Object.DestroyImmediate((Object)(object)val);
						}
						catch (Exception ex)
						{
							error = "Could not remove " + item.FullName + " from " + ((Object)parent).name + ": " + ex.Message;
							return false;
						}
					}
				}
			}
			error = null;
			return true;
		}

		private static bool IsVisualComponent(Component component)
		{
			if (!(component is Transform) && !(component is MeshFilter) && !(component is Renderer) && !(component is Light) && !(component is ParticleSystem) && !(component is TrailRenderer) && !(component is LineRenderer))
			{
				return component is Projector;
			}
			return true;
		}

		private static List<Type> GetRemovalOrder(Transform transform)
		{
			List<Type> list = new List<Type>();
			HashSet<Type> visited = new HashSet<Type>();
			HashSet<Type> recursionStack = new HashSet<Type>();
			Component[] components = ((Component)transform).gameObject.GetComponents<Component>();
			foreach (Component val in components)
			{
				if ((Object)(object)val != (Object)null && !TopologicalSort(((object)val).GetType(), visited, recursionStack, list))
				{
					return null;
				}
			}
			list.Reverse();
			return list;
		}

		private static bool TopologicalSort(Type type, HashSet<Type> visited, HashSet<Type> recursionStack, List<Type> result)
		{
			if (visited.Contains(type))
			{
				return true;
			}
			List<Type> list = null;
			int num = result.FindIndex(type.IsSubclassOf);
			if (num >= 0)
			{
				list = result.Skip(num).ToList();
				result.RemoveRange(num, result.Count - num);
			}
			if (!recursionStack.Add(type))
			{
				return false;
			}
			foreach (Type dependency in GetDependencies(type))
			{
				if (!TopologicalSort(dependency, visited, recursionStack, result))
				{
					return false;
				}
			}
			if (list != null)
			{
				result.AddRange(list);
			}
			else
			{
				result.Add(type);
			}
			recursionStack.Remove(type);
			visited.Add(type);
			return true;
		}

		private static List<Type> GetDependencies(Type type)
		{
			if (ComponentDependencies.TryGetValue(type, out var value))
			{
				return value;
			}
			value = new List<Type>();
			foreach (RequireComponent customAttribute in ((MemberInfo)type).GetCustomAttributes<RequireComponent>(inherit: true))
			{
				value.Add(customAttribute.m_Type0);
				value.Add(customAttribute.m_Type1);
				value.Add(customAttribute.m_Type2);
			}
			value = value.Where((Type dependency) => dependency != null).Distinct().ToList();
			ComponentDependencies[type] = value;
			return value;
		}
	}
}
namespace UniversalGrasp.Patches
{
	[HarmonyPatch(typeof(Humanoid), "DropItem")]
	internal static class HumanoidDropItemPatch
	{
		private static void Postfix(Humanoid __instance, Inventory inventory, bool __result)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (__result && (Object)(object)val != (Object)null && (Object)(object)val == (Object)(object)Player.m_localPlayer && inventory == ((Humanoid)val).GetInventory())
			{
				HandItemController.ReconcileWithInventory(val);
			}
		}
	}
	internal static class InventoryGuiPatches
	{
		[HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")]
		private static class OnSelectedItemPatch
		{
			private static bool Prefix(InventoryGrid __0, ItemData __1)
			{
				return !TrySetHandItem(__0, __1, leftHand: true);
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")]
		private static class OnRightClickItemPatch
		{
			private static bool Prefix(InventoryGrid __0, ItemData __1)
			{
				return !TrySetHandItem(__0, __1, leftHand: false);
			}
		}

		private static bool TrySetHandItem(InventoryGrid grid, ItemData item, bool leftHand)
		{
			if (!HandItemController.IsSelectionActive())
			{
				return false;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)grid == (Object)null || item == null || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return false;
			}
			if (grid.GetInventory() != ((Humanoid)localPlayer).GetInventory())
			{
				return false;
			}
			HandItemController.SetHandItem(localPlayer, item, leftHand);
			return true;
		}
	}
	[HarmonyPatch(typeof(VisEquipment), "AttachItem", new Type[]
	{
		typeof(int),
		typeof(int),
		typeof(Transform),
		typeof(bool),
		typeof(bool),
		typeof(int)
	})]
	internal static class VisEquipmentAttachItemPatch
	{
		private static readonly MethodInfo ObjectEqualityMethod = AccessTools.Method(typeof(Object), "op_Equality", new Type[2]
		{
			typeof(Object),
			typeof(Object)
		}, (Type[])null);

		private static readonly MethodInfo ChooseAttachSourceMethod = AccessTools.Method(typeof(VisEquipmentAttachItemPatch), "ChooseAttachSource", (Type[])null, (Type[])null);

		[HarmonyTranspiler]
		[HarmonyPriority(800)]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			int num = FindAttachSelectionNullCheck(list);
			if (num < 0)
			{
				return list;
			}
			InjectAttachSelection(list, num);
			return list;
		}

		[HarmonyPostfix]
		private static void CorrectInvalidHandOffset(GameObject __result, Transform joint, VisualCloneCleaner.PreparedSourceScope __state)
		{
			//IL_0020: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)__result != (Object)null && (Object)(object)joint != (Object)null && IsHandJoint(joint))
				{
					Vector3 localPosition = __result.transform.localPosition;
					if (((Vector3)(ref localPosition)).sqrMagnitude > 100f)
					{
						__result.transform.localPosition = Vector3.zero;
					}
				}
			}
			finally
			{
				VisualCloneCleaner.ReleaseScope(__state);
			}
		}

		[HarmonyPrefix]
		private static void BeginPreparedSourceScope(out VisualCloneCleaner.PreparedSourceScope __state)
		{
			__state = VisualCloneCleaner.BeginScope();
		}

		[HarmonyFinalizer]
		private static Exception RestoreInstantiationState(Exception __exception, VisualCloneCleaner.PreparedSourceScope __state)
		{
			VisualCloneCleaner.ReleaseScope(__state);
			return __exception;
		}

		private static int FindAttachSelectionNullCheck(IReadOnlyList<CodeInstruction> code)
		{
			int num = -1;
			for (int i = 0; i < code.Count; i++)
			{
				if (!IsLoadLocal(code[i], 1))
				{
					continue;
				}
				int num2 = NextMeaningfulInstruction(code, i + 1);
				int num3 = NextMeaningfulInstruction(code, num2 + 1);
				int num4 = NextMeaningfulInstruction(code, num3 + 1);
				int num5 = NextMeaningfulInstruction(code, num4 + 1);
				int num6 = NextMeaningfulInstruction(code, num5 + 1);
				if (num2 >= 0 && num3 >= 0 && num4 >= 0 && num5 >= 0 && num6 >= 0 && !(code[num2].opcode != OpCodes.Ldnull) && CodeInstructionExtensions.Calls(code[num3], ObjectEqualityMethod) && code[num4].opcode.FlowControl == FlowControl.Cond_Branch && !(code[num5].opcode != OpCodes.Ldnull) && !(code[num6].opcode != OpCodes.Ret))
				{
					if (num >= 0)
					{
						return -1;
					}
					num = i;
				}
			}
			return num;
		}

		private static void InjectAttachSelection(List<CodeInstruction> code, int selectionIndex)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			CodeInstruction val = new CodeInstruction(OpCodes.Ldloc_0, (object)null);
			MoveLabelsAndBlocks(code[selectionIndex], val);
			code.InsertRange(selectionIndex, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[6]
			{
				val,
				new CodeInstruction(OpCodes.Ldloc_1, (object)null),
				new CodeInstruction(OpCodes.Ldarg_3, (object)null),
				new CodeInstruction(OpCodes.Ldarg_S, (object)(byte)5),
				new CodeInstruction(OpCodes.Call, (object)ChooseAttachSourceMethod),
				new CodeInstruction(OpCodes.Stloc_1, (object)null)
			});
		}

		private static int NextMeaningfulInstruction(IReadOnlyList<CodeInstruction> code, int startIndex)
		{
			for (int i = Math.Max(0, startIndex); i < code.Count; i++)
			{
				if (code[i].opcode != OpCodes.Nop)
				{
					return i;
				}
			}
			return -1;
		}

		private static bool IsLoadLocal(CodeInstruction instruction, int localIndex)
		{
			if (localIndex == 0 && instruction.opcode == OpCodes.Ldloc_0)
			{
				return true;
			}
			if (localIndex == 1 && instruction.opcode == OpCodes.Ldloc_1)
			{
				return true;
			}
			if (localIndex == 2 && instruction.opcode == OpCodes.Ldloc_2)
			{
				return true;
			}
			if (localIndex == 3 && instruction.opcode == OpCodes.Ldloc_3)
			{
				return true;
			}
			if (instruction.opcode != OpCodes.Ldloc && instruction.opcode != OpCodes.Ldloc_S)
			{
				return false;
			}
			if (instruction.operand is LocalBuilder localBuilder)
			{
				return localBuilder.LocalIndex == localIndex;
			}
			if (instruction.operand is LocalVariableInfo localVariableInfo)
			{
				return localVariableInfo.LocalIndex == localIndex;
			}
			if (!(instruction.operand is int num) || num != localIndex)
			{
				if (instruction.operand is byte b)
				{
					return b == localIndex;
				}
				return false;
			}
			return true;
		}

		private static void MoveLabelsAndBlocks(CodeInstruction source, CodeInstruction destination)
		{
			destination.labels.AddRange(source.labels);
			source.labels.Clear();
			destination.blocks.AddRange(source.blocks);
			source.blocks.Clear();
		}

		private static GameObject ChooseAttachSource(GameObject itemPrefab, GameObject vanillaSource, Transform joint, bool backAttach)
		{
			if ((Object)(object)itemPrefab == (Object)null || (Object)(object)joint == (Object)null || backAttach || ((Object)itemPrefab).name == "Lantern" || !IsHandJoint(joint))
			{
				return vanillaSource;
			}
			if (IsWeapon(itemPrefab) && (Object)(object)vanillaSource != (Object)null)
			{
				return vanillaSource;
			}
			GameObject val = AttachSourceSelector.Find(itemPrefab) ?? vanillaSource;
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			if (VisualCloneCleaner.TryPrepareSource(val, out var result, out var _))
			{
				return result;
			}
			return val;
		}

		private static bool IsWeapon(GameObject itemPrefab)
		{
			ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
			if ((Object)(object)component != (Object)null && component.m_itemData != null)
			{
				return component.m_itemData.IsWeapon();
			}
			return false;
		}

		private static bool IsHandJoint(Transform joint)
		{
			if (!(((Object)joint).name == "LeftHand_Attach"))
			{
				return ((Object)joint).name == "RightHand_Attach";
			}
			return true;
		}
	}
}