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 Steam Achievements And Discoveries v1.0.0
plugins/Steam_Achievements_And_Discoveries.dll
Decompiled 21 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Steam Achievements & Discoveries")] [assembly: AssemblyDescription("The complete Valheim compendium: every Steam achievement and everything you can discover, find, collect, craft, fight, explore or complete, including content from other mods. Client side, no network traffic.")] [assembly: AssemblyCompany("Igao")] [assembly: AssemblyProduct("Steam Achievements & Discoveries")] [assembly: AssemblyCopyright("Igao")] [assembly: ComVisible(false)] [assembly: Guid("7B3E5A21-4C9D-4F18-A6E2-3D0B9C51E7A4")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } internal static class SCMPanel { private sealed class PendingPanel { public string Command; public bool AdminOnly; } private static readonly object Gate = new object(); private static readonly List<PendingPanel> Pending = new List<PendingPanel>(); private static bool _assemblyHooked; public static void Register(string Command, bool AdminOnly = false) { if (string.IsNullOrWhiteSpace(Command)) { throw new ArgumentException("Panel command cannot be empty.", "Command"); } PendingPanel panel = new PendingPanel { Command = Command.Trim(), AdminOnly = AdminOnly }; if (TryRegister(panel)) { return; } lock (Gate) { if (!Pending.Any((PendingPanel p) => string.Equals(p.Command, panel.Command, StringComparison.OrdinalIgnoreCase) && p.AdminOnly == panel.AdminOnly)) { Pending.Add(panel); } if (!_assemblyHooked) { _assemblyHooked = true; AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad; } } } private static void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args) { try { if (!(args?.LoadedAssembly == null) && string.Equals(args.LoadedAssembly.GetName().Name, "Ultimate_SCM", StringComparison.OrdinalIgnoreCase)) { FlushPending(); } } catch { } } private static void FlushPending() { PendingPanel[] array; lock (Gate) { array = Pending.ToArray(); } PendingPanel[] array2 = array; foreach (PendingPanel pendingPanel in array2) { if (TryRegister(pendingPanel)) { lock (Gate) { Pending.Remove(pendingPanel); } } } lock (Gate) { if (Pending.Count == 0 && _assemblyHooked) { AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad; _assemblyHooked = false; } } } private static bool TryRegister(PendingPanel panel) { try { Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly a) => string.Equals(a.GetName().Name, "Ultimate_SCM", StringComparison.OrdinalIgnoreCase)); if (assembly == null) { return false; } Type type = assembly.GetType("UltimateSCM.PanelAPI", throwOnError: false, ignoreCase: false); if (type == null) { return false; } MethodInfo method = type.GetMethod("RegisterPanelForAssembly", BindingFlags.Static | BindingFlags.Public, null, new Type[3] { typeof(string), typeof(bool), typeof(Assembly) }, null); if (method == null) { return false; } method.Invoke(null, new object[3] { panel.Command, panel.AdminOnly, typeof(SCMPanel).Assembly }); return true; } catch { return false; } } } namespace SteamAD { [BepInPlugin("Igao.SteamAchievementsDiscoveries", "Steam Achievements & Discoveries", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "Igao.SteamAchievementsDiscoveries"; public const string Name = "Steam Achievements & Discoveries"; public const string Version = "1.0.0"; public const string PanelCommand = "discoveries_config"; public const string Author = "Igao"; public const string DiscordUrl = "https://discord.gg/aMkN89Cuas"; public const string UqolUrl = "https://thunderstore.io/c/valheim/p/Igao/Ultimate_QOL/"; public const string OdinBetUrl = "https://thunderstore.io/c/valheim/p/Igao/OdinBet_ForgeOfPotential/"; internal static Plugin Instance; private Harmony _harmony; private readonly Dictionary<string, float> _lastWarn = new Dictionary<string, float>(); private void Awake() { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown Instance = this; Cfg.Initialize(((BaseUnityPlugin)this).Config); Translation.Initialize(Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location)); _harmony = new Harmony("Igao.SteamAchievementsDiscoveries"); PatchAllSafely(); Panel.Ensure(((Component)this).gameObject); try { SCMPanel.Register("discoveries_config"); } catch (Exception ex) { Warn("Ultimate SCM registration skipped: " + ex.Message); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Steam Achievements & Discoveries 1.0.0 loaded."); } private void PatchAllSafely() { Type[] types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type type in types) { if (type.IsDefined(typeof(HarmonyPatch), inherit: false)) { try { _harmony.CreateClassProcessor(type).Patch(); } catch (Exception ex) { Warn("Skipped patch " + type.Name + ": " + ex.Message); } } } } private void Update() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)Player.m_localPlayer != (Object)null && !Panel.Typing && GameAccess.KeyDown(Cfg.PanelKey.Value) && !GameAccess.KeyboardBusy()) { if (Panel.IsOpen) { Panel.Close(); } else { Panel.Open(); } } } catch (Exception ex) { WarnOnce("key", ex.Message); } try { Discovery.Tick(); } catch (Exception ex2) { WarnOnce("tick", ex2.ToString()); } } internal static void Log(string message) { try { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)message); } } catch { } } internal static void Warn(string message) { try { if ((Object)(object)Instance != (Object)null) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)message); } else { Debug.LogWarning((object)("[Steam Achievements & Discoveries] " + message)); } } catch { } } internal static void WarnOnce(string source, string message) { if (!((Object)(object)Instance == (Object)null)) { float unscaledTime = Time.unscaledTime; if (!Instance._lastWarn.TryGetValue(source, out var value) || !(unscaledTime - value < 30f)) { Instance._lastWarn[source] = unscaledTime; Warn(source + ": " + message); } } } } internal sealed class LinkedMod { internal string Guid; internal string Name = ""; internal string Description = ""; internal string Version = ""; internal string Command; internal string IconPath; internal bool AdminOnly; internal bool IsUqol; internal string Url; internal string IconSpec; internal bool ScmSelf; internal object ScmRegistration; private Texture2D _icon; private bool _iconLoaded; internal float FitWidth = -1f; internal int FitVersion = -1; internal string FitName; internal string FitDesc; internal string FitVersionText; internal float FitDescH; internal Texture2D Icon { get { if (_iconLoaded) { return _icon; } _iconLoaded = true; _icon = Integration.ScmIcon(ScmRegistration) ?? Integration.LoadImage(IconPath, mipmaps: false); return _icon; } } } internal static class Integration { internal const string UqolGuid = "Igao.UltimateQOL"; private const string ScmGuid = "Igao.UltimateSCM"; private static readonly string[,] Known = new string[2, 2] { { "Igao.UltimateQOL", "ultimateqol_config" }, { "OdinBet.ForgeOfPotential", "odinbet_config" } }; internal static readonly List<LinkedMod> Mods = new List<LinkedMod>(); private static readonly string[,] Promos = new string[2, 5] { { "Igao.UltimateQOL", "Ultimate QOL", "All-in-one QOL: craft/build from chests, station automation, animals, farming, portals, bosses and progression, difficulty, advanced building, map sharing, smooth saves. Server-synced in-game admin panel, 15 languages.", "https://thunderstore.io/c/valheim/p/Igao/Ultimate_QOL/", "p:piece_workbench" }, { "OdinBet.ForgeOfPotential", "OdinBet ForgeOfPotential", "Gamble on your gear. Or lose it. Set the odds at the Refinement Forge, choose what a failure costs, announce every win and wreck. Plus an in-game settings panel, a Hall of Fame, Elemental Infusions on Thunder Nights and Odin's visit.", "https://thunderstore.io/c/valheim/p/Igao/OdinBet_ForgeOfPotential/", "p:forge" } }; private static int _pluginCount = -1; private static int _scmRevision = int.MinValue; private static bool _scmResolved; private static FieldInfo _scmRegistrations; private static PropertyInfo _scmRevisionProp; private static Type _scmPanel; private static readonly Dictionary<string, FieldInfo> RegFields = new Dictionary<string, FieldInfo>(StringComparer.Ordinal); private static PropertyInfo _regIcon; private static FieldInfo _commands; private static MethodInfo _loadImage; private static bool _loadImageResolved; internal static bool UqolInstalled { get { try { return Chainloader.PluginInfos.ContainsKey("Igao.UltimateQOL"); } catch { return false; } } } internal static void Refresh() { int count; try { count = Chainloader.PluginInfos.Count; } catch { return; } ResolveScm(); int num = ScmRevision(); if (count == _pluginCount && num == _scmRevision) { return; } _pluginCount = count; _scmRevision = num; Dictionary<string, LinkedMod> dictionary = new Dictionary<string, LinkedMod>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < Mods.Count; i++) { if (!string.IsNullOrEmpty(Mods[i].Guid) && !dictionary.ContainsKey(Mods[i].Guid)) { dictionary[Mods[i].Guid] = Mods[i]; } } Mods.Clear(); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Igao.SteamAchievementsDiscoveries" }; List<LinkedMod> list = new List<LinkedMod>(); try { if (_scmPanel != null && Chainloader.PluginInfos.TryGetValue("Igao.UltimateSCM", out var value) && value != null) { LinkedMod linkedMod = Reuse(dictionary, "Igao.UltimateSCM") ?? FromPlugin(value); linkedMod.ScmSelf = true; hashSet.Add("Igao.UltimateSCM"); list.Add(linkedMod); } for (int j = 0; j < Known.GetLength(0); j++) { string text = Known[j, 0]; if (!hashSet.Contains(text) && Chainloader.PluginInfos.TryGetValue(text, out var value2) && value2 != null) { hashSet.Add(text); LinkedMod linkedMod2 = Reuse(dictionary, text) ?? FromPlugin(value2); linkedMod2.Command = Known[j, 1]; linkedMod2.IsUqol = string.Equals(text, "Igao.UltimateQOL", StringComparison.OrdinalIgnoreCase); list.Add(linkedMod2); } } IDictionary dictionary2 = null; try { dictionary2 = ((_scmRegistrations != null) ? (_scmRegistrations.GetValue(null) as IDictionary) : null); } catch { } if (dictionary2 != null) { object[] array; try { array = new object[dictionary2.Count]; dictionary2.Values.CopyTo(array, 0); } catch { array = new object[0]; } foreach (object obj4 in array) { if (obj4 == null) { continue; } string text2 = RegString(obj4, "SourcePluginGuid"); string text3 = RegString(obj4, "Command"); if (string.IsNullOrEmpty(text3) || text3.Trim().StartsWith("discoveries_config", StringComparison.OrdinalIgnoreCase)) { continue; } string text4 = (string.IsNullOrEmpty(text2) ? ("cmd:" + text3) : text2); if (!hashSet.Contains(text4)) { hashSet.Add(text4); LinkedMod linkedMod3 = Reuse(dictionary, text4); if (linkedMod3 == null) { linkedMod3 = ((!string.IsNullOrEmpty(text2) && Chainloader.PluginInfos.TryGetValue(text2, out var value3) && value3 != null) ? FromPlugin(value3) : new LinkedMod()); linkedMod3.Guid = text4; } linkedMod3.Command = text3; linkedMod3.ScmRegistration = obj4; object obj5 = RegValue(obj4, "AdminOnly"); linkedMod3.AdminOnly = obj5 is bool && (bool)obj5; string text5 = RegString(obj4, "Name"); string text6 = RegString(obj4, "Description"); string text7 = RegString(obj4, "Version"); string text8 = RegString(obj4, "IconPath"); if (!string.IsNullOrEmpty(text5)) { linkedMod3.Name = text5.Trim(); } if (!string.IsNullOrEmpty(text6)) { linkedMod3.Description = text6.Trim(); } if (!string.IsNullOrEmpty(text7)) { linkedMod3.Version = text7.Trim(); } if (!string.IsNullOrEmpty(text8)) { linkedMod3.IconPath = text8; } if (string.IsNullOrEmpty(linkedMod3.Name)) { linkedMod3.Name = text3; } list.Add(linkedMod3); } } } } catch (Exception ex) { Plugin.Warn("Integration list: " + ex.Message); } LinkedMod linkedMod4 = null; for (int l = 0; l < list.Count; l++) { if (list[l].ScmSelf) { linkedMod4 = list[l]; } } list.RemoveAll((LinkedMod x) => x.ScmSelf || x.AdminOnly); list.Sort((LinkedMod x, LinkedMod y) => string.Compare(x.Name, y.Name, StringComparison.CurrentCultureIgnoreCase)); if (linkedMod4 != null) { Mods.Add(linkedMod4); } Mods.AddRange(list); for (int num2 = 0; num2 < Promos.GetLength(0); num2++) { bool flag; try { flag = Chainloader.PluginInfos.ContainsKey(Promos[num2, 0]); } catch { flag = false; } if (!flag) { LinkedMod linkedMod5 = new LinkedMod(); linkedMod5.Guid = "promo:" + Promos[num2, 0]; linkedMod5.Name = Promos[num2, 1]; linkedMod5.Description = Promos[num2, 2]; linkedMod5.Url = Promos[num2, 3]; linkedMod5.IconSpec = Promos[num2, 4]; linkedMod5.IsUqol = string.Equals(Promos[num2, 0], "Igao.UltimateQOL", StringComparison.OrdinalIgnoreCase); if (!string.IsNullOrEmpty(linkedMod5.Url)) { Mods.Add(linkedMod5); } } } } private static LinkedMod Reuse(Dictionary<string, LinkedMod> old, string key) { LinkedMod value; return old.TryGetValue(key, out value) ? value : null; } internal static bool Open(LinkedMod m) { try { if (m == null) { return false; } if (m.ScmSelf) { return OpenScm(); } if (string.IsNullOrEmpty(m.Command)) { return false; } Terminal instance = (Terminal)(object)Console.instance; if ((Object)(object)instance == (Object)null) { return false; } string text = m.Command.Trim(); int num = text.IndexOf(' '); string text2 = ((num < 0) ? text : text.Substring(0, num)); if (_commands == null) { _commands = typeof(Terminal).GetField("commands", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } IDictionary dictionary = ((_commands != null) ? (_commands.GetValue(null) as IDictionary) : null); if (dictionary == null) { return false; } string key = text2.ToLowerInvariant(); if (!dictionary.Contains(key)) { key = text2.ToLower(); } if (!dictionary.Contains(key)) { return false; } object? obj = dictionary[key]; ConsoleCommand val = (ConsoleCommand)((obj is ConsoleCommand) ? obj : null); if (val == null || !val.IsValid(instance, false)) { return false; } instance.TryRunCommand(text, false, false); return true; } catch (Exception ex) { Plugin.Warn("Could not open the panel of " + ((m != null) ? m.Name : "?") + ": " + ex.Message); return false; } } private static bool OpenScm() { if (_scmPanel == null) { return false; } PropertyInfo property = _scmPanel.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); object obj = ((property != null) ? property.GetValue(null, null) : null); if (obj == null) { return false; } MethodInfo method = _scmPanel.GetMethod("Open", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); if (method == null) { return false; } method.Invoke(obj, null); return true; } private static void ResolveScm() { if (_scmResolved) { return; } Assembly assembly = null; try { if (Chainloader.PluginInfos.TryGetValue("Igao.UltimateSCM", out var value) && value != null && (Object)(object)value.Instance != (Object)null) { assembly = ((object)value.Instance).GetType().Assembly; } } catch { } if (assembly == null) { if ((Object)(object)Player.m_localPlayer != (Object)null) { _scmResolved = true; } return; } _scmResolved = true; try { Type type = assembly.GetType("UltimateSCM.ExternalPanelRegistry", throwOnError: false); if (type != null) { _scmRegistrations = type.GetField("Registrations", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _scmRevisionProp = type.GetProperty("Revision", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } _scmPanel = assembly.GetType("UltimateSCM.UI.ConfigPanel", throwOnError: false); } catch (Exception ex) { Plugin.Warn("Ultimate SCM integration: " + ex.Message); } } private static int ScmRevision() { try { return (_scmRevisionProp != null) ? ((int)_scmRevisionProp.GetValue(null, null)) : ((_scmPanel != null) ? (-2) : (-1)); } catch { return -3; } } private static object RegValue(object reg, string field) { Type type = reg.GetType(); string key = type.FullName + "." + field; if (!RegFields.TryGetValue(key, out var value)) { value = type.GetField(field, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); RegFields[key] = value; } try { return (value != null) ? value.GetValue(reg) : null; } catch { return null; } } private static string RegString(object reg, string field) { return RegValue(reg, field) as string; } internal static Texture2D ScmIcon(object reg) { if (reg == null) { return null; } try { if (_regIcon == null) { _regIcon = reg.GetType().GetProperty("IconTexture", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } return (Texture2D)((_regIcon != null) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } private static LinkedMod FromPlugin(PluginInfo info) { LinkedMod linkedMod = new LinkedMod(); try { linkedMod.Guid = info.Metadata.GUID; linkedMod.Name = info.Metadata.Name ?? linkedMod.Guid; linkedMod.Version = ((info.Metadata.Version != null) ? info.Metadata.Version.ToString() : ""); string text = PackageRoot(info.Location); if (text != null) { string path = Path.Combine(text, "manifest.json"); if (File.Exists(path)) { string json = File.ReadAllText(path); string text2 = JsonString(json, "name"); string text3 = JsonString(json, "description"); string text4 = JsonString(json, "version_number"); if (!string.IsNullOrEmpty(text2)) { linkedMod.Name = text2.Replace('_', ' '); } if (text3 != null) { linkedMod.Description = text3; } if (!string.IsNullOrEmpty(text4)) { linkedMod.Version = text4; } } string text5 = Path.Combine(text, "icon.png"); if (File.Exists(text5)) { linkedMod.IconPath = text5; } } } catch (Exception ex) { Plugin.Warn("Integration data of " + linkedMod.Guid + ": " + ex.Message); } return linkedMod; } private static string PackageRoot(string location) { if (string.IsNullOrEmpty(location)) { return null; } string text = Path.GetDirectoryName(location); string text2 = null; try { text2 = Path.GetFullPath(Paths.PluginPath).TrimEnd('\\', '/'); } catch { } for (int i = 0; i < 4; i++) { if (string.IsNullOrEmpty(text)) { break; } string a; try { a = Path.GetFullPath(text).TrimEnd('\\', '/'); } catch { return null; } if (text2 != null && string.Equals(a, text2, StringComparison.OrdinalIgnoreCase)) { return null; } if (File.Exists(Path.Combine(text, "manifest.json"))) { return text; } text = Directory.GetParent(text)?.FullName; } return null; } private static string JsonString(string json, string key) { Match match = Regex.Match(json, "\"" + Regex.Escape(key) + "\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\""); if (!match.Success) { return null; } return Regex.Unescape(match.Groups[1].Value).Trim(); } internal static Texture2D LoadImage(string path, bool mipmaps) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown if (string.IsNullOrEmpty(path)) { return null; } try { if (Application.isBatchMode || !File.Exists(path)) { return null; } if (!_loadImageResolved) { _loadImageResolved = true; Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule"); if (type != null) { _loadImage = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(Texture2D), typeof(byte[]) }, null); } } if (_loadImage == null) { return null; } byte[] array = File.ReadAllBytes(path); if (array.Length == 0) { return null; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, mipmaps); ((Object)val).hideFlags = (HideFlags)61; object obj = _loadImage.Invoke(null, new object[2] { val, array }); if (obj is bool && !(bool)obj) { Object.Destroy((Object)(object)val); return null; } ((Texture)val).wrapMode = (TextureWrapMode)1; ((Texture)val).filterMode = (FilterMode)((!mipmaps) ? 1 : 2); if (mipmaps) { ((Texture)val).anisoLevel = 8; ((Texture)val).mipMapBias = -0.6f; } return val; } catch (Exception ex) { Plugin.Warn("Image " + path + ": " + ex.Message); return null; } } } internal static class Cfg { internal static ConfigEntry<KeyCode> PanelKey; internal static ConfigEntry<float> Scale; internal static ConfigEntry<int> Spoilers; internal static ConfigEntry<int> SpoilBosses; internal static ConfigEntry<int> SpoilCreatures; internal static ConfigEntry<int> SpoilWorld; internal static ConfigEntry<int> SpoilItems; internal static ConfigEntry<int> Notifications; internal static ConfigEntry<bool> ShowMods; internal static ConfigEntry<bool> Sound; internal static ConfigEntry<bool> TrackWorld; internal static ConfigEntry<bool> ScanModNames; internal static ConfigEntry<bool> GridView; internal static ConfigEntry<string> NotifyFoundPages; internal static ConfigEntry<string> NotifyDonePages; internal static ConfigEntry<bool> NotifyGoals; internal static ConfigEntry<bool> NotifyReady; internal static readonly string[] NotifyPages = new string[14] { "biomes", "creatures", "bosses", "places", "events", "traders", "nature", "materials", "food", "equipment", "fish", "trophies", "recipes", "pieces" }; private const string NotifyDefault = "biomes,creatures,bosses,places,events,traders"; private static readonly HashSet<string> FoundSet = new HashSet<string>(StringComparer.Ordinal); private static readonly HashSet<string> DoneSet = new HashSet<string>(StringComparer.Ordinal); private static string _foundSrc; private static string _doneSrc; internal static ConfigEntry<bool> AllowAchievements; internal static ConfigEntry<bool> AllowWithCheats; private static HashSet<string> Pages(ConfigEntry<string> entry, HashSet<string> set, ref string src) { string text = ((entry != null) ? (entry.Value ?? "") : ""); if (text == src) { return set; } src = text; set.Clear(); string[] array = text.Split(',', ';', ' '); foreach (string text2 in array) { if (text2.Length > 0) { set.Add(text2.Trim().ToLowerInvariant()); } } return set; } internal static bool PageNotify(string page, bool done) { if (Notifications == null || Notifications.Value <= 0 || string.IsNullOrEmpty(page)) { return false; } return done ? Pages(NotifyDonePages, DoneSet, ref _doneSrc).Contains(page) : Pages(NotifyFoundPages, FoundSet, ref _foundSrc).Contains(page); } internal static void SetPageNotify(string page, bool done, bool on) { ConfigEntry<string> val = (done ? NotifyDonePages : NotifyFoundPages); HashSet<string> hashSet = new HashSet<string>(done ? Pages(NotifyDonePages, DoneSet, ref _doneSrc) : Pages(NotifyFoundPages, FoundSet, ref _foundSrc), StringComparer.Ordinal); if (on) { hashSet.Add(page); } else { hashSet.Remove(page); } List<string> list = new List<string>(); for (int i = 0; i < NotifyPages.Length; i++) { if (hashSet.Contains(NotifyPages[i])) { list.Add(NotifyPages[i]); } } val.Value = string.Join(",", list.ToArray()); } internal static void SetAllNotify(bool on) { string value = (on ? string.Join(",", NotifyPages) : ""); NotifyFoundPages.Value = value; NotifyDonePages.Value = value; } internal static int SpoilerOf(Entry e) { ConfigEntry<int> val; switch (e.Page) { case "bosses": val = SpoilBosses; break; case "creatures": val = SpoilCreatures; break; case "biomes": case "places": case "events": case "traders": case "nature": val = SpoilWorld; break; default: val = SpoilItems; break; } return (val != null && val.Value >= 0) ? val.Value : Spoilers.Value; } internal static int SpoilerSum() { return SpoilBosses.Value * 7 + SpoilCreatures.Value * 13 + SpoilWorld.Value * 29 + SpoilItems.Value * 53; } internal static void Initialize(ConfigFile c) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Expected O, but got Unknown //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Expected O, but got Unknown PanelKey = c.Bind<KeyCode>("1 - Panel", "PanelKey", (KeyCode)285, "Key that opens and closes the panel (console command: discoveries_config)."); Scale = c.Bind<float>("1 - Panel", "PanelScale", 1f, new ConfigDescription("Size of the panel.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.7f, 1.6f), Array.Empty<object>())); GridView = c.Bind<bool>("1 - Panel", "GridView", true, "Cards in a grid (on) or a compact list (off)."); ShowMods = c.Bind<bool>("1 - Panel", "ShowModContent", true, "Also list the content added by other mods (items, creatures, pieces, places...)."); Sound = c.Bind<bool>("1 - Panel", "NotificationSound", true, "Plays a short sound for completed cards and finished collections."); TrackWorld = c.Bind<bool>("4 - Performance", "TrackWorldExploration", true, "Records biomes, places and merchants per world and measures the explored map (page World exploration). Turn off if you want the lightest possible mod."); ScanModNames = c.Bind<bool>("4 - Performance", "DetectModOfOrigin", true, "Finds which mod added each card when the compendium is built (small one-time cost). Off lists all other-mod content as 'Other mods'."); Spoilers = c.Bind<int>("2 - Discoveries", "SpoilerMode", 0, new ConfigDescription("0 = no spoilers (undiscovered content is shown as ?), 1 = hints (a little information about what is missing), 2 = reveal everything. Looking at a card never counts as discovering it.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>())); ConfigDescription val = new ConfigDescription("-1 = follow SpoilerMode, 0 = no spoilers, 1 = hints, 2 = reveal everything, only for this kind of content.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-1, 2), Array.Empty<object>()); SpoilBosses = c.Bind<int>("2 - Discoveries", "SpoilerBosses", -1, val); SpoilCreatures = c.Bind<int>("2 - Discoveries", "SpoilerCreatures", -1, val); SpoilWorld = c.Bind<int>("2 - Discoveries", "SpoilerWorld", -1, val); SpoilItems = c.Bind<int>("2 - Discoveries", "SpoilerItems", -1, val); Notifications = c.Bind<int>("2 - Discoveries", "Notifications", 1, new ConfigDescription("0 = no notifications at all, 1 (or 2) = on: what is announced is chosen per category below (also on the Notifications page of the panel).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2), Array.Empty<object>())); NotifyFoundPages = c.Bind<string>("2 - Discoveries", "NotifyDiscovered", "biomes,creatures,bosses,places,events,traders", "Categories whose new discoveries are announced (comma separated): " + string.Join(",", NotifyPages) + "."); NotifyDonePages = c.Bind<string>("2 - Discoveries", "NotifyCompleted", "biomes,creatures,bosses,places,events,traders", "Categories whose completed cards (every step done) are announced (comma separated), same names as above."); NotifyGoals = c.Bind<bool>("2 - Discoveries", "NotifyGoals", true, "Cards marked as goals announce every step and their completion, whatever their category."); NotifyReady = c.Bind<bool>("2 - Discoveries", "NotifyCompendiumReady", true, "Shows the small message in the bottom right corner while the compendium is prepared and when it is ready."); AllowAchievements = c.Bind<bool>("3 - Steam achievements", "AllowAchievements", false, "Lets you earn Steam achievements even though the game is modded or the world has modifiers (cheats you used still block them). Ignored when Ultimate QOL is installed: its own option is used instead. Off keeps vanilla behavior."); AllowWithCheats = c.Bind<bool>("3 - Steam achievements", "AchievementsEvenWithCheats", false, "Also lets you earn achievements after using cheats (same as the game command yesiuseddevcommandsbutiwantmyachievementsanyway). Only used while the option above is on. Ignored when Ultimate QOL is installed. Off keeps vanilla behavior."); } } internal static class Translation { internal static readonly string[] Languages = new string[15] { "English", "German", "French", "Spanish", "Italian", "Japanese", "Korean", "Chinese", "Polish", "Portuguese_Brazilian", "Portuguese_European", "Russian", "Turkish", "Dutch", "Swedish" }; private static readonly Dictionary<string, string> EnglishTexts = new Dictionary<string, string>(StringComparer.Ordinal); private static readonly Dictionary<string, string> Active = new Dictionary<string, string>(StringComparer.Ordinal); private static string _folder; private static string _loaded; internal static int Version; internal static void Initialize(string pluginFolder) { _folder = Path.Combine(pluginFolder ?? "", "Translations"); if (!File.Exists(Path.Combine(_folder, "English.json"))) { Plugin.Warn("The Translations folder was not found next to the DLL (" + _folder + "): the English texts built into the DLL are used."); } Load(ReadLanguage("English"), EnglishTexts); _loaded = null; Refresh(); } internal static void Refresh() { string text = Normalize(GameAccess.SelectedLanguage()); if (!(text == _loaded)) { _loaded = text; Active.Clear(); if (!string.Equals(text, "English", StringComparison.Ordinal)) { Load(ReadLanguage(text), Active); } Version++; } } internal static bool Has(string key) { return key != null && (Active.ContainsKey(key) || EnglishTexts.ContainsKey(key)); } internal static string T(string key) { if (key == null) { return ""; } if (!Active.TryGetValue(key, out var value) && !EnglishTexts.TryGetValue(key, out value)) { return key; } return value; } internal static string T(string key, params object[] args) { string text = T(key); try { return string.Format(CultureInfo.InvariantCulture, text, args); } catch { return text; } } private static string Normalize(string language) { if (string.IsNullOrEmpty(language)) { return "English"; } string text = language.Trim(); if (text.IndexOf("Portuguese", StringComparison.OrdinalIgnoreCase) >= 0) { return (text.IndexOf("Brazil", StringComparison.OrdinalIgnoreCase) >= 0) ? "Portuguese_Brazilian" : "Portuguese_European"; } if (text.IndexOf("Chinese", StringComparison.OrdinalIgnoreCase) >= 0) { return "Chinese"; } for (int i = 0; i < Languages.Length; i++) { if (string.Equals(Languages[i], text, StringComparison.OrdinalIgnoreCase)) { return Languages[i]; } } return "English"; } private static string ReadLanguage(string lang) { string text = ReadFile(Path.Combine(_folder ?? "", lang + ".json")); if (!string.IsNullOrEmpty(text)) { return text; } try { using Stream stream = typeof(Translation).Assembly.GetManifestResourceStream("SteamAD.Translations." + lang + ".json"); if (stream == null) { return null; } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); return streamReader.ReadToEnd(); } catch (Exception ex) { Plugin.Warn("Built-in translation " + lang + " could not be read: " + ex.Message); return null; } } private static string ReadFile(string path) { try { return File.Exists(path) ? File.ReadAllText(path, Encoding.UTF8) : null; } catch (Exception ex) { Plugin.Warn("Could not read " + path + ": " + ex.Message); return null; } } private static void Load(string json, Dictionary<string, string> into) { if (string.IsNullOrEmpty(json)) { return; } int i = 0; try { Skip(json, ref i); if (i >= json.Length || json[i] != '{') { return; } i++; while (true) { Skip(json, ref i); if (i >= json.Length || json[i] == '}') { break; } if (json[i] == ',') { i++; continue; } string text = ReadString(json, ref i); Skip(json, ref i); if (i >= json.Length || json[i] != ':') { break; } i++; Skip(json, ref i); string text2 = ReadString(json, ref i); if (text != null && text2 != null) { into[text] = text2; } } } catch (Exception ex) { Plugin.Warn("Invalid translation file: " + ex.Message); } } private static void Skip(string s, ref int i) { while (i < s.Length && (char.IsWhiteSpace(s[i]) || s[i] == '\ufeff')) { i++; } } private static string ReadString(string s, ref int i) { if (i >= s.Length || s[i] != '"') { throw new FormatException("string expected at " + i); } i++; StringBuilder stringBuilder = new StringBuilder(); while (i < s.Length) { char c = s[i++]; switch (c) { case '"': return stringBuilder.ToString(); default: stringBuilder.Append(c); continue; case '\\': break; } if (i >= s.Length) { break; } char c2 = s[i++]; switch (c2) { case 'n': stringBuilder.Append('\n'); break; case 't': stringBuilder.Append('\t'); break; case 'r': stringBuilder.Append('\r'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'u': if (i + 4 <= s.Length) { stringBuilder.Append((char)int.Parse(s.Substring(i, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture)); i += 4; } break; default: stringBuilder.Append(c2); break; } } throw new FormatException("unterminated string"); } } internal static class RF { private static readonly Dictionary<string, FieldInfo> Cache = new Dictionary<string, FieldInfo>(StringComparer.Ordinal); internal static FieldInfo Field(Type t, string name) { if (t == null) { return null; } string key = t.FullName + "." + name; if (!Cache.TryGetValue(key, out var value)) { try { value = AccessTools.Field(t, name); } catch { value = null; } Cache[key] = value; } return value; } internal static object Obj(object o, string name) { if (o == null) { return null; } FieldInfo fieldInfo = Field(o.GetType(), name); try { return (fieldInfo != null) ? fieldInfo.GetValue(o) : null; } catch { return null; } } internal static object Static(Type t, string name) { FieldInfo fieldInfo = Field(t, name); try { return (fieldInfo != null) ? fieldInfo.GetValue(null) : null; } catch { return null; } } internal static T Get<T>(object o, string name, T fallback) { object obj = Obj(o, name); return (obj is T) ? ((T)obj) : fallback; } } internal static class GameAccess { private static bool _locResolved; private static PropertyInfo _locInstance; private static MethodInfo _locLocalize; private static MethodInfo _locSelected; internal static bool KeyDown(KeyCode key) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_000f: Unknown result type (might be due to invalid IL or missing references) if ((int)key == 0) { return false; } try { return ZInput.GetKeyDown(key, false); } catch { return false; } } internal static bool KeyHeld(KeyCode key) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_000f: Unknown result type (might be due to invalid IL or missing references) if ((int)key == 0) { return false; } try { return ZInput.GetKey(key, false); } catch { return false; } } internal static bool KeyboardBusy() { try { if (Console.IsVisible()) { return true; } if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) { return true; } if (TextInput.IsVisible()) { return true; } if (Menu.IsVisible()) { return true; } if (Minimap.InTextInput()) { return true; } if (StoreGui.IsVisible()) { return true; } } catch { } return false; } private static void ResolveLocalization() { if (_locResolved) { return; } _locResolved = true; try { Type type = AccessTools.TypeByName("Localization"); if (!(type == null)) { _locInstance = type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _locLocalize = type.GetMethod("Localize", BindingFlags.Instance | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); _locSelected = type.GetMethod("GetSelectedLanguage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null); } } catch { } } internal static string Localize(string token) { if (string.IsNullOrEmpty(token) || token.IndexOf('$') < 0) { return token ?? ""; } ResolveLocalization(); try { object obj = ((_locInstance != null) ? _locInstance.GetValue(null, null) : null); if (obj != null && _locLocalize != null) { return (string)_locLocalize.Invoke(obj, new object[1] { token }); } } catch { } return token; } internal static string Name(string token, string fallback) { if (string.IsNullOrEmpty(token)) { return fallback ?? ""; } if (token.IndexOf('$') < 0) { return token; } string text = Localize(token); if (string.IsNullOrEmpty(text) || text[0] == '[' || text[0] == '$') { return fallback ?? token; } return text; } internal static string SelectedLanguage() { ResolveLocalization(); try { object obj = ((_locInstance != null) ? _locInstance.GetValue(null, null) : null); if (obj != null && _locSelected != null) { string text = _locSelected.Invoke(obj, null) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } catch { } try { return PlayerPrefs.GetString("language", "English"); } catch { return "English"; } } internal static string Humanize(string raw) { if (string.IsNullOrEmpty(raw)) { return ""; } string input = raw.Replace('_', ' '); input = Regex.Replace(input, "(?<=[a-z])(?=[A-Z])", " "); input = Regex.Replace(input, "(?<=[A-Za-z])(?=[0-9])", " "); input = Regex.Replace(input, "\\s+", " ").Trim(); if (input.Length > 0 && char.IsLower(input[0])) { input = char.ToUpperInvariant(input[0]) + input.Substring(1); } return input; } internal static Sprite FirstIcon(SharedData shared) { if (shared == null || shared.m_icons == null || shared.m_icons.Length == 0) { return null; } return shared.m_icons[0]; } internal static Sprite ItemIcon(string prefab) { try { if ((Object)(object)ObjectDB.instance == (Object)null) { return null; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(prefab); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null); return ((Object)(object)val != (Object)null && val.m_itemData != null) ? FirstIcon(val.m_itemData.m_shared) : null; } catch { return null; } } internal static string ItemName(string prefab) { try { GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefab) : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); if ((Object)(object)val2 != (Object)null && val2.m_itemData != null) { return Name(val2.m_itemData.m_shared.m_name, Humanize(prefab)); } } catch { } return Humanize(prefab); } internal static string PieceName(string prefab) { try { GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null); Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null); if ((Object)(object)val2 != (Object)null) { return Name(val2.m_name, Humanize(prefab)); } } catch { } return Humanize(prefab); } internal static void Center(string text) { try { if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, false); } } catch { } } } internal static class UqolBridge { private static bool _resolved; private static float _nextScan; private static Assembly _asm; private static FieldInfo _allow; private static FieldInfo _anyway; private static PropertyInfo _effective; private static PropertyInfo _canEdit; private static MethodInfo _request; internal static bool Installed { get { Resolve(); return _asm != null; } } internal static bool HasAchievementOptions { get { Resolve(); return _allow != null && _anyway != null && _request != null; } } internal static bool Allowed { get { Resolve(); return Read(_allow); } } internal static bool Anyway { get { Resolve(); return Read(_anyway); } } internal static bool CanEdit { get { Resolve(); try { return _canEdit != null && (bool)_canEdit.GetValue(null, null); } catch { return false; } } } private static void Resolve() { if (_resolved && (_asm != null || (Object)(object)Player.m_localPlayer != (Object)null)) { return; } if (_asm == null && Time.unscaledTime >= _nextScan) { _nextScan = Time.unscaledTime + 2f; try { if (Chainloader.PluginInfos.TryGetValue("Igao.UltimateQOL", out var value) && value != null && (Object)(object)value.Instance != (Object)null) { _asm = ((object)value.Instance).GetType().Assembly; } } catch { } } if (_asm == null) { if ((Object)(object)Player.m_localPlayer != (Object)null) { _resolved = true; } return; } _resolved = true; try { Type type = _asm.GetType("UltimateQOL.Cfg", throwOnError: false); Type type2 = _asm.GetType("UltimateQOL.Setting", throwOnError: false); Type type3 = _asm.GetType("UltimateQOL.ConfigSync", throwOnError: false); if (type != null) { _allow = type.GetField("AchievementsAllowed", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); _anyway = type.GetField("AchievementsAnyway", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } if (type2 != null) { _effective = type2.GetProperty("Effective", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } if (type3 != null) { _canEdit = type3.GetProperty("LocalCanEdit", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (type2 != null) { _request = type3.GetMethod("RequestChange", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { type2, typeof(object) }, null); } } } catch (Exception ex) { Plugin.Warn("Ultimate QOL link: " + ex.Message); } } private static bool Read(FieldInfo field) { try { object obj = ((field != null) ? field.GetValue(null) : null); if (obj == null || _effective == null) { return false; } object value = _effective.GetValue(obj, null); return value is bool && (bool)value; } catch { return false; } } internal static int Set(bool anyway, bool value) { Resolve(); try { FieldInfo fieldInfo = (anyway ? _anyway : _allow); object obj = ((fieldInfo != null) ? fieldInfo.GetValue(null) : null); if (obj == null || _request == null) { return -1; } object obj2 = _request.Invoke(null, new object[2] { obj, value }); return (obj2 is int) ? ((int)obj2) : (-1); } catch (Exception ex) { Plugin.Warn("Ultimate QOL option change failed: " + ex.Message); return -1; } } } [Flags] internal enum St { None = 0, Seen = 1, Defeated = 2, Trophy = 4, Tamed = 8, Bred = 0x10, Ridden = 0x20, Obtained = 0x40, Crafted = 0x80, Consumed = 0x100, Learned = 0x200, Ingredients = 0x400, Built = 0x800, Picked = 0x1000, Entered = 0x2000, Met = 0x4000, Unlocked = 0x8000, Vegvisir = 0x10000, Altar = 0x20000, Summoned = 0x40000, PowerUsed = 0x80000, Maxed = 0x100000 } internal enum Kind { Biome, Creature, Place, Event, Trader, Item, Recipe, Piece, Nature } internal enum EntryState { Unknown, Discovered, Complete } internal sealed class Entry { internal int Index; internal Kind Kind; internal string Key = ""; internal string Page = ""; internal string Sub = ""; internal string Token = ""; internal string Prefab = ""; internal string[] Prefabs; internal string[] ResTokens; internal int[] ResAmounts; internal string[] TrophyPrefabs; internal string[] TrophyTokens; internal bool Mod; internal string ModName = ""; internal Biome Biome; internal St Applicable; internal St Gate; internal St Done; internal float Count; internal Color Tint = new Color(0f, 0f, 0f, 0f); internal bool Boss; internal bool Emblem; internal Func<Sprite> IconFn; internal Sprite Icon; internal bool IconTried; internal string Name; internal int NameVersion = -1; internal string SubName; internal int SubVersion = -1; internal string SortName; private string _starKey; internal string StarKey => _starKey ?? (_starKey = "star." + Key); internal EntryState State { get { if ((Done & Gate) == 0) { return EntryState.Unknown; } return ((Done & Applicable) != Applicable) ? EntryState.Discovered : EntryState.Complete; } } internal int StepsTotal => Bits(Applicable); internal int StepsDone => Bits(Done & Applicable); internal static int Bits(St s) { int num = (int)s; int num2 = 0; while (num != 0) { num &= num - 1; num2++; } return num2; } } internal sealed class StrSet { internal static int Changes; internal readonly string Key; internal readonly HashSet<string> Set = new HashSet<string>(StringComparer.Ordinal); internal bool Dirty; internal int Count => Set.Count; internal StrSet(string key) { Key = key; } private static string Clean(string s) { return (s.IndexOf(';') < 0) ? s : s.Replace(';', ','); } internal bool Contains(string s) { return !string.IsNullOrEmpty(s) && Set.Contains(Clean(s)); } internal bool Add(string s) { if (string.IsNullOrEmpty(s)) { return false; } if (!Set.Add(Clean(s))) { return false; } Dirty = true; Changes++; return true; } internal bool Remove(string s) { if (string.IsNullOrEmpty(s) || !Set.Remove(Clean(s))) { return false; } Dirty = true; Changes++; return true; } internal void Load(Dictionary<string, string> data) { Set.Clear(); Dirty = false; Changes++; if (data == null || !data.TryGetValue(Key, out var value) || string.IsNullOrEmpty(value)) { return; } string[] array = value.Split(new char[1] { ';' }); for (int i = 0; i < array.Length; i++) { if (array[i].Length > 0) { Set.Add(array[i]); } } } internal void Save(Dictionary<string, string> data) { if (Dirty && data != null) { Dirty = false; data[Key] = string.Join(";", Set); } } } internal sealed class NumMap { internal readonly string Key; internal readonly Dictionary<string, double> Map = new Dictionary<string, double>(StringComparer.Ordinal); internal bool Dirty; internal NumMap(string key) { Key = key; } private static string Clean(string s) { return (s.IndexOf(';') < 0 && s.IndexOf('=') < 0) ? s : s.Replace(';', ',').Replace('=', '-'); } internal double Get(string k) { double value; return (k != null && Map.TryGetValue(Clean(k), out value)) ? value : 0.0; } internal bool Has(string k) { return k != null && Map.ContainsKey(Clean(k)); } internal void Set(string k, double v) { if (!string.IsNullOrEmpty(k)) { k = Clean(k); if (!Map.TryGetValue(k, out var value) || value != v) { Map[k] = v; Dirty = true; } } } internal void Add(string k, double delta) { Set(k, Get(k) + delta); } internal void Load(Dictionary<string, string> data) { Map.Clear(); Dirty = false; if (data == null || !data.TryGetValue(Key, out var value) || string.IsNullOrEmpty(value)) { return; } string[] array = value.Split(new char[1] { ';' }); for (int i = 0; i < array.Length; i++) { int num = array[i].LastIndexOf('='); if (num > 0 && double.TryParse(array[i].Substring(num + 1), NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { Map[array[i].Substring(0, num)] = result; } } } internal void Save(Dictionary<string, string> data) { if (!Dirty || data == null) { return; } Dirty = false; StringBuilder stringBuilder = new StringBuilder(); bool flag = true; foreach (KeyValuePair<string, double> item in Map) { if (!flag) { stringBuilder.Append(';'); } flag = false; stringBuilder.Append(item.Key).Append('=').Append(item.Value.ToString("R", CultureInfo.InvariantCulture)); } data[Key] = stringBuilder.ToString(); } } internal struct JournalItem { internal long Ticks; internal St Stage; internal string Key; } internal static class Store { internal static readonly StrSet Seen = new StrSet("SAD.seen"); internal static readonly StrSet Tamed = new StrSet("SAD.tamed"); internal static readonly StrSet Bred = new StrSet("SAD.bred"); internal static readonly StrSet Ridden = new StrSet("SAD.ridden"); internal static readonly StrSet Places = new StrSet("SAD.places"); internal static readonly StrSet Dungeons = new StrSet("SAD.dungeons"); internal static readonly StrSet Entered = new StrSet("SAD.entered"); internal static readonly StrSet Events = new StrSet("SAD.events"); internal static readonly StrSet Traders = new StrSet("SAD.traders"); internal static readonly StrSet Fav = new StrSet("SAD.fav"); internal static readonly StrSet Goals = new StrSet("SAD.goals"); internal static readonly StrSet Revealed = new StrSet("SAD.revealed"); internal static readonly NumMap Nums = new NumMap("SAD.num"); private static readonly NumMap First = new NumMap("SAD.first"); internal static readonly StrSet CatSeen = new StrSet("SAD.cat"); internal static readonly StrSet Maxed = new StrSet("SAD.maxed"); internal static readonly StrSet Vegvisir = new StrSet("SAD.vegv"); internal static readonly StrSet Altars = new StrSet("SAD.altar"); internal static readonly StrSet Summoned = new StrSet("SAD.summoned"); internal static readonly HashSet<string> NewKeys = new HashSet<string>(StringComparer.Ordinal); internal static int NewSinceLast; internal static StrSet WorldPlaces = new StrSet("SAD.wp.0"); internal static readonly List<JournalItem> Journal = new List<JournalItem>(); private const int JournalMax = 120; private const string JournalKey = "SAD.diary"; private static readonly StrSet[] Sets = new StrSet[17] { Seen, Tamed, Bred, Ridden, Places, Dungeons, Entered, Events, Traders, Fav, Goals, Revealed, CatSeen, Vegvisir, Altars, Summoned, Maxed }; private static Player _player; private static ZNet _znet; private static long _world = long.MinValue; private static bool _journalDirty; private static float _nextFlush; internal static bool Ready => (Object)(object)_player != (Object)null; internal static void Sync(Player p) { if ((Object)(object)p == (Object)null) { return; } Dictionary<string, string> customData = p.m_customData; if (customData == null) { return; } if (p != _player) { Flush(force: true); _player = p; for (int i = 0; i < Sets.Length; i++) { Sets[i].Load(customData); } Nums.Load(customData); First.Load(customData); LoadJournal(customData); _world = long.MinValue; _znet = null; } ZNet instance = ZNet.instance; if (instance == _znet && _world != long.MinValue) { return; } _znet = instance; long num = 0L; try { num = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetWorldUID() : 0); } catch { } if (num == 0) { try { string text = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetWorldName() : null); if (!string.IsNullOrEmpty(text)) { num = (long)Convert.ToUInt32(ShortHash(text), 16) + 1L; } } catch { } } if (num == 0) { _znet = null; } if (num != _world) { if (WorldPlaces.Dirty && (Object)(object)_player != (Object)null) { WorldPlaces.Save(customData); } _world = num; WorldPlaces = new StrSet("SAD.wp." + num.ToString(CultureInfo.InvariantCulture)); WorldPlaces.Load(customData); } } internal static void Reset() { Flush(force: true); _player = null; } internal static void BeforeSave(Player p) { if ((Object)(object)p != (Object)null && p == _player) { Flush(force: true); } } internal static void Flush(bool force) { if ((Object)(object)_player == (Object)null) { return; } float unscaledTime = Time.unscaledTime; if (!force && unscaledTime < _nextFlush) { return; } _nextFlush = unscaledTime + 5f; try { Dictionary<string, string> customData = _player.m_customData; if (customData == null) { return; } for (int i = 0; i < Sets.Length; i++) { Sets[i].Save(customData); } Nums.Save(customData); First.Save(customData); WorldPlaces.Save(customData); if (!_journalDirty) { return; } _journalDirty = false; StringBuilder stringBuilder = new StringBuilder(); for (int j = 0; j < Journal.Count; j++) { if (j > 0) { stringBuilder.Append(';'); } StringBuilder stringBuilder2 = stringBuilder.Append(Journal[j].Ticks.ToString(CultureInfo.InvariantCulture)).Append('|'); int stage = (int)Journal[j].Stage; stringBuilder2.Append(stage.ToString(CultureInfo.InvariantCulture)).Append('|').Append(Journal[j].Key); } customData["SAD.diary"] = stringBuilder.ToString(); } catch (Exception ex) { Plugin.Warn("Could not store the discoveries: " + ex.Message); } } internal static double NowMinutes() { return (DateTime.UtcNow - new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMinutes; } internal static double FirstOf(string key) { return string.IsNullOrEmpty(key) ? 0.0 : First.Get(ShortHash(key)); } internal static void SetFirst(string key) { if (!string.IsNullOrEmpty(key)) { string k = ShortHash(key); if (!First.Has(k)) { First.Set(k, Math.Floor(NowMinutes())); } } } internal static string ShortHash(string s) { uint num = 2166136261u; for (int i = 0; i < s.Length; i++) { num ^= s[i]; num *= 16777619; } return num.ToString("x"); } internal static void MarkNewCards(List<Entry> all) { NewKeys.Clear(); NewSinceLast = 0; if ((Object)(object)_player == (Object)null) { return; } bool flag = CatSeen.Count == 0; for (int i = 0; i < all.Count; i++) { string s = ShortHash(all[i].Key); if (!CatSeen.Contains(s)) { if (!flag) { NewKeys.Add(all[i].Key); } CatSeen.Add(s); } } NewSinceLast = NewKeys.Count; } internal static void AddJournal(string key, St stage) { if (!string.IsNullOrEmpty(key)) { JournalItem item = default(JournalItem); item.Ticks = DateTime.UtcNow.Ticks; item.Stage = stage; item.Key = ((key.IndexOf(';') < 0 && key.IndexOf('|') < 0) ? key : key.Replace(';', ',').Replace('|', '/')); Journal.Add(item); if (Journal.Count > 120) { Journal.RemoveRange(0, Journal.Count - 120); } _journalDirty = true; } } private static void LoadJournal(Dictionary<string, string> data) { Journal.Clear(); _journalDirty = false; if (!data.TryGetValue("SAD.diary", out var value) || string.IsNullOrEmpty(value)) { return; } string[] array = value.Split(new char[1] { ';' }); JournalItem item = default(JournalItem); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '|' }); if (array2.Length >= 3 && long.TryParse(array2[0], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && int.TryParse(array2[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { item.Ticks = result; item.Stage = (St)result2; item.Key = array2[2]; Journal.Add(item); } } } } internal static class PlayerView { internal static Player P; internal static HashSet<string> Trophies = new HashSet<string>(StringComparer.Ordinal); internal static readonly HashSet<string> Biomes = new HashSet<string>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> Kills = new Dictionary<string, float>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> Crafted = new Dictionary<string, float>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> Eaten = new Dictionary<string, float>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> Picked = new Dictionary<string, float>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> Placed = new Dictionary<string, float>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> Pickup = new Dictionary<string, float>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> KnownWorlds = new Dictionary<string, float>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> KnownKeys = new Dictionary<string, float>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> Commands = new Dictionary<string, float>(StringComparer.Ordinal); internal static readonly Dictionary<string, float> General = new Dictionary<string, float>(StringComparer.Ordinal); internal static HashSet<string> Materials = new HashSet<string>(StringComparer.Ordinal); internal static HashSet<string> Recipes = new HashSet<string>(StringComparer.Ordinal); private static readonly FieldInfo TrophyField = AccessTools.Field(typeof(Player), "m_trophies"); private static readonly FieldInfo MaterialField = AccessTools.Field(typeof(Player), "m_knownMaterial"); private static readonly FieldInfo RecipeField = AccessTools.Field(typeof(Player), "m_knownRecipes"); private static readonly FieldInfo BiomeField = AccessTools.Field(typeof(Player), "m_knownBiome"); internal static int Version; internal static void Refresh(Player p) { Version++; P = p; Trophies = ReadSet(TrophyField, p) ?? Trophies; Materials = ReadSet(MaterialField, p) ?? Materials; Recipes = ReadSet(RecipeField, p) ?? Recipes; RefreshBiomes(p); RefreshStats(); } private static HashSet<string> ReadSet(FieldInfo field, Player p) { try { return (field != null) ? (field.GetValue(p) as HashSet<string>) : null; } catch { return null; } } private static string Norm(string s) { if (string.IsNullOrEmpty(s)) { return ""; } char[] array = new char[s.Length]; int length = 0; foreach (char c in s) { if (c >= 'A' && c <= 'Z') { array[length++] = (char)(c + 32); } else if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) { array[length++] = c; } } return new string(array, 0, length); } private static void RefreshBiomes(Player p) { //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) //IL_0071: Unknown result type (might be due to invalid IL or missing references) Biomes.Clear(); try { IEnumerable enumerable = ((BiomeField != null) ? (BiomeField.GetValue(p) as IEnumerable) : null); if (enumerable != null) { foreach (object item in enumerable) { if (item != null) { string s = item.ToString(); if (int.TryParse(s, out var result)) { s = ((object)(Biome)result/*cast due to .constrained prefix*/).ToString(); } string text = Norm(s); if (text.Length > 0) { Biomes.Add(text); } } } } Biomes.Add(Norm(((object)p.GetCurrentBiome()/*cast due to .constrained prefix*/).ToString())); } catch { } } internal unsafe static bool KnowsBiome(Biome biome) { return Biomes.Contains(Norm(((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString())); } private static void RefreshStats() { //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) Kills.Clear(); Crafted.Clear(); Eaten.Clear(); Picked.Clear(); Placed.Clear(); Pickup.Clear(); KnownWorlds.Clear(); KnownKeys.Clear(); Commands.Clear(); General.Clear(); try { PlayerProfile o = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null); if (!(RF.Obj(o, "m_playerStats") is Array array)) { return; } for (int i = 0; i < array.Length; i++) { object value = array.GetValue(i); if (value == null) { continue; } Merge(Crafted, RF.Obj(value, "m_itemCraftStats") as Dictionary<string, float>); Merge(Eaten, RF.Obj(value, "m_foodEatenStats") as Dictionary<string, float>); Merge(Picked, RF.Obj(value, "m_pickableStats") as Dictionary<string, float>); Merge(Placed, RF.Obj(value, "m_piecesPlacedStats") as Dictionary<string, float>); Merge(Pickup, RF.Obj(value, "m_itemPickupStats") as Dictionary<string, float>); Merge(KnownWorlds, RF.Obj(value, "m_knownWorlds") as Dictionary<string, float>); Merge(KnownKeys, RF.Obj(value, "m_knownWorldKeys") as Dictionary<string, float>); Merge(Commands, RF.Obj(value, "m_knownCommands") as Dictionary<string, float>); if (RF.Obj(value, "m_stats") is Dictionary<PlayerStatType, float> dictionary) { foreach (KeyValuePair<PlayerStatType, float> item in dictionary) { string key = ((object)item.Key/*cast due to .constrained prefix*/).ToString(); if (!General.TryGetValue(key, out var value2) || item.Value > value2) { General[key] = item.Value; } } } if (RF.Obj(value, "m_enemyStats") is Dictionary<string, float>[] array2) { for (int j = 0; j < array2.Length; j++) { Merge(Kills, array2[j]); } } } } catch (Exception ex) { Plugin.Warn("Could not read the game statistics: " + ex.Message); } } private static void Merge(Dictionary<string, float> into, Dictionary<string, float> from) { if (from == null) { return; } foreach (KeyValuePair<string, float> item in from) { if (!into.TryGetValue(item.Key, out var value) || item.Value > value) { into[item.Key] = item.Value; } } } internal static float Value(Dictionary<string, float> d, string key) { float value; return (!string.IsNullOrEmpty(key) && d.TryGetValue(key, out value)) ? value : 0f; } internal static float Of(Dictionary<string, float> d, Entry e) { float num = Math.Max(Value(d, e.Prefab), Value(d, e.Token)); if (e.Prefabs != null) { for (int i = 0; i < e.Prefabs.Length; i++) { num = Math.Max(num, Value(d, e.Prefabs[i])); } } return num; } } internal static class Baseline { internal static readonly HashSet<string> Items = new HashSet<string>(StringComparer.Ordinal); internal static readonly HashSet<string> Prefabs = new HashSet<string>(StringComparer.Ordinal); internal static readonly HashSet<string> Locations = new HashSet<string>(StringComparer.Ordinal); internal static readonly HashSet<string> Events = new HashSet<string>(StringComparer.Ordinal); internal static void CaptureItems(ObjectDB db) { if ((Object)(object)db == (Object)null || db.m_items == null) { return; } for (int i = 0; i < db.m_items.Count; i++) { if ((Object)(object)db.m_items[i] != (Object)null) { Items.Add(((Object)db.m_items[i]).name); } } } internal static void CapturePrefabs(ZNetScene scene) { if ((Object)(object)scene == (Object)null || scene.m_prefabs == null) { return; } for (int i = 0; i < scene.m_prefabs.Count; i++) { if ((Object)(object)scene.m_prefabs[i] != (Object)null) { Prefabs.Add(((Object)scene.m_prefabs[i]).name); } } } internal static void CaptureLocations(ZoneSystem zone) { if ((Object)(object)zone == (Object)null || zone.m_locations == null) { return; } for (int i = 0; i < zone.m_locations.Count; i++) { if (zone.m_locations[i] != null) { Locations.Add(zone.m_locations[i].m_prefabName); } } } internal static void CaptureEvents(RandEventSystem system) { if ((Object)(object)system == (Object)null || system.m_events == null) { return; } for (int i = 0; i < system.m_events.Count; i++) { if (system.m_events[i] != null) { Events.Add(system.m_events[i].m_name); } } } internal static bool ModItem(string prefab) { return Items.Count > 0 && !Items.Contains(prefab); } internal static bool ModPrefab(string prefab) { return Prefabs.Count > 0 && !Prefabs.Contains(prefab); } internal static bool ModLocation(string prefab) { return Locations.Count > 0 && !Locations.Contains(prefab); } internal static bool ModEvent(string name) { return Events.Count > 0 && !Events.Contains(name); } } internal static class Catalog { internal static readonly List<Entry> All = new List<Entry>(); internal static readonly Dictionary<string, Entry> ByKey = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> CreatureByPrefab = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> CreatureByToken = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> ItemByPrefab = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> ItemByToken = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> PieceByPrefab = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> NatureByPrefab = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> PieceByToken = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> NatureByToken = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> PlaceByPrefab = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> EventByName = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> TraderByToken = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, Entry> BiomeByName = new Dictionary<string, Entry>(StringComparer.Ordinal); internal static readonly Dictionary<string, List<Entry>> ByPage = new Dictionary<string, List<Entry>>(StringComparer.Ordinal); internal static bool Ready; internal static bool Building; internal static float Progress; private static readonly Stopwatch Clock = new Stopwatch(); private const long BudgetMs = 3L; internal static int Version; private static ObjectDB _db; private static ZNetScene _scene; private static bool _spawnBiomes; internal static readonly List<string> ModList = new List<string>(); private static readonly Dictionary<string, Biome> BossBiomes = new Dictionary<string, Biome>(StringComparer.Ordinal) { { "Eikthyr", (Biome)1 }, { "gd_king", (Biome)8 }, { "Bonemass", (Biome)2 }, { "Dragon", (Biome)4 }, { "GoblinKing", (Biome)16 }, { "SeekerQueen", (Biome)512 }, { "Fader", (Biome)32 } }; internal static bool SpawnResolved => _spawnBiomes; internal static bool CurrentForWorld => Ready && _db == ObjectDB.instance && _scene == ZNetScene.instance; internal static bool CanBuild => !Building && (Object)(object)ObjectDB.instance != (Object)null && (Object)(object)ZNetScene.instance != (Object)null && (Object)(object)ZoneSystem.instance != (Object)null && ObjectDB.instance.m_items != null && ObjectDB.instance.m_items.Count > 0 && ZNetScene.instance.m_prefabs != null && ZNetScene.instance.m_prefabs.Count > 0; private static bool OverBudget() { if (!Clock.IsRunning) { Clock.Start(); return false; } if (Clock.ElapsedMilliseconds < 3) { return false; } Clock.Reset(); Clock.Start(); return true; } private static void Clear() { Ready = false; All.Clear(); ByKey.Clear(); CreatureByPrefab.Clear(); CreatureByToken.Clear(); ItemByPrefab.Clear(); ItemByToken.Clear(); PieceByPrefab.Clear(); NatureByPrefab.Clear(); PlaceByPrefab.Clear(); EventByName.Clear(); TraderByToken.Clear(); BiomeByName.Clear(); ByPage.Clear(); PieceByToken.Clear(); NatureByToken.Clear(); _spawnBiomes = false; } private static void Add(Entry e) { e.Index = All.Count; All.Add(e); ByKey[e.Key] = e; if (!ByPage.TryGetValue(e.Page, out var value)) { value = new List<Entry>(); ByPage[e.Page] = value; } value.Add(e); } internal static IEnumerator Build() { Building = true; Progress = 0f; Clock.Reset(); Clear(); _db = ObjectDB.instance; _scene = ZNetScene.instance; Entry dummy = null; try { BuildBiomes(); } catch (Exception ex) { Exception ex2 = ex; Plugin.Warn("Catalog (biomes): " + ex2.Message); } Progress = 0.05f; yield return null; List<GameObject> prefabs = new List<GameObject>(_scene.m_prefabs); int i = 0; while (i < prefabs.Count) { try { AddFromPrefab(prefabs[i]); } catch (Exception ex) { Plugin.Warn(string.Concat(str3: ex.Message, str0: "Catalog (prefab ", str1: ((Object)(object)prefabs[i] != (Object)null) ? ((Object)prefabs[i]).name : "?", str2: "): ")); } Progress = 0.05f + 0.35f * (float)i / (float)prefabs.Count; if (OverBudget()) { yield return null; } int num = i + 1; i = num; } yield return null; HashSet<string> craftable = new HashSet<string>(StringComparer.Ordinal); List<Recipe> recipes = new List<Recipe>(_db.m_recipes); int i2 = 0; while (i2 < recipes.Count) { Recipe r = recipes[i2]; if ((Object)(object)r != (Object)null && r.m_enabled && (Object)(object)r.m_item != (Object)null && r.m_item.m_itemData != null && r.m_item.m_itemData.m_shared != null) { craftable.Add(r.m_item.m_itemData.m_shared.m_name); } int num = i2 + 1; i2 = num; } List<GameObject> items = new List<GameObject>(_db.m_items); int i3 = 0; while (i3 < items.Count) { try { AddItem(items[i3], craftable); } catch (Exception ex) { Plugin.Warn(string.Concat(str3: ex.Message, str0: "Catalog (item ", str1: ((Object)(object)items[i3] != (Object)null) ? ((Object)items[i3]).name : "?", str2: "): ")); } Progress = 0.4f + 0.2f * (float)i3 / (float)items.Count; if (OverBudget()) { yield return null; } int num = i3 + 1; i3 = num; } yield return null; int i4 = 0; while (i4 < recipes.Count) { try { AddRecipe(recipes[i4]); } catch (Exception ex3) { Plugin.Warn("Catalog (recipe): " + ex3.Message); } Progress = 0.6f + 0.2f * (float)i4 / (float)recipes.Count; if (OverBudget()) { yield return null; } int num = i4 + 1; i4 = num; } yield return null; int i5 = 0; while (i5 < items.Count) { try { AddPiecesOf(items[i5]); } catch (Exception ex4) { Plugin.Warn("Catalog (pieces): " + ex4.Message); } Progress = 0.8f + 0.17f * (float)i5 / (float)items.Count; if (OverBudget()) { yield return null; } int num = i5 + 1; i5 = num; } yield return null; try { BuildPlaces(); } catch (Exception ex) { Exception ex5 = ex; Plugin.Warn("Catalog (places): " + ex5.Message); } try { BuildEvents(); } catch (Exception ex) { Exception ex6 = ex; Plugin.Warn("Catalog (events): " + ex6.Message); } yield return null; try { TryResolveSpawnBiomes(); } catch (Exception ex) { Exception ex7 = ex; Plugin.Warn("Catalog (creature biomes): " + ex7.Message); } if (dummy != null) { yield return null; } if (Cfg.ScanModNames.Value) { IEnumerator names = ResolveModNames(); while (names.MoveNext()) { yield return null; } } BuildModList(); Version++; Progress = 1f; Ready = true; Building = false; Plugin.Log("Catalog ready: " + All.Count + " cards (" + CountMods() + " from mods)."); } private static Dictionary<Assembly, string> PluginMap() { Dictionary<Assembly, string> dictionary = new Dictionary<Assembly, string>(); try { foreach (KeyValuePair<string, PluginInfo> pluginInfo in Chainloader.PluginInfos) { PluginInfo value = pluginInfo.Value; if (value != null && !((Object)(object)value.Instance == (Object)null) && value.Metadata != null && !string.Equals(value.Metadata.GUID, "com.jotunn.jotunn", StringComparison.OrdinalIgnoreCase) && !string.Equals(value.Metadata.GUID, "Igao.SteamAchievementsDiscoveries", StringComparison.OrdinalIgnoreCase)) { Assembly assembly = ((object)value.Instance).GetType().Assembly; if (!dictionary.ContainsKey(assembly)) { dictionary[assembly] = (value.Metadata.Name ?? value.Metadata.GUID).Replace('_', ' '); } } } } catch { } return dictionary; } private static IEnumerator ResolveModNames() { Dictionary<Assembly, string> map = PluginMap(); int n = 0; int i = 0; while (i < All.Count) { Entry e = All[i]; int num; if (e.Mod) { try { GameObject go = null; if (e.Kind == Kind.Item || e.Kind == Kind.Recipe) { go = (((Object)(object)_db != (Object)null) ? _db.GetItemPrefab(e.Prefab) : null); } else if (e.Kind == Kind.Creature || e.Kind == Kind.Trader || e.Kind == Kind.Nature || e.Kind == Kind.Piece) { go = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(e.Prefab) : null); } if ((Object)(object)go != (Object)null) { MonoBehaviour[] comps = go.GetComponentsInChildren<MonoBehaviour>(true); for (int c = 0; c < comps.Length; c = num) { if (!((Object)(object)comps[c] == (Object)null)) { if (map.TryGetValue(((object)comps[c]).GetType().Assembly, out var name)) { e.ModName = name; break; } name = null; } num = c + 1; } } } catch { } num = n + 1; n = num; if ((num & 0xF) == 0 && OverBudget()) { yield return null; } } num = i + 1; i = num; } } private static void BuildModList() { ModList.Clear(); HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); for (int i = 0; i < All.Count; i++) { if (All[i].Mod && hashSet.Add(All[i].ModName)) { ModList.Add(All[i].ModName); } } ModList.Sort(delegate(string a, string b) { if (a.Length == 0) { return (b.Length != 0) ? 1 : 0; } return (b.Length == 0) ? (-1) : string.Compare(a, b, StringComparison.CurrentCultureIgnoreCase); }); } internal static void Abort() { Building = false; } private static int CountMods() { int num = 0; for (int i = 0; i < All.Count; i++) { if (All[i].Mod) { num++; } } return num; } private static void BuildBiomes() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) foreach (Biome value in Enum.GetValues(typeof(Biome))) { if ((int)value == 0 || (int)value == 895 || (int)value == 639) { continue; } string text = ((object)value/*cast due to .constrained prefix*/).ToString(); Color32 biomeColor = Heightmap.GetBiomeColor(value); Entry entry = new Entry(); entry.Kind = Kind.Biome; entry.Key = "biome:" + text; entry.Page = "biomes"; entry.Token = "$biome_" + text.ToLowerInvariant(); entry.Prefab = text; entry.Biome = value; entry.Applicable = St.Seen; entry.Gate = St.Seen; entry.Tint = new Color((float)(int)biomeColor.r / 255f, (float)(int)biomeColor.g / 255f, (float)(int)biomeColor.b / 255f, 1f); entry.Emblem = true; if (Art.BiomeItem.TryGetValue(text, out var typical)) { entry.IconFn = () => GameAccess.ItemIcon(typical); } Add(entry); BiomeByName[text] = entry; } } private static void AddFromPrefab(GameObject go) { if ((Object)(object)go == (Object)null) { return; } Character component = go.GetComponent<Character>(); if ((Object)(object)component != (Object)null) { if (!((Object)(object)go.GetComponent<Player>() != (Object)null)) { Trader component2 = go.GetComponent<Trader>(); if ((Object)(object)component2 != (Object)null) { AddTrader(go, component2); } else { AddCreature(go, component); } } return; } Trader component3 = go.GetComponent<Trader>(); if ((Object)(object)component3 != (Object)null) { AddTrader(go, component3); return; } Pickable component4 = go.GetComponent<Pickable>(); if ((Object)(object)component4 != (Object)null) { AddNature(go, component4); } } private static void AddPrefab(Entry e, string prefab) { for (int i = 0; i < e.Prefabs.Length; i++) { if (e.Prefabs[i] == prefab) { return; } } string[] array = e.Prefabs; Array.Resize(ref array, array.Length + 1); array[^1] = prefab; e.Prefabs = array; } private static void AddCreature(GameObject go, Character c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Invalid comparison between Unknown and I4 //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Invalid comparison between Unknown and I4 //IL_0122: 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_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Invalid comparison between Unknown and I4 Faction faction = c.m_faction; if ((int)faction == 12 || (int)faction == 0 || (int)faction == 11) { return; } string name = c.m_name; if (string.IsNullOrEmpty(name)) { return; } string name2 = ((Object)go).name; if (CreatureByToken.TryGetValue(name, out var value)) { AddPrefab(value, name2); CreatureByPrefab[name2] = value; return; } bool boss = c.m_boss; value = new Entry(); value.Kind = Kind.Creature; value.Key = "creature:" + name; value.Page = (boss ? "bosses" : "creatures"); value.Sub = (boss ? "sub_bosses" : (((int)faction == 1) ? "sub_wildlife" : "sub_monsters")); value.Token = name; value.Prefab = name2; value.Prefabs = new string[1] { name2 }; value.Boss = boss; value.Mod = Baseline.ModPrefab(name2); value.Gate = St.Seen; value.Applicable = St.Seen | St.Defeated; if (BossBiomes.TryGetValue(name2, out var value2)) { value.Biome = value2; value.Applicable |= St.Altar | St.Summoned | St.PowerUsed; if (name2 != "Fader") { value.Applicable |= St.Vegvisir; } } List<string> list = new List<string>(); List<string> list2 = new List<string>(); CharacterDrop component = go.GetComponent<CharacterDrop>(); if (RF.Obj(component, "m_drops") is IList list3) { for (int i = 0; i < list3.Count; i++) { object obj = RF.Obj(list3[i], "m_prefab"); GameObject val = (GameObject)((obj is GameObject) ? obj : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null); if (!((Object)(object)val2 == (Object)null) && val2.m_itemData != null && val2.m_itemData.m_shared != null && (int)val2.m_itemData.m_shared.m_itemType == 13) { list.Add(((Object)val).name); list2.Add(val2.m_itemData.m_shared.m_name); } } } if (list.Count > 0) { value.TrophyPrefabs = list.ToArray(); value.TrophyTokens = list2.ToArray(); value.Applicable |= St.Trophy; string first = list[0]; value.IconFn = () => GameAccess.ItemIcon(first); } Tameable component2 = go.GetComponent<Tameable>(); if ((Object)(object)component2 != (Object)null) { value.Applicable |= St.Tamed; if (RF.Obj(component2, "m_saddleItem") != null) { value.Applicable |= St.Ridden; } } if ((Object)(object)go.GetComponent<Procreation>() != (Object)null) { value.Applicable |= St.Bred; } Add(value); CreatureByToken[name] = value; CreatureByPrefab[name2] = value; } private static void AddTrader(GameObject go, Trader trader) { //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) string name = trader.m_name; if (!string.IsNullOrEmpty(name) && !TraderByToken.ContainsKey(name)) { Entry entry = new Entry(); entry.Kind = Kind.Trader; entry.Key = "trader:" + name; entry.Page = "traders"; entry.Token = name; entry.Prefab = ((Object)go).name; entry.Mod = Baseline.ModPrefab(((Object)go).name); entry.Gate = St.Met; entry.Applicable = St.Met; entry.Emblem = true; entry.Tint = new Color(0.62f, 0.46f, 0.2f, 1f); string traderPrefab = ((Object)go).name; entry.IconFn = () => Art.TraderIcon(traderPrefab); Add(entry); TraderByToken[name] = entry; } } private static void AddNature(GameObject go, Pickable pick) { object obj = RF.Obj(pick, "m_itemPrefab"); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null) { return; } ItemDrop component = val.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { return; } string name = ((Object)go).name; if (!NatureByPrefab.ContainsKey(name)) { string text = RF.Get(pick, "m_overrideName", ""); Entry entry = new Entry(); entry.Kind = Kind.Nature; entry.Key = "nature:" + name; entry.Page = "nature"; entry.Token = (string.IsNullOrEmpty(text) ? component.m_itemData.m_shared.m_name : text); entry.Prefab = name; entry.Mod = Baseline.ModPrefab(name); entry.Gate = St.Picked; entry.Applicable = St.Picked; string itemPrefabName = ((Object)val).name; entry.IconFn = () => GameAccess.ItemIcon(itemPrefabName); Add(entry); NatureByPrefab[name] = entry; if (!NatureByToken.ContainsKey(entry.Token)) { NatureByToken[entry.Token] = entry; } } } private static void AddItem(GameObject go, HashSet<string> craftable) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Invalid comparison between Unknown and I4 //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Invalid comparison between Unknown and I4 if ((Object)(object)go == (Object)null) { return; } ItemDrop component = go.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null) { return; } SharedData shared = component.m_itemData.m_shared; if (shared == null || string.IsNullOrEmpty(shared.m_name)) { return; } ItemType itemType = shared.m_itemType; if ((int)itemType == 0 || (int)itemType == 10 || (Object)(object)GameAccess.FirstIcon(shared) == (Object)null) { return; } string name = ((Object)go).name; if (ItemByToken.TryGetValue(shared.m_name, out var value)) { ItemByPrefab[name] = value; return; } ItemPage(shared, out var page, out var sub); value = new Entry(); value.Kind = Kind.Item; value.Key = "item:" + name; value.Page = page; value.Sub = sub; value.Token = shared.m_name; value.Prefab = name; value.Mod = Baseline.ModItem(name); value.Gate = St.Obtained; value.Applicable = St.Obtained; if (craftable.Contains(shared.m_name) && (int)itemType != 13) { value.Applicable |= St.Crafted; } if (page == "equipment" && shared.m_maxQuality > 1) { value.Applicable |= St.Maxed; } if ((int)itemType == 2 && (shared.m_food > 0f || shared.m_foodStamina > 0f || shared.m_foodEitr > 0f)) { value.Applicable |= St.Consumed; } SharedData shared2 = shared; value.IconFn = () => GameAccess.FirstIcon(shared2); Add(value); ItemByToken[shared.m_name] = value; ItemByPrefab[name] = value; } private static void ItemPage(SharedData sh, out string page, out string sub) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected I4, but got Unknown ItemType itemType = sh.m_itemType; ItemType val = itemType; switch (val - 1) { case 12: page = "trophies"; sub = ""; break; case 20: page = "fish"; sub = ""; break; case 1: if (sh.m_food > 0f || sh.m_foodStamina > 0f || sh.m_foodEitr > 0f) { page = "food"; sub = "sub_food"; } else if ((Object)(object)sh.m_consumeStatusEffect != (Object)null) { page = "food"; sub = "sub_potions"; } else { page = "materials"; sub = "sub_misc"; } break; case 2: case 3: case 13: case 19: case 21: page = "equipment"; sub = "sub_weapons"; break; case 4: page = "equipment"; sub = "sub_shields"; break; case 5: case 6: case 10: case 11: case 16: page = "equipment"; sub = "sub_armor"; break; case 14: case 18: page = "equipment"; sub = "sub_tools"; break; case 17: case 23: page = "equipment"; sub = "sub_utility"; break; case 8: case 22: page = "equipment"; sub = "sub_ammo"; break; case 0: page = "materials"; sub = "sub_materials"; break; default: page = "materials"; sub = "sub_misc"; break; } } private static void AddRecipe(Recipe r) { if ((Object)(object)r == (Object)null || !r.m_enabled || (Object)(object)r.m_item == (Object)null || r.m_item.m_itemData == null) { return; } SharedData shared = r.m_item.m_itemData.m_shared; if (shared == null || string.IsNullOrEmpty(shared.m_name) || (Object)(object)GameAccess.FirstIcon(shared) == (Object)null) { return; } string name = ((Object)((Component)r.m_item).gameObject).name; string text = (string.IsNullOrEmpty(((Object)r).name) ? name : ((Object)r).name); if (ByKey.ContainsKey("recipe:" + text)) { return; } Entry entry = new Entry(); entry.Kind = Kind.Recipe; entry.Key = "recipe:" + text; entry.Page = "recipes"; entry.Sub = (((Object)(object)r.m_craftingStation != (Object)null && !string.IsNullOrEmpty(r.m_craftingStation.m_name)) ? r.m_craftingStation.m_name : "sub_hands"); entry.Token = shared.m_name; entry.Prefab = name; entry.Mod = Baseline.ModItem(name); entry.Gate = St.Learned; entry.Applicable = St.Crafted | St.Learned | St.Ingredients; List<string> list = new List<string>(); List<int> list2 = new List<int>(); if (r.m_resources != null) { for (int i = 0; i < r.m_resources.Length; i++) { Requirement val = r.m_resources[i]; if (val != null && !((Object)(object)val.m_resItem == (Object)null) && val.m_resItem.m_itemData != null && val.m_resItem.m_itemData.m_shared != null) { list.Add(val.m_resItem.m_itemData.m_shared.m_name); list2.Add(val.m_amount); } } } entry.ResTokens = list.ToArray(); entry.ResAmounts = list2.ToArray(); SharedData shared2 = shared; entry.IconFn = () => GameAccess.FirstIcon(shared2); Add(entry); } private static void AddPiecesOf(GameObject itemGo) { if ((Object)(object)itemGo == (Object)null) { return; } ItemDrop component = itemGo.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || component.m_itemData == null || component.m_itemData.m_shared == null) { return; } PieceTable buildPieces = component.m_itemData.m_shared.m_buildPieces; if ((Object)(object)buildPieces == (Object)null || buildPieces.m_pieces == null) { return; } for (int i = 0; i < buildPieces.m_pieces.Count; i++) { GameObject val = buildPieces.m_pieces[i]; Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null); if ((Object)(object)val2 == (Object)null || !val2.m_enabled || val2.m_repairPiece || val2.m_removePiece || string.IsNullOrEmpty(val2.m_name)) { continue; } string name = ((Object)val).name; if (!PieceByPrefab.ContainsKey(name)) { Entry entry = new Entry(); entry.Kind = Kind.Piece; entry.Key = "piece:" + name; entry.Page = "pieces"; entry.Sub = "pc_" + ((object)Unsafe.As<PieceCategory, PieceCategory>(ref val2.m_category)/*cast due to .constrained prefix*/).ToString().ToLowerInvariant(); entry.Token = val2.m_name; entry.Prefab = name; entry.Mod = Baseline.ModPrefab(name); entry.Gate = St.Unlocked; entry.Applicable = St.Built | St.Unlocked; Piece piece = val2; entry.IconFn = () => piece.m_icon; Add(entry); PieceByPrefab[name] = entry; if (!PieceByToken.ContainsKey(entry.Token)) { PieceByToken[entry.Token] = entry; } } } } private static void BuildPlaces() { //IL_00e5: 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_00f3: Unknown result type (might be due to invalid IL or missing references) ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null || instance.m_locations == null) { return; } for (int i = 0; i < instance.m_locations.Count; i++) { ZoneLocation val = instance.m_locations[i]; if (val == null || !val.m_enable || val.m_quantity <= 0 || string.IsNullOrEmpty(val.m_prefabName)) { continue; } string prefabName = val.m_prefabName; if (!PlaceByPrefab.ContainsKey(prefabName)) { Entry entry = new Entry(); entry.Kind = Kind.Place; entry.Key = "place:" + prefabName; entry.Page = "places"; entry.Token = "$location_" + prefabName.ToLowerInvariant(); entry.Prefab = prefabName; entry.Biome = val.m_biome; entry.Sub = BiomeSub(val.m_biome); entry.Mod = Baseline.ModLocation(prefabName); entry.Gate = St.Seen; entry.Applicable = St.Seen; Tint(entry); entry.Emblem = true; string locName = prefabName; entry.IconFn = () => Art.LocationIcon(locName) ?? Art.PinIcon((PinType)3); Add(entry); PlaceByPrefab[prefabName] = entry; } } } private static void BuildEvents() { //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) RandEventSystem instance = RandEventSystem.instance; if ((Object)(object)instance == (Object)null || instance.m_events == null) { return; } for (int i = 0; i < instance.m_events.Count; i++) { RandomEvent val = instance.m_events[i]; if (val == null || string.IsNullOrEmpty(val.m_name) || !val.m_enabled || val.m_devDisabled || EventByName.ContainsKey(val.m_name)) { continue; } Entry entry = new Entry(); entry.Kind = Kind.Event; entry.Key = "event:" + val.m_name; entry.Page = "events"; entry.Token = "$event_" + val.m_name.ToLowerInvariant(); entry.Prefab = val.m_name; entry.Biome = val.m_biome; entry.Sub = BiomeSub(val.m_biome); entry.Mod = Baseline.ModEvent(val.m_name); entry.Gate = St.Seen; entry.Applicable = St.Seen; Tint(entry); entry.Emblem = true; string trophy = null; try { if (val.m_spawn != null) { foreach (SpawnData item in val.m_spawn) { if (item == null || (Object)(object)item.m_prefab == (Object)null || !CreatureByPrefab.TryGetValue(((Object)item.m_prefab).name, out var value) || value.TrophyPrefabs == null || value.TrophyPrefabs.Length == 0) { continue; } trophy = value.TrophyPrefabs[0]; break; } } } catch { } if (trophy != null) { entry.IconFn = () => GameAccess.ItemIcon(trophy); } else { entry.IconFn = () => Art.PinIcon((PinType)11); } Add(entry); EventByName[val.m_name] = entry; } } private static string BiomeSub(Biome mask) { //IL_001f: U