Decompiled source of OttoLens v1.0.4

OttoLens.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
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 System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using OttoLens.Model;
using OttoLens.Readers;
using OttoLens.UI;
using TMPro;
using UnityEngine;
using UnityEngine.PostProcessing;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("OttoLens")]
[assembly: AssemblyCompany("potto007")]
[assembly: AssemblyProduct("OttoLens")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Paul Otto")]
[assembly: ComVisible(false)]
[assembly: Guid("7c1d5a2e-3b64-4f1a-9e0c-6a2f5d8b1c47")]
[assembly: AssemblyFileVersion("1.0.4")]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.4.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 OttoLens
{
	internal static class LensDirt
	{
		private static readonly Dictionary<PostProcessingProfile, float> Originals = new Dictionary<PostProcessingProfile, float>();

		internal static void Apply(CameraEffects? effects)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)effects == (Object)null)
			{
				return;
			}
			PostProcessingBehaviour component = ((Component)effects).GetComponent<PostProcessingBehaviour>();
			PostProcessingProfile val = (((Object)(object)component != (Object)null) ? component.profile : null);
			if (!((Object)(object)val == (Object)null) && val.bloom != null)
			{
				if (!Originals.TryGetValue(val, out var value))
				{
					value = val.bloom.settings.lensDirt.intensity;
					Originals[val] = value;
				}
				Write(val, OttoLensPlugin.RemoveLensDirt.Value ? 0f : value);
			}
		}

		internal static void ApplyCurrent()
		{
			Apply(CameraEffects.instance);
		}

		internal static void Restore()
		{
			foreach (KeyValuePair<PostProcessingProfile, float> original in Originals)
			{
				if ((Object)(object)original.Key != (Object)null && original.Key.bloom != null)
				{
					Write(original.Key, original.Value);
				}
			}
			Originals.Clear();
		}

		private static void Write(PostProcessingProfile profile, float intensity)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			Settings settings = profile.bloom.settings;
			if (!Mathf.Approximately(settings.lensDirt.intensity, intensity))
			{
				settings.lensDirt.intensity = intensity;
				profile.bloom.settings = settings;
			}
		}
	}
	[HarmonyPatch]
	internal static class Patches
	{
		private static readonly HashSet<Type> LoggedExceptions = new HashSet<Type>();

		private static GameObject? _lastHover;

		private static bool _lastPlaceMode;

		private static ILensReader? _lastReader;

		private static Component? _lastTarget;

		private static ILensReader? _wearNTearReader;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Hud), "UpdateCrosshair", new Type[]
		{
			typeof(Player),
			typeof(float)
		})]
		private static void UpdateCrosshairPostfix(Hud __instance, Player player)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				KeyCode value = OttoLensPlugin.ToggleKey.Value;
				if ((int)value != 0 && !KeyboardIsBusy() && Input.GetKeyDown(value))
				{
					OttoLensPlugin.Enabled.Value = !OttoLensPlugin.Enabled.Value;
				}
				Frame(__instance, player);
			}
			catch (Exception ex)
			{
				LogOnce(ex);
				LensPanel.Instance?.SetTarget(null, null, null);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Hud), "OnDestroy")]
		private static void HudDestroyPostfix()
		{
			try
			{
				Teardown();
			}
			catch (Exception ex)
			{
				LogOnce(ex);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(CameraEffects), "Awake")]
		private static void CameraEffectsAwakePostfix(CameraEffects __instance)
		{
			try
			{
				LensDirt.Apply(__instance);
			}
			catch (Exception ex)
			{
				LogOnce(ex);
			}
		}

		internal static void Teardown()
		{
			_lastHover = null;
			_lastPlaceMode = false;
			_lastReader = null;
			_lastTarget = null;
			LensPanel.Release();
			LensFormat.ClearCaches();
			LensReaders.ClearLocalizedCaches();
			ContainerReader.ClearOpenedChests();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(InventoryGui), "Show")]
		private static void InventoryGuiShowPostfix(Container container)
		{
			try
			{
				if (!((Object)(object)container == (Object)null))
				{
					Piece piece = container.m_piece;
					if (!((Object)(object)piece != (Object)null) || !piece.IsPlacedByPlayer())
					{
						ContainerReader.RecordOpen(container);
					}
				}
			}
			catch (Exception ex)
			{
				LogOnce(ex);
			}
		}

		internal static void InvalidateHoverCache()
		{
			_lastHover = null;
			_lastReader = null;
			_lastTarget = null;
		}

		private static void Frame(Hud hud, Player player)
		{
			if (!OttoLensPlugin.Enabled.Value || (Object)(object)player == (Object)null || !hud.IsVisible() || InventoryGui.IsVisible())
			{
				LensPanel.Instance?.SetTarget(null, null, null);
				return;
			}
			bool flag = ((Character)player).InPlaceMode();
			GameObject val = ResolveHoverObject(player, flag);
			if ((Object)(object)val == (Object)null)
			{
				_lastHover = null;
				_lastPlaceMode = flag;
				_lastReader = null;
				_lastTarget = null;
				LensPanel.Instance?.SetTarget(null, null, null);
				return;
			}
			if ((Object)(object)val != (Object)(object)_lastHover || flag != _lastPlaceMode)
			{
				_lastHover = val;
				_lastPlaceMode = flag;
				ILensReader reader;
				Component target;
				if (flag)
				{
					Piece hoveringPiece = player.GetHoveringPiece();
					WearNTear val2 = ((hoveringPiece != null) ? ((Component)hoveringPiece).GetComponentInParent<WearNTear>() : null);
					if (_wearNTearReader == null)
					{
						_wearNTearReader = LensReaders.All.FirstOrDefault((ILensReader r) => r is WearNTearReader);
					}
					if ((Object)(object)val2 != (Object)null && _wearNTearReader != null)
					{
						_lastReader = _wearNTearReader;
						_lastTarget = (Component?)(object)val2;
					}
					else
					{
						_lastReader = null;
						_lastTarget = null;
					}
				}
				else if (LensReaders.Resolve(val, out reader, out target))
				{
					_lastReader = reader;
					_lastTarget = target;
				}
				else
				{
					_lastReader = null;
					_lastTarget = null;
				}
			}
			if (_lastReader != null && !_lastReader.Enabled)
			{
				_lastHover = null;
				_lastPlaceMode = flag;
				_lastReader = null;
				_lastTarget = null;
				LensPanel.Instance?.SetTarget(null, null, null);
			}
			else if (_lastReader == null || (Object)(object)_lastTarget == (Object)null)
			{
				LensPanel.Instance?.SetTarget(null, null, null);
			}
			else if (!LensReaders.PassesCoreGuards(_lastTarget, ((TMP_Text)hud.m_hoverName).text))
			{
				LensPanel.Instance?.SetTarget(null, null, null);
			}
			else
			{
				LensPanel.Get()?.SetTarget(_lastTarget, _lastReader, val);
			}
		}

		private static GameObject? ResolveHoverObject(Player player, bool placeMode)
		{
			GameObject hoverObject = ((Humanoid)player).GetHoverObject();
			if ((Object)(object)hoverObject != (Object)null)
			{
				return hoverObject;
			}
			if (!placeMode || OttoLensPlugin.ShowBuildPieces.Value == OttoLensPlugin.PieceStatus.Off)
			{
				return null;
			}
			Piece hoveringPiece = player.GetHoveringPiece();
			if (!((Object)(object)hoveringPiece != (Object)null))
			{
				return null;
			}
			return ((Component)hoveringPiece).gameObject;
		}

		private static bool KeyboardIsBusy()
		{
			if (!Console.IsVisible() && !TextInput.IsVisible() && !Minimap.InTextInput() && !Menu.IsVisible() && (!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()))
			{
				if ((Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_buildUi != (Object)null)
				{
					return Hud.instance.m_buildUi.SearchFieldFocused;
				}
				return false;
			}
			return true;
		}

		private static void LogOnce(Exception ex)
		{
			if (LoggedExceptions.Add(ex.GetType()))
			{
				OttoLensPlugin.Log.LogWarning((object)("OttoLens hook failed with " + ex.GetType().Name + ": " + ex.Message + ". Further " + ex.GetType().Name + " reports are suppressed.\n" + ex.StackTrace));
			}
		}
	}
	[BepInPlugin("potto007.OttoLens", "OttoLens", "1.0.4")]
	public class OttoLensPlugin : BaseUnityPlugin
	{
		public enum SortRows
		{
			Count,
			Slot
		}

		public enum PieceStatus
		{
			Off,
			WithHammer,
			Always
		}

		internal const string ModName = "OttoLens";

		internal const string ModVersion = "1.0.4";

		internal const string Author = "potto007";

		internal const string ModGUID = "potto007.OttoLens";

		internal static readonly ManualLogSource Log = Logger.CreateLogSource("OttoLens");

		private readonly Harmony _harmony = new Harmony("potto007.OttoLens");

		private const string PanelSection = "OttoLens";

		private const string CameraSection = "Camera";

		private const string TargetSection = "Targets";

		private static readonly Version UnboundToggleKeySince = new Version(1, 0, 2);

		private static readonly Regex SavedByHeader = new Regex("^## Settings file was created by plugin .+ v(\\d+(?:\\.\\d+){1,3})");

		internal static ConfigEntry<bool> Enabled = null;

		internal static ConfigEntry<KeyCode> ToggleKey = null;

		internal static ConfigEntry<int> OffsetX = null;

		internal static ConfigEntry<int> OffsetY = null;

		internal static ConfigEntry<int> PanelWidth = null;

		internal static ConfigEntry<float> GuiScale = null;

		internal static ConfigEntry<int> MaxRows = null;

		internal static ConfigEntry<SortRows> SortRowsBy = null;

		internal static ConfigEntry<bool> ShowNames = null;

		internal static ConfigEntry<bool> ShowFullHealth = null;

		internal static ConfigEntry<bool> AvoidHoverText = null;

		internal static ConfigEntry<int> RefreshHz = null;

		internal static ConfigEntry<float> FadeSeconds = null;

		internal static ConfigEntry<float> BackdropAlpha = null;

		internal static ConfigEntry<bool> ShowDays = null;

		internal static ConfigEntry<bool> RemoveLensDirt = null;

		internal static ConfigEntry<bool> HideUnopenedWorldChests = null;

		internal static ConfigEntry<bool> ShowContainers = null;

		internal static ConfigEntry<bool> ShowFires = null;

		internal static ConfigEntry<bool> ShowSmelters = null;

		internal static ConfigEntry<bool> ShowCooking = null;

		internal static ConfigEntry<bool> ShowFermenting = null;

		internal static ConfigEntry<bool> ShowPlants = null;

		internal static ConfigEntry<bool> ShowPickables = null;

		internal static ConfigEntry<bool> ShowRespawn = null;

		internal static ConfigEntry<PieceStatus> ShowBuildPieces = null;

		internal static ConfigEntry<bool> ShowMineables = null;

		internal static ConfigEntry<bool> ShowTreesAndRocks = null;

		internal static ConfigEntry<bool> ShowStands = null;

		internal static ConfigEntry<bool> ShowCreatures = null;

		internal static ConfigEntry<bool> ShowMisc = null;

		public void Awake()
		{
			BindConfig();
			_harmony.PatchAll(typeof(OttoLensPlugin).Assembly);
			LensDirt.ApplyCurrent();
			Log.LogInfo((object)"OttoLens 1.0.4 loaded.");
		}

		private Version? ReadSavedByVersion()
		{
			try
			{
				if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
				{
					return null;
				}
				using StreamReader streamReader = new StreamReader(((BaseUnityPlugin)this).Config.ConfigFilePath);
				Match match = SavedByHeader.Match(streamReader.ReadLine() ?? string.Empty);
				return match.Success ? new Version(match.Groups[1].Value) : null;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not read the version header of " + Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath) + ": " + ex.Message));
				return null;
			}
		}

		private static void MigrateToggleKey(Version? savedBy)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Invalid comparison between Unknown and I4
			if (!(savedBy == null) && !(savedBy >= UnboundToggleKeySince) && (int)ToggleKey.Value == 104)
			{
				ToggleKey.Value = (KeyCode)0;
				Log.LogInfo((object)$"ToggleKey changed from H to None (unbound), the default since v{UnboundToggleKeySince.ToString(3)}. The config was last saved by v{savedBy}.");
			}
		}

		private void BindConfig()
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Expected O, but got Unknown
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Expected O, but got Unknown
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Expected O, but got Unknown
			//IL_0245: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Expected O, but got Unknown
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_028c: Expected O, but got Unknown
			Version version = ReadSavedByVersion();
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("OttoLens", "Enabled", true, "Master switch. Off hides the panel and skips every reader.");
			ToggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("OttoLens", "ToggleKey", (KeyCode)0, "Key that flips the master toggle at runtime. None (default) leaves it unbound.");
			OffsetX = ((BaseUnityPlugin)this).Config.Bind<int>("OttoLens", "OffsetX", 150, new ConfigDescription("Left edge of the panel, pixels right of screen centre.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(96, 600), Array.Empty<object>()));
			OffsetY = ((BaseUnityPlugin)this).Config.Bind<int>("OttoLens", "OffsetY", -32, new ConfigDescription("Top edge of the panel, pixels below screen centre (negative).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-400, -16), Array.Empty<object>()));
			PanelWidth = ((BaseUnityPlugin)this).Config.Bind<int>("OttoLens", "PanelWidth", 300, new ConfigDescription("Fixed panel width in pixels.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(240, 420), Array.Empty<object>()));
			GuiScale = ((BaseUnityPlugin)this).Config.Bind<float>("OttoLens", "GuiScale", 1f, new ConfigDescription("Scale of the whole panel. 1.15 is a good value at 1440p.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.75f, 1.6f), Array.Empty<object>()));
			MaxRows = ((BaseUnityPlugin)this).Config.Bind<int>("OttoLens", "MaxRows", 10, new ConfigDescription("Item rows per block before 'and N more'. Also clamped to what fits on screen.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 16), Array.Empty<object>()));
			SortRowsBy = ((BaseUnityPlugin)this).Config.Bind<SortRows>("OttoLens", "SortRows", SortRows.Count, "Row order: Count sorts by count descending then name; Slot keeps the container's own order.");
			ShowNames = ((BaseUnityPlugin)this).Config.Bind<bool>("OttoLens", "ShowNames", true, "Show the item name column. Off shows icon and count only.");
			ShowFullHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("OttoLens", "ShowFullHealth", false, "Show the Health row on a build piece at 100 percent.");
			AvoidHoverText = ((BaseUnityPlugin)this).Config.Bind<bool>("OttoLens", "AvoidHoverText", true, "Push the panel right when the vanilla hover line is wide enough to run under it.");
			RefreshHz = ((BaseUnityPlugin)this).Config.Bind<int>("OttoLens", "RefreshHz", 4, new ConfigDescription("Value refresh rate while the panel is visible.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10), Array.Empty<object>()));
			FadeSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("OttoLens", "FadeSeconds", 0.08f, new ConfigDescription("Show and hide fade, in seconds.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>()));
			BackdropAlpha = ((BaseUnityPlugin)this).Config.Bind<float>("OttoLens", "BackdropAlpha", 0.88f, new ConfigDescription("Opacity of the panel plate.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>()));
			ShowDays = ((BaseUnityPlugin)this).Config.Bind<bool>("OttoLens", "ShowDays", true, "Add a game day figure to fuel and grow times that run longer than half a day.");
			RemoveLensDirt = ((BaseUnityPlugin)this).Config.Bind<bool>("Camera", "RemoveLensDirt", true, "The Eye of Odin makes all things clear. Removes the smudges that bloom draws over bright light.");
			HideUnopenedWorldChests = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "HideUnopenedWorldChests", true, "Hide the contents of world-placed chests until the player has opened them once.");
			ShowContainers = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Containers", true, "Chests, carts and ship holds.");
			ShowFires = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Fires", true, "Fireplaces, hearths, torches and braziers.");
			ShowSmelters = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Smelters", true, "Smelters, kilns, blast furnaces and windmill fed smelters.");
			ShowCooking = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Cooking", true, "Cooking stations and ovens.");
			ShowFermenting = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Fermenting", true, "Fermenters, beehives and sap collectors.");
			ShowPlants = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Plants", true, "Planted crops and saplings.");
			ShowPickables = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Pickables", true, "Pickable plants and item piles.");
			ShowRespawn = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "ShowRespawn", false, "Show the respawn countdown on an already picked pickable. Needs Pickables on.");
			ShowBuildPieces = ((BaseUnityPlugin)this).Config.Bind<PieceStatus>("Targets", "BuildPieces", PieceStatus.WithHammer, "Build piece health and support: Off, WithHammer (place mode only) or Always (any hovered piece).");
			ShowMineables = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Mineables", true, "Mineable rocks (MineRock and MineRock5). On by default; rocks are Hoverable and cost no extra raycast.");
			ShowTreesAndRocks = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "TreesAndRocks", false, "Trees, stumps and logs. Off by default: they are read from the vanilla hover, so this costs nothing while it is off and no extra raycast while it is on.");
			ShowStands = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Stands", true, "Item stands and armor stands.");
			ShowCreatures = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Creatures", true, "Tamed creatures and pets.");
			ShowMisc = ((BaseUnityPlugin)this).Config.Bind<bool>("Targets", "Misc", true, "Tombstones, wisp spawners, shield generators, feasts, ground items and crafting stations.");
			MigrateToggleKey(version);
			if (version != null && version < new Version("1.0.4"))
			{
				((BaseUnityPlugin)this).Config.Save();
			}
			MaxRows.SettingChanged += OnRebuildSettingChanged;
			ShowNames.SettingChanged += OnRebuildSettingChanged;
			SortRowsBy.SettingChanged += OnRebuildSettingChanged;
			Enabled.SettingChanged += OnEnabledChanged;
			RemoveLensDirt.SettingChanged += OnLensDirtChanged;
			((BaseUnityPlugin)this).Config.SettingChanged += OnTargetSettingChanged;
		}

		private static void OnRebuildSettingChanged(object sender, EventArgs e)
		{
			LensPanel.RequestRebuild();
		}

		private static void OnLensDirtChanged(object sender, EventArgs e)
		{
			LensDirt.ApplyCurrent();
		}

		private static void OnTargetSettingChanged(object sender, SettingChangedEventArgs e)
		{
			if (e.ChangedSetting.Definition.Section == "Targets")
			{
				Patches.InvalidateHoverCache();
			}
		}

		private static void OnEnabledChanged(object sender, EventArgs e)
		{
			if (!Enabled.Value)
			{
				LensPanel.Destroy();
			}
		}

		private void OnDestroy()
		{
			try
			{
				Patches.Teardown();
				LensFormat.UnhookLanguage();
				LensReaders.UnhookLanguage();
				LensDirt.Restore();
			}
			catch (Exception arg)
			{
				Log.LogWarning((object)$"Teardown failed: {arg}");
			}
			_harmony.UnpatchSelf();
		}
	}
	public static class LensFormat
	{
		private static readonly Dictionary<string, string> NameCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly CultureInfo Culture = CultureInfo.InvariantCulture;

		private static bool _languageHooked;

		public static string Time(float seconds)
		{
			if (seconds <= 0f || float.IsNaN(seconds))
			{
				return "Ready";
			}
			int num = Mathf.CeilToInt(seconds);
			if (num < 600)
			{
				return string.Format(Culture, "{0}:{1:00}", num / 60, num % 60);
			}
			if (num < 3600)
			{
				return string.Format(Culture, "{0}m {1}s", num / 60, num % 60);
			}
			return string.Format(Culture, "{0}h {1:00}m", num / 3600, num % 3600 / 60);
		}

		public static string TimeWithDays(float seconds)
		{
			string text = Time(seconds);
			if (seconds <= 0f || !OttoLensPlugin.ShowDays.Value)
			{
				return text;
			}
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null || instance.m_dayLengthSec <= 0)
			{
				return text;
			}
			float num = seconds / (float)instance.m_dayLengthSec;
			if (num < 0.5f)
			{
				return text;
			}
			return string.Format(Culture, "{0} ({1:0.0} days)", text, num);
		}

		public static string Count(int current, int max)
		{
			if (max <= 0)
			{
				return current.ToString(Culture);
			}
			return string.Format(Culture, "{0}/{1}", current, max);
		}

		public static string Count(float current, int max)
		{
			return Count(Mathf.CeilToInt(current), max);
		}

		public static string Percent(float fraction)
		{
			return string.Format(Culture, "{0}%", Mathf.RoundToInt(Mathf.Clamp01(fraction) * 100f));
		}

		public static string Free(int freeSlots)
		{
			return string.Format(Culture, "Free {0}", freeSlots);
		}

		public static LensColor Ramp(float fraction)
		{
			if (!(fraction > 0.5f))
			{
				if (!(fraction > 0.15f))
				{
					return LensColor.Bad;
				}
				return LensColor.Warn;
			}
			return LensColor.Good;
		}

		public static LensColor FillRamp(float fraction)
		{
			if (!(fraction >= 1f))
			{
				if (!(fraction >= 0.9f))
				{
					return LensColor.Dim;
				}
				return LensColor.Warn;
			}
			return LensColor.Bad;
		}

		public static string Name(string? token)
		{
			if (string.IsNullOrEmpty(token))
			{
				return "";
			}
			if (!_languageHooked)
			{
				_languageHooked = true;
				Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(ClearCaches));
			}
			if (NameCache.TryGetValue(token, out string value))
			{
				return value;
			}
			string text;
			try
			{
				Localization instance = Localization.instance;
				text = ((instance != null) ? instance.Localize(token) : StripToken(token));
				if (text.Length == 0 || text.Contains("$") || text.Contains("MISSING KEY"))
				{
					text = StripToken(token);
				}
			}
			catch (Exception)
			{
				text = StripToken(token);
			}
			NameCache[token] = text;
			return text;
		}

		public static string Upper(string text)
		{
			return text.ToUpperInvariant();
		}

		internal static void ClearCaches()
		{
			NameCache.Clear();
		}

		internal static void UnhookLanguage()
		{
			if (_languageHooked)
			{
				Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(ClearCaches));
				_languageHooked = false;
			}
		}

		private static string StripToken(string token)
		{
			return token.Replace("$", "").Replace('_', ' ');
		}
	}
}
namespace OttoLens.UI
{
	internal sealed class LensPanel : MonoBehaviour
	{
		private sealed class TextSlot
		{
			public readonly TextMeshProUGUI Tmp;

			public readonly GameObject Go;

			private string _last = "";

			private Color _lastColor;

			public TextSlot(TextMeshProUGUI tmp, Color color)
			{
				//IL_0025: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				Tmp = tmp;
				Go = ((Component)tmp).gameObject;
				_lastColor = color;
			}

			public void SetText(string text)
			{
				if (!string.Equals(text, _last, StringComparison.Ordinal))
				{
					_last = text;
					((TMP_Text)Tmp).text = text;
				}
			}

			public void SetColor(Color color)
			{
				//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_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_001c: Unknown result type (might be due to invalid IL or missing references)
				if (!(color == _lastColor))
				{
					_lastColor = color;
					((Graphic)Tmp).color = color;
				}
			}
		}

		private sealed class StatusRow
		{
			private readonly GameObject _go;

			private readonly TextSlot _label;

			private readonly GameObject _meter;

			private readonly RectTransform _fill;

			private readonly Image _fillImage;

			private readonly TextSlot _value;

			private float _lastFill = -1f;

			private Color _lastFillColor;

			public StatusRow(Transform parent, TMP_FontAsset font, bool primary)
			{
				//IL_0066: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e9: 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_0115: Unknown result type (might be due to invalid IL or missing references)
				//IL_012f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0149: Unknown result type (might be due to invalid IL or missing references)
				//IL_0163: Unknown result type (might be due to invalid IL or missing references)
				//IL_017d: 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_019f: Unknown result type (might be due to invalid IL or missing references)
				//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
				//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
				RectTransform val = NewRect(primary ? "StatusRow_0" : "StatusRow_n", parent);
				_go = ((Component)val).gameObject;
				SetPreferredHeight(val, primary ? 22f : 18f);
				AddRowLayout(val, 6f);
				_label = NewText("Label", (Transform)(object)val, font, 15f, (TextAlignmentOptions)4097, Palette.Body, ellipsis: true);
				LayoutElement obj = _label.Go.AddComponent<LayoutElement>();
				obj.minWidth = 64f;
				obj.preferredWidth = 64f;
				RectTransform val2 = NewRect("Meter", (Transform)(object)val);
				_meter = ((Component)val2).gameObject;
				LayoutElement obj2 = ((Component)val2).gameObject.AddComponent<LayoutElement>();
				obj2.preferredWidth = 120f;
				obj2.preferredHeight = (primary ? 10f : 6f);
				AddImage(val2, null, primary ? Palette.MeterTrack : Palette.RuleDim);
				_fill = NewRect("Fill", (Transform)(object)val2);
				_fill.anchorMin = new Vector2(0f, 0f);
				_fill.anchorMax = new Vector2(0f, 1f);
				_fill.pivot = new Vector2(0f, 0.5f);
				_fill.anchoredPosition = new Vector2(1f, 0f);
				_fill.sizeDelta = new Vector2(0f, -2f);
				_fillImage = AddImage(_fill, null, Palette.MeterGold);
				_lastFillColor = Palette.MeterGold;
				_value = NewText("Value", (Transform)(object)val, font, 15f, (TextAlignmentOptions)4100, Palette.Count, ellipsis: false);
				LayoutElement obj3 = _value.Go.AddComponent<LayoutElement>();
				obj3.minWidth = 0f;
				obj3.flexibleWidth = 1f;
				_go.SetActive(false);
			}

			public bool Apply(LensMeter? meter, bool primary)
			{
				if (!meter.HasValue)
				{
					_go.SetActive(false);
					return false;
				}
				LensMeter value = meter.Value;
				_label.SetText(value.Label);
				_meter.SetActive(value.Fraction.HasValue);
				_go.SetActive(true);
				TickValues(meter, primary);
				return true;
			}

			public void TickValues(LensMeter? meter, bool primary)
			{
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: Unknown result type (might be due to invalid IL or missing references)
				//IL_0099: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a7: 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)
				if (!meter.HasValue)
				{
					return;
				}
				LensMeter value = meter.Value;
				_value.SetText(value.ValueText);
				Color val = Palette.Get(value.Color);
				_value.SetColor(primary ? val : Palette.Count);
				if (value.Fraction.HasValue)
				{
					float num = Mathf.Clamp01(value.Fraction.Value) * 118f;
					if (!Mathf.Approximately(num, _lastFill))
					{
						_lastFill = num;
						_fill.sizeDelta = new Vector2(num, -2f);
					}
					if (val != _lastFillColor)
					{
						_lastFillColor = val;
						((Graphic)_fillImage).color = val;
					}
				}
			}
		}

		private sealed class ItemRow
		{
			public readonly GameObject Go;

			private readonly Image _art;

			private readonly TextSlot _name;

			private readonly TextSlot _count;

			private readonly LayoutElement _countLe;

			private Sprite? _lastSprite;

			private bool _artEnabled;

			public ItemRow(Transform parent, TMP_FontAsset font, Sprite? tile)
			{
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				//IL_007b: 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)
				//IL_009b: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
				//IL_0128: Unknown result type (might be due to invalid IL or missing references)
				RectTransform val = NewRect("ItemRow", parent);
				Go = ((Component)val).gameObject;
				SetPreferredHeight(val, 32f);
				AddRowLayout(val, 8f);
				RectTransform val2 = NewRect("Icon", (Transform)(object)val);
				LayoutElement obj = ((Component)val2).gameObject.AddComponent<LayoutElement>();
				obj.preferredWidth = 30f;
				obj.preferredHeight = 30f;
				AddImage(val2, tile, Palette.Slot);
				RectTransform val3 = NewRect("Art", (Transform)(object)val2);
				val3.anchorMin = Vector2.zero;
				val3.anchorMax = Vector2.one;
				val3.offsetMin = new Vector2(1f, 1f);
				val3.offsetMax = new Vector2(-1f, -1f);
				_art = AddImage(val3, null, Color.white);
				_art.preserveAspect = true;
				((Behaviour)_art).enabled = false;
				_name = NewText("Name", (Transform)(object)val, font, 15f, (TextAlignmentOptions)4097, Palette.Body, ellipsis: true);
				SetFlexibleName(_name.Go);
				_count = NewText("Count", (Transform)(object)val, font, 15f, (TextAlignmentOptions)4100, Palette.Count, ellipsis: false);
				_countLe = _count.Go.AddComponent<LayoutElement>();
				_countLe.minWidth = 48f;
				_countLe.preferredWidth = 48f;
				Go.SetActive(false);
			}

			public void Apply(LensItem item, bool showNames, bool output)
			{
				//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
				//IL_0099: Unknown result type (might be due to invalid IL or missing references)
				if (item.Sprite != _lastSprite)
				{
					_lastSprite = item.Sprite;
					_art.sprite = item.Sprite;
				}
				bool flag = (Object)(object)item.Sprite != (Object)null;
				if (flag != _artEnabled)
				{
					_artEnabled = flag;
					((Behaviour)_art).enabled = flag;
				}
				_name.SetText(item.Name);
				_name.Go.SetActive(showNames);
				_countLe.flexibleWidth = (showNames ? 0f : 1f);
				_count.SetColor(output ? Palette.MeterGood : Palette.Count);
				TickCount(item);
				Go.SetActive(true);
			}

			public void TickCount(LensItem item)
			{
				_count.SetText((item.Count > 0) ? item.Count.ToString(CultureInfo.InvariantCulture) : "");
			}
		}

		private sealed class ItemBlockView
		{
			private readonly GameObject _go;

			private readonly ItemRow[] _rows;

			private readonly List<int> _order = new List<int>(16);

			private int _shown;

			public ItemBlockView(Transform parent, TMP_FontAsset font, int index)
			{
				//IL_0064: Unknown result type (might be due to invalid IL or missing references)
				RectTransform val = NewRect($"ItemBlock_{index}", parent);
				_go = ((Component)val).gameObject;
				AddColumnLayout(val, 1f, 0, 0);
				RectTransform rect = NewRect("BlockRule", (Transform)(object)val);
				SetPreferredHeight(rect, 2f);
				AddImage(rect, Probe(RuleNames), Palette.RuleDim);
				Sprite tile = Probe(TileNames);
				_rows = new ItemRow[16];
				for (int i = 0; i < _rows.Length; i++)
				{
					_rows[i] = new ItemRow((Transform)(object)val, font, tile);
				}
				_go.SetActive(false);
			}

			public int Apply(LensItemBlock? block, int budget, bool showNames, ref int hidden)
			{
				if (block == null || block.Items.Count == 0 || budget <= 0)
				{
					if (block != null)
					{
						hidden += block.Items.Count;
					}
					_shown = 0;
					_go.SetActive(false);
					return 0;
				}
				List<LensItem> items = block.Items;
				_order.Clear();
				for (int i = 0; i < items.Count; i++)
				{
					_order.Add(i);
				}
				if (!block.PreserveOrder && OttoLensPlugin.SortRowsBy.Value == OttoLensPlugin.SortRows.Count)
				{
					_order.Sort(delegate(int a, int b)
					{
						int num2 = items[b].Count.CompareTo(items[a].Count);
						return (num2 == 0) ? string.CompareOrdinal(items[a].Name, items[b].Name) : num2;
					});
				}
				_shown = Mathf.Min(items.Count, Mathf.Min(budget, 16));
				for (int num = 0; num < _rows.Length; num++)
				{
					if (num < _shown)
					{
						_rows[num].Apply(items[_order[num]], showNames, block.IsOutput);
					}
					else if (_rows[num].Go.activeSelf)
					{
						_rows[num].Go.SetActive(false);
					}
				}
				hidden += items.Count - _shown;
				_go.SetActive(true);
				return _shown;
			}

			public void Tick(LensItemBlock? block)
			{
				if (block == null || _shown == 0)
				{
					return;
				}
				List<LensItem> items = block.Items;
				for (int i = 0; i < _shown && i < _order.Count; i++)
				{
					int num = _order[i];
					if (num < items.Count)
					{
						_rows[i].TickCount(items[num]);
					}
				}
			}
		}

		private static class Palette
		{
			public static readonly Color Panel = Hex(1511692, 0.88f);

			public static readonly Color Outline = Hex(722950, 0.7f);

			public static readonly Color EdgeGold = Hex(13215050, 1f);

			public static readonly Color TitleGold = Hex(14267483, 1f);

			public static readonly Color RuleGold = Hex(13214247, 0.85f);

			public static readonly Color RuleDim = Hex(7035454, 0.55f);

			public static readonly Color Body = Hex(14996919, 1f);

			public static readonly Color Count = Hex(15787727, 1f);

			public static readonly Color Dim = Hex(10259566, 1f);

			public static readonly Color Slot = Hex(986122, 0.55f);

			public static readonly Color MeterTrack = Hex(2760986, 0.9f);

			public static readonly Color MeterGood = Hex(7244605, 1f);

			public static readonly Color MeterGold = Hex(13214247, 1f);

			public static readonly Color MeterWarn = Hex(13138472, 1f);

			public static readonly Color MeterBad = Hex(11027514, 1f);

			public static Color Get(LensColor role)
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0021: 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)
				//IL_0029: Unknown result type (might be due to invalid IL or missing references)
				//IL_002c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_003c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0041: Unknown result type (might be due to invalid IL or missing references)
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_0042: Unknown result type (might be due to invalid IL or missing references)
				return (Color)(role switch
				{
					LensColor.Good => MeterGood, 
					LensColor.Warn => MeterWarn, 
					LensColor.Bad => MeterBad, 
					LensColor.Dim => Dim, 
					_ => MeterGold, 
				});
			}

			private static Color Hex(int rgb, float alpha)
			{
				//IL_0030: Unknown result type (might be due to invalid IL or missing references)
				return new Color((float)((rgb >> 16) & 0xFF) / 255f, (float)((rgb >> 8) & 0xFF) / 255f, (float)(rgb & 0xFF) / 255f, alpha);
			}
		}

		private const int ItemRowsPerBlock = 16;

		private const int SecondaryRows = 2;

		private const float ItemRowHeight = 32f;

		private const float ItemRowStride = 33f;

		private const float MeterWidth = 120f;

		private const float MeterFillWidth = 118f;

		private static readonly Dictionary<string, Sprite?> SpriteCache = new Dictionary<string, Sprite>(StringComparer.Ordinal);

		private static readonly string[] PlateNames = new string[4] { "woodpanel_trophys", "woodpanel_settings", "panel_bkg_128", "darken_blob" };

		private static readonly string[] RuleNames = new string[2] { "panel_separator", "divider" };

		private static readonly string[] TileNames = new string[2] { "item_background", "inventory_slot" };

		private static bool _probed;

		private static bool _rebuildRequested;

		private static bool _parentWarned;

		internal static LensPanel? Instance;

		private RectTransform _root;

		private CanvasGroup _group;

		private Image _plate;

		private TextSlot _title;

		private TextSlot _headline;

		private GameObject _statusBlock;

		private StatusRow[] _statusRows;

		private ItemBlockView[] _blocks;

		private GameObject _overflowBlock;

		private TextSlot _overflow;

		private TextSlot _footer;

		private Component? _target;

		private ILensReader? _reader;

		private GameObject? _hover;

		private string? _signature;

		private bool _visible;

		private Coroutine? _tick;

		private Coroutine? _fade;

		private int _tickHz;

		private WaitForSeconds? _tickWait;

		private float _nextRetry;

		private Component? _failedTarget;

		private ILensReader? _failedReader;

		private static readonly HashSet<(Type Reader, Type Exception)> LoggedReaderFailures = new HashSet<(Type, Type)>();

		internal static LensPanel? Get()
		{
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Instance != (Object)null)
			{
				return Instance;
			}
			Hud instance = Hud.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_rootObject == (Object)null || (Object)(object)instance.m_hoverName == (Object)null)
			{
				return null;
			}
			Transform transform = instance.m_rootObject.transform;
			if ((Object)(object)transform == (Object)null)
			{
				if (!_parentWarned)
				{
					_parentWarned = true;
					OttoLensPlugin.Log.LogWarning((object)"Hud root has no transform; the lens panel is disabled for this session.");
				}
				return null;
			}
			ProbeSprites();
			GameObject val = new GameObject("OttoLensPanel", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(transform, false);
			LensPanel lensPanel = val.AddComponent<LensPanel>();
			lensPanel.Build(((TMP_Text)instance.m_hoverName).font);
			Instance = lensPanel;
			return lensPanel;
		}

		internal static void RequestRebuild()
		{
			_rebuildRequested = true;
		}

		internal static void Destroy()
		{
			LensPanel instance = Instance;
			Instance = null;
			if ((Object)(object)instance != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)instance).gameObject);
			}
		}

		internal static void Release()
		{
			Destroy();
			SpriteCache.Clear();
			_probed = false;
		}

		internal void SetTarget(Component? target, ILensReader? reader, GameObject? hover)
		{
			if ((Object)(object)target == (Object)null || reader == null || (Object)(object)hover == (Object)null)
			{
				if (_visible)
				{
					BeginHide();
				}
				_target = null;
				_reader = null;
				_hover = null;
				_failedTarget = null;
				_failedReader = null;
			}
			else if (target == _target)
			{
				bool flag = hover != _hover;
				_hover = hover;
				_reader = reader;
				if (_visible || Time.unscaledTime < _nextRetry)
				{
					if (flag && _visible)
					{
						Refresh();
					}
				}
				else
				{
					_nextRetry = Time.unscaledTime + 1f / (float)Mathf.Clamp(OttoLensPlugin.RefreshHz.Value, 1, 10);
					Refresh();
				}
			}
			else
			{
				_target = target;
				_reader = reader;
				_hover = hover;
				_signature = null;
				_nextRetry = 0f;
				_failedTarget = null;
				_failedReader = null;
				Refresh();
			}
		}

		private void Refresh()
		{
			if ((Object)(object)_target == (Object)null || _reader == null || (Object)(object)_hover == (Object)null)
			{
				BeginHide();
				return;
			}
			if (_target == _failedTarget && _reader == _failedReader)
			{
				BeginHide();
				return;
			}
			ILensReader reader = _reader;
			LensReport lensReport;
			try
			{
				lensReport = reader.Read(_target, _hover);
			}
			catch (Exception ex)
			{
				_failedTarget = _target;
				_failedReader = reader;
				LogReaderFailure(reader, ex);
				lensReport = null;
			}
			if (lensReport == null || !lensReport.HasContent)
			{
				BeginHide();
				return;
			}
			string text = lensReport.Signature();
			if (_rebuildRequested || _signature == null || !string.Equals(text, _signature, StringComparison.Ordinal))
			{
				_rebuildRequested = false;
				_signature = text;
				Rebuild(lensReport);
			}
			else
			{
				Tick(lensReport);
			}
			if (!_visible)
			{
				BeginShow();
			}
		}

		private void Rebuild(LensReport report)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			ApplyGeometry();
			bool value = OttoLensPlugin.ShowNames.Value;
			_title.SetText(LensFormat.Upper(report.Title));
			_headline.SetText(report.Headline ?? "");
			_headline.SetColor(Palette.Get(report.HeadlineColor));
			_headline.Go.SetActive(!string.IsNullOrEmpty(report.Headline));
			int num = 0;
			num += (_statusRows[0].Apply(report.PrimaryMeter, primary: true) ? 1 : 0);
			num += (_statusRows[1].Apply(report.Secondary1, primary: false) ? 1 : 0);
			num += (_statusRows[2].Apply(report.Secondary2, primary: false) ? 1 : 0);
			_statusBlock.SetActive(num > 0);
			int blocks = ((report.Block0 != null && report.Block0.Items.Count > 0) ? 1 : 0) + ((report.Block1 != null && report.Block1.Items.Count > 0) ? 1 : 0);
			int totalTypes = (report.Block0?.Items.Count ?? 0) + (report.Block1?.Items.Count ?? 0);
			bool overflowPossible;
			int rowsFit;
			int num2 = RowBudget(num, blocks, totalTypes, out overflowPossible, out rowsFit);
			int hidden = 0;
			int num3 = _blocks[0].Apply(report.Block0, num2, value, ref hidden);
			int budget = Mathf.Max(1, Mathf.Min(num2, rowsFit - num3));
			_blocks[1].Apply(report.Block1, budget, value, ref hidden);
			bool flag = hidden > 0 && overflowPossible;
			bool flag2 = !string.IsNullOrEmpty(report.Footer);
			_overflow.SetText(flag ? $"and {hidden} more" : "");
			_overflow.Go.SetActive(flag);
			_footer.SetText(report.Footer ?? "");
			_footer.Go.SetActive(flag2);
			_overflowBlock.SetActive(flag || flag2);
			LayoutRebuilder.ForceRebuildLayoutImmediate(_root);
		}

		private void Tick(LensReport report)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			_headline.SetText(report.Headline ?? "");
			_headline.SetColor(Palette.Get(report.HeadlineColor));
			_statusRows[0].TickValues(report.PrimaryMeter, primary: true);
			_statusRows[1].TickValues(report.Secondary1, primary: false);
			_statusRows[2].TickValues(report.Secondary2, primary: false);
			_blocks[0].Tick(report.Block0);
			_blocks[1].Tick(report.Block1);
			_footer.SetText(report.Footer ?? "");
		}

		private int RowBudget(int statusCount, int blocks, int totalTypes, out bool overflowPossible, out int rowsFit)
		{
			//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_0083: 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)
			int num = Mathf.Clamp(OttoLensPlugin.MaxRows.Value, 1, 16);
			float num2 = 44f;
			if (statusCount > 0)
			{
				num2 += 26f + 18f * (float)(statusCount - 1) + 2f * (float)statusCount + 2f;
			}
			num2 += (float)blocks * 5f;
			num2 += 42f;
			float num3 = 1080f;
			Transform parent = ((Transform)_root).parent;
			RectTransform val = (RectTransform)(object)((parent is RectTransform) ? parent : null);
			if (val != null)
			{
				Rect rect = val.rect;
				if (((Rect)(ref rect)).height > 0f)
				{
					rect = val.rect;
					num3 = ((Rect)(ref rect)).height;
				}
			}
			float num4 = Mathf.Min(OttoLensPlugin.OffsetY.Value, -16);
			float num5 = Mathf.Clamp(OttoLensPlugin.GuiScale.Value, 0.75f, 1.6f);
			rowsFit = Mathf.FloorToInt((num4 + num3 / 2f - 24f - num2 * num5) / (33f * num5));
			int result = Mathf.Max(1, Mathf.Min(num, rowsFit));
			if (totalTypes == num + 1 && rowsFit >= totalTypes && totalTypes <= 16)
			{
				overflowPossible = false;
				return totalTypes;
			}
			overflowPossible = true;
			return result;
		}

		private void ApplyGeometry()
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: 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_0136: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Max(OttoLensPlugin.OffsetX.Value, 96);
			float num2 = Mathf.Min(OttoLensPlugin.OffsetY.Value, -16);
			float num3 = Mathf.Clamp(OttoLensPlugin.PanelWidth.Value, 240, 420);
			float num4 = Mathf.Clamp(OttoLensPlugin.GuiScale.Value, 0.75f, 1.6f);
			if (OttoLensPlugin.AvoidHoverText.Value && (Object)(object)Hud.instance != (Object)null && (Object)(object)Hud.instance.m_hoverName != (Object)null)
			{
				float num5 = ((TMP_Text)Hud.instance.m_hoverName).preferredWidth / 2f + 16f;
				if (num5 > num)
				{
					num = num5;
				}
			}
			_root.anchoredPosition = new Vector2(num, num2);
			_root.sizeDelta = new Vector2(num3, _root.sizeDelta.y);
			((Transform)_root).localScale = new Vector3(num4, num4, 1f);
			Color panel = Palette.Panel;
			panel.a = Mathf.Clamp(OttoLensPlugin.BackdropAlpha.Value, 0.5f, 1f);
			if (((Graphic)_plate).color != panel)
			{
				((Graphic)_plate).color = panel;
			}
		}

		private void BeginShow()
		{
			_visible = true;
			((Component)this).gameObject.SetActive(true);
			StartTick();
			StartFade(1f, deactivateAtEnd: false);
		}

		private void BeginHide()
		{
			if (_visible)
			{
				_visible = false;
				_signature = null;
				StopTick();
				if (((Component)this).gameObject.activeSelf)
				{
					StartFade(0f, deactivateAtEnd: true);
				}
			}
		}

		private void StartTick()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			StopTick();
			int num = Mathf.Clamp(OttoLensPlugin.RefreshHz.Value, 1, 10);
			if (_tickWait == null || num != _tickHz)
			{
				_tickHz = num;
				_tickWait = new WaitForSeconds(1f / (float)num);
			}
			_tick = ((MonoBehaviour)this).StartCoroutine(TickLoop());
		}

		private void StopTick()
		{
			if (_tick != null)
			{
				((MonoBehaviour)this).StopCoroutine(_tick);
				_tick = null;
			}
		}

		private IEnumerator TickLoop()
		{
			while (true)
			{
				yield return _tickWait;
				if ((Object)(object)_target == (Object)null)
				{
					break;
				}
				Refresh();
			}
			BeginHide();
		}

		private void StartFade(float to, bool deactivateAtEnd)
		{
			if (_fade != null)
			{
				((MonoBehaviour)this).StopCoroutine(_fade);
				_fade = null;
			}
			float num = Mathf.Clamp(OttoLensPlugin.FadeSeconds.Value, 0f, 0.5f);
			if (num <= 0f)
			{
				_group.alpha = to;
				if (deactivateAtEnd)
				{
					((Component)this).gameObject.SetActive(false);
				}
			}
			else
			{
				_fade = ((MonoBehaviour)this).StartCoroutine(FadeTo(to, num, deactivateAtEnd));
			}
		}

		private IEnumerator FadeTo(float to, float seconds, bool deactivateAtEnd)
		{
			float from = _group.alpha;
			float t = 0f;
			while (t < seconds)
			{
				t += Time.unscaledDeltaTime;
				_group.alpha = Mathf.Lerp(from, to, t / seconds);
				yield return null;
			}
			_group.alpha = to;
			_fade = null;
			if (deactivateAtEnd)
			{
				((Component)this).gameObject.SetActive(false);
			}
		}

		private void OnDestroy()
		{
			if (Instance == this)
			{
				Instance = null;
			}
		}

		private void Build(TMP_FontAsset font)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_0224: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_043b: Unknown result type (might be due to invalid IL or missing references)
			//IL_044a: Expected O, but got Unknown
			//IL_045c: Unknown result type (might be due to invalid IL or missing references)
			//IL_047c: Unknown result type (might be due to invalid IL or missing references)
			//IL_048b: Expected O, but got Unknown
			_root = (RectTransform)((Component)this).transform;
			_root.anchorMin = new Vector2(0.5f, 0.5f);
			_root.anchorMax = new Vector2(0.5f, 0.5f);
			_root.pivot = new Vector2(0f, 1f);
			_root.sizeDelta = new Vector2(300f, 0f);
			Image obj = ((Component)this).gameObject.AddComponent<Image>();
			obj.sprite = null;
			((Graphic)obj).color = Palette.Outline;
			((Graphic)obj).raycastTarget = false;
			_group = ((Component)this).gameObject.AddComponent<CanvasGroup>();
			_group.alpha = 0f;
			_group.blocksRaycasts = false;
			_group.interactable = false;
			VerticalLayoutGroup obj2 = ((Component)this).gameObject.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj2).padding = new RectOffset(10, 10, 8, 8);
			((HorizontalOrVerticalLayoutGroup)obj2).spacing = 2f;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj2).childControlHeight = true;
			((LayoutGroup)obj2).childAlignment = (TextAnchor)0;
			ContentSizeFitter obj3 = ((Component)this).gameObject.AddComponent<ContentSizeFitter>();
			obj3.verticalFit = (FitMode)2;
			obj3.horizontalFit = (FitMode)0;
			RectTransform val = NewRect("Plate", (Transform)(object)_root);
			val.anchorMin = Vector2.zero;
			val.anchorMax = Vector2.one;
			val.offsetMin = new Vector2(1f, 1f);
			val.offsetMax = new Vector2(-1f, -1f);
			_plate = AddImage(val, Probe(PlateNames), Palette.Panel);
			((Component)val).gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
			RectTransform obj4 = NewRect("GoldEdge", (Transform)(object)_root);
			obj4.anchorMin = new Vector2(0f, 0f);
			obj4.anchorMax = new Vector2(0f, 1f);
			obj4.pivot = new Vector2(0f, 0.5f);
			obj4.sizeDelta = new Vector2(3f, 0f);
			obj4.anchoredPosition = Vector2.zero;
			AddImage(obj4, null, Palette.EdgeGold);
			((Component)obj4).gameObject.AddComponent<LayoutElement>().ignoreLayout = true;
			RectTransform val2 = NewRect("TitleRow", (Transform)(object)_root);
			SetPreferredHeight(val2, 22f);
			AddRowLayout(val2, 6f);
			_title = NewText("Title", (Transform)(object)val2, font, 17f, (TextAlignmentOptions)4097, Palette.TitleGold, ellipsis: true);
			SetFlexibleName(_title.Go);
			_headline = NewText("Headline", (Transform)(object)val2, font, 15f, (TextAlignmentOptions)4100, Palette.TitleGold, ellipsis: false);
			_headline.Go.AddComponent<LayoutElement>().minWidth = 0f;
			RectTransform rect = NewRect("TitleRule", (Transform)(object)_root);
			SetPreferredHeight(rect, 2f);
			AddImage(rect, Probe(RuleNames), Palette.RuleGold);
			RectTransform val3 = NewRect("StatusBlock", (Transform)(object)_root);
			_statusBlock = ((Component)val3).gameObject;
			AddColumnLayout(val3, 2f, 2, 2);
			_statusRows = new StatusRow[3];
			for (int i = 0; i < _statusRows.Length; i++)
			{
				_statusRows[i] = new StatusRow((Transform)(object)val3, font, i == 0);
			}
			_blocks = new ItemBlockView[2];
			for (int j = 0; j < _blocks.Length; j++)
			{
				_blocks[j] = new ItemBlockView((Transform)(object)_root, font, j);
			}
			RectTransform val4 = NewRect("OverflowBlock", (Transform)(object)_root);
			_overflowBlock = ((Component)val4).gameObject;
			AddColumnLayout(val4, 1f, 0, 0);
			RectTransform rect2 = NewRect("BlockRule", (Transform)(object)val4);
			SetPreferredHeight(rect2, 2f);
			AddImage(rect2, Probe(RuleNames), Palette.RuleDim);
			_overflow = NewText("Overflow", (Transform)(object)val4, font, 14f, (TextAlignmentOptions)4097, Palette.Dim, ellipsis: true);
			((TMP_Text)_overflow.Tmp).fontStyle = (FontStyles)2;
			SetPreferredHeight((RectTransform)_overflow.Go.transform, 18f);
			_footer = NewText("Footer", (Transform)(object)val4, font, 14f, (TextAlignmentOptions)4097, Palette.Dim, ellipsis: true);
			SetPreferredHeight((RectTransform)_footer.Go.transform, 18f);
			_statusBlock.SetActive(false);
			_overflowBlock.SetActive(false);
			((Component)this).gameObject.SetActive(false);
		}

		private static void ProbeSprites()
		{
			if (_probed)
			{
				return;
			}
			_probed = true;
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			string[] plateNames = PlateNames;
			foreach (string item in plateNames)
			{
				hashSet.Add(item);
			}
			plateNames = RuleNames;
			foreach (string item2 in plateNames)
			{
				hashSet.Add(item2);
			}
			plateNames = TileNames;
			foreach (string item3 in plateNames)
			{
				hashSet.Add(item3);
			}
			try
			{
				Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>();
				foreach (Sprite val in array)
				{
					if ((Object)(object)val != (Object)null && hashSet.Contains(((Object)val).name) && !SpriteCache.ContainsKey(((Object)val).name))
					{
						SpriteCache[((Object)val).name] = val;
					}
				}
			}
			catch (Exception ex)
			{
				OttoLensPlugin.Log.LogDebug((object)("Sprite probe failed: " + ex.GetType().Name + ": " + ex.Message));
			}
		}

		private static Sprite? Probe(string[] names)
		{
			for (int i = 0; i < names.Length; i++)
			{
				if (SpriteCache.TryGetValue(names[i], out Sprite value) && (Object)(object)value != (Object)null)
				{
					return value;
				}
			}
			OttoLensPlugin.Log.LogDebug((object)("No vanilla sprite found for " + string.Join(", ", names) + "; drawing a plain quad."));
			return null;
		}

		private static RectTransform NewRect(string name, Transform parent)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			RectTransform val = (RectTransform)new GameObject(name, new Type[1] { typeof(RectTransform) }).transform;
			((Transform)val).SetParent(parent, false);
			return val;
		}

		private static Image AddImage(RectTransform rect, Sprite? sprite, Color color)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			Image obj = ((Component)rect).gameObject.AddComponent<Image>();
			obj.sprite = sprite;
			obj.type = (Type)(((Object)(object)sprite != (Object)null && sprite.border != Vector4.zero) ? 1 : 0);
			((Graphic)obj).color = color;
			((Graphic)obj).raycastTarget = false;
			return obj;
		}

		private static void SetPreferredHeight(RectTransform rect, float height)
		{
			(((Component)rect).gameObject.GetComponent<LayoutElement>() ?? ((Component)rect).gameObject.AddComponent<LayoutElement>()).preferredHeight = height;
		}

		private static HorizontalLayoutGroup AddRowLayout(RectTransform rect, float spacing)
		{
			HorizontalLayoutGroup obj = ((Component)rect).gameObject.AddComponent<HorizontalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)obj).spacing = spacing;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((LayoutGroup)obj).childAlignment = (TextAnchor)3;
			return obj;
		}

		private static VerticalLayoutGroup AddColumnLayout(RectTransform rect, float spacing, int padTop, int padBottom)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected O, but got Unknown
			VerticalLayoutGroup obj = ((Component)rect).gameObject.AddComponent<VerticalLayoutGroup>();
			((LayoutGroup)obj).padding = new RectOffset(0, 0, padTop, padBottom);
			((HorizontalOrVerticalLayoutGroup)obj).spacing = spacing;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
			((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
			((LayoutGroup)obj).childAlignment = (TextAnchor)0;
			return obj;
		}

		private static void SetFlexibleName(GameObject go)
		{
			LayoutElement obj = go.GetComponent<LayoutElement>() ?? go.AddComponent<LayoutElement>();
			obj.minWidth = 0f;
			obj.preferredWidth = 0f;
			obj.flexibleWidth = 1f;
		}

		private static TextSlot NewText(string name, Transform parent, TMP_FontAsset font, float size, TextAlignmentOptions align, Color color, bool ellipsis)
		{
			//IL_0042: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			RectTransform obj = NewRect(name, parent);
			((Component)obj).gameObject.SetActive(false);
			TextMeshProUGUI val = ((Component)obj).gameObject.AddComponent<TextMeshProUGUI>();
			if ((Object)(object)font != (Object)null)
			{
				((TMP_Text)val).font = font;
			}
			((Component)obj).gameObject.SetActive(true);
			((TMP_Text)val).fontSize = size;
			((TMP_Text)val).alignment = align;
			((Graphic)val).color = color;
			((TMP_Text)val).richText = false;
			((Graphic)val).raycastTarget = false;
			((TMP_Text)val).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)val).overflowMode = (TextOverflowModes)(ellipsis ? 1 : 0);
			((TMP_Text)val).text = "";
			return new TextSlot(val, color);
		}

		private static void LogReaderFailure(ILensReader reader, Exception ex)
		{
			Type type = reader.GetType();
			if (LoggedReaderFailures.Add((type, ex.GetType())))
			{
				OttoLensPlugin.Log.LogWarning((object)(type.Name + " threw " + ex.GetType().Name + ": " + ex.Message + ". Further " + ex.GetType().Name + " reports from " + type.Name + " are suppressed.\n" + ex.StackTrace));
			}
		}
	}
}
namespace OttoLens.Readers
{
	internal sealed class ArmorStandReader : ILensReader
	{
		private static int[] s_itemKeys = Array.Empty<int>();

