Decompiled source of BoarderArmorStandStats v1.1.0

BoarderArmorStandStats.dll

Decompiled 3 weeks ago
using 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>");
				}
			}
		}
	}
}