Decompiled source of FairyDust Tooltip BepInEx v2.0.0

BepInEx\plugins\FairyDust.Tooltip.dll

Decompiled 5 days ago
using System;
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.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using FairyDust.Tooltip.Configuration;
using FairyDust.Tooltip.Features.InventoryTooltip;
using FairyDust.Tooltip.Host;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using madeinfairyland.fairyengine;
using madeinfairyland.fairyengine.actor;
using madeinfairyland.fairyengine.actor.player;
using madeinfairyland.fairyengine.ui;
using madeinfairyland.forsakenfrontiers;
using madeinfairyland.forsakenfrontiers.actor.player;
using madeinfairyland.forsakenfrontiers.actor.player.datadeck;
using madeinfairyland.forsakenfrontiers.actor.player.equipment;
using madeinfairyland.forsakenfrontiers.train;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("FairyDust.Tooltip")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FairyDust.Tooltip")]
[assembly: AssemblyTitle("FairyDust.Tooltip")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace FairyDust.Tooltip
{
	public sealed class FairyDustTooltipMod
	{
		private readonly IFairyDustHost host;

		private InventoryTooltipModule inventoryTooltipModule;

		private bool disposed;

		private bool loggedConfigReloadFailure;

		private long nextConfigReloadAt;

		public FairyDustTooltipMod(IFairyDustHost host)
		{
			this.host = host;
		}

		public void Initialize()
		{
			disposed = false;
			Config.Initialize(host);
			host.Log.Info($"{"FairyDust.Tooltip"} v{"2.0.0"} loaded under {host.LoaderName}.");
			host.Log.Info("Game root: " + host.GameRootDirectory);
			host.Log.Info("Config file: " + Config.ConfigFilePath);
			inventoryTooltipModule = new InventoryTooltipModule(host);
			inventoryTooltipModule.Initialize();
		}

		public void Shutdown()
		{
			if (!disposed)
			{
				disposed = true;
				inventoryTooltipModule?.Dispose();
				inventoryTooltipModule = null;
				host.Log.Info("FairyDust.Tooltip shut down.");
			}
		}

		public void OnUpdate()
		{
			ReloadConfigIfDue();
			inventoryTooltipModule?.OnUpdate();
		}

		public void OnSceneWasLoaded(int buildIndex, string sceneName)
		{
			ForsakenScene.WorldLoaded(sceneName);
		}

		private void ReloadConfigIfDue()
		{
			long timestamp = Stopwatch.GetTimestamp();
			if (timestamp < nextConfigReloadAt)
			{
				return;
			}
			nextConfigReloadAt = timestamp + Stopwatch.Frequency;
			try
			{
				if (Config.ReloadIfChanged())
				{
					loggedConfigReloadFailure = false;
					host.Log.Info("Tooltip config reloaded.");
				}
			}
			catch (Exception ex)
			{
				nextConfigReloadAt = timestamp + Stopwatch.Frequency * 5;
				if (!loggedConfigReloadFailure)
				{
					loggedConfigReloadFailure = true;
					host.Log.Warning("Tooltip config reload failed: " + ex);
				}
			}
		}
	}
	public static class Metadata
	{
		public const string Name = "FairyDust.Tooltip";

		public const string Version = "2.0.0";

		public const string Author = "MadeInPG13";
	}
}
namespace FairyDust.Tooltip.Host
{
	public interface IFairyDustConfig<T> where T : new()
	{
		string FilePath { get; }

		T Values { get; }

		void Initialize();

		void Save();

		bool ReloadIfChanged();
	}
	public interface IFairyDustHost
	{
		string LoaderName { get; }

		string GameRootDirectory { get; }

		string ConfigDirectory { get; }

		IFairyDustLogger Log { get; }

		IFairyDustConfig<T> CreateConfig<T>(string categoryName) where T : new();

		void PatchAll(Assembly assembly);

		void UnpatchSelf();
	}
	public interface IFairyDustLogger
	{
		void Info(string message);

		void Warning(string message);

		void Error(string message);

		void Error(string message, Exception exception);
	}
}
namespace FairyDust.Tooltip.Features.InventoryTooltip
{
	internal sealed class ActiveDataDeckContext
	{
		private FlavorData localFlavor;

		public FFDataDeck Deck { get; set; }

		public Canvas DeckCanvas { get; set; }

		public bool TryGetLocalFlavor(out FlavorData flavor)
		{
			flavor = localFlavor;
			return flavor != null;
		}

		public void RememberLocalFlavor(FFDataDeck deck, FlavorData flavor)
		{
			if (flavor != null && LocalPlayerDeck.IsLocalPlayerDeck(deck))
			{
				localFlavor = flavor;
			}
		}

		public void ClearFlavor()
		{
			localFlavor = null;
		}
	}
	internal sealed class DataDeckToggleBinding
	{
		private Action<bool> handler;

		private FFDataDeck bound;

		public void Subscribe(FFDataDeck deck, Action<bool> onToggled)
		{
			if (!Il2CppGameInterop.SameInstance(bound, deck))
			{
				Clear();
				bound = deck;
				handler = DelegateSupport.ConvertDelegate<Action<bool>>((Delegate)onToggled);
				deck.EvtOnToggledDataDeck += handler;
			}
		}

		public void Clear()
		{
			if ((Object)(object)bound != (Object)null && (Delegate)(object)handler != (Delegate)null)
			{
				try
				{
					bound.EvtOnToggledDataDeck -= handler;
				}
				catch
				{
				}
			}
			bound = null;
			handler = null;
		}
	}
	internal static class LocalPlayerDeck
	{
		public static bool IsOpen(FFDataDeck deck)
		{
			if ((Object)(object)deck != (Object)null)
			{
				return deck.DataDeckOpen;
			}
			return false;
		}

		public static bool IsLocalPlayerDeck(FFDataDeck candidate)
		{
			FFPlayer current = FairyLocalPlayer.Current;
			FFDataDeck val = ((current != null) ? current.DataDeck : null);
			if ((Object)(object)val != (Object)null)
			{
				return Il2CppGameInterop.SameInstance(val, candidate);
			}
			return false;
		}

		public static bool CanAdopt(FFDataDeck deck)
		{
			if ((Object)(object)deck == (Object)null)
			{
				return false;
			}
			if (!IsLocalPlayerDeck(deck))
			{
				return false;
			}
			return ForsakenScene.IsValid(deck);
		}
	}
	internal static class EquipmentTitleResolver
	{
		public static string Resolve(BoundSlot slot)
		{
			FFEquipment val = slot?.Equipment;
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			string fairyName = TryGetFairyName(val);
			foreach (string item in EnumerateCandidateNames(val, fairyName))
			{
				string configuredTitle = ItemDisplayNames.GetConfiguredTitle(item);
				if (configuredTitle != null)
				{
					return configuredTitle;
				}
			}
			string text = TryResolveShopTitle(val, fairyName);
			if (text != null)
			{
				return text;
			}
			string text2 = TryResolveTypeTitle(val);
			if (text2 != null)
			{
				return text2;
			}
			string text3 = TryResolveItemTitle(val, fairyName);
			if (text3 != null)
			{
				return text3;
			}
			return "[NEW ITEM]";
		}

		private static IEnumerable<string> EnumerateCandidateNames(FFEquipment equipment, string fairyName)
		{
			yield return fairyName;
			yield return SafeName(() => ((Object)equipment).name);
			yield return SafeName(delegate
			{
				GameObject gameObject = ((Component)equipment).gameObject;
				return (gameObject == null) ? null : ((Object)gameObject).name;
			});
			yield return GetTypeName(equipment);
			yield return ItemDisplayNames.CleanSourceName(GetTypeName(equipment));
		}

		private static string TryResolveShopTitle(FFEquipment equipment, string fairyName)
		{
			try
			{
				FFTrader current = FairyTrader.Current;
				Il2CppReferenceArray<ShopItem> val = ((current != null) ? current.ShopItems : null);
				if (val == null)
				{
					return null;
				}
				for (int i = 0; i < ((Il2CppArrayBase<ShopItem>)(object)val).Length; i++)
				{
					ShopItem val2 = ((Il2CppArrayBase<ShopItem>)(object)val)[i];
					if (val2 != null && MatchesShopPrefab(val2.prefab, equipment, fairyName))
					{
						string stableShopItemName = GetStableShopItemName(val2);
						string text = ItemDisplayNames.GetConfiguredTitle(stableShopItemName) ?? ItemDisplayNames.GetConfiguredTitle(val2.itemName);
						if (text != null)
						{
							return text;
						}
						return CacheAndReturn(equipment, fairyName, ItemDisplayNames.GetFallbackTitle(stableShopItemName), stableShopItemName, val2.itemName);
					}
				}
			}
			catch
			{
				return null;
			}
			return null;
		}

		private static string GetStableShopItemName(ShopItem item)
		{
			string text = SafeName(() => item.OriginalItemName);
			if (string.IsNullOrWhiteSpace(text))
			{
				return ItemDisplayNames.CleanDisplayTitle(SafeName(() => item.itemName));
			}
			return text;
		}

		private static string TryResolveTypeTitle(FFEquipment equipment)
		{
			string typeName = GetTypeName(equipment);
			return CacheAndReturn(equipment, TryGetFairyName(equipment), ItemDisplayNames.GetFallbackTitle(typeName), typeName);
		}

		private static string TryResolveItemTitle(FFEquipment equipment, string fairyName)
		{
			foreach (string item in EnumerateItemNames(equipment, fairyName))
			{
				string fallbackTitle = ItemDisplayNames.GetFallbackTitle(item);
				if (fallbackTitle != null)
				{
					return CacheAndReturn(equipment, fairyName, fallbackTitle, item);
				}
			}
			return null;
		}

		private static string CacheAndReturn(FFEquipment equipment, string fairyName, string title, params string[] extraNames)
		{
			if (title != null)
			{
				Config.CacheDisplayName(EnumerateCacheNames(equipment, fairyName, extraNames), title);
			}
			return title;
		}

		private static IEnumerable<string> EnumerateCacheNames(FFEquipment equipment, string fairyName, IEnumerable<string> extraNames)
		{
			foreach (string item in EnumerateCandidateNames(equipment, fairyName))
			{
				yield return item;
			}
			foreach (string extraName in extraNames)
			{
				yield return extraName;
			}
		}

		private static IEnumerable<string> EnumerateItemNames(FFEquipment equipment, string fairyName)
		{
			yield return SafeName(() => ((Object)equipment).name);
			yield return SafeName(delegate
			{
				GameObject gameObject = ((Component)equipment).gameObject;
				return (gameObject == null) ? null : ((Object)gameObject).name;
			});
			yield return fairyName;
		}

		private static bool MatchesShopPrefab(GameObject prefab, FFEquipment equipment, string fairyName)
		{
			if ((Object)(object)prefab == (Object)null || (Object)(object)equipment == (Object)null)
			{
				return false;
			}
			if (SameName(TryGetFairyName(prefab), fairyName))
			{
				return true;
			}
			string left = SafeName(() => ((Object)prefab).name);
			if (SameName(left, SafeName(() => ((Object)equipment).name)) || SameName(left, SafeName(delegate
			{
				GameObject gameObject = ((Component)equipment).gameObject;
				return (gameObject == null) ? null : ((Object)gameObject).name;
			})))
			{
				return true;
			}
			return SameName(GetTypeName(SafeGetComponent<FFEquipment>(prefab)), GetTypeName(equipment));
		}

		private static string TryGetFairyName(FFEquipment equipment)
		{
			try
			{
				FairyObject obj = ((equipment != null) ? ((Il2CppObjectBase)equipment).TryCast<FairyObject>() : null);
				return (obj != null) ? obj.FairyName : null;
			}
			catch
			{
				return null;
			}
		}

		private static string TryGetFairyName(GameObject gameObject)
		{
			try
			{
				FairyObject obj = ((gameObject != null) ? gameObject.GetComponent<FairyObject>() : null);
				return (obj != null) ? obj.FairyName : null;
			}
			catch
			{
				return null;
			}
		}

		private static T SafeGetComponent<T>(GameObject gameObject) where T : Component
		{
			try
			{
				return ((Object)(object)gameObject != (Object)null) ? gameObject.GetComponent<T>() : default(T);
			}
			catch
			{
				return default(T);
			}
		}

		private static string SafeName(Func<string> read)
		{
			try
			{
				return read();
			}
			catch
			{
				return null;
			}
		}

		private static string GetTypeName(object instance)
		{
			return instance?.GetType().Name;
		}

		private static bool SameName(string left, string right)
		{
			string text = Config.NormalizeKey(ItemDisplayNames.CleanSourceName(left));
			string b = Config.NormalizeKey(ItemDisplayNames.CleanSourceName(right));
			if (text.Length > 0)
			{
				return string.Equals(text, b, StringComparison.Ordinal);
			}
			return false;
		}
	}
	internal static class FairyItemEquipmentResolver
	{
		private static MethodInfo[] concreteTryCastMethods;

		private static readonly Dictionary<IntPtr, FFEquipment> equipmentByItem = new Dictionary<IntPtr, FFEquipment>();

		private static readonly HashSet<MethodInfo> failedMethods = new HashSet<MethodInfo>();

		public static FFEquipment Resolve(FairyItem linked)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			if ((Object)(object)linked == (Object)null)
			{
				return null;
			}
			IntPtr intPtr = NativePointer(linked);
			if (intPtr != IntPtr.Zero && equipmentByItem.TryGetValue(intPtr, out var value))
			{
				return value;
			}
			Ensure();
			MethodInfo[] array = concreteTryCastMethods;
			foreach (MethodInfo methodInfo in array)
			{
				if (!failedMethods.Contains(methodInfo))
				{
					object obj;
					try
					{
						obj = methodInfo.Invoke(linked, null);
					}
					catch
					{
						failedMethods.Add(methodInfo);
						continue;
					}
					if (obj != null)
					{
						return Cache(intPtr, (FFEquipment)obj);
					}
				}
			}
			return Cache(intPtr, ((Il2CppObjectBase)linked).TryCast<FFEquipment>());
		}

		public static void Clear()
		{
			equipmentByItem.Clear();
			failedMethods.Clear();
		}

		private static void Ensure()
		{
			if (concreteTryCastMethods != null)
			{
				return;
			}
			try
			{
				MethodInfo tryCastMethod = typeof(FairyItem).GetRuntimeMethods().FirstOrDefault((MethodInfo m) => m.Name == "TryCast" && m.IsGenericMethodDefinition);
				if (tryCastMethod == null)
				{
					concreteTryCastMethods = Array.Empty<MethodInfo>();
					return;
				}
				concreteTryCastMethods = (from t in (from t in typeof(FFEquipment).Assembly.GetTypes()
						where t.IsSubclassOf(typeof(FFEquipment)) && !t.IsAbstract
						select t).OrderByDescending(DepthUnderEquipment)
					select tryCastMethod.MakeGenericMethod(t)).ToArray();
			}
			catch
			{
				concreteTryCastMethods = Array.Empty<MethodInfo>();
			}
		}

		private static FFEquipment Cache(IntPtr pointer, FFEquipment equipment)
		{
			if (pointer != IntPtr.Zero && (Object)(object)equipment != (Object)null)
			{
				equipmentByItem[pointer] = equipment;
			}
			return equipment;
		}

		private static IntPtr NativePointer(object instance)
		{
			Il2CppObjectBase val = (Il2CppObjectBase)((instance is Il2CppObjectBase) ? instance : null);
			if (val == null)
			{
				return IntPtr.Zero;
			}
			return val.Pointer;
		}

		private static int DepthUnderEquipment(Type t)
		{
			int num = 0;
			Type type = t;
			while (type != null && type != typeof(FFEquipment))
			{
				num++;
				type = type.BaseType;
			}
			return num;
		}
	}
	internal static class FairyLocalPlayer
	{
		public static FFPlayer Current
		{
			get
			{
				FairyPlayer localPlayer = FairyEngine.LocalPlayer;
				if (localPlayer == null)
				{
					return null;
				}
				return ((Il2CppObjectBase)localPlayer).TryCast<FFPlayer>();
			}
		}

		public static FFWorld World
		{
			get
			{
				FFPlayer current = Current;
				if (current == null)
				{
					return null;
				}
				return current._ffWorld;
			}
		}

		public static FFTrain Train
		{
			get
			{
				FFWorld world = World;
				object obj = ((world != null) ? world._train : null);
				if (obj == null)
				{
					FFPlayer current = Current;
					if (current == null)
					{
						return null;
					}
					obj = current.Train;
				}
				return (FFTrain)obj;
			}
		}
	}
	internal static class FairyTrader
	{
		public static FFTrader Current
		{
			get
			{
				FFTrain train = FairyLocalPlayer.Train;
				if (train == null)
				{
					return null;
				}
				return train.Trader;
			}
		}
	}
	internal static class Il2CppGameInterop
	{
		private static IntPtr PointerOf(object instance)
		{
			Il2CppObjectBase val = (Il2CppObjectBase)((instance is Il2CppObjectBase) ? instance : null);
			if (val != null)
			{
				return val.Pointer;
			}
			return IntPtr.Zero;
		}

		public static bool SameInstance(object a, object b)
		{
			return NativeObjectIdentity.SameObject(a, b, PointerOf);
		}
	}
	internal static class NativeObjectIdentity
	{
		public static bool SameObject(object left, object right, Func<object, IntPtr> pointerOf)
		{
			if (left == right)
			{
				return left != null;
			}
			if (left == null || right == null)
			{
				return false;
			}
			return SamePointer(pointerOf(left), pointerOf(right));
		}

		public static bool SamePointer(IntPtr left, IntPtr right)
		{
			if (left != IntPtr.Zero)
			{
				return left == right;
			}
			return false;
		}
	}
	internal sealed class InventoryTooltipModule : IDisposable
	{
		private readonly IFairyDustHost host;

		private readonly EquipSlotBinder binder;

		private readonly DataDeckToggleBinding deckToggleBinding = new DataDeckToggleBinding();

		private readonly ActiveDataDeckContext context = new ActiveDataDeckContext();

		private readonly EquipSlotHoverTooltips tooltips;

		private Object lastInventoryUIGroupForCanvas;

		private bool disposed;

		private bool inventoryTooltipsEnabled;

		internal static InventoryTooltipModule Instance { get; private set; }

		public InventoryTooltipModule(IFairyDustHost host)
		{
			this.host = host ?? throw new ArgumentNullException("host");
			binder = new EquipSlotBinder(new EquipSlotLocator(), new EquipSlotHoverBindingRegistry());
			tooltips = new EquipSlotHoverTooltips(binder, context);
		}

		public void Initialize()
		{
			disposed = false;
			Instance = this;
			inventoryTooltipsEnabled = Config.Item.EnableInventoryTooltips;
			ForsakenScene.OnWorldLoaded += ResetSceneState;
			host.PatchAll(typeof(InventoryTooltipModule).Assembly);
			host.Log.Info("Inventory tooltip module initialized.");
		}

		public void OnUpdate()
		{
			if (!disposed)
			{
				ApplyInventoryTooltipEnabledState();
				if (inventoryTooltipsEnabled)
				{
					tooltips.OnUpdate();
				}
			}
		}

		public void Dispose()
		{
			if (!disposed)
			{
				disposed = true;
				ForsakenScene.OnWorldLoaded -= ResetSceneState;
				deckToggleBinding.Clear();
				CleanupBindings();
				tooltips.Dispose();
				host.UnpatchSelf();
				if (Instance == this)
				{
					Instance = null;
				}
			}
		}

		internal void OnHarmonyDeckRefreshData(FFDataDeck instance)
		{
			if (!LocalPlayerDeck.IsLocalPlayerDeck(instance) || !LocalPlayerDeck.IsOpen(instance))
			{
				return;
			}
			if ((Object)(object)context.Deck == (Object)null)
			{
				TryAdoptLocalDeck(instance);
			}
			if (!((Object)(object)context.Deck == (Object)null) && Il2CppGameInterop.SameInstance(instance, context.Deck))
			{
				RebindEquipSlotsIfDeckReady();
				if (inventoryTooltipsEnabled)
				{
					tooltips.RefreshHover();
				}
			}
		}

		internal void RememberLocalFlavor(FFDataDeck dataDeck, FlavorData flavor)
		{
			context.RememberLocalFlavor(dataDeck, flavor);
		}

		internal void TryAdoptLocalDeck(FFDataDeck dataDeck)
		{
			if (LocalPlayerDeck.CanAdopt(dataDeck) && !AlreadyWiredTo(dataDeck))
			{
				deckToggleBinding.Clear();
				CleanupBindings();
				context.Deck = dataDeck;
				context.DeckCanvas = null;
				lastInventoryUIGroupForCanvas = null;
				deckToggleBinding.Subscribe(dataDeck, OnDeckToggled);
				if (inventoryTooltipsEnabled && dataDeck.DataDeckOpen)
				{
					RebindEquipSlotsIfDeckReady();
				}
			}
		}

		private void ResetSceneState()
		{
			deckToggleBinding.Clear();
			CleanupBindings();
			context.Deck = null;
			context.DeckCanvas = null;
			context.ClearFlavor();
			FairyItemEquipmentResolver.Clear();
			lastInventoryUIGroupForCanvas = null;
		}

		private void OnDeckToggled(bool open)
		{
			if (!open)
			{
				tooltips.ClearHover();
			}
			else if (inventoryTooltipsEnabled)
			{
				RebindEquipSlotsIfDeckReady();
			}
		}

		private void RebindEquipSlotsIfDeckReady()
		{
			if (!inventoryTooltipsEnabled)
			{
				CleanupBindings();
				return;
			}
			FFDataDeck deck = context.Deck;
			if (!((Object)(object)deck == (Object)null))
			{
				if (!LocalPlayerDeck.IsOpen(deck))
				{
					tooltips.ClearHover();
					return;
				}
				binder.TryRebind(deck, tooltips.OnPointerEnter, tooltips.OnPointerExit);
				RefreshDeckCanvas(deck);
			}
		}

		private void RefreshDeckCanvas(FFDataDeck deck)
		{
			if ((Object)(object)deck.inventoryUIGroup != (Object)null)
			{
				if (lastInventoryUIGroupForCanvas != deck.inventoryUIGroup)
				{
					lastInventoryUIGroupForCanvas = (Object)(object)deck.inventoryUIGroup;
					context.DeckCanvas = ((Component)deck.inventoryUIGroup).GetComponentInParent<Canvas>(true);
				}
			}
			else
			{
				lastInventoryUIGroupForCanvas = null;
				context.DeckCanvas = ((Component)deck).GetComponentInParent<Canvas>(true);
			}
		}

		private void ApplyInventoryTooltipEnabledState()
		{
			bool enableInventoryTooltips = Config.Item.EnableInventoryTooltips;
			if (enableInventoryTooltips != inventoryTooltipsEnabled)
			{
				inventoryTooltipsEnabled = enableInventoryTooltips;
				if (!enableInventoryTooltips)
				{
					CleanupBindings();
				}
				else
				{
					RebindEquipSlotsIfDeckReady();
				}
			}
		}

		private void CleanupBindings()
		{
			binder.UnbindAll();
			tooltips.ClearHover();
			FairyItemEquipmentResolver.Clear();
		}

		private bool AlreadyWiredTo(FFDataDeck dataDeck)
		{
			return Il2CppGameInterop.SameInstance(dataDeck, context.Deck);
		}
	}
	internal static class ForsakenScene
	{
		public const string WorldName = "Forsaken Frontiers";

		internal static event Action OnWorldLoaded;

		public static bool IsWorldScene(string sceneName)
		{
			return string.Equals(sceneName, "Forsaken Frontiers", StringComparison.Ordinal);
		}

		internal static void WorldLoaded(string sceneName)
		{
			if (IsWorldScene(sceneName))
			{
				ForsakenScene.OnWorldLoaded?.Invoke();
			}
		}

		public static bool IsValid(FFDataDeck deck)
		{
			//IL_0021: 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)
			Scene scene = (((Object)(object)deck.inventoryUIGroup != (Object)null) ? ((Component)deck.inventoryUIGroup).gameObject : ((Component)deck).gameObject).scene;
			return string.Equals(((Scene)(ref scene)).name, "Forsaken Frontiers", StringComparison.Ordinal);
		}
	}
	internal sealed class BoundSlot
	{
		public int SlotIndex { get; }

		public RectTransform HoverRect { get; }

		public RectTransform TooltipAnchor { get; }

		public GameObject InspectRoot { get; }

		public FairyUIItemBase UiItem { get; private set; }

		public FairyItem LinkedItem { get; private set; }

		public FFEquipment Equipment { get; private set; }

		public BoundSlot(int slotIndex, RectTransform hoverRect, RectTransform tooltipAnchor, GameObject inspectRoot)
		{
			SlotIndex = slotIndex;
			HoverRect = hoverRect;
			TooltipAnchor = tooltipAnchor;
			InspectRoot = inspectRoot;
			RefreshItemCache();
		}

		public bool RefreshItemCache()
		{
			FairyItem linkedItem = LinkedItem;
			UiItem = null;
			LinkedItem = null;
			Equipment = null;
			if ((Object)(object)InspectRoot == (Object)null)
			{
				return (Object)(object)linkedItem != (Object)null;
			}
			FairyUIItemBase[] array = Il2CppArrayBase<FairyUIItemBase>.op_Implicit(InspectRoot.GetComponentsInChildren<FairyUIItemBase>(true));
			foreach (FairyUIItemBase val in array)
			{
				FairyItem linkedToItem = val.LinkedToItem;
				if (!((Object)(object)linkedToItem == (Object)null))
				{
					UiItem = val;
					LinkedItem = linkedToItem;
					Equipment = FairyItemEquipmentResolver.Resolve(linkedToItem);
					return !Il2CppGameInterop.SameInstance(linkedItem, LinkedItem);
				}
			}
			return (Object)(object)linkedItem != (Object)null;
		}
	}
	internal sealed class EquipSlotBinder
	{
		private readonly EquipSlotLocator locator;

		private readonly EquipSlotHoverBindingRegistry hoverBindingRegistry;

		private readonly Dictionary<int, BoundSlot> slotsByIndex = new Dictionary<int, BoundSlot>();

		private Transform lastBoundSlotsRoot;

		private Transform lastSearchRoot;

		public TextMeshProUGUI StyleTemplate { get; private set; }

		public int BoundSlotCount => slotsByIndex.Count;

		public EquipSlotBinder(EquipSlotLocator locator, EquipSlotHoverBindingRegistry hoverBindingRegistry)
		{
			this.locator = locator;
			this.hoverBindingRegistry = hoverBindingRegistry;
		}

		public bool TryRebind(FFDataDeck deck, Action<BoundSlot> onPointerEnter, Action<BoundSlot> onPointerExit)
		{
			if ((Object)(object)deck == (Object)null)
			{
				UnbindAll();
				return false;
			}
			Transform primarySearchRoot = EquipSlotLocator.GetPrimarySearchRoot(deck);
			if ((Object)(object)lastSearchRoot == (Object)(object)primarySearchRoot && slotsByIndex.Count == 4 && CurrentBindingsAreValid())
			{
				return true;
			}
			if (!locator.TryFindSlots(deck, out var slots, out var slotsRootUsed))
			{
				UnbindAll();
				return false;
			}
			if ((Object)(object)lastBoundSlotsRoot == (Object)(object)slotsRootUsed && slotsByIndex.Count == 4 && CurrentBindingsAreValid())
			{
				return true;
			}
			UnbindAll();
			lastBoundSlotsRoot = slotsRootUsed;
			lastSearchRoot = primarySearchRoot;
			foreach (BoundSlot item in slots)
			{
				slotsByIndex[item.SlotIndex] = item;
				hoverBindingRegistry.Bind(item, onPointerEnter, onPointerExit);
			}
			StyleTemplate = locator.FindStyleTemplate(deck, slotsRootUsed);
			return true;
		}

		public void RefreshSlotItemCaches()
		{
			foreach (BoundSlot value in slotsByIndex.Values)
			{
				value.RefreshItemCache();
			}
		}

		public void UnbindAll()
		{
			hoverBindingRegistry.UnbindAll();
			slotsByIndex.Clear();
			lastBoundSlotsRoot = null;
			lastSearchRoot = null;
			StyleTemplate = null;
		}

		private bool CurrentBindingsAreValid()
		{
			for (int i = 1; i <= 4; i++)
			{
				if (!slotsByIndex.TryGetValue(i, out var value) || (Object)(object)value.HoverRect == (Object)null || (Object)(object)value.TooltipAnchor == (Object)null)
				{
					return false;
				}
			}
			return true;
		}
	}
	internal sealed class EquipSlotHoverBindingRegistry
	{
		private sealed class PointerHoverRelay
		{
			public BoundSlot Slot;

			public Action<BoundSlot> OnEnter;

			public Action<BoundSlot> OnExit;

			public void HandleEnter(BaseEventData _)
			{
				OnEnter?.Invoke(Slot);
			}

			public void HandleExit(BaseEventData _)
			{
				OnExit?.Invoke(Slot);
			}
		}

		private sealed class SlotEventBinding
		{
			public BoundSlot Slot;

			public EventTrigger Trigger;

			public Entry EnterEntry;

			public Entry ExitEntry;

			public PointerHoverRelay Relay;

			public bool CreatedTrigger;

			public Graphic RaycastGraphic;

			public bool OriginalRaycastTarget;

			public bool ChangedRaycastTarget;

			public bool CreatedRaycastGraphic;
		}

		private static readonly Color NearlyInvisibleRaycast = new Color(0f, 0f, 0f, 0.01f);

		private readonly List<SlotEventBinding> eventBindings = new List<SlotEventBinding>();

		public void Bind(BoundSlot slot, Action<BoundSlot> onPointerEnter, Action<BoundSlot> onPointerExit)
		{
			SlotEventBinding slotEventBinding = EnsureRaycastTarget(slot.HoverRect);
			slotEventBinding.Slot = slot;
			GameObject gameObject = ((Component)slot.HoverRect).gameObject;
			EventTrigger val = gameObject.GetComponent<EventTrigger>();
			if ((Object)(object)val == (Object)null)
			{
				val = gameObject.AddComponent<EventTrigger>();
				slotEventBinding.CreatedTrigger = true;
			}
			PointerHoverRelay pointerHoverRelay = new PointerHoverRelay
			{
				Slot = slot,
				OnEnter = onPointerEnter,
				OnExit = onPointerExit
			};
			slotEventBinding.Trigger = val;
			slotEventBinding.Relay = pointerHoverRelay;
			slotEventBinding.EnterEntry = AddEventTriggerEntry(val, (EventTriggerType)0, pointerHoverRelay.HandleEnter);
			slotEventBinding.ExitEntry = AddEventTriggerEntry(val, (EventTriggerType)1, pointerHoverRelay.HandleExit);
			eventBindings.Add(slotEventBinding);
		}

		public void UnbindAll()
		{
			for (int num = eventBindings.Count - 1; num >= 0; num--)
			{
				Unbind(eventBindings[num]);
			}
			eventBindings.Clear();
		}

		private static void Unbind(SlotEventBinding binding)
		{
			if ((Object)(object)binding.Trigger != (Object)null)
			{
				if (binding.EnterEntry != null)
				{
					binding.Trigger.triggers.Remove(binding.EnterEntry);
				}
				if (binding.ExitEntry != null)
				{
					binding.Trigger.triggers.Remove(binding.ExitEntry);
				}
				if (binding.CreatedTrigger && binding.Trigger.triggers.Count == 0)
				{
					Object.Destroy((Object)(object)binding.Trigger);
				}
			}
			if (binding.CreatedRaycastGraphic && (Object)(object)binding.RaycastGraphic != (Object)null)
			{
				Object.Destroy((Object)(object)binding.RaycastGraphic);
			}
			else if (binding.ChangedRaycastTarget && (Object)(object)binding.RaycastGraphic != (Object)null)
			{
				binding.RaycastGraphic.raycastTarget = binding.OriginalRaycastTarget;
			}
		}

		private static Entry AddEventTriggerEntry(EventTrigger trigger, EventTriggerType type, Action<BaseEventData> handler)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			Entry val = new Entry
			{
				eventID = type
			};
			((UnityEvent<BaseEventData>)(object)val.callback).AddListener(DelegateSupport.ConvertDelegate<UnityAction<BaseEventData>>((Delegate)handler));
			trigger.triggers.Add(val);
			return val;
		}

		private static SlotEventBinding EnsureRaycastTarget(RectTransform rect)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			SlotEventBinding slotEventBinding = new SlotEventBinding();
			if ((Object)(object)rect == (Object)null)
			{
				return slotEventBinding;
			}
			Graphic component = ((Component)rect).GetComponent<Graphic>();
			if ((Object)(object)component == (Object)null)
			{
				Image val = ((Component)rect).gameObject.AddComponent<Image>();
				((Graphic)val).color = NearlyInvisibleRaycast;
				((Graphic)val).raycastTarget = true;
				slotEventBinding.RaycastGraphic = (Graphic)(object)val;
				slotEventBinding.CreatedRaycastGraphic = true;
			}
			else if (!component.raycastTarget)
			{
				slotEventBinding.RaycastGraphic = component;
				slotEventBinding.OriginalRaycastTarget = component.raycastTarget;
				slotEventBinding.ChangedRaycastTarget = true;
				component.raycastTarget = true;
			}
			return slotEventBinding;
		}
	}
	internal sealed class EquipSlotLocator
	{
		public const int EquipSlotCount = 4;

		public bool TryFindSlots(FFDataDeck deck, out IReadOnlyList<BoundSlot> slots, out Transform slotsRootUsed)
		{
			slots = Array.Empty<BoundSlot>();
			slotsRootUsed = null;
			foreach (Transform deckSearchRoot in GetDeckSearchRoots(deck))
			{
				foreach (Transform item in EnumerateNamedSlotsTransforms(deckSearchRoot))
				{
					if (TryBuildSlotsFromSlotsRoot(item, out var result))
					{
						slots = result;
						slotsRootUsed = item;
						return true;
					}
				}
			}
			return false;
		}

		public TextMeshProUGUI FindStyleTemplate(FFDataDeck deck, Transform slotsRoot)
		{
			if ((Object)(object)deck == (Object)null)
			{
				return null;
			}
			TextMeshProUGUI[] array = Il2CppArrayBase<TextMeshProUGUI>.op_Implicit(((Component)GetPrimarySearchRoot(deck)).GetComponentsInChildren<TextMeshProUGUI>(true));
			TextMeshProUGUI val = null;
			float num = -1f;
			TextMeshProUGUI[] array2 = array;
			foreach (TextMeshProUGUI val2 in array2)
			{
				if (!((Object)(object)val2 == (Object)null) && ((Component)val2).gameObject.activeInHierarchy && (!((Object)(object)slotsRoot != (Object)null) || !((TMP_Text)val2).transform.IsChildOf(slotsRoot)) && ((TMP_Text)val2).fontSize > num)
				{
					num = ((TMP_Text)val2).fontSize;
					val = val2;
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			for (int j = 0; j < array.Length; j++)
			{
				if ((Object)(object)array[j] != (Object)null)
				{
					return array[j];
				}
			}
			return null;
		}

		public static Transform GetPrimarySearchRoot(FFDataDeck deck)
		{
			if (!((Object)(object)deck.inventoryUIGroup != (Object)null))
			{
				return ((Component)deck).transform;
			}
			return ((Component)deck.inventoryUIGroup).transform;
		}

		private static IEnumerable<Transform> GetDeckSearchRoots(FFDataDeck deck)
		{
			if ((Object)(object)deck.inventoryUIGroup != (Object)null)
			{
				yield return ((Component)deck.inventoryUIGroup).transform;
			}
			yield return ((Component)deck).transform;
		}

		private static IEnumerable<Transform> EnumerateNamedSlotsTransforms(Transform searchRoot)
		{
			foreach (Transform componentsInChild in ((Component)searchRoot).GetComponentsInChildren<Transform>(true))
			{
				if (((Object)componentsInChild).name.Equals("slots", StringComparison.OrdinalIgnoreCase))
				{
					yield return componentsInChild;
				}
			}
		}

		private static bool TryBuildSlotsFromSlotsRoot(Transform slotsRoot, out IReadOnlyList<BoundSlot> result)
		{
			List<BoundSlot> list = new List<BoundSlot>(4);
			for (int i = 1; i <= 4; i++)
			{
				Transform val = FindSlotTransform(slotsRoot, i);
				if ((Object)(object)val == (Object)null)
				{
					result = Array.Empty<BoundSlot>();
					return false;
				}
				RectTransform component = ((Component)val).GetComponent<RectTransform>();
				RectTransform val2 = ResolveTooltipAnchor(val);
				if ((Object)(object)component == (Object)null || (Object)(object)val2 == (Object)null)
				{
					result = Array.Empty<BoundSlot>();
					return false;
				}
				list.Add(new BoundSlot(i, component, val2, ((Component)val).gameObject));
			}
			result = list;
			return true;
		}

		private static Transform FindSlotTransform(Transform slotsRoot, int index)
		{
			string text = "slot" + index;
			Transform val = FindDirectChild(slotsRoot, text, "Slot" + index);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			return FindDescendantByName(slotsRoot, text, skipRoot: true);
		}

		private static RectTransform ResolveTooltipAnchor(Transform slotRoot)
		{
			RectTransform val = TryRectTransform(FindDirectChild(slotRoot, "ui_item", "UI_Item"));
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			val = TryRectTransform(FindDescendantByName(slotRoot, "ui_item", skipRoot: false));
			return val ?? ((Component)slotRoot).GetComponent<RectTransform>();
		}

		private static RectTransform TryRectTransform(Transform transform)
		{
			if (!((Object)(object)transform != (Object)null))
			{
				return null;
			}
			return ((Component)transform).GetComponent<RectTransform>();
		}

		private static Transform FindDirectChild(Transform parent, params string[] names)
		{
			foreach (string text in names)
			{
				Transform val = parent.Find(text);
				if ((Object)(object)val != (Object)null)
				{
					return val;
				}
			}
			return null;
		}

		private static Transform FindDescendantByName(Transform root, string matchName, bool skipRoot)
		{
			foreach (Transform componentsInChild in ((Component)root).GetComponentsInChildren<Transform>(true))
			{
				if ((!skipRoot || !((Object)(object)componentsInChild == (Object)(object)root)) && ((Object)componentsInChild).name.Equals(matchName, StringComparison.OrdinalIgnoreCase))
				{
					return componentsInChild;
				}
			}
			return null;
		}
	}
	internal sealed class EquipSlotHoverTooltips
	{
		private readonly EquipSlotBinder binder;

		private readonly TooltipPresenter presenter = new TooltipPresenter();

		private readonly TooltipContentBuilder contentBuilder = new TooltipContentBuilder();

		private readonly ActiveDataDeckContext context;

		private BoundSlot hoveredSlot;

		private bool lastReveal;

		public EquipSlotHoverTooltips(EquipSlotBinder binder, ActiveDataDeckContext context)
		{
			this.binder = binder;
			this.context = context;
		}

		public void OnPointerEnter(BoundSlot slot)
		{
			hoveredSlot = slot;
			lastReveal = IsRevealKeyHeld();
			Refresh();
		}

		public void OnUpdate()
		{
			if (hoveredSlot != null)
			{
				bool flag = IsRevealKeyHeld();
				if (flag != lastReveal)
				{
					lastReveal = flag;
					Refresh();
				}
			}
		}

		public void OnPointerExit(BoundSlot slot)
		{
			if (hoveredSlot != null && hoveredSlot.SlotIndex == slot.SlotIndex)
			{
				hoveredSlot = null;
				presenter.Hide();
			}
		}

		public void ClearHover()
		{
			hoveredSlot = null;
			presenter.Hide();
		}

		public void Dispose()
		{
			presenter.Destroy();
		}

		public void RefreshHover()
		{
			if (hoveredSlot != null)
			{
				Refresh();
			}
		}

		private static bool IsRevealKeyHeld()
		{
			if (!Input.GetKey((KeyCode)304))
			{
				return Input.GetKey((KeyCode)303);
			}
			return true;
		}

		private void Refresh()
		{
			if ((Object)(object)context.Deck == (Object)null || (Object)(object)context.DeckCanvas == (Object)null || hoveredSlot == null)
			{
				presenter.Hide();
				return;
			}
			hoveredSlot.RefreshItemCache();
			if (!contentBuilder.TryBuild(hoveredSlot, lastReveal, out var data))
			{
				presenter.Hide();
				return;
			}
			FlavorData flavor = null;
			if (!context.TryGetLocalFlavor(out flavor))
			{
				FFPlayer current = FairyLocalPlayer.Current;
				flavor = ((current != null) ? current.Flavor : null);
			}
			presenter.Show(context.DeckCanvas, hoveredSlot.HoverRect, hoveredSlot.TooltipAnchor, data, 8f, 0f, binder.StyleTemplate, flavor);
		}
	}
	internal static class ShadowPropertySetter
	{
		private static readonly PropertyInfo EffectColorProperty = GetShadowProperty("effectColor");

		private static readonly PropertyInfo EffectDistanceProperty = GetShadowProperty("effectDistance");

		private static readonly PropertyInfo UseGraphicAlphaProperty = GetShadowProperty("useGraphicAlpha");

		public static void Set(Outline outline, string propertyName, object value)
		{
			if (!((Object)(object)outline == (Object)null))
			{
				Shadow val = ((Il2CppObjectBase)outline).TryCast<Shadow>();
				if (!((Object)(object)val == (Object)null))
				{
					ResolveProperty(propertyName)?.SetValue(val, value);
				}
			}
		}

		private static PropertyInfo ResolveProperty(string propertyName)
		{
			return propertyName switch
			{
				"effectColor" => EffectColorProperty, 
				"effectDistance" => EffectDistanceProperty, 
				"useGraphicAlpha" => UseGraphicAlphaProperty, 
				_ => null, 
			};
		}

		private static PropertyInfo GetShadowProperty(string propertyName)
		{
			return typeof(Shadow).GetProperty(propertyName, BindingFlags.Instance | BindingFlags.Public);
		}
	}
	internal sealed class TooltipContentBuilder
	{
		private readonly List<string> lines = new List<string>();

		public bool TryBuild(BoundSlot slot, bool reveal, out TooltipData data)
		{
			data = null;
			FFEquipment equipment = slot.Equipment;
			string text = EquipmentTitleResolver.Resolve(slot);
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			lines.Clear();
			if (string.Equals(text, "[NEW ITEM]", StringComparison.Ordinal))
			{
				lines.Add("Check for an update to FairyDust.Tooltip");
			}
			if ((Object)(object)equipment != (Object)null)
			{
				AppendEquipmentBodyLines(equipment, reveal);
			}
			data = new TooltipData(text, lines);
			return true;
		}

		private void AppendEquipmentBodyLines(FFEquipment equipment, bool reveal)
		{
			bool num = Config.Item.ShowDurability || reveal;
			bool flag = Config.Item.ShowMetal || reveal;
			bool flag2 = Config.Item.ShowWaterproof || reveal;
			bool flag3 = Config.Item.ShowEmp || reveal;
			if (num)
			{
				AppendDurability(equipment);
			}
			if (flag)
			{
				AppendMaterial(equipment);
			}
			if (flag2)
			{
				AppendWater(equipment);
			}
			if (flag3)
			{
				AppendEmp(equipment);
			}
		}

		private void AppendDurability(FFEquipment equipment)
		{
			if (TryGetDurability(equipment, out var remaining, out var max))
			{
				lines.Add((max > 0) ? $"Durability {remaining} / {max}" : $"Durability {remaining}");
			}
		}

		private void AppendMaterial(FFEquipment equipment)
		{
			if (equipment.IsMetal)
			{
				lines.Add("Metal");
			}
		}

		private void AppendWater(FFEquipment equipment)
		{
			lines.Add(equipment.DisabledWhenSubmerged ? "Not waterproof" : "Waterproof");
		}

		private void AppendEmp(FFEquipment equipment)
		{
			lines.Add(equipment.IsDisabledByEmp ? "Vulnerable to EMP" : "EMP safe");
		}

		private static bool TryGetDurability(FFEquipment equipment, out int remaining, out int max)
		{
			FFSprayTool val = ((Il2CppObjectBase)equipment).TryCast<FFSprayTool>();
			if ((Object)(object)val != (Object)null)
			{
				remaining = val.currentPaint;
				max = val.paint;
				if (max <= 0)
				{
					return remaining > 0;
				}
				return true;
			}
			if (equipment.useDurability && equipment.maxDurability > 0)
			{
				remaining = equipment.durability;
				max = equipment.maxDurability;
				return true;
			}
			remaining = 0;
			max = 0;
			return false;
		}
	}
	internal sealed class TooltipData
	{
		public string Title { get; }

		public IReadOnlyList<string> Lines { get; }

		public TooltipData(string title, IEnumerable<string> lines)
		{
			Title = title;
			Lines = lines?.ToArray() ?? Array.Empty<string>();
		}
	}
	internal sealed class TooltipPresenter
	{
		private const string RootName = "FairyDust_EquipTooltip";

		private const float TitleFontScaleFromTemplate = 0.65f;

		private const float MinTitleFontSize = 12f;

		private const float MaxTitleFontSize = 21f;

		private const float FallbackTitleFontSize = 14f;

		private const float DetailFontScale = 0.76f;

		private const int MaxMeasuredTextCacheEntries = 32;

		private readonly StringBuilder textBuilder = new StringBuilder();

		private readonly Dictionary<string, Vector2> measuredSizeByText = new Dictionary<string, Vector2>(StringComparer.Ordinal);

		private GameObject rootObject;

		private Canvas canvasRef;

		private RectTransform canvasRectCached;

		private RectTransform panelRect;

		private RectTransform accentRect;

		private CanvasGroup canvasGroup;

		private TextMeshProUGUI text;

		private Image panelBackground;

		private Outline panelOutline;

		private Image accentBar;

		private string lastRenderedText;

		public void Hide()
		{
			if ((Object)(object)rootObject != (Object)null && (Object)(object)canvasGroup != (Object)null)
			{
				canvasGroup.alpha = 0f;
				canvasGroup.interactable = false;
				canvasGroup.blocksRaycasts = false;
			}
		}

		public void Show(Canvas canvas, RectTransform slotRect, RectTransform iconAlignRect, TooltipData data, float offsetX, float offsetY, TextMeshProUGUI template, FlavorData flavor)
		{
			if ((Object)(object)canvas == (Object)null || (Object)(object)slotRect == (Object)null || data == null)
			{
				Hide();
				return;
			}
			EnsureCreated(canvas, template);
			ApplyFlavorStyling(flavor);
			bool needMeshRebuild = !rootObject.activeSelf;
			rootObject.SetActive(true);
			canvasGroup.alpha = 1f;
			UpdateText(data, needMeshRebuild);
			Position(slotRect, iconAlignRect, offsetX, offsetY);
		}

		private void EnsureCreated(Canvas canvas, TextMeshProUGUI template)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Expected O, but got Unknown
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Expected O, but got Unknown
			//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0241: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0282: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Expected O, but got Unknown
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)rootObject != (Object)null)
			{
				if ((Object)(object)rootObject.transform.parent != (Object)(object)((Component)canvas).transform)
				{
					rootObject.transform.SetParent(((Component)canvas).transform, false);
				}
				if (canvasRef != canvas)
				{
					canvasRef = canvas;
					canvasRectCached = ((Component)canvas).GetComponent<RectTransform>();
				}
				return;
			}
			canvasRef = canvas;
			canvasRectCached = ((Component)canvas).GetComponent<RectTransform>();
			rootObject = new GameObject("FairyDust_EquipTooltip");
			rootObject.transform.SetParent(((Component)canvas).transform, false);
			rootObject.SetActive(false);
			panelRect = rootObject.AddComponent<RectTransform>();
			canvasGroup = rootObject.AddComponent<CanvasGroup>();
			panelBackground = rootObject.AddComponent<Image>();
			panelOutline = rootObject.AddComponent<Outline>();
			panelRect.anchorMin = new Vector2(0.5f, 0.5f);
			panelRect.anchorMax = new Vector2(0.5f, 0.5f);
			panelRect.pivot = new Vector2(0f, 0.5f);
			panelRect.sizeDelta = new Vector2(180f, 64f);
			canvasGroup.alpha = 0f;
			canvasGroup.interactable = false;
			canvasGroup.blocksRaycasts = false;
			((Graphic)panelBackground).raycastTarget = false;
			ShadowPropertySetter.Set(panelOutline, "effectDistance", (object)new Vector2(1f, -1f));
			ShadowPropertySetter.Set(panelOutline, "useGraphicAlpha", true);
			GameObject val = new GameObject("Accent");
			val.transform.SetParent(rootObject.transform, false);
			accentRect = val.AddComponent<RectTransform>();
			accentRect.anchorMin = new Vector2(0f, 0f);
			accentRect.anchorMax = new Vector2(0f, 1f);
			accentRect.pivot = new Vector2(0f, 0.5f);
			accentRect.anchoredPosition = Vector2.zero;
			accentRect.sizeDelta = new Vector2(4f, 0f);
			accentBar = val.AddComponent<Image>();
			((Graphic)accentBar).raycastTarget = false;
			GameObject val2 = new GameObject("Text");
			val2.transform.SetParent(rootObject.transform, false);
			RectTransform obj = val2.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = new Vector2(14f, 6f);
			obj.offsetMax = new Vector2(-12f, -6f);
			text = val2.AddComponent<TextMeshProUGUI>();
			((Graphic)text).raycastTarget = false;
			((TMP_Text)text).enableWordWrapping = false;
			((TMP_Text)text).overflowMode = (TextOverflowModes)0;
			((TMP_Text)text).alignment = (TextAlignmentOptions)257;
			((TMP_Text)text).richText = true;
			((TMP_Text)text).lineSpacing = 0f;
			((TMP_Text)text).paragraphSpacing = 0f;
			((TMP_Text)text).fontStyle = (FontStyles)33;
			if ((Object)(object)template != (Object)null)
			{
				((TMP_Text)text).font = ((TMP_Text)template).font;
				((TMP_Text)text).fontSharedMaterial = ((TMP_Text)template).fontSharedMaterial;
				((TMP_Text)text).fontSize = Mathf.Clamp(((TMP_Text)template).fontSize * 0.65f, 12f, 21f);
			}
			else
			{
				((TMP_Text)text).fontSize = 14f;
			}
		}

		public void Destroy()
		{
			if ((Object)(object)rootObject != (Object)null)
			{
				Object.Destroy((Object)(object)rootObject);
			}
			rootObject = null;
			canvasRef = null;
			canvasRectCached = null;
			panelRect = null;
			accentRect = null;
			canvasGroup = null;
			text = null;
			panelBackground = null;
			panelOutline = null;
			accentBar = null;
			lastRenderedText = null;
			measuredSizeByText.Clear();
		}

		private void ApplyFlavorStyling(FlavorData flavor)
		{
			//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_002f: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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)
			//IL_0040: 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)
			Color primary;
			Color panelFill;
			if (flavor != null)
			{
				primary = flavor.color;
				primary.a = 1f;
				panelFill = PanelFillFromSource(flavor.DarkAccentColor, 0.42f);
			}
			else
			{
				primary = Color.white;
				panelFill = PanelFillFromSource(Color.white, 0.38f);
			}
			ApplyThemedChrome(primary, panelFill);
		}

		private static Color PanelFillFromSource(Color source, float mix)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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)
			return new Color(Mathf.Lerp(0.02f, source.r, mix), Mathf.Lerp(0.02f, source.g, mix), Mathf.Lerp(0.02f, source.b, mix), 0.96f);
		}

		private void ApplyThemedChrome(Color primary, Color panelFill)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			((Graphic)panelBackground).color = panelFill;
			ShadowPropertySetter.Set(panelOutline, "effectColor", (object)new Color(primary.r, primary.g, primary.b, 0.92f));
			((Graphic)accentBar).color = new Color(primary.r, primary.g, primary.b, 1f);
			((Graphic)text).color = new Color(primary.r, primary.g, primary.b, 1f);
		}

		private void UpdateText(TooltipData data, bool needMeshRebuild)
		{
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			string value = Mathf.Max(8f, ((TMP_Text)this.text).fontSize * 0.76f).ToString(CultureInfo.InvariantCulture);
			textBuilder.Clear();
			textBuilder.Append(data.Title);
			foreach (string line in data.Lines)
			{
				textBuilder.Append('\n');
				textBuilder.Append("<size=");
				textBuilder.Append(value);
				textBuilder.Append('>');
				textBuilder.Append(line);
				textBuilder.Append("</size>");
			}
			string text = textBuilder.ToString();
			bool flag = !string.Equals(lastRenderedText, text, StringComparison.Ordinal);
			if (flag)
			{
				((TMP_Text)this.text).text = text;
				lastRenderedText = text;
			}
			if (!needMeshRebuild && measuredSizeByText.TryGetValue(text, out var value2))
			{
				panelRect.sizeDelta = value2;
				return;
			}
			if (needMeshRebuild || flag)
			{
				((TMP_Text)this.text).ForceMeshUpdate(true, true);
			}
			Vector2 preferredValues = ((TMP_Text)this.text).GetPreferredValues(text);
			float num = Mathf.Clamp(preferredValues.x + 28f, 120f, 320f);
			float num2 = Mathf.Clamp(preferredValues.y + 12f, 30f, 180f);
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(num, num2);
			panelRect.sizeDelta = val;
			if (measuredSizeByText.Count < 32)
			{
				measuredSizeByText[text] = val;
			}
		}

		private void Position(RectTransform slotRect, RectTransform iconAlignRect, float offsetX, float offsetY)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: 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_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			Canvas val = canvasRef;
			RectTransform obj = canvasRectCached;
			Camera val2 = (((int)val.renderMode == 0) ? null : val.worldCamera);
			RectTransform val3 = (((Object)(object)iconAlignRect != (Object)null) ? iconAlignRect : slotRect);
			Rect rect = slotRect.rect;
			float xMax = ((Rect)(ref rect)).xMax;
			rect = slotRect.rect;
			Vector3 val4 = default(Vector3);
			((Vector3)(ref val4))..ctor(xMax, ((Rect)(ref rect)).center.y, 0f);
			rect = val3.rect;
			float x = ((Rect)(ref rect)).center.x;
			rect = val3.rect;
			Vector3 val5 = default(Vector3);
			((Vector3)(ref val5))..ctor(x, ((Rect)(ref rect)).center.y, 0f);
			Vector2 val6 = RectTransformUtility.WorldToScreenPoint(val2, ((Transform)slotRect).TransformPoint(val4));
			Vector2 val7 = RectTransformUtility.WorldToScreenPoint(val2, ((Transform)val3).TransformPoint(val5));
			Vector2 val8 = default(Vector2);
			((Vector2)(ref val8))..ctor(val6.x, val7.y);
			Vector2 val9 = default(Vector2);
			RectTransformUtility.ScreenPointToLocalPointInRectangle(obj, val8, val2, ref val9);
			Vector2 sizeDelta = panelRect.sizeDelta;
			val9.x += offsetX;
			val9.y += offsetY;
			Rect rect2 = obj.rect;
			val9.x = Mathf.Clamp(val9.x, ((Rect)(ref rect2)).xMin + 6f, ((Rect)(ref rect2)).xMax - sizeDelta.x - 6f);
			val9.y = Mathf.Clamp(val9.y, ((Rect)(ref rect2)).yMin + sizeDelta.y * 0.5f + 6f, ((Rect)(ref rect2)).yMax - sizeDelta.y * 0.5f - 6f);
			panelRect.anchoredPosition = val9;
		}
	}
}
namespace FairyDust.Tooltip.Features.InventoryTooltip.Patches
{
	internal static class FFDataDeckPatches
	{
		[HarmonyPatch(typeof(FFDataDeck), "OpenDataDeck")]
		internal static class OpenDataDeckPatch
		{
			[HarmonyPostfix]
			public static void Postfix(FFDataDeck __instance)
			{
				InventoryTooltipModule.Instance?.TryAdoptLocalDeck(__instance);
			}
		}

