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 BoarderArmorStandStats v1.1.0
BoarderArmorStandStats.dll
Decompiled 2 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("BoarderArmorStandStats")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Azumatt")] [assembly: AssemblyProduct("BoarderArmorStandStats")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.1.0.0")] [module: UnverifiableCode] [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 BoarderArmorStandStats { public interface ITransientContainer { bool SuppressPersistence { get; } void OnContainerDropRequested(); } public class ArmourStandBridge : Container, ITransientContainer { internal ArmorStand? ArmourStand; private SlotTypeMap? _acceptedTypes; private int _syncedItemCount; private static readonly string[] ContainerRpcEndpoints = new string[6] { "RequestOpen", "OpenRespons", "RPC_RequestStack", "RPC_StackResponse", "RequestTakeAll", "TakeAllRespons" }; public bool ViewingInventory { get { if ((Object)(object)InventoryGui.instance != (Object)null && InventoryGui.instance.m_currentContainer == this) { return InventoryGui.IsVisible(); } return false; } } public bool SuppressPersistence => true; public void OnContainerDropRequested() { Inventory inventory = ((Container)this).GetInventory(); if (inventory != null) { inventory.RemoveAll(); } } private void Awake() { if (ArmourStand == null) { ArmourStand = ((Component)this).GetComponent<ArmorStand>(); } if ((Object)(object)ArmourStand == (Object)null) { throw new InvalidOperationException("No ArmorStand attached to " + ((Object)this).name); } base.m_name = ArmourStand.m_name; base.m_width = ArmourStand.m_slots.Count; base.m_height = 1; Switch componentInChildren = ((Component)this).GetComponentInChildren<Switch>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.m_name = ArmourStand.m_name; } ((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(SyncToFigure)); _acceptedTypes = SlotTypeMap.BuildFrom(ArmourStand); } private void OnDestroy() { DetachRpcHandlers(); BridgeRegistry.Deregister(this); } private void DetachRpcHandlers() { if (!((Object)(object)base.m_nview == (Object)null)) { string[] containerRpcEndpoints = ContainerRpcEndpoints; foreach (string text in containerRpcEndpoints) { base.m_nview.Unregister(text); } } } internal static string SlotItemKey(int slotIndex) { return $"{slotIndex}_item"; } private void SyncToFigure() { if (ViewingInventory) { PushToAmourStand(); } } public void PullFromArmourStandIfChanged() { if ((Object)(object)ArmourStand == (Object)null) { return; } int nrOfAttachedItems = ArmourStand.GetNrOfAttachedItems(); int num = base.m_inventory.NrOfItems(); if (nrOfAttachedItems == num && _syncedItemCount == nrOfAttachedItems) { return; } base.m_loading = true; base.m_inventory.RemoveAll(); ZDO zDO = base.m_nview.GetZDO(); for (int i = 0; i < ArmourStand.m_slots.Count; i++) { string @string = zDO.GetString(SlotItemKey(i), ""); if (!string.IsNullOrEmpty(@string)) { ItemData val = HydrateFromZdo(@string, i); if (val != null) { base.m_inventory.AddItem(val); } } } _syncedItemCount = base.m_inventory.NrOfItems(); base.m_loading = false; } private void PushToAmourStand() { if ((Object)(object)ArmourStand == (Object)null) { return; } int nrOfAttachedItems = ArmourStand.GetNrOfAttachedItems(); int num = base.m_inventory.NrOfItems(); if (nrOfAttachedItems == num && _syncedItemCount == num) { return; } WipeAllSlots(); foreach (ItemData allItem in base.m_inventory.GetAllItems()) { int x = allItem.m_gridPos.x; WriteSlot(allItem, x); } ArmourStand.UpdateVisual(); ArmourStand.UpdateSupports(); _syncedItemCount = num; } private void WipeAllSlots() { if ((Object)(object)ArmourStand == (Object)null) { return; } ZDO zDO = base.m_nview.GetZDO(); bool flag = base.m_nview.IsOwner(); for (int i = 0; i < ArmourStand.m_slots.Count; i++) { if (flag) { zDO.Set(SlotItemKey(i), ""); } ArmourStand.SetVisualItem(i, "", 0); } } private void WriteSlot(ItemData piece, int slot) { if (!((Object)(object)ArmourStand == (Object)null) && piece != null && slot >= 0 && slot < ArmourStand.m_slots.Count && base.m_nview.IsOwner()) { ArmorStandSlot val = ArmourStand.m_slots[slot]; if (ArmourStand.CanAttach(val, piece)) { ZDO zDO = base.m_nview.GetZDO(); zDO.Set(SlotItemKey(slot), ((Object)piece.m_dropPrefab).name); ItemDrop.SaveToZDO(slot, piece, zDO); } } } internal ItemData? HydrateFromZdo(string prefabId, int zdoIndex = -1) { if (string.IsNullOrEmpty(prefabId)) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefabId); if ((Object)(object)itemPrefab == (Object)null) { return null; } ItemDrop val = default(ItemDrop); if (!itemPrefab.TryGetComponent<ItemDrop>(ref val)) { return null; } ItemData val2 = val.m_itemData.Clone(); if (zdoIndex >= 0) { ItemDrop.LoadFromZDO(zdoIndex, val2, base.m_nview.GetZDO()); } return val2; } public void RelocateToSlot(ItemData? piece) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ArmourStand == (Object)null || piece == null) { return; } int num = LocateCompatibleSlot(piece, allowOccupied: true); if (num < 0) { BoarderArmorStandStatsPlugin.BoarderArmorStandStatsPluginLogger.LogWarning((object)("No compatible slot for " + piece.m_shared.m_name)); return; } Vector2i val = default(Vector2i); ((Vector2i)(ref val))..ctor(num, 0); if (!(piece.m_gridPos == val)) { ItemData itemAt = base.m_inventory.GetItemAt(val.x, val.y); if (itemAt != null && itemAt != piece) { Vector2i gridPos = piece.m_gridPos; piece.m_gridPos = val; itemAt.m_gridPos = gridPos; } else { piece.m_gridPos = val; } base.m_inventory.Changed(); } } public AttachmentCheck ValidateAttachment(ItemData piece, int targetSlot, bool isInternal) { //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: 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 ((Object)(object)ArmourStand == (Object)null || piece == null) { return AttachmentCheck.Deny("Invalid context"); } if (!isInternal) { foreach (ItemData allItem in base.m_inventory.GetAllItems()) { if (allItem.m_shared.m_itemType == piece.m_shared.m_itemType) { return AttachmentCheck.Deny($"Duplicate {piece.m_shared.m_itemType} type"); } } } if (targetSlot >= 0 && targetSlot < ArmourStand.m_slots.Count) { ArmorStandSlot val = ArmourStand.m_slots[targetSlot]; if (!isInternal && ArmourStand.HaveAttachment(targetSlot)) { return AttachmentCheck.Deny("Position taken"); } if (!ArmourStand.CanAttach(val, piece)) { return AttachmentCheck.Deny($"{piece.m_shared.m_itemType} cannot attach here"); } return AttachmentCheck.Permit(); } if (LocateCompatibleSlot(piece, isInternal) < 0) { return AttachmentCheck.Deny($"No attach point for {piece.m_shared.m_itemType}"); } return AttachmentCheck.Permit(); } private int LocateCompatibleSlot(ItemData? piece, bool allowOccupied) { if ((Object)(object)ArmourStand == (Object)null || piece == null) { return -1; } SlotTypeMap? acceptedTypes = _acceptedTypes; if (acceptedTypes != null && acceptedTypes.HasEntries) { foreach (KeyValuePair<int, ItemType> entry in _acceptedTypes.Entries) { int key = entry.Key; if ((allowOccupied || !ArmourStand.HaveAttachment(key)) && ArmourStand.CanAttach(ArmourStand.m_slots[key], piece)) { return key; } } } for (int i = 0; i < ArmourStand.m_slots.Count; i++) { if ((allowOccupied || !ArmourStand.HaveAttachment(i)) && ArmourStand.CanAttach(ArmourStand.m_slots[i], piece)) { return i; } } return -1; } public void NotifyUser(string text = "") { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { ((Character)Player.m_localPlayer).Message((MessageType)2, string.IsNullOrEmpty(text) ? "$piece_armorstand_cantattach" : text, 0, (Sprite)null); } } } internal class SlotTypeMap { public Dictionary<int, ItemType> Entries { get; } = new Dictionary<int, ItemType>(); public bool HasEntries => Entries.Count > 0; public static SlotTypeMap BuildFrom(ArmorStand figure) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) SlotTypeMap slotTypeMap = new SlotTypeMap(); if ((Object)(object)figure == (Object)null) { return slotTypeMap; } for (int i = 0; i < figure.m_slots.Count; i++) { ArmorStandSlot val = figure.m_slots[i]; List<ItemType> supportedTypes = val.m_supportedTypes; if (supportedTypes != null && supportedTypes.Count > 0) { slotTypeMap.Entries[i] = val.m_supportedTypes[0]; } } return slotTypeMap; } } public readonly struct AttachmentCheck { public bool Allowed { get; } public string Message { get; } private AttachmentCheck(bool allowed, string message) { Allowed = allowed; Message = message; } public static AttachmentCheck Permit() { return new AttachmentCheck(allowed: true, ""); } public static AttachmentCheck Deny(string message) { return new AttachmentCheck(allowed: false, message); } } public class EquipmentMetrics { public float TotalArmor; public float TotalWeight; public float MovementPenalty; public readonly Dictionary<DamageType, float> Resistances = new Dictionary<DamageType, float>(); public readonly List<string> ArmorBreakdown = new List<string>(); public readonly Dictionary<string, int> SetCounts = new Dictionary<string, int>(); } internal static class BridgeRegistry { private static ArmourStandBridge?[] _table = new ArmourStandBridge[64]; private static bool[] _isTombstone = new bool[64]; private static int _population; private static int ComputeSlot(Inventory inv) { return (((object)inv).GetHashCode() & 0x7FFFFFFF) % _table.Length; } public static void Register(ArmourStandBridge bridge) { Inventory inventory = ((Container)bridge).GetInventory(); if (inventory == null) { return; } int num = ComputeSlot(inventory); int num2 = -1; for (int i = 0; i < _table.Length; i++) { int num3 = (num + i) % _table.Length; if (_isTombstone[num3] && num2 < 0) { num2 = num3; continue; } if ((Object)(object)_table[num3] == (Object)null && !_isTombstone[num3]) { int num4 = ((num2 >= 0) ? num2 : num3); _table[num4] = bridge; _isTombstone[num4] = false; _population++; if ((float)_population > (float)_table.Length * 0.7f) { Expand(); } return; } if (!_isTombstone[num3]) { ArmourStandBridge? obj = _table[num3]; if (((obj != null) ? ((Container)obj).GetInventory() : null) == inventory) { _table[num3] = bridge; return; } } } if (num2 >= 0) { _table[num2] = bridge; _isTombstone[num2] = false; _population++; if ((float)_population > (float)_table.Length * 0.7f) { Expand(); } } } public static void Deregister(ArmourStandBridge bridge) { Inventory inventory = ((Container)bridge).GetInventory(); if (inventory == null) { for (int i = 0; i < _table.Length; i++) { if (_table[i] == bridge) { _table[i] = null; _isTombstone[i] = true; _population = Math.Max(0, _population - 1); break; } } return; } int num = ComputeSlot(inventory); for (int j = 0; j < _table.Length; j++) { int num2 = (num + j) % _table.Length; if ((Object)(object)_table[num2] == (Object)null && !_isTombstone[num2]) { break; } if (!_isTombstone[num2] && _table[num2] == bridge) { _table[num2] = null; _isTombstone[num2] = true; _population = Math.Max(0, _population - 1); break; } } } public static bool Resolve(Inventory? inv, out ArmourStandBridge bridge) { bridge = null; if (inv == null) { return false; } int num = ComputeSlot(inv); for (int i = 0; i < _table.Length; i++) { int num2 = (num + i) % _table.Length; if ((Object)(object)_table[num2] == (Object)null && !_isTombstone[num2]) { return false; } if (!_isTombstone[num2]) { ArmourStandBridge armourStandBridge = _table[num2]; if (!((Object)(object)armourStandBridge == (Object)null) && ((Container)armourStandBridge).GetInventory() == inv) { bridge = armourStandBridge; return true; } } } return false; } public static void Reset() { Array.Clear(_table, 0, _table.Length); Array.Clear(_isTombstone, 0, _isTombstone.Length); _population = 0; } private static void Expand() { ArmourStandBridge[] table = _table; bool[] isTombstone = _isTombstone; _table = new ArmourStandBridge[table.Length * 2]; _isTombstone = new bool[table.Length * 2]; _population = 0; for (int i = 0; i < table.Length; i++) { if ((Object)(object)table[i] != (Object)null && !isTombstone[i]) { Register(table[i]); } } } } [HarmonyPatch(typeof(ZNetScene), "Awake")] internal static class AddMannequinBridgeComponentToArmorStand { private static void Postfix(ZNetScene __instance) { GameObject prefab = __instance.GetPrefab("ArmorStand"); if (!((Object)(object)prefab == (Object)null) && !((Object)(object)prefab.GetComponent<ArmourStandBridge>() != (Object)null)) { prefab.AddComponent<ArmourStandBridge>(); } } } [HarmonyPatch(typeof(Container))] internal static class BridgeLifecycleHooks { [HarmonyPostfix] [HarmonyPatch("Awake")] private static void AfterAwake(Container __instance) { if (__instance is ArmourStandBridge bridge) { BridgeRegistry.Register(bridge); } } } [HarmonyPatch(typeof(Container))] internal static class TransientContainerInterceptor { [HarmonyPrefix] [HarmonyPatch("Save")] private static bool InterceptSave(Container __instance) { return !(__instance is ITransientContainer transientContainer) || !transientContainer.SuppressPersistence; } [HarmonyPrefix] [HarmonyPatch("DropAllItems", new Type[] { })] private static bool InterceptDrop(Container __instance) { if (!(__instance is ITransientContainer transientContainer)) { return true; } transientContainer.OnContainerDropRequested(); return false; } } [HarmonyPatch(typeof(Game), "Start")] internal static class SessionBoundaryHandler { private static void Prefix() { BridgeRegistry.Reset(); GridCellRenderer.InvalidateCache(); } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] internal static class ItemInsertionValidator { [HarmonyPriority(800)] private static bool Prefix(Inventory __instance, ItemData item, ref bool __result) { if (!BridgeRegistry.Resolve(__instance, out ArmourStandBridge bridge)) { return true; } if (item.m_stack != 1) { bridge.NotifyUser("Armor stands only accept single items"); __result = false; return false; } AttachmentCheck attachmentCheck = bridge.ValidateAttachment(item, -1, isInternal: false); if (attachmentCheck.Allowed || !bridge.ViewingInventory) { return true; } BoarderArmorStandStatsPlugin.BoarderArmorStandStatsPluginLogger.LogDebug((object)("Rejected " + item.m_shared.m_name + ": " + attachmentCheck.Message)); bridge.NotifyUser(attachmentCheck.Message); __result = false; return false; } [HarmonyPriority(800)] private static void Postfix(Inventory __instance, ItemData item, bool __result) { if (__result && BridgeRegistry.Resolve(__instance, out ArmourStandBridge bridge)) { bridge.RelocateToSlot(item); } } } [HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")] internal static class DragDropInterceptor { [HarmonyPriority(800)] private static bool Prefix(InventoryGui __instance, InventoryGrid grid, Modifier mod, Vector2i pos) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (!(__instance.m_currentContainer is ArmourStandBridge armourStandBridge)) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsTeleporting()) { return true; } if ((int)mod != 0) { return true; } ItemData dragItem = __instance.m_dragItem; if (dragItem == null) { return true; } Inventory inventory = grid.GetInventory(); Inventory inventory2 = ((Container)armourStandBridge).GetInventory(); if (inventory != inventory2) { return true; } __instance.m_dragAmount = 1; bool isInternal = __instance.m_dragInventory == inventory2; AttachmentCheck attachmentCheck = armourStandBridge.ValidateAttachment(dragItem, pos.x, isInternal); if (attachmentCheck.Allowed) { return true; } armourStandBridge.NotifyUser(attachmentCheck.Message); __instance.m_dragAmount = dragItem.m_stack; return false; } } [HarmonyPatch(typeof(InventoryGui), "Show")] internal static class ContainerOpenHook { private static void Postfix(Container container) { if (container is ArmourStandBridge armourStandBridge) { armourStandBridge.PullFromArmourStandIfChanged(); } } } [HarmonyPatch(typeof(Switch), "Interact")] internal static class InteractionRouter { private static bool Prefix(Switch __instance, Humanoid character, bool hold, bool alt, ref bool __result) { if (!BoarderArmorStandStatsPlugin.EnableContainerFeature.Value) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return true; } Player val = (Player)(object)((character is Player) ? character : null); if (val != null && (Object)(object)val != (Object)(object)localPlayer) { return true; } GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject(); if ((Object)(object)hoverObject == (Object)null) { return true; } ArmorStand componentInParent = hoverObject.GetComponentInParent<ArmorStand>(); if ((Object)(object)componentInParent == (Object)null) { return true; } ArmourStandBridge armourStandBridge = default(ArmourStandBridge); if (!((Component)componentInParent).TryGetComponent<ArmourStandBridge>(ref armourStandBridge)) { return true; } if (!((Container)armourStandBridge).m_nview.IsOwner()) { ((Container)armourStandBridge).m_nview.ClaimOwnership(); } if (alt && !hold) { __result = true; return true; } armourStandBridge.PullFromArmourStandIfChanged(); InventoryGui.instance.Show((Container)(object)armourStandBridge, 1); __result = true; return false; } } [HarmonyPatch(typeof(ArmorStand), "UseItem")] internal static class HotbarInteractionBlock { private static bool Prefix(ArmorStand __instance, ref bool __result) { ArmourStandBridge armourStandBridge = default(ArmourStandBridge); if (!((Component)__instance).TryGetComponent<ArmourStandBridge>(ref armourStandBridge)) { return true; } if (((Container)armourStandBridge).IsInUse()) { __result = false; return false; } if (!BoarderArmorStandStatsPlugin.EnableContainerFeature.Value) { return true; } bool button; if (ZInput.IsNonClassicFunctionality() && ZInput.IsGamepadActive()) { button = ZInput.GetButton("JoyAltKeys"); } else { if (ZInput.GetButton("AltPlace")) { goto IL_005c; } button = ZInput.GetButton("JoyAltPlace"); } if (!button) { __result = false; return false; } goto IL_005c; IL_005c: return true; } } [HarmonyPatch(typeof(ArmorStand), "UpdateVisual")] internal static class VisualSyncHook { private static void Postfix(ArmorStand __instance) { ArmourStandBridge armourStandBridge = default(ArmourStandBridge); if (((Component)__instance).TryGetComponent<ArmourStandBridge>(ref armourStandBridge) && !armourStandBridge.ViewingInventory) { armourStandBridge.PullFromArmourStandIfChanged(); } } } [HarmonyPatch(typeof(Switch), "GetHoverText")] internal static class HoverTextEnhancer { private static void Postfix(Switch __instance, ref string __result) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) ArmorStand componentInParent = ((Component)__instance).GetComponentInParent<ArmorStand>(); if ((Object)(object)componentInParent == (Object)null || (Object)(object)__instance == (Object)(object)componentInParent.m_changePoseSwitch || !BoarderArmorStandStatsPlugin.EnableContainerFeature.Value) { return; } __result = ""; if (PrivateArea.CheckAccess(((Component)__instance).transform.position, 0f, false, false)) { string text = __instance.m_name + "\n[<color=yellow><b>$KEY_Use</b></color>] $piece_container_open"; if (componentInParent.GetNrOfAttachedItems() > 0) { text += "\n[<color=yellow><b>L.Shift + $KEY_Use</b></color>] $piece_itemstand_take"; } __result = Localization.instance.Localize(text); string text2 = HoverInfoGenerator.Generate(componentInParent); if (!string.IsNullOrEmpty(text2)) { __result += Localization.instance.Localize(text2); } } else { __result = Localization.instance.Localize(__instance.m_name + "\n$piece_noaccess"); } } } [HarmonyPatch(typeof(InventoryGrid), "UpdateGui")] internal static class GridVisualEnhancer { private static void Postfix(InventoryGrid __instance) { if (__instance.m_inventory == null || !BridgeRegistry.Resolve(__instance.m_inventory, out ArmourStandBridge bridge)) { return; } for (int i = 0; i < __instance.m_elements.Count; i++) { Element val = __instance.m_elements[i]; if (!((Object)(object)val?.m_go == (Object)null)) { GridCellRenderer.Render(val, i, bridge); } } } } [BepInPlugin("Azumatt.BoarderArmorStandStats", "BoarderArmorStandStats", "1.1.0")] public class BoarderArmorStandStatsPlugin : BaseUnityPlugin { internal const string ModName = "BoarderArmorStandStats"; internal const string ModVersion = "1.1.0"; internal const string Author = "Azumatt"; private const string ModGUID = "Azumatt.BoarderArmorStandStats"; private static string ConfigFileName = "Azumatt.BoarderArmorStandStats.cfg"; private static string ConfigFileFullPath; internal static string ConnectionError; private readonly Harmony _harmony = new Harmony("Azumatt.BoarderArmorStandStats"); public static readonly ManualLogSource BoarderArmorStandStatsPluginLogger; private static ConfigEntry<bool> _serverConfigLocked; public static ConfigEntry<bool> EnableContainerFeature; public void Awake() { _serverConfigLocked = config("1 - General", "Lock Configuration", value: true, "If on, the configuration is locked and can be changed by server admins only."); EnableContainerFeature = config("2 - Container", "Enable Container Feature", value: true, "Enable opening armor stands as containers (E opens container, Shift+E for normal interaction)"); Assembly executingAssembly = Assembly.GetExecutingAssembly(); _harmony.PatchAll(executingAssembly); SetupWatcher(); } private void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } private void SetupWatcher() { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); fileSystemWatcher.Changed += ReadConfigValues; fileSystemWatcher.Created += ReadConfigValues; fileSystemWatcher.Renamed += ReadConfigValues; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { if (!File.Exists(ConfigFileFullPath)) { return; } try { BoarderArmorStandStatsPluginLogger.LogDebug((object)"ReadConfigValues called"); ((BaseUnityPlugin)this).Config.Reload(); } catch { BoarderArmorStandStatsPluginLogger.LogError((object)("There was an issue loading your " + ConfigFileName)); BoarderArmorStandStatsPluginLogger.LogError((object)"Please check your config entries for spelling and format!"); } } private ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags); return ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val); } private ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } static BoarderArmorStandStatsPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; ConnectionError = ""; BoarderArmorStandStatsPluginLogger = Logger.CreateLogSource("BoarderArmorStandStats"); _serverConfigLocked = null; EnableContainerFeature = null; } } internal static class GridCellRenderer { private const string PlaceholderTag = "EquipHint"; private static Sprite[]? _iconCache; public static void Render(Element cell, int position, ArmourStandBridge bridge) { if ((Object)(object)cell?.m_go == (Object)null || (Object)(object)bridge?.ArmourStand == (Object)null) { return; } CellStateTracker component = cell.m_go.GetComponent<CellStateTracker>(); if (((Behaviour)cell.m_icon).enabled) { RemoveHint(cell.m_go); string @string = ((Container)bridge).m_nview.GetZDO().GetString(ArmourStandBridge.SlotItemKey(position), ""); if (!string.IsNullOrEmpty(@string)) { ItemData equipment = bridge.HydrateFromZdo(@string, position); ApplyWearIndicator(cell.m_go, component, equipment); } else { ClearWearIndicator(cell.m_go, component); } } else { ClearWearIndicator(cell.m_go, component); ShowHint(cell.m_go, position, bridge); } } private static void ApplyWearIndicator(GameObject host, CellStateTracker? tracker, ItemData? equipment) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: 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_007c: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) Button val = default(Button); if (host.TryGetComponent<Button>(ref val)) { if (tracker == null) { tracker = host.AddComponent<CellStateTracker>(); } if (!tracker.capturedOriginal) { tracker.baseColors = ((Selectable)val).colors; tracker.capturedOriginal = true; } if (equipment == null || !equipment.m_shared.m_useDurability) { ((Selectable)val).colors = tracker.baseColors; return; } Color val2 = DeriveConditionColor(equipment.GetDurabilityPercentage()); ColorBlock baseColors = tracker.baseColors; ((ColorBlock)(ref baseColors)).normalColor = ((ColorBlock)(ref baseColors)).normalColor + val2; ((ColorBlock)(ref baseColors)).highlightedColor = ((ColorBlock)(ref baseColors)).highlightedColor + val2; ((Selectable)val).colors = baseColors; } } private static void ClearWearIndicator(GameObject host, CellStateTracker? tracker) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) Button val = default(Button); if (host.TryGetComponent<Button>(ref val) && (Object)(object)tracker != (Object)null && tracker.capturedOriginal) { ((Selectable)val).colors = tracker.baseColors; } } private static Color DeriveConditionColor(float ratio) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //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_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if (!(ratio >= 0.75f)) { if (!(ratio >= 0.5f)) { if (ratio >= 0.25f) { return new Color(0.6f, 0.3f, 0f, 0.4f); } return new Color(0.6f, 0f, 0f, 0.5f); } return new Color(0.5f, 0.5f, 0f, 0.35f); } return new Color(0f, 0.5f, 0f, 0.3f); } private static void ShowHint(GameObject host, int position, ArmourStandBridge bridge) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)host.transform.Find("EquipHint") != (Object)null)) { Sprite val = ResolveIconForPosition(position, bridge); if (!((Object)(object)val == (Object)null)) { GameObject val2 = new GameObject("EquipHint"); val2.transform.SetParent(host.transform, false); val2.transform.SetAsFirstSibling(); RectTransform obj = val2.AddComponent<RectTransform>(); obj.anchorMin = Vector2.zero; obj.anchorMax = Vector2.one; obj.offsetMin = Vector2.zero; obj.offsetMax = Vector2.zero; Image obj2 = val2.AddComponent<Image>(); obj2.sprite = val; ((Graphic)obj2).color = new Color(1f, 1f, 1f, 0.5f); ((Graphic)obj2).raycastTarget = false; } } } private static void RemoveHint(GameObject host) { Transform val = host.transform.Find("EquipHint"); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } private static Sprite? ResolveIconForPosition(int position, ArmourStandBridge bridge) { if ((Object)(object)bridge.ArmourStand == (Object)null) { return null; } if (position >= bridge.ArmourStand.m_slots.Count) { return null; } if (_iconCache == null) { _iconCache = (Sprite[]?)(object)new Sprite[8]; } if (position < _iconCache.Length && (Object)(object)_iconCache[position] != (Object)null) { return _iconCache[position]; } ArmorStandSlot val = bridge.ArmourStand.m_slots[position]; if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } ItemDrop val2 = default(ItemDrop); foreach (GameObject item in ObjectDB.instance.m_items) { if ((Object)(object)item == (Object)null || !item.TryGetComponent<ItemDrop>(ref val2) || val2.m_itemData == null || !bridge.ArmourStand.CanAttach(val, val2.m_itemData)) { continue; } try { Sprite[] array = val2.m_itemData.m_shared?.m_icons; if (array == null || array.Length <= 0) { continue; } Sprite val3 = array[0]; if (position < _iconCache.Length) { _iconCache[position] = val3; } return val3; } catch (Exception ex) { BoarderArmorStandStatsPlugin.BoarderArmorStandStatsPluginLogger.LogWarning((object)("Icon extraction failed for " + ((Object)item).name + ": " + ex.Message)); } } return null; } public static void InvalidateCache() { _iconCache = null; } } internal class CellStateTracker : MonoBehaviour { public ColorBlock baseColors; public bool capturedOriginal; public CellStateTracker(ColorBlock baseColors, bool capturedOriginal) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) this.baseColors = baseColors; this.capturedOriginal = capturedOriginal; ((MonoBehaviour)this)..ctor(); } } public static class HoverInfoGenerator { public static string Generate(ArmorStand figure) { if ((Object)(object)figure?.m_nview == (Object)null) { return ""; } List<ItemData> list = GatherEquipment(figure); if (list.Count == 0) { return ""; } EquipmentMetrics figure2 = ComputeMetrics(list); EquipmentMetrics player = ComputePlayerMetrics(); return FormatOutput(figure2, player); } private static List<ItemData> GatherEquipment(ArmorStand figure) { List<ItemData> list = new List<ItemData>(); ZDO zDO = figure.m_nview.GetZDO(); ItemDrop val2 = default(ItemDrop); for (int i = 0; i < figure.m_slots.Count; i++) { string text = ((zDO != null) ? zDO.GetString(ArmourStandBridge.SlotItemKey(i), "") : null); if (!string.IsNullOrEmpty(text)) { ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(text) : null); if (!((Object)(object)val == (Object)null) && val.TryGetComponent<ItemDrop>(ref val2)) { ItemData val3 = val2.m_itemData.Clone(); ItemDrop.LoadFromZDO(i, val3, zDO); list.Add(val3); } } } return list; } private static EquipmentMetrics ComputeMetrics(List<ItemData> equipment) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) EquipmentMetrics equipmentMetrics = new EquipmentMetrics(); foreach (ItemData item in equipment) { if (item.m_shared.m_armor > 0f) { float armor = item.GetArmor(); equipmentMetrics.TotalArmor += armor; equipmentMetrics.ArmorBreakdown.Add(Localization.instance.Localize($" {item.m_shared.m_name}: {armor:F0}")); } equipmentMetrics.TotalWeight += item.GetWeight(-1); equipmentMetrics.MovementPenalty += item.m_shared.m_movementModifier; foreach (DamageModPair damageModifier in item.m_shared.m_damageModifiers) { float num = ConvertModifier(damageModifier.m_modifier); if (!equipmentMetrics.Resistances.ContainsKey(damageModifier.m_type)) { equipmentMetrics.Resistances[damageModifier.m_type] = 0f; } equipmentMetrics.Resistances[damageModifier.m_type] += num; } if (!string.IsNullOrEmpty(item.m_shared.m_setName)) { if (!equipmentMetrics.SetCounts.ContainsKey(item.m_shared.m_setName)) { equipmentMetrics.SetCounts[item.m_shared.m_setName] = 0; } equipmentMetrics.SetCounts[item.m_shared.m_setName]++; } } return equipmentMetrics; } private static EquipmentMetrics? ComputePlayerMetrics() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return null; } List<ItemData> list = new List<ItemData>(); foreach (ItemData allItem in ((Humanoid)localPlayer).GetInventory().GetAllItems()) { if (allItem.m_equipped && allItem.IsEquipable()) { list.Add(allItem); } } if (list.Count <= 0) { return null; } return ComputeMetrics(list); } private static float ConvertModifier(DamageModifier mod) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected I4, but got Unknown return (mod - 1) switch { 0 => 50f, 4 => 75f, 2 => 100f, 1 => -50f, 5 => -100f, _ => 0f, }; } private static string FormatOutput(EquipmentMetrics figure, EquipmentMetrics? player) { //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("\n\n"); stringBuilder.Append($"\n<color=white>$item_armor: {figure.TotalArmor:F0}</color>"); WriteDelta(stringBuilder, figure.TotalArmor, player?.TotalArmor ?? 0f, higherIsBetter: true); stringBuilder.Append($"\n<color=white>$item_weight: {figure.TotalWeight:F1}</color>"); WriteDelta(stringBuilder, figure.TotalWeight, player?.TotalWeight ?? 0f, higherIsBetter: false); if (figure.MovementPenalty != 0f || (player?.MovementPenalty ?? 0f) != 0f) { float num = figure.MovementPenalty * 100f; stringBuilder.Append($"\n<color=white>$item_movement_modifier: {num:+0;-0}%</color>"); if (player != null) { WriteDelta(stringBuilder, num, player.MovementPenalty * 100f, higherIsBetter: true, "%"); } } if (figure.Resistances.Count > 0) { stringBuilder.Append("\n<color=#FFA500>Resistances:</color>"); foreach (KeyValuePair<DamageType, float> resistance in figure.Resistances) { string arg = ((resistance.Value >= 0f) ? "white" : "#FF4444"); stringBuilder.Append($" {resistance.Key}:<color={arg}>{resistance.Value:+0;-0}%</color>"); if (player != null && player.Resistances.TryGetValue(resistance.Key, out var value)) { float num2 = resistance.Value - value; if (num2 != 0f) { string arg2 = ((num2 > 0f) ? "#00FF00" : "#FF4444"); stringBuilder.Append($"<color={arg2}>({num2:+0;-0})</color>"); } } } } if (figure.SetCounts.Count <= 0) { return stringBuilder.ToString(); } stringBuilder.Append("\n<color=#BB88FF>Set:</color>"); foreach (KeyValuePair<string, int> setCount in figure.SetCounts) { stringBuilder.Append($" {Localization.instance.Localize(setCount.Key)} x{setCount.Value}"); } return stringBuilder.ToString(); } private static void WriteDelta(StringBuilder output, float figureVal, float playerVal, bool higherIsBetter, string suffix = "") { if (playerVal != 0f) { float num = figureVal - playerVal; if (num != 0f) { string text = ((higherIsBetter ? (num > 0f) : (num < 0f)) ? "#00FF00" : "#FF4444"); string format = ((suffix == "%") ? "{0:+0;-0}%" : ((suffix == "" && figureVal < 10f) ? "{0:+0.0;-0.0}" : "{0:+0;-0}")); output.Append(" <color=" + text + ">(" + string.Format(format, num) + ")</color>"); } } } } }