using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.Json; using HarmonyLib; using Il2CppTMPro; using MelonLoader; using MelonLoader.Preferences; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(PickupTabOverlay), "Pickup Tab Overlay", "0.1.27", "kp0hyc", null)] [assembly: MelonGame("Valko Game Studios", "Labyrinthine")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = "")] [assembly: AssemblyCompany("PickupTabOverlay")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("PickupTabOverlay")] [assembly: AssemblyTitle("PickupTabOverlay")] [assembly: AssemblyVersion("1.0.0.0")] public class PickupTabOverlay : MelonMod { private readonly struct TmpOverlayModel { public readonly string Text; public readonly float Width; public readonly float Height; public readonly int StyleKey; public TmpOverlayModel(string text, float width, float height, int styleKey) { Text = text; Width = width; Height = height; StyleKey = styleKey; } } private readonly struct ObjectiveTextStyle { public static readonly ObjectiveTextStyle Fallback = new ObjectiveTextStyle(hasLiveStyle: false, "fallback", 30, 28, 24, null, null, null, null, null); public readonly bool HasLiveStyle; public readonly string Source; public readonly int HeaderFontSize; public readonly int RowFontSize; public readonly int FooterFontSize; public readonly object Color; public readonly object MutedColor; public readonly object Font; public readonly object FontSharedMaterial; public readonly object SourceText; public int StyleKey => (((((((HeaderFontSize * 397) ^ RowFontSize) * 397) ^ FooterFontSize) * 397) ^ ((Font != null) ? RuntimeHelpers.GetHashCode(Font) : 0)) * 397) ^ ((FontSharedMaterial != null) ? RuntimeHelpers.GetHashCode(FontSharedMaterial) : 0); public ObjectiveTextStyle(bool hasLiveStyle, string source, int headerFontSize, int rowFontSize, int footerFontSize, object color, object mutedColor, object font, object fontSharedMaterial, object sourceText) { HasLiveStyle = hasLiveStyle; Source = source; HeaderFontSize = headerFontSize; RowFontSize = rowFontSize; FooterFontSize = footerFontSize; Color = color; MutedColor = mutedColor; Font = font; FontSharedMaterial = fontSharedMaterial; SourceText = sourceText; } public static ObjectiveTextStyle FromConfig(int headerFontSize, int rowFontSize, int footerFontSize, object color) { return new ObjectiveTextStyle(hasLiveStyle: false, "config", headerFontSize, rowFontSize, footerFontSize, color, CopyColorWithAlpha(color, 0.82f), null, null, null); } } private readonly struct PickupEntry { public readonly int Id; public readonly string Name; public readonly int Count; public readonly bool LeftHand; public readonly bool RightHand; public PickupEntry(int id, string name, int count, bool leftHand, bool rightHand) { Id = id; Name = name; Count = count; LeftHand = leftHand; RightHand = rightHand; } } private readonly struct CollectibleEntry { public readonly string Label; public readonly int Amount; public CollectibleEntry(string label, int amount) { Label = label; Amount = amount; } } private readonly struct UnlockEntry { public readonly string Category; public readonly string Name; public readonly int Id; public readonly string ItemClass; public readonly int Count; public UnlockEntry(string category, string name, int id, string itemClass, int count) { Category = category; Name = name; Id = id; ItemClass = itemClass; Count = count; } public UnlockEntry WithCount(int count) { return new UnlockEntry(Category, Name, Id, ItemClass, count); } } private readonly struct ShandadaItemInfo { public readonly string Name; public readonly string ItemClass; public ShandadaItemInfo(string name, string itemClass) { Name = name; ItemClass = itemClass; } } private const string HarmonyId = "kp0hyc.pickuptaboverlay"; private const string PrefCategoryName = "PickupTabOverlay"; private const int MinimumReadableRowFontSize = 18; private const int MinimumReadableFooterFontSize = 16; private static readonly List s_entries = new List(); private static readonly Dictionary s_collectiblesFound = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet s_countedCollectibles = new HashSet(); private static readonly List s_unlockEntries = new List(); private static readonly Dictionary s_unlockEntryIndexes = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly HashSet s_countedUnlockPickups = new HashSet(); private static readonly HashSet s_undiscoveredCosmeticItemIds = new HashSet(); private static readonly Dictionary s_customizationNameCache = new Dictionary(); private static readonly Dictionary s_customizationClassCache = new Dictionary(); private static readonly Dictionary s_shandadaItems = new Dictionary(); private static MelonPreferences_Entry s_enabled; private static MelonPreferences_Entry s_panelWidth; private static MelonPreferences_Entry s_showIds; private static MelonPreferences_Entry s_showHandLabels; private static MelonPreferences_Entry s_showEmptyMessage; private static MelonPreferences_Entry s_showCollectibles; private static MelonPreferences_Entry s_showUnlockPickups; private static MelonPreferences_Entry s_debugLogging; private static MelonPreferences_Entry s_legacyDebugLogging; private static MelonPreferences_Entry s_useTextMeshProOverlay; private static MelonPreferences_Entry s_useObjectiveTextStyle; private static MelonPreferences_Entry s_objectiveTextScalePercent; private static MelonPreferences_Entry s_headerFontSize; private static MelonPreferences_Entry s_rowFontSize; private static MelonPreferences_Entry s_footerFontSize; private static MelonPreferences_Entry s_maxVisibleItems; private static DateTime s_nextRefreshUtc = DateTime.MinValue; private static DateTime s_nextWarningUtc = DateTime.MinValue; private static DateTime s_nextObjectiveStyleRefreshUtc = DateTime.MinValue; private static DateTime s_nextTmpOverlayRetryUtc = DateTime.MinValue; private static DateTime s_nextTmpDiagnosticsUtc = DateTime.MinValue; private static ObjectiveTextStyle s_objectiveTextStyle = ObjectiveTextStyle.Fallback; private static string s_lastLoggedObjectiveStyleSignature; private static string s_lastLoggedRendererSignature; private static string s_lastLoggedTmpSourceSignature; private static string s_lastLoggedTmpDiagnosticsSignature; private static string s_lastLoggedTmpLayoutSignature; private static string s_lastLoggedDirectTmpFailure; private static bool s_tmpOverlayReady; private static bool s_tmpOverlayFailed; private static bool s_tmpOverlayVisible; private static bool s_loggedTmpOverlayReady; private static bool s_tmpOverlayUsesDirectUi; private static bool s_shandadaItemsLoaded; private static string s_tmpLastText; private static int s_tmpLastStyleKey; private static float s_tmpLastWidth; private static float s_tmpLastHeight; private static Type s_rectType; private static Type s_colorType; private static Type s_vector2Type; private static Type s_guiType; private static Type s_screenType; private static Type s_inputType; private static Type s_keyCodeType; private static Type s_labyrinthineInputManagerType; private static Type s_inputButtonType; private static Type s_playerNetworkSyncType; private static Type s_gameManagerType; private static Type s_objectType; private static Type s_rndPlayerInventoryType; private static Type s_rndCollectibleType; private static Type s_customizationPickupType; private static Type s_customCaseItemUnlockType; private static Type s_customizationManagerType; private static Type s_localisationManagerType; private static Type s_objectiveUiType; private static Type s_tmpTextType; private static Type s_tmpFontAssetType; private static Type s_canvasType; private static Type s_componentType; private static Type s_gameObjectType; private static Type s_transformType; private static Type s_rectTransformType; private static Type s_resourcesType; private static Type s_textMeshProUguiType; private static Type s_renderModeType; private static Type s_textAlignmentOptionsType; private static Type s_textOverflowModesType; private static ConstructorInfo s_rectCtor; private static ConstructorInfo s_colorCtor; private static ConstructorInfo s_vector2Ctor; private static ConstructorInfo s_gameObjectCtor; private static ConstructorInfo s_gameObjectWithComponentsCtor; private static MethodInfo s_guiLabel; private static MethodInfo s_inputGetKey; private static MethodInfo s_gameGetButton; private static MethodInfo s_findObjectsOfTypeMethod; private static MethodInfo s_componentGetComponentInParentMethod; private static MethodInfo s_gameObjectGetComponentMethod; private static MethodInfo s_gameObjectAddComponentMethod; private static MethodInfo s_gameObjectSetActiveMethod; private static MethodInfo s_transformSetParentMethod; private static MethodInfo s_transformSetAsLastSiblingMethod; private static MethodInfo s_objectInstantiateMethod; private static MethodInfo s_objectDestroyMethod; private static MethodInfo s_objectDontDestroyOnLoadMethod; private static MethodInfo s_resourcesFindObjectsOfTypeAllMethod; private static PropertyInfo s_guiColorProperty; private static PropertyInfo s_screenWidthProperty; private static PropertyInfo s_screenHeightProperty; private static PropertyInfo s_guiSkinProperty; private static PropertyInfo s_guiSkinLabelProperty; private static PropertyInfo s_styleFontSizeProperty; private static PropertyInfo s_styleNormalProperty; private static PropertyInfo s_styleStateTextColorProperty; private static object s_keyCodeTab; private static object s_showTabMenuButton; private static object s_tmpOverlayRoot; private static object s_tmpOverlayTextObject; private static object s_tmpOverlayText; private static object s_tmpOverlayRectTransform; private static object s_larkeSansFont; private static bool s_reflectionReady; public override void OnInitializeMelon() { MelonPreferences_Category obj = MelonPreferences.CreateCategory("PickupTabOverlay"); s_enabled = obj.CreateEntry("Enabled", true, "Show local pickup inventory while the tab menu is open.", (string)null, false, false, (ValueValidator)null, (string)null); s_panelWidth = obj.CreateEntry("PanelWidth", 320, "Pickup panel width in pixels.", (string)null, false, false, (ValueValidator)null, (string)null); s_showIds = obj.CreateEntry("ShowItemIDs", true, "Show random-case item IDs next to pickup names.", (string)null, false, false, (ValueValidator)null, (string)null); s_showHandLabels = obj.CreateEntry("ShowHandLabels", true, "Mark currently selected left/right hand pickup items.", (string)null, false, false, (ValueValidator)null, (string)null); s_showEmptyMessage = obj.CreateEntry("ShowEmptyMessage", true, "Show a small message when no pickups are held.", (string)null, false, false, (ValueValidator)null, (string)null); s_showCollectibles = obj.CreateEntry("ShowCollectibles", true, "Show random-case collectibles picked up during the current run.", (string)null, false, false, (ValueValidator)null, (string)null); s_showUnlockPickups = obj.CreateEntry("ShowUnlockPickups", true, "Show cosmetic, blueprint, and stamp pickups during the current run.", (string)null, false, false, (ValueValidator)null, (string)null); s_legacyDebugLogging = obj.CreateEntry("DebugLogging", false, "Legacy pickup overlay diagnostics flag.", (string)null, false, false, (ValueValidator)null, (string)null); s_debugLogging = obj.CreateEntry("VerboseLogs", false, "Log pickup overlay diagnostics for troubleshooting.", (string)null, false, false, (ValueValidator)null, (string)null); s_useTextMeshProOverlay = obj.CreateEntry("UseTextMeshProOverlay", true, "Draw the pickup panel using TextMeshPro instead of IMGUI.", (string)null, false, false, (ValueValidator)null, (string)null); s_useObjectiveTextStyle = obj.CreateEntry("UseObjectiveTextStyle", true, "Use the game's live objective text size and color when available.", (string)null, false, false, (ValueValidator)null, (string)null); s_objectiveTextScalePercent = obj.CreateEntry("ObjectiveTextScalePercent", 100, "Multiplier for objective-derived pickup overlay text size.", (string)null, false, false, (ValueValidator)null, (string)null); s_headerFontSize = obj.CreateEntry("HeaderFontSize", 30, "Pickup panel header font size.", (string)null, false, false, (ValueValidator)null, (string)null); s_rowFontSize = obj.CreateEntry("RowFontSize", 28, "Pickup panel row font size.", (string)null, false, false, (ValueValidator)null, (string)null); s_footerFontSize = obj.CreateEntry("FooterFontSize", 24, "Pickup panel footer font size.", (string)null, false, false, (ValueValidator)null, (string)null); s_maxVisibleItems = obj.CreateEntry("MaxVisibleItems", 12, "Maximum pickup rows to show before collapsing the rest.", (string)null, false, false, (ValueValidator)null, (string)null); TryPatchCollectiblePickup(); TryPatchCustomizationPickup(); TryPatchCustomCaseItemUnlock(); LogDebug("PickupTabOverlay initialized."); } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { DestroyTmpOverlay(); s_entries.Clear(); s_collectiblesFound.Clear(); s_countedCollectibles.Clear(); s_unlockEntries.Clear(); s_unlockEntryIndexes.Clear(); s_countedUnlockPickups.Clear(); s_undiscoveredCosmeticItemIds.Clear(); s_nextRefreshUtc = DateTime.MinValue; s_nextObjectiveStyleRefreshUtc = DateTime.MinValue; s_objectiveTextStyle = ObjectiveTextStyle.Fallback; s_lastLoggedObjectiveStyleSignature = null; s_lastLoggedRendererSignature = null; s_lastLoggedTmpSourceSignature = null; s_lastLoggedTmpDiagnosticsSignature = null; s_lastLoggedTmpLayoutSignature = null; s_lastLoggedDirectTmpFailure = null; s_nextTmpDiagnosticsUtc = DateTime.MinValue; } private static void TryPatchCollectiblePickup() { //IL_0033: 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_005d: Expected O, but got Unknown try { MethodInfo methodInfo = GetRndCollectibleType()?.GetMethod("Pickup", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (methodInfo == null) { MelonLogger.Warning("Could not find RndCollectible.Pickup; random collectibles will not be tracked."); return; } new Harmony("kp0hyc.pickuptaboverlay").Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(PickupTabOverlay).GetMethod("RndCollectiblePickupPrefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LogDebug("Patched RndCollectible.Pickup for collectible tracking."); } catch (Exception ex) { MelonLogger.Warning("Could not patch RndCollectible.Pickup: " + ex.GetType().Name + " " + ex.Message); } } private static void TryPatchCustomizationPickup() { //IL_0033: 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_005d: Expected O, but got Unknown try { MethodInfo methodInfo = GetCustomizationPickupType()?.GetMethod("Pickup", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (methodInfo == null) { MelonLogger.Warning("Could not find CustomizationPickup.Pickup; cosmetic pickups will not be tracked."); return; } new Harmony("kp0hyc.pickuptaboverlay").Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(PickupTabOverlay).GetMethod("CustomizationPickupPrefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LogDebug("Patched CustomizationPickup.Pickup for cosmetic tracking."); } catch (Exception ex) { MelonLogger.Warning("Could not patch CustomizationPickup.Pickup: " + ex.GetType().Name + " " + ex.Message); } } private static void TryPatchCustomCaseItemUnlock() { //IL_0033: 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_005d: Expected O, but got Unknown try { MethodInfo methodInfo = GetCustomCaseItemUnlockType()?.GetMethod("Pickup", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (methodInfo == null) { MelonLogger.Warning("Could not find CustomCaseItemUnlock.Pickup; blueprint/stamp pickups will not be tracked."); return; } new Harmony("kp0hyc.pickuptaboverlay").Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(PickupTabOverlay).GetMethod("CustomCaseItemUnlockPrefix", BindingFlags.Static | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); LogDebug("Patched CustomCaseItemUnlock.Pickup for blueprint/stamp tracking."); } catch (Exception ex) { MelonLogger.Warning("Could not patch CustomCaseItemUnlock.Pickup: " + ex.GetType().Name + " " + ex.Message); } } private static void RndCollectiblePickupPrefix(object __instance) { try { if (__instance == null) { return; } int hashCode = RuntimeHelpers.GetHashCode(__instance); if (!s_countedCollectibles.Contains(hashCode) && TryClassifyCollectible(__instance, out var label, out var amount)) { s_countedCollectibles.Add(hashCode); if (!s_collectiblesFound.TryGetValue(label, out var value)) { value = 0; } s_collectiblesFound[label] = value + amount; } } catch (Exception ex) { LogWarning("collectible pickup tracking failed: " + ex.GetType().Name + " " + ex.Message); } } private static void CustomizationPickupPrefix(object __instance) { try { if (__instance == null) { return; } int hashCode = RuntimeHelpers.GetHashCode(__instance); if (!s_countedUnlockPickups.Contains(hashCode) && TryConvertInt(GetFieldOrProp(__instance, "ItemID") ?? GetFieldOrProp(__instance, "itemID"), out var result)) { s_countedUnlockPickups.Add(hashCode); bool flag = IsCustomizationItemAlreadyUnlocked(result); if (!flag) { s_undiscoveredCosmeticItemIds.Add(result); } string text = (s_undiscoveredCosmeticItemIds.Contains(result) ? "Undiscovered item" : ResolveCustomizationName(result)); if (string.IsNullOrWhiteSpace(text)) { text = CleanPickupObjectName(__instance, "Cosmetic"); } string text2 = ResolveCustomizationClass(result); AddUnlockEntry("Cosmetic", text, result, text2); LogDebug("Cosmetic pickup itemID=" + result + " alreadyUnlocked=" + flag + " class=" + (text2 ?? "{unknown}") + " name=" + text); } } catch (Exception ex) { LogWarning("cosmetic pickup tracking failed: " + ex.GetType().Name + " " + ex.Message); } } private static void CustomCaseItemUnlockPrefix(object __instance) { try { if (__instance == null) { return; } int hashCode = RuntimeHelpers.GetHashCode(__instance); if (!s_countedUnlockPickups.Contains(hashCode)) { bool flag = false; bool flag2 = false; if (TryConvertBool(GetFieldOrProp(__instance, "unlockMazeType"), out var result) && result) { object fieldOrProp = GetFieldOrProp(__instance, "mazeType"); AddUnlockEntry("Map Blueprint", "Picked up", -1); LogDebug("Map blueprint pickup mazeType=" + FormatEnumValue(fieldOrProp, "Map")); flag = true; flag2 = true; } if (TryConvertBool(GetFieldOrProp(__instance, "unlockMonsterType"), out var result2) && result2) { object fieldOrProp2 = GetFieldOrProp(__instance, "monsterType"); LogDebug("Unexpected monster blueprint pickup monsterType=" + FormatEnumValue(fieldOrProp2, "Monster")); flag = true; } if (TryConvertBool(GetFieldOrProp(__instance, "unlockContractType"), out var result3) && result3) { object fieldOrProp3 = GetFieldOrProp(__instance, "contractType"); AddUnlockEntry("Stamps", "Collected", -1); LogDebug("Stamp pickup contractType=" + FormatEnumValue(fieldOrProp3, "Contract")); flag = true; flag2 = true; } if (!flag) { AddUnlockEntry("Blueprint", CleanPickupObjectName(__instance, "Custom Case Unlock"), -1); flag2 = true; LogDebug("Unknown custom-case unlock pickup name=" + CleanPickupObjectName(__instance, "Custom Case Unlock")); } else if (!flag2) { LogDebug("Custom-case unlock pickup had no visible overlay row."); } s_countedUnlockPickups.Add(hashCode); } } catch (Exception ex) { LogWarning("blueprint/stamp pickup tracking failed: " + ex.GetType().Name + " " + ex.Message); } } private static bool TryClassifyCollectible(object collectible, out string label, out int amount) { label = null; if (!TryConvertInt(GetFieldOrProp(collectible, "amount"), out amount) || amount <= 0) { amount = 1; } object fieldOrProp = GetFieldOrProp(collectible, "collectibleType"); string text = ((fieldOrProp == null) ? string.Empty : fieldOrProp.ToString()); string collectibleObjectName = GetCollectibleObjectName(collectible); string text2 = (text + " " + collectibleObjectName + " " + (GetFieldOrProp(collectible, "pickupMessage") ?? string.Empty)).ToLowerInvariant(); if (text2.Contains("ticket")) { label = "Tickets"; return true; } if (text.IndexOf("Reroll", StringComparison.OrdinalIgnoreCase) >= 0 || text2.Contains("reroll")) { label = "Reroll Coins"; return true; } return false; } private static string GetCollectibleObjectName(object collectible) { string text = GetFieldOrProp(GetFieldOrProp(collectible, "gameObject") ?? collectible, "name") as string; if (!string.IsNullOrWhiteSpace(text)) { return text; } return string.Empty; } private static void AddUnlockEntry(string category, string name, int id, string itemClass = null) { if (string.IsNullOrWhiteSpace(category)) { category = "Unlock"; } if (string.IsNullOrWhiteSpace(name)) { name = "Unknown"; } name = CleanUnityName(name); itemClass = FormatItemClass(itemClass); string key = category + "|" + id + "|" + itemClass + "|" + name; if (s_unlockEntryIndexes.TryGetValue(key, out var value) && value >= 0 && value < s_unlockEntries.Count) { s_unlockEntries[value] = s_unlockEntries[value].WithCount(s_unlockEntries[value].Count + 1); return; } s_unlockEntryIndexes[key] = s_unlockEntries.Count; s_unlockEntries.Add(new UnlockEntry(category, name, id, itemClass, 1)); } private static string ResolveCustomizationName(int itemId) { if (s_customizationNameCache.TryGetValue(itemId, out var value)) { return value; } string text = null; if (TryGetShandadaItemInfo(itemId, out var info) && !string.IsNullOrWhiteSpace(info.Name)) { text = info.Name; } object obj = TryGetCustomizationItem(itemId); if (obj != null && string.IsNullOrWhiteSpace(text)) { text = GetFieldOrProp(obj, "Name") as string; if (string.IsNullOrWhiteSpace(text)) { text = GetLocalisation(GetFieldOrProp(obj, "LocalisationKey") as string, null); } } if (!string.IsNullOrWhiteSpace(text)) { s_customizationNameCache[itemId] = text; return text; } return null; } private static object TryGetCustomizationItem(int itemId) { try { object customizationCollection = GetCustomizationCollection(); if (customizationCollection == null || itemId < 0 || itemId > 65535) { return null; } MethodInfo methodInfo = customizationCollection.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "TryGetItem" && m.GetParameters().Length == 2); if (methodInfo == null) { return null; } object[] array = new object[2] { (ushort)itemId, null }; if (TryConvertBool(methodInfo.Invoke(customizationCollection, array), out var result) && result) { return array[1]; } } catch { } return null; } private static string ResolveCustomizationClass(int itemId) { if (s_customizationClassCache.TryGetValue(itemId, out var value)) { return value; } string text = null; if (TryGetShandadaItemInfo(itemId, out var info)) { text = info.ItemClass; } if (string.IsNullOrWhiteSpace(text)) { object obj = TryGetCustomizationItem(itemId); text = (GetFieldOrProp(obj, "ItemRarity") ?? GetFieldOrProp(obj, "itemRarity"))?.ToString(); } text = FormatItemClass(text); if (!string.IsNullOrWhiteSpace(text)) { s_customizationClassCache[itemId] = text; } return text; } private static bool TryGetShandadaItemInfo(int itemId, out ShandadaItemInfo info) { EnsureShandadaItemsLoaded(); return s_shandadaItems.TryGetValue(itemId, out info); } private static void EnsureShandadaItemsLoaded() { if (s_shandadaItemsLoaded) { return; } s_shandadaItemsLoaded = true; try { Assembly assembly = typeof(PickupTabOverlay).Assembly; string text = assembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith("shandada_game_item_id_map.json", StringComparison.OrdinalIgnoreCase)); if (string.IsNullOrWhiteSpace(text)) { LogDebug("Embedded Shandadapanda item map was not found."); return; } using Stream stream = assembly.GetManifestResourceStream(text); if (stream == null) { return; } using JsonDocument jsonDocument = JsonDocument.Parse(stream); if (!jsonDocument.RootElement.TryGetProperty("gameToWebsite", out var value) || value.ValueKind != JsonValueKind.Array) { return; } foreach (JsonElement item in value.EnumerateArray()) { if (TryGetJsonInt(item, "gameItemId", out var value2) && value2 >= 0) { string jsonString = GetJsonString(item, "websiteName"); string text2 = GetJsonString(item, "class") ?? GetJsonString(item, "websiteMapClass") ?? GetJsonString(item, "websiteEvent"); if (!string.IsNullOrWhiteSpace(jsonString) || !string.IsNullOrWhiteSpace(text2)) { s_shandadaItems[value2] = new ShandadaItemInfo(jsonString, text2); } } } LogDebug("Loaded " + s_shandadaItems.Count + " Shandadapanda item names."); } catch (Exception ex) { LogWarning("Shandadapanda item map load failed: " + ex.GetType().Name + " " + ex.Message); } } private static bool TryGetJsonInt(JsonElement element, string name, out int value) { value = 0; if (!element.TryGetProperty(name, out var value2)) { return false; } if (value2.ValueKind == JsonValueKind.Number) { return value2.TryGetInt32(out value); } if (value2.ValueKind == JsonValueKind.String) { return int.TryParse(value2.GetString(), out value); } return false; } private static string GetJsonString(JsonElement element, string name) { if (!element.TryGetProperty(name, out var value) || value.ValueKind != JsonValueKind.String) { return null; } return value.GetString(); } private static string FormatItemClass(string itemClass) { if (string.IsNullOrWhiteSpace(itemClass)) { return null; } itemClass = itemClass.Trim(); int num = itemClass.LastIndexOf('.'); if (num >= 0 && num + 1 < itemClass.Length) { itemClass = itemClass.Substring(num + 1); } itemClass = CleanUnityName(itemClass).Trim(); if (!string.IsNullOrWhiteSpace(itemClass)) { return itemClass.ToLowerInvariant(); } return null; } private static object GetCustomizationCollection() { object customizationManager = GetCustomizationManager(); return GetFieldOrProp(customizationManager, "Collection") ?? GetFieldOrProp(customizationManager, "collection"); } private static object GetCustomizationManager() { Type customizationManagerType = GetCustomizationManagerType(); return GetStaticFieldOrProp(customizationManagerType, "Instance") ?? FindFirstObjectOfType(customizationManagerType); } private static bool IsCustomizationItemAlreadyUnlocked(int itemId) { if (itemId < 0 || itemId > 65535) { return false; } if (TryIsCustomizationItemUnlocked(GetCurrentCustomizationSave(), itemId, out var unlocked)) { return unlocked; } if (TryUnlockedItemsListContains(GetFieldOrProp(GetCustomizationManager(), "UnlockedItems"), itemId, out unlocked)) { return unlocked; } LogDebug("Could not determine cosmetic unlock state for itemID=" + itemId + "; hiding name."); return false; } private static bool TryIsCustomizationItemUnlocked(object customizationSave, int itemId, out bool unlocked) { unlocked = false; if (customizationSave == null || itemId < 0 || itemId > 65535) { return false; } try { return TryConvertBool(customizationSave.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "IsItemUnlocked" && m.GetParameters().Length == 1)?.Invoke(customizationSave, new object[1] { (ushort)itemId }), out unlocked); } catch { unlocked = false; return false; } } private static bool TryUnlockedItemsListContains(object list, int itemId, out bool unlocked) { unlocked = false; if (list == null) { return false; } foreach (object item in EnumerateItems(list)) { if (TryConvertInt(item, out var result) && result == itemId) { unlocked = true; return true; } } return true; } private static object GetCurrentCustomizationSave() { return GetFieldOrProp(GetCustomizationManager(), "CustomizationSave"); } private static object LoadCustomizationSave() { try { return (FindType("CharacterCustomization.CustomizationSave")?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Load" && m.GetParameters().Length == 0))?.Invoke(null, Array.Empty()); } catch { return null; } } private static string GetLocalisation(string key, string defaultValue) { if (string.IsNullOrWhiteSpace(key)) { return defaultValue; } try { string text = (GetLocalisationManagerType()?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetLocalisation" && m.GetParameters().Length == 2))?.Invoke(null, new object[2] { key, defaultValue }) as string; return string.IsNullOrWhiteSpace(text) ? defaultValue : text; } catch { return defaultValue; } } private static string FormatEnumValue(object value, string fallback) { if (value == null) { return fallback; } string text = value.ToString(); if (string.IsNullOrWhiteSpace(text)) { return fallback; } return CleanUnityName(text); } private static string CleanPickupObjectName(object pickup, string fallback) { string text = GetFieldOrProp(GetFieldOrProp(pickup, "gameObject") ?? pickup, "name") as string; if (string.IsNullOrWhiteSpace(text)) { return fallback; } string text2 = CleanUnityName(text); if (text2.StartsWith("CPickup - ", StringComparison.OrdinalIgnoreCase)) { text2 = text2.Substring("CPickup - ".Length).Trim(); } if (!string.IsNullOrWhiteSpace(text2)) { return text2; } return fallback; } public override void OnUpdate() { if (s_enabled == null || !s_enabled.Value) { SetTmpOverlayVisible(visible: false); return; } DateTime utcNow = DateTime.UtcNow; if (utcNow >= s_nextRefreshUtc) { s_nextRefreshUtc = utcNow.AddMilliseconds(250.0); try { RefreshPickupEntries(); } catch (Exception ex) { LogWarning("pickup refresh failed: " + ex.GetType().Name + " " + ex.Message); } } try { UpdateTmpOverlay(); } catch (Exception ex2) { ResetTmpOverlayState(failed: false); LogWarning("TextMeshPro pickup overlay update failed: " + ex2.GetType().Name + " " + ex2.Message); } } public override void OnGUI() { if (s_enabled == null || !s_enabled.Value || !EnsureReflection() || !IsTabMenuPressed() || (!HasAnyDisplayedPickup() && (s_showEmptyMessage == null || !s_showEmptyMessage.Value)) || (IsTextMeshProOverlayEnabled() && s_tmpOverlayReady)) { return; } try { DrawPickupPanel(); } catch (Exception ex) { LogWarning("pickup overlay draw failed: " + ex.GetType().Name + " " + ex.Message); } } private static void RefreshPickupEntries() { s_entries.Clear(); } private static void DrawPickupPanel() { int val = Clamp((s_maxVisibleItems == null) ? 12 : s_maxVisibleItems.Value, 1, 40); ObjectiveTextStyle textStyle = ResolveTextStyle(); LogOverlayRenderer("IMGUI fallback", GetFallbackRendererReason() + " " + DescribeTextStyleForLog(textStyle)); List collectibleRows = GetCollectibleRows(); List unlockRows = GetUnlockRows(); float width = ResolveOverlayWidth(unlockRows); int num = Math.Min(s_entries.Count, val); float singleRowHeight = GetSingleRowHeight(textStyle.RowFontSize); float num2 = ((s_entries.Count > num) ? Math.Max(20f, (float)textStyle.FooterFontSize + 8f) : 0f); float num3 = collectibleRows.Sum((CollectibleEntry row) => EstimateDisplayRowHeight(row.Label + ": " + row.Amount, textStyle.RowFontSize, width)); float num4 = unlockRows.Sum((UnlockEntry row) => EstimateDisplayRowHeight(FormatUnlockEntry(row), textStyle.RowFontSize, width)); float num5 = ((!HasAnyDisplayedPickup()) ? singleRowHeight : 0f); float num6 = num3 + num4 + (float)num * singleRowHeight + num2 + num5 + 8f; float num7 = 34f; float x = Math.Max(12f, (float)ScreenWidth() - width - num7); float num8 = Math.Max(12f, (float)ScreenHeight() - num6 - num7); foreach (CollectibleEntry item in collectibleRows) { string text = item.Label + ": " + item.Amount; float num9 = EstimateDisplayRowHeight(text, textStyle.RowFontSize, width); LabelShadowed(x, num8, width, num9, text, textStyle.RowFontSize, textStyle.Color); num8 += num9; } foreach (UnlockEntry item2 in unlockRows) { string text2 = FormatUnlockEntry(item2); float num10 = EstimateDisplayRowHeight(text2, textStyle.RowFontSize, width); LabelShadowed(x, num8, width, num10, text2, textStyle.RowFontSize, textStyle.Color); num8 += num10; } if (!HasAnyDisplayedPickup()) { LabelShadowed(x, num8, width, singleRowHeight, "No pickups", textStyle.RowFontSize, textStyle.MutedColor); return; } for (int num11 = 0; num11 < num; num11++) { PickupEntry entry = s_entries[num11]; LabelShadowed(x, num8, width, singleRowHeight, FormatEntry(entry), textStyle.RowFontSize, textStyle.Color); num8 += singleRowHeight; } if (s_entries.Count > num) { int num12 = s_entries.Count - num; LabelShadowed(x, num8, width, num2, "+ " + num12 + " more", textStyle.FooterFontSize, textStyle.MutedColor); } } private static void UpdateTmpOverlay() { if (!IsTextMeshProOverlayEnabled()) { SetTmpOverlayVisible(visible: false); } else { if (!EnsureReflection()) { return; } if (!IsTabMenuPressed() || (!HasAnyDisplayedPickup() && (s_showEmptyMessage == null || !s_showEmptyMessage.Value))) { SetTmpOverlayVisible(visible: false); return; } ObjectiveTextStyle objectiveTextStyle = ResolveTextStyle(); TmpOverlayModel model = BuildTmpOverlayModel(objectiveTextStyle); if (EnsureTmpOverlay()) { LogOverlayRenderer(s_tmpOverlayUsesDirectUi ? "TextMeshPro direct UI" : "TextMeshPro cloned UI", DescribeTextStyleForLog(objectiveTextStyle)); ApplyTmpTextStyle(objectiveTextStyle); ApplyTmpLayout(model.Width, model.Height); ApplyTmpText(model.Text, model.StyleKey); LogTmpLayoutDiagnostics(objectiveTextStyle, model); SetTmpOverlayVisible(visible: true); } } } private static bool IsTextMeshProOverlayEnabled() { if (s_useTextMeshProOverlay != null) { return s_useTextMeshProOverlay.Value; } return true; } private static TmpOverlayModel BuildTmpOverlayModel(ObjectiveTextStyle textStyle) { int val = Clamp((s_maxVisibleItems == null) ? 12 : s_maxVisibleItems.Value, 1, 40); List collectibleRows = GetCollectibleRows(); List unlockRows = GetUnlockRows(); float width = ResolveOverlayWidth(unlockRows); int num = Math.Min(s_entries.Count, val); float singleRowHeight = GetSingleRowHeight(textStyle.RowFontSize); float num2 = ((s_entries.Count > num) ? Math.Max(20f, (float)textStyle.FooterFontSize + 8f) : 0f); float num3 = ((!HasAnyDisplayedPickup()) ? singleRowHeight : 0f) + (float)num * singleRowHeight + num2 + 10f; StringBuilder stringBuilder = new StringBuilder(); foreach (CollectibleEntry item in collectibleRows) { string text = item.Label + ": " + item.Amount; num3 += EstimateDisplayRowHeight(text, textStyle.RowFontSize, width); AppendTmpLine(stringBuilder, text, textStyle.RowFontSize, muted: false); } foreach (UnlockEntry item2 in unlockRows) { string text2 = FormatUnlockEntry(item2); num3 += EstimateDisplayRowHeight(text2, textStyle.RowFontSize, width); AppendTmpLine(stringBuilder, text2, textStyle.RowFontSize, muted: false); } if (!HasAnyDisplayedPickup()) { AppendTmpLine(stringBuilder, "No pickups", textStyle.RowFontSize, muted: true); } else { for (int i = 0; i < num; i++) { AppendTmpLine(stringBuilder, FormatEntry(s_entries[i]), textStyle.RowFontSize, muted: false); } } if (s_entries.Count > num) { AppendTmpLine(stringBuilder, "+ " + (s_entries.Count - num) + " more", textStyle.FooterFontSize, muted: true); } return new TmpOverlayModel(stringBuilder.ToString().TrimEnd('\n'), width, num3, textStyle.StyleKey); } private static float ResolveOverlayWidth(List unlockRows) { int num = Clamp((s_panelWidth == null) ? 320 : s_panelWidth.Value, 220, 900); int value = (unlockRows.Any((UnlockEntry row) => string.Equals(row.Category, "Cosmetic", StringComparison.OrdinalIgnoreCase)) ? Math.Max(num, 680) : num); int val = Math.Max(220, ScreenWidth() - 80); return Clamp(value, 220, Math.Min(900, val)); } private static float GetSingleRowHeight(int fontSize) { return Math.Max(30f, (float)fontSize + 10f); } private static float EstimateDisplayRowHeight(string text, int fontSize, float width) { int num = EstimateWrappedLineCount(text, fontSize, width); return Math.Max(GetSingleRowHeight(fontSize), (float)num * ((float)fontSize + 8f) + 4f); } private static int EstimateWrappedLineCount(string text, int fontSize, float width) { if (string.IsNullOrWhiteSpace(text)) { return 1; } float num = Math.Max(80f, width - 8f); float num2 = Math.Max(7f, (float)fontSize * 0.54f); int num3 = Math.Max(8, (int)Math.Floor(num / num2)); int num4 = 1; int num5 = 0; string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { int num6 = Math.Max(1, text2.Length); if (num5 == 0) { num4 += Math.Max(0, (num6 - 1) / num3); num5 = num6 % num3; if (num5 == 0) { num5 = num3; } continue; } if (num5 + 1 + num6 <= num3) { num5 += 1 + num6; continue; } num4++; num4 += Math.Max(0, (num6 - 1) / num3); num5 = num6 % num3; if (num5 == 0) { num5 = num3; } } return Math.Max(1, num4); } private static void AppendTmpLine(StringBuilder builder, string text, int fontSize, bool muted) { if (builder.Length > 0) { builder.Append('\n'); } builder.Append(text ?? string.Empty); } private static string EscapeTmpRichText(string text) { if (string.IsNullOrEmpty(text)) { return string.Empty; } return text.Replace("<", "<").Replace(">", ">"); } private static bool EnsureTmpOverlay() { if (s_tmpOverlayReady && s_tmpOverlayRoot != null && s_tmpOverlayText != null && s_tmpOverlayRectTransform != null) { return true; } if (s_tmpOverlayFailed && DateTime.UtcNow < s_nextTmpOverlayRetryUtc) { return false; } try { if (TryCreateDirectTmpOverlay(out var failureReason)) { return true; } LogDirectTmpFailure(failureReason); if (s_gameObjectType == null || s_rectTransformType == null || s_canvasType == null || s_textMeshProUguiType == null || s_textAlignmentOptionsType == null || s_vector2Ctor == null || s_objectInstantiateMethod == null) { LogTmpOverlayDiagnostics("required Unity/TMP types or methods are missing"); return MarkTmpOverlayFailed("required Unity/TMP types are not available"); } if (!TryFindTmpOverlaySource(out var sourceText)) { LogTmpOverlayDiagnostics("source search found no usable active TMP source"); s_nextTmpOverlayRetryUtc = DateTime.UtcNow.AddMilliseconds(500.0); return false; } string text = DescribeTmpTextSource(sourceText); if (!string.Equals(s_lastLoggedTmpSourceSignature, text, StringComparison.Ordinal)) { s_lastLoggedTmpSourceSignature = text; LogDebug("TextMeshPro pickup overlay source " + text + "."); } object fieldOrProp = GetFieldOrProp(sourceText, "gameObject"); object fieldOrProp2 = GetFieldOrProp(GetComponentInParent(sourceText, s_canvasType), "transform"); if (fieldOrProp == null || fieldOrProp2 == null) { return MarkTmpOverlayFailed("objective TextMeshPro source has no canvas parent"); } s_tmpOverlayRoot = CloneGameObject(fieldOrProp, fieldOrProp2); s_tmpOverlayTextObject = s_tmpOverlayRoot; s_tmpOverlayText = GetComponent(s_tmpOverlayTextObject, s_textMeshProUguiType); s_tmpOverlayRectTransform = GetComponent(s_tmpOverlayTextObject, s_rectTransformType); if (s_tmpOverlayText == null || s_tmpOverlayRectTransform == null) { return MarkTmpOverlayFailed("could not clone TextMeshProUGUI component"); } SetFieldOrProp(s_tmpOverlayRoot, "name", "kp0hyc PickupTabOverlay Text"); object fieldOrProp3 = GetFieldOrProp(s_tmpOverlayTextObject, "transform"); s_transformSetAsLastSiblingMethod?.Invoke(fieldOrProp3, Array.Empty()); SetRectTransformAnchors(s_tmpOverlayRectTransform, 1f, 0f, 1f, 0f, 1f, 0f); SetFieldOrProp(s_tmpOverlayText, "alignment", Enum.Parse(s_textAlignmentOptionsType, "BottomRight")); SetFieldOrProp(s_tmpOverlayText, "richText", false); SetFieldOrProp(s_tmpOverlayText, "enableAutoSizing", false); SetFieldOrProp(s_tmpOverlayText, "enableWordWrapping", true); if (s_textOverflowModesType != null) { SetFieldOrProp(s_tmpOverlayText, "overflowMode", Enum.Parse(s_textOverflowModesType, "Overflow")); } SetFieldOrProp(s_tmpOverlayText, "raycastTarget", false); s_tmpOverlayReady = true; s_tmpOverlayFailed = false; s_tmpOverlayVisible = true; s_tmpOverlayUsesDirectUi = false; SetTmpOverlayVisible(visible: false); if (!s_loggedTmpOverlayReady) { s_loggedTmpOverlayReady = true; LogDebug("TextMeshPro pickup overlay created by cloning source UI."); } return true; } catch (Exception ex) { ResetTmpOverlayState(failed: true); LogWarning("TextMeshPro pickup overlay creation failed: " + ex.GetType().Name + " " + ex.Message); return false; } } private static bool TryCreateDirectTmpOverlay(out string failureReason) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) failureReason = null; GameObject val = null; GameObject val2 = null; try { if ((object)s_textMeshProUguiType == null) { s_textMeshProUguiType = typeof(TextMeshProUGUI); } if ((object)s_textAlignmentOptionsType == null) { s_textAlignmentOptionsType = typeof(TextAlignmentOptions); } if ((object)s_textOverflowModesType == null) { s_textOverflowModesType = typeof(TextOverflowModes); } RectTransform val3 = ResolveTmpOverlayParentTransform(GameObject.Find("Global/Global Canvas/Player_UI/Tab Menu")); if ((Object)(object)val3 == (Object)null) { failureReason = "game Tab Menu/Player_UI RectTransform is not available"; return false; } val = new GameObject("kp0hyc PickupTabOverlay Root"); val.transform.SetParent((Transform)(object)val3, false); RectTransform val4 = val.GetComponent() ?? val.AddComponent(); val2 = new GameObject("kp0hyc PickupTabOverlay Text"); val2.transform.SetParent(val.transform, false); RectTransform val5 = val2.GetComponent() ?? val2.AddComponent(); TextMeshProUGUI val6 = val2.AddComponent(); if ((Object)(object)val6 == (Object)null || (Object)(object)val4 == (Object)null || (Object)(object)val5 == (Object)null) { failureReason = "created direct GameObjects but TextMeshProUGUI/RectTransform was null"; Object.Destroy((Object)(object)val); return false; } val4.anchorMin = new Vector2(1f, 0f); val4.anchorMax = new Vector2(1f, 0f); val4.pivot = new Vector2(1f, 0f); val4.anchoredPosition = new Vector2(-34f, 34f); val5.anchorMin = new Vector2(0f, 0f); val5.anchorMax = new Vector2(1f, 1f); val5.pivot = new Vector2(1f, 0f); val5.offsetMin = Vector2.zero; val5.offsetMax = Vector2.zero; SetFieldOrProp(val6, "alignment", (object)(TextAlignmentOptions)1028); SetFieldOrProp(val6, "richText", false); SetFieldOrProp(val6, "enableAutoSizing", false); SetFieldOrProp(val6, "enableWordWrapping", true); SetFieldOrProp(val6, "overflowMode", (object)(TextOverflowModes)0); SetFieldOrProp(val6, "raycastTarget", false); SetFieldOrProp(val6, "text", string.Empty); SetFieldOrProp(val6, "fontSize", (float)Clamp((s_rowFontSize == null) ? 18 : s_rowFontSize.Value, 18, 72)); SetFieldOrProp(val6, "color", ObjectiveColor()); object obj = FindTmpFontByName("Larke Sans Regular SDF"); if (obj != null) { SetFieldOrProp(val6, "font", obj); } s_tmpOverlayRoot = val; s_tmpOverlayTextObject = val2; s_tmpOverlayText = val6; s_tmpOverlayRectTransform = val4; s_tmpOverlayReady = true; s_tmpOverlayFailed = false; s_tmpOverlayVisible = true; s_tmpOverlayUsesDirectUi = true; SetTmpOverlayVisible(visible: false); if (!s_loggedTmpOverlayReady) { s_loggedTmpOverlayReady = true; LogDebug("TextMeshPro pickup overlay created with TellMeCosmetics-style direct UI setup."); } return true; } catch (Exception ex) { failureReason = ex.GetType().Name + " " + ex.Message; try { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } } catch { } return false; } } private static RectTransform ResolveTmpOverlayParentTransform(GameObject tabMenu) { RectTransform val = (((Object)(object)tabMenu == (Object)null) ? null : tabMenu.GetComponent()); if ((Object)(object)val != (Object)null) { return val; } GameObject val2 = GameObject.Find("Global/Global Canvas/Player_UI"); RectTransform val3 = (((Object)(object)val2 == (Object)null) ? null : val2.GetComponent()); if ((Object)(object)val3 != (Object)null) { return val3; } GameObject val4 = GameObject.Find("Global/Global Canvas"); RectTransform val5 = (((Object)(object)val4 == (Object)null) ? null : val4.GetComponent()); if ((Object)(object)val5 != (Object)null) { return val5; } return null; } private static void LogDirectTmpFailure(string failureReason) { if (string.IsNullOrWhiteSpace(failureReason)) { failureReason = "unknown"; } if (!string.Equals(s_lastLoggedDirectTmpFailure, failureReason, StringComparison.Ordinal)) { s_lastLoggedDirectTmpFailure = failureReason; LogDebug("Direct TextMeshPro pickup overlay failed: " + failureReason); } } private static bool TryFindTmpOverlaySource(out object sourceText) { sourceText = null; if (IsUsableTmpOverlaySource(s_objectiveTextStyle.SourceText)) { sourceText = s_objectiveTextStyle.SourceText; return true; } if (TryGetObjectiveTextStyleFromObjectiveUi(out var style) && IsUsableTmpOverlaySource(style.SourceText)) { s_objectiveTextStyle = style; sourceText = style.SourceText; return true; } if (TryGetObjectiveTextStyleFromTmpScan(out var style2) && IsUsableTmpOverlaySource(style2.SourceText)) { s_objectiveTextStyle = style2; sourceText = style2.SourceText; return true; } return false; } private static void LogTmpOverlayDiagnostics(string reason) { if (!IsDebugLoggingEnabled()) { return; } DateTime utcNow = DateTime.UtcNow; if (!(utcNow < s_nextTmpDiagnosticsUtc)) { string text = BuildTmpOverlayDiagnostics(reason); if (string.Equals(s_lastLoggedTmpDiagnosticsSignature, text, StringComparison.Ordinal)) { s_nextTmpDiagnosticsUtc = utcNow.AddSeconds(5.0); return; } s_lastLoggedTmpDiagnosticsSignature = text; s_nextTmpDiagnosticsUtc = utcNow.AddSeconds(2.0); LogDebug(text); } } private static void LogTmpLayoutDiagnostics(ObjectiveTextStyle textStyle, TmpOverlayModel model) { if (IsDebugLoggingEnabled()) { object rectTransform = ((s_tmpOverlayTextObject == null) ? null : GetComponent(s_tmpOverlayTextObject, s_rectTransformType)); object componentInParent = GetComponentInParent(s_tmpOverlayText, s_canvasType); string b = (s_tmpOverlayUsesDirectUi ? "direct" : "cloned") + "|" + model.Width.ToString("0.#") + "|" + model.Height.ToString("0.#") + "|" + textStyle.RowFontSize + "|" + DescribeRectTransform(s_tmpOverlayRectTransform) + "|" + DescribeRectTransform(rectTransform) + "|" + GetCanvasScaleFactor(s_tmpOverlayText).ToString("0.##"); if (!string.Equals(s_lastLoggedTmpLayoutSignature, b, StringComparison.Ordinal)) { s_lastLoggedTmpLayoutSignature = b; LogDebug("TMP pickup layout renderer=" + (s_tmpOverlayUsesDirectUi ? "direct" : "cloned") + " screen=" + ScreenWidth() + "x" + ScreenHeight() + " model=" + model.Width.ToString("0.#") + "x" + model.Height.ToString("0.#") + " root={" + DescribeRectTransform(s_tmpOverlayRectTransform) + "} text={" + DescribeRectTransform(rectTransform) + "} rootScale=" + DescribeVector(GetFieldOrProp(GetFieldOrProp(s_tmpOverlayRoot, "transform"), "lossyScale")) + " textScale=" + DescribeVector(GetFieldOrProp(GetFieldOrProp(s_tmpOverlayTextObject, "transform"), "lossyScale")) + " canvasScale=" + GetCanvasScaleFactor(s_tmpOverlayText).ToString("0.##") + " canvas='" + TruncateForLog(GetUnityObjectName(componentInParent), 60) + "' " + DescribeTextStyleForLog(textStyle)); } } } private static string BuildTmpOverlayDiagnostics(string reason) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("TMP diagnostics reason="); stringBuilder.Append(reason); stringBuilder.Append(" types={"); stringBuilder.Append("GameObject=").Append(TypeNameForLog(s_gameObjectType)); stringBuilder.Append(", RectTransform=").Append(TypeNameForLog(s_rectTransformType)); stringBuilder.Append(", Canvas=").Append(TypeNameForLog(s_canvasType)); stringBuilder.Append(", TMP_Text=").Append(TypeNameForLog(s_tmpTextType)); stringBuilder.Append(", TextMeshProUGUI=").Append(TypeNameForLog(s_textMeshProUguiType)); stringBuilder.Append(", TextAlignmentOptions=").Append(TypeNameForLog(s_textAlignmentOptionsType)); stringBuilder.Append(", TextOverflowModes=").Append(TypeNameForLog(s_textOverflowModesType)); stringBuilder.Append("} methods={"); stringBuilder.Append("FindObjectsOfType=").Append(s_findObjectsOfTypeMethod != null); stringBuilder.Append(", GetComponentInParent=").Append(s_componentGetComponentInParentMethod != null); stringBuilder.Append(", GetComponent=").Append(s_gameObjectGetComponentMethod != null); stringBuilder.Append(", AddComponent=").Append(s_gameObjectAddComponentMethod != null); stringBuilder.Append(", Instantiate=").Append(s_objectInstantiateMethod != null); stringBuilder.Append(", SetActive=").Append(s_gameObjectSetActiveMethod != null); stringBuilder.Append("}"); AppendObjectiveUiDiagnostics(stringBuilder); AppendTmpScanDiagnostics(stringBuilder, s_textMeshProUguiType, "TextMeshProUGUI"); if (s_tmpTextType != null && (object)s_tmpTextType != s_textMeshProUguiType) { AppendTmpScanDiagnostics(stringBuilder, s_tmpTextType, "TMP_Text"); } return stringBuilder.ToString(); } private static void AppendObjectiveUiDiagnostics(StringBuilder builder) { int num = 0; int num2 = 0; int num3 = 0; List list = new List(); try { foreach (object item in FindObjectsOfType(GetObjectiveUiType())) { num++; if (IsActiveInHierarchy(item)) { num2++; } object fieldOrProp = GetFieldOrProp(item, "displayText"); if (fieldOrProp != null) { num3++; if (list.Count < 2) { list.Add(DescribeTmpTextSource(fieldOrProp)); } } else if (list.Count < 2) { list.Add("objectiveUi='" + TruncateForLog(GetUnityObjectName(item), 50) + "' displayText=null"); } } } catch (Exception ex) { list.Add("error=" + ex.GetType().Name + ":" + ex.Message); } builder.Append(" objectiveUI={total=").Append(num).Append(", active=") .Append(num2) .Append(", withDisplayText=") .Append(num3) .Append(", samples=[") .Append(string.Join("; ", list)) .Append("]}"); } private static void AppendTmpScanDiagnostics(StringBuilder builder, Type type, string label) { if (type == null) { builder.Append(" ").Append(label).Append("={type=null}"); return; } int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; int num6 = 0; List list = new List(); try { foreach (object item in FindObjectsOfType(type)) { num++; bool flag = IsActiveInHierarchy(item); if (flag) { num2++; } if (IsPickupTmpOverlayObject(item)) { num5++; continue; } if (!TryConvertFloat(GetFieldOrProp(item, "fontSize"), out var result) || result <= 1f) { num4++; if (list.Count < 4) { list.Add("noFontSize {" + DescribeTmpTextSource(item) + "}"); } continue; } if (GetComponentInParent(item, s_canvasType) == null) { num6++; if (list.Count < 4) { list.Add("noCanvas {" + DescribeTmpTextSource(item) + "}"); } continue; } num3++; if (list.Count < 4) { list.Add("usable active=" + flag + " {" + DescribeTmpTextSource(item) + "}"); } } } catch (Exception ex) { list.Add("error=" + ex.GetType().Name + ":" + ex.Message); } builder.Append(" ").Append(label).Append("={total=") .Append(num) .Append(", active=") .Append(num2) .Append(", usable=") .Append(num3) .Append(", ownOverlay=") .Append(num5) .Append(", noFontSize=") .Append(num4) .Append(", noCanvas=") .Append(num6) .Append(", samples=[") .Append(string.Join("; ", list)) .Append("]}"); } private static bool IsUsableTmpOverlaySource(object sourceText) { if (sourceText == null || !IsActiveInHierarchy(sourceText)) { return false; } if (GetFieldOrProp(sourceText, "gameObject") == null) { return false; } return GetComponentInParent(sourceText, s_canvasType) != null; } private static bool MarkTmpOverlayFailed(string reason) { ResetTmpOverlayState(failed: true); LogWarning("TextMeshPro pickup overlay unavailable: " + reason); return false; } private static void ApplyTmpTextStyle(ObjectiveTextStyle textStyle) { if (s_tmpOverlayText != null) { if (textStyle.Font != null) { SetFieldOrProp(s_tmpOverlayText, "font", textStyle.Font); } if (textStyle.FontSharedMaterial != null) { SetFieldOrProp(s_tmpOverlayText, "fontSharedMaterial", textStyle.FontSharedMaterial); } SetFieldOrProp(s_tmpOverlayText, "fontSize", (float)textStyle.RowFontSize); SetFieldOrProp(s_tmpOverlayText, "color", textStyle.Color ?? ObjectiveColor()); } } private static void ApplyTmpLayout(float width, float height) { if (s_tmpOverlayRectTransform != null && (!(Math.Abs(width - s_tmpLastWidth) < 0.1f) || !(Math.Abs(height - s_tmpLastHeight) < 0.1f))) { s_tmpLastWidth = width; s_tmpLastHeight = height; SetFieldOrProp(s_tmpOverlayRectTransform, "sizeDelta", Vector2(width, height)); SetFieldOrProp(s_tmpOverlayRectTransform, "anchoredPosition", Vector2(-34f, 34f)); } } private static void ApplyTmpText(string text, int styleKey) { if (s_tmpOverlayText != null && (!string.Equals(s_tmpLastText, text, StringComparison.Ordinal) || s_tmpLastStyleKey != styleKey)) { s_tmpLastText = text; s_tmpLastStyleKey = styleKey; SetFieldOrProp(s_tmpOverlayText, "text", text); } } private static void SetTmpOverlayVisible(bool visible) { if (s_tmpOverlayRoot == null || s_gameObjectSetActiveMethod == null || s_tmpOverlayVisible == visible) { return; } try { s_gameObjectSetActiveMethod.Invoke(s_tmpOverlayRoot, new object[1] { visible }); s_tmpOverlayVisible = visible; } catch { ResetTmpOverlayState(failed: false); } } private static void DestroyTmpOverlay() { if (s_tmpOverlayRoot != null && s_objectDestroyMethod != null) { try { s_objectDestroyMethod.Invoke(null, new object[1] { s_tmpOverlayRoot }); } catch { } } ResetTmpOverlayState(failed: false); } private static void ResetTmpOverlayState(bool failed) { s_tmpOverlayRoot = null; s_tmpOverlayTextObject = null; s_tmpOverlayText = null; s_tmpOverlayRectTransform = null; s_tmpOverlayReady = false; s_tmpOverlayVisible = false; s_tmpOverlayFailed = failed; s_tmpOverlayUsesDirectUi = false; s_nextTmpOverlayRetryUtc = (failed ? DateTime.UtcNow.AddSeconds(5.0) : DateTime.MinValue); s_loggedTmpOverlayReady = false; s_tmpLastText = null; s_tmpLastStyleKey = 0; s_tmpLastWidth = 0f; s_tmpLastHeight = 0f; } private static object CreateGameObject(string name, params Type[] components) { if (s_gameObjectWithComponentsCtor != null) { return s_gameObjectWithComponentsCtor.Invoke(new object[2] { name, components }); } object obj = s_gameObjectCtor.Invoke(new object[1] { name }); foreach (Type type in components) { s_gameObjectAddComponentMethod?.Invoke(obj, new object[1] { type }); } return obj; } private static object CloneGameObject(object sourceGameObject, object parentTransform) { if (sourceGameObject == null || parentTransform == null || s_objectInstantiateMethod == null) { return null; } return s_objectInstantiateMethod.Invoke(null, new object[3] { sourceGameObject, parentTransform, false }); } private static object GetComponent(object gameObject, Type componentType) { if (gameObject == null || componentType == null || s_gameObjectGetComponentMethod == null) { return null; } try { return s_gameObjectGetComponentMethod.Invoke(gameObject, new object[1] { componentType }); } catch { return null; } } private static void SetRectTransformAnchors(object rectTransform, float anchorMinX, float anchorMinY, float anchorMaxX, float anchorMaxY, float pivotX, float pivotY) { SetFieldOrProp(rectTransform, "anchorMin", Vector2(anchorMinX, anchorMinY)); SetFieldOrProp(rectTransform, "anchorMax", Vector2(anchorMaxX, anchorMaxY)); SetFieldOrProp(rectTransform, "pivot", Vector2(pivotX, pivotY)); } private static ObjectiveTextStyle ResolveTextStyle() { if (s_useObjectiveTextStyle != null && s_useObjectiveTextStyle.Value) { TryRefreshObjectiveTextStyle(); } if (s_objectiveTextStyle.HasLiveStyle) { return s_objectiveTextStyle; } return ObjectiveTextStyle.FromConfig(Clamp((s_headerFontSize == null) ? 20 : s_headerFontSize.Value, 20, 76), Clamp((s_rowFontSize == null) ? 18 : s_rowFontSize.Value, 18, 72), Clamp((s_footerFontSize == null) ? 16 : s_footerFontSize.Value, 16, 68), ObjectiveColor()); } private static void TryRefreshObjectiveTextStyle() { DateTime utcNow = DateTime.UtcNow; if (utcNow < s_nextObjectiveStyleRefreshUtc) { return; } s_nextObjectiveStyleRefreshUtc = utcNow.AddSeconds((!s_objectiveTextStyle.HasLiveStyle) ? 1 : 2); try { if (TryGetObjectiveTextStyleFromObjectiveUi(out var style) || TryGetObjectiveTextStyleFromTmpScan(out style)) { s_objectiveTextStyle = style; string text = DescribeTextStyleForLog(style); if (!string.Equals(s_lastLoggedObjectiveStyleSignature, text, StringComparison.Ordinal)) { s_lastLoggedObjectiveStyleSignature = text; LogDebug("Objective text style " + text); } } } catch (Exception ex) { LogWarning("objective text style refresh failed: " + ex.GetType().Name + " " + ex.Message); } } private static bool TryGetObjectiveTextStyleFromObjectiveUi(out ObjectiveTextStyle style) { style = ObjectiveTextStyle.Fallback; Type objectiveUiType = GetObjectiveUiType(); if (objectiveUiType == null) { return false; } foreach (object item in FindObjectsOfType(objectiveUiType)) { if (item != null && IsActiveInHierarchy(item) && TryGetTextStyleFromTmpText(GetFieldOrProp(item, "displayText"), "ObjectiveUI", out style)) { return true; } } return false; } private static bool TryGetObjectiveTextStyleFromTmpScan(out ObjectiveTextStyle style) { style = ObjectiveTextStyle.Fallback; Type type = GetTextMeshProUguiType() ?? GetTmpTextType(); if (type == null) { return false; } ObjectiveTextStyle objectiveTextStyle = ObjectiveTextStyle.Fallback; int num = int.MinValue; foreach (object item in FindObjectsOfType(type)) { if (item != null && IsActiveInHierarchy(item) && !IsPickupTmpOverlayObject(item) && TryGetTextStyleFromTmpText(item, "TMP scan", out var style2)) { int num2 = ScoreTmpTextCandidate(item, style2.RowFontSize); if (num2 > num) { num = num2; objectiveTextStyle = style2; } } } if (!objectiveTextStyle.HasLiveStyle) { return false; } style = objectiveTextStyle; return true; } private static int ScoreTmpTextCandidate(object tmpText, int rowFontSize) { int num = rowFontSize; if (TryConvertFloat(GetFieldOrProp(tmpText, "fontSize"), out var result)) { num += (int)Math.Round(result * 10f); } string text = GetFieldOrProp(tmpText, "text") as string; if (!string.IsNullOrWhiteSpace(text)) { if (text.Length > 3) { num += 100; } if (text == text.ToUpperInvariant()) { num += 200; } if (text.IndexOf("Pickups", StringComparison.OrdinalIgnoreCase) >= 0) { num -= 500; } } string text2 = (GetFieldOrProp(GetFieldOrProp(tmpText, "gameObject"), "name") as string) ?? string.Empty; if (text2.IndexOf("objective", StringComparison.OrdinalIgnoreCase) >= 0) { num += 10000; } if (text2.IndexOf("mission", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("task", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("quest", StringComparison.OrdinalIgnoreCase) >= 0) { num += 2000; } if (text2.IndexOf("map", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("seed", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("monster", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("pickup", StringComparison.OrdinalIgnoreCase) >= 0) { num -= 1000; } return num; } private static bool TryGetTextStyleFromTmpText(object tmpText, string source, out ObjectiveTextStyle style) { style = ObjectiveTextStyle.Fallback; if (tmpText == null) { return false; } if (!TryConvertFloat(GetFieldOrProp(tmpText, "fontSize"), out var result) || result <= 1f) { return false; } int num = Clamp((s_objectiveTextScalePercent == null) ? 100 : s_objectiveTextScalePercent.Value, 50, 200); int val = Clamp((s_rowFontSize == null) ? 18 : s_rowFontSize.Value, 18, 72); int val2 = Clamp((s_headerFontSize == null) ? 20 : s_headerFontSize.Value, 20, 76); int val3 = Clamp((s_footerFontSize == null) ? 16 : s_footerFontSize.Value, 16, 68); int num2 = Clamp(Math.Max((int)Math.Round(result * (float)num / 100f), val), 18, 84); int headerFontSize = Clamp(Math.Max(num2 + 2, val2), 20, 88); int footerFontSize = Clamp(Math.Max(num2 - 4, val3), 16, 76); object color = NormalizeTextColor(GetFieldOrProp(tmpText, "color")); object fieldOrProp = GetFieldOrProp(tmpText, "font"); object fieldOrProp2 = GetFieldOrProp(tmpText, "fontSharedMaterial"); style = new ObjectiveTextStyle(hasLiveStyle: true, source, headerFontSize, num2, footerFontSize, color, CopyColorWithAlpha(color, 0.82f), fieldOrProp, fieldOrProp2, tmpText); return true; } private static float GetCanvasScaleFactor(object component) { try { Type canvasType = GetCanvasType(); if (canvasType == null) { return 1f; } object componentInParent = GetComponentInParent(component, canvasType); if (componentInParent == null) { return 1f; } float result; return (TryConvertFloat(GetFieldOrProp(componentInParent, "scaleFactor"), out result) && result > 0f) ? Clamp(result, 0.5f, 4f) : 1f; } catch { return 1f; } } private static object GetComponentInParent(object component, Type requestedType) { if (component == null || requestedType == null || s_componentGetComponentInParentMethod == null) { return null; } try { return s_componentGetComponentInParentMethod.Invoke(component, new object[1] { requestedType }); } catch { return null; } } private static IEnumerable FindObjectsOfType(Type type) { if (type == null || s_findObjectsOfTypeMethod == null) { yield break; } object list = null; try { list = s_findObjectsOfTypeMethod.Invoke(null, new object[1] { type }); } catch { } foreach (object item in EnumerateItems(list)) { if (item != null) { yield return item; } } } private static IEnumerable FindResourceObjectsOfType(Type type) { if (type == null || s_resourcesFindObjectsOfTypeAllMethod == null) { yield break; } object list = null; try { list = s_resourcesFindObjectsOfTypeAllMethod.Invoke(null, new object[1] { type }); } catch { } foreach (object item in EnumerateItems(list)) { if (item != null) { yield return item; } } } private static object FindTmpFontByName(string name) { if (string.IsNullOrWhiteSpace(name)) { return null; } if (s_larkeSansFont != null && string.Equals(GetFieldOrProp(s_larkeSansFont, "name")?.ToString(), name, StringComparison.OrdinalIgnoreCase)) { return s_larkeSansFont; } foreach (object item in FindResourceObjectsOfType(s_tmpFontAssetType)) { if (string.Equals(GetFieldOrProp(item, "name")?.ToString(), name, StringComparison.OrdinalIgnoreCase)) { s_larkeSansFont = item; return item; } } return null; } private static bool HasAnyDisplayedPickup() { if (s_entries.Count <= 0 && GetCollectibleRows().Count <= 0) { return GetUnlockRows().Count > 0; } return true; } private static List GetCollectibleRows() { List list = new List(); if (s_showCollectibles == null || !s_showCollectibles.Value) { return list; } AddCollectibleRow(list, "Tickets"); AddCollectibleRow(list, "Reroll Coins"); return list; } private static void AddCollectibleRow(List rows, string label) { if (s_collectiblesFound.TryGetValue(label, out var value) && value > 0) { rows.Add(new CollectibleEntry(label, value)); } } private static List GetUnlockRows() { if (s_showUnlockPickups == null || !s_showUnlockPickups.Value) { return new List(); } return s_unlockEntries.ToList(); } private static string FormatEntry(PickupEntry entry) { string text = entry.Name; if (entry.Count > 1) { text = text + " x" + entry.Count; } if (s_showIds != null && s_showIds.Value && entry.Id >= 0) { text = text + " #" + entry.Id; } if (s_showHandLabels != null && s_showHandLabels.Value) { if (entry.LeftHand && entry.RightHand) { text += " L/R"; } else if (entry.LeftHand) { text += " L"; } else if (entry.RightHand) { text += " R"; } } return text; } private static string FormatUnlockEntry(UnlockEntry entry) { if (string.Equals(entry.Category, "Stamps", StringComparison.OrdinalIgnoreCase)) { return "Stamps: " + entry.Count; } string text = entry.Category + ": " + entry.Name; if (entry.Id >= 0) { text = text + " #" + entry.Id; } if (string.Equals(entry.Category, "Cosmetic", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(entry.ItemClass)) { text = text + " (" + entry.ItemClass + ")"; } if (entry.Count > 1) { text = text + " x" + entry.Count; } return text; } private static object GetLocalRndPlayerInventory() { object fieldOrProp = GetFieldOrProp(GetStaticFieldOrProp(GetPlayerNetworkSyncType(), "instance"), "RndPlayerInventory"); if (fieldOrProp != null) { return fieldOrProp; } fieldOrProp = GetFieldOrProp(GetFieldOrProp(GetStaticFieldOrProp(GetGameManagerType(), "instance"), "mainPlayer"), "RndPlayerInventory"); if (fieldOrProp != null) { return fieldOrProp; } return FindOwnedRndPlayerInventory(); } private static object FindOwnedRndPlayerInventory() { try { if (!EnsureReflection() || s_findObjectsOfTypeMethod == null) { return null; } Type rndPlayerInventoryType = GetRndPlayerInventoryType(); if (rndPlayerInventoryType == null) { return null; } object? list = s_findObjectsOfTypeMethod.Invoke(null, new object[1] { rndPlayerInventoryType }); object obj = null; foreach (object item in EnumerateItems(list)) { if (item != null) { obj = obj ?? item; if (TryConvertBool(GetFieldOrProp(GetFieldOrProp(item, "playerNetworkSync"), "isOwned"), out var result) && result) { return item; } } } return obj; } catch { return null; } } private static int GetHandItemId(string propertyName) { try { if (TryConvertInt(GetFieldOrProp(GetStaticFieldOrProp(FindType("InventoryController"), "Instance"), propertyName), out var result)) { return result; } } catch { } return -1; } private static bool TryGetItemId(object item, out int id) { if (TryConvertInt(GetFieldOrProp(item, "SerializedID"), out id)) { return true; } return TryConvertInt(GetFieldOrProp(item, "ID"), out id); } private static string GetItemDisplayName(object item) { try { string text = item.GetType().GetMethod("GetItemName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(item, Array.Empty()) as string; if (!string.IsNullOrWhiteSpace(text)) { return text; } } catch { } string text2 = GetFieldOrProp(item, "name") as string; if (!string.IsNullOrWhiteSpace(text2)) { return CleanUnityName(text2); } text2 = GetFieldOrProp(item, "nameLocalisationKey") as string; if (!string.IsNullOrWhiteSpace(text2)) { return text2; } return null; } private static string CleanUnityName(string name) { if (string.IsNullOrWhiteSpace(name)) { return name; } string text = name.Trim(); if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(0, text.Length - 7).Trim(); } if (text.StartsWith("SO_", StringComparison.OrdinalIgnoreCase)) { text = text.Substring(3); } return text.Replace('_', ' '); } private static bool IsTabMenuPressed() { if (IsTabMenuObjectActive()) { return true; } try { if (s_gameGetButton != null && s_showTabMenuButton != null && TryConvertBool(s_gameGetButton.Invoke(null, new object[1] { s_showTabMenuButton }), out var result)) { return result; } } catch { } try { if (s_inputGetKey != null && s_keyCodeTab != null && TryConvertBool(s_inputGetKey.Invoke(null, new object[1] { s_keyCodeTab }), out var result2)) { return result2; } } catch { } return false; } private static bool IsTabMenuObjectActive() { try { if (IsActiveInHierarchy(GetFieldOrProp(GetStaticFieldOrProp(FindType("UI_Controller"), "instance"), "tabMenu"))) { return true; } GameObject val = GameObject.Find("Global/Global Canvas/Player_UI/Tab Menu"); return (Object)(object)val != (Object)null && val.activeInHierarchy; } catch { return false; } } private static bool IsActiveInHierarchy(object obj) { if (obj == null) { return false; } bool result; return TryConvertBool(GetFieldOrProp(GetFieldOrProp(obj, "gameObject") ?? obj, "activeInHierarchy"), out result) && result; } private static bool EnsureReflection() { if (s_reflectionReady) { return true; } try { s_rectType = FindType("UnityEngine.Rect"); s_colorType = FindType("UnityEngine.Color"); s_vector2Type = FindType("UnityEngine.Vector2"); s_guiType = FindType("UnityEngine.GUI"); s_screenType = FindType("UnityEngine.Screen"); s_inputType = FindType("UnityEngine.Input"); s_keyCodeType = FindType("UnityEngine.KeyCode"); s_objectType = FindType("UnityEngine.Object"); s_componentType = FindType("UnityEngine.Component"); s_gameObjectType = FindType("UnityEngine.GameObject"); s_transformType = FindType("UnityEngine.Transform"); s_rectTransformType = FindType("UnityEngine.RectTransform"); s_objectiveUiType = FindType("Objectives.ObjectiveUI"); s_tmpTextType = FindTmpType("TMP_Text"); s_tmpFontAssetType = FindTmpType("TMP_FontAsset"); s_textMeshProUguiType = FindTmpType("TextMeshProUGUI"); s_canvasType = FindType("UnityEngine.Canvas"); s_renderModeType = FindType("UnityEngine.RenderMode"); s_resourcesType = FindType("UnityEngine.Resources"); s_textAlignmentOptionsType = FindTmpType("TextAlignmentOptions"); s_textOverflowModesType = FindTmpType("TextOverflowModes"); s_labyrinthineInputManagerType = FindType("LabyrinthineInputManager"); s_inputButtonType = FindType("InputButton"); if (s_rectType == null || s_colorType == null || s_guiType == null) { return LogReflectionNotReady(); } s_rectCtor = s_rectType.GetConstructor(new Type[4] { typeof(float), typeof(float), typeof(float), typeof(float) }); s_colorCtor = s_colorType.GetConstructor(new Type[4] { typeof(float), typeof(float), typeof(float), typeof(float) }); s_vector2Ctor = s_vector2Type?.GetConstructor(new Type[2] { typeof(float), typeof(float) }); s_gameObjectCtor = s_gameObjectType?.GetConstructor(new Type[1] { typeof(string) }); s_gameObjectWithComponentsCtor = s_gameObjectType?.GetConstructor(new Type[2] { typeof(string), typeof(Type[]) }); s_guiColorProperty = s_guiType.GetProperty("color", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); s_screenWidthProperty = s_screenType?.GetProperty("width", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); s_screenHeightProperty = s_screenType?.GetProperty("height", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); s_guiSkinProperty = s_guiType.GetProperty("skin", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); s_guiLabel = s_guiType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Label" && m.GetParameters().Length == 2 && m.GetParameters()[0].ParameterType == s_rectType && m.GetParameters()[1].ParameterType == typeof(string)); s_inputGetKey = s_inputType?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetKey" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == s_keyCodeType); if (s_keyCodeType != null) { s_keyCodeTab = Enum.Parse(s_keyCodeType, "Tab"); } if (s_labyrinthineInputManagerType != null && s_inputButtonType != null) { s_gameGetButton = s_labyrinthineInputManagerType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetButton" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == s_inputButtonType); s_showTabMenuButton = Enum.Parse(s_inputButtonType, "ShowTabMenu"); } s_findObjectsOfTypeMethod = s_objectType?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "FindObjectsOfType" && !m.ContainsGenericParameters && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Type)); s_componentGetComponentInParentMethod = s_componentType?.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetComponentInParent" && !m.ContainsGenericParameters && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Type)); s_gameObjectGetComponentMethod = s_gameObjectType?.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "GetComponent" && !m.ContainsGenericParameters && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Type)); s_gameObjectAddComponentMethod = s_gameObjectType?.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "AddComponent" && !m.ContainsGenericParameters && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Type)); s_gameObjectSetActiveMethod = s_gameObjectType?.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "SetActive" && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(bool)); s_transformSetParentMethod = s_transformType?.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "SetParent" && m.GetParameters().Length == 2 && m.GetParameters()[1].ParameterType == typeof(bool)); s_transformSetAsLastSiblingMethod = s_transformType?.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "SetAsLastSibling" && m.GetParameters().Length == 0); s_objectInstantiateMethod = s_objectType?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Instantiate" && !m.ContainsGenericParameters && m.GetParameters().Length == 3 && m.GetParameters()[1].ParameterType == s_transformType && m.GetParameters()[2].ParameterType == typeof(bool)); s_objectDestroyMethod = s_objectType?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "Destroy" && m.GetParameters().Length == 1); s_objectDontDestroyOnLoadMethod = s_objectType?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "DontDestroyOnLoad" && m.GetParameters().Length == 1); s_resourcesFindObjectsOfTypeAllMethod = s_resourcesType?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "FindObjectsOfTypeAll" && !m.ContainsGenericParameters && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Type)); if (s_rectCtor == null || s_colorCtor == null || s_guiLabel == null) { return LogReflectionNotReady(); } InitLabelSkinAccessors(); s_reflectionReady = true; return true; } catch (Exception ex) { LogWarning("Unity reflection init failed: " + ex.GetType().Name + " " + ex.Message); return false; } } private static void InitLabelSkinAccessors() { try { object obj = s_guiSkinProperty?.GetValue(null); PropertyInfo propertyInfo = ((s_guiSkinLabelProperty == null && obj != null) ? obj.GetType().GetProperty("label", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) : s_guiSkinLabelProperty); if (propertyInfo != null) { s_guiSkinLabelProperty = propertyInfo; } object obj2 = s_guiSkinLabelProperty?.GetValue(obj); if (obj2 != null) { Type type = obj2.GetType(); s_styleFontSizeProperty = type.GetProperty("fontSize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); s_styleNormalProperty = type.GetProperty("normal", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); s_styleStateTextColorProperty = (s_styleNormalProperty?.GetValue(obj2))?.GetType().GetProperty("textColor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } } catch { } } private static bool LogReflectionNotReady() { LogWarning("Unity IMGUI reflection is not ready yet."); return false; } private static void LabelShadowed(float x, float y, float width, float height, string text, int fontSize, object color) { Label(x + 1f, y + 1f, width, height, text, Color(0f, 0f, 0f, 0.7f), fontSize); Label(x, y, width, height, text, color, fontSize); } private static object ObjectiveColor() { return Color(1f, 1f, 1f, 1f); } private static object MutedObjectiveColor() { return Color(1f, 1f, 1f, 0.82f); } private static object NormalizeTextColor(object color) { if (TryReadColor(color, out var r, out var g, out var b, out var a)) { return Color(Clamp01(r), Clamp01(g), Clamp01(b), Clamp01((a <= 0f) ? 1f : a)); } return ObjectiveColor(); } private static object CopyColorWithAlpha(object color, float alpha) { if (TryReadColor(color, out var r, out var g, out var b, out var _)) { return Color(Clamp01(r), Clamp01(g), Clamp01(b), Clamp01(alpha)); } return Color(1f, 1f, 1f, Clamp01(alpha)); } private static bool TryReadColor(object color, out float r, out float g, out float b, out float a) { r = 1f; g = 1f; b = 1f; a = 1f; if (color == null) { return false; } if (TryConvertFloat(GetFieldOrProp(color, "r"), out r) && TryConvertFloat(GetFieldOrProp(color, "g"), out g) && TryConvertFloat(GetFieldOrProp(color, "b"), out b)) { return TryConvertFloat(GetFieldOrProp(color, "a"), out a); } return false; } private static void Label(float x, float y, float width, float height, string text, object color, int fontSize) { SetGuiColor(color); object oldFontSize = null; object oldTextColor = null; object skinLabelStyle = GetSkinLabelStyle(); object obj = ((skinLabelStyle == null) ? null : s_styleNormalProperty?.GetValue(skinLabelStyle)); try { if (skinLabelStyle != null && s_styleFontSizeProperty != null) { oldFontSize = s_styleFontSizeProperty.GetValue(skinLabelStyle); } if (obj != null && s_styleStateTextColorProperty != null) { oldTextColor = s_styleStateTextColorProperty.GetValue(obj); } SetLabelFontSize(skinLabelStyle, fontSize); SetLabelTextColor(obj, color); s_guiLabel.Invoke(null, new object[2] { Rect(x, y, width, height), text }); } finally { RestoreLabelStyle(skinLabelStyle, obj, oldFontSize, oldTextColor); SetGuiColor(Color(1f, 1f, 1f, 1f)); } } private static object GetSkinLabelStyle() { try { object obj = s_guiSkinProperty?.GetValue(null); return (obj == null) ? null : s_guiSkinLabelProperty?.GetValue(obj); } catch { return null; } } private static void SetLabelFontSize(object style, int fontSize) { try { s_styleFontSizeProperty?.SetValue(style, fontSize); } catch { } } private static void SetLabelTextColor(object normal, object color) { try { s_styleStateTextColorProperty?.SetValue(normal, color); } catch { } } private static void RestoreLabelStyle(object style, object normal, object oldFontSize, object oldTextColor) { try { if (style != null && oldFontSize != null) { s_styleFontSizeProperty?.SetValue(style, oldFontSize); } if (normal != null && oldTextColor != null) { s_styleStateTextColorProperty?.SetValue(normal, oldTextColor); } } catch { } } private static object Rect(float x, float y, float width, float height) { return s_rectCtor.Invoke(new object[4] { x, y, width, height }); } private static object Color(float r, float g, float b, float a) { return s_colorCtor.Invoke(new object[4] { r, g, b, a }); } private static object Vector2(float x, float y) { return s_vector2Ctor.Invoke(new object[2] { x, y }); } private static void SetGuiColor(object color) { try { s_guiColorProperty?.SetValue(null, color); } catch { } } private static int ScreenWidth() { try { return (s_screenWidthProperty == null) ? 1920 : Convert.ToInt32(s_screenWidthProperty.GetValue(null)); } catch { return 1920; } } private static int ScreenHeight() { try { return (s_screenHeightProperty == null) ? 1080 : Convert.ToInt32(s_screenHeightProperty.GetValue(null)); } catch { return 1080; } } private static Type GetPlayerNetworkSyncType() { if (s_playerNetworkSyncType == null) { s_playerNetworkSyncType = FindType("PlayerNetworkSync"); } return s_playerNetworkSyncType; } private static Type GetGameManagerType() { if (s_gameManagerType == null) { s_gameManagerType = FindType("GameManager"); } return s_gameManagerType; } private static Type GetRndPlayerInventoryType() { if (s_rndPlayerInventoryType == null) { s_rndPlayerInventoryType = FindType("ValkoGames.Labyrinthine.Cases.Inventory.RndPlayerInventory"); } return s_rndPlayerInventoryType; } private static Type GetRndCollectibleType() { if (s_rndCollectibleType == null) { s_rndCollectibleType = FindType("ValkoGames.Labyrinthine.Cases.Interactions.RndCollectible"); } return s_rndCollectibleType; } private static Type GetCustomizationPickupType() { if (s_customizationPickupType == null) { s_customizationPickupType = FindType("CharacterCustomization.CustomizationPickup"); } return s_customizationPickupType; } private static Type GetCustomCaseItemUnlockType() { if (s_customCaseItemUnlockType == null) { s_customCaseItemUnlockType = FindType("ValkoGames.Labyrinthine.Cases.Custom.CustomCaseItemUnlock"); } return s_customCaseItemUnlockType; } private static Type GetCustomizationManagerType() { if (s_customizationManagerType == null) { s_customizationManagerType = FindType("CharacterCustomization.CustomizationManager"); } return s_customizationManagerType; } private static Type GetLocalisationManagerType() { if (s_localisationManagerType == null) { s_localisationManagerType = FindType("Localisation.LocalisationManager") ?? FindType("LocalisationManager"); } return s_localisationManagerType; } private static Type GetObjectiveUiType() { if (s_objectiveUiType == null) { s_objectiveUiType = FindType("Objectives.ObjectiveUI"); } return s_objectiveUiType; } private static Type GetTmpTextType() { if (s_tmpTextType == null) { s_tmpTextType = FindTmpType("TMP_Text"); } return s_tmpTextType; } private static Type GetTextMeshProUguiType() { if (s_textMeshProUguiType == null) { s_textMeshProUguiType = FindTmpType("TextMeshProUGUI"); } return s_textMeshProUguiType; } private static Type GetCanvasType() { if (s_canvasType == null) { s_canvasType = FindType("UnityEngine.Canvas"); } return s_canvasType; } private static Type FindTmpType(string typeName) { return FindType("Il2CppTMPro." + typeName) ?? FindType("TMPro." + typeName); } private static Type FindType(string fullName) { string il2cppName = (fullName.StartsWith("Il2Cpp.", StringComparison.Ordinal) ? fullName : ("Il2Cpp." + fullName)); string shortName = (fullName.Contains(".") ? fullName.Substring(fullName.LastIndexOf('.') + 1) : fullName); Type type = Type.GetType(fullName) ?? Type.GetType(il2cppName); if (type != null) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type2 = assembly.GetType(fullName, throwOnError: false) ?? assembly.GetType(il2cppName, throwOnError: false); if (type2 != null) { return type2; } } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { Type[] source; try { source = assembly2.GetTypes(); } catch (ReflectionTypeLoadException ex) { source = ex.Types.Where((Type t) => t != null).ToArray(); } catch { continue; } Type type3 = source.FirstOrDefault((Type t) => t.Name == shortName || t.FullName == fullName || t.FullName == il2cppName); if (type3 != null) { return type3; } } return null; } private static object GetStaticFieldOrProp(Type type, string name) { if (type == null) { return null; } try { PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(null); } } catch { } try { return type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); } catch { return null; } } private static object GetFieldOrProp(object obj, string name) { if (obj == null) { return null; } Type type = obj.GetType(); try { PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(obj); } } catch { } try { return type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj); } catch { return null; } } private static bool SetFieldOrProp(object obj, string name, object value) { if (obj == null) { return false; } Type type = obj.GetType(); try { PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { property.SetValue(obj, value); return true; } } catch { } try { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { field.SetValue(obj, value); return true; } } catch { } return false; } private static object FindFirstObjectOfType(Type type) { if (type == null) { return null; } try { foreach (object item in EnumerateItems(((s_objectType ?? FindType("UnityEngine.Object"))?.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == "FindObjectsOfType" && !m.ContainsGenericParameters && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(Type)))?.Invoke(null, new object[1] { type }))) { if (item != null) { return item; } } } catch { } return null; } private static void ForEachListItem(object list, Action action) { foreach (object item in EnumerateItems(list)) { action(item); } } private static IEnumerable EnumerateItems(object list) { if (list == null) { yield break; } if (list is Array array) { foreach (object item in array) { yield return item; } yield break; } if (list is IEnumerable enumerable) { foreach (object item2 in enumerable) { yield return item2; } yield break; } if (!TryConvertInt(GetFieldOrProp(list, "Count"), out var count)) { TryConvertInt(GetFieldOrProp(list, "Length"), out count); } if (count <= 0) { yield break; } MethodInfo getItem = list.GetType().GetMethod("get_Item", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (getItem == null) { yield break; } for (int i = 0; i < count; i++) { object obj = null; try { obj = getItem.Invoke(list, new object[1] { i }); } catch { } if (obj != null) { yield return obj; } } } private static bool TryConvertInt(object value, out int result) { try { if (value == null) { result = 0; return false; } result = Convert.ToInt32(value); return true; } catch { result = 0; return false; } } private static bool TryConvertFloat(object value, out float result) { try { if (value == null) { result = 0f; return false; } result = Convert.ToSingle(value); return true; } catch { result = 0f; return false; } } private static bool TryConvertBool(object value, out bool result) { try { if (value == null) { result = false; return false; } result = Convert.ToBoolean(value); return true; } catch { result = false; return false; } } private static void LogOverlayRenderer(string renderer, string details) { string b = renderer + "|" + details; if (!string.Equals(s_lastLoggedRendererSignature, b, StringComparison.Ordinal)) { s_lastLoggedRendererSignature = b; LogDebug("Pickup overlay renderer=" + renderer + " " + details); } } private static string GetFallbackRendererReason() { if (!IsTextMeshProOverlayEnabled()) { return "reason=TextMeshPro disabled"; } if (s_tmpOverlayFailed) { return "reason=TextMeshPro unavailable/retrying"; } if (!s_tmpOverlayReady) { return "reason=TextMeshPro not ready"; } return "reason=TextMeshPro ready"; } private static string DescribeTextStyleForLog(ObjectiveTextStyle style) { string text = (string.IsNullOrWhiteSpace(style.Source) ? "unknown" : style.Source); return "styleSource=" + text + " headerFont=" + style.HeaderFontSize + " rowFont=" + style.RowFontSize + " footerFont=" + style.FooterFontSize + " tmpSource={" + DescribeTmpTextSource(style.SourceText) + "}"; } private static string DescribeTmpTextSource(object tmpText) { if (tmpText == null) { return "none"; } try { string text = GetUnityObjectName(GetFieldOrProp(tmpText, "gameObject")) ?? GetUnityObjectName(tmpText) ?? tmpText.GetType().Name; string text2 = GetFieldOrProp(tmpText, "text") as string; float result; string text3 = (TryConvertFloat(GetFieldOrProp(tmpText, "fontSize"), out result) ? result.ToString("0.##") : "?"); string text4 = GetCanvasScaleFactor(tmpText).ToString("0.##"); return "name='" + TruncateForLog(text, 60) + "' fontSize=" + text3 + " canvasScale=" + text4 + " text='" + TruncateForLog(text2, 60) + "'"; } catch { return tmpText.GetType().Name; } } private static string DescribeRectTransform(object rectTransform) { if (rectTransform == null) { return "none"; } try { object fieldOrProp = GetFieldOrProp(rectTransform, "rect"); return "name='" + TruncateForLog(GetUnityObjectName(rectTransform), 50) + "' anchorMin=" + DescribeVector(GetFieldOrProp(rectTransform, "anchorMin")) + " anchorMax=" + DescribeVector(GetFieldOrProp(rectTransform, "anchorMax")) + " pivot=" + DescribeVector(GetFieldOrProp(rectTransform, "pivot")) + " anchoredPosition=" + DescribeVector(GetFieldOrProp(rectTransform, "anchoredPosition")) + " sizeDelta=" + DescribeVector(GetFieldOrProp(rectTransform, "sizeDelta")) + " rect=" + DescribeRect(fieldOrProp); } catch { return rectTransform.GetType().Name; } } private static string DescribeRect(object rect) { if (rect == null) { return "none"; } float result; string obj = (TryConvertFloat(GetFieldOrProp(rect, "width"), out result) ? result.ToString("0.#") : "?"); float result2; string text = (TryConvertFloat(GetFieldOrProp(rect, "height"), out result2) ? result2.ToString("0.#") : "?"); return obj + "x" + text; } private static string DescribeVector(object vector) { if (vector == null) { return "none"; } float result; bool num = TryConvertFloat(GetFieldOrProp(vector, "x"), out result); float result2; bool flag = TryConvertFloat(GetFieldOrProp(vector, "y"), out result2); float result3; bool flag2 = TryConvertFloat(GetFieldOrProp(vector, "z"), out result3); if (!num && !flag && !flag2) { return vector.ToString(); } if (!flag2) { return "(" + result.ToString("0.###") + "," + result2.ToString("0.###") + ")"; } return "(" + result.ToString("0.###") + "," + result2.ToString("0.###") + "," + result3.ToString("0.###") + ")"; } private static bool IsPickupTmpOverlayObject(object tmpText) { if (tmpText == null) { return false; } if (tmpText == s_tmpOverlayText) { return true; } object fieldOrProp = GetFieldOrProp(tmpText, "gameObject"); if (fieldOrProp == s_tmpOverlayTextObject || fieldOrProp == s_tmpOverlayRoot) { return true; } return (GetUnityObjectName(fieldOrProp) ?? string.Empty).IndexOf("kp0hyc PickupTabOverlay", StringComparison.OrdinalIgnoreCase) >= 0; } private static string GetUnityObjectName(object obj) { if (obj == null) { return null; } try { string text = GetFieldOrProp(obj, "name") as string; if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } object fieldOrProp = GetFieldOrProp(obj, "gameObject"); if (fieldOrProp != null && fieldOrProp != obj) { text = GetFieldOrProp(fieldOrProp, "name") as string; if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } } } catch { } return null; } private static string TruncateForLog(string text, int maxLength) { if (string.IsNullOrWhiteSpace(text)) { return string.Empty; } string text2 = text.Replace("\r", " ").Replace("\n", " ").Trim(); if (text2.Length <= maxLength) { return text2; } return text2.Substring(0, Math.Max(0, maxLength - 3)) + "..."; } private static string TypeNameForLog(Type type) { object obj; if (!(type == null)) { obj = type.FullName; if (obj == null) { return type.Name; } } else { obj = "null"; } return (string)obj; } private static int Clamp(int value, int min, int max) { if (value < min) { return min; } if (value <= max) { return value; } return max; } private static float Clamp(float value, float min, float max) { if (value < min) { return min; } if (!(value > max)) { return value; } return max; } private static float Clamp01(float value) { if (value < 0f) { return 0f; } if (!(value > 1f)) { return value; } return 1f; } private static void LogWarning(string message) { if (!(DateTime.UtcNow < s_nextWarningUtc)) { s_nextWarningUtc = DateTime.UtcNow.AddSeconds(10.0); MelonLogger.Warning(message); } } private static void LogDebug(string message) { if (IsDebugLoggingEnabled()) { MelonLogger.Msg("[Debug] " + message); } } private static bool IsDebugLoggingEnabled() { if (s_debugLogging == null || !s_debugLogging.Value) { if (s_legacyDebugLogging != null) { return s_legacyDebugLogging.Value; } return false; } return true; } }