		[HarmonyPatch(typeof(FFDataDeck), "RefreshData")]
		internal static class RefreshDataPatch
		{
			[HarmonyPostfix]
			public static void Postfix(FFDataDeck __instance)
			{
				InventoryTooltipModule.Instance?.OnHarmonyDeckRefreshData(__instance);
			}
		}

		[HarmonyPatch(typeof(FFDataDeck), "ApplyFlavor")]
		internal static class ApplyFlavorPatch
		{
			[HarmonyPostfix]
			public static void Postfix(FFDataDeck __instance, FlavorData flavor)
			{
				InventoryTooltipModule.Instance?.RememberLocalFlavor(__instance, flavor);
			}
		}
	}
}
namespace FairyDust.Tooltip.Configuration
{
	public static class Config
	{
		private readonly struct DisplayNameReader
		{
			public Func<ItemPreferences, string> Read { get; }

			public DisplayNameReader(Func<ItemPreferences, string> read)
			{
				Read = read;
			}
		}

		private static readonly ItemPreferences DefaultItem = new ItemPreferences();

		private static readonly Dictionary<string, DisplayNameReader> DisplayNameReaders = BuildDisplayNameReaders();

		private static IFairyDustConfig<ItemPreferences> config;

		public static string ConfigFilePath { get; private set; } = string.Empty;