		private static int[] s_variantKeys = Array.Empty<int>();

		private readonly LensReport _report = new LensReport();

		private readonly LensItemBlock _block = new LensItemBlock();

		private readonly List<LensItem> _pool = new List<LensItem>();

		private readonly List<int> _order = new List<int>();

		public Type TargetType => typeof(ArmorStand);

		public bool Enabled => OttoLensPlugin.ShowStands.Value;

		public LensReport? Read(Component target, GameObject hover)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			ArmorStand val = (ArmorStand)target;
			ZNetView nview = val.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			if (!LensReaders.HasWardAccess((Component)(object)val))
			{
				return null;
			}
			List<ArmorStandSlot> slots = val.m_slots;
			int count = slots.Count;
			if (count == 0)
			{
				return null;
			}
			EnsureKeys(count);
			ZDO zDO = nview.GetZDO();
			List<int> order = _order;
			order.Clear();
			int num = 0;
			int num2 = 0;
			for (int i = 0; i < count; i++)
			{
				int num3 = zDO.GetInt(s_itemKeys[i], 0);
				if (num3 == 0)
				{
					continue;
				}
				num++;
				int variant = zDO.GetInt(s_variantKeys[i], 0);
				if (!ItemStandReader.TryResolveItem(num3, variant, out string name, out Sprite sprite, out ItemType type))
				{
					name = slots[i].m_currentItemName;
					if (name.Length == 0)
					{
						continue;
					}
				}
				LensItem lensItem = Pooled(num2);
				lensItem.Sprite = sprite;
				lensItem.Name = LensFormat.Name(name);
				lensItem.Count = 1;
				lensItem.Quality = 0;
				order.Add((Rank(type) << 8) | num2);
				num2++;
			}
			if (num2 == 0)
			{
				return null;
			}
			order.Sort();
			LensReport report = _report;
			report.Reset();
			report.Title = LensFormat.Name(val.m_name);
			LensItemBlock block = _block;
			block.Clear();
			block.PreserveOrder = true;
			for (int j = 0; j < order.Count; j++)
			{
				block.Items.Add(_pool[order[j] & 0xFF]);
			}
			report.Block0 = block;
			report.Footer = LensFormat.Free(count - num);
			return report;
		}

