Decompiled source of StorageCapacityDisplay v1.0.2

BepInEx\plugins\StorageCapacityDisplay\StorageCapacityDisplay.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using SpaceCraft;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("StorageCapacityDisplay")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: AssemblyInformationalVersion("1.0.2+ecdd65ea4c865da89993b1bd0cff03ebb79cedaf")]
[assembly: AssemblyProduct("StorageCapacityDisplay")]
[assembly: AssemblyTitle("StorageCapacityDisplay")]
[assembly: AssemblyVersion("1.0.2.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 StorageCapacityDisplay
{
	public sealed class CapacityDisplay : MonoBehaviour
	{
		private Plugin plugin;

		private InventoryAssociatedProxy proxy;

		private InventoryAssociated associated;

		private InventoriesHandler handler;

		private Inventory inventory;

		private bool watching;

		private bool pending;

		private bool dirty = true;

		private bool disposed;

		private bool suspended = true;

		private int generation;

		private int lastUsed = -1;

		private int lastTotal = -1;

		private float nextSample;

		private float nextRequest;

		private float nextVisual;

		private string lastFormat;

		private GameObject visual;

		private TextMeshProUGUI text;

		private Transform visualTransform;

		private Plugin.TextMaterial material;

		private bool billboard;

		private TextMeshProUGUI nameplate;

		private Canvas nameplateCanvas;

		private readonly List<WorldObjectText> nameplateCandidates = new List<WorldObjectText>();

		private readonly Vector3[] corners = (Vector3[])(object)new Vector3[4];

		private float nextPlacement;

		internal void Initialize(Plugin owner, InventoryAssociatedProxy inventoryProxy, InventoryAssociated inventoryAssociated)
		{
			plugin = owner;
			proxy = inventoryProxy;
			associated = inventoryAssociated;
		}

		internal void Tick(Plugin.TickContext context, ref int requestBudget, ref int visualBudget)
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			if (disposed || (Object)(object)plugin == (Object)null)
			{
				return;
			}
			float now = context.Now;
			InventoriesHandler val = context.Handler;
			if (!context.Enabled || !((Component)this).gameObject.activeInHierarchy || (Object)(object)val == (Object)null || !((NetworkBehaviour)val).IsSpawned)
			{
				Suspend();
				return;
			}
			if ((Object)(object)handler != (Object)(object)val)
			{
				Suspend();
				handler = val;
			}
			suspended = false;
			if (inventory == null)
			{
				if (!pending && now >= nextRequest && requestBudget > 0)
				{
					requestBudget--;
					Resolve(now);
				}
				return;
			}
			if ((Object)(object)text == (Object)null && now >= nextVisual && visualBudget > 0)
			{
				visualBudget--;
				nextVisual = now + 2f;
				CreateVisual();
			}
			if ((Object)(object)text == (Object)null)
			{
				return;
			}
			if (now >= nextPlacement && visualBudget > 0)
			{
				visualBudget--;
				nextPlacement = now + 2f;
				RefreshPlacement();
			}
			int num;
			if (context.HasCamera)
			{
				if (context.NearbyOnly)
				{
					Vector3 val2 = context.CameraPosition - visualTransform.position;
					num = ((((Vector3)(ref val2)).sqrMagnitude <= context.DistanceSquared) ? 1 : 0);
				}
				else
				{
					num = 1;
				}
			}
			else
			{
				num = 0;
			}
			bool flag = (byte)num != 0;
			SetVisible(flag);
			if (flag && (dirty || now >= nextSample || lastFormat != context.Format))
			{
				dirty = false;
				nextSample = now + 1f;
				int num2 = CapacityText.UsedSlots(inventory);
				int size = inventory.GetSize();
				if (num2 != lastUsed || size != lastTotal || lastFormat != context.Format)
				{
					lastUsed = num2;
					lastTotal = size;
					lastFormat = context.Format;
					((TMP_Text)text).text = CapacityText.Format(lastFormat, num2, size);
				}
			}
		}

		private void Resolve(float now)
		{
			nextRequest = now + 2f;
			if ((Object)(object)proxy != (Object)null)
			{
				if (((NetworkBehaviour)proxy).IsSpawned)
				{
					pending = true;
					int epoch = generation;
					InventoriesHandler requestedHandler = handler;
					proxy.GetInventory((Action<Inventory, WorldObject>)delegate(Inventory value, WorldObject worldObject)
					{
						Resolved(value, epoch, requestedHandler);
					});
				}
			}
			else
			{
				if (!((Object)(object)associated != (Object)null) || associated.GetInventoryId() < 0)
				{
					return;
				}
				int inventoryId = associated.GetInventoryId();
				Inventory inventoryById = handler.GetInventoryById(inventoryId);
				if (((NetworkBehaviour)handler).IsServer)
				{
					Bind(inventoryById);
					return;
				}
				pending = true;
				int epoch2 = generation;
				InventoriesHandler requestedHandler2 = handler;
				handler.GetInventoryById(inventoryId, (Action<Inventory>)delegate(Inventory value)
				{
					Resolved(value, epoch2, requestedHandler2);
				}, (HashSet<Group>)null);
			}
		}

		private void Resolved(Inventory value, int epoch, InventoriesHandler requestedHandler)
		{
			if ((Object)(object)this == (Object)null || disposed || generation != epoch || (Object)(object)handler != (Object)(object)requestedHandler)
			{
				return;
			}
			pending = false;
			if ((Object)(object)plugin == (Object)null || !plugin.EnabledSetting.Value || !((Component)this).gameObject.activeInHierarchy)
			{
				return;
			}
			try
			{
				Bind(value);
			}
			catch (Exception ex)
			{
				Suspend();
				plugin.Warn(ex);
			}
		}

		private void Bind(Inventory value)
		{
			if (value != null && value != inventory)
			{
				Unbind();
				inventory = value;
				plugin.Register(inventory.GetId(), this);
				Inventory obj = inventory;
				obj.inventoryContentModified = (Action<WorldObject, bool>)Delegate.Combine(obj.inventoryContentModified, new Action<WorldObject, bool>(ContentChanged));
				dirty = true;
				lastUsed = (lastTotal = -1);
				if ((Object)(object)handler != (Object)null && ((NetworkBehaviour)handler).IsSpawned && !((NetworkBehaviour)handler).IsServer)
				{
					handler.BeginInventoryWatch(inventory);
					watching = true;
					handler.CheckInventoryWatchAndDirty(inventory);
				}
			}
		}

		internal void Snapshot(InventoriesHandler sourceHandler, Inventory value)
		{
			if (!((Object)(object)handler != (Object)(object)sourceHandler) && inventory != null && inventory.GetId() == value.GetId())
			{
				if (value != inventory)
				{
					Inventory obj = inventory;
					obj.inventoryContentModified = (Action<WorldObject, bool>)Delegate.Remove(obj.inventoryContentModified, new Action<WorldObject, bool>(ContentChanged));
					inventory = value;
					Inventory obj2 = inventory;
					obj2.inventoryContentModified = (Action<WorldObject, bool>)Delegate.Combine(obj2.inventoryContentModified, new Action<WorldObject, bool>(ContentChanged));
				}
				dirty = true;
			}
		}

		private void ContentChanged(WorldObject item, bool added)
		{
			dirty = true;
		}

		private void Unbind()
		{
			Inventory val = inventory;
			bool num = watching;
			inventory = null;
			watching = false;
			if (val != null)
			{
				val.inventoryContentModified = (Action<WorldObject, bool>)Delegate.Remove(val.inventoryContentModified, new Action<WorldObject, bool>(ContentChanged));
				if ((Object)(object)plugin != (Object)null)
				{
					plugin.Unregister(val.GetId(), this);
				}
			}
			if (num && val != null && (Object)(object)handler != (Object)null && ((NetworkBehaviour)handler).IsSpawned)
			{
				handler.StopInventoryWatch(val);
			}
		}

		internal void Suspend()
		{
			if (suspended)
			{
				return;
			}
			suspended = true;
			generation++;
			pending = false;
			SetVisible(visible: false);
			try
			{
				Unbind();
			}
			catch (Exception ex)
			{
				if ((Object)(object)plugin != (Object)null)
				{
					plugin.Warn(ex);
				}
			}
		}

		private void SetVisible(bool visible)
		{
			if ((Object)(object)visual == (Object)null || visual.activeSelf == visible)
			{
				return;
			}
			visual.SetActive(visible);
			if (billboard && !((Object)(object)plugin == (Object)null))
			{
				if (visible)
				{
					plugin.AddBillboard(visualTransform);
				}
				else
				{
					plugin.RemoveBillboard(visualTransform);
				}
			}
		}

		private void CreateVisual()
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Expected O, but got Unknown
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Expected O, but got Unknown
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)visual != (Object)null)
			{
				SetVisible(visible: false);
				Object.Destroy((Object)(object)visual);
			}
			ReleaseMaterial();
			visual = null;
			visualTransform = null;
			billboard = false;
			FindNameplate();
			TextMeshProUGUI val = nameplate;
			TMP_FontAsset val2 = (((Object)(object)val != (Object)null && (Object)(object)((TMP_Text)val).font != (Object)null) ? ((TMP_Text)val).font : TMP_Settings.defaultFontAsset);
			if (!((Object)(object)val2 == (Object)null))
			{
				visual = new GameObject("StorageCapacityDisplay.Counter", new Type[2]
				{
					typeof(RectTransform),
					typeof(Canvas)
				});
				visualTransform = visual.transform;
				visual.SetActive(false);
				visual.transform.SetParent(((Component)this).transform, false);
				Canvas component = visual.GetComponent<Canvas>();
				component.renderMode = (RenderMode)2;
				component.overrideSorting = false;
				RectTransform val3 = (RectTransform)visual.transform;
				val3.sizeDelta = new Vector2(600f, 100f);
				PositionVisual();
				nextPlacement = Time.unscaledTime + 2f;
				GameObject val4 = new GameObject("Capacity", new Type[2]
				{
					typeof(RectTransform),
					typeof(TextMeshProUGUI)
				});
				val4.transform.SetParent((Transform)(object)val3, false);
				RectTransform val5 = (RectTransform)val4.transform;
				val5.anchorMin = Vector2.zero;
				val5.anchorMax = Vector2.one;
				Vector2 offsetMin = (val5.offsetMax = Vector2.zero);
				val5.offsetMin = offsetMin;
				text = val4.GetComponent<TextMeshProUGUI>();
				((TMP_Text)text).font = val2;
				material = plugin.GetTextMaterial(val2);
				((TMP_Text)text).fontSharedMaterial = material.Value;
				((TMP_Text)text).fontSize = 64f;
				((TMP_Text)text).enableAutoSizing = true;
				((TMP_Text)text).fontSizeMin = 18f;
				((TMP_Text)text).fontSizeMax = 64f;
				((Graphic)text).color = new Color(0.87f, 0.96f, 1f, 1f);
				((TMP_Text)text).alignment = (TextAlignmentOptions)514;
				((TMP_Text)text).textWrappingMode = (TextWrappingModes)0;
				((TMP_Text)text).overflowMode = (TextOverflowModes)1;
				((TMP_Text)text).richText = false;
				((Graphic)text).raycastTarget = false;
				lastUsed = (lastTotal = -1);
				dirty = true;
			}
		}

		private void FindNameplate()
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Invalid comparison between Unknown and I4
			nameplate = null;
			nameplateCanvas = null;
			((Component)this).GetComponentsInChildren<WorldObjectText>(true, nameplateCandidates);
			foreach (WorldObjectText nameplateCandidate in nameplateCandidates)
			{
				if ((Object)(object)nameplateCandidate == (Object)null || (Object)(object)nameplateCandidate.textContainer == (Object)null)
				{
					continue;
				}
				Canvas componentInParent = ((Component)nameplateCandidate.textContainer).GetComponentInParent<Canvas>(true);
				if (!((Object)(object)componentInParent == (Object)null) && (int)componentInParent.renderMode == 2)
				{
					nameplate = nameplateCandidate.textContainer;
					nameplateCanvas = componentInParent;
					if (((Component)nameplate).gameObject.activeInHierarchy)
					{
						break;
					}
				}
			}
			nameplateCandidates.Clear();
		}

		private void RefreshPlacement()
		{
			if (billboard || (Object)(object)nameplate == (Object)null || (Object)(object)nameplateCanvas == (Object)null)
			{
				FindNameplate();
			}
			PositionVisual();
		}

		private void PositionVisual()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			float width = 0.6f;
			Vector3 position = default(Vector3);
			bool flag = false;
			if ((Object)(object)nameplate != (Object)null && (Object)(object)nameplateCanvas != (Object)null && (int)nameplateCanvas.renderMode == 2)
			{
				((TMP_Text)nameplate).rectTransform.GetWorldCorners(corners);
				flag = CounterPlacement.TryNameplate(corners[0], corners[3], ((TMP_Text)nameplate).transform.up, ((TMP_Text)nameplate).transform.forward, out position, out width);
			}
			bool flag2 = billboard;
			billboard = !flag;
			if (flag)
			{
				if (visualTransform.position != position)
				{
					visualTransform.position = position;
				}
				Quaternion rotation = ((TMP_Text)nameplate).transform.rotation;
				if (visualTransform.rotation != rotation)
				{
					visualTransform.rotation = rotation;
				}
			}
			else if (!flag2)
			{
				Bounds val = LocalBounds();
				visualTransform.localPosition = new Vector3(((Bounds)(ref val)).center.x, ((Bounds)(ref val)).max.y + 0.12f, ((Bounds)(ref val)).center.z);
			}
			if (flag2 != billboard)
			{
				if (billboard && visual.activeSelf)
				{
					plugin.AddBillboard(visualTransform);
				}
				else
				{
					plugin.RemoveBillboard(visualTransform);
				}
			}
			Vector3 lossyScale = ((Component)this).transform.lossyScale;
			float num = width / 600f;
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(num / Mathf.Max(Mathf.Abs(lossyScale.x), 0.0001f), num / Mathf.Max(Mathf.Abs(lossyScale.y), 0.0001f), num / Mathf.Max(Mathf.Abs(lossyScale.z), 0.0001f));
			if (visualTransform.localScale != val2)
			{
				visualTransform.localScale = val2;
			}
		}

		private Bounds LocalBounds()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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_004d: 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_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: 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)
			Bounds result = default(Bounds);
			((Bounds)(ref result))..ctor(Vector3.zero, new Vector3(0.6f, 0.8f, 0.6f));
			bool flag = false;
			Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if (!(val is MeshRenderer) && !(val is SkinnedMeshRenderer))
				{
					continue;
				}
				Bounds localBounds = val.localBounds;
				for (int j = 0; j < 8; j++)
				{
					Vector3 val2 = ((Bounds)(ref localBounds)).center + Vector3.Scale(((Bounds)(ref localBounds)).extents, new Vector3((float)(((j & 1) != 0) ? 1 : (-1)), (float)(((j & 2) != 0) ? 1 : (-1)), (float)(((j & 4) != 0) ? 1 : (-1))));
					Vector3 val3 = ((Component)this).transform.InverseTransformPoint(((Component)val).transform.TransformPoint(val2));
					if (!flag)
					{
						((Bounds)(ref result))..ctor(val3, Vector3.zero);
						flag = true;
					}
					else
					{
						((Bounds)(ref result)).Encapsulate(val3);
					}
				}
			}
			return result;
		}

		private void OnDisable()
		{
			try
			{
				Suspend();
			}
			catch (Exception ex)
			{
				if ((Object)(object)plugin != (Object)null)
				{
					plugin.Warn(ex);
				}
			}
		}

		private void OnDestroy()
		{
			Dispose();
		}

		private void ReleaseMaterial()
		{
			Plugin.TextMaterial textMaterial = material;
			material = null;
			if ((Object)(object)plugin != (Object)null)
			{
				plugin.ReleaseTextMaterial(textMaterial);
			}
		}

		internal void Dispose()
		{
			if (disposed)
			{
				return;
			}
			disposed = true;
			try
			{
				Suspend();
			}
			catch (Exception ex)
			{
				if ((Object)(object)plugin != (Object)null)
				{
					plugin.Warn(ex);
				}
			}
			if ((Object)(object)visual != (Object)null)
			{
				Object.Destroy((Object)(object)visual);
			}
			ReleaseMaterial();
		}
	}
	internal static class CapacityText
	{
		internal static int UsedSlots(Inventory inventory)
		{
			return inventory.GetInsideWorldObjects().Count;
		}

		internal static string Format(string format, int used, int total)
		{
			if (string.IsNullOrWhiteSpace(format))
			{
				format = "{used} / {total}";
			}
			return format.Replace("{used}", used.ToString(CultureInfo.InvariantCulture)).Replace("{total}", total.ToString(CultureInfo.InvariantCulture));
		}
	}
	internal static class CounterPlacement
	{
		internal static bool TryNameplate(Vector3 bottomLeft, Vector3 bottomRight, Vector3 up, Vector3 forward, out Vector3 position, out float width)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_0048: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			position = default(Vector3);
			width = 0.6f;
			float num = Vector3.Distance(bottomLeft, bottomRight);
			if (float.IsNaN(num) || float.IsInfinity(num) || num < 0.001f)
			{
				return false;
			}
			width = Mathf.Clamp(num, 0.3f, 1.2f);
			position = (bottomLeft + bottomRight) * 0.5f - up * (width / 12f + 0.015f) - forward * 0.012f;
			return true;
		}
	}
	[BepInPlugin("noxie.planetcrafter.storagecapacitydisplay", "Storage Capacity Display", "1.0.2")]
	[BepInProcess("Planet Crafter.exe")]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal struct TickContext
		{
			internal InventoriesHandler Handler;

			internal bool Enabled;

			internal bool HasCamera;

			internal bool NearbyOnly;

			internal Vector3 CameraPosition;

			internal float Now;

			internal float DistanceSquared;

			internal string Format;
		}

		internal sealed class TextMaterial
		{
			internal Material Source;

			internal Material Value;

			internal int References;
		}

		public const string Id = "noxie.planetcrafter.storagecapacitydisplay";

		internal static Plugin Instance;

		internal ConfigEntry<bool> EnabledSetting;

		internal ConfigEntry<bool> NearbyOnly;

		internal ConfigEntry<float> MaxDistance;

		internal ConfigEntry<string> Format;

		private Camera viewCamera;

		private readonly List<CapacityDisplay> displays = new List<CapacityDisplay>();

		private readonly SubscriberIndex<CapacityDisplay> subscriptions = new SubscriberIndex<CapacityDisplay>();

		private readonly List<Transform> billboards = new List<Transform>();

		private readonly Dictionary<Material, TextMaterial> materials = new Dictionary<Material, TextMaterial>();

		private Harmony harmony;

		private float nextWarning;

		private bool ready;

		private bool discover = true;

		private int tickCursor;

		private void Awake()
		{
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			Instance = this;
			EnabledSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Show storage capacity counters.");
			Format = ((BaseUnityPlugin)this).Config.Bind<string>("General", "DisplayFormat", "{used} / {total}", "Plain text. Tokens: {used}, {total}.");
			NearbyOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowOnlyWhenNearby", false, "Hide counters beyond MaxDisplayDistance. Otherwise use normal world rendering.");
			MaxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("General", "MaxDisplayDistance", 20f, new ConfigDescription("Display range in metres when ShowOnlyWhenNearby is enabled.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 200f), Array.Empty<object>()));
			harmony = new Harmony("noxie.planetcrafter.storagecapacitydisplay");
			try
			{
				harmony.PatchAll(typeof(Plugin).Assembly);
				ready = true;
				((MonoBehaviour)this).StartCoroutine(Tick());
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Storage Capacity Display 1.0.2 loaded. Container discovery and inventory snapshot Harmony patches applied; read-only counters enabled.");
			}
			catch (Exception ex)
			{
				harmony.UnpatchSelf();
				((BaseUnityPlugin)this).Logger.LogError((object)("Storage Capacity Display startup failed; its patches were removed. " + ex));
			}
		}

		internal void Discover(ActionOpenable action)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			if (!ready || (Object)(object)action == (Object)null || action.notAContainer)
			{
				return;
			}
			Scene scene = ((Component)action).gameObject.scene;
			if (!((Scene)(ref scene)).IsValid())
			{
				return;
			}
			try
			{
				InventoryAssociatedProxy componentInParent = ((Component)action).GetComponentInParent<InventoryAssociatedProxy>();
				InventoryAssociated componentInParent2 = ((Component)action).GetComponentInParent<InventoryAssociated>();
				Component val = (Component)(((Object)(object)componentInParent != (Object)null) ? ((object)componentInParent) : ((object)componentInParent2));
				if (!((Object)(object)val == (Object)null) && !((Object)(object)val.GetComponentInParent<PlayerMainController>() != (Object)null))
				{
					CapacityDisplay component = val.GetComponent<CapacityDisplay>();
					if (!((Object)(object)component != (Object)null))
					{
						component = val.gameObject.AddComponent<CapacityDisplay>();
						component.Initialize(this, componentInParent, componentInParent2);
						displays.Add(component);
					}
				}
			}
			catch (Exception ex)
			{
				Warn(ex);
			}
		}

		internal void Register(int id, CapacityDisplay display)
		{
			subscriptions.Add(id, display);
		}

		internal void Unregister(int id, CapacityDisplay display)
		{
			subscriptions.Remove(id, display);
		}

		internal void AddBillboard(Transform target)
		{
			if (!billboards.Contains(target))
			{
				billboards.Add(target);
			}
		}

		internal void RemoveBillboard(Transform target)
		{
			billboards.Remove(target);
		}

		internal TextMaterial GetTextMaterial(TMP_FontAsset font)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			Material material = ((TMP_Asset)font).material;
			if (materials.TryGetValue(material, out var value))
			{
				value.References++;
				return value;
			}
			Material val = new Material(material);
			val.SetFloat("unity_GUIZTestMode", 4f);
			if (val.HasProperty("_ZTestMode"))
			{
				val.SetFloat("_ZTestMode", 4f);
			}
			value = new TextMaterial
			{
				Source = material,
				Value = val,
				References = 1
			};
			materials.Add(material, value);
			return value;
		}

		internal void ReleaseTextMaterial(TextMaterial material)
		{
			if (material != null && --material.References == 0)
			{
				materials.Remove(material.Source);
				if ((Object)(object)material.Value != (Object)null)
				{
					Object.Destroy((Object)(object)material.Value);
				}
			}
		}

		internal void Snapshot(InventoriesHandler handler, Inventory inventory)
		{
			if (inventory == null || !subscriptions.TryGet(inventory.GetId(), out var subscribers))
			{
				return;
			}
			foreach (CapacityDisplay item in subscribers)
			{
				if ((Object)(object)item != (Object)null)
				{
					item.Snapshot(handler, inventory);
				}
			}
		}

		private IEnumerator Tick()
		{
			WaitForSecondsRealtime delay = new WaitForSecondsRealtime(0.2f);
			while (ready)
			{
				if (discover)
				{
					discover = false;
					ActionOpenable[] array = Object.FindObjectsByType<ActionOpenable>((FindObjectsInactive)1, (FindObjectsSortMode)0);
					foreach (ActionOpenable action in array)
					{
						Discover(action);
					}
				}
				viewCamera = Camera.main;
				bool flag = (Object)(object)viewCamera != (Object)null;
				float value = MaxDistance.Value;
				TickContext context = new TickContext
				{
					Handler = InventoriesHandler.Instance,
					Enabled = EnabledSetting.Value,
					HasCamera = flag,
					NearbyOnly = NearbyOnly.Value,
					CameraPosition = (flag ? ((Component)viewCamera).transform.position : Vector3.zero),
					Now = Time.unscaledTime,
					DistanceSquared = value * value,
					Format = Format.Value
				};
				int requestBudget = 4;
				int visualBudget = 4;
				for (int num = displays.Count - 1; num >= 0; num--)
				{
					if ((Object)(object)displays[num] == (Object)null)
					{
						displays.RemoveAt(num);
					}
				}
				int count = displays.Count;
				for (int j = 0; j < count; j++)
				{
					CapacityDisplay capacityDisplay = displays[(tickCursor + j) % count];
					try
					{
						capacityDisplay.Tick(context, ref requestBudget, ref visualBudget);
					}
					catch (Exception ex)
					{
						capacityDisplay.Suspend();
						Warn(ex);
					}
				}
				tickCursor = ((count != 0) ? ((tickCursor + 4) % count) : 0);
				yield return delay;
			}
		}

		private void LateUpdate()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (!ready || billboards.Count == 0 || (Object)(object)viewCamera == (Object)null)
			{
				return;
			}
			Quaternion rotation = ((Component)viewCamera).transform.rotation;
			for (int num = billboards.Count - 1; num >= 0; num--)
			{
				Transform val = billboards[num];
				if ((Object)(object)val == (Object)null)
				{
					billboards.RemoveAt(num);
				}
				else if (val.rotation != rotation)
				{
					val.rotation = rotation;
				}
			}
		}

		internal void Warn(Exception ex)
		{
			if (!(Time.unscaledTime < nextWarning))
			{
				nextWarning = Time.unscaledTime + 30f;
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not refresh a storage counter; container gameplay is unaffected. " + ex));
			}
		}

		private void OnDestroy()
		{
			ready = false;
			((MonoBehaviour)this).StopAllCoroutines();
			foreach (CapacityDisplay display in displays)
			{
				if ((Object)(object)display != (Object)null)
				{
					display.Dispose();
					Object.Destroy((Object)(object)display);
				}
			}
			displays.Clear();
			subscriptions.Clear();
			billboards.Clear();
			foreach (TextMaterial value in materials.Values)
			{
				if ((Object)(object)value.Value != (Object)null)
				{
					Object.Destroy((Object)(object)value.Value);
				}
			}
			materials.Clear();
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}
	}
	[HarmonyPatch(typeof(ActionOpenable), "Start")]
	internal static class ContainerStarted
	{
		private static void Postfix(ActionOpenable __instance)
		{
			Plugin.Instance?.Discover(__instance);
		}
	}
	[HarmonyPatch(typeof(InventoriesHandler), "UpdateOrCreateInventoryFromMessage")]
	internal static class InventorySnapshot
	{
		private static void Postfix(InventoriesHandler __instance, Inventory __result)
		{
			try
			{
				Plugin.Instance?.Snapshot(__instance, __result);
			}
			catch (Exception ex)
			{
				Plugin.Instance?.Warn(ex);
			}
		}
	}
	internal sealed class SubscriberIndex<T> where T : class
	{
		private readonly Dictionary<int, HashSet<T>> entries = new Dictionary<int, HashSet<T>>();

		internal int Count => entries.Count;

		internal void Add(int id, T subscriber)
		{
			if (!entries.TryGetValue(id, out var value))
			{
				value = new HashSet<T>();
				entries.Add(id, value);
			}
			value.Add(subscriber);
		}

		internal void Remove(int id, T subscriber)
		{
			if (entries.TryGetValue(id, out var value))
			{
				value.Remove(subscriber);
				if (value.Count == 0)
				{
					entries.Remove(id);
				}
			}
		}

		internal bool TryGet(int id, out HashSet<T> subscribers)
		{
			return entries.TryGetValue(id, out subscribers);
		}

		internal void Clear()
		{
			entries.Clear();
		}
	}
}