		public static ItemPreferences Item { get; private set; } = new ItemPreferences();


		public static void Initialize(IFairyDustHost host)
		{
			if (host == null)
			{
				throw new ArgumentNullException("host");
			}
			config = host.CreateConfig<ItemPreferences>("Item");
			config.Initialize();
			Item = config.Values;
			ConfigFilePath = config.FilePath;
		}

		public static void Save()
		{
			GetConfig().Save();
			Item = GetConfig().Values;
		}

		public static bool ReloadIfChanged()
		{
			bool num = GetConfig().ReloadIfChanged();
			if (num)
			{
				Item = GetConfig().Values;
			}
			return num;
		}

		public static string GetDisplayName(string itemName)
		{
			string text = TryGetName(Item, itemName);
			if (!string.IsNullOrWhiteSpace(text))
			{
				string text2 = TryGetName(DefaultItem, itemName);
				string text3 = text.Trim();
				if (!string.Equals(text3, text2?.Trim(), StringComparison.Ordinal))
				{
					return ItemDisplayNames.CleanDisplayTitle(text3);
				}
			}
			return TryGetCachedDisplayName(itemName);
		}

		public static void CacheDisplayName(IEnumerable<string> itemNames, string displayName)
		{
			if (string.IsNullOrWhiteSpace(displayName))
			{
				return;
			}
			string text = ItemDisplayNames.CleanDisplayTitle(displayName);
			if (string.IsNullOrWhiteSpace(text))
			{
				return;
			}
			string[] array = (from name in itemNames
				select NormalizeKey(ItemDisplayNames.CleanSourceName(name)) into key
				where key.Length > 0
				select key).Distinct<string>(StringComparer.Ordinal).ToArray();
			if (array.Length == 0)
			{
				return;
			}
			Dictionary<string, string> dictionary = DisplayNameCacheCodec.Parse(Item.DisplayNameCache);
			bool flag = false;
			string[] array2 = array;
			foreach (string key2 in array2)
			{
				if (!dictionary.TryGetValue(key2, out var value) || !string.Equals(value, text, StringComparison.Ordinal))
				{
					dictionary[key2] = text;
					flag = true;
				}
			}
			string text2 = DisplayNameCacheCodec.Serialize(dictionary);
			if (!string.Equals(Item.DisplayNameCache, text2, StringComparison.Ordinal))
			{
				Item.DisplayNameCache = text2;
				flag = true;
			}
			if (flag)
			{
				Save();
			}
		}

