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);
}
}
}
}