Decompiled source of WiresStandSwap v0.2.0

WiresStandSwap.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("WiresStandSwap")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("WiresStandSwap")]
[assembly: AssemblyTitle("WiresStandSwap")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace WiresStandSwap
{
	internal static class ArmorLoadouts
	{
		private sealed class Entry
		{
			internal int Index;

			internal int Hash;

			internal int Variant;

			internal byte[] Data;

			internal ItemData Displayed;

			internal ItemData Outgoing;

			internal ItemData StandCopy;

			internal Vector2i Destination;

			internal bool WasEquipped;

			internal bool Removed;

			internal bool Added;

			internal bool Queued;
		}

		private sealed class Transaction
		{
			internal Player Player;

			internal ArmorStand Stand;

			internal ZNetView View;

			internal readonly List<Entry> Slots = new List<Entry>();
		}

		private static readonly HashSet<int> Pending = new HashSet<int>();

		private static readonly MethodInfo QueueEquip = AccessTools.Method(typeof(Player), "QueueEquipAction", (Type[])null, (Type[])null);

		private static readonly FieldInfo ActionQueue = AccessTools.Field(typeof(Player), "m_actionQueue");

		private static readonly FieldInfo InventoryItems = AccessTools.Field(typeof(Inventory), "m_inventory");

		private static readonly MethodInfo InventoryChanged = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null);

		private static readonly MethodInfo SetupEquipment = AccessTools.Method(typeof(Humanoid), "SetupEquipment", (Type[])null, (Type[])null);

		private static int _equipmentBatchDepth;

		internal static bool SuppressEquipmentRefresh => _equipmentBatchDepth > 0;

		internal static bool ShouldSuppressEffect(EffectList effects)
		{
			return SuppressEquipmentRefresh;
		}

		internal static bool CanAttach(ArmorStand stand, int index, ItemData item)
		{
			//IL_0041: 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_004e: 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_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Invalid comparison between Unknown and I4
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Invalid comparison between Unknown and I4
			if ((Object)(object)stand == (Object)null || index < 0 || index >= stand.m_slots.Count || item == null || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return false;
			}
			ArmorStandSlot val = stand.m_slots[index];
			ItemType item2 = (((int)item.m_shared.m_attachOverride == 0) ? item.m_shared.m_itemType : item.m_shared.m_attachOverride);
			if (val.m_supportedTypes.Count > 0 && !val.m_supportedTypes.Contains(item2))
			{
				return false;
			}
			if ((int)item.m_shared.m_itemType == 7 || (int)item.m_shared.m_itemType == 11)
			{
				return true;
			}
			Transform transform = item.m_dropPrefab.transform;
			if (!((Object)(object)transform.Find("attach") != (Object)null))
			{
				return (Object)(object)transform.Find("attach_skin") != (Object)null;
			}
			return true;
		}

		private static bool IsBody(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0012: 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_0017: Invalid comparison between Unknown and I4
			//IL_0005: 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_0009: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			if ((int)type <= 11)
			{
				if (type - 6 <= 1 || (int)type == 11)
				{
					goto IL_001e;
				}
			}
			else if (type - 17 <= 1 || (int)type == 24)
			{
				goto IL_001e;
			}
			return false;
			IL_001e:
			return true;
		}

		private static bool FitsDisplayed(ArmorStand stand, int index, ItemData displayed, ItemData candidate)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (candidate == null || !CanAttach(stand, index, candidate))
			{
				return false;
			}
			if (IsBody(displayed.m_shared.m_itemType))
			{
				return displayed.m_shared.m_itemType == candidate.m_shared.m_itemType;
			}
			string text = UsedItems.Family(displayed);
			if (text != null)
			{
				return text == UsedItems.Family(candidate);
			}
			return false;
		}

		private static List<ItemData> RawItems(Inventory inventory)
		{
			if (inventory != null && !(InventoryItems == null))
			{
				return InventoryItems.GetValue(inventory) as List<ItemData>;
			}
			return null;
		}

		private static void NotifyInventoryChanged(Inventory inventory)
		{
			if (inventory == null || InventoryChanged == null)
			{
				throw new InvalidOperationException("Inventory refresh unavailable.");
			}
			InventoryChanged.Invoke(inventory, new object[2] { false, false });
		}

		private static void BeginEquipmentBatch(Transaction tx)
		{
			_equipmentBatchDepth++;
		}

		private static void EndEquipmentBatch(Player player)
		{
			if (_equipmentBatchDepth > 0)
			{
				_equipmentBatchDepth--;
				if (_equipmentBatchDepth <= 0 && (Object)(object)player != (Object)null && SetupEquipment != null)
				{
					SetupEquipment.Invoke(player, null);
				}
			}
		}

		private static bool ValidateSnapshot(Transaction tx)
		{
			Player player = tx.Player;
			ZNetView view = tx.View;
			if ((Object)(object)player == (Object)null || (Object)(object)view == (Object)null || !view.IsValid() || ActionsBusy(player))
			{
				return false;
			}
			ZDO zDO = view.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			int num = 0;
			for (int i = 0; i < tx.Stand.m_slots.Count; i++)
			{
				if (zDO.GetInt(StringExtensionMethods.GetStableHashCode(i + "_item"), 0) != 0)
				{
					num++;
				}
			}
			if (num != tx.Slots.Count)
			{
				return false;
			}
			foreach (Entry slot in tx.Slots)
			{
				int num2 = zDO.GetInt(StringExtensionMethods.GetStableHashCode(slot.Index + "_item"), 0);
				int num3 = zDO.GetInt(StringExtensionMethods.GetStableHashCode(slot.Index + "_variant"), 0);
				byte[] byteArray = zDO.GetByteArray(StringExtensionMethods.GetStableHashCode(slot.Index + "_itemData"), (byte[])null);
				if (num2 != slot.Hash || num3 != slot.Variant || !StandSwaps.EqualBytes(byteArray, slot.Data))
				{
					return false;
				}
			}
			return true;
		}

		private static bool ActionsBusy(Player player)
		{
			if (ActionQueue == null)
			{
				return true;
			}
			if (ActionQueue.GetValue(player) is ICollection collection)
			{
				return collection.Count > 0;
			}
			return true;
		}

		private static void Message(Player player, string text)
		{
			if (player != null)
			{
				((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}

		internal static bool TryStart(ArmorStand stand, Player player)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = (((Object)(object)stand == (Object)null) ? null : (Object.op_Implicit((Object)(object)stand.m_netViewOverride) ? stand.m_netViewOverride : ((Component)stand).GetComponent<ZNetView>()));
			if ((Object)(object)val == (Object)null || !val.IsValid() || (Object)(object)player == (Object)null || (Object)(object)Plugin.Instance == (Object)null || ((Character)player).InAttack() || ((Character)player).InDodge() || ((Character)player).IsStaggering() || ((Character)player).IsDead() || (((Character)player).IsSwimming() && !((Character)player).IsOnGround()) || !PrivateArea.CheckAccess(((Component)val).transform.position, 0f, false, false))
			{
				return false;
			}
			int instanceID = ((Object)val).GetInstanceID();
			if (Pending.Contains(instanceID))
			{
				return false;
			}
			Transaction tx = new Transaction
			{
				Player = player,
				Stand = stand,
				View = val
			};
			if (!Prepare(tx, first: true))
			{
				return false;
			}
			Pending.Add(instanceID);
			((MonoBehaviour)Plugin.Instance).StartCoroutine(WaitAndCommit(tx, instanceID));
			return true;
		}

		private static bool Prepare(Transaction tx, bool first)
		{
			//IL_052f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0531: Unknown result type (might be due to invalid IL or missing references)
			//IL_0537: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Invalid comparison between Unknown and I4
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Invalid comparison between Unknown and I4
			//IL_05f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_05f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_044e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0453: Unknown result type (might be due to invalid IL or missing references)
			//IL_047a: Unknown result type (might be due to invalid IL or missing references)
			Player player = tx.Player;
			if ((Object)(object)player == (Object)null || ActionsBusy(player))
			{
				Message(player, "Finish changing equipment first");
				return false;
			}
			Inventory inv = ((Humanoid)player).GetInventory();
			ZDO zDO = tx.View.GetZDO();
			if (inv == null || zDO == null)
			{
				return false;
			}
			HashSet<ItemData> usedItems = new HashSet<ItemData>();
			HashSet<Vector2i> hashSet = new HashSet<Vector2i>();
			List<Entry> list = new List<Entry>();
			for (int i = 0; i < tx.Stand.m_slots.Count; i++)
			{
				int stableHashCode = StringExtensionMethods.GetStableHashCode(i + "_item");
				int num = zDO.GetInt(stableHashCode, 0);
				if (num == 0)
				{
					continue;
				}
				if (!StandSwaps.IsEquipmentItem(num))
				{
					Message(player, "Loadout contains unsupported equipment");
					return false;
				}
				ItemData displayed = StandSwaps.ReadDisplayed(zDO, num, i);
				if (displayed == null || !displayed.IsEquipable() || displayed.m_stack != 1 || (displayed.m_shared.m_useDurability && displayed.m_durability <= 0f) || (displayed.m_shared.m_dlc.Length > 0 && !DLCMan.instance.IsDLCInstalled(displayed.m_shared.m_dlc)) || (Game.m_worldLevel > 0 && displayed.m_worldLevel < Game.m_worldLevel && ((int)displayed.m_shared.m_itemType == 18 || (int)displayed.m_shared.m_itemType == 24)))
				{
					Message(player, "Cannot equip part of this loadout");
					return false;
				}
				Func<ItemData, bool> func = (ItemData candidate) => candidate != null && !usedItems.Contains(candidate) && !player.IsEquipActionQueued(candidate) && inv.ContainsItem(candidate) && candidate.m_stack == 1 && FitsDisplayed(tx.Stand, i, displayed, candidate);
				ItemData val = null;
				if (!IsBody(displayed.m_shared.m_itemType))
				{
					foreach (ItemData allItem in inv.GetAllItems())
					{
						if (((Humanoid)player).IsItemEquiped(allItem) && UsedItems.Family(allItem) == UsedItems.Family(displayed) && func(allItem))
						{
							val = allItem;
							break;
						}
					}
				}
				if (val == null)
				{
					val = UsedItems.Find(player, displayed, func);
				}
				if (val == null && UsedItems.Find(player, displayed, (ItemData item) => !usedItems.Contains(item)) != null)
				{
					Message(player, "Stand cannot hold matching equipment");
					return false;
				}
				Entry entry = new Entry
				{
					Index = i,
					Hash = num,
					Displayed = displayed,
					Outgoing = val,
					Variant = zDO.GetInt(StringExtensionMethods.GetStableHashCode(i + "_variant"), 0),
					Data = zDO.GetByteArray(StringExtensionMethods.GetStableHashCode(i + "_itemData"), (byte[])null)
				};
				if (entry.Data != null)
				{
					entry.Data = (byte[])entry.Data.Clone();
				}
				if (val != null)
				{
					if (!usedItems.Add(val))
					{
						return false;
					}
					entry.Destination = val.m_gridPos;
					entry.WasEquipped = ((Humanoid)player).IsItemEquiped(val);
					hashSet.Add(entry.Destination);
				}
				list.Add(entry);
			}
			if (list.Count == 0)
			{
				Message(player, "Nothing on the armor stand");
				return false;
			}
			foreach (Entry item in list)
			{
				if (item.Outgoing == null)
				{
					if (!FindFreeCell(player, item.Displayed, hashSet, out var destination))
					{
						Message(player, "Not enough free equipment space");
						return false;
					}
					item.Destination = destination;
					hashSet.Add(destination);
				}
			}
			if (first)
			{
				tx.Slots.AddRange(list);
				return true;
			}
			if (tx.Slots.Count != list.Count)
			{
				return false;
			}
			for (int num2 = 0; num2 < list.Count; num2++)
			{
				Entry entry2 = tx.Slots[num2];
				Entry entry3 = list[num2];
				if (entry2.Index != entry3.Index || entry2.Hash != entry3.Hash || entry2.Variant != entry3.Variant || entry2.Outgoing != entry3.Outgoing || entry2.Destination != entry3.Destination || !StandSwaps.EqualBytes(entry2.Data, entry3.Data))
				{
					return false;
				}
			}
			return true;
		}

		private static bool FindFreeCell(Player player, ItemData item, HashSet<Vector2i> reserved, out Vector2i destination)
		{
			//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_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: 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_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			destination = new Vector2i(-1, -1);
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (IsBody(item.m_shared.m_itemType))
			{
				Type type = AccessTools.TypeByName("EquipmentAndQuickSlots.Slots");
				MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "GetEquipmentSlots", (Type[])null, (Type[])null));
				if (methodInfo != null)
				{
					try
					{
						if (!(methodInfo.Invoke(null, new object[1] { true }) is IEnumerable enumerable))
						{
							return false;
						}
						foreach (object item2 in enumerable)
						{
							Type type2 = item2.GetType();
							MethodInfo methodInfo2 = AccessTools.Method(type2, "ItemFits", (Type[])null, (Type[])null);
							MethodInfo methodInfo3 = AccessTools.Method(type, "WouldFitEquipmentSlot", (Type[])null, (Type[])null);
							PropertyInfo propertyInfo = AccessTools.Property(type2, "GridPosition");
							if (!(methodInfo2 == null) && !(propertyInfo == null) && (bool)methodInfo2.Invoke(item2, new object[1] { item }) && (!(methodInfo3 != null) || (bool)methodInfo3.Invoke(null, new object[2] { item2, item })))
							{
								Vector2i val = (Vector2i)propertyInfo.GetValue(item2, null);
								if (inventory.GetItemAt(val.x, val.y) == null && !reserved.Contains(val))
								{
									destination = val;
									return true;
								}
							}
						}
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("EAQS loadout slot lookup: " + ex));
						return false;
					}
				}
			}
			int num = inventory.GetHeight();
			Type type3 = AccessTools.TypeByName("EquipmentAndQuickSlots.Slots");
			if (type3 != null)
			{
				PropertyInfo propertyInfo2 = AccessTools.Property(type3, "VisibleRows");
				if (propertyInfo2 == null)
				{
					return false;
				}
				num = (int)propertyInfo2.GetValue(null, null);
			}
			Vector2i val2 = default(Vector2i);
			for (int i = 0; i < num; i++)
			{
				for (int j = 0; j < inventory.GetWidth(); j++)
				{
					((Vector2i)(ref val2))..ctor(j, i);
					if (inventory.GetItemAt(j, i) == null && !reserved.Contains(val2))
					{
						destination = val2;
						return true;
					}
				}
			}
			return false;
		}

		private static IEnumerator WaitAndCommit(Transaction tx, int id)
		{
			try
			{
				ZNetView view = tx.View;
				if (!((Object)(object)view == (Object)null) && view.IsValid())
				{
					if (!view.IsOwner())
					{
						view.InvokeRPC("RPC_RequestOwn", Array.Empty<object>());
					}
					float until = Time.time + 1.8f;
					while ((Object)(object)view != (Object)null && view.IsValid() && !view.IsOwner() && Time.time < until)
					{
						yield return (object)new WaitForSeconds(0.08f);
					}
					if ((Object)(object)view == (Object)null || !view.IsValid() || !view.IsOwner())
					{
						Message(tx.Player, "Stand busy; try again");
					}
					else if (Commit(tx))
					{
						yield return FlushVisuals(tx);
					}
				}
			}
			finally
			{
				Pending.Remove(id);
			}
		}

		private static IEnumerator FlushVisuals(Transaction tx)
		{
			ZNetView view = tx.View;
			foreach (Entry e in tx.Slots)
			{
				if (!((Object)(object)view == (Object)null) && view.IsValid())
				{
					int hash = ((e.StandCopy != null) ? StringExtensionMethods.GetStableHashCode(((Object)e.StandCopy.m_dropPrefab).name) : 0);
					int variant = ((e.StandCopy != null) ? e.StandCopy.m_variant : 0);
					if (hash != e.Hash || variant != e.Variant)
					{
						yield return null;
						view.InvokeRPC(ZNetView.Everybody, "RPC_SetVisualItem", new object[3] { e.Index, hash, variant });
					}
					continue;
				}
				yield break;
			}
		}

		private static bool Commit(Transaction tx)
		{
			//IL_0079: 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)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_06e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_036b: Unknown result type (might be due to invalid IL or missing references)
			//IL_057d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0582: Unknown result type (might be due to invalid IL or missing references)
			//IL_0590: Unknown result type (might be due to invalid IL or missing references)
			//IL_0596: Unknown result type (might be due to invalid IL or missing references)
			Player player = tx.Player;
			ZNetView view = tx.View;
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)view == (Object)null || !view.IsValid() || !view.IsOwner() || ((Character)player).InAttack() || ((Character)player).InDodge() || ((Character)player).IsStaggering() || ((Character)player).IsDead() || (((Character)player).IsSwimming() && !((Character)player).IsOnGround()) || Vector3.Distance(((Component)player).transform.position, ((Component)view).transform.position) > 5f || !PrivateArea.CheckAccess(((Component)view).transform.position, 0f, false, false))
			{
				return false;
			}
			if (!ValidateSnapshot(tx))
			{
				Message(player, "Loadout changed; try again");
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			ZDO zDO = view.GetZDO();
			List<ItemData> list = RawItems(inventory);
			if (inventory == null || zDO == null || list == null)
			{
				return false;
			}
			List<ItemData> list2 = new List<ItemData>();
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (((Humanoid)player).IsItemEquiped(allItem))
				{
					list2.Add(allItem);
				}
			}
			foreach (Entry slot in tx.Slots)
			{
				if (slot.Outgoing != null && (inventory.GetItemAt(slot.Destination.x, slot.Destination.y) != slot.Outgoing || slot.WasEquipped != ((Humanoid)player).IsItemEquiped(slot.Outgoing)))
				{
					Message(player, "Equipped items changed; try again");
					return false;
				}
				if (slot.Outgoing == null && inventory.GetItemAt(slot.Destination.x, slot.Destination.y) != null)
				{
					Message(player, "Inventory changed; try again");
					return false;
				}
			}
			bool flag = false;
			bool flag2 = false;
			try
			{
				flag = true;
				foreach (Entry slot2 in tx.Slots)
				{
					if (slot2.Outgoing != null)
					{
						slot2.StandCopy = slot2.Outgoing.Clone();
						slot2.StandCopy.m_stack = 1;
						slot2.StandCopy.m_equipped = false;
						StandSwaps.StripEquipmentCellTags(slot2.StandCopy);
					}
				}
				BeginEquipmentBatch(tx);
				flag2 = true;
				foreach (Entry slot3 in tx.Slots)
				{
					if (slot3.WasEquipped)
					{
						((Humanoid)player).UnequipItem(slot3.Outgoing, false);
					}
				}
				foreach (Entry slot4 in tx.Slots)
				{
					if (slot4.Outgoing != null)
					{
						if (!list.Remove(slot4.Outgoing))
						{
							throw new InvalidOperationException("Could not remove outgoing item.");
						}
						slot4.Removed = true;
					}
				}
				foreach (Entry slot5 in tx.Slots)
				{
					slot5.Displayed.m_equipped = false;
					slot5.Displayed.m_stack = 1;
					slot5.Displayed.m_gridPos = slot5.Destination;
					StandSwaps.StripEquipmentCellTags(slot5.Displayed);
					if (list.Contains(slot5.Displayed) || inventory.GetItemAt(slot5.Destination.x, slot5.Destination.y) != null)
					{
						throw new InvalidOperationException("Loadout destination became occupied.");
					}
					list.Add(slot5.Displayed);
					slot5.Added = true;
					if (slot5.Displayed.m_shared.m_equipDuration > 0f)
					{
						if (QueueEquip == null)
						{
							throw new InvalidOperationException("Vanilla equip queue unavailable");
						}
						QueueEquip.Invoke(player, new object[1] { slot5.Displayed });
						slot5.Queued = true;
					}
					else if (!((Humanoid)player).EquipItem(slot5.Displayed, false))
					{
						throw new InvalidOperationException("Could not equip loadout equipment.");
					}
				}
				flag2 = false;
				EndEquipmentBatch(player);
				NotifyInventoryChanged(inventory);
				foreach (Entry slot6 in tx.Slots)
				{
					int stableHashCode = StringExtensionMethods.GetStableHashCode(slot6.Index + "_itemData");
					int stableHashCode2 = StringExtensionMethods.GetStableHashCode(slot6.Index + "_item");
					int stableHashCode3 = StringExtensionMethods.GetStableHashCode(slot6.Index + "_variant");
					if (slot6.StandCopy != null)
					{
						ItemDrop.SaveToZDO(slot6.StandCopy, zDO, slot6.Index);
						zDO.Set(stableHashCode2, StringExtensionMethods.GetStableHashCode(((Object)slot6.StandCopy.m_dropPrefab).name), false);
						zDO.Set(stableHashCode3, slot6.StandCopy.m_variant, false);
					}
					else
					{
						zDO.RemoveByteArray(stableHashCode);
						zDO.Set(stableHashCode2, 0, false);
						zDO.Set(stableHashCode3, 0, false);
					}
				}
				tx.Stand.m_effects.Create(((Component)tx.Stand).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				Game.instance.IncrementPlayerStat((PlayerStatType)25, 1f, false);
				Plugin.Log.LogInfo((object)("Shift+E full armor-stand loadout: " + tx.Slots.Count + " pieces exchanged."));
				return true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Full loadout swap aborted: " + ex));
				if (flag)
				{
					try
					{
						if (!flag2)
						{
							BeginEquipmentBatch(tx);
							flag2 = true;
						}
						foreach (Entry slot7 in tx.Slots)
						{
							if (slot7.Queued)
							{
								((Humanoid)player).RemoveEquipAction(slot7.Displayed);
							}
							if (slot7.Added && list.Contains(slot7.Displayed))
							{
								if (((Humanoid)player).IsItemEquiped(slot7.Displayed))
								{
									((Humanoid)player).UnequipItem(slot7.Displayed, false);
								}
								list.Remove(slot7.Displayed);
							}
						}
						foreach (Entry slot8 in tx.Slots)
						{
							if (slot8.Removed && !list.Contains(slot8.Outgoing))
							{
								slot8.Outgoing.m_gridPos = slot8.Destination;
								list.Add(slot8.Outgoing);
							}
						}
						foreach (ItemData allItem2 in inventory.GetAllItems())
						{
							if (((Humanoid)player).IsItemEquiped(allItem2) && !list2.Contains(allItem2))
							{
								((Humanoid)player).UnequipItem(allItem2, false);
							}
						}
						foreach (ItemData item in list2)
						{
							if (list.Contains(item) && !((Humanoid)player).IsItemEquiped(item))
							{
								((Humanoid)player).EquipItem(item, false);
							}
						}
						if (flag2)
						{
							flag2 = false;
							EndEquipmentBatch(player);
						}
						NotifyInventoryChanged(inventory);
						foreach (Entry slot9 in tx.Slots)
						{
							int stableHashCode4 = StringExtensionMethods.GetStableHashCode(slot9.Index + "_itemData");
							int stableHashCode5 = StringExtensionMethods.GetStableHashCode(slot9.Index + "_item");
							if (slot9.Data != null)
							{
								zDO.Set(stableHashCode4, slot9.Data);
							}
							else
							{
								zDO.RemoveByteArray(stableHashCode4);
							}
							zDO.Set(stableHashCode5, slot9.Hash, false);
							zDO.Set(StringExtensionMethods.GetStableHashCode(slot9.Index + "_variant"), slot9.Variant, false);
						}
						foreach (Entry slot10 in tx.Slots)
						{
							view.InvokeRPC(ZNetView.Everybody, "RPC_SetVisualItem", new object[3] { slot10.Index, slot10.Hash, slot10.Variant });
						}
					}
					catch (Exception ex2)
					{
						if (flag2)
						{
							try
							{
								flag2 = false;
								EndEquipmentBatch(player);
							}
							catch (Exception ex3)
							{
								Plugin.Log.LogError((object)("Equipment refresh failed: " + ex3));
							}
							flag2 = false;
						}
						Plugin.Log.LogError((object)("CRITICAL loadout rollback failed: " + ex2));
						Message(player, "Loadout rollback error: check log");
						return false;
					}
				}
				if (flag2)
				{
					try
					{
						flag2 = false;
						EndEquipmentBatch(player);
					}
					catch (Exception ex4)
					{
						Plugin.Log.LogError((object)("Equipment refresh failed: " + ex4));
					}
				}
				Message(player, "Loadout cancelled; nothing changed");
				return false;
			}
		}

		internal static void Clear()
		{
			Pending.Clear();
			_equipmentBatchDepth = 0;
		}
	}
	internal static class EquipmentSlotsCompat
	{
		private static bool IsArmorType(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0012: 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_0017: Invalid comparison between Unknown and I4
			//IL_0005: 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_0009: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			if ((int)type <= 11)
			{
				if (type - 6 <= 1 || (int)type == 11)
				{
					goto IL_001e;
				}
			}
			else if (type - 17 <= 1 || (int)type == 24)
			{
				goto IL_001e;
			}
			return false;
			IL_001e:
			return true;
		}

		internal static ItemData FindOccupiedSlot(Player player, ItemData displayed)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: 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_0168: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || displayed == null || !IsArmorType(displayed.m_shared.m_itemType))
			{
				return null;
			}
			Type type = AccessTools.TypeByName("EquipmentAndQuickSlots.Slots");
			if (type == null)
			{
				return null;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "GetEquipmentSlots", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(type, "WouldFitEquipmentSlot", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return null;
			}
			try
			{
				IEnumerable enumerable = methodInfo.Invoke(null, new object[1] { true }) as IEnumerable;
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (enumerable == null || inventory == null)
				{
					return null;
				}
				foreach (object item in enumerable)
				{
					if (item == null)
					{
						continue;
					}
					Type type2 = item.GetType();
					MethodInfo methodInfo3 = AccessTools.Method(type2, "ItemFits", (Type[])null, (Type[])null);
					PropertyInfo propertyInfo = AccessTools.Property(type2, "GridPosition");
					if (!(methodInfo3 == null) && !(propertyInfo == null) && (bool)methodInfo3.Invoke(item, new object[1] { displayed }) && (!(methodInfo2 != null) || (bool)methodInfo2.Invoke(null, new object[2] { item, displayed })))
					{
						Vector2i val = (Vector2i)propertyInfo.GetValue(item, null);
						ItemData itemAt = inventory.GetItemAt(val.x, val.y);
						if (itemAt != null && itemAt.m_shared.m_itemType == displayed.m_shared.m_itemType && !player.IsEquipActionQueued(itemAt))
						{
							return itemAt;
						}
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("EAQS occupied-slot lookup failed: " + ex));
				}
			}
			return null;
		}

		internal static bool FindFreeSlot(Player player, ItemData item, out Vector2i position, out bool hasMatchingCells, out bool lookupFailed)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: 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_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			position = Vector2i.zero;
			hasMatchingCells = false;
			lookupFailed = false;
			if ((Object)(object)player == (Object)null || item == null || !IsArmorType(item.m_shared.m_itemType))
			{
				return false;
			}
			Type type = AccessTools.TypeByName("EquipmentAndQuickSlots.Slots");
			if (type == null)
			{
				return false;
			}
			MethodInfo methodInfo = AccessTools.Method(type, "GetEquipmentSlots", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				lookupFailed = true;
				return false;
			}
			try
			{
				if (!(methodInfo.Invoke(null, new object[1] { true }) is IEnumerable enumerable))
				{
					lookupFailed = true;
					return false;
				}
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (inventory == null)
				{
					return false;
				}
				foreach (object item2 in enumerable)
				{
					if (item2 == null)
					{
						continue;
					}
					Type type2 = item2.GetType();
					MethodInfo methodInfo2 = AccessTools.Method(type2, "ItemFits", (Type[])null, (Type[])null);
					PropertyInfo propertyInfo = AccessTools.Property(type2, "GridPosition");
					if (methodInfo2 == null || propertyInfo == null || !(bool)methodInfo2.Invoke(item2, new object[1] { item }))
					{
						continue;
					}
					MethodInfo methodInfo3 = AccessTools.Method(type, "WouldFitEquipmentSlot", (Type[])null, (Type[])null);
					if (!(methodInfo3 != null) || (bool)methodInfo3.Invoke(null, new object[2] { item2, item }))
					{
						hasMatchingCells = true;
						Vector2i val = (Vector2i)propertyInfo.GetValue(item2, null);
						if (inventory.GetItemAt(val.x, val.y) == null)
						{
							position = val;
							return true;
						}
					}
				}
			}
			catch (Exception ex)
			{
				lookupFailed = true;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("EAQS slot discovery failed: " + ex));
				}
			}
			return false;
		}
	}
	[BepInPlugin("wire.valheim.standswap", "Wire's Stand Swap", "0.2.0")]
	[BepInProcess("valheim.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "wire.valheim.standswap";

		public const string Name = "Wire's Stand Swap";

		public const string Version = "0.2.0";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("wire.valheim.standswap");
			_harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Wire's Stand Swap 0.2.0 loaded.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			Instance = null;
			Log = null;
			StandSwaps.Clear();
			ArmorLoadouts.Clear();
			StandRegistry.Clear();
			UsedItems.Clear();
		}
	}
	internal static class InputAndEquipment
	{
		[HarmonyPatch(typeof(ItemStand), "Interact")]
		private static class ItemStandPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(ItemStand __instance, Humanoid user, bool hold, ref bool __result)
			{
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Expected O, but got Unknown
				if (!Pressed(user, hold) || !__instance.m_canBeRemoved || (Object)(object)__instance.m_guardianPower != (Object)null || !__instance.HaveAttachment())
				{
					return true;
				}
				if (!StandSwaps.IsEquipmentItem(__instance.GetAttachedItem()))
				{
					return true;
				}
				__result = StandSwaps.TryItemStand(__instance, (Player)user);
				return false;
			}
		}

		[HarmonyPatch(typeof(Player), "Interact")]
		private static class FullLoadoutPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(Player __instance, GameObject go, bool hold)
			{
				if ((Object)(object)go == (Object)null || (!ZInput.GetKey((KeyCode)304, true) && !ZInput.GetKey((KeyCode)303, true)) || !ZInput.GetKey((KeyCode)101, true))
				{
					return true;
				}
				ArmorStand val = StandRegistry.Resolve(go);
				if ((Object)(object)val == (Object)null)
				{
					return true;
				}
				if (!hold)
				{
					ArmorLoadouts.TryStart(val, __instance);
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(ArmorStand), "Awake")]
		private static class RegisterArmorStandPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ArmorStand __instance)
			{
				StandRegistry.Register(__instance);
			}
		}

		[HarmonyPatch(typeof(ItemStand), "GetHoverText")]
		private static class ItemStandHoverPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ItemStand __instance, ref string __result)
			{
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				if (__instance.m_canBeRemoved && !((Object)(object)__instance.m_guardianPower != (Object)null) && __instance.HaveAttachment() && StandSwaps.IsEquipmentItem(__instance.GetAttachedItem()) && PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, false, false))
				{
					__result = __result + "\n[<color=yellow><b>Shift + E</b></color>] " + StandSwaps.DescribeAction(__instance.GetAttachedItem(), __instance);
				}
			}
		}

		[HarmonyPatch(typeof(Switch), "GetHoverText")]
		private static class ArmorStandHoverPatch
		{
			private const string Shortcut = "\n[<color=yellow><b>Shift + E</b></color>] Swap full loadout";

			[HarmonyPostfix]
			private static void Postfix(Switch __instance, ref string __result)
			{
				if ((Object)(object)StandRegistry.ForSwitch(__instance) != (Object)null)
				{
					__result += "\n[<color=yellow><b>Shift + E</b></color>] Swap full loadout";
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "SetupEquipment")]
		private static class BatchEquipmentRefreshPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(Humanoid __instance)
			{
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
				{
					return !ArmorLoadouts.SuppressEquipmentRefresh;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(EffectList), "Create")]
		private static class BatchEquipmentEffectsPatch
		{
			[HarmonyPrefix]
			private static bool Prefix(EffectList __instance, ref GameObject[] __result)
			{
				if (!ArmorLoadouts.ShouldSuppressEffect(__instance))
				{
					return true;
				}
				__result = (GameObject[])(object)new GameObject[0];
				return false;
			}
		}

		[HarmonyPatch(typeof(Humanoid), "EquipItem")]
		private static class TrackEquipmentPatch
		{
			[HarmonyPostfix]
			private static void Postfix(Humanoid __instance, ItemData item, bool __result)
			{
				if (__result && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					UsedItems.Record(item);
				}
			}
		}

		internal static bool Pressed(Humanoid user, bool hold)
		{
			if (!hold && (Object)(object)user != (Object)null && (Object)(object)user == (Object)(object)Player.m_localPlayer && (ZInput.GetKey((KeyCode)304, true) || ZInput.GetKey((KeyCode)303, true)))
			{
				return ZInput.GetKey((KeyCode)101, true);
			}
			return false;
		}
	}
	internal static class StandRegistry
	{
		private sealed class Owner
		{
			internal ArmorStand Stand;
		}

		private static ConditionalWeakTable<Switch, Owner> _owners = new ConditionalWeakTable<Switch, Owner>();

		internal static void Register(ArmorStand stand)
		{
			if ((Object)(object)stand == (Object)null || stand.m_slots == null)
			{
				return;
			}
			foreach (ArmorStandSlot slot in stand.m_slots)
			{
				RegisterSwitch(slot.m_switch, stand);
			}
			RegisterSwitch(stand.m_changePoseSwitch, stand);
		}

		private static void RegisterSwitch(Switch sw, ArmorStand stand)
		{
			if (!((Object)(object)sw == (Object)null))
			{
				_owners.Remove(sw);
				_owners.Add(sw, new Owner
				{
					Stand = stand
				});
			}
		}

		internal static ArmorStand ForSwitch(Switch sw)
		{
			if ((Object)(object)sw == (Object)null)
			{
				return null;
			}
			if (!_owners.TryGetValue(sw, out var value) || !((Object)(object)value.Stand != (Object)null))
			{
				return null;
			}
			return value.Stand;
		}

		internal static ArmorStand Resolve(GameObject hovered)
		{
			if ((Object)(object)hovered == (Object)null)
			{
				return null;
			}
			ArmorStand val = ForSwitch(hovered.GetComponentInParent<Switch>());
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			return hovered.GetComponentInParent<ArmorStand>();
		}

		internal static void Clear()
		{
			_owners = new ConditionalWeakTable<Switch, Owner>();
		}
	}
	internal static class StandSwaps
	{
		private sealed class Request
		{
			internal Player Player;

			internal ZNetView View;

			internal ItemStand ItemStand;

			internal ArmorStand ArmorStand;

			internal int Index;

			internal int ExpectedHash;

			internal byte[] ExpectedData;

			internal int OldQuality;

			internal int OldVariant;

			internal int OldSlotVariant;

			internal ItemData ExpectedOutgoing;

			internal bool WillReplace;

			internal ItemType DisplayedType;

			internal string DisplayedFamily;

			internal bool UseEmptyEquipmentCell;

			internal Vector2i EmptyEquipmentCell;
		}

		private static readonly HashSet<int> Pending = new HashSet<int>();

		private static readonly MethodInfo ItemCanAttach = AccessTools.Method(typeof(ItemStand), "CanAttach", (Type[])null, (Type[])null);

		private static readonly MethodInfo ArmorCanAttach = AccessTools.Method(typeof(ArmorStand), "CanAttach", (Type[])null, (Type[])null);

		private static readonly MethodInfo QueueEquip = AccessTools.Method(typeof(Player), "QueueEquipAction", (Type[])null, (Type[])null);

		private static readonly MethodInfo UpdateArmorSupports = AccessTools.Method(typeof(ArmorStand), "UpdateSupports", (Type[])null, (Type[])null);

		private static readonly FieldInfo ArmorCloths = AccessTools.Field(typeof(ArmorStand), "m_cloths");

		internal static bool IsEquipmentItem(int prefabHash)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (prefabHash == 0 || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return false;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabHash);
			if ((Object)(object)itemPrefab == (Object)null)
			{
				return false;
			}
			ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
			if ((Object)(object)component != (Object)null)
			{
				return IsExchangeable(component.m_itemData.m_shared.m_itemType);
			}
			return false;
		}

		private static bool IsExchangeable(ItemType t)
		{
			//IL_0000: 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_000d: Invalid comparison between Unknown and I4
			//IL_001c: 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_0021: Invalid comparison between Unknown and I4
			//IL_000f: 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)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			if (UsedItems.IsWeaponOrTool(t))
			{
				return true;
			}
			if ((int)t <= 11)
			{
				if (t - 5 <= 2 || (int)t == 11)
				{
					goto IL_0028;
				}
			}
			else if (t - 17 <= 1 || (int)t == 24)
			{
				goto IL_0028;
			}
			return false;
			IL_0028:
			return true;
		}

		internal static bool TryItemStand(ItemStand stand, Player player)
		{
			if ((Object)(object)stand == (Object)null || !stand.m_canBeRemoved || (Object)(object)stand.m_guardianPower != (Object)null)
			{
				return false;
			}
			ZNetView view = (Object.op_Implicit((Object)(object)stand.m_netViewOverride) ? stand.m_netViewOverride : ((Component)stand).GetComponent<ZNetView>());
			return TryStart(new Request
			{
				Player = player,
				ItemStand = stand,
				View = view,
				Index = -1
			});
		}

		internal static bool TryArmorStand(ArmorStand stand, int index, Player player)
		{
			if ((Object)(object)stand == (Object)null || index < 0 || index >= stand.m_slots.Count)
			{
				return false;
			}
			ZNetView view = (Object.op_Implicit((Object)(object)stand.m_netViewOverride) ? stand.m_netViewOverride : ((Component)stand).GetComponent<ZNetView>());
			return TryStart(new Request
			{
				Player = player,
				ArmorStand = stand,
				View = view,
				Index = index
			});
		}

		internal static string DescribeAction(int displayedHash, ItemStand itemStand = null, ArmorStand armorStand = null, int index = -1)
		{
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				return "Swap / equip";
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(displayedHash);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return "Swap / equip";
			}
			ItemData val2 = val.m_itemData.Clone();
			val2.m_dropPrefab = itemPrefab;
			string text = ((Localization.instance != null) ? Localization.instance.Localize(val2.m_shared.m_name) : val2.m_shared.m_name);
			Player localPlayer = Player.m_localPlayer;
			Request request = new Request
			{
				Player = localPlayer,
				ItemStand = itemStand,
				ArmorStand = armorStand,
				Index = index,
				DisplayedType = val2.m_shared.m_itemType,
				DisplayedFamily = UsedItems.Family(val2)
			};
			return (((Object)(object)localPlayer != (Object)null && UsedItems.Find(localPlayer, val2, (ItemData candidate) => IsValidReplacement(request, candidate)) != null) ? "Swap " : "Equip ") + text;
		}

		private static int ItemKey(int index)
		{
			if (index >= 0)
			{
				return StringExtensionMethods.GetStableHashCode(index + "_item");
			}
			return ZDOVars.s_item;
		}

		private static int ItemDataKey(int index)
		{
			if (index >= 0)
			{
				return StringExtensionMethods.GetStableHashCode(index + "_itemData");
			}
			return ZDOVars.s_itemData;
		}

		internal static bool EqualBytes(byte[] a, byte[] b)
		{
			if (a == null || b == null)
			{
				return a == b;
			}
			if (a.Length != b.Length)
			{
				return false;
			}
			for (int i = 0; i < a.Length; i++)
			{
				if (a[i] != b[i])
				{
					return false;
				}
			}
			return true;
		}

		internal static void StripEquipmentCellTags(ItemData item)
		{
			if (item?.m_customData != null)
			{
				item.m_customData.Remove("eaqs_player");
				item.m_customData.Remove("eaqs_slot");
				item.m_customData.Remove("eaqs_parked");
			}
		}

		private static void Notify(Player player, string message)
		{
			if ((Object)(object)player != (Object)null)
			{
				((Character)player).Message((MessageType)2, message, 0, (Sprite)null, false);
			}
		}

		private static bool TryStart(Request request)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			Player player = request.Player;
			ZNetView view = request.View;
			if ((Object)(object)player == (Object)null || (Object)(object)view == (Object)null || !view.IsValid() || !PrivateArea.CheckAccess(((Component)view).transform.position, 0f, false, false))
			{
				return false;
			}
			if (((Character)player).InAttack() || ((Character)player).InDodge() || ((Character)player).IsStaggering() || ((Character)player).IsDead())
			{
				return false;
			}
			ZDO zDO = view.GetZDO();
			int num = zDO.GetInt(ItemKey(request.Index), 0);
			if (!IsEquipmentItem(num))
			{
				return false;
			}
			ItemData val = ReadDisplayed(zDO, num, request.Index);
			if (val != null)
			{
				request.DisplayedType = val.m_shared.m_itemType;
				request.DisplayedFamily = UsedItems.Family(val);
			}
			ItemData val2 = UsedItems.Find(player, val, (ItemData candidate) => IsValidReplacement(request, candidate));
			if (val2 == null && UsedItems.Find(player, val) != null)
			{
				Notify(player, "Stand cannot hold matching equipment");
				return false;
			}
			if (val == null || !val.IsEquipable())
			{
				Notify(player, "Cannot equip displayed item");
				return false;
			}
			if (val.m_shared.m_useDurability && val.m_durability <= 0f)
			{
				Notify(player, "Displayed item is broken");
				return false;
			}
			if (((Character)player).IsSwimming() && !((Character)player).IsOnGround())
			{
				Notify(player, "Cannot equip while swimming");
				return false;
			}
			if (val2 != null && !IsValidReplacement(request, val2))
			{
				Notify(player, "Stand cannot hold your current equipment");
				return false;
			}
			request.ExpectedOutgoing = val2;
			request.WillReplace = val2 != null;
			if (val2 == null)
			{
				request.UseEmptyEquipmentCell = EquipmentSlotsCompat.FindFreeSlot(player, val, out request.EmptyEquipmentCell, out var hasMatchingCells, out var lookupFailed);
				if (lookupFailed)
				{
					Notify(player, "Equipment slots unavailable; try again");
					return false;
				}
				if (!request.UseEmptyEquipmentCell && hasMatchingCells)
				{
					Notify(player, "Equipment slot occupied; clear it first");
					return false;
				}
				if (!request.UseEmptyEquipmentCell && !((Humanoid)player).GetInventory().CanAddItem(val, -1))
				{
					Notify(player, "Inventory full; cannot take equipment");
					return false;
				}
			}
			int instanceID = ((Object)view).GetInstanceID();
			if (Pending.Contains(instanceID) || (Object)(object)Plugin.Instance == (Object)null)
			{
				return false;
			}
			byte[] byteArray = zDO.GetByteArray(ItemDataKey(request.Index), (byte[])null);
			request.ExpectedHash = num;
			request.ExpectedData = ((byteArray != null) ? ((byte[])byteArray.Clone()) : null);
			request.OldQuality = zDO.GetInt(ZDOVars.s_quality, 1);
			request.OldVariant = zDO.GetInt(ZDOVars.s_variant, 0);
			request.OldSlotVariant = ((request.Index >= 0) ? zDO.GetInt(StringExtensionMethods.GetStableHashCode(request.Index + "_variant"), 0) : 0);
			Plugin.Log.LogInfo((object)("Shift+E " + (request.WillReplace ? "swap " : ("equip into " + (request.UseEmptyEquipmentCell ? "empty EAQS cell " : "inventory "))) + ((object)Unsafe.As<ItemType, ItemType>(ref val.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString() + " standSlot=" + request.Index + " displayed=" + val.m_shared.m_name + ", outgoing=" + ((val2 != null) ? (val2.m_shared.m_name + " (" + ((object)Unsafe.As<ItemType, ItemType>(ref val2.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString() + ")") : "<none>")));
			Pending.Add(instanceID);
			((MonoBehaviour)Plugin.Instance).StartCoroutine(WaitForOwnership(request, instanceID));
			return true;
		}

		internal static ItemData ReadDisplayed(ZDO zdo, int hash, int index)
		{
			if (!IsEquipmentItem(hash))
			{
				return null;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(hash);
			ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			ItemData val2 = val.m_itemData.Clone();
			val2.m_dropPrefab = itemPrefab;
			ItemDrop.LoadFromZDO(val2, zdo, index);
			if (zdo.GetByteArray(ItemDataKey(index), (byte[])null) == null)
			{
				val2.m_variant = zdo.GetInt((index < 0) ? ZDOVars.s_variant : StringExtensionMethods.GetStableHashCode(index + "_variant"), 0);
				if (index < 0)
				{
					val2.m_quality = zdo.GetInt(ZDOVars.s_quality, val2.m_quality);
				}
			}
			val2.m_equipped = false;
			return val2;
		}

		private static bool IsValidReplacement(Request request, ItemData item)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Invalid comparison between Unknown and I4
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Invalid comparison between Unknown and I4
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Invalid comparison between Unknown and I4
			if (item == null || item.m_stack != 1 || (Object)(object)item.m_dropPrefab == (Object)null || !((Humanoid)request.Player).GetInventory().ContainsItem(item))
			{
				return false;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if (!IsExchangeable(itemType))
			{
				return false;
			}
			try
			{
				if ((Object)(object)request.ItemStand != (Object)null)
				{
					if (ItemCanAttach == null)
					{
						return false;
					}
					return (bool)ItemCanAttach.Invoke(request.ItemStand, new object[1] { item });
				}
				if ((Object)(object)request.ArmorStand != (Object)null)
				{
					if ((int)request.DisplayedType == 5 || UsedItems.IsWeaponOrTool(request.DisplayedType))
					{
						if (request.DisplayedFamily == null || UsedItems.Family(item) != request.DisplayedFamily)
						{
							return false;
						}
					}
					else if (itemType != request.DisplayedType)
					{
						return false;
					}
					if (ArmorCanAttach == null)
					{
						return false;
					}
					ArmorStandSlot val = request.ArmorStand.m_slots[request.Index];
					if (!(bool)ArmorCanAttach.Invoke(request.ArmorStand, new object[2] { val, item }))
					{
						return false;
					}
					if ((int)itemType == 11 || (int)itemType == 7)
					{
						return true;
					}
					Transform transform = item.m_dropPrefab.transform;
					return (Object)(object)transform.Find("attach") != (Object)null || (Object)(object)transform.Find("attach_skin") != (Object)null;
				}
			}
			catch (Exception)
			{
				return false;
			}
			return false;
		}

		private static IEnumerator WaitForOwnership(Request request, int pendingId)
		{
			try
			{
				ZNetView view = request.View;
				if (!((Object)(object)view == (Object)null) && view.IsValid())
				{
					if (!view.IsOwner())
					{
						view.InvokeRPC("RPC_RequestOwn", Array.Empty<object>());
					}
					float timeout = Time.time + 1.8f;
					while ((Object)(object)view != (Object)null && view.IsValid() && !view.IsOwner() && Time.time < timeout)
					{
						yield return (object)new WaitForSeconds(0.08f);
					}
					if ((Object)(object)view == (Object)null || !view.IsValid() || !view.IsOwner())
					{
						Notify(request.Player, "Stand busy; try again");
					}
					else
					{
						Commit(request);
					}
				}
			}
			finally
			{
				Pending.Remove(pendingId);
			}
		}

		private static bool Commit(Request request)
		{
			//IL_07f5: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02de: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0425: Unknown result type (might be due to invalid IL or missing references)
			//IL_042a: Unknown result type (might be due to invalid IL or missing references)
			//IL_065d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0662: Unknown result type (might be due to invalid IL or missing references)
			//IL_0670: Unknown result type (might be due to invalid IL or missing references)
			//IL_0676: Unknown result type (might be due to invalid IL or missing references)
			//IL_077d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0782: Unknown result type (might be due to invalid IL or missing references)
			//IL_0790: Unknown result type (might be due to invalid IL or missing references)
			//IL_0796: Unknown result type (might be due to invalid IL or missing references)
			Player player = request.Player;
			ZNetView view = request.View;
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || (Object)(object)view == (Object)null || !view.IsValid() || !view.IsOwner() || ((Character)player).InAttack() || ((Character)player).InDodge() || ((Character)player).IsStaggering() || ((Character)player).IsDead() || Vector3.Distance(((Component)player).transform.position, ((Component)view).transform.position) > 5f || !PrivateArea.CheckAccess(((Component)view).transform.position, 0f, false, false))
			{
				return false;
			}
			ZDO zDO = view.GetZDO();
			int num = ItemKey(request.Index);
			int num2 = ItemDataKey(request.Index);
			if (zDO.GetInt(num, 0) != request.ExpectedHash || !EqualBytes(zDO.GetByteArray(num2, (byte[])null), request.ExpectedData))
			{
				Notify(player, "Stand changed; try again");
				return false;
			}
			ItemData val = ReadDisplayed(zDO, request.ExpectedHash, request.Index);
			ItemData val2 = UsedItems.Find(player, val, (ItemData candidate) => IsValidReplacement(request, candidate));
			if (val2 != request.ExpectedOutgoing)
			{
				Notify(player, "Equipment changed; try again");
				return false;
			}
			if (val == null || val.m_stack != 1 || !val.IsEquipable() || val.m_shared.m_itemType != request.DisplayedType || (request.WillReplace && !IsValidReplacement(request, val2)))
			{
				Notify(player, "Cannot swap this equipment");
				return false;
			}
			if (!request.WillReplace)
			{
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (request.UseEmptyEquipmentCell)
				{
					Vector2i emptyEquipmentCell = request.EmptyEquipmentCell;
					if (inventory.GetItemAt(emptyEquipmentCell.x, emptyEquipmentCell.y) != null)
					{
						Notify(player, "Equipment slot changed; try again");
						return false;
					}
				}
				else if (!inventory.CanAddItem(val, -1))
				{
					Notify(player, "Inventory full; cannot take equipment");
					return false;
				}
			}
			if (((Character)player).IsSwimming() && !((Character)player).IsOnGround())
			{
				Notify(player, "Cannot equip while swimming");
				return false;
			}
			if (val.m_shared.m_useDurability && val.m_durability <= 0f)
			{
				Notify(player, "Displayed item is broken");
				return false;
			}
			if (val.m_shared.m_equipDuration > 0f && QueueEquip == null)
			{
				Notify(player, "Cannot start equipping");
				return false;
			}
			if (request.WillReplace && player.IsEquipActionQueued(val2))
			{
				Notify(player, "Finish changing equipment first");
				return false;
			}
			Inventory inventory2 = ((Humanoid)player).GetInventory();
			Vector2i val3 = (request.WillReplace ? val2.m_gridPos : Vector2i.zero);
			bool flag = request.WillReplace && ((Humanoid)player).IsItemEquiped(val2);
			ItemData val4 = (request.WillReplace ? val2.Clone() : null);
			if (val4 != null)
			{
				val4.m_equipped = false;
				val4.m_stack = 1;
				StripEquipmentCellTags(val4);
			}
			val.m_stack = 1;
			val.m_equipped = false;
			StripEquipmentCellTags(val);
			bool flag2 = false;
			bool flag3 = false;
			bool flag4 = false;
			bool flag5 = false;
			try
			{
				if (flag)
				{
					((Humanoid)player).UnequipItem(val2, false);
				}
				if (request.WillReplace)
				{
					if (!inventory2.RemoveOneItem(val2))
					{
						throw new InvalidOperationException("Could not remove outgoing item.");
					}
					flag2 = true;
				}
				if (val.m_shared.m_equipDuration > 0f)
				{
					QueueEquip.Invoke(player, new object[1] { val });
					flag5 = true;
				}
				bool num3 = request.WillReplace || request.UseEmptyEquipmentCell;
				Vector2i val5 = (request.WillReplace ? val3 : request.EmptyEquipmentCell);
				if (!(num3 ? inventory2.AddItem(val, val5) : inventory2.AddItem(val)))
				{
					throw new InvalidOperationException("No inventory room for incoming item.");
				}
				flag3 = true;
				if (num3 && val.m_gridPos != val5)
				{
					throw new InvalidOperationException("Item did not occupy expected slot.");
				}
				flag4 = true;
				if (val4 != null)
				{
					ItemDrop.SaveToZDO(val4, zDO, request.Index);
					if (request.Index < 0)
					{
						zDO.Set(ZDOVars.s_quality, val4.m_quality, false);
						zDO.Set(ZDOVars.s_variant, val4.m_variant, false);
					}
					zDO.Set(num, StringExtensionMethods.GetStableHashCode(((Object)val4.m_dropPrefab).name), false);
				}
				else
				{
					zDO.RemoveByteArray(num2);
					zDO.Set(num, 0, false);
					if (request.Index < 0)
					{
						zDO.Set(ZDOVars.s_quality, 0, false);
						zDO.Set(ZDOVars.s_variant, 0, false);
					}
				}
				if (request.Index >= 0)
				{
					zDO.Set(StringExtensionMethods.GetStableHashCode(request.Index + "_variant"), val4?.m_variant ?? 0, false);
				}
				if (!flag5 && !((Humanoid)player).EquipItem(val, false))
				{
					throw new InvalidOperationException("Could not equip displayed item.");
				}
				int num4 = ((val4 != null) ? StringExtensionMethods.GetStableHashCode(((Object)val4.m_dropPrefab).name) : 0);
				int num5 = val4?.m_variant ?? 0;
				int num6 = val4?.m_quality ?? 0;
				bool flag6 = num4 != 0 && request.ExpectedHash == num4 && val.m_variant == num5;
				if ((Object)(object)request.ItemStand != (Object)null)
				{
					int num7 = zDO.GetInt(ZDOVars.s_type, 0);
					if (flag6)
					{
						view.InvokeRPC(ZNetView.Everybody, "SetVisualItem", new object[4] { 0, 0, 0, num7 });
					}
					view.InvokeRPC(ZNetView.Everybody, "SetVisualItem", new object[4] { num4, num5, num6, num7 });
					request.ItemStand.m_effects.Create(((Component)request.ItemStand).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
					Game.instance.IncrementPlayerStat((PlayerStatType)24, 1f, false);
				}
				else
				{
					if (flag6)
					{
						view.InvokeRPC(ZNetView.Everybody, "RPC_SetVisualItem", new object[3] { request.Index, 0, 0 });
					}
					view.InvokeRPC(ZNetView.Everybody, "RPC_SetVisualItem", new object[3] { request.Index, num4, num5 });
					if (num4 == 0)
					{
						UpdateArmorSupports?.Invoke(request.ArmorStand, null);
						ArmorCloths?.SetValue(request.ArmorStand, ((Component)request.ArmorStand).GetComponentsInChildren<Cloth>());
					}
					request.ArmorStand.m_effects.Create(((Component)request.ArmorStand).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
					Game.instance.IncrementPlayerStat((PlayerStatType)25, 1f, false);
				}
				return true;
			}
			catch (Exception ex)
			{
				if (flag4 || flag2 || flag3 || flag5)
				{
					try
					{
						if (flag5)
						{
							((Humanoid)player).RemoveEquipAction(val);
						}
						if (flag3)
						{
							((Humanoid)player).UnequipItem(val, false);
							inventory2.RemoveOneItem(val);
						}
						if (flag2)
						{
							inventory2.AddItem(val2, val3);
							if (flag)
							{
								((Humanoid)player).EquipItem(val2, false);
							}
						}
						if (flag4)
						{
							if (request.ExpectedData == null)
							{
								zDO.RemoveByteArray(num2);
							}
							else
							{
								zDO.Set(num2, request.ExpectedData);
							}
							zDO.Set(num, request.ExpectedHash, false);
							if (request.Index < 0)
							{
								zDO.Set(ZDOVars.s_quality, request.OldQuality, false);
								zDO.Set(ZDOVars.s_variant, request.OldVariant, false);
							}
							if (request.Index >= 0)
							{
								zDO.Set(StringExtensionMethods.GetStableHashCode(request.Index + "_variant"), request.OldSlotVariant, false);
							}
						}
					}
					catch (Exception ex2)
					{
						Plugin.Log.LogError((object)ex2);
					}
				}
				else if (flag)
				{
					((Humanoid)player).EquipItem(val2, false);
				}
				Plugin.Log.LogWarning((object)("Swap cancelled: " + ex));
				Notify(player, "Could not swap; equipment unchanged");
				return false;
			}
		}

		internal static void Clear()
		{
			Pending.Clear();
		}
	}
	internal static class UsedItems
	{
		private static readonly List<ItemData> RecentEquipment = new List<ItemData>();

		private static readonly Dictionary<ItemType, FieldInfo> WornArmor = new Dictionary<ItemType, FieldInfo>
		{
			{
				(ItemType)6,
				AccessTools.Field(typeof(Humanoid), "m_helmetItem")
			},
			{
				(ItemType)7,
				AccessTools.Field(typeof(Humanoid), "m_chestItem")
			},
			{
				(ItemType)11,
				AccessTools.Field(typeof(Humanoid), "m_legItem")
			},
			{
				(ItemType)17,
				AccessTools.Field(typeof(Humanoid), "m_shoulderItem")
			},
			{
				(ItemType)18,
				AccessTools.Field(typeof(Humanoid), "m_utilityItem")
			},
			{
				(ItemType)24,
				AccessTools.Field(typeof(Humanoid), "m_trinketItem")
			}
		};

		internal static bool IsWeaponOrTool(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0005: 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_0009: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if ((int)type <= 15)
			{
				if (type - 3 <= 1 || type - 14 <= 1)
				{
					goto IL_001e;
				}
			}
			else if ((int)type == 19 || (int)type == 22)
			{
				goto IL_001e;
			}
			return false;
			IL_001e:
			return true;
		}

		internal unsafe static string Family(ItemData item)
		{
			//IL_0013: 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_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Invalid comparison between Unknown and I4
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Invalid comparison between Unknown and I4
			//IL_0049: 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_0092: Invalid comparison between Unknown and I4
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Invalid comparison between Unknown and I4
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Invalid comparison between Unknown and I4
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Invalid comparison between Unknown and I4
			if (item == null || item.m_shared == null)
			{
				return null;
			}
			ItemType itemType = item.m_shared.m_itemType;
			if ((int)itemType == 5)
			{
				return "shield";
			}
			if ((int)itemType == 15)
			{
				return "torch";
			}
			if (!IsWeaponOrTool(itemType))
			{
				return null;
			}
			SkillType skillType = item.m_shared.m_skillType;
			if ((int)itemType != 19)
			{
				if ((int)skillType == 0)
				{
					if (!((Object)(object)item.m_dropPrefab != (Object)null))
					{
						return null;
					}
					return "prefab:" + ((Object)item.m_dropPrefab).name;
				}
				return "weapon:" + ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
			}
			if ((int)skillType == 12 || (int)skillType == 13 || (int)skillType == 104 || (int)skillType == 106 || (int)skillType == 107)
			{
				return "tool:" + ((object)(*(SkillType*)(&skillType))/*cast due to .constrained prefix*/).ToString();
			}
			if (!((Object)(object)item.m_dropPrefab != (Object)null))
			{
				return "toolname:" + item.m_shared.m_name;
			}
			return "toolprefab:" + ((Object)item.m_dropPrefab).name;
		}

		private static bool SameFamily(ItemData a, ItemData b)
		{
			string text = Family(a);
			if (text != null)
			{
				return text == Family(b);
			}
			return false;
		}

		internal static void Record(ItemData item)
		{
			if (item != null && Family(item) != null)
			{
				RecentEquipment.Remove(item);
				RecentEquipment.Insert(0, item);
				if (RecentEquipment.Count > 128)
				{
					RecentEquipment.RemoveRange(128, RecentEquipment.Count - 128);
				}
			}
		}

		private static ItemData MatchingRecent(Player player, ItemData displayed, bool exactKind, Func<ItemData, bool> canSwap)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = ((Humanoid)player).GetInventory();
			foreach (ItemData item in RecentEquipment)
			{
				if (item != null && inventory.ContainsItem(item) && SameFamily(item, displayed) && !player.IsEquipActionQueued(item) && (!exactKind || item.m_shared.m_itemType == displayed.m_shared.m_itemType) && (canSwap == null || canSwap(item)))
				{
					return item;
				}
			}
			return null;
		}

		private static ItemData FindSimilar(Player player, ItemData displayed, Func<ItemData, bool> canSwap)
		{
			//IL_006c: 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)
			Inventory inventory = ((Humanoid)player).GetInventory();
			ItemData result = null;
			int num = int.MinValue;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != null && allItem != displayed && allItem.m_stack == 1 && SameFamily(allItem, displayed) && !player.IsEquipActionQueued(allItem) && (canSwap == null || canSwap(allItem)))
				{
					int num2 = 0;
					if (allItem.m_shared.m_itemType == displayed.m_shared.m_itemType)
					{
						num2 += 100;
					}
					if ((Object)(object)allItem.m_dropPrefab != (Object)null && (Object)(object)displayed.m_dropPrefab != (Object)null && ((Object)allItem.m_dropPrefab).name == ((Object)displayed.m_dropPrefab).name)
					{
						num2 += 50;
					}
					if (((Humanoid)player).IsItemEquiped(allItem))
					{
						num2 += 200;
					}
					if (num2 > num)
					{
						result = allItem;
						num = num2;
					}
				}
			}
			return result;
		}

		internal static ItemData Find(Player player, ItemData displayed, Func<ItemData, bool> canSwap = null)
		{
			//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_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || displayed == null)
			{
				return null;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return null;
			}
			ItemType itemType = displayed.m_shared.m_itemType;
			if ((int)itemType == 5 || IsWeaponOrTool(itemType))
			{
				ItemData val = MatchingRecent(player, displayed, exactKind: true, canSwap);
				if (val != null)
				{
					return val;
				}
				val = MatchingRecent(player, displayed, exactKind: false, canSwap);
				return val ?? FindSimilar(player, displayed, canSwap);
			}
			if (WornArmor.TryGetValue(itemType, out var value) && value != null)
			{
				object? value2 = value.GetValue(player);
				ItemData val2 = (ItemData)((value2 is ItemData) ? value2 : null);
				if (val2 != null && val2.m_shared.m_itemType == itemType && inventory.ContainsItem(val2) && ((Humanoid)player).IsItemEquiped(val2) && (canSwap == null || canSwap(val2)))
				{
					return val2;
				}
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem != null && allItem.m_shared.m_itemType == itemType && ((Humanoid)player).IsItemEquiped(allItem) && (canSwap == null || canSwap(allItem)))
				{
					return allItem;
				}
			}
			ItemData val3 = EquipmentSlotsCompat.FindOccupiedSlot(player, displayed);
			if (val3 == null || (canSwap != null && !canSwap(val3)))
			{
				return null;
			}
			return val3;
		}

		internal static void Clear()
		{
			RecentEquipment.Clear();
		}
	}
}