		private static string TryGetName(ItemPreferences p, string name)
		{
			if (name == null || !DisplayNameReaders.TryGetValue(NormalizeKey(name), out var value))
			{
				return null;
			}
			return value.Read(p);
		}

		private static Dictionary<string, DisplayNameReader> BuildDisplayNameReaders()
		{
			Dictionary<string, DisplayNameReader> dictionary = new Dictionary<string, DisplayNameReader>(StringComparer.Ordinal);
			FieldInfo[] fields = typeof(ItemPreferences).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (!(fieldInfo.FieldType != typeof(string)) && !(fieldInfo.Name == "DisplayNameCache"))
				{
					AddField(dictionary, fieldInfo);
				}
			}
			AddAlias(dictionary, "adrenaline", "Adrenaline_Shot");
			AddAlias(dictionary, "environment marker", "Spraymark");
			AddAlias(dictionary, "spray tool", "Spraymark");
			AddAlias(dictionary, "soda", "Noisemaker");
			AddAlias(dictionary, "soda can", "Noisemaker");
			AddAlias(dictionary, "gun", "Shotgun");
			AddAlias(dictionary, "uv", "UVLight");
			AddAlias(dictionary, "ultraviolet light", "UVLight");
			return dictionary;
		}

		private static void AddField(Dictionary<string, DisplayNameReader> readers, FieldInfo field)
		{
			Add(readers, field.Name, new DisplayNameReader((ItemPreferences p) => (string)field.GetValue(p)));
		}

