Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of HuginsHandbook v1.4.1
HuginsHandbook.dll
Decompiled 2 days 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.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using UnityEngine; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.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("HuginsHandbook")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.4.1.0")] [assembly: AssemblyInformationalVersion("1.4.1")] [assembly: AssemblyProduct("HuginsHandbook")] [assembly: AssemblyTitle("HuginsHandbook")] [assembly: AssemblyVersion("1.4.1.0")] namespace HuginsHandbook; internal static class Format { private static readonly Regex Token = new Regex("\\{(key|use|handbook|button)(?::([^}]*))?\\}", RegexOptions.IgnoreCase); private static readonly Regex SentenceBreak = new Regex("(?<=[.!?])\\s+(?=[A-Z0-9{\\[(\"'*<])"); private static readonly Regex ListLine = new Regex("^\\s*(- |\\d+\\.\\s)"); private static readonly Regex Bold = new Regex("\\*\\*(.+?)\\*\\*"); private static readonly Regex Tags = new Regex("<[^>]*>"); public static string Resolve(string text) { if (string.IsNullOrEmpty(text)) { return text ?? ""; } string[] array = text.Replace("\r", "").Split('\n'); StringBuilder stringBuilder = new StringBuilder(); bool flag = true; string[] array2 = array; for (int i = 0; i < array2.Length; i++) { string text2 = array2[i]; if (HasUnbound(text2)) { if (ListLine.IsMatch(text2)) { continue; } text2 = string.Join(" ", from s in SentenceBreak.Split(text2) where !HasUnbound(s) select s).TrimEnd(); if (text2.Trim().Length == 0) { continue; } } if (!flag) { stringBuilder.Append('\n'); } stringBuilder.Append(ReplaceTokens(text2)); flag = false; } return stringBuilder.ToString(); } public static string FirstKey(string text) { if (string.IsNullOrEmpty(text)) { return null; } Match match = Token.Match(text); while (match.Success) { if (match.Groups[1].Value.Equals("key", StringComparison.OrdinalIgnoreCase)) { string text2 = KeyOf(match); if (!string.IsNullOrEmpty(text2) && text2 != "\u0001unbound") { return text2; } } match = match.NextMatch(); } return null; } private static bool HasUnbound(string text) { Match match = Token.Match(text); while (match.Success) { if (KeyOf(match) == "\u0001unbound") { return true; } match = match.NextMatch(); } return false; } private static string ReplaceTokens(string text) { return Token.Replace(text, delegate(Match match) { string text2 = KeyOf(match); if (text2 == null) { return match.Value; } return (text2 == "\u0001unbound") ? "" : Keys.Badge(text2); }); } private unsafe static string KeyOf(Match match) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) string text = match.Groups[1].Value.ToLowerInvariant(); string[] array = (match.Groups[2].Success ? match.Groups[2].Value : "").Split('|'); switch (text) { case "use": return GameKey("Use", "E"); case "button": { string text3 = array[0].Trim(); return GameKey(text3, (array.Length > 1 && array[1].Trim().Length > 0) ? array[1].Trim() : text3); } case "handbook": { KeyboardShortcut value = Plugin.HandbookKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey != 0) { value = Plugin.HandbookKey.Value; return Keys.Pretty(((object)(*(KeyboardShortcut*)(&value))/*cast due to .constrained prefix*/).ToString()); } return "\u0001unbound"; } default: { if (array.Length < 3) { return null; } string text2 = Keys.Lookup(array[0].Trim(), array[1].Trim(), array[2].Trim()); if (text2 == "\u0001unbound") { return "\u0001unbound"; } if (text2 != null) { return Keys.Pretty(text2); } if (array.Length <= 3 || array[3].Trim().Length <= 0) { return "?"; } return array[3].Trim(); } } } public static string GameKey(string button, string fallback) { if (string.IsNullOrEmpty(button)) { return fallback; } try { if (Localization.instance != null) { string text = Plain(Localization.instance.Localize("$KEY_" + button)).Trim().Trim('[', ']').Trim(); if (text.Length > 0 && text.Length <= 16 && text.IndexOf("KEY_", StringComparison.OrdinalIgnoreCase) < 0 && !text.StartsWith("$")) { return text; } } } catch { } return fallback; } public static string UseKey() { return GameKey("Use", "E"); } public static string Markup(string text) { StringBuilder stringBuilder = new StringBuilder(); string[] array = (text ?? "").Replace("\r", "").Split('\n'); foreach (string text2 in array) { string text3 = text2.TrimStart(); if (text3.StartsWith("## ", StringComparison.Ordinal)) { stringBuilder.Append("<size=21><b><color=#FFB24D>").Append(text3.Substring(3).Trim()).Append("</color></b></size>"); } else if (text3.StartsWith("- ", StringComparison.Ordinal)) { stringBuilder.Append(" • ").Append(text3.Substring(2)); } else { stringBuilder.Append(text2); } stringBuilder.Append('\n'); } return Bold.Replace(stringBuilder.ToString().TrimEnd(), "<b>$1</b>"); } public static string Plain(string text) { return Tags.Replace(text ?? "", ""); } public static string Escape(string text) { return (text ?? "").Replace("<", "‹").Replace(">", "›"); } public static string Clip(string text, int max) { text = (text ?? "").Replace("\r", " ").Replace("\n", " ").Trim(); if (text.Length > max) { return text.Substring(0, max - 1).TrimEnd() + "…"; } return text; } } internal class Condition { public string Kind; public string Arg = ""; public float Number; } internal class Hint { public string Id; public Entry Entry; public string Trigger; public string Text; public readonly List<Condition> Conditions = new List<Condition>(); } internal class Entry { public string Id; public string Title; public string Category = "Other"; public string Tags = ""; public string Body = ""; public int Order; public bool Generated; public bool IsSharedKeys; public bool Present = true; public string Badge = ""; public string Url; public string Search = ""; public readonly List<string> ModIds = new List<string>(); public readonly List<PluginInfo> Plugins = new List<PluginInfo>(); public readonly List<Hint> Hints = new List<Hint>(); } internal static class Handbook { public const string HotkeyCategory = "All hotkeys"; private const string ResourceName = "HuginsHandbook.handbook.txt"; public static readonly List<Entry> Entries = new List<Entry>(); public static readonly List<Entry> HotkeyEntries = new List<Entry>(); public static readonly List<Hint> Hints = new List<Hint>(); private static DateTime _stamp = DateTime.MinValue; private static bool _resolved; private const string FirstReleaseHash = "10956e9677ee3d885805221f36cb48216de3f52c52ee988ab876ff5936981a29"; private const string Muted = "#A3957D"; private const string Faint = "#8F8270"; private const string Gold = "#FFB24D"; public static string FolderPath => Path.Combine(Paths.ConfigPath, "HuginsHandbook"); public static string FilePath => Path.Combine(FolderPath, "handbook.txt"); private static string StampPath => Path.Combine(FolderPath, "handbook.default.sha256"); private static string NewDefaultPath => Path.Combine(FolderPath, "handbook.new-default.txt"); public static void Init() { try { Directory.CreateDirectory(FolderPath); string text = Embedded(); if (!string.IsNullOrEmpty(text)) { if (!File.Exists(FilePath)) { WriteDefault(text); } else { string text2 = Hash(File.ReadAllText(FilePath, Encoding.UTF8)); string text3 = (File.Exists(StampPath) ? File.ReadAllText(StampPath).Trim() : "10956e9677ee3d885805221f36cb48216de3f52c52ee988ab876ff5936981a29"); if (text2 != Hash(text)) { if (text2 == text3) { WriteDefault(text); Plugin.Log.LogInfo((object)"handbook.txt updated to the content of this version."); } else { File.WriteAllText(NewDefaultPath, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); Plugin.Log.LogWarning((object)"handbook.txt has your own edits and was kept. This version's content is in handbook.new-default.txt."); } } } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not write the default handbook: " + ex.Message)); } Load(); } private static void WriteDefault(string text) { File.WriteAllText(FilePath, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false)); File.WriteAllText(StampPath, Hash(text)); } private static string Hash(string text) { using SHA256 sHA = SHA256.Create(); byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes((text ?? "").Replace("\r", ""))); StringBuilder stringBuilder = new StringBuilder(array.Length * 2); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2")); } return stringBuilder.ToString(); } private static string Embedded() { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("HuginsHandbook.handbook.txt"); if (stream == null) { return ""; } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); return streamReader.ReadToEnd(); } public static void Load() { string text = null; try { if (File.Exists(FilePath)) { text = File.ReadAllText(FilePath, Encoding.UTF8); _stamp = File.GetLastWriteTimeUtc(FilePath); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read " + FilePath + ": " + ex.Message)); } if (string.IsNullOrWhiteSpace(text)) { text = Embedded(); } Entries.Clear(); Hints.Clear(); Parse(text); _resolved = false; Plugin.Log.LogInfo((object)("Handbook: " + Entries.Count + " entries, " + Hints.Count + " hints.")); } private static void ReloadIfChanged() { try { if (File.Exists(FilePath) && File.GetLastWriteTimeUtc(FilePath) != _stamp) { Load(); } } catch { } } public static void EnsureResolved() { if (!_resolved) { Resolve(); } } public static void Refresh() { ReloadIfChanged(); Keys.Rescan(); Resolve(); } private static void Resolve() { foreach (Entry entry in Entries) { entry.Plugins.Clear(); foreach (string modId in entry.ModIds) { PluginInfo val = Keys.FindPlugin(modId); if (val != null && !entry.Plugins.Contains(val)) { entry.Plugins.Add(val); } } entry.Present = entry.ModIds.Count == 0 || entry.Plugins.Count > 0; } BuildHotkeyEntries(); foreach (Entry entry2 in Entries) { Index(entry2); } foreach (Entry hotkeyEntry in HotkeyEntries) { Index(hotkeyEntry); } _resolved = true; } public static bool IsVisible(Entry entry) { if (!entry.Present) { return Plugin.ShowMissingMods.Value; } return true; } public static List<string> CategoriesInOrder() { List<string> list = new List<string>(); foreach (Entry entry in Entries) { if (IsVisible(entry) && !list.Contains(entry.Category)) { list.Add(entry.Category); } } return list; } public static int CountIn(string category) { if (category == "All hotkeys") { return HotkeyEntries.Count; } if (category == "All") { return Entries.Count(IsVisible); } return Entries.Count((Entry e) => IsVisible(e) && e.Category == category); } private static void BuildHotkeyEntries() { HotkeyEntries.Clear(); List<HotkeyInfo> list = Keys.Shared(); if (list.Count > 0) { HotkeyEntries.Add(new Entry { Id = "keys-bound-twice", Title = "Keys bound twice", Category = "All hotkeys", Generated = true, IsSharedKeys = true, Order = -1, Badge = list.Select((HotkeyInfo h) => h.Normalized).Distinct().Count() .ToString(CultureInfo.InvariantCulture) }); } int num = 0; foreach (IGrouping<PluginInfo, HotkeyInfo> item in (from h in Keys.All group h by h.Plugin).OrderBy<IGrouping<PluginInfo, HotkeyInfo>, string>((IGrouping<PluginInfo, HotkeyInfo> g) => g.Key.Metadata.Name, StringComparer.OrdinalIgnoreCase)) { int num2 = item.Count(); Entry entry = new Entry { Id = "keys-" + item.Key.Metadata.GUID, Title = item.Key.Metadata.Name, Category = "All hotkeys", Generated = true, Order = num++, Badge = ((num2 == 1) ? item.First().Pretty : (num2 + " keys")) }; entry.Plugins.Add(item.Key); HotkeyEntries.Add(entry); } } public static Entry FindEntry(string key) { if (string.IsNullOrWhiteSpace(key)) { return null; } key = key.Trim(); return Entries.FirstOrDefault((Entry e) => string.Equals(e.Id, key, StringComparison.OrdinalIgnoreCase)) ?? Entries.FirstOrDefault((Entry e) => string.Equals(e.Title, key, StringComparison.OrdinalIgnoreCase)); } public static string PageUrl(Entry entry) { if (entry == null || entry.Generated) { return null; } if (!string.IsNullOrEmpty(entry.Url)) { if (!entry.Url.Equals("none", StringComparison.OrdinalIgnoreCase)) { return entry.Url; } return null; } foreach (string modId in entry.ModIds) { int num = modId.IndexOf('-'); if (num > 0 && num != modId.Length - 1 && modId.IndexOf('.') < 0 && modId.IndexOf(' ') < 0 && !modId.StartsWith("Dashr-", StringComparison.OrdinalIgnoreCase)) { return "https://thunderstore.io/c/valheim/p/" + modId.Substring(0, num) + "/" + modId.Substring(num + 1) + "/"; } } return null; } private static string WithoutPictures(string body) { return Regex.Replace(body ?? "", "^\\s*\\[img:[^\\]]*\\]\\s*$", "", RegexOptions.IgnoreCase | RegexOptions.Multiline); } private static void Index(Entry entry) { List<HotkeyInfo> list = (entry.IsSharedKeys ? Keys.Shared() : Keys.For(entry.Plugins)); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(entry.Title).Append(' ').Append(entry.Category) .Append(' ') .Append(entry.Tags) .Append(' '); if (!entry.Generated) { stringBuilder.Append(Format.Plain(Format.Resolve(WithoutPictures(entry.Body)))).Append(' '); } foreach (string modId in entry.ModIds) { stringBuilder.Append(modId).Append(' '); } foreach (PluginInfo plugin in entry.Plugins) { stringBuilder.Append(plugin.Metadata.Name).Append(' ').Append(Keys.PackageName(plugin)) .Append(' '); } foreach (HotkeyInfo item in list) { stringBuilder.Append(item.Pretty).Append(' ').Append(item.Normalized) .Append(' ') .Append(item.Setting) .Append(' '); } foreach (Hint hint in entry.Hints) { stringBuilder.Append(Format.Plain(Format.Resolve(hint.Text))).Append(' '); } entry.Search = stringBuilder.ToString().ToLowerInvariant(); if (!entry.Generated) { entry.Badge = Format.FirstKey(entry.Body) ?? ((list.Count > 0) ? list[0].Pretty : ""); } } private static void Parse(string text) { Entry current = null; bool inBody = false; StringBuilder body = new StringBuilder(); HashSet<string> ids = new HashSet<string>(StringComparer.OrdinalIgnoreCase); int num = 0; string[] array = (text ?? "").Replace("\r\n", "\n").Split('\n'); for (int i = 0; i < array.Length; i++) { string text2 = array[i].TrimEnd('\r'); string text3 = text2.Trim(); if (text3.StartsWith("//", StringComparison.Ordinal)) { continue; } if (text3.StartsWith("[[", StringComparison.Ordinal) && text3.EndsWith("]]", StringComparison.Ordinal) && text3.Length > 4) { Finish(); current = new Entry { Title = text3.Substring(2, text3.Length - 4).Trim(), Order = num++ }; } else { if (current == null) { continue; } if (inBody) { body.Append(text2).Append('\n'); } else if (text3 == "---") { inBody = true; } else { if (text3.Length == 0) { continue; } int num2 = text3.IndexOf('='); if (num2 <= 0) { continue; } string text4 = text3.Substring(0, num2).Trim().ToLowerInvariant(); string text5 = text3.Substring(num2 + 1).Trim(); switch (text4) { case "id": current.Id = text5; break; case "category": current.Category = text5; break; case "url": current.Url = text5; break; case "tags": current.Tags = text5; break; case "mod": case "mods": { string[] array2 = text5.Split('|', ','); foreach (string text6 in array2) { if (text6.Trim().Length > 0) { current.ModIds.Add(text6.Trim()); } } break; } case "hint": { Hint hint = ParseHint(text5); if (hint != null) { hint.Entry = current; current.Hints.Add(hint); } else { Plugin.Log.LogWarning((object)("Handbook: could not read a hint in [[" + current.Title + "]]: " + text5)); } break; } } } } } Finish(); void Finish() { if (current != null) { current.Body = body.ToString().Trim('\n', '\r', ' '); if (string.IsNullOrEmpty(current.Id)) { current.Id = Slug(current.Title); } string id = current.Id; int num3 = 2; while (!ids.Add(current.Id)) { current.Id = id + "-" + num3++; } for (int k = 0; k < current.Hints.Count; k++) { current.Hints[k].Id = current.Id + "#" + (k + 1).ToString(CultureInfo.InvariantCulture); } Entries.Add(current); Hints.AddRange(current.Hints); current = null; inBody = false; body.Clear(); } } } private static Hint ParseHint(string value) { int num = value.IndexOf("::", StringComparison.Ordinal); if (num <= 0) { return null; } Hint hint = new Hint { Trigger = value.Substring(0, num).Trim(), Text = value.Substring(num + 2).Trim() }; string[] array = hint.Trigger.Split('&'); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0) { Condition condition = new Condition(); int num2 = text.IndexOf(':'); if (num2 < 0) { condition.Kind = text.ToLowerInvariant(); } else { condition.Kind = text.Substring(0, num2).Trim().ToLowerInvariant(); condition.Arg = text.Substring(num2 + 1).Trim(); } if (condition.Kind == "time") { float.TryParse(condition.Arg, NumberStyles.Float, CultureInfo.InvariantCulture, out condition.Number); } hint.Conditions.Add(condition); } } if (hint.Conditions.Count <= 0 || hint.Text.Length <= 0) { return null; } return hint; } private static string Slug(string title) { StringBuilder stringBuilder = new StringBuilder(); string text = (title ?? "").ToLowerInvariant(); foreach (char c in text) { if (char.IsLetterOrDigit(c)) { stringBuilder.Append(c); } else if (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '-') { stringBuilder.Append('-'); } } string text2 = stringBuilder.ToString().Trim('-'); if (text2.Length <= 0) { return "entry"; } return text2; } public static string Render(Entry entry) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<size=30><b><color=").Append("#FFB24D").Append('>') .Append(Format.Escape(entry.Title)) .Append("</color></b></size>\n"); List<string> list = new List<string>(); if (!entry.Generated && !string.IsNullOrEmpty(entry.Category)) { list.Add(entry.Category); } foreach (PluginInfo plugin in entry.Plugins) { list.Add(plugin.Metadata.Name + " " + plugin.Metadata.Version); } if (!entry.Present) { list.Add("<color=#E2725B>not installed</color>"); } if (list.Count > 0) { stringBuilder.Append("<size=15><color=").Append("#A3957D").Append('>') .Append(string.Join(" · ", list)) .Append("</color></size>\n"); } stringBuilder.Append('\n'); string text = PageUrl(entry); if (text != null) { stringBuilder.Append("[link: Open the mod page in your browser | ").Append(text).Append("]\n\n"); } if (entry.IsSharedKeys) { RenderShared(stringBuilder); return stringBuilder.ToString().TrimEnd(); } if (!string.IsNullOrWhiteSpace(entry.Body)) { stringBuilder.Append(Format.Markup(Format.Resolve(entry.Body))).Append("\n\n"); } List<HotkeyInfo> list2 = Keys.For(entry.Plugins); if (list2.Count > 0) { stringBuilder.Append(Heading(entry.Generated ? "Keys" : "Keys from this mod's settings")); stringBuilder.Append("<size=14><color=").Append("#8F8270").Append(">Read live from the mod. Change them in the mod settings (") .Append(Format.Resolve("{key:_shudnal.ConfigurationManager|General|Show config manager|F1}")) .Append(").</color></size>\n\n"); foreach (HotkeyInfo item in list2) { AppendKey(stringBuilder, item); } stringBuilder.Append('\n'); } if (entry.Hints.Count > 0) { stringBuilder.Append(Heading("Hugin's hints")); foreach (Hint hint in entry.Hints) { stringBuilder.Append(" • ").Append(Format.Resolve(hint.Text)).Append('\n'); stringBuilder.Append(" <size=14><color=").Append("#8F8270").Append('>') .Append(HintSystem.Describe(hint)); if (SeenStore.Has(hint.Id)) { stringBuilder.Append(" · already shown"); } stringBuilder.Append("</color></size>\n"); } } return stringBuilder.ToString().TrimEnd(); } private static void RenderShared(StringBuilder sb) { sb.Append("One press of these keys reaches more than one setting. Some are fine because they only act in different situations; ").Append("for the rest, rebind one side in the mod settings (").Append(Format.Resolve("{key:_shudnal.ConfigurationManager|General|Show config manager|F1}")) .Append(", switch on Keybinds only).\n\n"); foreach (IGrouping<string, HotkeyInfo> item in from h in Keys.Shared() group h by h.Normalized into g orderby g.Key select g) { HotkeyInfo hotkeyInfo = item.First(); sb.Append(Keys.Badge(hotkeyInfo.Pretty)).Append('\n'); foreach (HotkeyInfo item2 in item) { sb.Append(" ").Append(Format.Escape(item2.PluginName)).Append(" – ") .Append(Format.Escape(item2.Setting)) .Append(" <size=14><color=") .Append("#8F8270") .Append(">(") .Append(Format.Escape(item2.Section)) .Append(")</color></size>\n"); } sb.Append('\n'); } } private static void AppendKey(StringBuilder sb, HotkeyInfo key) { sb.Append(Keys.Badge(key.Pretty)).Append(" ").Append(Format.Escape(key.Setting)); sb.Append(" <size=14><color=").Append("#8F8270").Append(">(") .Append(Format.Escape(key.Section)) .Append(")</color></size>\n"); string text = Format.Clip(key.Description, 170); if (text.Length > 0) { sb.Append(" <size=14><color=#B5A88F>").Append(Format.Escape(text)).Append("</color></size>\n"); } if (key.SharedWith.Count > 0) { sb.Append(" <size=14><color=#FF8A65>Also bound to: ").Append(Format.Escape(string.Join(", ", key.SharedWith.Select((HotkeyInfo o) => o.PluginName + " – " + o.Setting)))).Append("</color></size>\n"); } } private static string Heading(string text) { return "<size=19><b><color=#FFB24D>" + text + "</color></b></size>\n"; } } internal static class HandbookWindow { private class Row { public GameObject Go; public Button Button; public Text Label; public Text Badge; public GameObject Selected; } private const float W = 1560f; private const float H = 880f; private const float Top = 128f; private const float Bottom = 66f; private const float EntryRowHeight = 30f; private const float CategoryRowHeight = 32f; private const float RowSpacing = 2f; private const int ListPadding = 6; private const string AllCategory = "All"; private static GameObject _root; private static InputField _search; private static Text _count; private static RectTransform _categoryContent; private static RectTransform _entryContent; private static RectTransform _detailContent; private static ScrollRect _entryScroll; private static ScrollRect _detailScroll; private static Button _styleButton; private static Button _replayButton; private static Button _resetButton; private static readonly List<Row> CategoryRows = new List<Row>(); private static readonly List<Row> EntryRows = new List<Row>(); private static Button _pageButton; private static GameObject _zoom; private static RawImage _zoomImage; private static int _detailGeneration; private const float MaxPictureHeight = 470f; private static readonly Regex Special = new Regex("^\\s*\\[(img|link|see):\\s*(.+?)\\s*\\]\\s*$", RegexOptions.IgnoreCase); private static Entry _backTarget; private static bool _followingLink; private static readonly List<string> Categories = new List<string>(); private static List<Entry> _visible = new List<Entry>(); private static string _category = "All"; private static string _query = ""; private static Entry _selected; private static bool _blocking; private static bool _suppressSearch; private static int _closedFrame = -100; private static float _resetArmedUntil; private static float _resetDoneUntil; public static bool IsOpen { get { if ((Object)(object)_root != (Object)null) { return _root.activeSelf; } return false; } } public static bool OwnsEscape { get { if (!IsOpen) { return Time.frameCount - _closedFrame <= 1; } return true; } } public static void Tick() { //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) if (_blocking && !IsOpen) { ReleaseInput(); } KeyboardShortcut value = Plugin.HandbookKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { if (IsOpen) { Close(); } else if (CanOpen()) { Open(HintCard.CurrentEntry); } } else { if (!IsOpen) { return; } if (UnityInput.Current.GetKeyDown((KeyCode)27)) { if ((Object)(object)_zoom != (Object)null && _zoom.activeSelf) { CloseZoom(); } else { Close(); } return; } if (UnityInput.Current.GetKeyDown((KeyCode)274)) { Step(1); } else if (UnityInput.Current.GetKeyDown((KeyCode)273)) { Step(-1); } float unscaledTime = Time.unscaledTime; if ((_resetArmedUntil > 0f && unscaledTime > _resetArmedUntil) || (_resetDoneUntil > 0f && unscaledTime > _resetDoneUntil)) { if (unscaledTime > _resetArmedUntil) { _resetArmedUntil = 0f; } if (unscaledTime > _resetDoneUntil) { _resetDoneUntil = 0f; } UpdateFooter(); } } } private static bool CanOpen() { if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } if (Reflect.StaticBool(typeof(Console), "IsVisible")) { return false; } if ((Object)(object)Chat.instance != (Object)null && Reflect.CallBool(Chat.instance, "HasFocus")) { return false; } if (Reflect.StaticBool(typeof(TextInput), "IsVisible")) { return false; } if (Reflect.StaticBool(typeof(Menu), "IsVisible")) { return false; } return true; } public static void Open(Entry focus) { Handbook.Refresh(); if (!EnsureBuilt()) { Plugin.Log.LogWarning((object)"The Handbook window could not be created yet."); return; } _root.SetActive(true); _root.transform.SetAsLastSibling(); Ui.FitScale(_root.transform, 1560f, 880f); if (!_blocking) { GUIManager.BlockInput(true); _blocking = true; } if (focus != null) { _category = "All"; SetSearchText(""); } RebuildCategories(); RebuildEntries(focus ?? _selected); ((Selectable)_search).Select(); _search.ActivateInputField(); } public static void Close() { CloseZoom(); if ((Object)(object)_root != (Object)null && _root.activeSelf) { _root.SetActive(false); _closedFrame = Time.frameCount; } ReleaseInput(); } private static void ReleaseInput() { if (!_blocking) { return; } _blocking = false; try { GUIManager.BlockInput(false); } catch { } } private static bool EnsureBuilt() { if ((Object)(object)_root != (Object)null) { return true; } if ((Object)(object)GUIManager.CustomGUIFront == (Object)null) { return false; } Build(GUIManager.CustomGUIFront.transform); return (Object)(object)_root != (Object)null; } private static void Build(Transform parent) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0122: 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_015a: Expected O, but got Unknown //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Expected O, but got Unknown //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_0384: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Expected O, but got Unknown //IL_03a7: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Expected O, but got Unknown //IL_03c9: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Expected O, but got Unknown CategoryRows.Clear(); EntryRows.Clear(); GUIManager instance = GUIManager.Instance; _root = instance.CreateWoodpanel(parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 1560f, 880f, true); ((Object)_root).name = "HuginsHandbook"; RectTransform val = (RectTransform)_root.transform; Text obj = Ui.Label((Transform)(object)val, "Title", Ui.TitleFont, 38, Ui.Orange, (TextAnchor)3); Ui.Place(((Graphic)obj).rectTransform, 30f, 14f, 420f, 52f); obj.horizontalOverflow = (HorizontalWrapMode)1; obj.text = "Hugin's Handbook"; Text obj2 = Ui.Label((Transform)(object)val, "Tagline", Ui.BodyFont, 17, Ui.Muted, (TextAnchor)3); Ui.Place(((Graphic)obj2).rectTransform, 450f, 22f, 480f, 36f); obj2.text = "Every mod. Every key. Searchable."; GameObject obj3 = instance.CreateButton("Close", (Transform)(object)val, new Vector2(0f, 1f), new Vector2(0f, 1f), Vector2.zero, 110f, 40f); Ui.Place((RectTransform)obj3.transform, 1420f, 18f, 110f, 40f); ((UnityEvent)obj3.GetComponent<Button>().onClick).AddListener(new UnityAction(Close)); float num = 1310f; GameObject obj4 = instance.CreateInputField((Transform)(object)val, new Vector2(0f, 1f), new Vector2(0f, 1f), Vector2.zero, (ContentType)0, "Search a mod, a key like F7 or Alt, or a word like chest", 18, num, 40f); Ui.Place((RectTransform)obj4.transform, 30f, 72f, num, 40f); _search = obj4.GetComponent<InputField>(); ((UnityEvent<string>)(object)_search.onValueChanged).AddListener((UnityAction<string>)OnSearchChanged); _count = Ui.Label((Transform)(object)val, "Count", Ui.BodyFont, 16, Ui.Muted, (TextAnchor)5); Ui.Place(((Graphic)_count).rectTransform, 1350f, 72f, 180f, 40f); float height = 686f; Ui.Scroll((Transform)(object)val, "Categories", 30f, 128f, 300f, height, out _categoryContent, 2f, 6); _entryScroll = Ui.Scroll((Transform)(object)val, "Entries", 340f, 128f, 330f, height, out _entryContent, 2f, 6); _detailScroll = Ui.Scroll((Transform)(object)val, "Detail", 680f, 128f, 850f, height, out _detailContent, 12f, 18); float num2 = 1360f; float num3 = num2 - 10f - 170f; float num4 = num3 - 10f - 190f; float num5 = num4 - 10f - 190f; _pageButton = FooterButton(val, "Mod page", num5, 190f, new UnityAction(OpenModPage)); Text obj5 = Ui.Label((Transform)(object)val, "Help", Ui.BodyFont, 15, Ui.Muted, (TextAnchor)3); Ui.Place(((Graphic)obj5).rectTransform, 32f, 824f, num5 - 44f, 40f); obj5.resizeTextForBestFit = true; obj5.resizeTextMinSize = 11; obj5.resizeTextMaxSize = 15; obj5.text = "Esc closes · Up/Down browse · click a picture to enlarge it · keys are read live from each mod"; _styleButton = FooterButton(val, "Hints", num4, 190f, new UnityAction(CycleStyle)); _replayButton = FooterButton(val, "Replay hint", num3, 170f, new UnityAction(ReplayHint)); _resetButton = FooterButton(val, "Reset hints", num2, 170f, new UnityAction(ResetHints)); BuildZoom(val); _root.SetActive(false); } private static Button FooterButton(RectTransform root, string text, float left, float width, UnityAction action) { //IL_0011: 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown GameObject obj = GUIManager.Instance.CreateButton(text, (Transform)(object)root, new Vector2(0f, 1f), new Vector2(0f, 1f), Vector2.zero, width, 38f); Ui.Place((RectTransform)obj.transform, left, 826f, width, 38f); Button component = obj.GetComponent<Button>(); ((UnityEvent)component.onClick).AddListener(action); return component; } private static Row MakeRow(RectTransform parent, float height, int fontSize) { //IL_0031: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0116: 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_0174: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: 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_0288: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) RectTransform val = Ui.New("Row", (Transform)(object)parent); LayoutElement obj = ((Component)val).gameObject.AddComponent<LayoutElement>(); obj.minHeight = height; obj.preferredHeight = height; Image val2 = ((Component)val).gameObject.AddComponent<Image>(); ((Graphic)val2).color = Color.white; Button val3 = ((Component)val).gameObject.AddComponent<Button>(); ((Selectable)val3).targetGraphic = (Graphic)(object)val2; ColorBlock defaultColorBlock = ColorBlock.defaultColorBlock; ((ColorBlock)(ref defaultColorBlock)).normalColor = new Color(1f, 1f, 1f, 0f); ((ColorBlock)(ref defaultColorBlock)).highlightedColor = new Color(1f, 0.8f, 0.5f, 0.14f); ((ColorBlock)(ref defaultColorBlock)).pressedColor = new Color(1f, 0.7f, 0.35f, 0.28f); ((ColorBlock)(ref defaultColorBlock)).selectedColor = new Color(1f, 1f, 1f, 0f); ((ColorBlock)(ref defaultColorBlock)).disabledColor = new Color(1f, 1f, 1f, 0f); ((ColorBlock)(ref defaultColorBlock)).colorMultiplier = 1f; ((ColorBlock)(ref defaultColorBlock)).fadeDuration = 0.06f; ((Selectable)val3).colors = defaultColorBlock; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)val3).navigation = navigation; RectTransform val4 = Ui.New("Selected", (Transform)(object)val); Ui.Stretch(val4, 0f, 0f, 0f, 0f); Image obj2 = ((Component)val4).gameObject.AddComponent<Image>(); ((Graphic)obj2).color = new Color(1f, 0.63f, 0.24f, 0.2f); ((Graphic)obj2).raycastTarget = false; RectTransform obj3 = Ui.New("Bar", (Transform)(object)val4); obj3.anchorMin = new Vector2(0f, 0f); obj3.anchorMax = new Vector2(0f, 1f); obj3.pivot = new Vector2(0f, 0.5f); obj3.sizeDelta = new Vector2(3f, 0f); obj3.anchoredPosition = Vector2.zero; Image obj4 = ((Component)obj3).gameObject.AddComponent<Image>(); ((Graphic)obj4).color = Ui.Orange; ((Graphic)obj4).raycastTarget = false; ((Component)val4).gameObject.SetActive(false); Text val5 = Ui.Label((Transform)(object)val, "Label", Ui.BoldFont, fontSize, Ui.Body, (TextAnchor)3); Ui.Stretch(((Graphic)val5).rectTransform, 12f, 0f, 84f, 0f); val5.verticalOverflow = (VerticalWrapMode)0; val5.resizeTextForBestFit = true; val5.resizeTextMinSize = Mathf.Max(11, fontSize - 6); val5.resizeTextMaxSize = fontSize; Text val6 = Ui.Label((Transform)(object)val, "Badge", Ui.BoldFont, fontSize - 2, Ui.Orange, (TextAnchor)5); RectTransform rectTransform = ((Graphic)val6).rectTransform; rectTransform.anchorMin = new Vector2(1f, 0f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(1f, 0.5f); rectTransform.sizeDelta = new Vector2(80f, 0f); rectTransform.anchoredPosition = new Vector2(-8f, 0f); val6.horizontalOverflow = (HorizontalWrapMode)1; return new Row { Go = ((Component)val).gameObject, Button = val3, Label = val5, Badge = val6, Selected = ((Component)val4).gameObject }; } private static void EnsureRows(List<Row> rows, RectTransform parent, int count, float height, int fontSize) { while (rows.Count < count) { rows.Add(MakeRow(parent, height, fontSize)); } } private static void RebuildCategories() { //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown Categories.Clear(); Categories.Add("All"); Categories.AddRange(Handbook.CategoriesInOrder()); if (Handbook.HotkeyEntries.Count > 0) { Categories.Add("All hotkeys"); } if (!Categories.Contains(_category)) { _category = "All"; } EnsureRows(CategoryRows, _categoryContent, Categories.Count, 32f, 17); for (int i = 0; i < CategoryRows.Count; i++) { Row row = CategoryRows[i]; bool flag = i < Categories.Count; row.Go.SetActive(flag); if (flag) { string category = Categories[i]; row.Label.text = category; row.Badge.text = Handbook.CountIn(category).ToString(); row.Selected.SetActive(category == _category); ((UnityEventBase)row.Button.onClick).RemoveAllListeners(); ((UnityEvent)row.Button.onClick).AddListener((UnityAction)delegate { SelectCategory(category); }); } } } private static void SelectCategory(string category) { _category = category; RebuildCategories(); RebuildEntries(null); } private static void OnSearchChanged(string text) { if (!_suppressSearch) { _query = text ?? ""; RebuildEntries(null); } } private static void SetSearchText(string text) { _suppressSearch = true; if ((Object)(object)_search != (Object)null) { _search.text = text; } _suppressSearch = false; _query = text; } private static void RebuildEntries(Entry prefer) { //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Expected O, but got Unknown List<string> terms = (_query ?? "").ToLowerInvariant().Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries).ToList(); IEnumerable<Entry> source; if (!(_category == "All hotkeys")) { source = Handbook.Entries.Where((Entry e) => Handbook.IsVisible(e) && (_category == "All" || e.Category == _category)); } else { IEnumerable<Entry> hotkeyEntries = Handbook.HotkeyEntries; source = hotkeyEntries; } List<Entry> list = source.Where((Entry e) => terms.All((string t) => e.Search.Contains(t))).ToList(); if (terms.Count > 0) { list = (from e in list orderby Score(e, terms) descending, e.Order select e).ToList(); } _visible = list; EnsureRows(EntryRows, _entryContent, list.Count, 30f, 16); for (int num = 0; num < EntryRows.Count; num++) { Row row = EntryRows[num]; bool flag = num < list.Count; row.Go.SetActive(flag); if (flag) { Entry entry = list[num]; row.Label.text = (entry.Present ? Format.Escape(entry.Title) : ("<color=#8F8270>" + Format.Escape(entry.Title) + "</color>")); row.Badge.text = ((entry.Badge != null && entry.Badge.Length <= 12) ? entry.Badge : ""); ((UnityEventBase)row.Button.onClick).RemoveAllListeners(); ((UnityEvent)row.Button.onClick).AddListener((UnityAction)delegate { Select(entry); }); } } _count.text = ((list.Count == 1) ? "1 entry" : (list.Count + " entries")); _entryScroll.verticalNormalizedPosition = 1f; Entry entry2 = null; if (prefer != null && list.Contains(prefer)) { entry2 = prefer; } else if (_selected != null && list.Contains(_selected)) { entry2 = _selected; } else if (list.Count > 0) { entry2 = list[0]; } Select(entry2); } private static int Score(Entry entry, List<string> terms) { string text = entry.Title.ToLowerInvariant(); string text2 = entry.Tags.ToLowerInvariant(); int num = 0; foreach (string term in terms) { num = ((!text.StartsWith(term)) ? ((!text.Contains(term)) ? ((!text2.Contains(term)) ? (num + 1) : (num + 2)) : (num + 3)) : (num + 4)); } return num; } private static void Select(Entry entry) { if (!_followingLink) { _backTarget = null; } _selected = entry; for (int i = 0; i < EntryRows.Count; i++) { EntryRows[i].Selected.SetActive(i < _visible.Count && _visible[i] == entry); } ShowDetail((entry != null) ? Handbook.Render(entry) : NothingFound()); int num = ((entry != null) ? _visible.IndexOf(entry) : (-1)); if (num >= 0) { ScrollTo(num); } UpdateFooter(); } private static void ShowDetail(string rich) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Expected O, but got Unknown //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Expected O, but got Unknown _detailGeneration++; for (int num = ((Transform)_detailContent).childCount - 1; num >= 0; num--) { GameObject gameObject = ((Component)((Transform)_detailContent).GetChild(num)).gameObject; gameObject.SetActive(false); Object.Destroy((Object)(object)gameObject); } StringBuilder stringBuilder = new StringBuilder(); List<string> list = new List<string>(); if (_backTarget != null && _backTarget != _selected) { Entry back = _backTarget; AddLink("« Back to " + back.Title, (UnityAction)delegate { FollowLink(back, keepTrail: false); }); } string[] array = (rich ?? "").Replace("\r", "").Split('\n'); foreach (string text in array) { Match match = Special.Match(text); if (match.Success) { AddParagraph(stringBuilder); string[] array2 = match.Groups[2].Value.Split(new char[1] { '|' }, 2); string text2 = array2[0].Trim(); string text3 = ((array2.Length > 1) ? array2[1].Trim() : ""); string text4 = match.Groups[1].Value.ToLowerInvariant(); if (text4 == "img") { list.Add(text2); AddPicture(text2, text3); } else if (text4 == "see") { Entry target = Handbook.FindEntry(text2); if (target != null && Handbook.IsVisible(target)) { Entry from = _selected; AddLink((text3.Length > 0) ? text3 : target.Title, (UnityAction)delegate { _backTarget = from; FollowLink(target, keepTrail: true); }); } } else { string url = ((text3.Length > 0) ? text3 : text2); AddLink(text2, (UnityAction)delegate { OpenUrl(url); }); } } else if (text.Trim().Length == 0) { AddParagraph(stringBuilder); } else { if (stringBuilder.Length > 0) { stringBuilder.Append('\n'); } stringBuilder.Append(text); } } AddParagraph(stringBuilder); Images.Keep(list); LayoutRebuilder.ForceRebuildLayoutImmediate(_detailContent); _detailScroll.verticalNormalizedPosition = 1f; } private static void AddParagraph(StringBuilder paragraph) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) if (paragraph.Length != 0) { Text obj = Ui.Label((Transform)(object)_detailContent, "Text", Ui.BodyFont, 17, Ui.Body, (TextAnchor)0, outline: false); obj.lineSpacing = 1.12f; obj.text = paragraph.ToString(); paragraph.Length = 0; } } private static void AddPicture(string source, string caption) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: 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_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Expected O, but got Unknown //IL_0211: Unknown result type (might be due to invalid IL or missing references) RectTransform holder = Ui.New("Picture", (Transform)(object)_detailContent); LayoutElement layout = ((Component)holder).gameObject.AddComponent<LayoutElement>(); layout.minHeight = 56f; layout.preferredHeight = 56f; Text status = Ui.Label((Transform)(object)holder, "Status", Ui.BodyFont, 15, Ui.Muted, (TextAnchor)4, outline: false); Ui.Stretch(((Graphic)status).rectTransform, 0f, 0f, 0f, 0f); status.text = ((Images.IsWeb(source) && !Plugin.LoadPictures.Value) ? "Pictures are switched off (F1 › Hugin's Handbook › Load pictures)" : "Loading picture …"); RectTransform frame = Ui.New("Image", (Transform)(object)holder); frame.anchorMin = new Vector2(0.5f, 1f); frame.anchorMax = new Vector2(0.5f, 1f); frame.pivot = new Vector2(0.5f, 1f); frame.anchoredPosition = Vector2.zero; frame.sizeDelta = Vector2.zero; RawImage image = ((Component)frame).gameObject.AddComponent<RawImage>(); ((Behaviour)image).enabled = false; Button obj = ((Component)frame).gameObject.AddComponent<Button>(); ((Selectable)obj).targetGraphic = (Graphic)(object)image; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)obj).navigation = navigation; ColorBlock defaultColorBlock = ColorBlock.defaultColorBlock; ((ColorBlock)(ref defaultColorBlock)).highlightedColor = new Color(1f, 0.92f, 0.8f, 1f); ((ColorBlock)(ref defaultColorBlock)).selectedColor = Color.white; ((Selectable)obj).colors = defaultColorBlock; ((UnityEvent)obj.onClick).AddListener((UnityAction)delegate { Zoom(image.texture); }); if (!string.IsNullOrEmpty(caption)) { Ui.Label((Transform)(object)_detailContent, "Caption", Ui.BodyFont, 15, Ui.Muted, (TextAnchor)1, outline: false).text = "<i>" + caption + "</i>"; } int generation = _detailGeneration; UnityAction val = default(UnityAction); Images.Request(source, delegate(Texture2D texture) { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Expected O, but got Unknown //IL_0090: Expected O, but got Unknown if (!((Object)(object)holder == (Object)null) && generation == _detailGeneration) { if ((Object)(object)texture == (Object)null) { if (Images.IsWeb(source) && Plugin.LoadPictures.Value) { status.text = "Picture unavailable here. Click to open it in your browser."; Button obj2 = ((Component)holder).gameObject.AddComponent<Button>(); ((Selectable)obj2).targetGraphic = (Graphic)(object)status; ButtonClickedEvent onClick = obj2.onClick; UnityAction obj3 = val; if (obj3 == null) { UnityAction val2 = delegate { OpenUrl(source); }; UnityAction val3 = val2; val = val2; obj3 = val3; } ((UnityEvent)onClick).AddListener(obj3); } else if (!Images.IsWeb(source)) { status.text = "Picture not found: images/" + source; } } else { Rect rect = _detailContent.rect; float num = Mathf.Min(Mathf.Max(200f, ((Rect)(ref rect)).width - 36f), (float)((Texture)texture).width); float num2 = num * (float)((Texture)texture).height / (float)((Texture)texture).width; if (num2 > 470f) { num2 = 470f; num = num2 * (float)((Texture)texture).width / (float)((Texture)texture).height; } frame.sizeDelta = new Vector2(num, num2); image.texture = (Texture)(object)texture; ((Behaviour)image).enabled = true; status.text = ""; layout.minHeight = num2; layout.preferredHeight = num2; LayoutRebuilder.MarkLayoutForRebuild(_detailContent); } } }); } private static void FollowLink(Entry target, bool keepTrail) { if (target == null) { return; } if (!keepTrail) { _backTarget = null; } _followingLink = true; try { if (!_visible.Contains(target)) { _category = "All"; SetSearchText(""); RebuildCategories(); } RebuildEntries(target); if (_selected != target) { Select(target); } } finally { _followingLink = false; } } private static void AddLink(string label, UnityAction onClick) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) RectTransform obj = Ui.New("Link", (Transform)(object)_detailContent); LayoutElement obj2 = ((Component)obj).gameObject.AddComponent<LayoutElement>(); obj2.minHeight = 30f; obj2.preferredHeight = 30f; Image val = ((Component)obj).gameObject.AddComponent<Image>(); ((Graphic)val).color = Color.white; Button obj3 = ((Component)obj).gameObject.AddComponent<Button>(); ((Selectable)obj3).targetGraphic = (Graphic)(object)val; ColorBlock defaultColorBlock = ColorBlock.defaultColorBlock; ((ColorBlock)(ref defaultColorBlock)).normalColor = new Color(1f, 1f, 1f, 0f); ((ColorBlock)(ref defaultColorBlock)).highlightedColor = new Color(1f, 0.8f, 0.5f, 0.14f); ((ColorBlock)(ref defaultColorBlock)).pressedColor = new Color(1f, 0.7f, 0.35f, 0.28f); ((ColorBlock)(ref defaultColorBlock)).selectedColor = new Color(1f, 1f, 1f, 0f); ((Selectable)obj3).colors = defaultColorBlock; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)obj3).navigation = navigation; ((UnityEvent)obj3.onClick).AddListener(onClick); Text obj4 = Ui.Label((Transform)(object)obj, "Label", Ui.BoldFont, 17, Ui.Orange, (TextAnchor)3); Ui.Stretch(((Graphic)obj4).rectTransform, 6f, 0f, 6f, 0f); obj4.text = (label.StartsWith("«") ? label : ("» " + label)); } private static void OpenModPage() { OpenUrl(Handbook.PageUrl(_selected)); } private static void OpenUrl(string url) { if (Images.IsWeb(url)) { Application.OpenURL(url); } } private static void BuildZoom(RectTransform root) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0139: 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_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) RectTransform val = Ui.New("Zoom", (Transform)(object)root); Ui.Stretch(val, 0f, 0f, 0f, 0f); Image val2 = ((Component)val).gameObject.AddComponent<Image>(); ((Graphic)val2).color = new Color(0.03f, 0.02f, 0.01f, 0.92f); Button obj = ((Component)val).gameObject.AddComponent<Button>(); ((Selectable)obj).targetGraphic = (Graphic)(object)val2; ((Selectable)obj).transition = (Transition)0; Navigation navigation = default(Navigation); ((Navigation)(ref navigation)).mode = (Mode)0; ((Selectable)obj).navigation = navigation; ((UnityEvent)obj.onClick).AddListener(new UnityAction(CloseZoom)); RectTransform obj2 = Ui.New("Picture", (Transform)(object)val); obj2.anchorMin = new Vector2(0.5f, 0.5f); obj2.anchorMax = new Vector2(0.5f, 0.5f); obj2.pivot = new Vector2(0.5f, 0.5f); obj2.anchoredPosition = new Vector2(0f, 12f); _zoomImage = ((Component)obj2).gameObject.AddComponent<RawImage>(); ((Graphic)_zoomImage).raycastTarget = false; Text obj3 = Ui.Label((Transform)(object)val, "Hint", Ui.BodyFont, 15, Ui.Muted, (TextAnchor)4); ((Graphic)obj3).rectTransform.anchorMin = new Vector2(0f, 0f); ((Graphic)obj3).rectTransform.anchorMax = new Vector2(1f, 0f); ((Graphic)obj3).rectTransform.pivot = new Vector2(0.5f, 0f); ((Graphic)obj3).rectTransform.sizeDelta = new Vector2(0f, 30f); ((Graphic)obj3).rectTransform.anchoredPosition = new Vector2(0f, 10f); obj3.text = "Click anywhere or press Esc to go back"; _zoom = ((Component)val).gameObject; _zoom.SetActive(false); } private static void Zoom(Texture texture) { //IL_002e: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)texture == (Object)null) && !((Object)(object)_zoom == (Object)null) && !((Object)(object)_root == (Object)null)) { RectTransform val = (RectTransform)_root.transform; Rect rect = val.rect; float num = ((Rect)(ref rect)).width - 60f; rect = val.rect; float num2 = ((Rect)(ref rect)).height - 90f; float num3 = Mathf.Min(num / (float)texture.width, num2 / (float)texture.height); ((RectTransform)((Component)_zoomImage).transform).sizeDelta = new Vector2((float)texture.width * num3, (float)texture.height * num3); _zoomImage.texture = texture; _zoom.transform.SetAsLastSibling(); _zoom.SetActive(true); } } private static void CloseZoom() { if ((Object)(object)_zoom != (Object)null) { _zoom.SetActive(false); } if ((Object)(object)_zoomImage != (Object)null) { _zoomImage.texture = null; } } private static string NothingFound() { return "<size=24><b><color=#FFB24D>Nothing found</color></b></size>\n\nTry a shorter word, a mod name or a key such as \"Alt\" or \"F7\".\nPick \"All\" on the left to search every category."; } private static void Step(int delta) { if (_visible.Count != 0) { int num = ((_selected != null) ? _visible.IndexOf(_selected) : (-1)); num = Mathf.Clamp(num + delta, 0, _visible.Count - 1); Select(_visible[num]); } } private static void ScrollTo(int index) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) Canvas.ForceUpdateCanvases(); Rect rect = _entryContent.rect; float height = ((Rect)(ref rect)).height; float num; if (!((Object)(object)_entryScroll.viewport != (Object)null)) { num = 0f; } else { rect = _entryScroll.viewport.rect; num = ((Rect)(ref rect)).height; } float num2 = num; if (!(height <= num2) && !(num2 <= 0f)) { float num3 = 32f; float num4 = 6f + (float)index * num3; float num5 = height - num2; float num6 = (1f - _entryScroll.verticalNormalizedPosition) * num5; float num7 = num6; if (num4 < num6) { num7 = num4; } else if (num4 + 30f > num6 + num2) { num7 = num4 + 30f - num2; } _entryScroll.verticalNormalizedPosition = 1f - Mathf.Clamp01(num7 / num5); } } private static void UpdateFooter() { Ui.SetButtonText(_styleButton, "Hints: " + Plugin.Style.Value); bool flag = _selected != null && _selected.Hints.Count > 0; if ((Object)(object)_replayButton != (Object)null) { ((Selectable)_replayButton).interactable = flag; } Ui.SetButtonText(_replayButton, flag ? "Replay hint" : "No hint here"); string value = ((_resetDoneUntil > 0f) ? "Hints reset" : ((_resetArmedUntil > 0f) ? "Click to confirm" : "Reset hints")); Ui.SetButtonText(_resetButton, value); if ((Object)(object)_pageButton != (Object)null) { ((Selectable)_pageButton).interactable = !string.IsNullOrEmpty(Handbook.PageUrl(_selected)); } } private static void CycleStyle() { Plugin.Style.Value = (HintStyle)((int)(Plugin.Style.Value + 1) % 4); UpdateFooter(); } private static void ReplayHint() { if (_selected != null && _selected.Hints.Count != 0) { HintSystem.Replay(_selected.Hints[0]); } } private static void ResetHints() { if (_resetArmedUntil > 0f) { _resetArmedUntil = 0f; _resetDoneUntil = Time.unscaledTime + 3f; SeenStore.Clear(); if (_selected != null) { Select(_selected); } } else { _resetArmedUntil = Time.unscaledTime + 4f; } UpdateFooter(); } } internal static class HintCard { private const float Width = 470f; private static GameObject _root; private static CanvasGroup _group; private static Text _kicker; private static Text _title; private static Text _body; private static Text _footer; private static Entry _entry; private static float _shownAt; private static float _hideAt; public static bool Visible { get { if ((Object)(object)_root != (Object)null) { return _root.activeSelf; } return false; } } public static Entry CurrentEntry { get { if (!Visible) { return null; } return _entry; } } public static void Show(string title, string text, Entry entry) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Expected O, but got Unknown if (EnsureBuilt()) { _entry = entry; _kicker.text = "HUGIN'S HINT"; _title.text = Format.Escape(title); _body.text = text; _footer.text = Keys.Badge(Keys.Pretty(((object)Plugin.HandbookKey.Value/*cast due to .constrained prefix*/).ToString())) + " opens the Handbook here"; _root.SetActive(true); _root.transform.SetAsLastSibling(); Ui.FitScale(_root.transform, 470f, 260f); LayoutRebuilder.ForceRebuildLayoutImmediate((RectTransform)_root.transform); _shownAt = Time.unscaledTime; _hideAt = _shownAt + Plugin.CardSeconds.Value; _group.alpha = 0f; } } public static void Hide() { if ((Object)(object)_root != (Object)null) { _root.SetActive(false); } } public static void Tick() { if (Visible) { float unscaledTime = Time.unscaledTime; if (unscaledTime >= _hideAt) { _root.SetActive(false); return; } float num = Mathf.Clamp01((unscaledTime - _shownAt) / 0.35f); float num2 = Mathf.Clamp01((_hideAt - unscaledTime) / 0.8f); _group.alpha = Mathf.Min(num, num2); } } private static bool EnsureBuilt() { if ((Object)(object)_root != (Object)null) { return true; } if ((Object)(object)GUIManager.CustomGUIFront == (Object)null) { return false; } Build(GUIManager.CustomGUIFront.transform); return (Object)(object)_root != (Object)null; } private static void Build(Transform parent) { //IL_0010: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Expected O, but got Unknown //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Expected O, but got Unknown //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Expected O, but got Unknown //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Expected O, but got Unknown _root = GUIManager.Instance.CreateWoodpanel(parent, new Vector2(1f, 0f), new Vector2(1f, 0f), Vector2.zero, 470f, 160f, false); ((Object)_root).name = "HuginsHandbookHint"; RectTransform val = (RectTransform)_root.transform; val.pivot = new Vector2(1f, 0f); val.anchoredPosition = new Vector2(-28f, 190f); _group = _root.AddComponent<CanvasGroup>(); _group.blocksRaycasts = false; _group.interactable = false; VerticalLayoutGroup obj = _root.AddComponent<VerticalLayoutGroup>(); ((LayoutGroup)obj).padding = new RectOffset(24, 24, 18, 16); ((HorizontalOrVerticalLayoutGroup)obj).spacing = 4f; ((LayoutGroup)obj).childAlignment = (TextAnchor)0; ((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false; ContentSizeFitter obj2 = _root.AddComponent<ContentSizeFitter>(); obj2.horizontalFit = (FitMode)0; obj2.verticalFit = (FitMode)2; _kicker = Ui.Label((Transform)val, "Kicker", Ui.BoldFont, 13, Ui.Muted, (TextAnchor)0); _title = Ui.Label((Transform)val, "Title", Ui.BoldFont, 22, Ui.Orange, (TextAnchor)0); _body = Ui.Label((Transform)val, "Body", Ui.BodyFont, 18, Ui.Body, (TextAnchor)0); _body.lineSpacing = 1.08f; _footer = Ui.Label((Transform)val, "Footer", Ui.BodyFont, 14, Ui.Muted, (TextAnchor)2); _root.SetActive(false); } } internal static class HintSystem { private class Pending { public Hint Hint; public float Until; } private class LookType { public readonly string Name; public readonly Type Type; public LookType(string name, Type type) { Name = name; Type = type; } } private static readonly LookType[] LookTypes = new LookType[16] { new LookType("Container", typeof(Container)), new LookType("TeleportWorld", typeof(TeleportWorld)), new LookType("Beehive", typeof(Beehive)), new LookType("Fermenter", typeof(Fermenter)), new LookType("Vagon", typeof(Vagon)), new LookType("Ship", typeof(Ship)), new LookType("RuneStone", typeof(RuneStone)), new LookType("CookingStation", typeof(CookingStation)), new LookType("Smelter", typeof(Smelter)), new LookType("CraftingStation", typeof(CraftingStation)), new LookType("Pickable", typeof(Pickable)), new LookType("Plant", typeof(Plant)), new LookType("Tameable", typeof(Tameable)), new LookType("Bed", typeof(Bed)), new LookType("Sign", typeof(Sign)), new LookType("ItemStand", typeof(ItemStand)) }; private static readonly List<Pending> Waiting = new List<Pending>(); private static readonly List<string> Held = new List<string>(); private static readonly List<string> Items = new List<string>(); private static readonly HashSet<string> LookingAtTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static Player _player; private static float _sessionStart; private static float _nextPoll; private static float _nextItemScan; private static float _lastShown; private static bool _inventory; private static bool _map; private static bool _ship; private static bool _riding; private static string _container; private static string _station; private static string _lookingAt; private static string _tamed; private static string _biome; private static Hint _verifyHint; private static string _verifyName; private static float _verifyAt; public const string WelcomeId = "hh-welcome"; public static void Tick() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { _player = null; Waiting.Clear(); _verifyHint = null; return; } if ((Object)(object)localPlayer != (Object)(object)_player) { _player = localPlayer; _sessionStart = Time.time; _lastShown = Time.time - Plugin.HintGap.Value; _nextItemScan = 0f; Waiting.Clear(); } VerifyHugin(); if (Time.time < _nextPoll) { return; } _nextPoll = Time.time + 0.5f; if (!SeenStore.Current.Contains("hh-welcome")) { if (!(Time.time - _sessionStart < 3f) && !((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting() && !Reflect.CallBool(localPlayer, "InIntro") && !HandbookWindow.IsOpen && !HintCard.Visible && !Reflect.StaticBool(typeof(Menu), "IsVisible")) { ShowWelcome(); } } else { if (Plugin.Style.Value == HintStyle.Off || ((Character)localPlayer).IsDead() || ((Character)localPlayer).IsTeleporting() || Reflect.CallBool(localPlayer, "InIntro")) { return; } Handbook.EnsureResolved(); Capture(localPlayer); HashSet<string> seen = SeenStore.Current; foreach (Hint hint in Handbook.Hints) { if (hint.Entry.Present && !seen.Contains(hint.Id) && !Waiting.Any((Pending q) => q.Hint == hint) && Matches(hint)) { Waiting.Add(new Pending { Hint = hint, Until = Time.time + 25f }); } } Waiting.RemoveAll((Pending q) => q.Until < Time.time || seen.Contains(q.Hint.Id)); if (Waiting.Count != 0 && !(Time.time - _lastShown < Plugin.HintGap.Value) && !HandbookWindow.IsOpen && !HintCard.Visible && !Reflect.StaticBool(typeof(Menu), "IsVisible")) { Pending pending = Waiting.OrderBy((Pending q) => Handbook.Hints.IndexOf(q.Hint)).First(); Waiting.Remove(pending); Show(pending.Hint, replay: false); } } } public static void Replay(Hint hint) { Show(hint, replay: true); } private static void ShowWelcome() { Entry entry = Handbook.Entries.FirstOrDefault((Entry e) => e.Id == "welcome") ?? new Entry { Id = "welcome", Title = "Hugin's Handbook" }; Show(new Hint { Id = "hh-welcome", Entry = entry, Trigger = "start", Text = "Welcome, warrior! Press {handbook} any time to open Hugin's Handbook. Every mod of this world is explained there, with pictures, keys and a search. I will drop by whenever you meet something new." }, replay: false, Plugin.Style.Value != HintStyle.Card); } private static void Show(Hint hint, bool replay, bool preferHugin = false) { string title = hint.Entry.Title; string text = Format.Resolve(hint.Text); if (!replay) { SeenStore.Mark(hint.Id); } if (Format.Plain(text).Trim().Length == 0) { return; } if (!replay) { _lastShown = Time.time; } Player localPlayer = Player.m_localPlayer; HintStyle value = Plugin.Style.Value; if (!replay && (Object)(object)localPlayer != (Object)null && !((Character)localPlayer).InInterior() && Hugin.Available && (preferHugin || value == HintStyle.Hugin || (value == HintStyle.Auto && Hugin.VanillaTutorialsEnabled))) { string text2 = "hh." + hint.Id; if (Hugin.TryShow(text2, title, text)) { _verifyHint = hint; _verifyName = text2; _verifyAt = Time.time + 2.5f; return; } } HintCard.Show(title, text, hint.Entry); } private static void VerifyHugin() { if (_verifyHint != null && !(Time.time < _verifyAt)) { Hint verifyHint = _verifyHint; _verifyHint = null; if (Hugin.IsPending(_verifyName) == false && !Hugin.HasSeen(_verifyName)) { HintCard.Show(verifyHint.Entry.Title, Format.Resolve(verifyHint.Text), verifyHint.Entry); } } } private static void Capture(Player player) { //IL_0261: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) _inventory = Reflect.StaticBool(typeof(InventoryGui), "IsVisible"); _map = Reflect.StaticBool(typeof(Minimap), "IsOpen"); _container = null; _station = null; if (_inventory) { Container val = Reflect.CurrentContainer(); if ((Object)(object)val != (Object)null) { _container = Clean(((Object)((Component)val).gameObject).name) + " " + val.m_name; } CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); if ((Object)(object)currentCraftingStation != (Object)null) { _station = Clean(((Object)((Component)currentCraftingStation).gameObject).name) + " " + currentCraftingStation.m_name; } } Held.Clear(); object obj = Reflect.Call(player, "GetRightItem"); AddHeld((ItemData)((obj is ItemData) ? obj : null)); object obj2 = Reflect.Call(player, "GetLeftItem"); AddHeld((ItemData)((obj2 is ItemData) ? obj2 : null)); if (Time.time >= _nextItemScan) { _nextItemScan = Time.time + 2f; Items.Clear(); Inventory inventory = ((Humanoid)player).GetInventory(); if (inventory != null) { foreach (ItemData allItem in inventory.GetAllItems()) { if (allItem != null && (Object)(object)allItem.m_dropPrefab != (Object)null) { Items.Add(((Object)allItem.m_dropPrefab).name); } } } } _lookingAt = null; _tamed = null; LookingAtTypes.Clear(); GameObject hoverObject = ((Humanoid)player).GetHoverObject(); if ((Object)(object)hoverObject != (Object)null) { ZNetView componentInParent = hoverObject.GetComponentInParent<ZNetView>(); _lookingAt = Clean(((Object)(((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).gameObject : hoverObject)).name); LookType[] lookTypes = LookTypes; foreach (LookType lookType in lookTypes) { if ((Object)(object)hoverObject.GetComponentInParent(lookType.Type) != (Object)null) { LookingAtTypes.Add(lookType.Name); } } Character componentInParent2 = hoverObject.GetComponentInParent<Character>(); if ((Object)(object)componentInParent2 != (Object)null && componentInParent2.IsTamed()) { _tamed = _lookingAt; } } _ship = Reflect.CallBool(player, "IsAttachedToShip") || Reflect.Call(player, "GetStandingOnShip") != null; _riding = Reflect.CallBool(player, "IsRiding"); _biome = ((object)player.GetCurrentBiome()/*cast due to .constrained prefix*/).ToString(); } private static void AddHeld(ItemData item) { if (item != null && (Object)(object)item.m_dropPrefab != (Object)null) { Held.Add(((Object)item.m_dropPrefab).name); } } private static bool Matches(Hint hint) { foreach (Condition condition in hint.Conditions) { if (!Check(condition)) { return false; } } return hint.Conditions.Count > 0; } private static bool Check(Condition c) { switch (c.Kind) { case "start": return Time.time - _sessionStart >= 12f; case "time": return Time.time - _sessionStart >= c.Number; case "inventory": return _inventory; case "map": return _map; case "ship": return _ship; case "riding": return _riding; case "container": if (_container != null) { return Has(_container, c.Arg); } return false; case "station": if (_station != null) { return Has(_station, c.Arg); } return false; case "hold": return Held.Any((string name) => name.StartsWith(c.Arg, StringComparison.OrdinalIgnoreCase)); case "item": return Items.Any((string name) => Has(name, c.Arg)); case "hover": if (_lookingAt != null) { return Has(_lookingAt, c.Arg); } return false; case "hovertype": return LookingAtTypes.Contains(c.Arg); case "tamed": if (_tamed != null) { return Has(_tamed, c.Arg); } return false; case "biome": return string.Equals(_biome, c.Arg, StringComparison.OrdinalIgnoreCase); default: return false; } } private static bool Has(string value, string part) { if (!string.IsNullOrEmpty(part)) { return (value ?? "").IndexOf(part, StringComparison.OrdinalIgnoreCase) >= 0; } return true; } internal static string Clean(string name) { if (string.IsNullOrEmpty(name)) { return ""; } int num = name.IndexOf("(Clone)", StringComparison.Ordinal); if (num >= 0) { name = name.Substring(0, num); } name = name.Trim(); int num2 = name.LastIndexOf(" (", StringComparison.Ordinal); if (num2 > 0 && name.EndsWith(")", StringComparison.Ordinal)) { name = name.Substring(0, num2); } return name.Trim(); } public static string Describe(Hint hint) { return "Shows " + string.Join(" and ", hint.Conditions.Select(DescribeCondition)); } private static string DescribeCondition(Condition c) { switch (c.Kind) { case "start": return "when you start playing"; case "time": if (!(c.Number < 120f)) { return "after " + Mathf.RoundToInt(c.Number / 60f) + " min of play"; } return "after " + Mathf.RoundToInt(c.Number) + " s of play"; case "inventory": return "when you open your inventory"; case "map": return "when you open the map"; case "ship": return "when you are aboard a ship"; case "riding": return "when you ride"; case "container": if (c.Arg.Length <= 0) { return "when you open a chest"; } return "when you open a " + Words(c.Arg) + " chest"; case "station": if (c.Arg.Length <= 0) { return "when you use a crafting station"; } return "when you use a " + Words(c.Arg); case "hold": return "when you hold a " + Words(c.Arg); case "item": return "when you carry a " + Words(c.Arg); case "hover": return "when you look at a " + Words(c.Arg); case "hovertype": return "when you look at a " + Friendly(c.Arg); case "tamed": return "when you look at a tamed " + ((c.Arg.Length > 0) ? Words(c.Arg) : "animal"); case "biome": return "when you are in the " + Words(c.Arg); default: return c.Kind; } } private static string Friendly(string type) { return type.ToLowerInvariant() switch { "teleportworld" => "portal", "runestone" => "runestone", "vagon" => "cart", "container" => "chest", "craftingstation" => "crafting station", "cookingstation" => "cooking station", "itemstand" => "item stand", "tameable" => "tame animal", "pickable" => "plant you can pick", _ => Words(type).ToLowerInvariant(), }; } private static string Words(string name) { return Regex.Replace((name ?? "").Replace('_', ' '), "(?<=[a-z])(?=[A-Z])", " ").Trim(); } } internal static class Hugin { private static bool _initialized; private static FieldInfo _texts; private static Type _textType; private static PropertyInfo _instanceProperty; private static FieldInfo _instanceField; private static MethodInfo _showTutorial; private static MethodInfo _haveSeen; private static FieldInfo _shownTutorials; private static FieldInfo _tempTexts; private static FieldInfo _tutorialsEnabled; public static bool Available { get { Init(); if (_texts != null && _textType != null) { return _showTutorial != null; } return false; } } public static bool VanillaTutorialsEnabled { get { Init(); try { if (_tutorialsEnabled != null && _tutorialsEnabled.IsStatic && _tutorialsEnabled.FieldType == typeof(bool)) { return (bool)_tutorialsEnabled.GetValue(null); } } catch { } return true; } } private static void Init() { if (_initialized) { return; } _initialized = true; try { Type typeFromHandle = typeof(Tutorial); _texts = AccessTools.Field(typeFromHandle, "m_texts"); if (_texts != null && _texts.FieldType.IsGenericType) { _textType = _texts.FieldType.GetGenericArguments()[0]; } if (_textType == null) { _textType = AccessTools.Inner(typeFromHandle, "TutorialText"); } _instanceProperty = AccessTools.Property(typeFromHandle, "instance"); _instanceField = AccessTools.Field(typeFromHandle, "m_instance"); _showTutorial = AccessTools.Method(typeof(Player), "ShowTutorial", (Type[])null, (Type[])null); _haveSeen = AccessTools.Method(typeof(Player), "HaveSeenTutorial", (Type[])null, (Type[])null); _shownTutorials = AccessTools.Field(typeof(Player), "m_shownTutorials"); Type? typeFromHandle2 = typeof(Raven); _tempTexts = AccessTools.Field(typeFromHandle2, "m_tempTexts"); _tutorialsEnabled = AccessTools.Field(typeFromHandle2, "m_tutorialsEnabled"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Hugin hints unavailable, hint cards will be used: " + ex.Message)); } } public static bool TryShow(string name, string topic, string text) { if (!Available || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } try { object obj = TutorialInstance(); if (obj == null) { return false; } if (!(_texts.GetValue(obj) is IList list)) { return false; } object obj2 = null; foreach (object item in list) { if (item != null && object.Equals(Get(item, "m_name"), name)) { obj2 = item; break; } } if (obj2 == null) { obj2 = Activator.CreateInstance(_textType); list.Add(obj2); } Set(obj2, "m_name", name); Set(obj2, "m_topic", topic); Set(obj2, "m_label", topic); Set(obj2, "m_text", text); Set(obj2, "m_isMunin", false); ParameterInfo[] parameters = _showTutorial.GetParameters(); object[] parameters2; if (parameters.Length == 1) { parameters2 = new object[1] { name }; } else { if (parameters.Length != 2 || !(parameters[1].ParameterType == typeof(bool))) { return false; } parameters2 = new object[2] { name, true }; } _showTutorial.Invoke(Player.m_localPlayer, parameters2); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("Hugin could not deliver a hint, a card is used instead: " + ex.Message)); return false; } } public static bool? IsPending(string name) { Init(); if (_tempTexts == null || !_tempTexts.IsStatic) { return null; } try { if (!(_tempTexts.GetValue(null) is IList list)) { return null; } foreach (object item in list) { if (item != null) { if (AccessTools.Field(item.GetType(), "m_key") == null) { return null; } if (object.Equals(Get(item, "m_key"), name)) { return true; } } } return false; } catch { return null; } } public static bool HasSeen(string name) { Init(); try { if (_haveSeen != null && (Object)(object)Player.m_localPlayer != (Object)null) { return (bool)_haveSeen.Invoke(Player.m_localPlayer, new object[1] { name }); } } catch { } return false; } public static void ForgetAll(string prefix) { Init(); try { if (_shownTutorials != null && (Object)(object)Player.m_localPlayer != (Object)null && _shownTutorials.GetValue(Player.m_localPlayer) is HashSet<string> hashSet) { hashSet.RemoveWhere((string s) => s?.StartsWith(prefix, StringComparison.Ordinal) ?? false); } } catch { } } private static object TutorialInstance() { object obj = null; try { obj = _instanceProperty?.GetValue(null, null); } catch { obj = null; } if (obj == null) { try { obj = _instanceField?.GetValue(null); } catch { obj = null; } } if (obj == null) { obj = Object.FindFirstObjectByType<Tutorial>(); } return obj; } private static object Get(object target, string field) { return AccessTools.Field(target.GetType(), field)?.GetValue(target); } private static void Set(object target, string field, object value) { FieldInfo fieldInfo = AccessTools.Field(target.GetType(), field); if (!(fieldInfo == null) && (value == null || fieldInfo.FieldType.IsInstanceOfType(value))) { fieldInfo.SetValue(target, value); } } } internal static class Images { private const int MaxInMemory = 24; private const int MaxParallel = 2; private const int MaxWidth = 1280; private static readonly Dictionary<string, Texture2D> Loaded = new Dictionary<string, Texture2D>(); private static readonly LinkedList<string> Recent = new LinkedList<string>(); private static readonly HashSet<string> Failed = new HashSet<string>(); private static readonly HashSet<string> Keepers = new HashSet<string>(); private static readonly Dictionary<string, List<Action<Texture2D>>> Callbacks = new Dictionary<string, List<Action<Texture2D>>>(); private static readonly Queue<string> Downloads = new Queue<string>(); private static int _running; private static string CacheFolder => Path.Combine(Paths.CachePath, "HuginsHandbook"); public static string LocalFolder => Path.Combine(Paths.ConfigPath, "HuginsHandbook", "images"); public static bool IsWeb(string source) { if (source != null) { if (!source.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { return source.StartsWith("http://", StringComparison.OrdinalIgnoreCase); } return true; } return false; } public static void Request(string source, Action<Texture2D> done) { if (string.IsNullOrEmpty(source)) { done(null); return; } if (Loaded.TryGetValue(source, out var value) && (Object)(object)value != (Object)null) { Touch(source); done(value); return; } if (Failed.Contains(source)) { done(null); return; } if (Callbacks.TryGetValue(source, out var value2)) { value2.Add(done); return; } Callbacks[source] = new List<Action<Texture2D>> { done }; if (!IsWeb(source)) { string path = Path.Combine(LocalFolder, source.Replace('/', Path.DirectorySeparatorChar)); Finish(source, File.Exists(path) ? SafeRead(path) : null); return; } byte[] array = SafeRead(CachePath(source)); if (array != null) { Finish(source, array); } else if (!Plugin.LoadPictures.Value) { Callbacks.Remove(source); done(null); } else { Downloads.Enqueue(source); Pump(); } } public static void Keep(IEnumerable<string> sources) { Keepers.Clear(); foreach (string source in sources) { Keepers.Add(source); } } private static void Pump() { while (_running < 2 && Downloads.Count > 0 && (Object)(object)Plugin.Instance != (Object)null) { _running++; ((MonoBehaviour)Plugin.Instance).StartCoroutine(Download(Downloads.Dequeue())); } } private static IEnumerator Download(string url) { byte[] data = null; UnityWebRequest request = UnityWebRequest.Get(url); try { request.timeout = 20; yield return request.SendWebRequest(); if ((int)request.result == 1) { data = request.downloadHandler.data; } else { Plugin.Log.LogDebug((object)("Picture not loaded (" + request.error + "): " + url)); } } finally { ((IDisposable)request)?.Dispose(); } _running--; if (data != null && data.Length != 0) { SafeWrite(CachePath(url), data); } Finish(url, data); Pump(); } private static void Finish(string source, byte[] data) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown Texture2D val = null; try { if (data != null && data.Length != 0) { val = new Texture2D(2, 2, (TextureFormat)4, false); if (ImageConversion.LoadImage(val, data, false)) { val = Shrink(val); ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)1; } else { Object.Destroy((Object)(object)val); val = null; } } } catch (Exception ex) { Plugin.Log.LogDebug((object)("Picture could not be read: " + ex.Message)); val = null; } if ((Object)(object)val == (Object)null) { Failed.Add(source); } else { Loaded[source] = val; Touch(source); Trim(); } if (!Callbacks.TryGetValue(source, out var value)) { return; } Callbacks.Remove(source); foreach (Action<Texture2D> item in value) { try { item(val); } catch (Exception ex2) { Plugin.Log.LogDebug((object)("Picture callback failed: " + ex2.Message)); } } } private static Texture2D Shrink(Texture2D source) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0073: 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_009a: Expected O, but got Unknown if (((Texture)source).width <= 1280) { source.Apply(false, true); return source; } int num = 1280; int num2 = Mathf.Max(1, Mathf.RoundToInt((float)((Texture)source).height * (1280f / (float)((Texture)source).width))); RenderTexture temporary = RenderTexture.GetTemporary(num, num2, 0, (RenderTextureFormat)0); RenderTexture active = RenderTexture.active; Graphics.Blit((Texture)(object)source, temporary); RenderTexture.active = temporary; Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false); val.ReadPixels(new Rect(0f, 0f, (float)num, (float)num2), 0, 0); val.Apply(false, true); RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); Object.Destroy((Object)(object)source); return val; } private static void Touch(string source) { Recent.Remove(source); Recent.AddFirst(source); } private static void Trim() { LinkedListNode<string> linkedListNode = Recent.Last; while (Loaded.Count > 24 && linkedListNode != null) { LinkedListNode<string>? previous = linkedListNode.Previous; if (!Keepers.Contains(linkedListNode.Value)) { if (Loaded.TryGetValue(linkedListNode.Value, out var value) && (Object)(object)value != (Object)null) { Object.Destroy((Object)(object)value); } Loaded.Remove(linkedListNode.Value); Recent.Remove(linkedListNode); } linkedListNode = previous; } } private static string CachePath(string url) { using SHA1 sHA = SHA1.Create(); byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(url)); StringBuilder stringBuilder = new StringBuilder(array.Length * 2); byte[] array2 = array; foreach (byte b in array2) { stringBuilder.Append(b.ToString("x2")); } return Path.Combine(CacheFolder, stringBuilder?.ToString() + ".img"); } private static byte[] SafeRead(string path) { try { return File.Exists(path) ? File.ReadAllBytes(path) : null; } catch { return null; } } private static void SafeWrite(string path, byte[] data) { try { Directory.CreateDirectory(Path.GetDirectoryName(path)); File.WriteAllBytes(path, data); } catch (Exception ex) { Plugin.Log.LogDebug((object)("Picture cache not written: " + ex.Message)); } } } internal class HotkeyInfo { public PluginInfo Plugin; public string PluginName; public string Section; public string Setting; public string Pretty; public string Normalized; public string Description; public List<HotkeyInfo> SharedWith = new List<HotkeyInfo>(); } internal static class Keys { private static readonly Regex KeyLikeName = new Regex("(hot ?key|shortcut|key ?bind|\\bkey\\b)", RegexOptions.IgnoreCase); private static readonly Regex SimpleToken = new Regex("^[A-Za-z][A-Za-z0-9]{0,15}$"); private static readonly HashSet<string> HoldModifiers = new HashSet<string> { "Alt", "AltGr", "Ctrl", "Shift", "Cmd" }; private static readonly Dictionary<PluginInfo, string> PackageCache = new Dictionary<PluginInfo, string>(); private static readonly Dictionary<string, PluginInfo> PluginCache = new Dictionary<string, PluginInfo>(StringComparer.OrdinalIgnoreCase); private static float _scannedAt = -999f; public const string Unbound = "\u0001unbound"; public static List<HotkeyInfo> All { get; private set; } = new List<HotkeyInfo>(); public static void Rescan(bool force = false) { if (!force && Time.realtimeSinceStartup - _scannedAt < 2f) { return; } _scannedAt = Time.realtimeSinceStartup; List<HotkeyInfo> list = new List<HotkeyInfo>(); foreach (PluginInfo value in Chainloader.PluginInfos.Values) { IDictionary<ConfigDefinition, ConfigEntryBase> dictionary = Settings(value); if (dictionary == null) { continue; } ConfigDefinition[] array; try { array = dictionary.Keys.ToArray(); } catch { continue; } ConfigDefinition[] array2 = array; foreach (ConfigDefinition val in array2) { ConfigEntryBase val2; try { val2 = dictionary[val]; } catch { continue; } string text = RawKey(val2, val, explicitLookup: false); if (text != null) { HotkeyInfo hotkeyInfo = new HotkeyInfo { Plugin = value, PluginName = value.Metadata.Name, Section = val.Section, Setting = val.Key, Pretty = Pretty(text), Description = ((val2.Description != null) ? (val2.Description.Description ?? "") : "") }; hotkeyInfo.Normalized = Normalize(hotkeyInfo.Pretty); list.Add(hotkeyInfo); } } } foreach (IGrouping<string, HotkeyInfo> item in from h in list where !HoldModifiers.Contains(h.Pretty) group h by h.Normalized) { List<HotkeyInfo> list2 = item.ToList(); if (list2.Count < 2) { continue; } foreach (HotkeyInfo member in list2) { member.SharedWith = list2.Where((HotkeyInfo other) => other != member).ToList(); } } All = list; } public static List<HotkeyInfo> For(IEnumerable<PluginInfo> plugins) { HashSet<PluginInfo> set = new HashSet<PluginInfo>(plugins); return All.Where((HotkeyInfo h) => set.Contains(h.Plugin)).ToList(); } public static List<HotkeyInfo> Shared() { return All.Where((HotkeyInfo h) => h.SharedWith.Count > 0).ToList(); } public static string Lookup(string pluginId, string section, string setting) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown IDictionary<ConfigDefinition, ConfigEntryBase> dictionary = Settings(FindPlugin(pluginId)); if (dictionary == null) { return null; } try { ConfigDefinition val = new ConfigDefinition(section, setting); if (!dictionary.ContainsKey(val)) { return null; } ConfigEntryBase entry = dictionary[val]; string text = RawKey(entry, val, explicitLookup: true); if (text != null) { return text; } return IsCleared(entry) ? "\u0001unbound" : null; } catch { return null; } } private static bool IsCleared(ConfigEntryBase entry) { //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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 object boxedValue; try { boxedValue = entry.BoxedValue; } catch { return false; } if (boxedValue is KeyboardShortcut val) { return (int)((KeyboardShortcut)(ref val)).MainKey == 0; } if (boxedValue is KeyCode val2) { return (int)val2 == 0; } if (boxedValue is string text) { string text2 = text.Trim(); if (text2.Length != 0) { return text2.Equals("None", StringComparison.OrdinalIgnoreCase); } return true; } return false; } private static IDictionary<ConfigDefinition, ConfigEntryBase> Settings(PluginInfo info) { try { if (info == null || (Object)(object)info.Instance == (Object)null) { return null; } return (IDictionary<ConfigDefinition, ConfigEntryBase>)info.Instance.Config; } catch { return null; } } private unsafe static string RawKey(ConfigEntryBase entry, ConfigDefinition definition, bool explicitLookup) { //IL_001b: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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_009e: Unknown result type (might be due to invalid IL or missing references) object boxedValue; try { boxedValue = entry.BoxedValue; } catch { return null; } if (boxedValue is KeyboardShortcut val) { if ((int)((KeyboardShortcut)(ref val)).MainKey != 0) { return ((object)(*(KeyboardShortcut*)(&val))/*cast due to .constrained prefix*/).ToString(); } return null; } if (boxedValue is KeyCode val2) { if ((int)val2 != 0) { return ((object)(*(KeyCode*)(&val2))/*cast due to .constrained prefix*/).ToString(); } return null; } if (boxedValue is string text) { if (!explicitLookup && !KeyLikeName.IsMatch(definition.Key)) { return null; } string text2 = text.Trim(); if (!SimpleToken.IsMatch(text2)) { return null; } if (!Enum.TryParse<KeyCode>(text2, ignoreCase: true, out KeyCode result) || (int)result == 0) { return null; } return ((object)(*(KeyCode*)(&result))/*cast due to .constrained prefix*/).ToString(); } return null; } public static string Pretty(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return ""; } List<string> list = (from p in raw.Split('+') select p.Trim() into p where p.Length > 0 select p).ToList(); if (list.Count == 0) { return raw.Trim(); } string item = Name(list[0]); List<string> list2 = list.Skip(1).Select(Name).Distinct() .OrderBy(Rank) .ToList(); list2.Add(item); return string.Join("+", list2); } private static int Rank(string modifier) { return modifier switch { "Ctrl" => 0, "Shift" => 1, "Alt" => 2, _ => 3, }; } public static string Name(string key) { switch (key) { case "LeftAlt": case "RightAlt": return "Alt"; case "AltGr": return "AltGr"; case "LeftControl": case "RightControl": return "Ctrl"; case "LeftShift": case "RightShift": return "Shift"; case "LeftCommand": case "LeftWindows": case "RightCommand": case "RightWindows": case "LeftApple": case "RightApple": return "Cmd"; case "Mouse0": return "Left mouse"; case "Mouse1": return "Right mouse"; case "Mouse2": return "Middle mouse"; case "Mouse3": return "Mouse 4"; case "Mouse4": return "Mouse 5"; case "Mouse5": return "Mouse 6"; case "Mouse6": return "Mouse 7"; case "Equals": return "="; case "Minus": return "-"; case "BackQuote": return "`"; case "Return": return "Enter"; case "KeypadEnter": return "Num Enter";