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 RunicDisplayStands v1.3.8
RunicDisplayStands.dll
Decompiled 18 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RunicDisplayStands")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.3.8.0")] [assembly: AssemblyInformationalVersion("1.3.8")] [assembly: AssemblyProduct("RunicDisplayStands")] [assembly: AssemblyTitle("RunicDisplayStands")] [assembly: AssemblyVersion("1.3.8.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 RunicDisplayStands { public class ContainerBridge : MonoBehaviour { internal sealed class Transfer { private static readonly FieldInfo[] EquipmentFields = new string[11] { "m_leftItem", "m_rightItem", "m_chestItem", "m_legItem", "m_helmetItem", "m_shoulderItem", "m_utilityItem", "m_trinketItem", "m_ammoItem", "m_hiddenLeftItem", "m_hiddenRightItem" }.Select((string name) => AccessTools.Field(typeof(Humanoid), name)).ToArray(); private static readonly MethodInfo SetupEquipmentMethod = AccessTools.Method(typeof(Humanoid), "SetupEquipment", (Type[])null, (Type[])null); private readonly ContainerBridge _bridge; private readonly InventorySnapshot _playerSnapshot; private readonly InventorySnapshot _standSnapshot; private readonly ItemData[] _equipped; private readonly string _contents; private readonly Action _playerChanged; private readonly Action _standChanged; private bool _finished; internal Transfer(ContainerBridge bridge) { _bridge = bridge; Inventory inventory = ((Humanoid)bridge._user).GetInventory(); _playerSnapshot = new InventorySnapshot(inventory); _standSnapshot = new InventorySnapshot(bridge._inventory); _equipped = ((IEnumerable<FieldInfo>)EquipmentFields).Select((Func<FieldInfo, ItemData>)((FieldInfo field) => (ItemData)field.GetValue(bridge._user))).ToArray(); _contents = InventorySnapshot.Contents(inventory.GetAllItems().Concat(bridge._inventory.GetAllItems())); _playerChanged = inventory.m_onChanged; _standChanged = bridge._inventory.m_onChanged; inventory.m_onChanged = null; bridge._inventory.m_onChanged = null; } internal bool Finish(bool actionSucceeded) { if (_finished) { return false; } _finished = true; bool flag = false; Inventory playerInventory = ((Humanoid)_bridge._user).GetInventory(); try { flag = TransferBoundary.Complete(actionSucceeded, () => _bridge.HasAuthority && _bridge._baseline == _bridge.StandContents(_bridge.ReadStand()) && _contents == InventorySnapshot.Contents(playerInventory.GetAllItems().Concat(_bridge._inventory.GetAllItems())), () => _bridge.CommitStand(), delegate { _playerSnapshot.Restore(); _standSnapshot.Restore(); for (int i = 0; i < EquipmentFields.Length; i++) { EquipmentFields[i].SetValue(_bridge._user, _equipped[i]); } try { SetupEquipmentMethod.Invoke(_bridge._user, null); } catch (Exception ex4) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("Equipment restored; visual refresh failed: " + ex4.Message)); } } }); if (!flag) { _bridge.Warn("Transfer canceled; items stayed in their original inventories. Reopen the stand if its ownership changed."); } } catch (Exception ex) { _bridge.Warn("Transfer canceled: " + ex.Message); } finally { playerInventory.m_onChanged = _playerChanged; _bridge._inventory.m_onChanged = _standChanged; _bridge._transfer = null; try { InventoryChangedMethod.Invoke(playerInventory, new object[2] { flag, false }); InventoryChangedMethod.Invoke(_bridge._inventory, new object[2] { flag, false }); } catch (Exception ex2) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Inventory refresh failed: " + ex2.Message)); } } } if (flag) { _bridge._baseline = _bridge.StandContents(_bridge._inventory.GetAllItems()); try { _bridge.PublishVisuals(); } catch (Exception ex3) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Stand transfer saved; visual refresh failed: " + ex3.Message)); } } } return flag; } } private static ContainerBridge _opened; private static StandVirtualContainer _virtualContainer; private static bool _stackAllButtonOverridden; private static bool _stackAllButtonWasActive; private static string _stackAllButtonText; private static readonly FieldInfo ContainerInventoryField = AccessTools.Field(typeof(Container), "m_inventory"); private static readonly MethodInfo InventoryChangedMethod = AccessTools.DeclaredMethod(typeof(Inventory), "Changed", new Type[2] { typeof(bool), typeof(bool) }, (Type[])null); private Inventory _inventory; private ItemStand _itemStand; private ArmorStand _armorStand; private Player _user; private string _baseline; private bool _refreshing; private Transfer _transfer; internal static ContainerBridge Opened => _opened; internal bool InTransfer => _transfer != null; internal Inventory StandInventory => _inventory; internal bool IsArmorStand => (Object)(object)_armorStand != (Object)null; internal bool HasAuthority { get { //IL_005b: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_user != (Object)null && (Object)(object)_user == (Object)(object)Player.m_localPlayer && !((Character)_user).IsTeleporting() && (Object)(object)((Component)this).GetComponent<ZNetView>() != (Object)null && ((Component)this).GetComponent<ZNetView>().IsValid() && ((Component)this).GetComponent<ZNetView>().IsOwner()) { return PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, false); } return false; } } internal static bool IsStandInventory(Inventory inventory) { if ((Object)(object)_opened != (Object)null) { return _opened._inventory == inventory; } return false; } public static ContainerBridge GetOrCreate(GameObject go) { ContainerBridge component = go.GetComponent<ContainerBridge>(); if ((Object)(object)component != (Object)null) { return component; } component = go.AddComponent<ContainerBridge>(); component.Initialize(); return component; } private void Initialize() { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown _itemStand = ((Component)this).GetComponent<ItemStand>(); _armorStand = ((Component)this).GetComponent<ArmorStand>(); string text = (((Object)(object)_itemStand != (Object)null) ? _itemStand.GetHoverName() : "$piece_armorstand"); _inventory = new Inventory(text, (Sprite)null, (!((Object)(object)_armorStand != (Object)null)) ? 1 : 5, (!((Object)(object)_armorStand != (Object)null)) ? 1 : 2); Inventory inventory = _inventory; inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, new Action(OnInventoryChanged)); } private List<ItemData> ReadStand() { if ((Object)(object)_armorStand != (Object)null) { return ArmorStandAdapter.GetPlacedItems(_armorStand); } return new List<ItemData> { ItemStandAdapter.GetPlacedItem(_itemStand) }; } private string StandContents(IEnumerable<ItemData> items) { if (!((Object)(object)_armorStand == (Object)null)) { return string.Join("\n", from i in items.Where((ItemData i) => i != null).OrderBy(ArmorStandSlots.Slot) select ArmorStandSlots.Slot(i) + ":" + InventorySnapshot.Contents((IEnumerable<ItemData>)(object)new ItemData[1] { i })); } return InventorySnapshot.Contents(items); } public bool OpenAsContainer(Humanoid user) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) Player val = (Player)(object)((user is Player) ? user : null); if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || (Object)(object)InventoryGui.instance == (Object)null) { return false; } if ((Object)(object)_opened != (Object)null) { return false; } _user = val; if (!TryClaimOwnership()) { Warn("The stand is synchronizing. Please try again."); return false; } try { RefreshInventory(); } catch (Exception ex) { Warn(ex.Message); return false; } _opened = this; if ((Object)(object)_virtualContainer == (Object)null) { GameObject val2 = new GameObject("RunicDisplayStandsVirtualContainer"); val2.SetActive(false); _virtualContainer = val2.AddComponent<StandVirtualContainer>(); } ((Container)_virtualContainer).m_name = _inventory.GetName(); ((Container)_virtualContainer).m_width = _inventory.GetWidth(); ((Container)_virtualContainer).m_height = _inventory.GetHeight(); ContainerInventoryField.SetValue(_virtualContainer, _inventory); ((Component)_virtualContainer).transform.SetParent(((Component)this).transform, false); InventoryGui.instance.Show((Container)(object)_virtualContainer, 1); SetUseEquipButton(useEquip: true); return true; } public bool TakeOne(Humanoid user) { Player val = (Player)(object)((user is Player) ? user : null); if (val == null || (Object)(object)val != (Object)(object)Player.m_localPlayer || (Object)(object)_opened != (Object)null) { return false; } _user = val; if (!TryClaimOwnership()) { return false; } try { RefreshInventory(); return TakeItem(_inventory.GetItemAt(0, 0), use: false); } catch (Exception ex) { Warn(ex.Message); return false; } } internal bool TakeItem(ItemData item, bool use) { if (item == null || !_inventory.ContainsItem(item)) { return false; } ItemData received = item.Clone(); received.m_stack = 1; received.m_equipped = false; bool flag = Execute(delegate { if (!((Humanoid)_user).GetInventory().AddItem(received)) { throw new InvalidOperationException("There is no available inventory slot for that item."); } _inventory.RemoveOneItem(item); }); if (flag && use) { try { ((Humanoid)_user).EquipItem(received, false); } catch (Exception ex) { Warn("Item transferred, but could not equip it: " + ex.Message); } } return flag; } internal void SwapEquip() { if ((Object)(object)_user == (Object)null) { return; } if ((Object)(object)_itemStand != (Object)null) { TakeItem(_inventory.GetItemAt(0, 0), use: true); return; } ItemData[] outgoing = ArmorStandAdapter.PlayerLoadout(_user, _inventory); Execute(delegate { LoadoutSwap.Apply(((Humanoid)_user).GetInventory(), _inventory, outgoing, delegate(ItemData item) { ((Humanoid)_user).UnequipItem(item, false); }, (ItemData item) => ((Humanoid)_user).IsItemEquiped(item) || ((Humanoid)_user).EquipItem(item, false), RunicInventorySlots.Capture(_user)); }); } private bool Execute(Action action) { Transfer transfer = null; try { transfer = BeginTransfer(); action(); return transfer.Finish(actionSucceeded: true); } catch (Exception ex) { transfer?.Finish(actionSucceeded: false); Warn(ex.Message); return false; } } internal Transfer BeginTransfer() { if (_transfer != null) { throw new InvalidOperationException("Another stand action is in progress."); } if (!HasAuthority || _baseline != StandContents(ReadStand())) { throw new InvalidOperationException("The stand changed or access was lost. Reopen it and try again."); } return _transfer = new Transfer(this); } private bool CommitStand() { List<ItemData> allItems = _inventory.GetAllItems(); StandWriteBatch standWriteBatch; if ((Object)(object)_itemStand != (Object)null) { if (allItems.Count > 1) { throw new InvalidOperationException("An item stand holds one item."); } standWriteBatch = ItemStandAdapter.PrepareWrite(_itemStand, (allItems.Count == 0) ? null : allItems[0]); } else { standWriteBatch = ArmorStandAdapter.PrepareWrite(_armorStand, allItems); } if (!HasAuthority || _baseline != StandContents(ReadStand())) { return false; } return standWriteBatch.Commit(); } private void PublishVisuals() { List<ItemData> allItems = _inventory.GetAllItems(); if ((Object)(object)_itemStand != (Object)null) { ItemStandAdapter.PublishVisual(_itemStand, (allItems.Count == 0) ? null : allItems[0]); } else { ArmorStandAdapter.PublishVisuals(_armorStand, allItems); } } internal static void Closed() { if (!((Object)(object)_opened == (Object)null) && _opened._transfer == null) { _opened = null; SetUseEquipButton(useEquip: false); if ((Object)(object)_virtualContainer != (Object)null) { Object.Destroy((Object)(object)((Component)_virtualContainer).gameObject); _virtualContainer = null; } } } private void RefreshInventory() { //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) List<ItemData> list = ReadStand(); _baseline = StandContents(list); _refreshing = true; try { _inventory.GetAllItems().Clear(); for (int i = 0; i < list.Count; i++) { if (list[i] != null) { list[i].m_gridPos = StandGridLayout.Position(i, _inventory.GetWidth()); list[i].m_equipped = false; _inventory.GetAllItems().Add(list[i]); } } } finally { _refreshing = false; } } private void OnInventoryChanged() { if (!_refreshing) { _ = InTransfer; } } private bool TryClaimOwnership() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)this).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid() || !PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false)) { return false; } component.ClaimOwnership(); return HasAuthority; } private void LateUpdate() { if ((Object)(object)_opened == (Object)(object)this && !InTransfer && !HasAuthority) { Warn("Stand access changed. Reopen it to continue."); InventoryGui instance = InventoryGui.instance; if (instance != null) { instance.Hide(); } Closed(); } } internal void Warn(string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)message); } Player user = _user; if (user != null) { ((Character)user).Message((MessageType)2, "Runic Display Stands: " + message, 0, (Sprite)null, false); } } internal static void SetUseEquipButton(bool useEquip) { if ((Object)(object)InventoryGui.instance == (Object)null || (Object)(object)InventoryGui.instance.m_stackAllButton == (Object)null) { return; } Button stackAllButton = InventoryGui.instance.m_stackAllButton; TMP_Text componentInChildren = ((Component)stackAllButton).GetComponentInChildren<TMP_Text>(true); if (useEquip) { if (!_stackAllButtonOverridden) { _stackAllButtonWasActive = ((Component)stackAllButton).gameObject.activeSelf; _stackAllButtonText = ((componentInChildren != null) ? componentInChildren.text : null); _stackAllButtonOverridden = true; } ((Component)stackAllButton).gameObject.SetActive(true); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = "Use/equip"; } } else if (_stackAllButtonOverridden) { ((Component)stackAllButton).gameObject.SetActive(_stackAllButtonWasActive); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.text = _stackAllButtonText; } _stackAllButtonOverridden = false; _stackAllButtonText = null; } } } [HarmonyPatch(typeof(InventoryGui), "UpdateContainer")] [HarmonyPriority(0)] internal static class InventoryGui_UpdateContainer_Stand_Patch { private static void Postfix() { if ((Object)(object)ContainerBridge.Opened != (Object)null) { ContainerBridge.SetUseEquipButton(useEquip: true); } } } [HarmonyPatch(typeof(InventoryGui), "OnStackAll")] [HarmonyPriority(800)] internal static class InventoryGui_OnStackAll_Stand_Patch { private static bool Prefix() { if ((Object)(object)ContainerBridge.Opened == (Object)null) { return true; } ContainerBridge.Opened.SwapEquip(); return false; } } internal sealed class StandVirtualContainer : Container { } [HarmonyPatch(typeof(Container), "IsOwner")] internal static class VirtualContainer_IsOwner_Patch { private static bool Prefix(Container __instance, ref bool __result) { if (!(__instance is StandVirtualContainer)) { return true; } __result = (Object)(object)ContainerBridge.Opened != (Object)null && ContainerBridge.Opened.HasAuthority; return false; } } [HarmonyPatch(typeof(Container), "SetInUse")] internal static class VirtualContainer_SetInUse_Patch { private static bool Prefix(Container __instance) { return !(__instance is StandVirtualContainer); } } [HarmonyPatch(typeof(Container), "StackAll")] internal static class VirtualContainer_StackAll_Patch { private static bool Prefix(Container __instance) { if (!(__instance is StandVirtualContainer)) { return true; } ContainerBridge.Opened?.SwapEquip(); return false; } } [HarmonyPatch(typeof(InventoryGui), "Hide")] internal static class InventoryGui_Hide_Stand_Patch { private static void Prefix() { ContainerBridge.Closed(); } } [HarmonyPatch(typeof(InventoryGui), "CloseContainer")] internal static class InventoryGui_CloseContainer_Stand_Patch { private static void Prefix() { ContainerBridge.Closed(); } } internal static class ItemDataSerializer { internal static byte[] Serialize(ItemData item) { //IL_0011: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown if (item == null) { return Array.Empty<byte>(); } Inventory val = new Inventory("RunicDisplayStands", (Sprite)null, 1, 1); ItemData val2 = item.Clone(); val2.m_stack = 1; val2.m_gridPos = new Vector2i(0, 0); val.GetAllItems().Add(val2); ZPackage val3 = new ZPackage(); val.Save(val3); return val3.GetArray(); } internal static ItemData Deserialize(byte[] bytes) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_0023: Expected O, but got Unknown if (bytes == null || bytes.Length == 0) { return null; } try { Inventory val = new Inventory("RunicDisplayStands", (Sprite)null, 1, 1); val.Load(new ZPackage(bytes)); List<ItemData> allItems = val.GetAllItems(); if (allItems.Count != 1 || allItems[0].m_stack != 1) { throw new InvalidOperationException("Invalid saved stand inventory."); } ItemData obj = allItems[0]; obj.m_stack = 1; return obj; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not restore complete stand item data; stand left unchanged. " + ex.Message)); } throw; } } } internal static class ConfigSync { private const string RpcName = "RunicDisplayStands_ConfigSync"; private const int MaximumPayloadBytes = 4096; private const int MaximumEntries = 16; private const int MaximumKeyBytes = 128; private const int MaximumValueBytes = 2048; private static readonly List<ConfigEntryBase> _synced = new List<ConfigEntryBase>(); private static ZRoutedRpc _registeredRpc; public static void RegisterSyncedConfig(ConfigEntryBase entry) { if (entry == null) { throw new ArgumentNullException("entry"); } string text = entry.Definition.Key ?? string.Empty; if (!IsBoundedField(text, 128) || text.IndexOfAny(new char[2] { '=', '|' }) >= 0) { throw new ArgumentException("Synced config keys must be bounded and delimiter-free.", "entry"); } if (!_synced.Exists((ConfigEntryBase candidate) => candidate.Definition == entry.Definition)) { if (_synced.Count >= 16) { throw new InvalidOperationException("The synced config entry limit has been reached."); } _synced.Add(entry); EnsureRpcRegistered(); } } internal static void EnsureRpcRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && _registeredRpc != instance) { instance.Register<string>("RunicDisplayStands_ConfigSync", (Action<long, string>)OnConfigReceived); _registeredRpc = instance; } } public static void NotifyNewConnection(ZNetPeer peer) { EnsureRpcRegistered(); if (_registeredRpc == ZRoutedRpc.instance && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && peer != null && TrySerialize(out var payload)) { ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "RunicDisplayStands_ConfigSync", new object[1] { payload }); } } private static void OnConfigReceived(long sender, string payload) { ZNet instance = ZNet.instance; if (!((Object)(object)instance == (Object)null) && !instance.IsServer()) { ZNetPeer serverPeer = instance.GetServerPeer(); if (serverPeer != null && serverPeer.IsReady() && serverPeer.m_uid == sender && TryDeserialize(payload)) { Plugin.RebuildStandPrefabSet(); Plugin.Log.LogInfo((object)"Received admin config from server; local Stand Prefabs list updated."); } } } private static bool TrySerialize(out string payload) { payload = string.Empty; if (_synced.Count == 0 || _synced.Count > 16) { return false; } List<string> list = new List<string>(_synced.Count); foreach (ConfigEntryBase item in _synced) { string text = item.Definition.Key ?? string.Empty; string text2 = item.BoxedValue?.ToString() ?? string.Empty; if (!IsBoundedField(text, 128) || !IsBoundedField(text2, 2048) || text.IndexOfAny(new char[2] { '=', '|' }) >= 0 || text2.IndexOf('|') >= 0) { return false; } list.Add(text + "=" + text2); } payload = string.Join("|", list); if (!IsBoundedField(payload, 4096)) { payload = string.Empty; return false; } return true; } private static bool TryDeserialize(string payload) { if (!IsBoundedField(payload, 4096)) { return false; } string[] array = payload.Split(new char[1] { '|' }, StringSplitOptions.None); if (array.Length == 0 || array.Length > 16) { return false; } Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal); string[] array2 = array; foreach (string text in array2) { int num = text.IndexOf('='); if (num <= 0) { return false; } string text2 = text.Substring(0, num); string value = text.Substring(num + 1); if (!IsBoundedField(text2, 128) || !IsBoundedField(value, 2048) || text2.IndexOf('|') >= 0 || !dictionary.TryAdd(text2, value)) { return false; } } foreach (ConfigEntryBase item in _synced) { if (dictionary.TryGetValue(item.Definition.Key, out var value2)) { item.BoxedValue = value2; } } return true; } private static bool IsBoundedField(string value, int maximumBytes) { if (value != null) { return Encoding.UTF8.GetByteCount(value) <= maximumBytes; } return false; } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class ZNet_Awake_ConfigSync_Patch { private static void Postfix() { ConfigSync.EnsureRpcRegistered(); } } [HarmonyPatch(typeof(ZRoutedRpc), "AddPeer")] internal static class ZRoutedRpc_AddPeer_ConfigSync_Patch { private static void Postfix(ZNetPeer peer) { ConfigSync.NotifyNewConnection(peer); } } internal static class ArmorStandAdapter { public static int GetSlotCount(ArmorStand stand) { return stand.m_slots.Count; } public static List<ItemData> GetPlacedItems(ArmorStand stand) { List<KeyValuePair<int, ItemData>> list = new List<KeyValuePair<int, ItemData>>(); ZNetView component = ((Component)stand).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return ((IEnumerable<ItemData>)(object)new ItemData[9]).ToList(); } ZDO zdo = component.GetZDO(); for (int i = 0; i < GetSlotCount(stand); i++) { int itemKey = ItemKey(i); int num = AttachedItemIdentity.Resolve(stand.GetAttachedItem(i), component.IsOwner(), () => zdo.GetString(itemKey, string.Empty), (string legacyName) => StringExtensionMethods.GetStableHashCode(legacyName), delegate(int migratedHash) { zdo.Set(itemKey, migratedHash, false); zdo.RemoveString(itemKey); }); if (num == 0) { continue; } ItemData val = ItemDataSerializer.Deserialize(zdo.GetByteArray(ItemDataKey(i), (byte[])null)); if (val != null) { if ((Object)(object)val.m_dropPrefab == (Object)null || StringExtensionMethods.GetStableHashCode(((Object)val.m_dropPrefab).name) != num) { throw new InvalidOperationException("Saved stand data does not match the displayed equipment; stand left unchanged."); } } else { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(num); if ((Object)(object)itemPrefab == (Object)null) { throw new InvalidOperationException("A displayed equipment prefab is unavailable."); } val = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); ItemDrop.LoadFromZDO(val, zdo, i); val.m_stack = 1; } int key = zdo.GetInt(RoleKey(i), 0) - 1; list.Add(new KeyValuePair<int, ItemData>(key, val)); } return ArmorStandSlots.ArrangeSaved(list).ToList(); } internal static StandWriteBatch PrepareWrite(ArmorStand stand, IList<ItemData> items) { ItemData[] array = MapItemsToSlots(stand, items); StandWriteBatch standWriteBatch = new StandWriteBatch(((Component)stand).GetComponent<ZNetView>()); for (int i = 0; i < array.Length; i++) { standWriteBatch.Item(array[i], i); standWriteBatch.Int(RoleKey(i), (array[i] != null) ? (ArmorStandSlots.Slot(array[i]) + 1) : 0); } return standWriteBatch; } internal static void PublishVisuals(ArmorStand stand, IList<ItemData> items) { ItemData[] mapped = MapItemsToSlots(stand, items); ZNetView component = ((Component)stand).GetComponent<ZNetView>(); for (int i = 0; i < mapped.Length; i++) { component.InvokeRPC(ZNetView.Everybody, "RPC_SetVisualItem", new object[3] { i, 0, 0 }); } foreach (int item in from num in Enumerable.Range(0, mapped.Length) where mapped[num] != null orderby ArmorStandSlots.IsDisplayHand(ArmorStandSlots.Slot(mapped[num])) select num) { component.InvokeRPC(ZNetView.Everybody, "RPC_SetVisualItem", new object[3] { item, (mapped[item] != null) ? StringExtensionMethods.GetStableHashCode(((Object)mapped[item].m_dropPrefab).name) : 0, mapped[item]?.m_variant ?? 0 }); } } internal static ItemData[] PlayerLoadout(Player player, Inventory stand) { List<ItemData> list = ((Humanoid)player).GetInventory().GetEquippedItems().ToList(); string[] array = new string[2] { "m_hiddenLeftItem", "m_hiddenRightItem" }; foreach (string text in array) { object? value = AccessTools.Field(typeof(Humanoid), text).GetValue(player); ItemData val = (ItemData)((value is ItemData) ? value : null); if (val != null && !list.Contains(val)) { list.Add(val); } } return ArmorStandSlots.SelectPlayerItems(((Humanoid)player).GetInventory(), list, stand); } private static int ItemKey(int index) { return StringExtensionMethods.GetStableHashCode($"{index}_item"); } private static int ItemDataKey(int index) { return StringExtensionMethods.GetStableHashCode($"RunicDisplayStands_itemdata_{index}"); } private static int RoleKey(int index) { return StringExtensionMethods.GetStableHashCode($"RunicDisplayStands_role_{index}"); } private static bool MatchesVisual(VisSlot native, int role) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Invalid comparison between Unknown and I4 //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Invalid comparison between Unknown and I4 switch (role) { case 0: return (int)native == 6; case 1: return (int)native == 4; case 2: return (int)native == 5; case 3: return (int)native == 7; case 4: return (int)native == 8; case 5: case 8: if ((int)native != 1) { return (int)native == 3; } return true; case 6: case 7: if ((int)native != 0) { return (int)native == 2; } return true; default: return false; } } public static ItemData[] MapItemsToSlots(ArmorStand stand, IEnumerable<ItemData> items) { return ArmorStandSlots.MapNative(items, GetSlotCount(stand), (int i, int role) => MatchesVisual(stand.m_slots[i].m_slot, role)); } } internal static class AttachedItemIdentity { internal static int Resolve(int currentHash, bool isOwner, Func<string> readLegacyName, Func<string, int> getStableHash, Action<int> migrateOwnedValue) { if (currentHash != 0) { return currentHash; } string text = readLegacyName(); if (string.IsNullOrEmpty(text)) { return 0; } int num = getStableHash(text); if (isOwner && num != 0) { migrateOwnedValue?.Invoke(num); } return num; } } [HarmonyPatch(typeof(ItemStand), "Interact")] internal static class ItemStand_Interact_Patch { private static bool Prefix(ItemStand __instance, Humanoid __0, bool __1, bool __2, ref bool __result) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.IsManagedStand(((Component)__instance).gameObject)) { return true; } if (__1) { return true; } if (!PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, true, false)) { __result = true; return false; } if (__2 || IsTakeOneHeld()) { ContainerBridge orCreate = ContainerBridge.GetOrCreate(((Component)__instance).gameObject); __result = orCreate.TakeOne(__0); } else { ContainerBridge orCreate2 = ContainerBridge.GetOrCreate(((Component)__instance).gameObject); __result = orCreate2.OpenAsContainer(__0); } return false; } private static bool IsTakeOneHeld() { //IL_0005: 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) KeyboardShortcut value = Plugin.CfgTakeOneKey.Value; return ((KeyboardShortcut)(ref value)).IsDown(); } } [HarmonyPatch(typeof(Switch), "Interact")] internal static class ArmorStand_Switch_Interact_Patch { [HarmonyBefore(new string[] { "chazman.RunicInteraction" })] private static bool Prefix(Switch __instance, Humanoid __0, bool __1, ref bool __result) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) ArmorStand componentInParent = ((Component)__instance).GetComponentInParent<ArmorStand>(); if ((Object)(object)componentInParent == (Object)null) { return true; } if (!Plugin.IsManagedStand(((Component)componentInParent).gameObject)) { return true; } if (__1) { return true; } if (!PrivateArea.CheckAccess(((Component)componentInParent).transform.position, 0f, false, false)) { __result = true; return false; } ContainerBridge orCreate = ContainerBridge.GetOrCreate(((Component)componentInParent).gameObject); __result = orCreate.OpenAsContainer(__0); return false; } } [HarmonyPatch(typeof(InventoryGrid), "GetElement")] [HarmonyPriority(800)] internal static class InventoryGrid_GetElement_Bounds_Patch { private static readonly FieldInfo ElementsField = AccessTools.Field(typeof(InventoryGrid), "m_elements"); private static bool Prefix(int x, int y, int width, InventoryGrid __instance, ref InventoryElement __result) { if (!(ElementsField?.GetValue(__instance) is IList list)) { return true; } long num = (long)y * (long)width + x; if (x >= 0 && x < width && y >= 0 && width > 0 && num >= 0 && num < list.Count) { return true; } __result = null; return false; } } internal static class ItemStandAdapter { private static readonly MethodInfo GetOrientationMethod = AccessTools.Method(typeof(ItemStand), "GetOrientation", (Type[])null, (Type[])null); private static readonly int ItemDataKey = StringExtensionMethods.GetStableHashCode("RunicDisplayStands_itemdata"); public static ItemData GetPlacedItem(ItemStand stand) { ZNetView component = ((Component)stand).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return null; } ZDO zdo = component.GetZDO(); int num = AttachedItemIdentity.Resolve(stand.GetAttachedItem(), component.IsOwner(), () => zdo.GetString(ZDOVars.s_item, string.Empty), (string legacyName) => StringExtensionMethods.GetStableHashCode(legacyName), delegate(int migratedHash) { zdo.Set(ZDOVars.s_item, migratedHash, false); zdo.RemoveString(ZDOVars.s_item); }); if (num == 0) { return null; } ItemData val = ItemDataSerializer.Deserialize(zdo.GetByteArray(ItemDataKey, (byte[])null)); if (val != null) { if ((Object)(object)val.m_dropPrefab == (Object)null || StringExtensionMethods.GetStableHashCode(((Object)val.m_dropPrefab).name) != num) { throw new InvalidOperationException("Saved stand data does not match the displayed item; stand left unchanged."); } return val; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(num); if ((Object)(object)itemPrefab == (Object)null) { throw new InvalidOperationException("The displayed item prefab is unavailable."); } ItemData obj = itemPrefab.GetComponent<ItemDrop>().m_itemData.Clone(); ItemDrop.LoadFromZDO(obj, zdo, -1); obj.m_stack = 1; return obj; } internal static StandWriteBatch PrepareWrite(ItemStand stand, ItemData item) { if (item != null && item.m_stack != 1) { throw new InvalidOperationException("An item stand holds one item, not a stack."); } StandWriteBatch standWriteBatch = new StandWriteBatch(((Component)stand).GetComponent<ZNetView>()); standWriteBatch.Item(item); return standWriteBatch; } internal static void PublishVisual(ItemStand stand, ItemData item) { ((Component)stand).GetComponent<ZNetView>().InvokeRPC(ZNetView.Everybody, "SetVisualItem", new object[4] { (item != null) ? StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name) : 0, item?.m_variant ?? 0, item?.m_quality ?? 1, GetOrientation(stand) }); } private static int GetOrientation(ItemStand stand) { return (int)GetOrientationMethod.Invoke(stand, null); } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] internal static class NamedStandSlotLabels { private static void Postfix(InventoryGrid __instance, List<InventoryElement> ___m_elements) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: 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_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)ContainerBridge.Opened != (Object)null && ContainerBridge.Opened.IsArmorStand && ContainerBridge.IsStandInventory(__instance.GetInventory()); for (int i = 0; i < ___m_elements.Count; i++) { InventoryElement val = ___m_elements[i]; Transform val2 = ((Component)val).transform.Find("RunicStandSlotLabel"); if ((flag || (Object)(object)val2 != (Object)null) && i >= 5 && i < 9) { RectTransform val3 = (RectTransform)((Component)val).transform; RectTransform val4 = (RectTransform)((Component)___m_elements[i % 5]).transform; Vector2 anchoredPosition = val3.anchoredPosition; anchoredPosition.x = val4.anchoredPosition.x + (flag ? (__instance.m_elementSpace * 0.5f) : 0f); val3.anchoredPosition = anchoredPosition; } if (!flag) { if ((Object)(object)val2 != (Object)null) { ((Component)val2).gameObject.SetActive(false); ((Component)val).gameObject.SetActive(true); } continue; } if ((Object)(object)val2 == (Object)null) { Transform val5 = ((Component)val).transform.Find("binding"); if ((Object)(object)val5 == (Object)null) { continue; } val2 = Object.Instantiate<GameObject>(((Component)val5).gameObject, ((Component)val).transform, false).transform; ((Object)val2).name = "RunicStandSlotLabel"; } ((Component)val).gameObject.SetActive(i < 9); if (i < 9) { ((Component)val2).gameObject.SetActive(true); TMP_Text component = ((Component)val2).GetComponent<TMP_Text>(); ((Behaviour)component).enabled = true; component.text = ArmorStandSlots.Labels[i]; component.fontSize = 11f; component.enableAutoSizing = false; component.alignment = (TextAlignmentOptions)258; ((Graphic)component).color = new Color(1f, 0.76f, 0.35f); ((Graphic)component).raycastTarget = false; RectTransform rectTransform = component.rectTransform; rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(0.5f, 1f); rectTransform.anchoredPosition = new Vector2(0f, -2f); rectTransform.sizeDelta = new Vector2(0f, 15f); } } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] internal static class NamedStandAutoAdd { private static bool Prefix(Inventory __instance, ItemData __0, ref bool __result) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) ContainerBridge opened = ContainerBridge.Opened; if ((Object)(object)opened == (Object)null || !opened.IsArmorStand || !ContainerBridge.IsStandInventory(__instance)) { return true; } __result = false; if (!opened.InTransfer) { return false; } int num = ArmorStandSlots.DefaultSlot(__0); int[] array = new int[3] { num, 5, 6 }; foreach (int slot in array) { if (ArmorStandSlots.Accepts(slot, __0) && ArmorStandSlots.GetItem(__instance, slot) == null) { __result = __instance.AddItem(__0, ArmorStandSlots.Position(slot)); break; } } return false; } } [HarmonyPatch(typeof(InventoryGrid), "DropItem")] internal static class NamedStandDropGuard { private static bool Prefix(InventoryGrid __instance, ItemData __1, Vector2i __3, ref bool __result) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) ContainerBridge opened = ContainerBridge.Opened; if ((Object)(object)opened == (Object)null || !opened.IsArmorStand || !ContainerBridge.IsStandInventory(__instance.GetInventory())) { return true; } if (__3.x >= 0 && __3.x < 5 && __3.y >= 0 && __3.y < 2 && ArmorStandSlots.Accepts(__3.y * 5 + __3.x, __1)) { return true; } opened.Warn("That item does not fit the selected stand slot."); __result = false; return false; } } internal static class RunicInventorySlots { internal static Dictionary<int, Vector2i> Capture(Player player) { //IL_021b: Unknown result type (might be due to invalid IL or missing references) Dictionary<int, Vector2i> dictionary = new Dictionary<int, Vector2i>(); if (!Chainloader.PluginInfos.TryGetValue("chazman.RunicInventory", out var value)) { return dictionary; } try { object obj = AccessTools.Property(((object)value.Instance).GetType(), "Runtime")?.GetValue(value.Instance); if (obj == null) { return dictionary; } object[] array = new object[3] { player.GetPlayerID(), null, null }; MethodInfo methodInfo = AccessTools.Method(obj.GetType(), "TryCapture", (Type[])null, (Type[])null); if (methodInfo == null || !(bool)methodInfo.Invoke(obj, array) || array[1] == null) { return dictionary; } object obj2 = array[1]; Type type = obj2.GetType(); if (Convert.ToInt32(type.GetProperty("AuthorityMode").GetValue(obj2)) != 1 || (int)type.GetProperty("Width").GetValue(obj2) != ((Humanoid)player).GetInventory().GetWidth() || (int)type.GetProperty("Height").GetValue(obj2) != ((Humanoid)player).GetInventory().GetHeight()) { return dictionary; } foreach (object item in (IEnumerable)type.GetProperty("Roles").GetValue(obj2)) { Type type2 = item.GetType(); int num = Convert.ToInt32(type2.GetProperty("Role").GetValue(item)) - 1; if (num >= 0 && num <= 4 && !(bool)type2.GetProperty("Locked").GetValue(item)) { object value2 = type2.GetProperty("Coordinate").GetValue(item); int num2 = (int)value2.GetType().GetProperty("X").GetValue(value2); int num3 = (int)value2.GetType().GetProperty("Y").GetValue(value2); if (num2 >= 0 && num2 < ((Humanoid)player).GetInventory().GetWidth() && num3 >= 0 && num3 < ((Humanoid)player).GetInventory().GetHeight()) { dictionary[num] = new Vector2i(num2, num3); } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Protected equipment slot lookup unavailable: " + ex.Message)); } } return dictionary; } } internal static class StandSlotValidator { public static bool IsValidForItemStand(ItemData item) { return item != null; } public static bool IsValidForArmorSlot(ItemData item, int slotIndex) { return ArmorStandSlots.Accepts(slotIndex, item); } } [HarmonyPatch] internal static class StandGuiTransferPatch { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(InventoryGui), "OnSelectedItem", new Type[4] { typeof(InventoryGrid), typeof(ItemData), typeof(Vector2i), typeof(Modifier) }, (Type[])null); yield return AccessTools.Method(typeof(InventoryGui), "OnTakeAll", Type.EmptyTypes, (Type[])null); } [HarmonyPriority(800)] private static bool Prefix(out ContainerBridge.Transfer __state) { __state = null; ContainerBridge opened = ContainerBridge.Opened; if ((Object)(object)opened == (Object)null || opened.InTransfer) { return true; } try { __state = opened.BeginTransfer(); return true; } catch (Exception ex) { opened.Warn(ex.Message); return false; } } [HarmonyPriority(0)] private static Exception Finalizer(Exception __exception, ContainerBridge.Transfer __state) { __state?.Finish(__exception == null); return __exception; } } [HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")] internal static class StandRightClickPatch { private static bool Prefix(InventoryGrid __0, ItemData __1) { ContainerBridge opened = ContainerBridge.Opened; if ((Object)(object)opened == (Object)null || !ContainerBridge.IsStandInventory(__0.GetInventory())) { return true; } opened.TakeItem(__1, use: true); return false; } } [HarmonyPatch(typeof(Humanoid), "DropItem", new Type[] { typeof(Inventory), typeof(ItemData), typeof(int) })] internal static class StandWorldDropPatch { private static bool Prefix(Humanoid __instance, Inventory __0, ref bool __result) { ContainerBridge opened = ContainerBridge.Opened; if ((Object)(object)opened == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || (!opened.InTransfer && !ContainerBridge.IsStandInventory(__0))) { return true; } opened.Warn("Move the item into your inventory before dropping it on the ground."); __result = false; return false; } } [HarmonyPatch] internal static class StandUnscopedMutationPatch { private static IEnumerable<MethodBase> TargetMethods() { return from method in typeof(Inventory).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) where new string[8] { "AddItem", "RemoveItem", "RemoveOneItem", "RemoveAll", "MoveAll", "MoveItemToThis", "MoveInventoryToGrave", "StackAll" }.Contains(method.Name) select method; } [HarmonyPriority(800)] private static bool Prefix(Inventory __instance) { if (ContainerBridge.IsStandInventory(__instance)) { return ContainerBridge.Opened.InTransfer; } return true; } } [BepInPlugin("chazman.RunicDisplayStands", "RunicDisplayStands", "1.3.8")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "chazman.RunicDisplayStands"; public const string PluginName = "RunicDisplayStands"; public const string PluginVersion = "1.3.8"; public static Plugin Instance; internal static ManualLogSource Log; public static ConfigEntry<string> CfgStandPrefabs; public static ConfigEntry<bool> CfgGamepadSupport; public static ConfigEntry<KeyboardShortcut> CfgTakeOneKey; public static HashSet<string> StandPrefabSet = new HashSet<string>(); private readonly Harmony _harmony = new Harmony("chazman.RunicDisplayStands"); private void Awake() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; CfgStandPrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Admin", "Stand Prefabs", "itemstand,itemstandh,ArmorStand", new ConfigDescription("Comma separated list of prefab names that should be treated as item/armor stands.", (AcceptableValueBase)null, new object[1] { new ConfigurationManagerAttributes { IsAdminOnly = true } })); CfgGamepadSupport = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Gamepad Support", true, "Enable controller-friendly interact prompts and alt-take binding."); CfgTakeOneKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "Take One Item Key", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), "Hold this while interacting with a stand to take a single item instead of opening it."); RebuildStandPrefabSet(); CfgStandPrefabs.SettingChanged += delegate { RebuildStandPrefabSet(); }; ConfigSync.RegisterSyncedConfig((ConfigEntryBase)(object)CfgStandPrefabs); _harmony.PatchAll(); Log.LogInfo((object)"RunicDisplayStands v1.3.8 loaded."); } internal static void RebuildStandPrefabSet() { StandPrefabSet = (from s in CfgStandPrefabs.Value.Split(',') select s.Trim() into s where s.Length > 0 select s).ToHashSet<string>(StringComparer.OrdinalIgnoreCase); } internal static bool IsManagedStand(GameObject stand) { if ((Object)(object)stand == (Object)null) { return false; } if ((Object)(object)stand.GetComponent<Piece>() == (Object)null) { return false; } string text = ((Object)stand).name.Replace("(Clone)", ""); if (!StandPrefabSet.Contains(text)) { return text.StartsWith("piece_runic_hero_display", StringComparison.OrdinalIgnoreCase); } return true; } } public class ConfigurationManagerAttributes { public bool IsAdminOnly; } internal static class ArmorStandSlots { internal const int Count = 9; internal const int Width = 5; internal const int Height = 2; internal const int Helmet = 0; internal const int Chest = 1; internal const int Legs = 2; internal const int Cape = 3; internal const int Utility = 4; internal const int RightHand = 5; internal const int LeftHand = 6; internal const int Shield = 7; internal const int Weapon = 8; internal static readonly string[] Labels = new string[9] { "Helmet", "Chest", "Legs", "Cape", "Utility", "Right hand", "Left hand", "Shield", "Weapon" }; internal static bool IsWeapon(ItemData item) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 if (item != null) { if ((int)item.m_shared.m_itemType != 3 && (int)item.m_shared.m_itemType != 14 && (int)item.m_shared.m_itemType != 22) { return (int)item.m_shared.m_itemType == 4; } return true; } return false; } internal static bool IsHandItem(ItemData item) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 //IL_001f: 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_0035: Invalid comparison between Unknown and I4 if (!IsWeapon(item)) { if (item != null) { if ((int)item.m_shared.m_itemType != 5 && (int)item.m_shared.m_itemType != 19) { return (int)item.m_shared.m_itemType == 15; } return true; } return false; } return true; } internal static bool Accepts(int slot, ItemData item) { //IL_0014: 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Invalid comparison between Unknown and I4 //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Invalid comparison between Unknown and I4 //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Invalid comparison between Unknown and I4 if (item == null || item.m_stack != 1) { return false; } ItemType itemType = item.m_shared.m_itemType; switch (slot) { case 0: return (int)itemType == 6; case 1: return (int)itemType == 7; case 2: return (int)itemType == 11; case 3: return (int)itemType == 17; case 4: return (int)itemType == 18; case 5: case 6: return IsHandItem(item); case 7: return (int)itemType == 5; case 8: return IsWeapon(item); default: return false; } } internal static int DefaultSlot(ItemData item) { int[] array = new int[9] { 0, 1, 2, 3, 4, 7, 8, 5, 6 }; foreach (int num in array) { if (Accepts(num, item)) { return num; } } return -1; } internal static int Slot(ItemData item) { if (item.m_gridPos.x < 0 || item.m_gridPos.x >= 5 || item.m_gridPos.y < 0 || item.m_gridPos.y >= 2) { return -1; } return item.m_gridPos.y * 5 + item.m_gridPos.x; } internal static Vector2i Position(int slot) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return new Vector2i(slot % 5, slot / 5); } internal static ItemData GetItem(Inventory inventory, int slot) { //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_0008: 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) Vector2i val = Position(slot); return inventory.GetItemAt(val.x, val.y); } internal static bool IsDisplayHand(int slot) { if (slot != 5) { return slot == 6; } return true; } internal static int LegacySlot(ItemData item, ItemData[] roles) { int num = DefaultSlot(item); if (num >= 0 && roles[num] == null) { return num; } int[] array = new int[2] { 5, 6 }; foreach (int num2 in array) { if (roles[num2] == null && Accepts(num2, item)) { return num2; } } throw new InvalidOperationException("This older stand has extra equipment that does not fit the named slots. Its saved items were left unchanged."); } internal static ItemData[] ArrangeSaved(IEnumerable<KeyValuePair<int, ItemData>> attachments) { //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) ItemData[] array = (ItemData[])(object)new ItemData[9]; List<ItemData> list = new List<ItemData>(); foreach (KeyValuePair<int, ItemData> attachment in attachments) { if (attachment.Key < 0) { list.Add(attachment.Value); continue; } if (!Accepts(attachment.Key, attachment.Value) || array[attachment.Key] != null) { throw new InvalidOperationException("Saved stand role is invalid or duplicated; stand left unchanged."); } array[attachment.Key] = attachment.Value; } foreach (ItemData item in list) { array[LegacySlot(item, array)] = item; } for (int i = 0; i < array.Length; i++) { if (array[i] != null) { array[i].m_gridPos = Position(i); array[i].m_equipped = false; } } return array; } internal static ItemData[] MapNative(IEnumerable<ItemData> items, int count, Func<int, int, bool> matchesVisual) { ItemData[] array = (ItemData[])(object)new ItemData[count]; HashSet<int> hashSet = new HashSet<int>(); foreach (ItemData item in from i in items where i != null orderby IsDisplayHand(Slot(i)) select i) { int num = Slot(item); if (!Accepts(num, item) || !hashSet.Add(num)) { throw new InvalidOperationException("That item does not fit the selected named stand slot."); } int num2 = -1; for (int num3 = 0; num3 < count; num3++) { if (array[num3] == null && matchesVisual(num3, num)) { num2 = num3; break; } } if (num2 < 0) { throw new InvalidOperationException("This stand has no available attachment point for that slot; items were left unchanged."); } array[num2] = item; } return array; } internal static ItemData[] SelectPlayerItems(Inventory player, IEnumerable<ItemData> selected, Inventory stand) { ItemData[] source = selected.Concat(from i in player.GetAllItems() orderby i.m_gridPos.y, i.m_gridPos.x select i).Distinct().ToArray(); ItemData[] array = (ItemData[])(object)new ItemData[9]; int[] array2 = new int[7] { 0, 1, 2, 3, 4, 7, 8 }; foreach (int slot in array2) { bool combat = slot == 7 || slot == 8; if (!combat || GetItem(stand, slot) != null) { array[slot] = ((IEnumerable<ItemData>)source).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => player.GetAllItems().Contains(i) && Accepts(slot, i) && (combat || slot == 4 || i.m_equipped))); } } return array; } } internal sealed class InventorySnapshot { private static readonly FieldInfo[] Fields = typeof(ItemData).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); private readonly Inventory _inventory; private readonly ItemData[] _originals; private readonly ItemData[] _copies; internal InventorySnapshot(Inventory inventory) { _inventory = inventory; _originals = inventory.GetAllItems().ToArray(); _copies = _originals.Select((ItemData item) => item.Clone()).ToArray(); } internal void Restore() { for (int i = 0; i < _originals.Length; i++) { ItemData obj = _copies[i].Clone(); FieldInfo[] fields = Fields; foreach (FieldInfo fieldInfo in fields) { fieldInfo.SetValue(_originals[i], fieldInfo.GetValue(obj)); } } _inventory.GetAllItems().Clear(); _inventory.GetAllItems().AddRange(_originals); } internal static string Contents(IEnumerable<ItemData> items) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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) //IL_007d: Expected O, but got Unknown SortedDictionary<string, long> sortedDictionary = new SortedDictionary<string, long>(StringComparer.Ordinal); foreach (ItemData item in items.Where((ItemData item) => item != null)) { if (item.m_stack <= 0) { throw new InvalidOperationException("Invalid item stack."); } ItemData obj = item.Clone(); obj.m_stack = 1; obj.m_gridPos = new Vector2i(0, 0); obj.m_equipped = false; ZPackage val = new ZPackage(); obj.Save(val); string key = ((Object)item.m_dropPrefab).name + ":" + val.GetBase64(); sortedDictionary.TryGetValue(key, out var value); sortedDictionary[key] = checked(value + item.m_stack); } return string.Join("\n", sortedDictionary.Select((KeyValuePair<string, long> pair) => pair.Key + ":" + pair.Value)); } } internal static class LoadoutSwap { internal static void Apply(Inventory player, Inventory stand, ItemData[] outgoing, Action<ItemData> unequip, Func<ItemData, bool> equip, IReadOnlyDictionary<int, Vector2i> equipmentSlots = null) { //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0151: Unknown result type (might be due to invalid IL or missing references) if (outgoing.Length != 9) { throw new ArgumentException("Expected named loadout slots."); } ItemData[] array = (ItemData[])(object)new ItemData[9]; Dictionary<int, Vector2i> dictionary = new Dictionary<int, Vector2i>(); for (int i = 0; i < 9; i++) { if (ArmorStandSlots.IsDisplayHand(i)) { continue; } ItemData item = ArmorStandSlots.GetItem(stand, i); if (((i == 3 || i == 4) && item == null) || ((i == 8 || i == 7) && (item == null || outgoing[i] == null))) { continue; } array[i] = item; ItemData val = outgoing[i]; if (val != null) { dictionary[i] = val.m_gridPos; unequip(val); if (!player.RemoveItem(val)) { throw new InvalidOperationException("An equipped item could not be moved."); } } if (item != null) { stand.GetAllItems().Remove(item); } if (val != null) { ItemData val2 = val.Clone(); val2.m_gridPos = ArmorStandSlots.Position(i); val2.m_equipped = false; stand.GetAllItems().Add(val2); } } for (int j = 0; j < array.Length; j++) { ItemData val3 = array[j]; if (val3 != null) { val3.m_equipped = false; Vector2i value = default(Vector2i); bool flag = equipmentSlots?.TryGetValue(j, out value) ?? false; if (!flag) { flag = dictionary.TryGetValue(j, out value); } if (!((flag && player.GetItemAt(value.x, value.y) == null) ? player.AddItem(val3, value) : player.AddItem(val3))) { throw new InvalidOperationException("Make room in your inventory before swapping this loadout."); } if (!equip(val3)) { throw new InvalidOperationException("The replacement equipment could not be equipped; the swap was canceled."); } } } ItemData val4 = outgoing[4]; if (array[4] == null && val4 != null && !val4.m_equipped && !equip(val4)) { throw new InvalidOperationException("Your carried utility item could not be equipped; the swap was canceled."); } } } internal static class StandGridLayout { internal static int Width(int slots) { return Math.Min(8, Math.Max(1, slots)); } internal static int Height(int slots) { return Math.Max(1, (slots + Width(slots) - 1) / Width(slots)); } internal static Vector2i Position(int index, int width) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return new Vector2i(index % width, index / width); } } internal sealed class StandWriteBatch { private readonly ZNetView _view; private readonly ZDO _zdo; private readonly List<Action> _writes = new List<Action>(); private readonly List<Action> _undo = new List<Action>(); private readonly List<Func<bool>> _checks = new List<Func<bool>>(); internal StandWriteBatch(ZNetView view) { _view = view; _zdo = view.GetZDO(); } internal void Int(int key, int value) { int old = _zdo.GetInt(key, 0); _writes.Add(delegate { _zdo.Set(key, value, false); }); _undo.Add(delegate { _zdo.Set(key, old, false); }); _checks.Add(() => _zdo.GetInt(key, 0) == value); } internal void ClearLegacy(int key) { string old = _zdo.GetString(key, string.Empty); _writes.Add(delegate { _zdo.RemoveString(key); }); _undo.Add(delegate { if (old.Length > 0) { _zdo.Set(key, old); } else { _zdo.RemoveString(key); } }); _checks.Add(() => _zdo.GetString(key, string.Empty).Length == 0); } internal void Bytes(int key, byte[] value) { byte[] old = (_zdo.GetByteArray(key, (byte[])null) ?? Array.Empty<byte>()).ToArray(); byte[] prepared = value.ToArray(); _writes.Add(delegate { _zdo.Set(key, prepared); }); _undo.Add(delegate { _zdo.Set(key, old); }); _checks.Add(() => (_zdo.GetByteArray(key, (byte[])null) ?? Array.Empty<byte>()).SequenceEqual(prepared)); } internal void Item(ItemData item, int index = -1) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown string text = ((index < 0) ? "" : (index + "_")); int stableHashCode = StringExtensionMethods.GetStableHashCode(text + "item"); Int(stableHashCode, (item != null) ? StringExtensionMethods.GetStableHashCode(((Object)item.m_dropPrefab).name) : 0); ClearLegacy(stableHashCode); Int(StringExtensionMethods.GetStableHashCode(text + "variant"), item?.m_variant ?? 0); Int(StringExtensionMethods.GetStableHashCode(text + "quality"), item?.m_quality ?? 1); ZPackage val = new ZPackage(); if (item != null) { val.Write((byte)109); item.Save(val); } Bytes(StringExtensionMethods.GetStableHashCode(text + "itemData"), (item == null) ? Array.Empty<byte>() : val.GetArray()); int stableHashCode2 = StringExtensionMethods.GetStableHashCode((index < 0) ? "RunicDisplayStands_itemdata" : ("RunicDisplayStands_itemdata_" + index)); Bytes(stableHashCode2, (item == null) ? Array.Empty<byte>() : ItemDataSerializer.Serialize(item)); } internal bool Commit() { if (!_view.IsValid() || !_view.IsOwner() || _view.GetZDO() != _zdo) { return false; } try { foreach (Action write in _writes) { write(); } if (!_checks.All((Func<bool> check) => check())) { throw new InvalidOperationException("Stand write verification failed."); } return true; } catch { if (_view.IsValid() && _view.IsOwner() && _view.GetZDO() == _zdo) { for (int num = _undo.Count - 1; num >= 0; num--) { _undo[num](); } } throw; } } } internal static class TransferBoundary { internal static bool Complete(bool actionSucceeded, Func<bool> validate, Func<bool> commit, Action rollback) { bool flag = false; try { if (actionSucceeded && validate()) { flag = commit(); } return flag; } finally { if (!flag) { rollback(); } } } } }