		private static void AddAlias(Dictionary<string, DisplayNameReader> readers, string alias, string fieldName)
		{
			if (readers.TryGetValue(NormalizeKey(fieldName), out var value))
			{
				Add(readers, alias, value);
			}
		}

		private static void Add(Dictionary<string, DisplayNameReader> readers, string name, DisplayNameReader reader)
		{
			string text = NormalizeKey(name);
			if (text.Length > 0)
			{
				readers[text] = reader;
			}
		}

		internal static string NormalizeKey(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return string.Empty;
			}
			return new string(name.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray());
		}

		private static IFairyDustConfig<ItemPreferences> GetConfig()
		{
			return config ?? throw new InvalidOperationException("Config.Initialize() must be called before using preferences.");
		}

		private static string TryGetCachedDisplayName(string itemName)
		{
			string text = NormalizeKey(ItemDisplayNames.CleanSourceName(itemName));
			if (text.Length == 0)
			{
				return null;
			}
			if (!DisplayNameCacheCodec.Parse(Item.DisplayNameCache).TryGetValue(text, out var value) || string.IsNullOrWhiteSpace(value))
			{
				return null;
			}
			return ItemDisplayNames.CleanDisplayTitle(value);
		}
	}
	internal static class DisplayNameCacheCodec
	{
		public static Dictionary<string, string> Parse(string serialized)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			if (string.IsNullOrWhiteSpace(serialized))
			{
				return dictionary;
			}
			string[] array = serialized.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				int num = text.IndexOf('=');
				if (num > 0)
				{
					string text2 = Config.NormalizeKey(SafeUnescape(text.Substring(0, num)));
					string text3 = SafeUnescape(text.Substring(num + 1)).Trim();
					if (text2.Length > 0 && text3.Length > 0)
					{
						dictionary[text2] = text3;
					}
				}
			}
			return dictionary;
		}

		public static string Serialize(IReadOnlyDictionary<string, string> cache)
		{
			if (cache == null || cache.Count == 0)
			{
				return string.Empty;
			}
			return string.Join(";", from entry in cache.Where((KeyValuePair<string, string> entry) => entry.Key.Length > 0 && !string.IsNullOrWhiteSpace(entry.Value)).OrderBy<KeyValuePair<string, string>, string>((KeyValuePair<string, string> entry) => entry.Key, StringComparer.Ordinal)
				select Uri.EscapeDataString(entry.Key) + "=" + Uri.EscapeDataString(entry.Value.Trim()));
		}

		private static string SafeUnescape(string value)
		{
			try
			{
				return Uri.UnescapeDataString(value);
			}
			catch (UriFormatException)
			{
				return value;
			}
		}
	}
	public static class ItemDisplayNames
	{
		public const string NewItemTitle = "[NEW ITEM]";

		public const string NewItemDescription = "Check for an update to FairyDust.Tooltip";

		public static string GetTitle(string fairyName)
		{
			if (string.IsNullOrEmpty(fairyName))
			{
				return "[NEW ITEM]";
			}
			return GetConfiguredTitle(fairyName) ?? GetFallbackTitle(fairyName) ?? "[NEW ITEM]";
		}

		internal static string GetConfiguredTitle(string itemName)
		{
			return Config.GetDisplayName(itemName);
		}

		internal static string GetFallbackTitle(string itemName)
		{
			string text = CleanSourceName(itemName);
			if (string.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			return ToDisplayTitle(text);
		}

		internal static string CleanDisplayTitle(string displayTitle)
		{
			string text = CleanSourceName(displayTitle);
			if (string.IsNullOrWhiteSpace(text))
			{
				return null;
			}
			text = Regex.Replace(text, "\\s*[\\(\\[\\{][^\\)\\]\\}]*\\d+\\s*%[^\\)\\]\\}]*[\\)\\]\\}]\\s*", " ");
			text = Regex.Replace(text, "^\\s*(sale|discount)\\s*[-:]?\\s*\\d+\\s*%\\s*", string.Empty, RegexOptions.IgnoreCase);
			text = Regex.Replace(text, "\\s*[-:]*\\s*\\d+\\s*%\\s*(off|discount)?\\s*$", string.Empty, RegexOptions.IgnoreCase);
			return Regex.Replace(text, "\\s+", " ").Trim();
		}

		internal static string CleanSourceName(string itemName)
		{
			if (string.IsNullOrWhiteSpace(itemName))
			{
				return null;
			}
			return TrimPrefix(TrimSuffix(TrimSuffix(itemName.Trim(), "(Clone)"), "Clone"), "FF").Trim();
		}

		private static string ToDisplayTitle(string source)
		{
			string[] array = SeparateWords(source).Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0)
			{
				return null;
			}
			StringBuilder stringBuilder = new StringBuilder();
			TextInfo textInfo = CultureInfo.InvariantCulture.TextInfo;
			for (int i = 0; i < array.Length; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(' ');
				}
				stringBuilder.Append(FormatWord(array[i], textInfo));
			}
			return stringBuilder.ToString();
		}

		private static string SeparateWords(string source)
		{
			StringBuilder stringBuilder = new StringBuilder(source.Length + 8);
			for (int i = 0; i < source.Length; i++)
			{
				char c = source[i];
				char c2 = ((c == '_' || c == '-') ? ' ' : c);
				if (c2 == ' ')
				{
					AppendSingleSpace(stringBuilder);
					continue;
				}
				char previous = ((i > 0) ? source[i - 1] : '\0');
				char next = ((i + 1 < source.Length) ? source[i + 1] : '\0');
				if (stringBuilder.Length > 0 && ShouldStartNewWord(previous, c2, next))
				{
					AppendSingleSpace(stringBuilder);
				}
				stringBuilder.Append(c2);
			}
			return stringBuilder.ToString();
		}

		private static bool ShouldStartNewWord(char previous, char current, char next)
		{
			if ((!char.IsLower(previous) || !char.IsUpper(current)) && (!char.IsLetter(previous) || !char.IsDigit(current)) && (!char.IsDigit(previous) || !char.IsLetter(current)))
			{
				if (char.IsUpper(previous) && char.IsUpper(current))
				{
					return char.IsLower(next);
				}
				return false;
			}
			return true;
		}

		private static string FormatWord(string word, TextInfo textInfo)
		{
			string text = word.Trim();
			string text2 = Config.NormalizeKey(text);
			if (text2 == "uv" || text2 == "emp")
			{
				return text2.ToUpperInvariant();
			}
			if (text.Length <= 1)
			{
				return text.ToUpperInvariant();
			}
			return textInfo.ToTitleCase(text.ToLowerInvariant());
		}

		private static void AppendSingleSpace(StringBuilder builder)
		{
			if (builder.Length > 0 && builder[builder.Length - 1] != ' ')
			{
				builder.Append(' ');
			}
		}

		private static string TrimPrefix(string value, string prefix)
		{
			if (!value.StartsWith(prefix, StringComparison.Ordinal))
			{
				return value;
			}
			return value.Substring(prefix.Length);
		}

		private static string TrimSuffix(string value, string suffix)
		{
			if (!value.EndsWith(suffix, StringComparison.Ordinal))
			{
				return value;
			}
			return value.Substring(0, value.Length - suffix.Length);
		}
	}
	public sealed class ItemPreferences
	{
		public bool ShowMetal = true;

		public bool ShowWaterproof;

		public bool ShowEmp;

		public bool ShowDurability = true;

		public bool EnableInventoryTooltips = true;

		public string DisplayNameCache = "";

		public string Pickaxe = "Pickaxe";

		public string Flashlight = "Flashlight";

		public string Medkit = "Medkit";

		public string Lantern = "Lantern";

		public string Glowstick = "Glowstick";

		public string Adrenaline_Shot = "Adrenaline Shot";

		public string Walkietalkie = "Walkie-Talkie";

		public string Sledgehammer = "Sledgehammer";

		public string Boltcutters = "Boltcutters";

		public string Spraymark = "Environment Marker";

		public string Gasmask = "Gasmask";

		public string Stunlight = "Stun Light";

		public string Firecrackers = "Firecrackers";

		public string Rebreather = "Rebreather";

		public string Dynamite = "Dynamite";

		public string Noisemaker = "Soda Can";

		public string Shotgun = "Shotgun";

		public string Defib = "Defibrillator";

		public string Flare = "Flare";

		public string Pager = "Pager";

		public string UVLight = "UV Light";
	}
}
namespace FairyDust.Tooltip.BepInEx
{
	internal sealed class ConfigAdapter<T> : IFairyDustConfig<T> where T : new()
	{
		private readonly ConfigFile file;