		private LensItem Pooled(int index)
		{
			while (_pool.Count <= index)
			{
				_pool.Add(new LensItem());
			}
			return _pool[index];
		}

		private static void EnsureKeys(int count)
		{
			if (s_itemKeys.Length < count)
			{
				int[] array = new int[count];
				int[] array2 = new int[count];
				for (int i = 0; i < count; i++)
				{
					array[i] = StringExtensionMethods.GetStableHashCode(i + "_item");
					array2[i] = StringExtensionMethods.GetStableHashCode(i + "_variant");
				}
				s_itemKeys = array;
				s_variantKeys = array2;
			}
		}

		private static int Rank(ItemType type)
		{
			//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_0058: Expected I4, but got Unknown
			switch (type - 3)
			{
			case 0:
			case 1:
			case 11:
			case 12:
			case 16:
			case 17:
			case 19:
				return 0;
			case 2:
				return 1;
			case 3:
				return 2;
			case 4:
				return 3;
			case 8:
				return 4;
			case 14:
				return 5;
			case 15:
				return 6;
			default:
				return 7;
			}
		}
	}
	internal sealed class BeehiveReader : ILensReader
	{
		private static readonly Dictionary<int, Sprite?> SpriteCache = new Dictionary<int, Sprite>();

		private readonly LensReport _report = new LensReport();

		private readonly LensItemBlock _output = new LensItemBlock();

		private const float StateCacheSeconds = 2f;

		private Beehive? _cachedHive;

		private float _cachedUntil;

		private bool _cachedBiomeOk;

		private bool _cachedSpaceOk;

		public Type TargetType => typeof(Beehive);

		public bool Enabled => OttoLensPlugin.ShowFermenting.Value;

		public LensReport? Read(Component target, GameObject hover)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			Beehive val = (Beehive)target;
			ZNetView nview = val.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			if (!LensReaders.HasWardAccess(target))
			{
				return null;
			}
			ZDO zDO = nview.GetZDO();
			int num = zDO.GetInt(ZDOVars.s_level, 0);
			int maxHoney = val.m_maxHoney;
			bool flag = maxHoney > 0 && num >= maxHoney;
			ReadState(val, out var biomeOk, out var spaceOk);
			bool flag2 = biomeOk && spaceOk;
			bool flag3 = flag2 && val.m_effectOnlyInDaylight && !EnvMan.IsDaylight();
			LensReport report = _report;
			report.Reset();
			report.Title = LensFormat.Name(val.m_name);
			if (flag)
			{
				report.Headline = "FULL";
				report.HeadlineColor = LensColor.Good;
			}
			else if (!biomeOk)
			{
				report.Headline = "WRONG BIOME";
				report.HeadlineColor = LensColor.Bad;
			}
			else if (!spaceOk)
			{
				report.Headline = "NO SPACE";
				report.HeadlineColor = LensColor.Bad;
			}
			else if (flag3)
			{
				report.Headline = "ASLEEP";
				report.HeadlineColor = LensColor.Dim;
			}
			else
			{
				report.Headline = "HAPPY";
				report.HeadlineColor = LensColor.Gold;
			}
			float value = ((maxHoney > 0) ? Mathf.Clamp01((float)num / (float)maxHoney) : 0f);
			report.PrimaryMeter = LensReport.Meter("Honey", (maxHoney > 0) ? new float?(value) : ((float?)null), LensFormat.Count(num, maxHoney), (!flag) ? LensColor.Gold : LensColor.Good);
			if (!flag && flag2 && val.m_secPerUnit > 0f)
			{
				float num2 = zDO.GetFloat(ZDOVars.s_product, 0f);
				float num3 = Mathf.Max(0f, val.m_secPerUnit - num2);
				report.Secondary1 = LensReport.Meter("Next", Mathf.Clamp01(num3 / val.m_secPerUnit), LensFormat.Time(num3), flag3 ? LensColor.Dim : LensColor.Gold, drains: true);
			}
			if (num > 0 && (Object)(object)val.m_honeyItem != (Object)null)
			{
				ItemData itemData = val.m_honeyItem.m_itemData;
				_output.Clear();
				_output.IsOutput = true;
				_output.Items.Add(new LensItem(GetSprite(((Object)((Component)val.m_honeyItem).gameObject).name, itemData), LensFormat.Name(itemData.m_shared.m_name), num));
				report.Block1 = _output;
			}
			return report;
		}

		private void ReadState(Beehive hive, out bool biomeOk, out bool spaceOk)
		{
			float unscaledTime = Time.unscaledTime;
			if (hive != _cachedHive || unscaledTime >= _cachedUntil)
			{
				_cachedHive = hive;
				_cachedUntil = unscaledTime + 2f;
				_cachedBiomeOk = hive.CheckBiome();
				_cachedSpaceOk = _cachedBiomeOk && hive.HaveFreeSpace();
			}
			biomeOk = _cachedBiomeOk;
			spaceOk = _cachedSpaceOk;
		}

		private static Sprite? GetSprite(string prefabName, ItemData data)
		{
			int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName);
			if (SpriteCache.TryGetValue(stableHashCode, out Sprite value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Sprite val = null;
			try
			{
				val = data.GetIcon();
			}
			catch (Exception)
			{
			}
			SpriteCache[stableHashCode] = val;
			return val;
		}
	}
	internal sealed class ContainerReader : ILensReader
	{
		private const string HiddenFooter = "Contents unknown";

		private static readonly Dictionary<string, Sprite?> IconCache = new Dictionary<string, Sprite>(StringComparer.Ordinal);

		private readonly LensReport _report = new LensReport();

		private readonly LensItemBlock _block = new LensItemBlock();

		private readonly Dictionary<string, LensItem> _groupIndex = new Dictionary<string, LensItem>(StringComparer.Ordinal);

		private readonly List<LensItem> _groups = new List<LensItem>();

		private Container? _cachedContainer;

		private uint _cachedRevision;

		private int _cachedCount = -1;

		private int _cachedEpoch = -1;

		private string _cachedSignature = "";

		private static string? _discoveredName;

		private static int _discoveredHash;

		private static readonly HashSet<ZDOID> _openedChests = new HashSet<ZDOID>();

		public Type TargetType => typeof(Container);

		public bool Enabled => OttoLensPlugin.ShowContainers.Value;

		public LensReport? Read(Component target, GameObject hover)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Invalid comparison between Unknown and I4
			Container val = (Container)target;
			ZNetView nview = val.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			ZDO zDO = nview.GetZDO();
			if (zDO == null)
			{
				return null;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return null;
			}
			bool flag = (Object)(object)val.m_wagon != (Object)null || (Object)(object)((Component)val).GetComponentInParent<Ship>() != (Object)null;
			if (!flag && val.m_checkGuardStone && !LensReaders.HasWardAccess((Component)(object)val))
			{
				return null;
			}
			if ((int)val.m_privacy != 2 && ((Object)(object)val.m_piece == (Object)null || !val.CheckAccess(localPlayer.GetPlayerID())))
			{
				return null;
			}
			Inventory inventory = val.GetInventory();
			if (inventory == null)
			{
				return null;
			}
			LensReport report = _report;
			report.Reset();
			report.Title = LensFormat.Name(val.m_name);
			if (!flag && IsUnopenedWorldChest(val, zDO, localPlayer))
			{
				report.Footer = "Contents unknown";
				return report;
			}
			int num = inventory.NrOfItems();
			int num2 = inventory.GetWidth() * inventory.GetHeight();
			float num3 = ((num2 > 0) ? ((float)num / (float)num2) : 0f);
			LensColor color = LensFormat.FillRamp(num3);
			report.PrimaryMeter = LensReport.Meter("Slots", num3, LensFormat.Count(num, num2), color);
			report.Footer = LensFormat.Free(Math.Max(0, num2 - num));
			if (num == 0)
			{
				report.Headline = "EMPTY";
				report.HeadlineColor = LensColor.Dim;
			}
			else if (num3 >= 1f)
			{
				report.Headline = "FULL";
				report.HeadlineColor = LensColor.Bad;
			}
			uint lastRevision = val.m_lastRevision;
			int localizationEpoch = LensReaders.LocalizationEpoch;
			if (val != _cachedContainer || lastRevision != _cachedRevision || num != _cachedCount || localizationEpoch != _cachedEpoch)
			{
				Regroup(inventory);
				_cachedContainer = val;
				_cachedRevision = lastRevision;
				_cachedCount = num;
				_cachedEpoch = localizationEpoch;
				_cachedSignature = lastRevision + ":" + num + ":" + localizationEpoch;
			}
			if (_groups.Count > 0)
			{
				_block.Clear();
				_block.Items.AddRange(_groups);
				report.Block0 = _block;
			}
			report.ContentSignature = _cachedSignature;
			return report;
		}

		private static bool IsUnopenedWorldChest(Container container, ZDO zdo, Player player)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			if (!OttoLensPlugin.HideUnopenedWorldChests.Value)
			{
				return false;
			}
			Piece piece = container.m_piece;
			if ((Object)(object)piece != (Object)null && piece.IsPlacedByPlayer())
			{
				return false;
			}
			if ((int)container.m_discoverStat != 206)
			{
				string playerName = player.GetPlayerName();
				if (!string.Equals(playerName, _discoveredName, StringComparison.Ordinal))
				{
					_discoveredName = playerName;
					_discoveredHash = StringExtensionMethods.GetStableHashCode("Discovered_" + playerName);
				}
				return !zdo.GetBool(_discoveredHash, false);
			}
			return !_openedChests.Contains(zdo.m_uid);
		}

		internal static void RecordOpen(Container container)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			ZNetView nview = container.m_nview;
			if (!((Object)(object)nview == (Object)null) && nview.IsValid())
			{
				ZDO zDO = nview.GetZDO();
				if (zDO != null)
				{
					_openedChests.Add(zDO.m_uid);
				}
			}
		}

		internal static void ClearOpenedChests()
		{
			_openedChests.Clear();
		}

		private void Regroup(Inventory inventory)
		{
			_groupIndex.Clear();
			_groups.Clear();
			List<ItemData> allItems = inventory.GetAllItems();
			for (int i = 0; i < allItems.Count; i++)
			{
				ItemData val = allItems[i];
				if (val == null || val.m_shared == null)
				{
					continue;
				}
				string text = val.m_shared.m_name ?? "";
				if (_groupIndex.TryGetValue(text, out LensItem value))
				{
					value.Count += val.m_stack;
					int num = ((val.m_quality > 1) ? val.m_quality : 0);
					if (num > value.Quality)
					{
						value.Quality = num;
					}
				}
				else
				{
					int quality = ((val.m_quality > 1) ? val.m_quality : 0);
					LensItem lensItem = new LensItem(Icon(val, text), LensFormat.Name(text), val.m_stack, quality);
					_groupIndex[text] = lensItem;
					_groups.Add(lensItem);
				}
			}
		}

		private static Sprite? Icon(ItemData item, string token)
		{
			string key = ((item.m_variant == 0) ? token : (token + "#" + item.m_variant));
			if (IconCache.TryGetValue(key, out Sprite value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Sprite val;
			try
			{
				val = item.GetIcon();
			}
			catch (Exception)
			{
				val = null;
			}
			IconCache[key] = val;
			return val;
		}
	}
	internal sealed class CookingStationReader : ILensReader
	{
		private const int StatusDone = 1;

		private const int StatusBurnt = 2;

		private const int MaxSlots = 16;

		private static readonly int[] SlotKeys = BuildKeys("slot");

		private static readonly int[] SlotStatusKeys = BuildKeys("slotstatus");

		private static readonly Dictionary<string, Sprite?> SpriteCache = new Dictionary<string, Sprite>(StringComparer.Ordinal);

		private readonly LensReport _report = new LensReport();

		private readonly LensItemBlock _cooking = new LensItemBlock();

		private readonly LensItemBlock _done = new LensItemBlock();

		public Type TargetType => typeof(CookingStation);

		public bool Enabled => OttoLensPlugin.ShowCooking.Value;

		public LensReport? Read(Component target, GameObject hover)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			CookingStation val = (CookingStation)target;
			ZNetView nview = val.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			ZDO zDO = nview.GetZDO();
			if (zDO == null)
			{
				return null;
			}
			LensReport report = _report;
			report.Reset();
			_cooking.Clear();
			_done.Clear();
			_cooking.PreserveOrder = true;
			_done.PreserveOrder = true;
			int num = ((val.m_slots != null) ? Mathf.Min(val.m_slots.Length, 16) : 0);
			int num2 = 0;
			bool flag = false;
			bool flag2 = false;
			bool flag3 = false;
			float num3 = float.MaxValue;
			float num4 = 0f;
			float num5 = float.MaxValue;
			for (int i = 0; i < num; i++)
			{
				string text = zDO.GetString(SlotKeys[i], "");
				if (text.Length == 0)
				{
					continue;
				}
				num2++;
				float num6 = zDO.GetFloat(SlotKeys[i], 0f);
				int num7 = zDO.GetInt(SlotStatusKeys[i], 0);
				if (num7 == 2)
				{
					flag3 = true;
					AddSlot(_done, val.m_overCookedItem, text);
					continue;
				}
				ItemConversion val2 = FindConversion(val, text);
				if (val2 == null)
				{
					AddSlot(_cooking, null, text);
					flag = true;
					continue;
				}
				if (num7 == 1)
				{
					flag2 = true;
					AddSlot(_done, val2.m_to, text);
					if (val.m_canOvercookItems)
					{
						num5 = Mathf.Min(num5, val2.m_cookTime * 2f - num6);
					}
					continue;
				}
				flag = true;
				ItemDrop drop = ((text == ((Object)((Component)val2.m_from).gameObject).name) ? val2.m_from : val2.m_to);
				AddSlot(_cooking, drop, text);
				float num8 = val2.m_cookTime - num6;
				if (num8 < num3)
				{
					num3 = num8;
					num4 = val2.m_cookTime;
				}
			}
			if (num2 == 0 && !val.m_useFuel)
			{
				return null;
			}
			bool flag4 = val.m_requireFire && !val.IsFireLit();
			float num9 = (val.m_useFuel ? val.GetFuel() : 0f);
			bool flag5 = val.m_useFuel && num9 <= 0f;
			bool flag6 = (val.m_requireFire && !flag4) || (val.m_useFuel && num9 > 0f && (val.m_useFueldWhileEmpty || flag));
			report.Title = LensFormat.Name(val.m_name);
			if (flag4)
			{
				report.Headline = "NO FIRE";
				report.HeadlineColor = LensColor.Bad;
			}
			else if (flag5)
			{
				report.Headline = "NO FUEL";
				report.HeadlineColor = LensColor.Bad;
			}
			else if (flag)
			{
				report.Headline = "COOKING";
				report.HeadlineColor = LensColor.Gold;
			}
			else if (flag2)
			{
				report.Headline = "READY";
				report.HeadlineColor = LensColor.Good;
			}
			else if (flag3)
			{
				report.Headline = "BURNT";
				report.HeadlineColor = LensColor.Bad;
			}
			LensMeter? lensMeter = null;
			if (flag)
			{
				if (flag6 && num3 < float.MaxValue)
				{
					float num10 = Mathf.Max(num3, 0f);
					float value = ((num4 > 0f) ? Mathf.Clamp01(num10 / num4) : 0f);
					lensMeter = LensReport.Meter("Next", value, LensFormat.Time(num10), (num10 > 0f) ? LensColor.Gold : LensColor.Good, drains: true);
				}
			}
			else if (flag2)
			{
				lensMeter = LensReport.Meter("Next", 0f, "Ready", LensColor.Good, drains: true);
			}
			LensMeter? lensMeter2 = null;
			if (flag6 && num5 < float.MaxValue)
			{
				string valueText = ((num5 > 0f) ? LensFormat.Time(num5) : "Now");
				lensMeter2 = LensReport.Meter("Burns", null, valueText, LensColor.Warn, drains: true);
			}
			if (val.m_useFuel)
			{
				int num11 = Mathf.Max(val.m_maxFuel, 1);
				float num12 = Mathf.Clamp01(num9 / (float)num11);
				string label = (((Object)(object)val.m_fuelItem != (Object)null) ? LensFormat.Name(val.m_fuelItem.m_itemData.m_shared.m_name) : "Fuel");
				report.PrimaryMeter = LensReport.Meter(label, num12, LensFormat.Count(num9, num11), LensFormat.Ramp(num12));
				LensMeter? lensMeter3 = ((num9 > 0f) ? new LensMeter?(LensReport.Meter("Fuel", null, LensFormat.TimeWithDays(num9 * (float)val.m_secPerFuel), LensColor.Gold, drains: true)) : ((LensMeter?)null));
				report.Secondary1 = lensMeter ?? lensMeter2 ?? lensMeter3;
				report.Secondary2 = ((!lensMeter.HasValue) ? (lensMeter2.HasValue ? lensMeter3 : ((LensMeter?)null)) : (lensMeter2 ?? lensMeter3));
			}
			else
			{
				report.PrimaryMeter = lensMeter;
				report.Secondary1 = lensMeter2;
			}
			if (_cooking.Items.Count > 0)
			{
				_cooking.Label = "Cooking";
				report.Block0 = _cooking;
			}
			if (_done.Items.Count > 0)
			{
				_done.Label = "Done";
				_done.IsOutput = true;
				report.Block1 = _done;
			}
			if (num > 0)
			{
				report.Footer = LensFormat.Free(num - num2);
			}
			return report;
		}

		private static void AddSlot(LensItemBlock block, ItemDrop? drop, string prefabName)
		{
			List<LensItem> items = block.Items;
			string text = (((Object)(object)drop != (Object)null) ? LensFormat.Name(drop.m_itemData.m_shared.m_name) : LensFormat.Name(prefabName));
			for (int i = 0; i < items.Count; i++)
			{
				if (items[i].Name == text)
				{
					items[i].Count++;
					return;
				}
			}
			items.Add(new LensItem(GetSprite(prefabName, drop), text, 1));
		}

		private static ItemConversion? FindConversion(CookingStation station, string itemName)
		{
			List<ItemConversion> conversion = station.m_conversion;
			if (conversion == null)
			{
				return null;
			}
			for (int i = 0; i < conversion.Count; i++)
			{
				ItemConversion val = conversion[i];
				if (val != null && !((Object)(object)val.m_from == (Object)null) && !((Object)(object)val.m_to == (Object)null) && (((Object)((Component)val.m_from).gameObject).name == itemName || ((Object)((Component)val.m_to).gameObject).name == itemName))
				{
					return val;
				}
			}
			return null;
		}

		private static Sprite? GetSprite(string prefabName, ItemDrop? drop)
		{
			if (SpriteCache.TryGetValue(prefabName, out Sprite value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			if ((Object)(object)drop == (Object)null)
			{
				return null;
			}
			Sprite val;
			try
			{
				val = drop.m_itemData.GetIcon();
			}
			catch (Exception)
			{
				val = null;
			}
			SpriteCache[prefabName] = val;
			return val;
		}

		private static int[] BuildKeys(string prefix)
		{
			int[] array = new int[16];
			for (int i = 0; i < 16; i++)
			{
				array[i] = StringExtensionMethods.GetStableHashCode(prefix + i);
			}
			return array;
		}
	}
	internal sealed class CraftingStationReader : ILensReader
	{
		private static readonly Dictionary<string, Sprite?> IconCache = new Dictionary<string, Sprite>(StringComparer.Ordinal);

		private readonly LensReport _report = new LensReport();

		private readonly LensItemBlock _block = new LensItemBlock();

		private readonly Dictionary<string, LensItem> _groupIndex = new Dictionary<string, LensItem>(StringComparer.Ordinal);

		private readonly List<LensItem> _groups = new List<LensItem>();

		private CraftingStation? _cachedStation;

		private Container? _cachedContainer;

		private const float RoofCacheSeconds = 2f;

		private CraftingStation? _roofStation;

		private float _roofUntil;

		private bool _roofOk;

		private Container? _groupedContainer;

		private uint _groupedRevision;

		private int _groupedCount = -1;

		private int _groupedEpoch = -1;

		public Type TargetType => typeof(CraftingStation);

		public bool Enabled => OttoLensPlugin.ShowMisc.Value;

		public LensReport? Read(Component target, GameObject hover)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			CraftingStation val = (CraftingStation)target;
			ZNetView nview = val.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !val.InUseDistance((Humanoid)(object)localPlayer))
			{
				return null;
			}
			LensReport report = _report;
			report.Reset();
			report.Title = LensFormat.Name(val.m_name);
			if (val.m_craftRequireFire && !val.m_haveFire)
			{
				report.Headline = "NO FIRE";
				report.HeadlineColor = LensColor.Bad;
			}
			else if (val.m_craftRequireRoof && (Object)(object)val.m_roofCheckPoint != (Object)null && !HasRoof(val, localPlayer))
			{
				report.Headline = "NO ROOF";
				report.HeadlineColor = LensColor.Bad;
			}
			else
			{
				report.Headline = "READY";
				report.HeadlineColor = LensColor.Good;
			}
			LensMeter value = LensReport.Meter("Level", null, val.GetLevel(false).ToString(CultureInfo.InvariantCulture), LensColor.Gold);
			Container val2 = FindContainer(val);
			if ((Object)(object)val2 == (Object)null || !CanReadContainer(val2, localPlayer))
			{
				report.PrimaryMeter = value;
				return report;
			}
			Inventory inventory = val2.GetInventory();
			ZDO val3 = (((Object)(object)val2.m_nview != (Object)null && val2.m_nview.IsValid()) ? val2.m_nview.GetZDO() : null);
			if (inventory == null || val3 == null)
			{
				report.PrimaryMeter = value;
				return report;
			}
			int num = inventory.GetWidth() * inventory.GetHeight();
			int emptySlots = inventory.GetEmptySlots();
			int num2 = num - emptySlots;
			float num3 = ((num > 0) ? Mathf.Clamp01((float)num2 / (float)num) : 0f);
			report.PrimaryMeter = LensReport.Meter("Slots", (num > 0) ? new float?(num3) : ((float?)null), LensFormat.Count(num2, num), LensFormat.FillRamp(num3));
			report.Secondary1 = value;
			report.Footer = LensFormat.Free(emptySlots);
			if (num > 0 && emptySlots == 0)
			{
				report.Headline = "FULL";
				report.HeadlineColor = LensColor.Bad;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			uint lastRevision = val2.m_lastRevision;
			int localizationEpoch = LensReaders.LocalizationEpoch;
			if ((Object)(object)_groupedContainer != (Object)(object)val2 || _groupedRevision != lastRevision || _groupedCount != allItems.Count || _groupedEpoch != localizationEpoch)
			{
				_groupedContainer = val2;
				_groupedRevision = lastRevision;
				_groupedCount = allItems.Count;
				_groupedEpoch = localizationEpoch;
				Regroup(allItems);
			}
			if (_groups.Count > 0)
			{
				LensItemBlock block = _block;
				block.Clear();
				block.Label = "Contents";
				block.Items.AddRange(_groups);
				report.Block0 = block;
			}
			report.ContentSignature = report.Title + "|" + lastRevision.ToString(CultureInfo.InvariantCulture) + "|" + allItems.Count.ToString(CultureInfo.InvariantCulture) + "|" + report.Headline;
			return report;
		}

		private Container? FindContainer(CraftingStation station)
		{
			if ((Object)(object)_cachedStation != (Object)(object)station)
			{
				_cachedStation = station;
				_cachedContainer = ((Component)station).GetComponentInChildren<Container>();
			}
			return _cachedContainer;
		}

		private bool HasRoof(CraftingStation station, Player player)
		{
			float unscaledTime = Time.unscaledTime;
			if (station != _roofStation || unscaledTime >= _roofUntil)
			{
				_roofStation = station;
				_roofUntil = unscaledTime + 2f;
				_roofOk = station.CheckUsable(player, false);
			}
			return _roofOk;
		}

		private static bool CanReadContainer(Container container, Player player)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			if (container.m_checkGuardStone && !LensReaders.HasWardAccess((Component)(object)container))
			{
				return false;
			}
			if ((int)container.m_privacy != 2 && ((Object)(object)container.m_piece == (Object)null || !container.CheckAccess(player.GetPlayerID())))
			{
				return false;
			}
			return true;
		}

		private void Regroup(List<ItemData> items)
		{
			_groupIndex.Clear();
			_groups.Clear();
			for (int i = 0; i < items.Count; i++)
			{
				ItemData val = items[i];
				if (val != null && val.m_shared != null)
				{
					string name = val.m_shared.m_name;
					if (_groupIndex.TryGetValue(name, out LensItem value))
					{
						value.Count += val.m_stack;
						continue;
					}
					value = new LensItem(Icon(val, name), LensFormat.Name(name), val.m_stack);
					_groupIndex[name] = value;
					_groups.Add(value);
				}
			}
		}

		private static Sprite? Icon(ItemData item, string token)
		{
			if (IconCache.TryGetValue(token, out Sprite value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Sprite val;
			try
			{
				val = item.GetIcon();
			}
			catch (Exception)
			{
				val = null;
			}
			IconCache[token] = val;
			return val;
		}
	}
	internal sealed class DestructibleReader : ILensReader
	{
		private readonly LensReport _report = new LensReport();

		private static readonly Dictionary<string, string> TitleCache = new Dictionary<string, string>(StringComparer.Ordinal);

		private static readonly StringBuilder TitleBuilder = new StringBuilder(32);

		public Type TargetType => typeof(Destructible);

		public bool Enabled => OttoLensPlugin.ShowTreesAndRocks.Value;

		public LensReport? Read(Component target, GameObject hover)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			Destructible val = (Destructible)target;
			ZNetView nview = val.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			float num = ScaledMax(val.m_health);
			float num2 = nview.GetZDO().GetFloat(ZDOVars.s_health, num);
			if (num <= 0f || num2 <= 0f)
			{
				return null;
			}
			LensReport report = _report;
			report.Reset();
			report.Title = TitleFor(((Component)val).gameObject);
			report.PrimaryMeter = HealthMeter(num2, num);
			report.Secondary1 = ToolTierMeter(val.m_minToolTier);
			return report;
		}

		internal static float ScaledMax(float baseHealth)
		{
			return baseHealth + (float)Game.m_worldLevel * baseHealth * Game.instance.m_worldLevelMineHPMultiplier;
		}

		internal static LensMeter HealthMeter(float current, float max)
		{
			float num = Mathf.Clamp01(current / max);
			return LensReport.Meter("Health", num, LensFormat.Percent(num), LensFormat.Ramp(num));
		}

		internal static LensMeter? ToolTierMeter(int minToolTier)
		{
			if (minToolTier <= 0)
			{
				return null;
			}
			int num = HeldToolTier();
			if (num < minToolTier)
			{
				string valueText = ((num < 0) ? $"{minToolTier} needed" : $"{minToolTier} needed, holding {num}");
				return LensReport.Meter("Tool tier", null, valueText, LensColor.Bad);
			}
			return LensReport.Meter("Tool tier", null, minToolTier.ToString(), LensColor.Dim);
		}

		private static int HeldToolTier()
		{
			Player localPlayer = Player.m_localPlayer;
			return (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetCurrentWeapon() : null)?.m_shared?.m_toolTier ?? (-1);
		}

		internal static string TitleFor(GameObject go)
		{
			Piece component = go.GetComponent<Piece>();
			if ((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.m_name))
			{
				return LensFormat.Name(component.m_name);
			}
			string prefabName = Utils.GetPrefabName(go);
			if (TitleCache.TryGetValue(prefabName, out string value))
			{
				return value;
			}
			StringBuilder titleBuilder = TitleBuilder;
			titleBuilder.Clear();
			string[] array = prefabName.Split(new char[1] { '_' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].TrimEnd('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
				if (text.Length != 0)
				{
					if (titleBuilder.Length > 0)
					{
						titleBuilder.Append(' ');
					}
					titleBuilder.Append(text);
				}
			}
			string text2 = ((titleBuilder.Length > 0) ? titleBuilder.ToString() : prefabName);
			TitleCache[prefabName] = text2;
			return text2;
		}
	}
	internal sealed class FeastReader : ILensReader
	{
		private static readonly Dictionary<string, Sprite?> IconCache = new Dictionary<string, Sprite>(StringComparer.Ordinal);

		private readonly LensReport _report = new LensReport();

		private readonly LensItemBlock _block = new LensItemBlock();

		private readonly LensItem _item = new LensItem();

		public Type TargetType => typeof(Feast);

		public bool Enabled => OttoLensPlugin.ShowMisc.Value;

		public LensReport? Read(Component target, GameObject hover)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			Feast val = (Feast)target;
			ZNetView nview = val.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			ItemDrop foodItem = val.m_foodItem;
			if ((Object)(object)foodItem == (Object)null || foodItem.m_itemData == null || foodItem.m_itemData.m_shared == null)
			{
				return null;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !val.InUseDistance((Humanoid)(object)localPlayer))
			{
				return null;
			}
			int stack = val.GetStack();
			if (stack <= 0)
			{
				return null;
			}
			int eatStacks = val.m_eatStacks;
			float num = ((eatStacks > 0) ? Mathf.Clamp01((float)stack / (float)eatStacks) : 1f);
			string name = foodItem.m_itemData.m_shared.m_name;
			string text = LensFormat.Name(name);
			LensReport report = _report;
			report.Reset();
			report.Title = text;
			report.PrimaryMeter = LensReport.Meter("Servings", (eatStacks > 0) ? new float?(num) : ((float?)null), LensFormat.Count(stack, eatStacks), LensFormat.Ramp(num));
			LensItem item = _item;
			item.Sprite = Icon(foodItem.m_itemData, name);
			item.Name = text;
			item.Count = stack;
			item.Quality = 0;
			LensItemBlock block = _block;
			block.Clear();
			block.Label = "Food";
			block.Items.Add(item);
			report.Block0 = block;
			return report;
		}

		private static Sprite? Icon(ItemData item, string token)
		{
			if (IconCache.TryGetValue(token, out Sprite value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Sprite val;
			try
			{
				val = item.GetIcon();
			}
			catch (Exception)
			{
				val = null;
			}
			IconCache[token] = val;
			return val;
		}
	}
	internal sealed class FermenterReader : ILensReader
	{
		private static readonly Dictionary<int, Sprite?> SpriteCache = new Dictionary<int, Sprite>();

		private readonly LensReport _report = new LensReport();

		private readonly LensItemBlock _contents = new LensItemBlock();

		private readonly LensItemBlock _output = new LensItemBlock();

		public Type TargetType => typeof(Fermenter);

		public bool Enabled => OttoLensPlugin.ShowFermenting.Value;

		public LensReport? Read(Component target, GameObject hover)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			Fermenter val = (Fermenter)target;
			ZNetView nview = val.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			if (!LensReaders.HasWardAccess(target))
			{
				return null;
			}
			ZDO zDO = nview.GetZDO();
			int num = zDO.GetInt(ZDOVars.s_content, 0);
			if (num == 0)
			{
				return null;
			}
			long num2 = zDO.GetLong(ZDOVars.s_startTime, 0L);
			double num3 = ((num2 == 0L) ? 0.0 : (ZNet.instance.GetTime() - new DateTime(num2)).TotalSeconds);
			float fermentationDuration = val.m_fermentationDuration;
			bool flag = num3 > (double)fermentationDuration;
			float num4 = (flag ? 0f : Mathf.Max(0f, fermentationDuration - (float)num3));
			bool flag2 = !flag && (!val.m_hasRoof || val.m_exposed);
			LensReport report = _report;
			report.Reset();
			report.Title = LensFormat.Name(val.m_name);
			if (flag)
			{
				report.Headline = "READY";
				report.HeadlineColor = LensColor.Good;
			}
			float value = ((fermentationDuration > 0f) ? Mathf.Clamp01(num4 / fermentationDuration) : 0f);
			LensColor color = ((!flag) ? (flag2 ? LensColor.Warn : LensColor.Gold) : LensColor.Good);
			report.PrimaryMeter = LensReport.Meter("Next", value, LensFormat.TimeWithDays(num4), color, drains: true);
			ItemConversion itemConversion = val.GetItemConversion(num);
			if (itemConversion == null)
			{
				return report;
			}
			if (!flag && (Object)(object)itemConversion.m_from != (Object)null)
			{
				_contents.Clear();
				_contents.Items.Add(MakeItem(itemConversion.m_from, 1));
				report.Block0 = _contents;
			}
			if ((Object)(object)itemConversion.m_to != (Object)null)
			{
				_output.Clear();
				_output.Label = (flag ? null : "Makes");
				_output.IsOutput = true;
				_output.Items.Add(MakeItem(itemConversion.m_to, itemConversion.m_producedItems));
				report.Block1 = _output;
			}
			return report;
		}

		private static LensItem MakeItem(ItemDrop drop, int count)
		{
			ItemData itemData = drop.m_itemData;
			return new LensItem(GetSprite(((Object)((Component)drop).gameObject).name, itemData), LensFormat.Name(itemData.m_shared.m_name), count);
		}

		private static Sprite? GetSprite(string prefabName, ItemData data)
		{
			int stableHashCode = StringExtensionMethods.GetStableHashCode(prefabName);
			if (SpriteCache.TryGetValue(stableHashCode, out Sprite value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Sprite val = null;
			try
			{
				val = data.GetIcon();
			}
			catch (Exception)
			{
			}
			SpriteCache[stableHashCode] = val;
			return val;
		}
	}
	internal sealed class FireplaceReader : ILensReader
	{
		private const string FallbackFuelLabel = "Fuel";

		private readonly LensReport _report = new LensReport();

		public Type TargetType => typeof(Fireplace);

		public bool Enabled => OttoLensPlugin.ShowFires.Value;

		public LensReport? Read(Component target, GameObject hover)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			Fireplace val = (Fireplace)target;
			ZNetView nview = val.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return null;
			}
			if (val.m_infiniteFuel)
			{
				return null;
			}
			ZDO zDO = nview.GetZDO();
			float num = Mathf.Max(0f, zDO.GetFloat(ZDOVars.s_fuel, 0f));