Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of WiresStandSwap v0.2.0
WiresStandSwap.dll
Decompiled a week agousing 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(); } } }