Please disclose if your mod was created primarily 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 ZenItemStands v0.7.0
plugins/ZenItemStands.dll
Decompiled 5 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using JetBrains.Annotations; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; using Zen; using Zen.Compatibility; using Zen.Lib; using Zen.Lib.Config; using ZenItemStands.Compatibility; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("ZenItemStands")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("ZenItemStands")] [assembly: AssemblyCopyright("Copyright \ufffd 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")] [assembly: AssemblyFileVersion("0.0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.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 ZenItemStands { internal class ArmorStandContainer : StandContainer { internal ArmorStand Stand; protected override void Awake() { if (!Object.op_Implicit((Object)(object)Stand) && !((Component)this).TryGetComponent<ArmorStand>(ref Stand)) { throw new Exception("ArmorStand component not found on " + ((Object)this).name); } ((Container)this).m_name = Stand.m_name; ((Container)this).m_width = Stand.m_slots.Count; ((Container)this).m_height = 1; ((Component)this).GetComponentInChildren<Switch>().m_name = Stand.m_name; base.Awake(); } public override void ShowErrorMessage() { ((Character)Player.m_localPlayer).Message((MessageType)2, "$piece_armorstand_cantattach", 0, (Sprite)null); } public override bool IsSameType(ItemData armorItem, ItemData compareItem) { ArmorStandSlot slot = GetSlot(armorItem); if (slot != null) { return Stand.CanAttach(slot, compareItem); } return false; } private ArmorStandSlot? GetSlot(ItemData item) { ItemData item2 = item; return ((IEnumerable<ArmorStandSlot>)Stand.m_slots).FirstOrDefault((Func<ArmorStandSlot, bool>)((ArmorStandSlot slot) => slot.m_currentItemName == ItemDataExt.GetName(item2))); } private ItemData? GetAttachedItemData(int index) { string attachedItem = Stand.GetAttachedItem(index); Logging<Plugin>.Debug((object)$"Index: {index} Item: {attachedItem}", 0); return GetItemData(attachedItem, index); } protected override bool IsStandInventoryDiff() { int num = 0; for (int i = 0; i < Stand.m_slots.Count; i++) { ItemData attachedItemData = GetAttachedItemData(i); if (attachedItemData != null) { if (!InventoryExt.HaveItemData(((Container)this).m_inventory, attachedItemData)) { Logging<Plugin>.Info((object)("Diff Item: " + ItemDataExt.GetName(attachedItemData)), 0); return true; } num++; } } bool num2 = num != ((Container)this).m_inventory.NrOfItems(); if (!num2) { Logging<Plugin>.Debug((object)$"Container and stand are identical: {num} items", 0); } return num2; } public override bool CanAttach(ItemData item, bool ignoreExisting) { int index; return CanAttach(item, out index, ignoreExisting); } private bool CanAttach(ItemData item, out int index, bool ignoreExisting) { ItemData item2 = item; index = -1; for (int i = 0; i < Stand.m_slots.Count; i++) { if (CanAttachAt(i)) { index = i; return true; } } return false; bool CanAttachAt(int index) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Invalid comparison between Unknown and I4 //IL_0083: Unknown result type (might be due to invalid IL or missing references) ArmorStandSlot val = Stand.m_slots[index]; if (!Stand.CanAttach(val, item2)) { return false; } if (!ignoreExisting && Stand.HaveAttachment(index)) { return false; } ItemType itemType = item2.m_shared.m_itemType; if (((int)itemType == 7 || (int)itemType == 11) ? true : false) { return true; } foreach (Transform item3 in item2.m_dropPrefab.transform) { if (((Object)item3).name.StartsWith("attach")) { return true; } } return false; } } public override bool CanSwap(ItemData externItem, out ItemData swapItem) { if (CanAttach(externItem, ignoreExisting: true)) { foreach (ItemData allItem in ((Container)this).m_inventory.GetAllItems()) { if (IsSameType(allItem, externItem)) { swapItem = allItem; return true; } } } swapItem = null; return false; } public override void SwapEquip() { Inventory inventory = ((Container)this).m_inventory; Player localPlayer = Player.m_localPlayer; List<ItemData> equippedItems = ((Humanoid)localPlayer).GetInventory().GetEquippedItems(); if (((Humanoid)localPlayer).m_hiddenLeftItem != null) { equippedItems.Add(((Humanoid)localPlayer).m_hiddenLeftItem); } if (((Humanoid)localPlayer).m_hiddenRightItem != null) { equippedItems.Add(((Humanoid)localPlayer).m_hiddenRightItem); } ItemData[] array = inventory.GetAllItems().ToArray(); foreach (ItemData standItem in array) { ((Humanoid)(object)localPlayer).EquipContainerStandItem(standItem); } ((Humanoid)localPlayer).HideHandItems(false, true); Inventory inventory2 = ((Humanoid)localPlayer).GetInventory(); foreach (ItemData item in equippedItems) { if (inventory2.ContainsItem(item) && CanAttach(item, ignoreExisting: false)) { ((Humanoid)localPlayer).UnequipItem(item, false); inventory.MoveItemToThis(inventory2, item); } } } private bool AttachItem(ItemData item) { if (!CanAttach(item, out var index, ignoreExisting: false)) { return false; } if (((Container)this).m_nview.IsOwner()) { ZDO zDO = ((Container)this).m_nview.GetZDO(); zDO.Set(index + "_item", ((Object)item.m_dropPrefab).name); ItemDrop.SaveToZDO(index, item, zDO); } return true; } protected override void UpdateVisualFromContainer() { //IL_0055: 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) if (IsStandInventoryDiff()) { Logging<Plugin>.Info((object)"Updating visuals and slot data", 0); ClearArmorStand(); int num = ((Container)this).m_inventory.GetAllItems().Count(AttachItem); Stand.UpdateVisual(); Transform transform = ((Component)this).transform; Stand.m_effects.Create(transform.position, transform.rotation, (Transform)null, 1f, -1); Logging<Plugin>.Info((object)string.Format("{0} loaded {1} items from container", "ArmorStand", num), 0); } void ClearArmorStand() { for (int i = 0; i < Stand.m_slots.Count; i++) { if (((Container)this).m_nview.IsOwner()) { ((Container)this).m_nview.GetZDO().Set(i + "_item", string.Empty); } Stand.SetVisualItem(i, string.Empty, 0); } Stand.UpdateSupports(); } } protected override bool FillContainer() { Logging<Plugin>.Info((object)"Fill inventory from slots", 0); ((Container)this).m_inventory.RemoveAll(); for (int i = 0; i < Stand.m_slots.Count; i++) { if (Stand.HaveAttachment(i)) { ItemData attachedItemData = GetAttachedItemData(i); if (attachedItemData != null) { ((Container)this).m_inventory.AddItem(attachedItemData); Logging<Plugin>.Info((object)("Added item to container from ArmorStand slot: " + ItemDataExt.GetName(attachedItemData)), 0); } } } return true; } public override void ShowHideStackAllButton() { InventoryGuiExt.ShowStackAllButton(InventoryGui.instance, Configs.EnableArmorStandEquipButton.Value && !Conflict.IsConflict); } } internal static class ArmorStandExt { public static void UpdateContainer(this ArmorStand armorStand) { StandContainer.UpdateContainer(((Component)armorStand).gameObject); } public static void ResetPose(this ArmorStand armorStand) { if (armorStand.m_pose != 0) { armorStand.m_nview.InvokeRPC(ZNetView.Everybody, "RPC_SetPose", new object[1] { 0 }); } } public static void NextPose(this ArmorStand armorStand) { armorStand.m_nview.InvokeRPC(ZNetView.Everybody, "RPC_SetPose", new object[1] { (armorStand.m_pose + 1) % armorStand.m_poseCount }); } public static bool HasChestAndLegItem(this ArmorStand armorStand) { bool flag = false; bool flag2 = false; for (int i = 0; i < armorStand.m_slots.Count; i++) { string attachedItem = armorStand.GetAttachedItem(i); if (!Utility.IsNullOrWhiteSpace(attachedItem)) { ItemDrop component = ObjectDB.instance.GetItemPrefab(attachedItem).GetComponent<ItemDrop>(); if (ItemDataExt.IsItemType(component.m_itemData, (ItemType)7)) { flag = true; } if (ItemDataExt.IsItemType(component.m_itemData, (ItemType)11)) { flag2 = true; } if (flag && flag2) { return true; } } } return false; } } [HarmonyPatch] internal static class ArmorStandPatch { [HarmonyPatch(typeof(ArmorStand), "SetPose")] private static class ArmorStandSetPose { [UsedImplicitly] private static void Prefix(ref bool effect) { effect = false; } [UsedImplicitly] private static void Postfix(ArmorStand __instance) { __instance.UpdateSupports(); Logging<Plugin>.Info((object)string.Format("{0} index: {1}", "SetPose", __instance.m_pose), 0); } } [HarmonyPostfix] [HarmonyPatch(typeof(ArmorStand), "Awake")] private static void ArmorStand_Awake(ArmorStand __instance) { if (__instance.HasContainer(out ArmorStandContainer _) && !Configs.EnableArmorStandPoseChange.Value) { __instance.ResetPose(); } } [HarmonyPostfix] [HarmonyPatch(typeof(Switch), "GetHoverText")] private static void ArmorStand_Switch_GetHoverText(Switch __instance, ref string __result) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) ArmorStand componentInParent = ((Component)__instance).GetComponentInParent<ArmorStand>(); if (Object.op_Implicit((Object)(object)componentInParent) && componentInParent.HasContainer(out ArmorStandContainer c)) { __result = StandUI.GetHoverText(c); if (WardAccessExt.CanAccessWard(((Component)__instance).transform.position, false) && Configs.EnableArmorStandPoseChange.Value && componentInParent.HasChestAndLegItem()) { __result += Localization.instance.Localize("\n" + UI.PromptInteractAlt + " Change pose"); } } } [HarmonyPostfix] [HarmonyPatch(typeof(ArmorStand), "UpdateSupports")] private static void ArmorStand_UpdateSupports(ArmorStand __instance) { if (!Configs.EnableArmorStandPoseChange.Value) { return; } if (__instance.m_supports.Count == 0) { Logging<Plugin>.Warning((object)"m_supports is length 0 when it should be at least 1, aborting.", 0); return; } ArmorStandSupport obj = __instance.m_supports[0]; int pose = __instance.m_pose; foreach (GameObject support in obj.m_supports) { support.SetActive(pose == 0); } ((Component)((Component)__instance).transform.Find("New/Lod1/ArmorStand")).gameObject.SetActive(pose < 4); } [HarmonyPrefix] [HarmonyPatch(typeof(Switch), "Interact")] private static void ArmorStand_Switch_Interact(Switch __instance, Humanoid character, bool hold, bool alt, ref bool __result, ref bool __runOriginal) { ArmorStand componentInParent = ((Component)__instance).GetComponentInParent<ArmorStand>(); if (Object.op_Implicit((Object)(object)componentInParent) && componentInParent.HasContainer(out ArmorStandContainer c)) { __runOriginal = false; if (alt && !hold && Configs.EnableArmorStandPoseChange.Value) { componentInParent.NextPose(); __result = true; } else { __result = StandUI.Open(c, character, hold); } } } [HarmonyPrefix] [HarmonyPatch(typeof(ArmorStand), "UseItem")] private static void ArmorStand_UseItem(ArmorStand __instance, ref bool __result, ref bool __runOriginal) { if (__instance.HasContainer(out ArmorStandContainer c) && !StandUI.CanUseHotbarItem(((Container)c).IsInUse())) { __runOriginal = false; __result = false; } } [HarmonyPostfix] [HarmonyPatch(typeof(ArmorStand), "RPC_DropItemByName")] private static void ArmorStand_RPC_DropItemByName(ArmorStand __instance) { if (__instance.IsOpenContainer()) { InventoryGui.instance.Hide(); } } [HarmonyPostfix] [HarmonyPatch(typeof(ArmorStand), "UpdateVisual")] private static void ArmorStand_UpdateVisual(ArmorStand __instance) { __instance.UpdateContainer(); } } internal static class Configs { private const string SECTION_GENERAL = "General"; public static readonly ConfigEntry<StringList> StandPrefabNames; public static readonly ConfigEntry<bool> EnableHotbarUseItem; private const string SECTION_ARMORSTAND = "Piece: ArmorStand"; public static readonly ConfigEntry<bool> EnableArmorStandPoseChange; public static readonly ConfigEntry<bool> EnableArmorStandHoldToEquip; public static readonly ConfigEntry<bool> EnableArmorStandEquipButton; public static readonly ConfigEntry<bool> EnableArmorStandAmericanSpelling; private const string SECTION_ITEMSTAND = "Piece: ItemStand"; public static readonly ConfigEntry<bool> EnableItemStandEquipButton; public static readonly ConfigEntry<bool> ShowItemOnStandName; static Configs() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0046: Expected O, but got Unknown StringList val = new StringList(); ((List<string>)val).Add("itemstand"); ((List<string>)val).Add("itemstandh"); ((List<string>)val).Add("ArmorStand"); ((List<string>)val).Add("Placeable_HardRock"); StandPrefabNames = Config.Define<StringList>(true, "General", "Stand Prefabs", val, "List of all the prefabs that are item stands or armor stands.\nThese prefabs must have an ItemStand or ArmorStand component attached to them."); EnableHotbarUseItem = Config.Define<bool>(true, "General", "Enable Hotbar Use Item", false, "If enabled you can also use traditional 1-8 hotbar buttons to assign items to stands (Vanilla: true)"); EnableArmorStandPoseChange = Config.Define<bool>(true, "Piece: ArmorStand", "Enable Pose Changing", false, "Enable changing poses of armor stands. NOTE: Stand must have chest and legs armor."); EnableArmorStandHoldToEquip = Config.Define<bool>(true, "Piece: ArmorStand", "Enable Hold To Equip", false, "Should (Hold to equip) be available on Armor Stands hover text?"); EnableArmorStandEquipButton = Config.Define<bool>(true, "Piece: ArmorStand", "Enable Use/Equip Button", true, "Enable the use/equip button on armor stands to quickly swap equipped gear."); EnableArmorStandAmericanSpelling = Config.Define<bool>(false, "Piece: ArmorStand", "American Spelling", true, "Change the spelling to 'Armor Stand' instead of 'Armour Stand'"); EnableItemStandEquipButton = Config.Define<bool>(true, "Piece: ItemStand", "Enable Use/Equip Button", true, "Enable the use/equip button on item stands to quickly swap equipped gear."); ShowItemOnStandName = Config.Define<bool>(true, "Piece: ItemStand", "Show Item Name", true, "Show the name of the item on the stand in the hover text (Vanilla: true)\r\n[ZenHoverItem overrides this option]"); } } internal static class ItemStandExt { public static bool HasOrientations(this ItemStand itemStand) { return itemStand.m_orientationSettings.Count > 1; } public static void UpdateContainer(this ItemStand itemStand) { StandContainer.UpdateContainer(((Component)itemStand).gameObject); } } [HarmonyPatch] internal static class PetRock { private static bool HasContainer(this Pet pet, out ItemStandContainer c) { if (!Object.op_Implicit((Object)(object)pet.m_itemStand)) { return Object.op_Implicit((Object)(object)(c = null)); } return pet.m_itemStand.HasContainer(out c); } [HarmonyPrefix] [HarmonyPatch(typeof(Pet), "GetHoverText")] private static void Pet_GetHoverText(Pet __instance, ref string __result, ref bool __runOriginal) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (__instance.HasContainer(out ItemStandContainer c)) { __runOriginal = false; ((Container)c).m_name = __instance.m_tameable.GetHoverName(); ((Container)c).GetInventory().m_name = ((Container)c).m_name; if (!WardAccessExt.CanAccessWard(((Component)__instance).transform.position, false)) { __result = ((Container)c).GetHoverName(); return; } __result += __instance.m_tameable.GetHoverText(); __result = __result.Replace(StringExt.Localize(">] $hud_pet"), StringExt.Localize(">] $hud_pet ($msg_hold_equip_hover)")); } } [HarmonyPrefix] [HarmonyPatch(typeof(Pet), "Interact")] private static void Pet_Interact(Pet __instance, Humanoid user, bool alt, bool hold, ref bool __result, ref bool __runOriginal) { if (__instance.HasContainer(out ItemStandContainer _)) { __runOriginal = false; if (alt) { __instance.m_tameable.Interact(user, false, true); } else if (hold) { __instance.m_itemStand.Interact(user, false, false); } else { __instance.m_tameable.Interact(user, false, false); } } } } internal static class Startup { public static List<StandContainer> InitPrefabs() { Logging<Plugin>.Info((object)"Initializing Prefabs", 0); return Init(from p in ((IEnumerable<string>)Configs.StandPrefabNames.Value).Select((Func<string, GameObject>)ZNetScene.instance.GetPrefab) where (Object)(object)p != (Object)null select p); } public static List<StandContainer> Init(IEnumerable<GameObject> gameObjects) { List<StandContainer> list = new List<StandContainer>(); foreach (GameObject gameObject in gameObjects) { Logging<Plugin>.Info((object)("Initializing: " + ((Object)gameObject).name), 0); if (!list.Add<ArmorStand>(gameObject) && !list.Add<ItemStand>(gameObject)) { Logging<Plugin>.Error((object)(((Object)gameObject).name + " is not ArmorStand or ItemStand"), (ushort)0); } } return list; } private static bool Add<T>(this List<StandContainer> containers, GameObject obj) where T : Component { T component = obj.GetComponent<T>(); if (!Object.op_Implicit((Object)(object)component)) { return false; } if (!(component is ArmorStand)) { if (!(component is ItemStand)) { throw new Exception($"Unknown component type: {((object)component).GetType()}"); } containers.Add(AddContainer<ItemStandContainer>(obj)); } else { containers.Add(AddContainer<ArmorStandContainer>(obj)); } return true; } private static T AddContainer<T>(GameObject targetObject) where T : StandContainer { T result = default(T); if (targetObject.TryGetComponent<T>(ref result)) { return result; } result = targetObject.AddComponent<T>(); ((Container)result).m_checkGuardStone = true; return result; } } [HarmonyPatch(typeof(Container))] internal static class StandContainerPatch { [HarmonyPrefix] [HarmonyPatch("DropAllItems", new Type[] { })] private static void DropAllItems(Container __instance, ref bool __runOriginal) { if (__instance is StandContainer) { __instance.GetInventory().RemoveAll(); __runOriginal = false; } } [HarmonyPrefix] [HarmonyPatch("Save")] private static void Save(Container __instance, ref bool __runOriginal) { if (__instance is StandContainer standContainer) { Logging<Plugin>.Info((object)$"NON-PERSISTANT: {((object)standContainer).GetType()}", 0); __runOriginal = false; } } } internal static class StandContainerExt { public static bool HasContainer(this ArmorStand s, out ArmorStandContainer c) { return ((Component)s).TryGetComponent<ArmorStandContainer>(ref c); } public static bool HasContainer(this ItemStand s, out ItemStandContainer c) { return ((Component)s).TryGetComponent<ItemStandContainer>(ref c); } public static bool HasContainerStand(this GameObject g, out StandContainer c) { return g.TryGetComponent<StandContainer>(ref c); } } internal static class StandUI { private static StandContainer? _opened; private static float _closeTime; public static bool IsOpen => Object.op_Implicit((Object)(object)_opened); public static bool IsRecentlyClosed => Time.time < _closeTime + 0.5f; public static StandContainer Opened { get { if (!IsOpen) { throw new Exception("Can not access container, not open"); } return _opened; } } private static bool IsOpenContainer(GameObject g) { if (g.HasContainerStand(out StandContainer c)) { return c.IsOpen; } return false; } public static bool IsOpenContainer(this ItemStand s) { return IsOpenContainer(((Component)s).gameObject); } public static bool IsOpenContainer(this ArmorStand s) { return IsOpenContainer(((Component)s).gameObject); } internal static void Translations(string language) { if (Configs.EnableArmorStandAmericanSpelling.Value && language == "English") { Localization.instance.m_translations["piece_armorstand"] = "Armor Stand"; } } public static bool CanUseHotbarItem(bool isInUse) { if (Configs.EnableHotbarUseItem.Value && !isInUse) { return true; } if (isInUse) { ((Character)Player.m_localPlayer).Message((MessageType)2, "$msg_inuse", 0, (Sprite)null); } return false; } public static bool Open(StandContainer container, Humanoid user, bool hold) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (IsOpen || IsRecentlyClosed) { InventoryGui.instance.Hide(); return false; } if (!ZenMod<Plugin>.IsOnServerAndAllClients) { ((Container)container).m_nview.ClaimOwnership(); } if (!((Container)container).Interact(user, hold, false)) { return false; } _opened = container; _opened.ShowHideStackAllButton(); Logging<Plugin>.Info((object)$"Opened Stand: {((object)_opened).GetType()}", 0); Game instance = Game.instance; PlayerStatType val; if (!(container is ArmorStandContainer)) { if (!(container is ItemStandContainer)) { throw new Exception($"Invalid type of stand: {((object)container).GetType()}"); } val = (PlayerStatType)24; } else { val = (PlayerStatType)25; } instance.IncrementPlayerStat(val, 1f); return true; } public static void OnShowUI(InventoryGui gui, StandContainer? container) { container?.TryFill(); InventoryGridExt.ShowAmountText(gui.ContainerGrid, false, -1); } public static void OnHideUI(InventoryGui gui) { InventoryGui gui2 = gui; if (_opened is ArmorStandContainer armorStandContainer && Configs.EnableArmorStandPoseChange.Value && !armorStandContainer.Stand.HasChestAndLegItem()) { armorStandContainer.Stand.ResetPose(); } Timing.WhenNot((MonoBehaviour)(object)gui2, (Func<bool>)InventoryGui.IsVisible, (Action)delegate { Timing.Delay((MonoBehaviour)(object)gui2, 0.5f, (Action)delegate { InventoryGridExt.ShowAmountText(gui2.ContainerGrid, true, -1); }); }); Close(); } private static void Close() { if (IsOpen) { _closeTime = Time.time; _opened = null; } } public static bool CanSwap(ItemData item, out ItemData swapItem) { swapItem = null; if (Object.op_Implicit((Object)(object)_opened)) { return _opened.CanSwap(item, out swapItem); } return false; } public static void SwapEquip(bool isUIButtonPressed) { Logging<Plugin>.Info((object)"Swap equipment", 0); if (Conflict.IsConflict) { Logging<Plugin>.Warning((object)"Conflicting mod detected, abort equipment swap to protect against item duplication.", 0); return; } StandContainer opened = _opened; if (!(opened is ArmorStandContainer)) { if (opened is ItemStandContainer && isUIButtonPressed) { _opened.SwapEquip(); } } else if (isUIButtonPressed || Configs.EnableArmorStandHoldToEquip.Value) { _opened.SwapEquip(); } } public static void EquipContainerStandItem(this Humanoid player, ItemData standItem) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected I4, but got Unknown ItemType itemType = standItem.m_shared.m_itemType; ItemData val = (ItemData)((itemType - 3) switch { 3 => player.m_helmetItem, 4 => player.m_chestItem, 8 => player.m_legItem, 14 => player.m_shoulderItem, 15 => player.m_utilityItem, 21 => player.m_trinketItem, 2 => player.m_leftItem ?? player.m_hiddenLeftItem, 1 => player.m_leftItem ?? player.m_hiddenLeftItem, 19 => player.m_leftItem ?? player.m_hiddenLeftItem, 0 => player.m_rightItem ?? player.m_hiddenRightItem, 11 => player.m_rightItem ?? player.m_hiddenRightItem, 16 => player.m_rightItem ?? player.m_hiddenRightItem, 17 => player.m_rightItem ?? player.m_hiddenRightItem, 12 => player.m_rightItem ?? player.m_hiddenRightItem, _ => throw new ArgumentOutOfRangeException(), }); Inventory inventory = player.GetInventory(); Inventory inventory2 = ((Container)Opened).GetInventory(); if (val == null) { inventory.MoveItemToThis(inventory2, standItem); } else if (Opened.CanAttach(val, ignoreExisting: true) && Opened.IsSameType(standItem, val)) { InventoryExt.SwapItem(inventory, val, inventory2, standItem); } player.EquipItem(standItem, false); } private static bool CanHoldToEquip(StandContainer stand) { if (!(stand is ArmorStandContainer)) { if (stand is ItemStandContainer) { return false; } throw new Exception($"Unknown stand type: {((object)stand).GetType()}"); } if (!Conflict.IsConflict) { return Configs.EnableArmorStandHoldToEquip.Value; } return false; } public static string GetHoverText(StandContainer container) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) string hoverName = ((Container)container).GetHoverName(); hoverName = ((!WardAccessExt.CanAccessWard(((Component)container).transform.position, false)) ? (hoverName + "\n$piece_noaccess") : ((!CanHoldToEquip(container)) ? (hoverName + "\n" + UI.PromptInteract + " $piece_use") : (hoverName + "\n" + UI.PromptInteract + " $piece_use ($msg_hold_equip_hover)"))); return StringExt.Localize(hoverName); } } [HarmonyPatch] internal static class StandUIPatch { [HarmonyPatch(typeof(InventoryGui), "OnStackAll")] private static class InventoryGuiOnStackAll { private static bool _isButtonPressed; [UsedImplicitly] private static void Prefix() { _isButtonPressed = true; } [UsedImplicitly] private static void Postfix() { _isButtonPressed = false; } [HarmonyPrefix] [HarmonyPatch(typeof(Inventory), "StackAll")] [HarmonyPriority(900)] private static void Inventory_StackAll(ref bool __runOriginal) { if (StandUI.IsOpen) { StandUI.SwapEquip(_isButtonPressed); __runOriginal = false; } } } [HarmonyPrefix] [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] private static void InventoryGui_OnSelectedItem(InventoryGui __instance, InventoryGrid grid, ItemData item, Modifier mod, Vector2i pos, ref bool __runOriginal) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Invalid comparison between Unknown and I4 //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (!StandUI.IsOpen) { return; } Player localPlayer = Player.m_localPlayer; if (((Character)localPlayer).IsTeleporting() || ((int)mod != 2 && (int)mod != 0)) { return; } int dragAmount = __instance.m_dragAmount; if ((int)mod == 0) { Inventory inventory = grid.GetInventory(); Inventory dragInventory = __instance.m_dragInventory; ItemData dragItem = __instance.m_dragItem; if (dragItem == null || inventory == dragInventory) { return; } bool flag = false; ItemData itemAt = inventory.GetItemAt(pos.x, pos.y); if (inventory == ((Container)StandUI.Opened).GetInventory()) { __instance.m_dragAmount = 1; flag = ((itemAt == null || !StandUI.Opened.IsSameType(itemAt, dragItem)) ? StandUI.Opened.CanAttach(dragItem, ignoreExisting: false) : StandUI.Opened.CanAttach(dragItem, ignoreExisting: true)); } if (inventory == ((Humanoid)localPlayer).GetInventory()) { if (itemAt != null) { if (ItemDataExt.GetPrefabName(dragItem) == ItemDataExt.GetPrefabName(itemAt) && ItemDataExt.IsStackable(dragItem)) { return; } flag = StandUI.Opened.CanAttach(itemAt, StandUI.Opened.IsSameType(dragItem, itemAt)); } else { flag = true; } } if (flag) { if (itemAt != null) { InventoryExt.SwapItem(dragInventory, dragItem, inventory, itemAt); __instance.SetupDragItem((ItemData)null, (Inventory)null, 0); __runOriginal = false; } return; } } if ((int)mod == 2) { if ((Object)(object)grid == (Object)(object)__instance.ContainerGrid) { return; } if (item.m_stack == 1) { if (StandUI.Opened.CanAttach(item, ignoreExisting: false)) { return; } if (StandUI.CanSwap(item, out ItemData swapItem)) { InventoryExt.SwapItem(grid.GetInventory(), item, ((Container)StandUI.Opened).GetInventory(), swapItem); __runOriginal = false; return; } } } __runOriginal = false; __instance.m_dragAmount = dragAmount; StandUI.Opened.ShowErrorMessage(); } [HarmonyPostfix] [HarmonyPatch(typeof(InventoryGui), "Show")] private static void InventoryGui_Show(InventoryGui __instance, Container? container) { if (container is StandContainer container2) { StandUI.OnShowUI(__instance, container2); } } [HarmonyPostfix] [HarmonyPatch(typeof(InventoryGui), "Hide")] private static void InventoryGui_Hide(InventoryGui __instance) { if (StandUI.IsOpen) { StandUI.OnHideUI(__instance); } } [HarmonyPrefix] [HarmonyPatch(typeof(Localization), "Localize", new Type[] { typeof(string) })] private static void Localization_Localize(ref string text) { if (StandUI.IsOpen || StandUI.IsRecentlyClosed) { text = text.Replace("$inventory_stackall", "$inventory_use"); } } } internal class ItemStandContainer : StandContainer { private ItemStand _stand; protected override void Awake() { if (!Object.op_Implicit((Object)(object)_stand) && !((Component)this).TryGetComponent<ItemStand>(ref _stand)) { throw new Exception("ItemStand component not found on " + ((Object)this).name); } ((Container)this).m_width = 1; ((Container)this).m_height = 1; ((Container)this).m_name = _stand.m_name; base.Awake(); } public override void ShowErrorMessage() { ((Character)Player.m_localPlayer).Message((MessageType)2, "$piece_itemstand_cantattach", 0, (Sprite)null); } public override bool IsSameType(ItemData standItem, ItemData compareItem) { if (compareItem.m_stack > 1) { return false; } if (_stand.CanAttach(standItem)) { return _stand.CanAttach(compareItem); } return false; } public override bool CanAttach(ItemData itemData, bool ignoreExisting) { if (!ignoreExisting) { if (_stand.CanAttach(itemData)) { return ((Container)this).m_inventory.NrOfItems() == 0; } return false; } return _stand.CanAttach(itemData); } public override bool CanSwap(ItemData externItem, out ItemData swapItem) { ItemData val = (swapItem = GetContainerItem()); if (val != null && IsSameType(val, externItem)) { return true; } return false; } public override void SwapEquip() { ItemData containerItem = GetContainerItem(); if (containerItem != null) { if (!containerItem.IsEquipable()) { Logging<Plugin>.Info((object)"Item stand item is not equipable, aborting swap.", 0); } else { ((Humanoid)(object)Player.m_localPlayer).EquipContainerStandItem(containerItem); } } } private ItemData? GetContainerItem() { if (((Container)this).m_inventory.NrOfItems() <= 0) { return null; } return ((Container)this).m_inventory.GetItem(0); } private bool AttachItem(ItemData item) { if (!_stand.CanAttach(item)) { return false; } if (((Container)this).m_nview.IsOwner()) { Logging<Plugin>.Info((object)("Attaching item: " + ItemDataExt.GetPrefabName(item)), 0); ZDO zDO = ((Container)this).m_nview.GetZDO(); zDO.Set(ZDOVars.s_item, ItemDataExt.GetPrefabName(item)); ItemDrop.SaveToZDO(item, zDO); } return true; } private ItemData? GetAttachedItemData() { string attachedItem = _stand.GetAttachedItem(); return GetItemData(attachedItem); } protected override bool IsStandInventoryDiff() { if (((Container)this).m_inventory.NrOfItems() == 0) { return _stand.HaveAttachment(); } if (!_stand.HaveAttachment()) { return true; } ItemData attachedItemData = GetAttachedItemData(); int num; if (attachedItemData == null) { num = 0; } else { num = ((!ItemDataExt.IsEqual(attachedItemData, ((Container)this).m_inventory.GetItem(0))) ? 1 : 0); if (num != 0) { goto IL_0072; } } Logging<Plugin>.Debug((object)("Container and stand are identical: " + (((attachedItemData != null) ? ItemDataExt.GetName(attachedItemData) : null) ?? "No Item")), 0); goto IL_0072; IL_0072: return (byte)num != 0; } protected override bool FillContainer() { Logging<Plugin>.Info((object)"Fill inventory from item stand", 0); ((Container)this).m_inventory.RemoveAll(); ItemData attachedItemData = GetAttachedItemData(); if (attachedItemData == null) { return false; } ((Container)this).m_inventory.AddItem(attachedItemData); Logging<Plugin>.Info((object)("Added item to container from ItemStand: " + ItemDataExt.GetName(attachedItemData)), 0); return true; } protected override void UpdateVisualFromContainer() { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (IsStandInventoryDiff()) { Logging<Plugin>.Info((object)"Updating visuals data", 0); ClearItemStand(); ItemData containerItem = GetContainerItem(); if (containerItem == null || AttachItem(containerItem)) { UpdateVisualEveryone(); Transform transform = ((Component)this).transform; _stand.m_effects.Create(transform.position, transform.rotation, (Transform)null, 1f, -1); Logging<Plugin>.Info((object)("ItemStand updated visual from container: " + ((containerItem != null) ? ItemDataExt.GetPrefabName(containerItem) : null)), 0); } } void ClearItemStand() { if (((Container)this).m_nview.IsOwner()) { ((Container)this).m_nview.GetZDO().Set(ZDOVars.s_item, string.Empty); } _stand.SetVisualItem(string.Empty, 0, 0, 0); } } private void UpdateVisualEveryone() { ZDO zDO = ((Container)this).m_nview.GetZDO(); string @string = zDO.GetString(ZDOVars.s_item, ""); int @int = zDO.GetInt(ZDOVars.s_variant, 0); int int2 = zDO.GetInt(ZDOVars.s_quality, 0); ((Container)this).m_nview.InvokeRPC(ZNetView.Everybody, "SetVisualItem", new object[4] { string.Empty, 0, 0, 0 }); ((Container)this).m_nview.InvokeRPC(ZNetView.Everybody, "SetVisualItem", new object[4] { @string, @int, int2, _stand.GetOrientation() }); } public override void ShowHideStackAllButton() { if (Configs.EnableItemStandEquipButton.Value && !Conflict.IsConflict) { base.ShowHideStackAllButton(); } else { InventoryGuiExt.ShowStackAllButton(InventoryGui.instance, false); } } } [HarmonyPatch] internal static class ItemStandPatch { private static bool IsZenHoverItemInstalled => Chainloader.PluginInfos.ContainsKey("ZenDragon.ZenHoverItem"); [HarmonyPostfix] [HarmonyPatch(typeof(ItemStand), "GetHoverText")] private static void ItemStand_GetHoverText(ItemStand __instance, ref string __result) { if (!__instance.HasContainer(out ItemStandContainer c)) { return; } __result = StandUI.GetHoverText(c); if (__instance.HasOrientations() && __instance.HaveAttachment()) { __result += StringExt.Localize("\n" + UI.PromptInteractAlt + " $hud_changeorientation"); } if (!IsZenHoverItemInstalled && Configs.ShowItemOnStandName.Value) { ItemData? obj = ((Container)c).GetInventory().m_inventory.FirstOrDefault(); string text = ((obj != null) ? StringExt.Localize(ItemDataExt.GetName(obj)) : null); if (!Utility.IsNullOrWhiteSpace(text)) { __result += $"\n<color={UIColor.MinorInfo}>{text}</color>"; } } } [HarmonyPrefix] [HarmonyPatch(typeof(ItemStand), "UseItem")] private static void ItemStand_UseItem(ItemStand __instance, ref bool __runOriginal, ref bool __result) { if (__instance.HasContainer(out ItemStandContainer c) && !StandUI.CanUseHotbarItem(((Container)c).IsInUse())) { __runOriginal = false; __result = false; } } [HarmonyPrefix] [HarmonyPatch(typeof(ItemStand), "Interact")] private static void ItemStand_Interact(ItemStand __instance, Humanoid user, bool alt, bool hold, ref bool __result, ref bool __runOriginal) { if (__instance.HasContainer(out ItemStandContainer c) && !alt) { __result = StandUI.Open(c, user, hold); __runOriginal = false; } } [HarmonyPrefix] [HarmonyPatch(typeof(ItemStand), "SetVisualItem")] private static void ItemStand_SetVisualItem(ItemStand __instance, string itemName, int variant) { if (__instance.m_visualName != itemName || __instance.m_visualVariant != variant) { Object.Destroy((Object)(object)__instance.m_visualItem); } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemStand), "RPC_DropItem")] private static void ItemStand_RPC_DropItem(ItemStand __instance) { if (__instance.IsOpenContainer()) { InventoryGui.instance.Hide(); } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemStand), "UpdateVisual")] private static void ItemStand_UpdateVisual(ItemStand __instance) { __instance.UpdateContainer(); } } [Disable(new Type[] { typeof(StandContainer) })] [BepInPlugin("ZenDragon.ZenItemStands", "ZenItemStands", "0.7.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [SynchronizationMode(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] internal class Plugin : ZenMod<Plugin> { public const string PluginName = "ZenItemStands"; public const string PluginVersion = "0.7.0"; public const string PluginGUID = "ZenDragon.ZenItemStands"; protected override void Setup() { base.LanguageChanged += StandUI.Translations; base.HotLoad.PrefabInit += Startup.InitPrefabs; Conflict.Verify(); } protected override void TitleScene(bool isFirstBoot) { } protected override void WorldStart() { } protected override void Shutdown() { } protected override void HotRestore() { Startup.Init(from o in Object.FindObjectsByType<GameObject>((FindObjectsSortMode)0) where ((IEnumerable<string>)Configs.StandPrefabNames.Value).Any((string prefabName) => ((Object)o).name == prefabName + "(Clone)") select o); } } internal abstract class StandContainer : Container { public bool IsOpen { get { if (StandUI.IsOpen) { return (Object)(object)StandUI.Opened == (Object)(object)this; } return false; } } public abstract void ShowErrorMessage(); public abstract bool IsSameType(ItemData standItem, ItemData compareItem); public abstract bool CanAttach(ItemData itemData, bool ignoreExisting); public abstract bool CanSwap(ItemData externItem, out ItemData swapItem); public abstract void SwapEquip(); protected abstract void UpdateVisualFromContainer(); protected abstract bool FillContainer(); protected abstract bool IsStandInventoryDiff(); internal static void UpdateContainer(GameObject g) { if (g.HasContainerStand(out StandContainer c) && !c.IsOpen) { Logging<Plugin>.Debug((object)$"Updating container: {((object)c).GetType()}", 0); c.TryFill(); } } protected virtual void Awake() { ((Container)this).Awake(); if (ZNetView.m_forceDisableInit) { Object.Destroy((Object)(object)this); return; } Inventory inventory = ((Container)this).GetInventory(); inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, new Action(OnInventoryChanged)); ((ZenMod<Plugin>)(object)ZenMod<Plugin>.Instance).HotLoad.Track((Object)(object)this); } private void OnInventoryChanged() { if (IsOpen) { UpdateVisualFromContainer(); ShowHideStackAllButton(); } } private void OnDestroy() { base.m_nview.Unregister("RequestOpen"); base.m_nview.Unregister("OpenRespons"); base.m_nview.Unregister("RPC_RequestStack"); base.m_nview.Unregister("RPC_StackResponse"); base.m_nview.Unregister("RequestTakeAll"); base.m_nview.Unregister("TakeAllRespons"); ((ZenMod<Plugin>)(object)ZenMod<Plugin>.Instance).HotLoad.Untrack((Object)(object)this); } public bool TryFill() { bool loading = base.m_loading; base.m_loading = true; bool result = IsStandInventoryDiff() && FillContainer(); base.m_loading = loading; return result; } private bool CanFill() { if (((Container)this).IsOwner() || base.m_loading) { return true; } Logging<Plugin>.Error((object)"Not Container owner && m_loading is false", (ushort)0); return false; } protected ItemData? GetItemData(string itemName, int index = -1) { //IL_001a: Expected O, but got Unknown try { return ZdoExt.GetItemData(base.m_nview.GetZDO(), itemName, index); } catch (MissingItemException val) { Logging<Plugin>.Warning((object)((Exception)val).Message, 0); return null; } } public bool IsAnyItemEquipable() { return ((Container)this).GetInventory().GetAllItems().Any((ItemData item) => item.IsEquipable()); } public virtual void ShowHideStackAllButton() { InventoryGuiExt.ShowStackAllButton(InventoryGui.instance, IsAnyItemEquipable() && !Conflict.IsConflict); } } } namespace ZenItemStands.Compatibility { internal static class Conflict { private static readonly string[] ConflictedMods = new string[3] { "aedenthorn.ExtendedPlayerInventory", "randyknapp.mods.equipmentandquickslots", "com.bruce.valheim.comfyquickslots" }; internal static bool IsConflict { get; private set; } internal static void Verify() { if (ConflictedMods.Any((string modID) => Chainloader.PluginInfos.ContainsKey(modID))) { IsConflict = true; Logging<Plugin>.Message((object)("ZenItemStands: The Use/Equip quick swap functionality will be disabled.\r\nThis is to protect against item duplication due to one of the following mods being installed:\r\n" + GeneralExtensions.Join<string>((IEnumerable<string>)ConflictedMods, (Func<string, string>)null, "\n")), 0); } } } [HarmonyPatch] internal static class ExternAddItem { private static readonly Dictionary<Inventory, StandContainer> Inventories = new Dictionary<Inventory, StandContainer>(); [HarmonyPrefix] [HarmonyPatch(typeof(Game), "Start")] private static void Game_Start() { Inventories.Clear(); } [HarmonyPostfix] [HarmonyPatch(typeof(Container), "Awake")] private static void Container_Awake(Container __instance) { if (__instance is StandContainer standContainer && __instance.m_inventory != null) { Inventories[((Container)standContainer).m_inventory] = standContainer; } } [HarmonyPostfix] [HarmonyPatch(typeof(Container), "OnDestroyed")] private static void Container_OnDestroyed(Container __instance) { if (__instance is StandContainer standContainer && __instance.m_inventory != null) { Inventories.Remove(((Container)standContainer).m_inventory); } } [HarmonyPrefix] [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] private static void Inventory_AddItem(Inventory __instance, ItemData item, ref bool __result, ref bool __runOriginal) { if (Inventories.TryGetValue(__instance, out StandContainer value)) { __result = item.m_stack == 1 && value.CanAttach(item, value is ArmorStandContainer); __runOriginal = __result; } } } }