using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Microsoft.CodeAnalysis; using ProjectM.UI; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("PotionPool")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Client-only V Rising plugin: hovering a Blood Potion shows the Blood Pool I-V bonus lines for its type and quality.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("PotionPool")] [assembly: AssemblyTitle("PotionPool")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace PotionPool { internal static class BloodBonuses { internal const string Cyan = "#5EC8D4"; internal const string Locked = "#5A6270"; internal const string Mute = "#8B95A3"; internal const string VText = "All bonuses above increased by +20%, including the blood effect cap."; internal static readonly float[] Unlock = new float[5] { 0f, 30f, 60f, 90f, 100f }; internal static bool TryParse(string blob, out string type, out float quality) { type = null; quality = 0f; if (string.IsNullOrEmpty(blob)) { return false; } type = MatchType(blob); quality = MatchQuality(blob); if (type != null) { return quality > 0f; } return false; } internal static string Build(string type, float quality) { string text = Normalize(type); string[] array; char c; switch (text) { case "frail": case "frailed": return ""; default: switch (text.Length) { case 8: break; case 6: goto IL_007b; case 5: goto IL_009b; case 7: goto IL_00bb; case 9: goto IL_0183; default: goto IL_01ec; } c = text[0]; if (c != 'c') { if (c != 'd') { if (c == 'i' && text == "immortal") { array = Immortal(quality); break; } } else if (text == "draculin") { array = Draculin(quality); break; } } else if (text == "creature") { array = Creature(quality); break; } goto IL_01ec; case null: goto IL_01ec; IL_0183: if (!(text == "corrupted")) { goto IL_01ec; } array = Corrupted(quality); break; IL_007b: c = text[0]; if (c != 'm') { if (c == 'w' && text == "worker") { array = Worker(quality); break; } } else if (text == "mutant") { array = Mutant(quality); break; } goto IL_01ec; IL_00bb: c = text[0]; if (c != 's') { if (c == 'w' && text == "warrior") { array = Warrior(quality); break; } } else if (text == "scholar") { array = Scholar(quality); break; } goto IL_01ec; IL_01ec: array = null; break; IL_009b: c = text[0]; if (c != 'b') { if (c == 'r' && text == "rogue") { array = Rogue(quality); break; } } else if (text == "brute") { array = Brute(quality); break; } goto IL_01ec; } string[] array2 = array; if (array2 == null) { return FallbackGates(); } return string.Join("\n", array2); } private static string[] Creature(float q) { return new string[5] { L(1, q, N(q, 0f, 8f, 16f) + " maximum health. " + Lit("+40%") + " health regeneration."), L(2, q, N(q, 30f, 4f, 8f) + " movement speed. " + Lit("+8%") + " shapeshift speed."), L(3, q, N(q, 60f, 12f, 24f) + " healing received. " + Lit("+20") + " all resistances."), L(4, q, "Bite heals 5% of maximum health and grants " + Lit("+25%") + " attack speed and physical damage."), V(q) }; } private static string[] Worker(float q) { return new string[5] { L(1, q, N(q, 0f, 12f, 24f) + " resource yield."), L(2, q, N(q, 30f, 12f, 24f) + " damage to resource objects."), L(3, q, N(q, 60f, 8f, 16f) + " mount speed."), L(4, q, N(q, 90f, 3f, 3f) + " chance to instantly destroy a resource node and trigger a burst of speed."), V(q) }; } private static string[] Brute(float q) { return new string[5] { L(1, q, N(q, 0f, 7f, 14f) + " primary attack speed. " + Lit("+5%") + " physical power."), L(2, q, N(q, 30f, 5f, 10f) + " primary attack life leech. " + Lit("+4%") + " damage reduction."), L(3, q, N(q, 60f, 8f, 16f) + " maximum health. Heal for 5% of victim health on killing blow."), L(4, q, Lit("15%") + " chance to parry, reducing damage taken by 25%. Parrying grants " + Lit("+15%") + " physical power for 3.5s."), V(q) }; } private static string[] Rogue(float q) { return new string[5] { L(1, q, N(q, 0f, 8f, 16f) + " physical critical chance. " + Lit("+8%") + " physical critical power."), L(2, q, N(q, 30f, 4f, 8f) + " movement speed. " + Lit("+10%") + " additional speed for 3s on killing blow."), L(3, q, N(q, 60f, 7f, 14f) + " veil cooldown rate. 100% physical critical chance after using a Veil."), L(4, q, "50% chance on critical strike to expose armor, " + Lit("+15%") + " damage taken for 4s."), V(q) }; } private static string[] Warrior(float q) { return new string[5] { L(1, q, Lit("+10%") + " physical power. Veil attacks deal " + Lit("+40%") + " damage."), L(2, q, Lit("+14%") + " weapon cooldown rate. " + Lit("+5%") + " weapon skill leech."), L(3, q, Lit("+18%") + " weapon skill power. " + Lit("+24%") + " damage to full-health enemies."), L(4, q, Lit("+12%") + " weapon charge gain."), V(q) }; } private static string[] Scholar(float q) { return new string[5] { L(1, q, N(q, 0f, 6f, 12f) + " spell power. " + Lit("+10%") + " shield efficiency."), L(2, q, N(q, 30f, 6f, 12f) + " spell cooldown rate. Shield self for 25% of spell power when casting."), L(3, q, N(q, 60f, 10f, 20f) + " ultimate power. Reset spell cooldowns when using an Ultimate."), L(4, q, Lit("+12") + " spell charge gain."), V(q) }; } private static string[] Mutant(float q) { return new string[5] { L(1, q, N(q, 0f, 7f, 14f) + " spell power. " + Lit("+10%") + " shapeshift speed."), L(2, q, N(q, 30f, 14f, 28f) + " ultimate cooldown rate. Spawn 2 Mutant Rats when casting an Ultimate."), L(3, q, N(q, 60f, 10f, 20f) + " ultimate power. " + Lit("+12%") + " minion damage."), L(4, q, Lit("+7%") + " veil cooldown rate. Veil attacks reduce ultimate cooldown by 7s."), V(q) }; } private static string[] Draculin(float q) { return new string[5] { L(1, q, N(q, 0f, 6f, 12f) + " spell power. " + Lit("+8%") + " spell critical power."), L(2, q, N(q, 30f, 8f, 16f) + " spell critical chance. Spell hits grant " + Lit("+6%") + " movement speed for 3s."), L(3, q, N(q, 60f, 4f, 8f) + " spell leech. " + Lit("+3") + " spell charge gain."), L(4, q, "Spell critical strikes increase spell cooldown rate by " + Lit("+30%") + " for 3s."), V(q) }; } private static string[] Corrupted(float q) { return new string[5] { L(1, q, N(q, 0f, 25f, 50f) + " reduced damage from Corrupted attacks. " + Lit("+5%") + " life leech, but " + Lit("+10%") + " damage taken from other sources."), L(2, q, "Vile Corruption stack cap " + N(q, 30f, 4f, 8f, percent: false) + ", but " + N(q, 30f, 50f, 100f) + " increased blood drain."), L(3, q, N(q, 60f, 8f, 12f) + " movement speed. " + Lit("+14%") + " attack speed. 25% chance on hit taken to erupt and snare for 2s."), L(4, q, Lit("+10") + " spell and weapon charge gain. 30% chance to spawn a shadow that attacks you when using a spell."), V(q) }; } private static string[] Immortal(float q) { return new string[5] { L(1, q, Lit("+100%") + " physical power and spell power."), L(2, q, Lit("+15%") + " movement speed. " + Lit("+30%") + " primary attack speed."), L(3, q, Lit("+40%") + " damage reduction."), L(4, q, Lit("+100%") + " veil cooldown rate."), L(5, q, Lit("+40") + " spell and weapon charge gain.") }; } private static string V(float q) { return L(5, q, "All bonuses above increased by +20%, including the blood effect cap."); } private static string Lit(string s) { return "" + s + ""; } private static string FallbackGates() { return "I 0%+\nII 30%+\nIII 60%+\nIV 90%+\nV 100%"; } private static string L(int tier, float quality, string body) { string text = tier switch { 1 => "I", 2 => "II", 3 => "III", 4 => "IV", _ => "V", } + " " + tier switch { 1 => "0%", 2 => "30%", 3 => "60%", 4 => "90%", _ => "100%", }; bool num = quality + 0.001f >= Unlock[tier - 1]; string text2 = (num ? "#8B95A3" : "#5A6270"); string text3 = (num ? body : ("" + StripColors(body) + "")); return "" + text + "" + text3; } internal static string N(float quality, float thresh, float min, float max, bool percent = true) { float num = ((quality >= 100f) ? 1f : ((!(quality <= thresh) && !(max <= min)) ? ((quality - thresh) / (100f - thresh)) : 0f)); if (num < 0f) { num = 0f; } if (num > 1f) { num = 1f; } float num2 = min + num * (max - min); string text = ((num2 > 0f) ? "+" : ""); return "" + text + Fmt(num2) + (percent ? "%" : "") + ""; } private static string Fmt(float v) { double num = Math.Round(v, 1); if (Math.Abs(num - Math.Round(num)) < 0.049) { return ((int)Math.Round(num)).ToString(); } return num.ToString("0.0"); } private static string StripColors(string s) { if (string.IsNullOrEmpty(s)) { return s; } while (true) { int num = s.IndexOf("", num, StringComparison.Ordinal); if (num2 < 0) { break; } s = s.Remove(num, num2 - num + 1); } return s.Replace("", ""); } private static string Normalize(string type) { if (string.IsNullOrEmpty(type)) { return ""; } string text = type.Trim().ToLowerInvariant(); if (text.StartsWith("blood of the ")) { text = text.Substring(13); } if (text.StartsWith("blood type")) { text = text.Substring(10).Trim().TrimStart(':', ' '); } if (text == "frailed") { text = "frail"; } return text; } private static string MatchType(string blob) { string[] array = new string[13] { "Blood of the Immortal", "Immortal", "Draculin", "Corrupted", "Creature", "Worker", "Brute", "Rogue", "Warrior", "Scholar", "Mutant", "Frailed", "Frail" }; int num = IndexOfIgnore(blob, "Blood Type:"); if (num >= 0) { int num2 = num + "Blood Type:".Length; int i; for (i = num2; i < blob.Length && blob[i] != '\n' && blob[i] != '\r'; i++) { } string s = blob.Substring(num2, i - num2).Trim(); s = StripTags(s); int num3 = s.IndexOf('/'); if (num3 > 0) { s = s.Substring(0, num3).Trim(); } string text = MatchType(s); if (text != null) { return text; } if (s.Length > 0) { return s; } } string[] array2 = array; foreach (string text2 in array2) { if (IndexOfIgnore(blob, text2) >= 0) { return text2; } } return null; } private static float MatchQuality(string blob) { int num = IndexOfIgnore(blob, "Blood Quality:"); if (num >= 0) { int i = num + "Blood Quality:".Length; i = SkipSpacesAndTags(blob, i); float num2 = ReadNumber(blob, i); if (num2 > 0f) { return num2; } } int num3 = -1; float result = 0f; for (int j = 0; j < blob.Length; j++) { if (blob[j] == '%') { int num4 = j - 1; while (num4 >= 0 && (char.IsDigit(blob[num4]) || blob[num4] == '.' || blob[num4] == ' ')) { num4--; } float num5 = ReadNumber(blob, num4 + 1); if (!(num5 <= 0f) && !(num5 > 100f) && j > num3) { num3 = j; result = num5; } } } return result; } private static int SkipSpacesAndTags(string s, int i) { while (i < s.Length) { switch (s[i]) { case '\t': case ' ': case ':': i++; continue; case '<': { int num = s.IndexOf('>', i); if (num >= 0) { i = num + 1; continue; } break; } } break; } return i; } private static string StripTags(string s) { if (string.IsNullOrEmpty(s) || s.IndexOf('<') < 0) { return s; } StringBuilder stringBuilder = new StringBuilder(s.Length); bool flag = false; foreach (char c in s) { switch (c) { case '<': flag = true; continue; case '>': flag = false; continue; } if (!flag) { stringBuilder.Append(c); } } return stringBuilder.ToString().Trim(); } private static float ReadNumber(string s, int i) { while (i < s.Length && (s[i] == ' ' || s[i] == '\t' || s[i] == ':')) { i++; } int num = i; bool flag = false; while (i < s.Length && (char.IsDigit(s[i]) || (s[i] == '.' && !flag))) { if (s[i] == '.') { flag = true; } i++; } if (i == num) { return 0f; } if (float.TryParse(s.Substring(num, i - num), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } return 0f; } private static int IndexOfIgnore(string hay, string needle) { return hay.IndexOf(needle, StringComparison.OrdinalIgnoreCase); } } [HarmonyPatch(typeof(GridSelectionEntry), "OnPointerEnter")] internal static class ItemGridPointerEnterPatch { [HarmonyPostfix] private static void Postfix(GridSelectionEntry __instance) { //IL_005c: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance == (Object)null || ((Il2CppObjectBase)__instance).WasCollected) { return; } ItemGridSelectionEntry val = ((Il2CppObjectBase)__instance).TryCast(); if ((Object)(object)val == (Object)null || ((Il2CppObjectBase)val).WasCollected) { ThresholdAppender.CancelIfNotPotion("non-item-grid enter"); return; } Data slotData = val.SlotData; if (slotData == null || slotData.IsEmpty) { ThresholdAppender.CancelIfNotPotion("empty-slot enter"); return; } int value = slotData.EntryId._Value; if (!PotionGuids.IsBloodPotion(value)) { ThresholdAppender.CancelIfNotPotion("non-potion enter guid=" + value); } else { ThresholdAppender.Request(((Il2CppObjectBase)val).Pointer, value, "Grid.OnPointerEnter"); } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("PotionPool OnPointerEnter postfix: " + ex.Message)); } } } } [HarmonyPatch(typeof(GridSelectionEntry), "OnPointerExit")] internal static class ItemGridPointerExitPatch { [HarmonyPostfix] private static void Postfix(GridSelectionEntry __instance) { try { if (!((Object)(object)__instance == (Object)null) && !((Il2CppObjectBase)__instance).WasCollected) { ItemGridSelectionEntry val = ((Il2CppObjectBase)__instance).TryCast(); if (!((Object)(object)val == (Object)null) && !((Il2CppObjectBase)val).WasCollected) { ThresholdAppender.NotifyPointerExit(((Il2CppObjectBase)val).Pointer); } } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("PotionPool OnPointerExit postfix: " + ex.Message)); } } } } [HarmonyPatch(typeof(ActionBarEntry), "OnPointerEnter")] internal static class ActionBarPointerEnterPatch { [HarmonyPostfix] private static void Postfix(ActionBarEntry __instance) { //IL_002a: 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) try { if ((Object)(object)__instance == (Object)null || ((Il2CppObjectBase)__instance).WasCollected) { return; } int num = 0; try { num = __instance.EntryId._Value; } catch { } if (num == 0) { try { num = __instance._CurrentItemType._Value; } catch { } } if (!PotionGuids.IsBloodPotion(num)) { ThresholdAppender.CancelIfNotPotion("non-potion actionbar guid=" + num); } else { ThresholdAppender.Request(((Il2CppObjectBase)__instance).Pointer, num, "ActionBar.OnPointerEnter"); } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("PotionPool ActionBar OnPointerEnter: " + ex.Message)); } } } } [HarmonyPatch(typeof(ActionBarEntry), "OnPointerExit")] internal static class ActionBarPointerExitPatch { [HarmonyPostfix] private static void Postfix(ActionBarEntry __instance) { try { if (!((Object)(object)__instance == (Object)null) && !((Il2CppObjectBase)__instance).WasCollected) { ThresholdAppender.NotifyPointerExit(((Il2CppObjectBase)__instance).Pointer); } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("PotionPool ActionBar OnPointerExit: " + ex.Message)); } } } } [HarmonyPatch(typeof(LocalizedText), "LateUpdate")] internal static class LocalizedTextLateUpdatePatch { [HarmonyPrefix] private static bool Prefix(LocalizedText __instance) { try { return !ThresholdAppender.ShouldStopBinder(__instance); } catch { return true; } } [HarmonyPostfix] private static void Postfix(LocalizedText __instance) { try { ThresholdAppender.NotifyBinderTick(__instance); } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("PotionPool LocalizedText LateUpdate: " + ex.Message)); } } } } [BepInPlugin("fangly.PotionPool", "PotionPool", "1.0.0")] [BepInProcess("VRising.exe")] public class Plugin : BasePlugin { internal const string GUID = "fangly.PotionPool"; internal const string NAME = "PotionPool"; internal const string VERSION = "1.0.0"; private Harmony _harmony; private ThresholdAppender _appender; internal static Plugin Instance { get; private set; } internal static ManualLogSource Logger { get; private set; } public override void Load() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown Instance = this; Logger = ((BasePlugin)this).Log; if (Application.productName == "VRisingServer") { ((BasePlugin)this).Log.LogInfo((object)"PotionPool is a client-only plugin; not loading on VRisingServer."); return; } ClassInjector.RegisterTypeInIl2Cpp(); _appender = ((BasePlugin)this).AddComponent(); _harmony = new Harmony("fangly.PotionPool"); _harmony.PatchAll(typeof(ItemGridPointerEnterPatch)); _harmony.PatchAll(typeof(ItemGridPointerExitPatch)); _harmony.PatchAll(typeof(ActionBarPointerEnterPatch)); _harmony.PatchAll(typeof(ActionBarPointerExitPatch)); _harmony.PatchAll(typeof(LocalizedTextLateUpdatePatch)); ((BasePlugin)this).Log.LogInfo((object)"PotionPool 1.0.0 loaded (client). Inventory GridSelectionEntry + hotbar ActionBarEntry. Potion hover shows Blood Pool I-V bonus lines. FakeTooltip is NOT Harmony-patched."); } public override bool Unload() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)_appender != (Object)null) { Object.Destroy((Object)(object)_appender); _appender = null; } return true; } } internal static class PluginInfo { public const string GUID = "fangly.PotionPool"; public const string Name = "PotionPool"; public const string Version = "1.0.0"; } internal static class PotionGuids { internal const int PrisonPotion = 828432508; internal const int PrisonPotionBloodwine = 1223264867; internal const int PrisonPotionMixed = 2063723255; private static readonly HashSet Hashes = new HashSet { 828432508, 1223264867, 2063723255 }; internal static bool IsBloodPotion(int guidHash) { if (guidHash != 0) { return Hashes.Contains(guidHash); } return false; } } internal static class Preview { internal const string Marker = "\n"; internal const string OldMarker80 = "\n"; internal const string OldMarker90 = "\n\n"; internal const string VisibleAnchor = "I 0%"; internal const string BonusAnchor = "All bonuses above increased"; internal const string OldBonusAnchor = "Boost all above effects"; internal static bool HasMarker(string text) { if (string.IsNullOrEmpty(text)) { return false; } if (text.IndexOf("\n", StringComparison.Ordinal) >= 0) { return true; } if (text.IndexOf("All bonuses above increased", StringComparison.Ordinal) >= 0) { return true; } if (text.IndexOf("Boost all above effects", StringComparison.Ordinal) >= 0) { return true; } return text.IndexOf("I 0%", StringComparison.Ordinal) >= 0; } internal static string Append(string existing, string type, float quality) { string text = BloodBonuses.Build(type, quality); if (string.IsNullOrEmpty(text)) { return Strip(existing); } string text2 = Strip(existing).TrimEnd(); if (text2.Length > 0 && text2.IndexOf("", StringComparison.OrdinalIgnoreCase) < 0) { text2 = "" + text2 + ""; } return text2 + "\n" + text + ""; } internal static string Strip(string text) { if (string.IsNullOrEmpty(text)) { return ""; } int num = text.IndexOf("\n", StringComparison.Ordinal); if (num < 0) { num = text.IndexOf("\n\n", StringComparison.Ordinal); } if (num < 0) { num = text.IndexOf("\n", StringComparison.Ordinal); } if (num < 0) { num = text.IndexOf("\nI 0%", StringComparison.Ordinal); } if (num < 0) { num = text.IndexOf("\nI ", StringComparison.Ordinal); } if (num < 0) { num = text.IndexOf("I 0%", StringComparison.Ordinal); } if (num >= 0) { return text.Substring(0, num); } return text; } } public class ThresholdAppender : MonoBehaviour { private const int MaxWatchFrames = 3600; private const int TooltipGoneFrames = 12; private static bool _pending; private static int _frames; private static int _tooltipGoneFrames; private static int _appendsThisHover; private static int _rebuildsThisHover; private static bool _loggedStart; private static bool _loggedApply; private static bool _loggedRebuild; private static bool _loggedFail; private static bool _loggedBinder; private static bool _hadMarkerLast; private static int _guid; private static string _source; private static string _fieldName; private static FakeTooltip _cached; private static IntPtr _hoverPtr; private static LocalizedText _appliedLoc; private static TextMeshProUGUI _appliedTmp; private static bool _binderStopped; private static bool _locWasEnabled; private static bool _clippersHeld; private static string _bloodType; private static float _bloodQuality; private static bool _loggedMeta; private static string _lastBlock; private static readonly List Clippers = new List(8); private static readonly List ClipperWasEnabled = new List(8); private UnityAction _beforeRender; private WillRenderCanvases _preWillRender; private WillRenderCanvases _willRender; private static int _pollFrames; private static bool _loggedPoll; private static FakeTooltip[] _pollCache; private static float _pollCacheAt = -100f; private static CanvasGroup _raycastGroup; private static bool _raycastGroupAdded; private static bool _raycastWasBlocking; private static bool _loggedRaycast; public ThresholdAppender(IntPtr ptr) : base(ptr) { } internal static void Request(IntPtr hoverPtr, int guid, string source) { if (!_pending || !(hoverPtr != IntPtr.Zero) || !(_hoverPtr == hoverPtr) || _guid != guid) { ResetHoverState(); } _pending = true; _frames = 0; _tooltipGoneFrames = 0; _hoverPtr = hoverPtr; _guid = guid; _source = source; _cached = null; if (!_loggedStart) { _loggedStart = true; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)("PotionPool 1.0.0 hover start guid=" + guid + " source=" + source)); } } } internal static void NotifyPointerExit(IntPtr hoverPtr) { if (_pending && !(hoverPtr == IntPtr.Zero) && !(_hoverPtr != hoverPtr)) { StopAndStrip("OnPointerExit"); } } internal static void CancelIfNotPotion(string why) { if (_pending) { StopAndStrip(why); } } internal static bool ShouldStopBinder(LocalizedText loc) { if (!_pending || !_binderStopped || (Object)(object)loc == (Object)null) { return false; } if ((Object)(object)_appliedLoc == (Object)null || ((Il2CppObjectBase)_appliedLoc).WasCollected) { return false; } return ((Il2CppObjectBase)loc).Pointer == ((Il2CppObjectBase)_appliedLoc).Pointer; } internal static void NotifyBinderTick(LocalizedText loc) { if (_pending && !((Object)(object)loc == (Object)null) && !((Object)(object)_appliedLoc == (Object)null) && !((Il2CppObjectBase)_appliedLoc).WasCollected && !(((Il2CppObjectBase)loc).Pointer != ((Il2CppObjectBase)_appliedLoc).Pointer)) { Tick("LocalizedText.LateUpdate"); } } private static void ResetHoverState() { try { StripApplied(); } catch { } RestoreBinderAndClippers(); _appendsThisHover = 0; _rebuildsThisHover = 0; _loggedStart = false; _loggedApply = false; _loggedRebuild = false; _loggedFail = false; _loggedBinder = false; _loggedMeta = false; _hadMarkerLast = false; _bloodType = null; _bloodQuality = 0f; _lastBlock = null; _fieldName = null; _appliedLoc = null; _appliedTmp = null; _binderStopped = false; _locWasEnabled = false; _clippersHeld = false; } private static void StopAndStrip(string why) { try { bool flag = StripApplied(); ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)("PotionPool 1.0.0 hover stop guid=" + _guid + " why=" + why + " stripped=" + flag + " appends=" + _appendsThisHover + " rebuilt=" + _rebuildsThisHover + " binderStopped=" + _binderStopped)); } } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)("PotionPool stop: " + ex.Message)); } } finally { RestoreBinderAndClippers(); _pending = false; _hoverPtr = IntPtr.Zero; _cached = null; _appliedLoc = null; _appliedTmp = null; ResetHoverState(); } } private void OnEnable() { try { _beforeRender = UnityAction.op_Implicit((Action)OnBeforeRender); Application.onBeforeRender += _beforeRender; } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("PotionPool onBeforeRender subscribe: " + ex.Message)); } } try { Action action = OnPreWillRenderCanvases; _preWillRender = WillRenderCanvases.op_Implicit(action); Canvas.preWillRenderCanvases += _preWillRender; } catch (Exception ex2) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)("PotionPool preWillRenderCanvases subscribe: " + ex2.Message)); } } try { Action action2 = OnWillRenderCanvases; _willRender = WillRenderCanvases.op_Implicit(action2); Canvas.willRenderCanvases += _willRender; } catch (Exception ex3) { ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogDebug((object)("PotionPool willRenderCanvases subscribe: " + ex3.Message)); } } } private void OnDisable() { try { if ((Delegate)(object)_beforeRender != (Delegate)null) { Application.onBeforeRender -= _beforeRender; } } catch { } try { if ((Delegate)(object)_preWillRender != (Delegate)null) { Canvas.preWillRenderCanvases -= _preWillRender; } } catch { } try { if ((Delegate)(object)_willRender != (Delegate)null) { Canvas.willRenderCanvases -= _willRender; } } catch { } if (_pending) { StopAndStrip("appender-disable"); } } private void LateUpdate() { if (!_pending && ++_pollFrames >= 20) { _pollFrames = 0; PollTooltip(); } Tick("LateUpdate"); } private static void PollTooltip() { try { float unscaledTime = Time.unscaledTime; if (_pollCache == null || unscaledTime - _pollCacheAt > 5f) { _pollCache = Il2CppArrayBase.op_Implicit(Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0)); _pollCacheAt = unscaledTime; } FakeTooltip[] pollCache = _pollCache; if (pollCache == null) { return; } FakeTooltip[] array = pollCache; foreach (FakeTooltip val in array) { if ((Object)(object)val == (Object)null || ((Il2CppObjectBase)val).WasCollected || !((Component)val).gameObject.activeInHierarchy) { continue; } LocalizedText bloodPotion = val.BloodPotion; if ((Object)(object)bloodPotion == (Object)null || ((Il2CppObjectBase)bloodPotion).WasCollected || !((Component)bloodPotion).gameObject.activeInHierarchy) { continue; } string value = null; try { value = (((Object)(object)bloodPotion.Text != (Object)null) ? ((TMP_Text)bloodPotion.Text).text : null); } catch { } if (string.IsNullOrWhiteSpace(value)) { continue; } if (!_loggedPoll) { _loggedPoll = true; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)"PotionPool 1.0.0 tooltip poll picked up a potion tooltip without a pointer enter."); } } Request(((Il2CppObjectBase)val).Pointer, 0, "tooltip-poll"); break; } } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)("PotionPool poll: " + ex.Message)); } } } private static void OnBeforeRender() { Tick("onBeforeRender"); } private static void OnPreWillRenderCanvases() { Tick("preWillRenderCanvases"); } private static void OnWillRenderCanvases() { Tick("willRenderCanvases"); } private static void Tick(string phase) { if (!_pending) { return; } try { if (phase == "LateUpdate") { _frames++; } if (_frames >= 3600) { _frames = 0; } if (!TryGetTarget(out var loc, out var tmp, out var tt, out var field)) { _tooltipGoneFrames++; if (_tooltipGoneFrames >= 12) { StopAndStrip("tooltip-gone"); } else if (!_loggedFail) { _loggedFail = true; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)("PotionPool 1.0.0 apply failed guid=" + _guid + " phase=" + phase + " reason=no-visible-potion-TMP")); } } } else { _tooltipGoneFrames = 0; TryAppend(loc, tmp, tt, field, phase); } } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)("PotionPool I-V tick " + phase + ": " + ex.Message)); } } } private static void TryAppend(LocalizedText loc, TextMeshProUGUI tmp, FakeTooltip tt, string field, string phase) { string text = ((TMP_Text)tmp).text; if (string.IsNullOrEmpty(text)) { return; } bool flag = Preview.HasMarker(text); if (_hadMarkerLast && !flag) { _rebuildsThisHover++; if (!_loggedRebuild) { _loggedRebuild = true; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)("PotionPool 1.0.0 tooltip rebuilt guid=" + _guid + " field=" + field + " phase=" + phase)); } } } TryFillBloodMeta(tt, tmp); string text2 = Preview.Append(text, _bloodType, _bloodQuality); if (!string.Equals(text, text2, StringComparison.Ordinal)) { ((TMP_Text)tmp).text = text2; _appendsThisHover++; _lastBlock = text2; } HoldLayout(tmp, tt); StopBinder(loc); HoldClippers(tmp, tt); HoldRaycasts(tt); _appliedLoc = loc; _appliedTmp = tmp; _fieldName = field; _cached = tt; string text3 = ((TMP_Text)tmp).text; bool flag2 = (_hadMarkerLast = Preview.HasMarker(text3)); if (!_loggedApply) { _loggedApply = true; ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)("PotionPool 1.0.0 applied I-V guid=" + _guid + " field=" + field + " tmpHasIV=" + flag2 + " type=" + (_bloodType ?? "?") + " quality=" + _bloodQuality.ToString("0.##") + " binderStopped=" + _binderStopped + " locEnabled=" + ((Object)(object)loc != (Object)null && !((Il2CppObjectBase)loc).WasCollected && ((Behaviour)loc).enabled) + " rebuilt=" + (_rebuildsThisHover > 0) + " phase=" + phase + " len=" + (text3?.Length ?? 0))); } } else if (!flag2 && !_loggedFail) { _loggedFail = true; ManualLogSource logger3 = Plugin.Logger; if (logger3 != null) { logger3.LogInfo((object)("PotionPool 1.0.0 apply failed guid=" + _guid + " field=" + field + " tmpHasIV=false phase=" + phase)); } } } private static void StopBinder(LocalizedText loc) { if ((Object)(object)loc == (Object)null || ((Il2CppObjectBase)loc).WasCollected) { return; } loc._IsForceSet = true; loc._TextHasBeenModified = true; loc._HasTextBeenSet = true; loc.SetRectSizeToTextSize = true; if (_binderStopped && (Object)(object)_appliedLoc != (Object)null && !((Il2CppObjectBase)_appliedLoc).WasCollected && ((Il2CppObjectBase)_appliedLoc).Pointer == ((Il2CppObjectBase)loc).Pointer) { return; } if (_binderStopped) { RestoreBinderAndClippers(); } _locWasEnabled = ((Behaviour)loc).enabled; _appliedLoc = loc; _binderStopped = true; try { if (((Behaviour)loc).enabled) { ((Behaviour)loc).enabled = false; } } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("PotionPool disable LocalizedText: " + ex.Message)); } } if (!_loggedBinder) { _loggedBinder = true; bool flag = false; try { flag = ((Behaviour)loc).enabled; } catch { } ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogInfo((object)("PotionPool 1.0.0 binder stopped guid=" + _guid + " locEnabled=" + flag)); } } } private static void TryFillBloodMeta(FakeTooltip tt, TextMeshProUGUI written) { if (_bloodType != null && _bloodQuality > 0f) { return; } StringBuilder blob = new StringBuilder(256); try { if ((Object)(object)written != (Object)null && !((Il2CppObjectBase)written).WasCollected) { string text = ((TMP_Text)written).text; if (!string.IsNullOrEmpty(text)) { blob.Append('\n').Append(text); } } } catch { } if ((Object)(object)tt != (Object)null && !((Il2CppObjectBase)tt).WasCollected) { Add(tt.BloodPotion); Add(tt.Description); Add(tt.SubText); Add(tt.PreSubText); try { Il2CppArrayBase componentsInChildren = ((Component)tt).GetComponentsInChildren(true); if (componentsInChildren != null) { for (int i = 0; i < componentsInChildren.Length; i++) { Add(componentsInChildren[i]); } } } catch { } } if (!BloodBonuses.TryParse(blob.ToString(), out var type, out var quality)) { return; } _bloodType = type; _bloodQuality = quality; if (!_loggedMeta) { _loggedMeta = true; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)("PotionPool 1.0.0 parsed guid=" + _guid + " type=" + type + " quality=" + quality.ToString("0.##"))); } } void Add(LocalizedText loc) { if (!TryReadLoc(loc, out var _, out var tmp)) { return; } try { string text2 = ((TMP_Text)tmp).text; if (!string.IsNullOrEmpty(text2)) { blob.Append('\n').Append(text2); } } catch { } } } private static void HoldLayout(TextMeshProUGUI tmp, FakeTooltip tt) { try { ((TMP_Text)tmp).overflowMode = (TextOverflowModes)0; ((TMP_Text)tmp).enableWordWrapping = true; ((TMP_Text)tmp).maxVisibleLines = 32; ((TMP_Text)tmp).maxVisibleCharacters = 99999; ((TMP_Text)tmp).ForceMeshUpdate(true, true); } catch { } float num = 0f; try { num = ((TMP_Text)tmp).preferredHeight; } catch { } try { Grow(((TMP_Text)tmp).rectTransform, num); SetMinHeight(((Component)tmp).gameObject, num); GameObject val = (((Object)(object)tt != (Object)null) ? tt.DescriptionFrame : null); if ((Object)(object)val != (Object)null && !((Il2CppObjectBase)val).WasCollected) { Transform transform = val.transform; RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null); if ((Object)(object)val2 != (Object)null) { Grow(val2, num + 16f); } SetMinHeight(val, num + 16f); Transform transform2 = val.transform; LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)(object)((transform2 is RectTransform) ? transform2 : null)); if ((Object)(object)val2 != (Object)null) { Grow(val2, num + 16f); } } Grow(((TMP_Text)tmp).rectTransform, num); if ((Object)(object)_appliedLoc != (Object)null && !((Il2CppObjectBase)_appliedLoc).WasCollected) { Transform transform3 = ((Component)_appliedLoc).transform; RectTransform val3 = (RectTransform)(object)((transform3 is RectTransform) ? transform3 : null); if ((Object)(object)val3 != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(val3); } } else if ((Object)(object)((TMP_Text)tmp).rectTransform != (Object)null) { LayoutRebuilder.ForceRebuildLayoutImmediate(((TMP_Text)tmp).rectTransform); } } catch { } } private static void HoldClippers(TextMeshProUGUI tmp, FakeTooltip tt) { if (_clippersHeld || (Object)(object)tmp == (Object)null || ((Il2CppObjectBase)tmp).WasCollected) { return; } try { Transform val = null; try { GameObject val2 = (((Object)(object)tt != (Object)null) ? tt.DescriptionFrame : null); if ((Object)(object)val2 != (Object)null && !((Il2CppObjectBase)val2).WasCollected) { val = val2.transform; } } catch { } Transform val3 = ((TMP_Text)tmp).transform; while ((Object)(object)val3 != (Object)null) { CaptureClipper((Behaviour)(object)((Component)val3).GetComponent()); CaptureClipper((Behaviour)(object)((Component)val3).GetComponent()); if ((Object)(object)val != (Object)null && (Object)(object)val3 == (Object)(object)val) { break; } val3 = val3.parent; } _clippersHeld = true; } catch (Exception ex) { ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogDebug((object)("PotionPool unclip: " + ex.Message)); } } } private static void HoldRaycasts(FakeTooltip tt) { if ((Object)(object)_raycastGroup != (Object)null || (Object)(object)tt == (Object)null) { return; } try { if (((Il2CppObjectBase)tt).WasCollected) { return; } GameObject gameObject = ((Component)tt).gameObject; CanvasGroup val = gameObject.GetComponent(); if ((Object)(object)val == (Object)null) { val = gameObject.AddComponent(); _raycastGroupAdded = true; _raycastWasBlocking = true; } else { _raycastGroupAdded = false; _raycastWasBlocking = val.blocksRaycasts; } val.blocksRaycasts = false; _raycastGroup = val; if (!_loggedRaycast) { _loggedRaycast = true; ManualLogSource logger = Plugin.Logger; if (logger != null) { logger.LogInfo((object)("PotionPool 1.0.0 tooltip raycasts off (group " + (_raycastGroupAdded ? "added" : "existing") + ").")); } } } catch (Exception ex) { ManualLogSource logger2 = Plugin.Logger; if (logger2 != null) { logger2.LogDebug((object)("PotionPool raycast hold: " + ex.Message)); } } } private static void RestoreRaycasts() { try { if ((Object)(object)_raycastGroup != (Object)null && !((Il2CppObjectBase)_raycastGroup).WasCollected) { if (_raycastGroupAdded) { Object.Destroy((Object)(object)_raycastGroup); } else { _raycastGroup.blocksRaycasts = _raycastWasBlocking; } } } catch { } _raycastGroup = null; _raycastGroupAdded = false; } private static void CaptureClipper(Behaviour clipper) { if ((Object)(object)clipper == (Object)null) { return; } try { if (((Il2CppObjectBase)clipper).WasCollected) { return; } } catch { return; } Clippers.Add(clipper); bool flag = false; try { flag = clipper.enabled; } catch { } ClipperWasEnabled.Add(flag); try { if (flag) { clipper.enabled = false; } } catch { } } private static void RestoreBinderAndClippers() { try { if ((Object)(object)_appliedLoc != (Object)null && !((Il2CppObjectBase)_appliedLoc).WasCollected) { try { ((Behaviour)_appliedLoc).enabled = _locWasEnabled || !_binderStopped; } catch { } try { _appliedLoc._IsForceSet = false; _appliedLoc._TextHasBeenModified = false; } catch { } } } catch { } for (int i = 0; i < Clippers.Count; i++) { try { Behaviour val = Clippers[i]; if ((Object)(object)val == (Object)null) { continue; } try { if (((Il2CppObjectBase)val).WasCollected) { continue; } } catch { goto end_IL_0060; } val.enabled = i < ClipperWasEnabled.Count && ClipperWasEnabled[i]; end_IL_0060:; } catch { } } Clippers.Clear(); ClipperWasEnabled.Clear(); _binderStopped = false; _clippersHeld = false; _locWasEnabled = false; RestoreRaycasts(); } private static void Grow(RectTransform rt, float minHeight) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_002a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)rt == (Object)null) && !(minHeight <= 0f)) { Vector2 sizeDelta = rt.sizeDelta; if (sizeDelta.y < minHeight) { rt.sizeDelta = new Vector2(sizeDelta.x, minHeight); } } } private static void SetMinHeight(GameObject go, float minHeight) { if ((Object)(object)go == (Object)null || minHeight <= 0f) { return; } try { LayoutElement component = go.GetComponent(); if (!((Object)(object)component == (Object)null) && !((Il2CppObjectBase)component).WasCollected) { if (component.minHeight < minHeight) { component.minHeight = minHeight; } if (component.preferredHeight < minHeight) { component.preferredHeight = minHeight; } } } catch { } } private static bool StripApplied() { bool result = false; try { if ((Object)(object)_appliedTmp != (Object)null && !((Il2CppObjectBase)_appliedTmp).WasCollected) { string text = ((TMP_Text)_appliedTmp).text; if (Preview.HasMarker(text)) { ((TMP_Text)_appliedTmp).text = Preview.Strip(text); result = true; } } } catch { } try { if ((Object)(object)_cached != (Object)null && !((Il2CppObjectBase)_cached).WasCollected) { if (StripIfMarked(_cached.Description)) { result = true; } FakeTooltip tooltipPrefab = _cached.TooltipPrefab; if ((Object)(object)tooltipPrefab != (Object)null && !((Il2CppObjectBase)tooltipPrefab).WasCollected && ((Il2CppObjectBase)tooltipPrefab).Pointer != ((Il2CppObjectBase)_cached).Pointer && StripIfMarked(tooltipPrefab.Description)) { result = true; } } } catch { } try { if ((Object)(object)_cached != (Object)null && !((Il2CppObjectBase)_cached).WasCollected) { StripIfMarked(_cached.BloodPotion); } } catch { } return result; } private static bool StripIfMarked(LocalizedText loc) { if ((Object)(object)loc == (Object)null || ((Il2CppObjectBase)loc).WasCollected) { return false; } TextMeshProUGUI text = loc.Text; if ((Object)(object)text == (Object)null || ((Il2CppObjectBase)text).WasCollected) { return false; } string text2 = ((TMP_Text)text).text; if (!Preview.HasMarker(text2)) { return false; } ((TMP_Text)text).text = Preview.Strip(text2); try { loc._IsForceSet = false; loc._TextHasBeenModified = false; } catch { } return true; } private static bool TryGetTarget(out LocalizedText loc, out TextMeshProUGUI tmp, out FakeTooltip tt, out string field) { loc = null; tmp = null; tt = null; field = null; if (TryPickFrom(_cached, out loc, out tmp, out field)) { tt = _cached; return true; } _cached = null; Il2CppArrayBase val = Object.FindObjectsByType((FindObjectsInactive)0, (FindObjectsSortMode)0); if (val == null || val.Length == 0) { return false; } FakeTooltip val2 = null; int num = int.MinValue; LocalizedText val3 = null; TextMeshProUGUI val4 = null; string text = null; for (int i = 0; i < val.Length; i++) { FakeTooltip val5 = val[i]; if ((Object)(object)val5 == (Object)null || ((Il2CppObjectBase)val5).WasCollected || !((Behaviour)val5).isActiveAndEnabled) { continue; } try { if (!((Component)val5).gameObject.activeInHierarchy) { continue; } } catch { continue; } if (!TryPickFrom(val5, out var loc2, out var tmp2, out var field2)) { continue; } int num2 = FlavorScore(tmp2, field2); try { num2 += val5.SortingLayerOrder; } catch { } if (IsNestedPrefab(val5, Il2CppArrayBase.op_Implicit(val))) { num2 += 300; } try { CanvasGroup informationGroup = val5.InformationGroup; if ((Object)(object)informationGroup != (Object)null && !((Il2CppObjectBase)informationGroup).WasCollected && informationGroup.alpha <= 0.01f) { num2 -= 500; } } catch { } if ((Object)(object)val2 == (Object)null || num2 > num) { val2 = val5; num = num2; val3 = loc2; val4 = tmp2; text = field2; } } if ((Object)(object)val2 == (Object)null) { return false; } _cached = val2; tt = val2; loc = val3; tmp = val4; field = text; return true; } private static int FlavorScore(TextMeshProUGUI tmp, string field) { int num = 0; if (field == "Description") { num += 80; } if (field == "BloodPotion") { num += 20; } if (field == "Child") { num += 60; } string text = null; try { text = (((Object)(object)tmp != (Object)null) ? ((TMP_Text)tmp).text : null); } catch { } if (string.IsNullOrEmpty(text)) { return num; } if (Preview.HasMarker(text)) { num += 40; } if (text.IndexOf("potion", StringComparison.OrdinalIgnoreCase) >= 0) { num += 80; } if (text.Length > 24) { num += 10; } return num; } private static bool IsNestedPrefab(FakeTooltip candidate, FakeTooltip[] all) { foreach (FakeTooltip val in all) { if ((Object)(object)val == (Object)null || ((Il2CppObjectBase)val).WasCollected || (Object)(object)val == (Object)(object)candidate) { continue; } try { FakeTooltip tooltipPrefab = val.TooltipPrefab; if ((Object)(object)tooltipPrefab != (Object)null && !((Il2CppObjectBase)tooltipPrefab).WasCollected && ((Il2CppObjectBase)tooltipPrefab).Pointer == ((Il2CppObjectBase)candidate).Pointer) { return true; } } catch { } } return false; } private static bool TryPickFrom(FakeTooltip tt, out LocalizedText loc, out TextMeshProUGUI tmp, out string field) { loc = null; tmp = null; field = null; if ((Object)(object)tt == (Object)null || ((Il2CppObjectBase)tt).WasCollected) { return false; } if (!((Behaviour)tt).isActiveAndEnabled) { return false; } try { FakeTooltip tooltipPrefab = tt.TooltipPrefab; if ((Object)(object)tooltipPrefab != (Object)null && !((Il2CppObjectBase)tooltipPrefab).WasCollected && ((Il2CppObjectBase)tooltipPrefab).Pointer != ((Il2CppObjectBase)tt).Pointer && ((Behaviour)tooltipPrefab).isActiveAndEnabled && TryPickOwn(tooltipPrefab, out loc, out tmp, out field)) { return true; } } catch { } return TryPickOwn(tt, out loc, out tmp, out field); } private static bool TryPickOwn(FakeTooltip tt, out LocalizedText loc, out TextMeshProUGUI tmp, out string field) { loc = null; tmp = null; field = null; if ((Object)(object)tt == (Object)null || ((Il2CppObjectBase)tt).WasCollected) { return false; } if (TryReadLoc(tt.Description, out loc, out tmp) && FrameAllows(tt.DescriptionFrame)) { field = "Description"; return true; } if (TryReadFlavorChild(tt, out loc, out tmp)) { field = "Child"; return true; } if (TryReadLoc(tt.BloodPotion, out loc, out tmp)) { field = "BloodPotion"; return true; } if (TryReadLoc(tt.Description, out loc, out tmp)) { field = "Description"; return true; } if (TryReadLoc(tt.SubText, out loc, out tmp)) { field = "SubText"; return true; } return false; } private static bool TryReadFlavorChild(FakeTooltip tt, out LocalizedText loc, out TextMeshProUGUI tmp) { loc = null; tmp = null; LocalizedText val = null; try { val = tt.Description; } catch { } LocalizedText[] array; try { array = Il2CppArrayBase.op_Implicit(((Component)tt).GetComponentsInChildren(false)); } catch { return false; } if (array == null || array.Length == 0) { return false; } LocalizedText val2 = null; TextMeshProUGUI val3 = null; int num = 0; foreach (LocalizedText val4 in array) { if (!TryReadLoc(val4, out var outLoc, out var tmp2) || ((Object)(object)val != (Object)null && !((Il2CppObjectBase)val).WasCollected && ((Il2CppObjectBase)val4).Pointer == ((Il2CppObjectBase)val).Pointer)) { continue; } string text = null; try { text = ((TMP_Text)tmp2).text; } catch { } if (!string.IsNullOrEmpty(text)) { int num2 = 0; if (Preview.HasMarker(text)) { num2 += 40; } if (text.IndexOf("potion", StringComparison.OrdinalIgnoreCase) >= 0) { num2 += 80; } if (text.Length > 24) { num2 += 10; } if (num2 != 0 && ((Object)(object)val2 == (Object)null || num2 > num)) { val2 = outLoc; val3 = tmp2; num = num2; } } } if ((Object)(object)val2 == (Object)null) { return false; } loc = val2; tmp = val3; return true; } private static bool FrameAllows(GameObject frame) { if ((Object)(object)frame == (Object)null || ((Il2CppObjectBase)frame).WasCollected) { return true; } try { return frame.activeInHierarchy; } catch { return true; } } private static bool TryReadLoc(LocalizedText loc, out LocalizedText outLoc, out TextMeshProUGUI tmp) { outLoc = null; tmp = null; if ((Object)(object)loc == (Object)null || ((Il2CppObjectBase)loc).WasCollected) { return false; } try { if (!((Component)loc).gameObject.activeInHierarchy) { return false; } } catch { return false; } tmp = loc.Text; if ((Object)(object)tmp == (Object)null || ((Il2CppObjectBase)tmp).WasCollected) { return false; } try { if (!((Behaviour)tmp).isActiveAndEnabled) { return false; } if (!((Component)tmp).gameObject.activeInHierarchy) { return false; } } catch { return false; } outLoc = loc; return true; } } public static class MyPluginInfo { public const string PLUGIN_GUID = "fangly.PotionPool"; public const string PLUGIN_NAME = "PotionPool"; public const string PLUGIN_VERSION = "1.0.0"; } }