		private readonly string sectionName;

		private readonly Dictionary<FieldInfo, ConfigEntryBase> entries = new Dictionary<FieldInfo, ConfigEntryBase>();

		private DateTime lastWriteUtc = DateTime.MinValue;

		public string FilePath => file.ConfigFilePath;

		public T Values { get; private set; } = new T();


		public ConfigAdapter(ConfigFile file, string sectionName)
		{
			this.file = file;
			this.sectionName = sectionName;
		}

		public void Initialize()
		{
			Directory.CreateDirectory(Path.GetDirectoryName(FilePath) ?? string.Empty);
			BindFields();
			ReadValues();
			file.Save();
			lastWriteUtc = GetWriteUtc();
		}

		public void Save()
		{
			foreach (KeyValuePair<FieldInfo, ConfigEntryBase> entry in entries)
			{
				entry.Value.BoxedValue = entry.Key.GetValue(Values);
			}
			file.Save();
			lastWriteUtc = GetWriteUtc();
		}

		public bool ReloadIfChanged()
		{
			DateTime writeUtc = GetWriteUtc();
			if (writeUtc == DateTime.MinValue || writeUtc <= lastWriteUtc)
			{
				return false;
			}
			T values;
			bool num = TryReadValuesFromFile(out values);
			lastWriteUtc = writeUtc;
			if (!num)
			{
				return false;
			}
			Values = values;
			return true;
		}

