Decompiled source of WhiteKnuckleItemTooltipsRU v0.3.0
BepInEx\plugins\WhiteKnuckleItemTooltipsRU\WhiteKnuckleItemTooltips.dll
Decompiled 7 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; using WhiteKnuckleItemTooltips.Compatibility; using WhiteKnuckleItemTooltips.Core; using WhiteKnuckleItemTooltips.Detection; using WhiteKnuckleItemTooltips.Localization; using WhiteKnuckleItemTooltips.Runtime; using WhiteKnuckleItemTooltips.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("WhiteKnuckleItemTooltips")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.3.0.0")] [assembly: AssemblyInformationalVersion("0.3.0")] [assembly: AssemblyProduct("WhiteKnuckleItemTooltips")] [assembly: AssemblyTitle("WhiteKnuckleItemTooltips")] [assembly: AssemblyVersion("0.3.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace WhiteKnuckleItemTooltips { [BepInPlugin("kos.whiteknuckle.itemtooltipsru", "White Knuckle Item Tooltips", "0.3.0")] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "kos.whiteknuckle.itemtooltipsru"; public const string PluginName = "White Knuckle Item Tooltips"; public const string PluginVersion = "0.3.0"; private static MethodInfo? _itemObjectOnHoverTarget; private static int _instrumentedHoverCallSites; private void Awake() { RuntimeSettings settings = BindConfig(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"White Knuckle Item Tooltips 0.3.0 loading..."); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Reference mode: exact assemblies from this White Knuckle installation."); try { RuntimeService.Install(new RuntimeService(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, ((BaseUnityPlugin)this).Info.Location, settings)); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Could not initialize Item Tooltips runtime service: {arg}"); return; } InstallNativeHoverBridge(); RuntimeHost.RegisterSceneCallback(); RuntimeHost.EnsureAlive(); } private void InstallNativeHoverBridge() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_011f: Expected O, but got Unknown try { Harmony val = new Harmony("kos.whiteknuckle.itemtooltipsru.interactionbridge"); Type type = AccessTools.TypeByName("Item_Object"); _itemObjectOnHoverTarget = ((type == null) ? null : AccessTools.Method(type, "OnHover", (Type[])null, (Type[])null)); if (_itemObjectOnHoverTarget == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Interaction bridge failed: Item_Object.OnHover was not found."); return; } Type type2 = AccessTools.TypeByName("ENT_Player"); MethodInfo methodInfo = ((type2 == null) ? null : AccessTools.Method(type2, "InteractCheck", new Type[1] { typeof(int) }, (Type[])null)); if (methodInfo == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Interaction bridge failed: ENT_Player.InteractCheck(int) was not found."); return; } MethodInfo methodInfo2 = AccessTools.Method(typeof(Plugin), "InteractCheckPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(Plugin), "InteractCheckTranspiler", (Type[])null, (Type[])null); if (methodInfo2 == null || methodInfo3 == null) { ((BaseUnityPlugin)this).Logger.LogError((object)"Interaction bridge failed: patch methods were not found."); return; } _instrumentedHoverCallSites = 0; val.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Interaction bridge installed on ENT_Player.InteractCheck(int). " + $"Instrumented Item_Object.OnHover call sites: {_instrumentedHoverCallSites}.")); if (_instrumentedHoverCallSites == 0) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"No Item_Object.OnHover call sites were instrumented; runtime hover callbacks are unlikely to fire."); } } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Could not install interaction bridge: {arg}"); } } public static void InteractCheckPrefix() { RuntimeService.Current?.InteractionTick(); } public static IEnumerable<CodeInstruction> InteractCheckTranspiler(IEnumerable<CodeInstruction> instructions) { MethodInfo bridge = AccessTools.Method(typeof(Plugin), "ItemObjectHoverFromCallSite", (Type[])null, (Type[])null); MethodInfo target = _itemObjectOnHoverTarget; foreach (CodeInstruction instruction in instructions) { if (!(target != null) || !(bridge != null) || (!(instruction.opcode == OpCodes.Call) && !(instruction.opcode == OpCodes.Callvirt)) || !object.Equals(instruction.operand, target)) { yield return instruction; continue; } CodeInstruction val = new CodeInstruction(OpCodes.Dup, (object)null); MoveInstructionMetadata(instruction, val, "labels"); MoveInstructionMetadata(instruction, val, "blocks"); yield return val; yield return instruction; yield return new CodeInstruction(OpCodes.Call, (object)bridge); _instrumentedHoverCallSites++; } } private static void MoveInstructionMetadata(CodeInstruction source, CodeInstruction destination, string fieldName) { FieldInfo fieldInfo = AccessTools.Field(typeof(CodeInstruction), fieldName); if (fieldInfo == null) { return; } IList list = fieldInfo.GetValue(source) as IList; IList list2 = fieldInfo.GetValue(destination) as IList; if (list != null && list2 != null && list.Count != 0) { object[] array = new object[list.Count]; list.CopyTo(array, 0); object[] array2 = array; foreach (object value in array2) { list2.Add(value); } list.Clear(); } } public static void ItemObjectHoverFromCallSite(object itemObject) { RuntimeService.Current?.ItemHover(itemObject); } private RuntimeSettings BindConfig() { //IL_006e: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable item tooltips."); ConfigEntry<string> language = ((BaseUnityPlugin)this).Config.Bind<string>("Language", "Language", string.Empty, "Item Tooltips language. Valid persisted values: English or Russian. Use the main-menu LANGUAGE button to change it."); ConfigEntry<bool> fullInformation = ((BaseUnityPlugin)this).Config.Bind<bool>("Information", "FullInformation", false, "Show spoiler-bearing detailed descriptions. Toggled in game with ToggleFullInformationKey."); ConfigEntry<KeyboardShortcut> toggleFullInformationKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Information", "ToggleFullInformationKey", new KeyboardShortcut((KeyCode)120, Array.Empty<KeyCode>()), "Hotkey that toggles spoiler-safe/full item descriptions."); ConfigEntry<bool> inventoryTooltipEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "InventoryTooltipEnabled", true, "Show tooltips when hovering items in the inventory."); ConfigEntry<bool> worldTooltipEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "WorldTooltipEnabled", true, "Show tooltips when aiming at pickup items in the world."); ConfigEntry<bool> useCustomTooltipCard = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "UseCustomTooltipCard", true, "Use the dedicated item tooltip card instead of White Knuckle's native description field."); ConfigEntry<int> tooltipMaxWidth = ((BaseUnityPlugin)this).Config.Bind<int>("UI", "TooltipMaxWidth", 620, "Maximum tooltip card width in screen pixels."); return new RuntimeSettings(enabled, language, fullInformation, toggleFullInformationKey, inventoryTooltipEnabled, worldTooltipEnabled, useCustomTooltipCard, tooltipMaxWidth); } } } namespace WhiteKnuckleItemTooltips.UI { public sealed class ResolvedTooltipFont { public Font Font { get; } public string Source { get; } public ResolvedTooltipFont(Font font, string source) { Font = font ?? throw new ArgumentNullException("font"); Source = source ?? string.Empty; } } public sealed class FontResolver { private ResolvedTooltipFont? _cached; private const string CyrillicProbe = "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя"; public ResolvedTooltipFont Resolve() { if (_cached != null) { return _cached; } if (IsWkLocLoaded()) { Font val = Resources.FindObjectsOfTypeAll<Font>().Where(IsCyrillicCapable).OrderBy(FontPriority) .FirstOrDefault(); if ((Object)(object)val != (Object)null && FontPriority(val) < 100) { return _cached = new ResolvedTooltipFont(val, "WKLoc loaded font: " + ((Object)val).name); } } try { Font val2 = Font.CreateDynamicFontFromOSFont(new string[2] { "Segoe UI", "Arial" }, 18); if ((Object)(object)val2 != (Object)null && IsCyrillicCapable(val2)) { return _cached = new ResolvedTooltipFont(val2, "Windows dynamic font: " + ((Object)val2).name); } } catch { } string[] array = new string[2] { "Arial.ttf", "LegacyRuntime.ttf" }; foreach (string text in array) { try { Font builtinResource = Resources.GetBuiltinResource<Font>(text); if ((Object)(object)builtinResource != (Object)null && IsCyrillicCapable(builtinResource)) { return _cached = new ResolvedTooltipFont(builtinResource, "Unity builtin font: " + text); } } catch { } } Font val3 = ((IEnumerable<Font>)Resources.FindObjectsOfTypeAll<Font>()).FirstOrDefault((Func<Font, bool>)IsCyrillicCapable); if ((Object)(object)val3 != (Object)null) { return _cached = new ResolvedTooltipFont(val3, "Loaded Cyrillic font: " + ((Object)val3).name); } Font val4 = Resources.FindObjectsOfTypeAll<Font>().FirstOrDefault(); if ((Object)(object)val4 != (Object)null) { return _cached = new ResolvedTooltipFont(val4, "Last-resort loaded font: " + ((Object)val4).name); } throw new InvalidOperationException("No usable Unity font was found for the tooltip card."); } private static bool IsWkLocLoaded() { return Chainloader.PluginInfos.Values.Any((PluginInfo info) => string.Equals(info.Metadata.Name, "WKLoc", StringComparison.OrdinalIgnoreCase)); } private static bool IsCyrillicCapable(Font font) { try { font.RequestCharactersInTexture("АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя", 18, (FontStyle)0); return ((IEnumerable<char>)"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя").All((Func<char, bool>)font.HasCharacter); } catch { return false; } } private static int FontPriority(Font font) { string text = ((Object)font).name ?? string.Empty; if (text.Equals("main-legacy", StringComparison.OrdinalIgnoreCase)) { return 0; } if (text.Equals("main-legacy-os", StringComparison.OrdinalIgnoreCase)) { return 1; } if (text.IndexOf("main", StringComparison.OrdinalIgnoreCase) >= 0) { return 10; } return 100; } } public static class MenuCreditsContent { public const string TwitchPurpleHex = "#9146FF"; public const string AuthorMarkup = "Item Tooltips Mod by <color=#9146FF><b>KoshGamer</b></color>"; public const string TwitchUrl = "twitch.tv/koshgamer"; public const float TopOffsetPixels = 132f; public const float OverallOpacity = 0.9f; public static bool IsMainMenuScene(string? sceneName) { return string.Equals(sceneName, "Main-Menu", StringComparison.OrdinalIgnoreCase); } } public sealed class MenuCreditsView { private const float Width = 430f; private const float Height = 54f; private const float RightMargin = 18f; private static readonly Color TwitchPurple = new Color(29f / 51f, 14f / 51f, 1f, 1f); private static readonly Color UrlPurple = new Color(0.82f, 0.74f, 1f, 0.96f); private readonly GameObject _canvasObject; private readonly GameObject _creditsObject; private readonly Texture2D _twitchIconTexture; public bool IsVisible { get { if ((Object)(object)_creditsObject != (Object)null) { return _creditsObject.activeSelf; } return false; } } public MenuCreditsView(Font font) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)font == (Object)null) { throw new ArgumentNullException("font"); } _canvasObject = new GameObject("WK Item Tooltips Menu Credits Canvas", new Type[1] { typeof(RectTransform) }); Object.DontDestroyOnLoad((Object)(object)_canvasObject); Canvas obj = _canvasObject.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 31990; CanvasScaler obj2 = _canvasObject.AddComponent<CanvasScaler>(); obj2.uiScaleMode = (ScaleMode)0; obj2.scaleFactor = 1f; _creditsObject = new GameObject("KoshGamer Menu Credit", new Type[1] { typeof(RectTransform) }); _creditsObject.transform.SetParent(_canvasObject.transform, false); RectTransform component = _creditsObject.GetComponent<RectTransform>(); component.anchorMin = new Vector2(1f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(1f, 1f); component.anchoredPosition = new Vector2(-18f, -132f); component.sizeDelta = new Vector2(430f, 54f); CanvasGroup obj3 = _creditsObject.AddComponent<CanvasGroup>(); obj3.alpha = 0.9f; obj3.interactable = false; obj3.blocksRaycasts = false; Text obj4 = CreateText("Author", font, 16, (TextAnchor)2, new Color(0.96f, 0.96f, 0.94f, 0.96f)); obj4.text = "Item Tooltips Mod by <color=#9146FF><b>KoshGamer</b></color>"; obj4.supportRichText = true; SetTopRightRect(((Graphic)obj4).rectTransform, 0f, 0f, 430f, 23f); _twitchIconTexture = CreateTwitchIconTexture(); GameObject val = new GameObject("Twitch Icon", new Type[1] { typeof(RectTransform) }); val.transform.SetParent(_creditsObject.transform, false); RawImage obj5 = val.AddComponent<RawImage>(); obj5.texture = (Texture)(object)_twitchIconTexture; ((Graphic)obj5).color = Color.white; ((Graphic)obj5).raycastTarget = false; SetTopRightRect(((Graphic)obj5).rectTransform, -194f, -28f, 18f, 18f); Text obj6 = CreateText("Twitch URL", font, 15, (TextAnchor)2, UrlPurple); obj6.text = "twitch.tv/koshgamer"; SetTopRightRect(((Graphic)obj6).rectTransform, 0f, -26f, 188f, 22f); _creditsObject.SetActive(false); } public void SetVisible(bool visible) { if ((Object)(object)_creditsObject != (Object)null && _creditsObject.activeSelf != visible) { _creditsObject.SetActive(visible); } } public void Destroy() { if ((Object)(object)_twitchIconTexture != (Object)null) { Object.Destroy((Object)(object)_twitchIconTexture); } if ((Object)(object)_canvasObject != (Object)null) { Object.Destroy((Object)(object)_canvasObject); } } private Text CreateText(string name, Font font, int size, TextAnchor alignment, Color color) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) }); val.transform.SetParent(_creditsObject.transform, false); Text obj = val.AddComponent<Text>(); obj.font = font; obj.fontSize = size; obj.alignment = alignment; ((Graphic)obj).color = color; obj.horizontalOverflow = (HorizontalWrapMode)1; obj.verticalOverflow = (VerticalWrapMode)1; ((Graphic)obj).raycastTarget = false; obj.supportRichText = true; Shadow obj2 = val.AddComponent<Shadow>(); obj2.effectColor = new Color(0f, 0f, 0f, 0.72f); obj2.effectDistance = new Vector2(1f, -1f); obj2.useGraphicAlpha = true; return obj; } private static void SetTopRightRect(RectTransform rect, float x, float y, float width, float height) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) rect.anchorMin = new Vector2(1f, 1f); rect.anchorMax = new Vector2(1f, 1f); rect.pivot = new Vector2(1f, 1f); rect.anchoredPosition = new Vector2(x, y); rect.sizeDelta = new Vector2(width, height); } private static Texture2D CreateTwitchIconTexture() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(32, 32, (TextureFormat)4, false) { name = "KoshGamer Twitch Icon", filterMode = (FilterMode)0, wrapMode = (TextureWrapMode)1 }; Color32 val2 = default(Color32); ((Color32)(ref val2))..ctor((byte)0, (byte)0, (byte)0, (byte)0); Color32 color = default(Color32); ((Color32)(ref color))..ctor((byte)145, (byte)70, byte.MaxValue, byte.MaxValue); Color32 color2 = default(Color32); ((Color32)(ref color2))..ctor(byte.MaxValue, byte.MaxValue, byte.MaxValue, byte.MaxValue); Color32[] array = (Color32[])(object)new Color32[1024]; for (int i = 0; i < array.Length; i++) { array[i] = val2; } FillRect(array, 32, 4, 5, 27, 26, color); FillRect(array, 32, 7, 2, 12, 7, color); FillRect(array, 32, 10, 0, 12, 4, color); FillRect(array, 32, 8, 9, 23, 22, color2); FillRect(array, 32, 12, 13, 14, 19, color); FillRect(array, 32, 18, 13, 20, 19, color); val.SetPixels32(array); val.Apply(false, false); return val; } private static void FillRect(Color32[] pixels, int stride, int minX, int minY, int maxX, int maxY, Color32 color) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) for (int i = minY; i <= maxY; i++) { for (int j = minX; j <= maxX; j++) { pixels[i * stride + j] = color; } } } } public static class MenuLanguageButtonContent { public const float Width = 230f; public const float Height = 54f; public const float RightMargin = 18f; public const float TopOffsetPixels = 198f; public const int FontSize = 20; public const int HoverHintFontSize = 13; public static string Label(TooltipLanguage language) { if (language != TooltipLanguage.Russian) { return "LANGUAGE: EN"; } return "LANGUAGE: RU"; } public static string HoverHint(TooltipLanguage language) { if (language != TooltipLanguage.Russian) { return "Click to switch language"; } return "Нажми, чтобы сменить язык"; } } public sealed class MenuLanguageButtonView { private readonly GameObject _canvasObject; private readonly GameObject _rootObject; private readonly Text _label; private readonly Text _hoverHint; private readonly Action _onClick; private TooltipLanguage _language; public bool IsVisible { get { if ((Object)(object)_rootObject != (Object)null) { return _rootObject.activeSelf; } return false; } } public MenuLanguageButtonView(Font font, Action onClick) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0243: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Expected O, but got Unknown //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Expected O, but got Unknown //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_036f: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Expected O, but got Unknown //IL_0475: Unknown result type (might be due to invalid IL or missing references) //IL_04b9: Unknown result type (might be due to invalid IL or missing references) //IL_04ce: Unknown result type (might be due to invalid IL or missing references) //IL_04e3: Unknown result type (might be due to invalid IL or missing references) //IL_04f8: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)font == (Object)null) { throw new ArgumentNullException("font"); } _onClick = onClick ?? throw new ArgumentNullException("onClick"); _canvasObject = new GameObject("WK Item Tooltips Language Canvas", new Type[1] { typeof(RectTransform) }); Object.DontDestroyOnLoad((Object)(object)_canvasObject); Canvas obj = _canvasObject.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 31995; CanvasScaler obj2 = _canvasObject.AddComponent<CanvasScaler>(); obj2.uiScaleMode = (ScaleMode)0; obj2.scaleFactor = 1f; _canvasObject.AddComponent<GraphicRaycaster>(); _rootObject = new GameObject("LanguageButton", new Type[1] { typeof(RectTransform) }); _rootObject.transform.SetParent(_canvasObject.transform, false); RectTransform component = _rootObject.GetComponent<RectTransform>(); component.anchorMin = new Vector2(1f, 1f); component.anchorMax = new Vector2(1f, 1f); component.pivot = new Vector2(1f, 1f); component.anchoredPosition = new Vector2(-18f, -198f); component.sizeDelta = new Vector2(230f, 54f); Image val = _rootObject.AddComponent<Image>(); ((Graphic)val).color = new Color(0.045f, 0.035f, 0.065f, 0.88f); ((Graphic)val).raycastTarget = true; Outline outline = _rootObject.AddComponent<Outline>(); ((Shadow)outline).effectColor = new Color(29f / 51f, 14f / 51f, 1f, 0.95f); ((Shadow)outline).effectDistance = new Vector2(2f, -2f); ((Shadow)outline).useGraphicAlpha = true; Button obj3 = _rootObject.AddComponent<Button>(); ((Selectable)obj3).interactable = true; ((Selectable)obj3).targetGraphic = (Graphic)(object)val; ((Selectable)obj3).transition = (Transition)1; ColorBlock defaultColorBlock = ColorBlock.defaultColorBlock; ((ColorBlock)(ref defaultColorBlock)).normalColor = new Color(0.88f, 0.84f, 0.95f, 1f); ((ColorBlock)(ref defaultColorBlock)).highlightedColor = new Color(1f, 0.84f, 1f, 1f); ((ColorBlock)(ref defaultColorBlock)).pressedColor = new Color(0.67f, 0.52f, 0.82f, 1f); ((ColorBlock)(ref defaultColorBlock)).disabledColor = new Color(0.45f, 0.45f, 0.45f, 0.5f); ((ColorBlock)(ref defaultColorBlock)).colorMultiplier = 1.2f; ((ColorBlock)(ref defaultColorBlock)).fadeDuration = 0.08f; ((Selectable)obj3).colors = defaultColorBlock; ((UnityEvent)obj3.onClick).AddListener((UnityAction)delegate { _onClick(); }); GameObject val2 = new GameObject("Label", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(_rootObject.transform, false); _label = val2.AddComponent<Text>(); _label.font = font; _label.fontSize = 20; _label.fontStyle = (FontStyle)1; _label.alignment = (TextAnchor)4; ((Graphic)_label).color = Color.white; ((Graphic)_label).raycastTarget = false; _label.horizontalOverflow = (HorizontalWrapMode)1; _label.verticalOverflow = (VerticalWrapMode)1; RectTransform component2 = val2.GetComponent<RectTransform>(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = new Vector2(10f, 6f); component2.offsetMax = new Vector2(-10f, -6f); Shadow obj4 = val2.AddComponent<Shadow>(); obj4.effectColor = new Color(0f, 0f, 0f, 0.8f); obj4.effectDistance = new Vector2(1f, -1f); GameObject hintObject = new GameObject("HoverHint", new Type[1] { typeof(RectTransform) }); hintObject.transform.SetParent(_rootObject.transform, false); _hoverHint = hintObject.AddComponent<Text>(); _hoverHint.font = font; _hoverHint.fontSize = 13; _hoverHint.alignment = (TextAnchor)2; ((Graphic)_hoverHint).color = new Color(0.92f, 0.86f, 1f, 0.95f); ((Graphic)_hoverHint).raycastTarget = false; _hoverHint.horizontalOverflow = (HorizontalWrapMode)1; _hoverHint.verticalOverflow = (VerticalWrapMode)1; RectTransform component3 = hintObject.GetComponent<RectTransform>(); component3.anchorMin = new Vector2(1f, 0f); component3.anchorMax = new Vector2(1f, 0f); component3.pivot = new Vector2(1f, 1f); component3.anchoredPosition = new Vector2(0f, -7f); component3.sizeDelta = new Vector2(260f, 24f); hintObject.SetActive(false); EventTrigger obj5 = _rootObject.AddComponent<EventTrigger>(); obj5.triggers = new List<Entry>(); obj5.triggers.Add(CreateTrigger((EventTriggerType)0, delegate { hintObject.SetActive(true); })); obj5.triggers.Add(CreateTrigger((EventTriggerType)1, delegate { hintObject.SetActive(false); })); obj5.triggers.Add(CreateTrigger((EventTriggerType)2, delegate { //IL_001a: Unknown result type (might be due to invalid IL or missing references) ((Shadow)outline).effectColor = new Color(0.72f, 0.55f, 1f, 1f); })); obj5.triggers.Add(CreateTrigger((EventTriggerType)3, delegate { //IL_001a: Unknown result type (might be due to invalid IL or missing references) ((Shadow)outline).effectColor = new Color(29f / 51f, 14f / 51f, 1f, 0.95f); })); SetLanguage(TooltipLanguage.English); _rootObject.SetActive(false); } public void SetLanguage(TooltipLanguage language) { _language = language; _label.text = MenuLanguageButtonContent.Label(language); _hoverHint.text = MenuLanguageButtonContent.HoverHint(language); } public void SetVisible(bool visible) { if ((Object)(object)_rootObject != (Object)null && _rootObject.activeSelf != visible) { _rootObject.SetActive(visible); } } public void Destroy() { if ((Object)(object)_canvasObject != (Object)null) { Object.Destroy((Object)(object)_canvasObject); } } private static Entry CreateTrigger(EventTriggerType type, UnityAction<BaseEventData> callback) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown Entry val = new Entry { eventID = type, callback = new TriggerEvent() }; ((UnityEvent<BaseEventData>)(object)val.callback).AddListener(callback); return val; } } public static class NativeDescriptionFormatter { public static string Format(ResolvedItemInfo info, bool fullInformation, string? statusLine = null) { return Format(info, fullInformation, TooltipLanguage.Russian, statusLine); } public static string Format(ResolvedItemInfo info, bool fullInformation, TooltipLanguage language, string? statusLine = null) { if (info == null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(512); if (!string.IsNullOrWhiteSpace(statusLine)) { stringBuilder.Append("<color=#D6C58A><b>").Append(Escape(statusLine.Trim())).AppendLine("</b></color>"); } string value = (string.IsNullOrWhiteSpace(info.Name) ? LocalizedStrings.UnknownItemName(language) : info.Name.Trim()); stringBuilder.Append("<size=110%><b>").Append(Escape(value)).AppendLine("</b></size>"); if (!string.IsNullOrWhiteSpace(info.Category)) { stringBuilder.Append("<color=#B7B3AC><i>").Append(Escape(info.Category.Trim())).AppendLine("</i></color>"); } string value2 = info.DescriptionFor(fullInformation); if (!string.IsNullOrWhiteSpace(value2)) { stringBuilder.AppendLine(); stringBuilder.Append(Escape(value2)); } foreach (ItemStat stat in info.Stats) { if (stat != null && !string.IsNullOrWhiteSpace(stat.Label) && !string.IsNullOrWhiteSpace(stat.Value)) { stringBuilder.AppendLine(); stringBuilder.Append("<b>").Append(Escape(stat.Label.Trim())).Append(":</b> ") .Append(Escape(stat.Value.Trim())); } } return stringBuilder.ToString().TrimEnd(); } private static string Escape(string value) { return (value ?? string.Empty).Replace("&", "&").Replace("<", "<").Replace(">", ">"); } } public sealed class ToastView { private readonly GameObject _canvasObject; private readonly GameObject _panelObject; private readonly Text _text; private float _remaining; public ToastView(Font font) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Expected O, but got Unknown //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0124: 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_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Expected O, but got Unknown //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)font == (Object)null) { throw new ArgumentNullException("font"); } _canvasObject = new GameObject("WK Item Tooltips Toast Canvas", new Type[1] { typeof(RectTransform) }); Object.DontDestroyOnLoad((Object)(object)_canvasObject); Canvas obj = _canvasObject.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 32001; CanvasScaler obj2 = _canvasObject.AddComponent<CanvasScaler>(); obj2.uiScaleMode = (ScaleMode)0; obj2.scaleFactor = 1f; _panelObject = new GameObject("Toast", new Type[1] { typeof(RectTransform) }); _panelObject.transform.SetParent(_canvasObject.transform, false); Image obj3 = _panelObject.AddComponent<Image>(); ((Graphic)obj3).color = new Color(0.045f, 0.04f, 0.04f, 0.9f); ((Graphic)obj3).raycastTarget = false; RectTransform component = _panelObject.GetComponent<RectTransform>(); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.86f); component.anchorMax = val; component.anchorMin = val; component.pivot = new Vector2(0.5f, 0.5f); component.sizeDelta = new Vector2(340f, 48f); GameObject val2 = new GameObject("Text", new Type[1] { typeof(RectTransform) }); val2.transform.SetParent(_panelObject.transform, false); _text = val2.AddComponent<Text>(); _text.font = font; _text.fontSize = 18; _text.alignment = (TextAnchor)4; ((Graphic)_text).color = Color.white; ((Graphic)_text).raycastTarget = false; RectTransform component2 = val2.GetComponent<RectTransform>(); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = new Vector2(10f, 4f); component2.offsetMax = new Vector2(-10f, -4f); _panelObject.SetActive(false); } public void Show(string text, float seconds = 1.4f) { _text.text = text ?? string.Empty; _remaining = Mathf.Max(0.1f, seconds); _panelObject.SetActive(true); } public void Tick(float unscaledDeltaTime) { if (_panelObject.activeSelf) { _remaining -= Mathf.Max(0f, unscaledDeltaTime); if (_remaining <= 0f) { _panelObject.SetActive(false); } } } public void Destroy() { if ((Object)(object)_canvasObject != (Object)null) { Object.Destroy((Object)(object)_canvasObject); } } } public sealed class TooltipCardView { private readonly Font _font; private readonly GameObject _canvasObject; private readonly GameObject _panelObject; private readonly RectTransform _panelRect; private readonly Text _nameText; private readonly Text _categoryText; private readonly Text _descriptionText; private readonly List<Text> _statRows = new List<Text>(); private Vector2 _anchor; private TooltipAnchorMode _mode; private float _maxWidth = 620f; public bool IsVisible { get { if ((Object)(object)_panelObject != (Object)null) { return _panelObject.activeSelf; } return false; } } public TooltipCardView(Font font) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) _font = font ?? throw new ArgumentNullException("font"); _canvasObject = new GameObject("WK Item Tooltips Card Canvas", new Type[1] { typeof(RectTransform) }); Object.DontDestroyOnLoad((Object)(object)_canvasObject); Canvas obj = _canvasObject.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 32000; CanvasScaler obj2 = _canvasObject.AddComponent<CanvasScaler>(); obj2.uiScaleMode = (ScaleMode)0; obj2.scaleFactor = 1f; _panelObject = new GameObject("Item Tooltip Card", new Type[1] { typeof(RectTransform) }); _panelObject.transform.SetParent(_canvasObject.transform, false); Image obj3 = _panelObject.AddComponent<Image>(); ((Graphic)obj3).color = new Color(0.045f, 0.04f, 0.04f, 0.93f); ((Graphic)obj3).raycastTarget = false; _panelRect = _panelObject.GetComponent<RectTransform>(); _panelRect.anchorMin = Vector2.zero; _panelRect.anchorMax = Vector2.zero; _panelRect.pivot = new Vector2(0f, 1f); VerticalLayoutGroup obj4 = _panelObject.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj4).padding = new RectOffset(16, 16, 14, 14); ((HorizontalOrVerticalLayoutGroup)obj4).spacing = 5f; ((HorizontalOrVerticalLayoutGroup)obj4).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj4).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj4).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)obj4).childForceExpandWidth = true; ContentSizeFitter obj5 = _panelObject.AddComponent<ContentSizeFitter>(); obj5.horizontalFit = (FitMode)0; obj5.verticalFit = (FitMode)2; _nameText = CreateText("Name", 22, (TextAnchor)0, new Color(0.98f, 0.97f, 0.94f, 1f)); _nameText.fontStyle = (FontStyle)1; _categoryText = CreateText("Category", 15, (TextAnchor)0, new Color(0.72f, 0.7f, 0.67f, 1f)); _categoryText.fontStyle = (FontStyle)2; _descriptionText = CreateText("Description", 18, (TextAnchor)0, new Color(0.94f, 0.94f, 0.92f, 1f)); _panelObject.SetActive(false); } public void Show(TooltipDisplayModel model, Vector2 anchor, TooltipAnchorMode mode, float maxWidth) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) if (model == null) { Hide(); return; } _anchor = anchor; _mode = mode; _maxWidth = maxWidth; ApplyModel(model); _panelObject.SetActive(true); RebuildAndPosition(); } public void Refresh(TooltipDisplayModel model) { if (model == null) { Hide(); } else if (IsVisible) { ApplyModel(model); RebuildAndPosition(); } } public void Reposition(Vector2 anchor, TooltipAnchorMode mode) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _anchor = anchor; _mode = mode; if (IsVisible) { RebuildAndPosition(); } } public void Hide() { if ((Object)(object)_panelObject != (Object)null) { _panelObject.SetActive(false); } } public void Destroy() { if ((Object)(object)_canvasObject != (Object)null) { Object.Destroy((Object)(object)_canvasObject); } } private Text CreateText(string objectName, int size, TextAnchor alignment, Color color) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(objectName, new Type[1] { typeof(RectTransform) }); val.transform.SetParent(_panelObject.transform, false); Text obj = val.AddComponent<Text>(); obj.font = _font; obj.fontSize = size; obj.alignment = alignment; ((Graphic)obj).color = color; obj.horizontalOverflow = (HorizontalWrapMode)0; obj.verticalOverflow = (VerticalWrapMode)1; ((Graphic)obj).raycastTarget = false; obj.supportRichText = true; obj.lineSpacing = 1.05f; return obj; } private void ApplyModel(TooltipDisplayModel model) { _nameText.text = Escape(model.Name).ToUpperInvariant(); _categoryText.text = Escape(model.Category); ((Component)_categoryText).gameObject.SetActive(!string.IsNullOrWhiteSpace(model.Category)); _descriptionText.text = Escape(model.Description); ((Component)_descriptionText).gameObject.SetActive(!string.IsNullOrWhiteSpace(model.Description)); for (int i = 0; i < model.Stats.Count; i++) { Text orCreateStatRow = GetOrCreateStatRow(i); orCreateStatRow.text = "<b>" + Escape(model.Stats[i].Label) + ":</b> " + Escape(model.Stats[i].Value); ((Component)orCreateStatRow).gameObject.SetActive(true); } for (int j = model.Stats.Count; j < _statRows.Count; j++) { ((Component)_statRows[j]).gameObject.SetActive(false); } } private Text GetOrCreateStatRow(int index) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) while (_statRows.Count <= index) { Text item = CreateText($"Stat {_statRows.Count + 1}", 17, (TextAnchor)0, new Color(0.9f, 0.89f, 0.86f, 1f)); _statRows.Add(item); } return _statRows[index]; } private void RebuildAndPosition() { //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Max(320f, (float)Screen.width - 24f); float num2 = Mathf.Clamp(_maxWidth, 320f, num); float num3 = Mathf.Min(360f, num2); float num4 = Mathf.Clamp((float)Screen.width * 0.28f, num3, num2); _panelRect.SetSizeWithCurrentAnchors((Axis)0, num4); Canvas.ForceUpdateCanvases(); LayoutRebuilder.ForceRebuildLayoutImmediate(_panelRect); Canvas.ForceUpdateCanvases(); Rect rect = _panelRect.rect; Vector2 size = ((Rect)(ref rect)).size; TooltipPosition tooltipPosition = TooltipLayout.CalculatePosition(_anchor.x, _anchor.y, _mode, size.x, size.y, Screen.width, Screen.height); _panelRect.anchoredPosition = new Vector2(tooltipPosition.X, tooltipPosition.Y); } private static string Escape(string value) { return (value ?? string.Empty).Replace("<", "(").Replace(">", ")"); } } public sealed class TooltipController { private readonly ItemResolver _resolver; private readonly InventoryItemDetector _inventoryDetector; private readonly WorldItemDetector _worldDetector; private readonly TooltipView _view; private readonly Dictionary<string, ResolvedItemInfo> _cache = new Dictionary<string, ResolvedItemInfo>(StringComparer.OrdinalIgnoreCase); private string _renderedStableId = string.Empty; private bool _renderedFullInformation; private TooltipAnchorMode _renderedMode; public TooltipController(ItemResolver resolver, InventoryItemDetector inventoryDetector, WorldItemDetector worldDetector, TooltipView view) { _resolver = resolver ?? throw new ArgumentNullException("resolver"); _inventoryDetector = inventoryDetector ?? throw new ArgumentNullException("inventoryDetector"); _worldDetector = worldDetector ?? throw new ArgumentNullException("worldDetector"); _view = view ?? throw new ArgumentNullException("view"); } public void Tick(bool enabled, bool inventoryEnabled, bool worldEnabled, bool fullInformation, float worldInspectionDistance) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (!enabled) { Hide(); return; } if (inventoryEnabled && _inventoryDetector.TryGetHoveredItem(out RuntimeItemSnapshot snapshot, out Vector2 screenPosition)) { Render(snapshot, fullInformation, screenPosition, TooltipAnchorMode.Inventory); return; } bool flag = Cursor.visible && (int)Cursor.lockState != 1; if (worldEnabled && !flag && _worldDetector.TryGetAimedItem(worldInspectionDistance, out RuntimeItemSnapshot snapshot2, out Vector2 screenPosition2)) { Render(snapshot2, fullInformation, screenPosition2, TooltipAnchorMode.World); } else { Hide(); } } public void ForceRefresh() { _renderedStableId = string.Empty; } public void ClearCache() { _cache.Clear(); ForceRefresh(); } private void Render(RuntimeItemSnapshot snapshot, bool fullInformation, Vector2 anchor, TooltipAnchorMode mode) { //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) string text = ItemIdNormalizer.Normalize(snapshot.StableIdCandidate); if (string.IsNullOrEmpty(text)) { Hide(); return; } if (!_cache.TryGetValue(text, out ResolvedItemInfo value) || HasRicherRuntimeData(snapshot, value)) { value = _resolver.Resolve(snapshot); _cache[text] = value; } if (_view.IsVisible && string.Equals(_renderedStableId, text, StringComparison.OrdinalIgnoreCase) && _renderedFullInformation == fullInformation && _renderedMode == mode) { _view.Reposition(anchor, mode); return; } _view.Show(value, fullInformation, anchor, mode); _renderedStableId = text; _renderedFullInformation = fullInformation; _renderedMode = mode; } private static bool HasRicherRuntimeData(RuntimeItemSnapshot snapshot, ResolvedItemInfo cached) { int num = 0; foreach (ItemStat stat in cached.Stats) { if (stat.Source == ItemStatSource.Runtime) { num++; } } return snapshot.Stats.Count > num; } private void Hide() { _view.Hide(); _renderedStableId = string.Empty; } } public sealed class TooltipDisplayModel { public string Name { get; set; } = "Unknown item"; public string Category { get; set; } = string.Empty; public string Description { get; set; } = string.Empty; public IReadOnlyList<TooltipStatRow> Stats { get; set; } = new List<TooltipStatRow>(); } public sealed class TooltipStatRow { public string Label { get; } public string Value { get; } public TooltipStatRow(string label, string value) { Label = label; Value = value; } } public static class TooltipFormatter { public static TooltipDisplayModel Format(ResolvedItemInfo info, bool fullInformation) { return Format(info, fullInformation, TooltipLanguage.Russian); } public static TooltipDisplayModel Format(ResolvedItemInfo info, bool fullInformation, TooltipLanguage language) { if (info == null) { throw new ArgumentNullException("info"); } List<TooltipStatRow> list = new List<TooltipStatRow>(); foreach (ItemStat stat in info.Stats) { if (stat != null && !string.IsNullOrWhiteSpace(stat.Label) && !string.IsNullOrWhiteSpace(stat.Value)) { list.Add(new TooltipStatRow(Sanitize(stat.Label.Trim()), Sanitize(stat.Value.Trim()))); } } return new TooltipDisplayModel { Name = Sanitize(string.IsNullOrWhiteSpace(info.Name) ? LocalizedStrings.UnknownItemName(language) : info.Name.Trim()), Category = Sanitize(info.Category?.Trim() ?? string.Empty), Description = Sanitize(info.DescriptionFor(fullInformation)), Stats = list }; } internal static string Sanitize(string value) { return (value ?? string.Empty).Replace("•", "-").Replace("—", "-").Replace("–", "-") .Replace("−", "-") .Replace("→", "->") .Replace("\r\n", "\n") .Trim(); } } public enum TooltipAnchorMode { Inventory, World } public readonly struct TooltipPosition { public float X { get; } public float Y { get; } public TooltipPosition(float x, float y) { X = x; Y = y; } } public static class TooltipLayout { public static TooltipPosition CalculatePosition(float anchorX, float anchorY, TooltipAnchorMode mode, float cardWidth, float cardHeight, float screenWidth, float screenHeight, float margin = 12f) { if (IsInvalid(anchorX) || IsInvalid(anchorY) || cardWidth <= 0f || cardHeight <= 0f || screenWidth <= 0f || screenHeight <= 0f) { return new TooltipPosition(margin, Max(margin + cardHeight, screenHeight - margin)); } float value = ((mode == TooltipAnchorMode.Inventory) ? (anchorX + 26f) : (anchorX + 54f)); float value2 = ((mode == TooltipAnchorMode.Inventory) ? (anchorY - 22f) : (anchorY + 170f)); float max = Max(margin, screenWidth - cardWidth - margin); float num = cardHeight + margin; return new TooltipPosition(y: Clamp(value2, num, Max(num, screenHeight - margin)), x: Clamp(value, margin, max)); } private static bool IsInvalid(float value) { if (!float.IsNaN(value)) { return float.IsInfinity(value); } return true; } private static float Max(float left, float right) { if (!(left > right)) { return right; } return left; } private static float Clamp(float value, float min, float max) { if (value < min) { return min; } if (value > max) { return max; } return value; } } public sealed class TooltipView { private const float CardWidth = 400f; private const float HorizontalPadding = 16f; private const float VerticalPadding = 14f; private const float ScreenMargin = 12f; private readonly GameObject _canvasObject; private readonly GameObject _panelObject; private readonly RectTransform _panelRect; private readonly Text _text; private float _currentHeight = 100f; public bool IsVisible => _panelObject.activeSelf; public TooltipView() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) _canvasObject = new GameObject("WK Item Tooltips Canvas"); Object.DontDestroyOnLoad((Object)(object)_canvasObject); Canvas obj = _canvasObject.AddComponent<Canvas>(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 32000; CanvasScaler obj2 = _canvasObject.AddComponent<CanvasScaler>(); obj2.uiScaleMode = (ScaleMode)0; obj2.scaleFactor = 1f; _panelObject = new GameObject("Item Tooltip"); _panelObject.transform.SetParent(_canvasObject.transform, false); Image obj3 = _panelObject.AddComponent<Image>(); ((Graphic)obj3).color = new Color(0.045f, 0.04f, 0.04f, 0.93f); ((Graphic)obj3).raycastTarget = false; _panelRect = _panelObject.GetComponent<RectTransform>(); _panelRect.anchorMin = Vector2.zero; _panelRect.anchorMax = Vector2.zero; _panelRect.pivot = new Vector2(0f, 1f); _panelRect.sizeDelta = new Vector2(400f, _currentHeight); GameObject val = new GameObject("Text"); val.transform.SetParent(_panelObject.transform, false); _text = val.AddComponent<Text>(); _text.font = LoadBuiltinFont(); _text.fontSize = 18; ((Graphic)_text).color = new Color(0.94f, 0.94f, 0.92f, 1f); _text.alignment = (TextAnchor)0; _text.horizontalOverflow = (HorizontalWrapMode)0; _text.verticalOverflow = (VerticalWrapMode)1; _text.supportRichText = true; ((Graphic)_text).raycastTarget = false; _text.lineSpacing = 1.05f; RectTransform component = val.GetComponent<RectTransform>(); component.anchorMin = new Vector2(0f, 0f); component.anchorMax = new Vector2(1f, 1f); component.offsetMin = new Vector2(16f, 14f); component.offsetMax = new Vector2(-16f, -14f); _panelObject.SetActive(false); } public void Show(ResolvedItemInfo info, bool fullInformation, Vector2 anchor, TooltipAnchorMode mode) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) if (info == null) { Hide(); return; } _text.text = BuildMarkup(info, fullInformation); _panelObject.SetActive(true); Canvas.ForceUpdateCanvases(); float num = _text.preferredHeight + 28f; _currentHeight = Mathf.Clamp(num, 88f, 620f); _panelRect.sizeDelta = new Vector2(400f, _currentHeight); Reposition(anchor, mode); } public void Reposition(Vector2 anchor, TooltipAnchorMode mode) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) if (_panelObject.activeSelf) { Vector2 val = default(Vector2); if (mode == TooltipAnchorMode.Inventory) { ((Vector2)(ref val))..ctor(anchor.x + 26f, anchor.y - 22f); } else { ((Vector2)(ref val))..ctor(anchor.x + 54f, anchor.y + 170f); } float num = Mathf.Max(12f, (float)Screen.width - 400f - 12f); float num2 = _currentHeight + 12f; float num3 = Mathf.Max(num2, (float)Screen.height - 12f); val.x = Mathf.Clamp(val.x, 12f, num); val.y = Mathf.Clamp(val.y, num2, num3); _panelRect.anchoredPosition = val; } } public void Hide() { if ((Object)(object)_panelObject != (Object)null) { _panelObject.SetActive(false); } } public void Destroy() { if ((Object)(object)_canvasObject != (Object)null) { Object.Destroy((Object)(object)_canvasObject); } } private static string BuildMarkup(ResolvedItemInfo info, bool fullInformation) { StringBuilder stringBuilder = new StringBuilder(512); string value = EscapeRichText(string.IsNullOrWhiteSpace(info.Name) ? "Unknown item" : info.Name.ToUpperInvariant()); stringBuilder.Append("<size=22><b>").Append(value).AppendLine("</b></size>"); if (!string.IsNullOrWhiteSpace(info.Category)) { stringBuilder.Append("<size=15><color=#B7B3AC><i>").Append(EscapeRichText(info.Category)).AppendLine("</i></color></size>"); } string text = info.DescriptionFor(fullInformation); if (!string.IsNullOrWhiteSpace(text)) { stringBuilder.AppendLine(); stringBuilder.Append(EscapeRichText(text.Trim())); } if (info.Stats.Count > 0) { stringBuilder.AppendLine().AppendLine(); for (int i = 0; i < info.Stats.Count; i++) { ItemStat itemStat = info.Stats[i]; if (!string.IsNullOrWhiteSpace(itemStat.Label) && !string.IsNullOrWhiteSpace(itemStat.Value)) { stringBuilder.Append("<b>").Append(EscapeRichText(itemStat.Label.Trim())).Append(":</b> ") .Append(EscapeRichText(itemStat.Value.Trim())); if (i < info.Stats.Count - 1) { stringBuilder.AppendLine(); } } } } return stringBuilder.ToString().TrimEnd(); } private static string EscapeRichText(string value) { return value.Replace("<", "<").Replace(">", ">"); } private static Font LoadBuiltinFont() { try { Font builtinResource = Resources.GetBuiltinResource<Font>("Arial.ttf"); if ((Object)(object)builtinResource != (Object)null) { return builtinResource; } } catch { } try { Font builtinResource2 = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf"); if ((Object)(object)builtinResource2 != (Object)null) { return builtinResource2; } } catch { } Font[] array = Resources.FindObjectsOfTypeAll<Font>(); if (array.Length != 0 && (Object)(object)array[0] != (Object)null) { return array[0]; } throw new InvalidOperationException("No usable Unity font was found for item tooltips."); } } } namespace WhiteKnuckleItemTooltips.Runtime { public static class RuntimeHost { private static readonly RuntimeHostState State = new RuntimeHostState(); private static RuntimeHostBehaviour? _behaviour; private static bool _sceneCallbackRegistered; private static int _nextGeneration = 1; public static RuntimeHostBehaviour EnsureAlive() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown if ((Object)(object)_behaviour != (Object)null) { return _behaviour; } GameObject val = new GameObject("WhiteKnuckleItemTooltips.RuntimeHost") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)val); _behaviour = val.AddComponent<RuntimeHostBehaviour>(); State.MarkCreated(_nextGeneration++); return _behaviour; } public static void RegisterSceneCallback() { if (!_sceneCallbackRegistered) { SceneManager.sceneLoaded += HandleSceneLoaded; _sceneCallbackRegistered = true; } } public static void HandleSceneLoaded(Scene scene, LoadSceneMode mode) { EnsureAlive(); RuntimeService.Current?.SceneLoaded(((Scene)(ref scene)).name); } internal static void NotifyDestroyed(RuntimeHostBehaviour behaviour) { if (_behaviour == behaviour) { _behaviour = null; State.MarkLost(); } } } public sealed class RuntimeHostBehaviour : MonoBehaviour { private void Start() { RuntimeService.Current?.InitializeAfterPluginLoad(); } private void Update() { RuntimeService.Current?.Tick(); } private void OnDestroy() { RuntimeHost.NotifyDestroyed(this); } } public sealed class RuntimeHostState { public int Generation { get; private set; } public bool NeedsHost { get; private set; } = true; public void MarkCreated(int generation) { Generation = generation; NeedsHost = false; } public void MarkLost() { NeedsHost = true; } } public sealed class RuntimeItemSnapshot { public string StableIdCandidate { get; set; } = string.Empty; public string RuntimeName { get; set; } = string.Empty; public string RuntimeCategory { get; set; } = string.Empty; public string RuntimeSafeDescription { get; set; } = string.Empty; public string RuntimeSpoilerDescription { get; set; } = string.Empty; public float? Weight { get; set; } public int? Worth { get; set; } public bool? Pocketable { get; set; } public bool? Pouchable { get; set; } public List<ItemStat> Stats { get; } = new List<ItemStat>(); public object? RuntimeObject { get; set; } } public sealed class RuntimeSettings { public ConfigEntry<bool> Enabled { get; } public ConfigEntry<string> Language { get; } public ConfigEntry<bool> FullInformation { get; } public ConfigEntry<KeyboardShortcut> ToggleFullInformationKey { get; } public ConfigEntry<bool> InventoryTooltipEnabled { get; } public ConfigEntry<bool> WorldTooltipEnabled { get; } public ConfigEntry<bool> UseCustomTooltipCard { get; } public ConfigEntry<int> TooltipMaxWidth { get; } public RuntimeSettings(ConfigEntry<bool> enabled, ConfigEntry<string> language, ConfigEntry<bool> fullInformation, ConfigEntry<KeyboardShortcut> toggleFullInformationKey, ConfigEntry<bool> inventoryTooltipEnabled, ConfigEntry<bool> worldTooltipEnabled, ConfigEntry<bool> useCustomTooltipCard, ConfigEntry<int> tooltipMaxWidth) { Enabled = enabled; Language = language; FullInformation = fullInformation; ToggleFullInformationKey = toggleFullInformationKey; InventoryTooltipEnabled = inventoryTooltipEnabled; WorldTooltipEnabled = worldTooltipEnabled; UseCustomTooltipCard = useCustomTooltipCard; TooltipMaxWidth = tooltipMaxWidth; } } public sealed class RuntimeService { private readonly ConfigFile _config; private readonly ManualLogSource _log; private readonly RuntimeSettings _settings; private readonly Dictionary<TooltipLanguage, ItemResolver> _resolvers = new Dictionary<TooltipLanguage, ItemResolver>(); private readonly Dictionary<string, ResolvedItemInfo> _cache = new Dictionary<string, ResolvedItemInfo>(StringComparer.OrdinalIgnoreCase); private readonly FontResolver _fontResolver = new FontResolver(); private LanguageController? _languageController; private IWKLocAdapter? _wkLocAdapter; private FieldInfo? _uiManagerInstanceField; private MethodInfo? _setDescriptionMethod; private MethodInfo? _inventoryIsShowingMethod; private MethodInfo? _getLocalizationMethod; private TooltipCardView? _tooltipCard; private ToastView? _toastView; private MenuCreditsView? _menuCreditsView; private MenuLanguageButtonView? _menuLanguageButtonView; private ResolvedItemInfo? _currentInfo; private TooltipAnchorMode _currentMode; private Vector2 _currentAnchor; private bool _initializedAfterPluginLoad; private bool _hoverCallbackLogged; private bool _interactionBridgeLogged; private bool _nativeUiReadyLogged; private bool _hoverErrorLogged; private bool _menuCreditsFailed; private bool _menuCreditsLogged; private bool _menuLanguageButtonFailed; private bool _menuLanguageButtonLogged; private bool _customUiFailed; private bool _fontSourceLogged; private bool _customUiReadyLogged; private int _lastToggleFrame = -1; private int _lastInputFrame = -1; private int _lastHoverFrame = -1000; private string? _pendingToggleStatus; public static RuntimeService? Current { get; private set; } public TooltipLanguage CurrentLanguage => _languageController?.Current ?? TooltipLanguage.English; public RuntimeService(ConfigFile config, ManualLogSource log, string pluginAssemblyLocation, RuntimeSettings settings) { _config = config ?? throw new ArgumentNullException("config"); _log = log ?? throw new ArgumentNullException("log"); _settings = settings ?? throw new ArgumentNullException("settings"); LocaleDatabaseLoader localeDatabaseLoader = new LocaleDatabaseLoader(pluginAssemblyLocation, ReadEmbeddedDatabase, delegate(string message) { _log.LogWarning((object)message); }); ItemDatabase itemDatabase = localeDatabaseLoader.Load(TooltipLanguage.English); ItemDatabase itemDatabase2 = localeDatabaseLoader.Load(TooltipLanguage.Russian); LocalizedItemDatabaseRegistry localizedItemDatabaseRegistry = new LocalizedItemDatabaseRegistry(itemDatabase, itemDatabase2); _resolvers[TooltipLanguage.English] = new ItemResolver(localizedItemDatabaseRegistry.English, TooltipLanguage.English); _resolvers[TooltipLanguage.Russian] = new ItemResolver(localizedItemDatabaseRegistry.Russian, TooltipLanguage.Russian); _log.LogInfo((object)$"Item databases loaded: EN={itemDatabase.Count}, RU={itemDatabase2.Count} records."); ResolveNativeGameApi(); } public static RuntimeService Install(RuntimeService service) { Current = service ?? throw new ArgumentNullException("service"); return service; } public void InitializeAfterPluginLoad() { if (_initializedAfterPluginLoad) { return; } _initializedAfterPluginLoad = true; try { _wkLocAdapter = WKLocAdapter.DiscoverLoaded(delegate(string message) { _log.LogInfo((object)message); }); _languageController = new LanguageController(() => _settings.Language.Value, delegate(string value) { _settings.Language.Value = value; _config.Save(); }, _wkLocAdapter, ShowUserMessage, delegate(string message) { _log.LogWarning((object)message); }, RequestMainMenuReload); _languageController.Initialize(); _log.LogInfo((object)$"Item Tooltips language: {_languageController.Current}."); } catch (Exception arg) { _log.LogError((object)$"Language integration failed safely; English item tooltips will remain available: {arg}"); _languageController = null; } } public void InteractionTick() { if (!_interactionBridgeLogged) { _interactionBridgeLogged = true; _log.LogInfo((object)"Interaction bridge is ticking through ENT_Player.InteractCheck(int)."); } if (_settings.Enabled.Value) { TryToggleFullInformation(); } } public void ItemHover(object itemObject) { //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0153: 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) if (!_hoverCallbackLogged) { _hoverCallbackLogged = true; _log.LogInfo((object)"Native hover callback is firing from ENT_Player.InteractCheck call sites."); } if (!_settings.Enabled.Value || itemObject == null) { return; } try { bool flag = IsInventoryShowing(); if ((flag && !_settings.InventoryTooltipEnabled.Value) || (!flag && !_settings.WorldTooltipEnabled.Value)) { return; } TooltipLanguage currentLanguage = CurrentLanguage; if (!_resolvers.TryGetValue(currentLanguage, out ItemResolver value) || !WhiteKnuckleItemAdapter.TrySnapshot(itemObject, currentLanguage, out RuntimeItemSnapshot snapshot)) { return; } FillNativeLocalizedDescription(snapshot); string text = ItemIdNormalizer.Normalize(snapshot.StableIdCandidate); if (string.IsNullOrWhiteSpace(text)) { return; } string key = TooltipLanguageCodec.Serialize(currentLanguage) + ":" + text; if (!_cache.TryGetValue(key, out ResolvedItemInfo value2) || HasRicherRuntimeData(snapshot, value2)) { value2 = value.Resolve(snapshot); _cache[key] = value2; } _lastHoverFrame = Time.frameCount; _currentInfo = value2; _currentMode = ((!flag) ? TooltipAnchorMode.World : TooltipAnchorMode.Inventory); _currentAnchor = (Vector2)(flag ? Vector2.op_Implicit(Input.mousePosition) : new Vector2((float)Screen.width * 0.5f, (float)Screen.height * 0.5f)); if (_settings.UseCustomTooltipCard.Value && EnsureCustomUi()) { TooltipDisplayModel model = TooltipFormatter.Format(value2, _settings.FullInformation.Value, currentLanguage); _tooltipCard.Show(model, _currentAnchor, _currentMode, _settings.TooltipMaxWidth.Value); if (!string.IsNullOrWhiteSpace(_pendingToggleStatus)) { _toastView?.Show(_pendingToggleStatus); _pendingToggleStatus = null; } TrySetNativeDescription(string.Empty); if (!_customUiReadyLogged) { _customUiReadyLogged = true; _log.LogInfo((object)("Custom item tooltip card displayed successfully. First item: " + snapshot.StableIdCandidate + ".")); } } else { string pendingToggleStatus = _pendingToggleStatus; _pendingToggleStatus = null; string text2 = NativeDescriptionFormatter.Format(value2, _settings.FullInformation.Value, currentLanguage, pendingToggleStatus); if (!string.IsNullOrWhiteSpace(text2) && TrySetNativeDescription(text2) && !_nativeUiReadyLogged) { _nativeUiReadyLogged = true; _log.LogInfo((object)("Native item tooltip displayed successfully. First item: " + snapshot.StableIdCandidate + ".")); } } } catch (Exception arg) { if (!_hoverErrorLogged) { _hoverErrorLogged = true; _log.LogError((object)$"Native item hover handling failed; later hovers will keep retrying: {arg}"); } } } public void Tick() { UpdateMenuCredits(); _toastView?.Tick(Time.unscaledDeltaTime); TooltipCardView? tooltipCard = _tooltipCard; if (tooltipCard != null && tooltipCard.IsVisible && Time.frameCount - _lastHoverFrame > 1) { _tooltipCard.Hide(); _currentInfo = null; } } public void SceneLoaded(string sceneName) { _currentInfo = null; _lastHoverFrame = -1000; _tooltipCard?.Hide(); _log.LogDebug((object)("Item Tooltips runtime survived scene load: " + sceneName + ".")); } public void ShutdownViews() { _tooltipCard?.Destroy(); _toastView?.Destroy(); _menuCreditsView?.Destroy(); _menuLanguageButtonView?.Destroy(); _tooltipCard = null; _toastView = null; _menuCreditsView = null; _menuLanguageButtonView = null; } private void ResolveNativeGameApi() { try { Type type = AccessTools.TypeByName("CL_UIManager"); _uiManagerInstanceField = ((type == null) ? null : AccessTools.Field(type, "instance")); _setDescriptionMethod = ((type == null) ? null : AccessTools.Method(type, "SetDescription", new Type[1] { typeof(string) }, (Type[])null)); Type type2 = AccessTools.TypeByName("Inventory"); _inventoryIsShowingMethod = ((type2 == null) ? null : AccessTools.Method(type2, "IsShowingInventory", (Type[])null, (Type[])null)); Type type3 = AccessTools.TypeByName("CL_LocalizationManager"); _getLocalizationMethod = ((type3 == null) ? null : AccessTools.Method(type3, "GetLocalization", new Type[2] { typeof(string), typeof(string) }, (Type[])null)); if (_uiManagerInstanceField == null || _setDescriptionMethod == null) { _log.LogWarning((object)"Native descriptor API was not fully resolved. Hover callbacks will still be logged for diagnosis."); } else { _log.LogInfo((object)"Native descriptor API resolved: CL_UIManager.SetDescription(string)."); } } catch (Exception arg) { _log.LogError((object)$"Could not resolve White Knuckle native UI API: {arg}"); } } private bool EnsureCustomUi() { if (_customUiFailed) { return false; } if (_tooltipCard != null) { return true; } try { ResolvedTooltipFont resolvedTooltipFont = _fontResolver.Resolve(); _tooltipCard = new TooltipCardView(resolvedTooltipFont.Font); if (_toastView == null) { _toastView = new ToastView(resolvedTooltipFont.Font); } if (!_fontSourceLogged) { _fontSourceLogged = true; _log.LogInfo((object)("Tooltip font source: " + resolvedTooltipFont.Source + ".")); } return true; } catch (Exception arg) { _customUiFailed = true; _log.LogError((object)$"Custom tooltip UI creation failed; native description fallback will be used: {arg}"); return false; } } private void TryToggleFullInformation() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) int frameCount = Time.frameCount; if (_lastInputFrame == frameCount) { return; } _lastInputFrame = frameCount; KeyboardShortcut value = _settings.ToggleFullInformationKey.Value; KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey; if ((int)mainKey == 0 || !Input.GetKeyDown(mainKey)) { return; } foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers) { if (!Input.GetKey(modifier)) { return; } } if (_lastToggleFrame != frameCount) { _lastToggleFrame = frameCount; _settings.FullInformation.Value = !_settings.FullInformation.Value; _config.Save(); _cache.Clear(); string text = LocalizedStrings.FullInformationStatus(CurrentLanguage, _settings.FullInformation.Value); TooltipCardView? tooltipCard = _tooltipCard; if (tooltipCard != null && tooltipCard.IsVisible && _currentInfo != null) { _tooltipCard.Refresh(TooltipFormatter.Format(_currentInfo, _settings.FullInformation.Value, CurrentLanguage)); _toastView?.Show(text); _pendingToggleStatus = null; } else { _pendingToggleStatus = text; } _log.LogInfo((object)$"Full information toggled: {_settings.FullInformation.Value}."); } } private bool IsInventoryShowing() { try { object obj = _inventoryIsShowingMethod?.Invoke(null, null); if (obj is bool) { return (bool)obj; } } catch { } return false; } private void FillNativeLocalizedDescription(RuntimeItemSnapshot snapshot) { if (!string.IsNullOrWhiteSpace(snapshot.RuntimeSafeDescription) || _getLocalizationMethod == null || string.IsNullOrWhiteSpace(snapshot.StableIdCandidate)) { return; } try { string text = _getLocalizationMethod.Invoke(null, new object[2] { "items", "desc-" + snapshot.StableIdCandidate }) as string; if (!string.IsNullOrWhiteSpace(text)) { snapshot.RuntimeSafeDescription = text.Trim(); } } catch { } } private bool TrySetNativeDescription(string text) { try { object obj = _uiManagerInstanceField?.GetValue(null); if (obj == null || _setDescriptionMethod == null) { return false; } _setDescriptionMethod.Invoke(obj, new object[1] { text }); return true; } catch (Exception arg) { if (!_hoverErrorLogged) { _hoverErrorLogged = true; _log.LogError((object)$"CL_UIManager.SetDescription failed: {arg}"); } return false; } } private static bool HasRicherRuntimeData(RuntimeItemSnapshot snapshot, ResolvedItemInfo cached) { int num = 0; foreach (ItemStat stat in cached.Stats) { if (stat.Source == ItemStatSource.Runtime) { num++; } } return snapshot.Stats.Count > num; } private void UpdateMenuCredits() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); if (!MenuCreditsContent.IsMainMenuScene(((Scene)(ref activeScene)).name)) { _menuCreditsView?.SetVisible(visible: false); _menuLanguageButtonView?.SetVisible(visible: false); return; } Font val = null; if ((_menuCreditsView == null && !_menuCreditsFailed) || (_menuLanguageButtonView == null && !_menuLanguageButtonFailed) || _toastView == null) { try { val = _fontResolver.Resolve().Font; } catch (Exception ex) { _log.LogWarning((object)("Main-menu UI font could not be resolved: " + ex.Message)); } } if (_menuCreditsView == null && !_menuCreditsFailed && (Object)(object)val != (Object)null) { try { _menuCreditsView = new MenuCreditsView(val); if (!_menuCreditsLogged) { _menuCreditsLogged = true; _log.LogInfo((object)"Main-menu credit enabled for KoshGamer / twitch.tv/koshgamer."); } } catch (Exception ex2) { _menuCreditsFailed = true; _log.LogWarning((object)("Main-menu credit UI could not be created; item tooltips remain enabled: " + ex2.Message)); } } if (_menuLanguageButtonView == null && !_menuLanguageButtonFailed && _languageController != null && (Object)(object)val != (Object)null) { try { _menuLanguageButtonView = new MenuLanguageButtonView(val, OnLanguageButtonClicked); _menuLanguageButtonView.SetLanguage(_languageController.Current); if (!_menuLanguageButtonLogged) { _menuLanguageButtonLogged = true; _log.LogInfo((object)"Main-menu LANGUAGE button enabled."); } } catch (Exception ex3) { _menuLanguageButtonFailed = true; _log.LogWarning((object)("Main-menu language button could not be created; language remains configurable through saved state: " + ex3.Message)); } } if (_toastView == null && (Object)(object)val != (Object)null) { try { _toastView = new ToastView(val); } catch (Exception ex4) { _log.LogWarning((object)("Menu toast UI could not be created: " + ex4.Message)); } } _menuCreditsView?.SetVisible(visible: true); if (_languageController != null) { _menuLanguageButtonView?.SetLanguage(_languageController.Current); _menuLanguageButtonView?.SetVisible(visible: true); } } private void OnLanguageButtonClicked() { _languageController?.TryToggle(); } private void ShowUserMessage(string message) { if (_toastView == null) { try { Font font = _fontResolver.Resolve().Font; _toastView = new ToastView(font); } catch (Exception ex) { _log.LogWarning((object)("Could not show language message in UI: " + ex.Message)); } } if (_toastView != null) { _toastView.Show(message, 2.2f); } else { _log.LogWarning((object)message); } } private void RequestMainMenuReload() { _cache.Clear(); _currentInfo = null; _tooltipCard?.Hide(); _log.LogInfo((object)$"Language switched to {CurrentLanguage}; reloading Main-Menu."); SceneManager.LoadScene("Main-Menu"); } private string? ReadEmbeddedDatabase(string fileName) { string text = "WhiteKnuckleItemTooltips.Data." + fileName; try { using Stream stream = typeof(RuntimeService).Assembly.GetManifestResourceStream(text); if (stream == null) { return null; } using StreamReader streamReader = new StreamReader(stream); return streamReader.ReadToEnd(); } catch (Exception ex) { _log.LogWarning((object)("Could not read embedded item database '" + text + "': " + ex.Message)); return null; } } } public static class WhiteKnuckleItemAdapter { private sealed class ReferenceComparer : IEqualityComparer<object> { public static readonly ReferenceComparer Instance = new ReferenceComparer(); public new bool Equals(object? x, object? y) { return x == y; } public int GetHashCode(object obj) { return RuntimeHelpers.GetHashCode(obj); } } private static readonly string[] ItemWrapperMembers = new string[6] { "itemData", "item", "currentItem", "Raw", "pickupItem", "pickupable" }; private static readonly string[] EffectWrapperMembers = new string[6] { "handItemAsset", "handItem", "buff", "buffContainer", "effect", "effects" }; public static bool TrySnapshot(object? source, out RuntimeItemSnapshot snapshot) { return TrySnapshot(source, TooltipLanguage.Russian, out snapshot); } public static bool TrySnapshot(object? source, TooltipLanguage language, out RuntimeItemSnapshot snapshot) { snapshot = new RuntimeItemSnapshot(); if (source == null) { return false; } if (!TryFindItemData(source, out object itemData)) { return false; } string text = ReadString(itemData, "prefabName"); if (string.IsNullOrWhiteSpace(text)) { return false; } snapshot.StableIdCandidate = text.Trim(); snapshot.RuntimeName = FirstUseful(ReadString(itemData, "itemName"), ReadString(itemData, "displayName")); snapshot.RuntimeCategory = FirstUseful(ReadString(itemData, "itemTag"), ReadFirstStringFromEnumerable(itemData, "itemTags"), ReadFirstStringFromEnumerable(itemData, "tags")); snapshot.RuntimeSafeDescription = FirstUseful(ReadString(itemData, "itemDescription"), ReadString(itemData, "description"), ReadString(itemData, "desc")); snapshot.Weight = ReadNullableFloat(itemData, "itemWeight") ?? ReadNullableFloat(itemData, "weight"); snapshot.Worth = ReadNullableInt(itemData, "worth") ?? ReadNullableInt(itemData, "value"); snapshot.Pocketable = ReadNullableBool(itemData, "pocketable"); snapshot.Pouchable = ReadNullableBool(itemData, "pouchable"); snapshot.RuntimeObject = itemData; ExtractConfiguredStats(itemData, snapshot.Stats, language); return true; } private static bool TryFindItemData(object source, out object itemData) { Queue<(object, int)> queue = new Queue<(object, int)>(); HashSet<object> hashSet = new HashSet<object>(ReferenceComparer.Instance); EnqueueUnityContext(source, queue, 0); queue.Enqueue((source, 0)); while (queue.Count > 0) { var (obj, num) = queue.Dequeue(); if (obj == null || !hashSet.Add(obj)) { continue; } if (LooksLikeItemData(obj)) { itemData = obj; return true; } if (num >= 4) { continue; } string[] itemWrapperMembers = ItemWrapperMembers; foreach (string memberName in itemWrapperMembers) { if (TryReadMember(obj, memberName, out object value) && value != null) { queue.Enqueue((value, num + 1)); } } EnqueueUnityContext(obj, queue, num + 1); } itemData = null; return false; } private static void EnqueueUnityContext(object source, Queue<(object Value, int Depth)> queue, int depth) { try { Type type = Type.GetType("UnityEngine.GameObject, UnityEngine.CoreModule", throwOnError: false); Type type2 = Type.GetType("UnityEngine.Component, UnityEngine.CoreModule", throwOnError: false); if ((object)type == null || (object)type2 == null) { return; } if (type.IsInstanceOfType(source)) { EnqueueGameObjectComponents(source, type2, queue, depth); EnqueueParentGameObject(source, queue, depth + 1); } else if (type2.IsInstanceOfType(source)) { if (TryReadMember(source, "gameObject", out object value) && value != null) { queue.Enqueue((value, depth)); } EnqueueParentGameObject(source, queue, depth + 1); } } catch { } } private static void EnqueueGameObjectComponents(object gameObject, Type componentType, Queue<(object Value, int Depth)> queue, int depth) { try { if (!(gameObject.GetType().GetMethod("GetComponents", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(Type) }, null)?.Invoke(gameObject, new object[1] { componentType }) is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (item != null) { queue.Enqueue((item, depth)); } } } catch { } } private static void EnqueueParentGameObject(object source, Queue<(object Value, int Depth)> queue, int depth) { if (TryReadMember(source, "transform", out object value) && value != null && TryReadMember(value, "parent", out object value2) && value2 != null && TryReadMember(value2, "gameObject", out object value3) && value3 != null) { queue.Enqueue((value3, depth)); } } private static void ExtractConfiguredStats(object itemData, List<ItemStat> destination, TooltipLanguage language) { List<object> list = FindBuffContainers(itemData); if (list.Count == 0) { return; } HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); foreach (object item in list) { ExtractDuration(item, destination, hashSet, language); if (!TryReadMember(item, "buffs", out object value) || !(value is IEnumerable enumerable)) { continue; } foreach (object item2 in enumerable) { if (item2 == null) { continue; } string text = ReadString(item2, "id"); if (!string.IsNullOrWhiteSpace(text)) { float valueOrDefault = ReadNullableFloat(item2, "maxAmount").GetValueOrDefault(); if (Math.Abs(valueOrDefault) < 1E-05f) { valueOrDefault = ReadNullableFloat(item2, "amount").GetValueOrDefault(); } if (TryFormatBuff(text, valueOrDefault, language, out string label, out string value2) && hashSet.Add(label)) { destination.Add(new ItemStat(label, value2, ItemStatSource.Runtime)); } } } } } private static List<object> FindBuffContainers(object itemData) { List<object> list = new List<object>(); Queue<(object, int)> queue = new Queue<(object, int)>(); HashSet<object> hashSet = new HashSet<object>(ReferenceComparer.Instance); queue.Enqueue((itemData, 0)); while (queue.Count > 0) { var (obj, num) = queue.Dequeue(); if (obj == null || !hashSet.Add(obj)) { continue; } if (LooksLikeBuffContainer(obj)) { list.Add(obj); } else { if (num >= 4) { continue; } string[] effectWrapperMembers = EffectWrapperMembers; foreach (string memberName in effectWrapperMembers) { if (!TryReadMember(obj, memberName, out object value) || value == null) { continue; } Type type = Type.GetType("UnityEngine.GameObject, UnityEngine.CoreModule", throwOnError: false); Type type2 = Type.GetType("UnityEngine.Component, UnityEngine.CoreModule", throwOnError: false); if ((object)type != null && (object)type2 != null && type.IsInstanceOfType(value)) { EnqueueGameObjectComponents(value, type2, queue, num + 1); } else if (value is IEnumerable enumerable && !(value is string)) { foreach (object item in enumerable) { if (item != null) { queue.Enqueue((item, num + 1)); } } } else { queue.Enqueue((value, num + 1)); } } } } return list; } private static bool LooksLikeItemData(object candidate) { if (string.IsNullOrWhiteSpace(ReadString(candidate, "prefabName"))) { return false; } string name = candidate.GetType().Name; if (!string.Equals(name, "Item", StringComparison.OrdinalIgnoreCase) && !string.Equals(name, "ItemData", StringComparison.OrdinalIgnoreCase) && !HasMember(candidate, "itemName") && !HasMember(candidate, "itemWeight") && !HasMember(candidate, "handItemAsset") && !HasMember(candidate, "pocketable")) { return HasMember(candidate, "pouchable"); } return true; } private static bool LooksLikeBuffContainer(object candidate) { if (HasMember(candidate, "buffTime") && HasMember(candidate, "loseRate") && TryReadMember(candidate, "buffs", out object value)) { return value is IEnumerable; } return false; } private static void ExtractDuration(object container, List<ItemStat> destination, HashSet<string> seenLabels, TooltipLanguage language) { if (!(ReadNullableBool(container, "loseOverTime") ?? true)) { return; } float? num = ReadNullableFloat(container, "buffTime"); float? num2 = ReadNullableFloat(container, "loseRate"); if (!num.HasValue || !num2.HasValue || num.Value <= 0f || num2.Value <= 0f) { return; } float num3 = num.Value / num2.Value; if (!float.IsNaN(num3) && !float.IsInfinity(num3) && !(num3 <= 0f) && !(num3 > 86400f)) { string text = LocalizedStrings.BaseDurationLabel(language); if (seenLabels.Add(text)) { destination.Add(new ItemStat(text, FormatSeconds(num3, language), ItemStatSource.Runtime)); } } } private static bool TryFormatBuff(string id, float amount, TooltipLanguage language, out string label, out string value) { label = LocalizedStrings.BuffLabel(id.Trim(), language); value = string.Empty; if (label.Length == 0) { return false; } string text = id.Trim(); if (!(text == "addExtraJumps")) { if (text == "grabAnything") { value = LocalizedStrings.AnySurfaces(language); return true; } value = FormatPercent(amount, language); return true; } value = FormatSignedNumber(amount, language); return true; } private static string FormatPercent(float amount, TooltipLanguage language) { float num = amount * 100f; string text = FormatNumber(Math.Abs(num), language); return ((num > 0.0001f) ? "+" : ((num < -0.0001f) ? "-" : string.Empty)) + text + "%"; } private static string FormatSignedNumber(float amount, TooltipLanguage language) { return ((amount > 0.0001f) ? "+" : ((amount < -0.0001f) ? "-" : string.Empty)) + FormatNumber(Math.Abs(amount), language); } private static string FormatSeconds(float seconds, TooltipLanguage language) { return FormatNumber(seconds, language) + LocalizedStrings.SecondsSuffix(language); } private static string FormatNumber(float number, TooltipLanguage language) { float num = MathF.Round(number); if (Math.Abs(number - num) < 0.005f) { return num.ToString("0", CultureInfo.InvariantCulture); } string text = number.ToString("0.##", CultureInfo.InvariantCulture); if (language != TooltipLanguage.Russian) { return text; } return text.Replace('.', ','); } private static string ReadString(object source, string memberName) { if (!TryReadMember(source, memberName, out object value) || value == null) { return string.Empty; } return (value as string) ?? value.ToString() ?? string.Empty; } private static string ReadFirstStringFromEnumerable(object source, string memberName) { if (!TryReadMember(source, memberName, out object value) || !(value is IEnumerable enumerable) || value is string) { return string.Empty; } foreach (object item in enumerable) { string text = item?.ToString(); if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } } return string.Empty; } private static float? ReadNullableFloat(object source, string memberName) { if (!TryReadMember(source, memberName, out object value) || value == null) { return null; } try { return Convert.ToSingle(value, CultureInfo.InvariantCulture); } catch { return null; } } private static int? ReadNullableInt(object source, string memberName) { if (!TryReadMember(source, memberName, out object value) || value == null) { return null; } try { return Convert.ToInt32(value, CultureInfo.InvariantCulture); } catch { return null; } } private static bool? ReadNullableBool(object source, string memberName) { if (!TryReadMember(source, memberName, out object value) || value == null) { return null; } try { return Convert.ToBoolean(value, CultureInfo.InvariantCulture); } catch { return null; } } private static string FirstUseful(params string[] candidates) { foreach (string text in candidates) { if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(); } } return string.Empty; } private static bool HasMember(object source, string memberName) { Type type = source.GetType(); if (type.GetField(memberName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) != null) { return true; } PropertyInfo property = type.GetProperty(memberName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetIndexParameters().Length == 0; } return false; } private static bool TryReadMember(object source, string memberName, out object? value) { value = null; if (source == null) { return false; } try { Type type = source.GetType(); FieldInfo field = type.GetField(memberName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { value = field.GetValue(source); return true; } PropertyInfo property = type.GetProperty(memberName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.GetIndexParameters().Length == 0 && property.CanRead) { value = property.GetValue(source, null); return true; } } catch { } return false; } } } namespace WhiteKnuckleItemTooltips.Localization { public sealed class LanguageController { public const string MissingWKLocMessage = "Russian localization requires WKLoc"; private readonly Func<string?> _loadSaved; private readonly Action<string> _save; private readonly IWKLocAdapter _wkLoc; private readonly Action<string> _showUserMessage; private readonly Action<string> _logDiagnostic; private readonly Action _requestMainMenuReload; public TooltipLanguage Current { get; private set; } public LanguageController(Func<string?> loadSaved, Action<string> save, IWKLocAdapter wkLoc, Action<string> showUserMessage, Action<string> logDiagnostic, Action requestMainMenuReload) { _loadSaved = loadSaved ?? throw new ArgumentNullException("loadSaved"); _save = save ?? throw new ArgumentNullException("save"); _wkLoc = wkLoc ?? throw new ArgumentNullException("wkLoc"); _showUserMessage = showUserMessage ?? throw new ArgumentNullException("showUserMessage"); _logDiagnostic = logDiagnostic ?? throw new ArgumentNullException("logDiagnostic"); _requestMainMenuReload = requestMainMenuReload ?? throw new ArgumentNullException("requestMainMenuReload"); } public void Initialize() { if (!TooltipLanguageCodec.TryParse(_loadSaved(), out var language)) { Current = (_wkLoc.IsAvailable ? TooltipLanguage.Russian : TooltipLanguage.English); bool flag = false; if (Current == TooltipLanguage.Russian) { if (!TrySynchronizeWKLoc(enabled: true, out var changed)) { Current = TooltipLanguage.English; _logDiagnostic("WKLoc was detected but could not be enabled during language initialization; falling back to English."); } else { flag = changed; } } SaveCurrent(); if (flag) { _requestMainMenuReload(); } } else if (language == TooltipLanguage.Russian) { if (!_wkLoc.IsAvailable || !TrySynchronizeWKLoc(enabled: true, out var changed2)) { Current = TooltipLanguage.English; SaveCurrent(); _logDiagnostic("Saved Russian language could not be restored because compatible WKLoc is unavailable; recovered to English."); return; } Current = TooltipLanguage.Russian; if (changed2) { _requestMainMenuReload(); } } else { Current = TooltipLanguage.English; bool changed3 = default(bool); if (_wkLoc.IsAvailable && TrySynchronizeWKLoc(enabled: false, out changed3) && changed3) { _requestMainMenuReload(); } } } public bool TryToggle() { if (Current == TooltipLanguage.English) { return TrySwitchToRussian(); } return TrySwitchToEnglish(); } private bool TrySwitchToRussian() { if (!_wkLoc.IsAvailable) { _showUserMessage("Russian localization requires WKLoc"); return false; } if (!_wkLoc.TrySetTranslationEnabled(enabled: true, out bool _, out string diagnostic)) { _logDiagnostic(diagnostic); _showUserMessage("Russian localization requires WKLoc"); return false; } Current = TooltipLanguage.Russian; SaveCurrent(); _requestMainMenuReload(); return true; } private bool TrySwitchToEnglish() { if (_wkLoc.IsAvailable && !_wkLoc.TrySetTranslationEnabled(enabled: false, out bool _, out string diagnostic)) { _logDiagnostic(diagnostic); return false; } Current = TooltipLanguage.English; SaveCurrent(); _requestMainMenuReload(); return true; } private bool TrySynchronizeWKLoc(bool enabled, out bool changed) { changed = false; if (!_wkLoc.IsAvailable) { return false; } if (_wkLoc.