		private void BindFields()
		{
			FieldInfo[] fields = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				object value = fieldInfo.GetValue(Values);
				entries[fieldInfo] = BindField(fieldInfo, value);
			}
		}

		private ConfigEntryBase BindField(FieldInfo field, object defaultValue)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			return (ConfigEntryBase)typeof(ConfigFile).GetMethods().First((MethodInfo method) => method.Name == "Bind" && method.IsGenericMethodDefinition && method.GetParameters().Length == 4).MakeGenericMethod(field.FieldType)
				.Invoke(file, new object[4] { sectionName, field.Name, defaultValue, "FairyDust.Tooltip option." });
		}

		private void ReadValues()
		{
			T val = new T();
			foreach (KeyValuePair<FieldInfo, ConfigEntryBase> entry in entries)
			{
				entry.Key.SetValue(val, entry.Value.BoxedValue);
			}
			Values = val;
		}

		private bool TryReadValuesFromFile(out T values)
		{
			values = new T();
			if (!File.Exists(FilePath))
			{
				return false;
			}
			Dictionary<string, FieldInfo> dictionary = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public).ToDictionary<FieldInfo, string>((FieldInfo field) => field.Name, StringComparer.Ordinal);
			bool flag = false;
			string[] array = File.ReadAllLines(FilePath);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0 || text.StartsWith("#", StringComparison.Ordinal))
				{
					continue;
				}
				if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal))
				{
					flag = string.Equals(text.Substring(1, text.Length - 2).Trim(), sectionName, StringComparison.Ordinal);
				}
				else
				{
					if (!flag)
					{
						continue;
					}
					int num = text.IndexOf('=');
					if (num > 0)
					{
						string key = text.Substring(0, num).Trim();
						if (dictionary.TryGetValue(key, out var value) && TryConvert(text.Substring(num + 1).Trim(), value.FieldType, out var value2))
						{
							value.SetValue(values, value2);
						}
					}
				}
			}
			return true;
		}

		private static bool TryConvert(string text, Type targetType, out object value)
		{
			if (targetType == typeof(string))
			{
				value = text;
				return true;
			}
			if (targetType == typeof(bool) && bool.TryParse(text, out var result))
			{
				value = result;
				return true;
			}
			if (targetType == typeof(int) && int.TryParse(text, out var result2))
			{
				value = result2;
				return true;
			}
			value = null;
			return false;
		}

		private DateTime GetWriteUtc()
		{
			if (!File.Exists(FilePath))
			{
				return DateTime.MinValue;
			}
			return File.GetLastWriteTimeUtc(FilePath);
		}
	}
	internal sealed class HostAdapter : IFairyDustHost
	{
		private readonly BasePlugin plugin;

		private readonly Harmony harmony;

		public string LoaderName => "BepInEx";

		public string GameRootDirectory => Paths.GameRootPath;

		public string ConfigDirectory => Paths.ConfigPath;

		public IFairyDustLogger Log { get; }

		public HostAdapter(BasePlugin plugin)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			this.plugin = plugin;
			harmony = new Harmony("com.fairydust.tooltip");
			Log = new LoggerAdapter(plugin.Log);
		}

		public IFairyDustConfig<T> CreateConfig<T>(string categoryName) where T : new()
		{
			return new ConfigAdapter<T>(plugin.Config, categoryName);
		}

		public void PatchAll(Assembly assembly)
		{
			harmony.PatchAll(assembly);
		}

		public void UnpatchSelf()
		{
			harmony.UnpatchSelf();
		}
	}
	internal sealed class LoggerAdapter : IFairyDustLogger
	{
		private readonly ManualLogSource logger;

		public LoggerAdapter(ManualLogSource logger)
		{
			this.logger = logger;
		}

		public void Info(string message)
		{
			logger.LogInfo((object)message);
		}

		public void Warning(string message)
		{
			logger.LogWarning((object)message);
		}

		public void Error(string message)
		{
			logger.LogError((object)message);
		}

		public void Error(string message, Exception exception)
		{
			logger.LogError((object)(message + Environment.NewLine + exception));
		}
	}
	[BepInPlugin("com.fairydust.tooltip", "FairyDust.Tooltip", "2.0.0")]
	[BepInProcess("Forsaken Frontiers.exe")]
	public sealed class Plugin : BasePlugin
	{
		private FairyDustTooltipMod mod;

		private UpdateBridge updateBridge;

		public override void Load()
		{
			mod = new FairyDustTooltipMod(new HostAdapter((BasePlugin)(object)this));
			mod.Initialize();
			RuntimeState.Mod = mod;
			updateBridge = ((BasePlugin)this).AddComponent<UpdateBridge>();
		}

		public override bool Unload()
		{
			if ((Object)(object)updateBridge != (Object)null)
			{
				updateBridge.DetachRuntime();
				Object.Destroy((Object)(object)updateBridge);
				updateBridge = null;
			}
			mod?.Shutdown();
			if (RuntimeState.Mod == mod)
			{
				RuntimeState.Mod = null;
			}
			mod = null;
			return true;
		}
	}
	internal static class RuntimeState
	{
		public static FairyDustTooltipMod Mod { get; set; }
	}
	public sealed class UpdateBridge : MonoBehaviour
	{
		private FairyDustTooltipMod mod;

		private int lastSceneBuildIndex = int.MinValue;

		private string lastSceneName = string.Empty;

		private void Awake()
		{
			mod = RuntimeState.Mod;
			ForwardActiveSceneIfChanged();
		}

		private void Update()
		{
			ForwardActiveSceneIfChanged();
			mod?.OnUpdate();
		}

		public void DetachRuntime()
		{
			if (RuntimeState.Mod == mod)
			{
				RuntimeState.Mod = null;
			}
			mod = null;
		}

		private void OnDestroy()
		{
			DetachRuntime();
		}

		private void ForwardActiveSceneIfChanged()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			if (((Scene)(ref activeScene)).buildIndex != lastSceneBuildIndex || !(((Scene)(ref activeScene)).name == lastSceneName))
			{
				lastSceneBuildIndex = ((Scene)(ref activeScene)).buildIndex;
				lastSceneName = ((Scene)(ref activeScene)).name;
				mod?.OnSceneWasLoaded(((Scene)(ref activeScene)).buildIndex, ((Scene)(ref activeScene)).name);
			}
		}
	}
}