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 Achievement Hints SeneaL UI v1.6.4
plugins/AchievementHintsSeneaLUI/SeneaLAchievementHints.dll
Decompiled 8 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Jotunn.Managers; using Jotunn.Utils; using Microsoft.CodeAnalysis; using SeneaL.API; using SeneaLUI.Components; using SeneaLUI.Inv; using SeneaLUI.Theme; using SoftReferenceableAssets; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("SeneaLAchievementHints")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.6.4.0")] [assembly: AssemblyInformationalVersion("1.6.4")] [assembly: AssemblyProduct("SeneaLAchievementHints")] [assembly: AssemblyTitle("SeneaLAchievementHints")] [assembly: AssemblyVersion("1.6.4.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; } } } namespace SeneaLAchievementHints { [BepInPlugin("local.senealachievementhints", "Achievement Hints SeneaL UI", "1.6.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "local.senealachievementhints"; public const string SeneaGuid = "seneaL.valheim.ui"; private FeatureSession features; public ConfigFile Settings { get; private set; } private void Awake() { Settings = ConfigStorage.Attach((BaseUnityPlugin)(object)this, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); ConfigEntry<bool> food = Settings.Bind<bool>("Display", "FoodHistory", true, "음식 툴팁과 제작 상세에 섭취 완료/미섭취를 표시합니다. / Show food history."); ConfigEntry<bool> craft = Settings.Bind<bool>("Display", "CraftingHistory", true, "아이템 툴팁과 제작 상세에 제작 완료/미제작을 표시합니다. / Show crafting history."); ConfigEntry<bool> enemies = Settings.Bind<bool>("Display", "UnkilledMonsters", true, "첫 처치 기록이 없는 야생 생물 이름 왼쪽 위에 별도 배지를 표시합니다. / A separate badge above-left of never-killed creature names."); ConfigEntry<HintLanguage> language = Settings.Bind<HintLanguage>("Display", "Language", HintLanguage.Auto, "Auto uses the game language; Korean or English can be selected."); ConfigEntry<bool> filters = Settings.Bind<bool>("Display", "RecipeHistoryFilters", true, "제작창 상단에 미제작/미섭취 필터를 추가합니다. / Recipe history filters."); ConfigEntry<bool> badges = Settings.Bind<bool>("Display", "UneatenFoodBadges", true, "미섭취 음식 슬롯 오른쪽 가운데에 노란 ! 표시. 기존 음식 아이콘과 희귀도 테두리는 유지합니다. / Uneaten food badges."); ConfigEntry<bool> details = Settings.Bind<bool>("Display", "AchievementDetails", true, "업적 클릭 시 이름·진행도·검색·지역 필터가 있는 상세창을 표시합니다. / Achievement detail browser."); ConfigEntry<bool> spoilers = Settings.Bind<bool>("Display", "HideUndiscoveredTargets", true, "업적의 미발견 대상 이름·아이콘·분류를 숨깁니다. 알고 있는 제작법이나 관련 행동 기록이 있으면 표시. 생물은 처치 기록 기준. / Hide undiscovered achievement targets; creatures use kill history."); ConfigEntry<bool> previews = Settings.Bind<bool>("Display", "CreaturePreviewSamples", true, "업적 상세에 캐시한 생물 사진을 표시합니다. 끄면 트로피/업적 아이콘을 표시합니다. / Cached creature portraits. Disable to use trophy or achievement icons."); ConfigEntry<PreviewQuality> quality = Settings.Bind<PreviewQuality>("Display", "CreaturePreviewQuality", PreviewQuality.Standard256, "생물 사진 해상도. Standard256: 기본 256px, Low128: 저사양용 128px. / Creature portrait resolution."); CreaturePreviews.Enabled = () => previews.Value; CreaturePreviews.Warning = delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }; CreaturePreviews.Resolution = () => (quality.Value != PreviewQuality.Low128) ? 256 : 128; CraftFilters.Enabled = () => filters.Value; FoodBadges.Enabled = () => badges.Value; AchievementDetails.Enabled = () => details.Value; SpoilerPolicy.Enabled = () => spoilers.Value; AchievementDetails.Timing = delegate(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); }; Progress.ShowFood = () => food.Value; Progress.ShowCraft = () => craft.Value; Progress.ShowMonsters = () => enemies.Value; Progress.Language = () => language.Value.ToString(); Progress.Warning = delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }; try { if (!Chainloader.PluginInfos.TryGetValue("seneaL.valheim.ui", out var value)) { throw new NotSupportedException("SeneaL UI dependency is unavailable."); } Assembly assembly = ((object)value.Instance).GetType().Assembly; bool flag = false; try { flag = Bindings.VerifyInputs(assembly.Location, typeof(Game).Assembly.Location); } catch (Exception error) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Build fingerprint unavailable: " + FeatureSession.Reason(error))); } if (!flag) { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Unverified Valheim / SeneaL UI build. Running independent structural checks; only passing features will be enabled. Structural checks do not establish in-game compatibility."); } features = new FeatureSession("local.senealachievementhints", delegate(string message) { ((BaseUnityPlugin)this).Logger.LogInfo((object)message); }, delegate(string message) { ((BaseUnityPlugin)this).Logger.LogWarning((object)message); }); features.Enable("tooltip", "Item tooltip history", delegate(Harmony h) { Bindings.InstallTooltip(h, assembly); }, TooltipHints.Reset); features.Enable("crafting", "Crafting detail history", delegate(Harmony h) { Bindings.InstallCrafting(h, assembly); }, CraftHints.Reset); features.Enable("monsters", "Unkilled creature badges", delegate(Harmony h) { RuntimeStructure.Check(typeof(MonsterBadge)); MonsterBadge.Configure(); MonsterHints.CreateBadge = MonsterBadge.Create; Bindings.InstallMonsters(h, assembly); }, ResetMonsters); features.Enable("recipefilters", "Recipe history filters", delegate(Harmony h) { ExtensionBindings.InstallFilters(h, assembly); }, CraftFilters.Reset); features.Enable("foodbadges", "Uneaten food badges", delegate(Harmony h) { ExtensionBindings.InstallFoodBadges(h); }, FoodBadges.Reset, FoodBadges.Tick); features.Enable("details", "Achievement detail browser", delegate(Harmony h) { ExtensionBindings.InstallDetails(h, assembly); }, AchievementDetails.Reset, AchievementDetails.Tick); if (features.Has("details") && Chainloader.PluginInfos.ContainsKey("com.jotunn.jotunn")) { features.Enable("portraits", "Creature portraits", delegate { PreviewRenderer.Configure(); if (!CreaturePreviews.BackendReady) { throw new NotSupportedException("Renderer contract unavailable; original icons retained."); } }, CreaturePreviews.Shutdown, delegate { CreaturePreviews.Tick(AchievementDetails.PreviewActive, Time.unscaledTime); }); } if (Chainloader.PluginInfos.ContainsKey("seneaL.valheim.api")) { features.Enable("settings", "SeneaL API settings", delegate { RuntimeStructure.Check(typeof(ApiSettings)); ApiSettings.Register(food, craft, enemies, language); ApiSettings.RegisterExtras(filters, badges, details); ApiSettings.RegisterPrivacy(spoilers); ApiSettings.RegisterPreview(previews, quality); }, ApiSettings.Remove); } ((BaseUnityPlugin)this).Logger.LogInfo((object)("Ready: " + features.Count + " independent features enabled; character action records remain read-only.")); } catch (Exception ex) { features?.Dispose(); features = null; Progress.Reset(); ((BaseUnityPlugin)this).Logger.LogWarning((object)("Achievement hints inactive; native SeneaL UI retained. " + ex)); } } private void Update() { features?.Tick(); } private void OnDestroy() { features?.Dispose(); features = null; Progress.Reset(); } private static void ResetMonsters() { MonsterHints.Reset(); MonsterBadge.Shutdown(); } } public static class ConfigStorage { public const string FileName = "senealachievementhints.cfg"; public static ConfigFile Attach(BaseUnityPlugin plugin, Action<string> warning) { ConfigFile config = plugin.Config; try { FieldInfo field = typeof(BaseUnityPlugin).GetField("<Config>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (field == null || field.FieldType != typeof(ConfigFile) || field.IsStatic) { throw new NotSupportedException("BepInEx plugin configuration storage is unavailable."); } ConfigFile val = Open(config, plugin.Info.Metadata, warning); if (val != config) { field.SetValue(plugin, val); } return plugin.Config; } catch (Exception ex) { warning?.Invoke("Cannot attach senealachievementhints.cfg; retaining the default plugin configuration. " + ex.Message); return config; } } public static ConfigFile Open(ConfigFile previous, BepInPlugin metadata, Action<string> warning) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown try { string text = Path.Combine(Path.GetDirectoryName(previous.ConfigFilePath), "senealachievementhints.cfg"); if (Directory.Exists(text)) { throw new IOException("The configuration path is a directory."); } if (!File.Exists(text) && File.Exists(previous.ConfigFilePath)) { try { File.Copy(previous.ConfigFilePath, text, overwrite: false); } catch (IOException) { if (!File.Exists(text)) { throw; } } } return new ConfigFile(text, false, metadata) { SaveOnConfigSet = previous.SaveOnConfigSet }; } catch (Exception ex2) when (ex2 is IOException || ex2 is UnauthorizedAccessException) { previous.SaveOnConfigSet = false; warning?.Invoke("Cannot open senealachievementhints.cfg; using the previous configuration without saving changes. " + ex2.Message); return previous; } } } public enum History { Hidden, Missing, Done } public struct ItemHistory : IEquatable<ItemHistory> { public string Name; public History Food; public History Craft; public bool Korean; public bool Visible { get { if (Food == History.Hidden) { return Craft != History.Hidden; } return true; } } public bool Equals(ItemHistory other) { if (Name == other.Name && Food == other.Food && Craft == other.Craft) { return Korean == other.Korean; } return false; } } public static class Progress { public static Func<bool> ShowFood = () => true; public static Func<bool> ShowCraft = () => true; public static Func<bool> ShowMonsters = () => true; public static Func<string> Language = () => "Auto"; public static Action<string> Warning = delegate { }; private static readonly HashSet<string> craftable = new HashSet<string>(StringComparer.Ordinal); private static ObjectDB database; private static Achievements achievements; private static int recipeCount = -1; private static float nextRefresh; public static bool Korean { get { string a = Language(); if (string.Equals(a, "Korean", StringComparison.OrdinalIgnoreCase)) { return true; } if (string.Equals(a, "English", StringComparison.OrdinalIgnoreCase)) { return false; } if (Localization.instance != null) { return Localization.instance.GetSelectedLanguage() == "Korean"; } return false; } } public static PlayerStats CurrentStats() { if (!Object.op_Implicit((Object)(object)Player.m_localPlayer) || !Object.op_Implicit((Object)(object)Game.instance)) { return null; } PlayerStats[] array = Game.instance.GetPlayerProfile()?.m_playerStats; if (array == null || array.Length == 0) { return null; } return array[0]; } private static History Read(Dictionary<string, float> values, string name) { if (values == null || string.IsNullOrEmpty(name)) { return History.Hidden; } if (!values.TryGetValue(name, out var value) || !(value > 0f)) { return History.Missing; } return History.Done; } public static ItemHistory Item(ItemData item, bool recipeOutput = false) { SharedData val = item?.m_shared; PlayerStats val2 = CurrentStats(); if (val == null || val2 == null || string.IsNullOrEmpty(val.m_name)) { return default(ItemHistory); } string name = val.m_name; ItemHistory result = new ItemHistory { Name = name, Korean = Korean }; if (ShowFood() && FoodEligibility.Tracks(item)) { result.Food = Read(val2.m_foodEatenStats, name); } if (ShowCraft() && (recipeOutput || Read(val2.m_itemCraftStats, name) == History.Done || IsCraftable(name))) { result.Craft = Read(val2.m_itemCraftStats, name); } return result; } public static bool Unkilled(Character character) { if (!ShowMonsters() || !Object.op_Implicit((Object)(object)character) || character.IsPlayer() || character.IsTamed() || character.IsDead()) { return false; } Dictionary<string, float>[] array = CurrentStats()?.m_enemyStats; if (array != null && array.Length != 0) { return Read(array[0], character.m_name) == History.Missing; } return false; } private static bool IsCraftable(string name) { //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) ObjectDB instance = ObjectDB.instance; Achievements instance2 = Achievements.m_instance; int num = (Object.op_Implicit((Object)(object)instance) ? (instance.m_recipes?.Count ?? 0) : 0); if (instance != database || instance2 != achievements || num != recipeCount || Time.unscaledTime >= nextRefresh) { database = instance; achievements = instance2; recipeCount = num; nextRefresh = Time.unscaledTime + 5f; craftable.Clear(); if (Object.op_Implicit((Object)(object)instance) && instance.m_recipes != null) { foreach (Recipe recipe in instance.m_recipes) { string text = ((!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item)) ? null : recipe.m_item.m_itemData?.m_shared?.m_name); if (!string.IsNullOrEmpty(text)) { craftable.Add(text); } } } if (Object.op_Implicit((Object)(object)instance2) && instance2.m_achievementLists != null) { foreach (AchievementList achievementList in instance2.m_achievementLists) { if (!Object.op_Implicit((Object)(object)achievementList) || achievementList.m_achievements == null) { continue; } foreach (Achievement achievement in achievementList.m_achievements) { if (!Object.op_Implicit((Object)(object)achievement) || achievement.m_itemCraftTriggers == null) { continue; } foreach (DictStatRequirement itemCraftTrigger in achievement.m_itemCraftTriggers) { if (!string.IsNullOrEmpty(itemCraftTrigger.m_stat)) { craftable.Add(itemCraftTrigger.m_stat); } } } } } } return craftable.Contains(name); } public static void Reset() { FoodEligibility.Reset(); craftable.Clear(); database = null; achievements = null; recipeCount = -1; nextRefresh = 0f; } } public static class HintText { public const string DoneColor = "9EDB93"; public const string MissingColor = "F3C46C"; public static string FoodLabel(bool ko) { if (!ko) { return "Food history"; } return "섭취 기록"; } public static string CraftLabel(bool ko) { if (!ko) { return "Crafting history"; } return "제작 기록"; } public static string FoodValue(History status, bool ko) { if (status != History.Done) { if (!ko) { return "Not eaten"; } return "미섭취"; } if (!ko) { return "Eaten"; } return "섭취 완료"; } public static string CraftValue(History status, bool ko) { if (status != History.Done) { if (!ko) { return "Not crafted"; } return "미제작"; } if (!ko) { return "Crafted"; } return "제작 완료"; } private static string Line(string label, string value, History status) { return label + ": <color=#" + ((status == History.Done) ? "9EDB93" : "F3C46C") + ">" + value + "</color>"; } public static string Description(string original, ItemHistory state) { if (!state.Visible) { return original; } string text = ""; if (state.Craft != History.Hidden) { text = Line(CraftLabel(state.Korean), CraftValue(state.Craft, state.Korean), state.Craft); } if (state.Food != History.Hidden) { text = text + ((text.Length > 0) ? "\n" : "") + Line(FoodLabel(state.Korean), FoodValue(state.Food, state.Korean), state.Food); } if (!string.IsNullOrEmpty(original)) { return text + "\n\n" + original; } return text; } public static string MonsterBadge(bool korean) { if (!korean) { return "UNKILLED"; } return "미처치"; } } public static class FoodEligibility { private static readonly HashSet<string> required = new HashSet<string>(StringComparer.Ordinal); private static Achievements source; private static int listCount = -1; private static float nextRefresh; public static bool Tracks(ItemData item) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 SharedData val = item?.m_shared; if (val == null || string.IsNullOrEmpty(val.m_name)) { return false; } if ((int)val.m_itemType == 2 && val.m_food > 0f) { return true; } Refresh(); return required.Contains(val.m_name); } private static void Refresh() { //IL_00e3: 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_00ea: 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) Achievements instance = Achievements.m_instance; int num = (Object.op_Implicit((Object)(object)instance) ? (instance.m_achievementLists?.Count ?? 0) : 0); if (instance == source && num == listCount && Time.unscaledTime < nextRefresh) { return; } source = instance; listCount = num; nextRefresh = Time.unscaledTime + 5f; required.Clear(); if (!Object.op_Implicit((Object)(object)instance) || instance.m_achievementLists == null) { return; } foreach (AchievementList achievementList in instance.m_achievementLists) { if (!Object.op_Implicit((Object)(object)achievementList) || achievementList.m_achievements == null) { continue; } foreach (Achievement achievement in achievementList.m_achievements) { if (!Object.op_Implicit((Object)(object)achievement) || achievement.m_foodEatenTriggers == null) { continue; } foreach (DictStatRequirement foodEatenTrigger in achievement.m_foodEatenTriggers) { if (!string.IsNullOrEmpty(foodEatenTrigger.m_stat)) { required.Add(foodEatenTrigger.m_stat); } } } } } public static void Reset() { required.Clear(); source = null; listCount = -1; nextRefresh = 0f; } } public static class Bindings { private static MethodInfo Method(Type type, string name, bool isStatic, Type result, params Type[] args) { MethodInfo methodInfo = AccessTools.DeclaredMethod(type, name, args, (Type[])null); if (methodInfo == null || methodInfo.IsStatic != isStatic || methodInfo.ReturnType != result) { throw new MissingMethodException(type.FullName, name); } return methodInfo; } public static void Install(Harmony harmony, Assembly senea) { InstallTooltip(harmony, senea); InstallCrafting(harmony, senea); InstallMonsters(harmony, senea); } public static void InstallTooltip(Harmony harmony, Assembly senea) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown RuntimeStructure.Check(typeof(TooltipHints)); MethodInfo methodInfo = Method(senea.GetType("SeneaLUI.Inv.Tips", throwOnError: true), "ForItem", true, typeof(Tip), typeof(ItemData), typeof(int), typeof(bool), typeof(int)); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(TooltipHints), "Postfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } public static void InstallCrafting(Harmony harmony, Assembly senea) { //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_0114: Expected O, but got Unknown //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown RuntimeStructure.Check(typeof(CraftHints)); Type type = senea.GetType("SeneaLUI.Inv.CraftView", throwOnError: true); MethodInfo methodInfo = Method(type, "BuildDetail", false, typeof(void), typeof(Recipe), typeof(ItemData), typeof(int), typeof(int), typeof(int), typeof(bool)); MethodInfo methodInfo2 = Method(type, "TickDetail", false, typeof(void), typeof(Player), typeof(CraftingStation)); CraftHints.Prepare(type, methodInfo); CraftHints.Transpiler(PatchProcessor.GetOriginalInstructions((MethodBase)methodInfo, (ILGenerator)null)).ToArray(); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(CraftHints), "Built", (Type[])null), new HarmonyMethod(typeof(CraftHints), "Transpiler", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(CraftHints), "Tick", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } public static void InstallMonsters(Harmony harmony, Assembly senea) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown RuntimeStructure.Check(typeof(MonsterHints)); Type type = senea.GetType("SeneaLUI.Hud.EnemyHudView", throwOnError: true); MethodInfo methodInfo = Method(type, "TickMobs", false, typeof(void), typeof(Transform), typeof(int)); MethodInfo methodInfo2 = Method(typeof(EnemyHud), "UpdateHuds", false, typeof(void), typeof(Player), typeof(Sadle), typeof(float)); MonsterHints.Prepare(type); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(MonsterHints), "After", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(typeof(MonsterHints), "HudUpdated", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } public static string Hash(string path) { using FileStream inputStream = File.OpenRead(path); using SHA256 sHA = SHA256.Create(); return BitConverter.ToString(sHA.ComputeHash(inputStream)).Replace("-", ""); } public static bool VerifyInputs(string senea, string game) { string text = Hash(senea); string text2 = Hash(game); if (text == "FB4B06CEB57A9F811351093176F4ECB784369D32AB5319A4715513F9C47D5F95" || text == "537000308C1D8F7C1593BE79E83A2BEF69F6317C5CFBB90DC06A9777D842D226") { return text2 == "94998885B103D6E5F5480123EAEE0A093E82AF2DDEEEE37536C15BF555F24F3A"; } return false; } } public sealed class FeatureSession : IDisposable { private sealed class Feature { public string Id; public Harmony Patches; public Action Reset; public Action Tick; } private readonly string owner; private readonly Action<string> info; private readonly Action<string> warning; private readonly List<Feature> enabled = new List<Feature>(); public int Count => enabled.Count; public FeatureSession(string owner, Action<string> info, Action<string> warning) { this.owner = owner; this.info = info; this.warning = warning; } public bool Has(string id) { return enabled.Exists((Feature feature) => feature.Id == id); } public bool Enable(string id, string label, Action<Harmony> install, Action reset, Action tick = null) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Expected O, but got Unknown if (Has(id)) { throw new InvalidOperationException("Feature already registered: " + id); } Feature feature = new Feature { Id = id, Patches = new Harmony(owner + "." + id), Reset = reset, Tick = tick }; try { install(feature.Patches); info("Feature enabled: " + label + " (structural checks passed)."); enabled.Add(feature); return true; } catch (Exception error) { Remove(feature); warning("Feature disabled: " + label + ". " + Reason(error)); return false; } } public static string Reason(Exception error) { while (error.InnerException != null) { error = error.InnerException; } return error.GetType().Name + ": " + error.Message; } public void Tick() { for (int num = enabled.Count - 1; num >= 0; num--) { Feature feature = enabled[num]; try { feature.Tick?.Invoke(); } catch (Exception error) { Remove(feature); enabled.RemoveAt(num); warning("Feature disabled during update: " + feature.Id + ". " + Reason(error)); } } } private void Remove(Feature feature) { try { feature.Patches.UnpatchSelf(); } catch (Exception error) { warning("Feature patch cleanup: " + feature.Id + ". " + Reason(error)); } try { feature.Reset?.Invoke(); } catch (Exception error2) { warning("Feature UI cleanup: " + feature.Id + ". " + Reason(error2)); } } public void Dispose() { for (int num = enabled.Count - 1; num >= 0; num--) { Remove(enabled[num]); } enabled.Clear(); RuntimeStructure.Reset(); } } public static class RuntimeStructure { private sealed class Walk { public readonly Queue<MethodBase> Methods = new Queue<MethodBase>(); public readonly HashSet<MethodBase> Seen = new HashSet<MethodBase>(); private readonly HashSet<Type> shapes = new HashSet<Type>(); private readonly HashSet<Type> roots = new HashSet<Type>(); public void Root(Type type) { if (!roots.Add(type)) { return; } Shape(type); foreach (MethodBase item in type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Cast<MethodBase>().Concat(type.GetConstructors(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))) { Methods.Enqueue(item); } Type[] nestedTypes = type.GetNestedTypes(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (Type type2 in nestedTypes) { Root(type2); } } public void Shape(Type type) { if (type == null || !shapes.Add(type)) { return; } if (type.HasElementType) { Shape(type.GetElementType()); return; } if (type.IsGenericType) { Type[] genericArguments = type.GetGenericArguments(); foreach (Type type2 in genericArguments) { Shape(type2); } } if (Owned(type)) { FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { Shape(fieldInfo.FieldType); } if (type.TypeInitializer != null) { Methods.Enqueue(type.TypeInitializer); } } } } private const BindingFlags Declared = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private static readonly HashSet<MethodBase> validated = new HashSet<MethodBase>(); public static MethodInfo Method(Type type, string name, bool isStatic, Type result, params Type[] args) { MethodInfo methodInfo = AccessTools.DeclaredMethod(type, name, args, (Type[])null); if (methodInfo == null || methodInfo.IsStatic != isStatic || methodInfo.ReturnType != result || methodInfo.ContainsGenericParameters) { throw new MissingMethodException(type.FullName, name + " (required signature)"); } return methodInfo; } public static FieldInfo Field(Type type, string name, Type fieldType) { FieldInfo fieldInfo = AccessTools.Field(type, name); if (fieldInfo == null || fieldInfo.IsStatic || fieldInfo.FieldType != fieldType) { throw new MissingFieldException(type.FullName, name + " : " + fieldType.FullName); } return fieldInfo; } public static void Check(params Type[] roots) { Walk walk = new Walk(); foreach (Type type in roots) { walk.Root(type); } while (walk.Methods.Count > 0) { MethodBase methodBase = walk.Methods.Dequeue(); if (validated.Contains(methodBase) || !walk.Seen.Add(methodBase) || methodBase.ContainsGenericParameters || methodBase.GetMethodBody() == null) { continue; } foreach (CodeInstruction originalInstruction in PatchProcessor.GetOriginalInstructions(methodBase, (ILGenerator)null)) { if (originalInstruction.operand == null && (originalInstruction.opcode.OperandType == OperandType.InlineMethod || originalInstruction.opcode.OperandType == OperandType.InlineField || originalInstruction.opcode.OperandType == OperandType.InlineType || originalInstruction.opcode.OperandType == OperandType.InlineTok)) { throw new MissingMemberException("Unresolved " + originalInstruction.opcode.OperandType.ToString() + " reference in " + methodBase.DeclaringType.FullName + "." + methodBase.Name); } if (originalInstruction.operand is FieldInfo fieldInfo) { bool flag = originalInstruction.opcode == OpCodes.Ldsfld || originalInstruction.opcode == OpCodes.Ldsflda || originalInstruction.opcode == OpCodes.Stsfld; bool flag2 = originalInstruction.opcode == OpCodes.Ldfld || originalInstruction.opcode == OpCodes.Ldflda || originalInstruction.opcode == OpCodes.Stfld; if ((flag || flag2) && fieldInfo.IsStatic != flag) { throw new NotSupportedException("Field storage changed: " + fieldInfo.DeclaringType.FullName + "." + fieldInfo.Name); } walk.Shape(fieldInfo.DeclaringType); walk.Shape(fieldInfo.FieldType); } else if (originalInstruction.operand is MethodBase methodBase2) { if (originalInstruction.opcode == OpCodes.Callvirt && methodBase2.IsStatic) { throw new NotSupportedException("Instance method changed: " + methodBase2); } walk.Shape(methodBase2.DeclaringType); ParameterInfo[] parameters = methodBase2.GetParameters(); foreach (ParameterInfo parameterInfo in parameters) { walk.Shape(parameterInfo.ParameterType); } if (methodBase2 is MethodInfo methodInfo) { walk.Shape(methodInfo.ReturnType); } if (Owned(methodBase2.DeclaringType)) { walk.Methods.Enqueue(methodBase2); if (methodBase2 is ConstructorInfo && methodBase2.DeclaringType.IsDefined(typeof(CompilerGeneratedAttribute), inherit: false)) { walk.Root(methodBase2.DeclaringType); } } } else if (originalInstruction.operand is Type type2) { walk.Shape(type2); } } } validated.UnionWith(walk.Seen); } private static bool Owned(Type type) { if (type != null && type.Assembly == typeof(RuntimeStructure).Assembly && type.Namespace == typeof(RuntimeStructure).Namespace && !type.FullName.StartsWith(typeof(RuntimeStructure).FullName, StringComparison.Ordinal) && type != typeof(FeatureSession)) { return type.FullName != "SeneaLAchievementHints.Plugin"; } return false; } public static void Reset() { validated.Clear(); } } public static class TooltipHints { private sealed class Entry { public int Version; public ItemHistory State; public Tip Display; } private static ConditionalWeakTable<Tip, Entry> cache = new ConditionalWeakTable<Tip, Entry>(); private static int serial = int.MinValue; private static bool failed; public static Tip Decorate(Tip original, ItemHistory state) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_0099: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0111: 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_0129: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown if (original == null || !state.Visible) { return original; } Entry value = cache.GetValue(original, (Tip _) => new Entry()); if (value.Display != null && value.Version == original.Version && value.State.Equals(state)) { return value.Display; } Tip val = new Tip { Title = original.Title, Sub = original.Sub, Body = original.Body, Note = original.Note, Warn = original.Warn, HasScroll = original.HasScroll, Scroll = original.Scroll, TitleColor = original.TitleColor, Icon = original.Icon, Rarity = original.Rarity, Source = original.Source, RichText = original.RichText, Width = original.Width, Split = original.Split, VneiKey = original.VneiKey, VneiAlt = original.VneiAlt, Version = ++serial }; val.Rows.AddRange(original.Rows); val.Footer.AddRange(original.Footer); if (state.Craft != History.Hidden) { Add(val, HintText.CraftLabel(state.Korean), HintText.CraftValue(state.Craft, state.Korean), state.Craft); } if (state.Food != History.Hidden) { Add(val, HintText.FoodLabel(state.Korean), HintText.FoodValue(state.Food, state.Korean), state.Food); } value.Version = original.Version; value.State = state; value.Display = val; return val; } private static void Add(Tip tip, string label, string value, History status) { //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_0050: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) tip.AddRow(label, value, (string)null); int index = tip.Rows.Count - 1; Row value2 = tip.Rows[index]; value2.ValueColor = ((status == History.Done) ? new Color(0.62f, 0.86f, 0.58f) : new Color(0.95f, 0.77f, 0.42f)); tip.Rows[index] = value2; } public static void Postfix(ItemData __0, ref Tip __result) { if (failed) { return; } try { __result = Decorate(__result, Progress.Item(__0)); } catch (Exception ex) { failed = true; Progress.Warning("Item history display disabled: " + ex.Message); } } public static void Reset() { cache = new ConditionalWeakTable<Tip, Entry>(); failed = false; } } public static class CraftHints { private sealed class Entry { public ItemHistory State; } private static ConditionalWeakTable<object, Entry> views = new ConditionalWeakTable<object, Entry>(); private static MethodInfo build; private static FieldInfo recipe; private static FieldInfo item; private static FieldInfo quality; private static FieldInfo variant; private static FieldInfo multiplier; private static FieldInfo ghost; private static bool failed; public static void Prepare(Type type, MethodInfo buildDetail) { build = buildDetail; recipe = Field(type, "_dRecipe", typeof(Recipe)); item = Field(type, "_dItem", typeof(ItemData)); quality = Field(type, "_dQuality", typeof(int)); variant = Field(type, "_dVariant", typeof(int)); multiplier = Field(type, "_dMult", typeof(int)); ghost = Field(type, "_dGhost", typeof(bool)); MethodBody methodBody = build.GetMethodBody(); if (methodBody == null || methodBody.LocalVariables.Count == 0 || methodBody.LocalVariables[0].LocalType != typeof(ItemData)) { throw new NotSupportedException("CraftView.BuildDetail output-item local changed."); } } private static FieldInfo Field(Type type, string name, Type fieldType) { FieldInfo fieldInfo = AccessTools.Field(type, name); if (fieldInfo == null || fieldInfo.FieldType != fieldType || fieldInfo.IsStatic) { throw new MissingFieldException(type.FullName, name); } return fieldInfo; } public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown List<CodeInstruction> list = instructions.ToList(); List<int> list2 = new List<int>(); for (int i = 0; i < list.Count; i++) { if (list[i].operand is MethodInfo { Name: "Unrepeat" } methodInfo && methodInfo.DeclaringType.FullName == "SeneaLUI.Inv.CraftData" && methodInfo.IsStatic && methodInfo.ReturnType == typeof(string) && methodInfo.GetParameters().Length == 1 && methodInfo.GetParameters()[0].ParameterType == typeof(string)) { list2.Add(i); } } if (list2.Count != 1) { throw new NotSupportedException("Expected one native crafting-description layout anchor."); } list.InsertRange(list2[0] + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[2] { new CodeInstruction(OpCodes.Ldloc_0, (object)null), new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(CraftHints), "Description", (Type[])null, (Type[])null)) }); return list; } public static string Description(string original, ItemData output) { if (failed) { return original; } try { return HintText.Description(original, Progress.Item(output, recipeOutput: true)); } catch (Exception error) { Fail(error); return original; } } public static void Built(object __instance, Recipe __0, ItemData __1) { if (failed) { return; } try { views.GetValue(__instance, (object _) => new Entry()).State = Progress.Item(__1 ?? ((!Object.op_Implicit((Object)(object)__0)) ? null : __0.m_item?.m_itemData), recipeOutput: true); } catch (Exception error) { Fail(error); } } public static void Tick(object __instance) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown if (failed) { return; } try { if (!views.TryGetValue(__instance, out var value)) { return; } Recipe val = (Recipe)recipe.GetValue(__instance); ItemData val2 = (ItemData)item.GetValue(__instance); ItemHistory state = Progress.Item(val2 ?? ((!Object.op_Implicit((Object)(object)val)) ? null : val.m_item?.m_itemData), recipeOutput: true); if (!state.Equals(value.State)) { value.State = state; if (Object.op_Implicit((Object)(object)val) || val2 != null) { build.Invoke(__instance, new object[6] { val, val2, quality.GetValue(__instance), variant.GetValue(__instance), multiplier.GetValue(__instance), ghost.GetValue(__instance) }); } } } catch (Exception error) { Fail(error); } } private static void Fail(Exception error) { failed = true; Progress.Warning("Crafting history display disabled: " + (error.InnerException ?? error).Message); } public static void Reset() { views = new ConditionalWeakTable<object, Entry>(); failed = false; } } public static class MonsterHints { private sealed class Entry { public TMP_Text Label; public IMonsterBadge Badge; public int Seen; } private static readonly Dictionary<object, Entry> marks = new Dictionary<object, Entry>(); private static readonly List<object> stale = new List<object>(); private static FieldInfo mobs; private static FieldInfo who; private static FieldInfo name; private static WeakReference lastView; private static int generation; private static bool failed; public static Func<TMP_Text, IMonsterBadge> CreateBadge; public static void Prepare(Type viewType) { mobs = AccessTools.Field(viewType, "_mobs"); if (mobs == null || mobs.IsStatic || !typeof(IDictionary).IsAssignableFrom(mobs.FieldType) || !mobs.FieldType.IsGenericType || mobs.FieldType.GetGenericArguments().Length != 2) { throw new MissingFieldException(viewType.FullName, "_mobs (instance dictionary)"); } Type obj = mobs.FieldType.GetGenericArguments()[1]; who = AccessTools.Field(obj, "Who"); name = AccessTools.Field(obj, "Name"); if (who == null || who.IsStatic || who.FieldType != typeof(Character) || name == null || name.IsStatic || name.FieldType != typeof(TMP_Text)) { throw new NotSupportedException("SeneaL enemy name bindings changed."); } } public static void After(object __instance) { if (!failed) { if (lastView?.Target != __instance) { Clear(); lastView = new WeakReference(__instance); } Refresh(__instance); } } public static void HudUpdated() { if (!failed) { object obj = lastView?.Target; if (obj != null) { Refresh(obj); } } } private static void Refresh(object view) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown try { if (!(mobs.GetValue(view) is IDictionary dictionary)) { Clear(); return; } int num = ++generation; bool flag = Progress.ShowMonsters(); string text = HintText.MonsterBadge(Progress.Korean); foreach (object value2 in dictionary.Values) { Character character = (Character)who.GetValue(value2); TMP_Text val = (TMP_Text)name.GetValue(value2); bool flag2 = flag && Object.op_Implicit((Object)(object)val) && !string.IsNullOrEmpty(val.text) && Progress.Unkilled(character); if (!marks.TryGetValue(value2, out var value)) { if (!flag2) { continue; } value = new Entry(); marks.Add(value2, value); } value.Seen = num; if (value.Badge != null && (value.Label != val || !value.Badge.Alive)) { value.Badge.Dispose(); value.Badge = null; } if (flag2) { if (value.Badge == null) { if (CreateBadge == null) { throw new InvalidOperationException("Creature badge renderer is unavailable."); } value.Label = val; value.Badge = CreateBadge(val); } value.Badge.Show(text); } else { value.Badge?.Hide(); } } stale.Clear(); foreach (KeyValuePair<object, Entry> mark in marks) { if (mark.Value.Seen != num) { mark.Value.Badge?.Dispose(); stale.Add(mark.Key); } } foreach (object item in stale) { marks.Remove(item); } stale.Clear(); } catch (Exception error) { Fail(error); } } private static void Clear() { foreach (Entry value in marks.Values) { value.Badge?.Dispose(); } marks.Clear(); stale.Clear(); } private static void Fail(Exception error) { failed = true; Clear(); Progress.Warning("Unkilled marker disabled: " + error.Message); } public static void Reset() { Clear(); lastView = null; CreateBadge = null; generation = 0; failed = false; } } public interface IMonsterBadge : IDisposable { bool Alive { get; } void Show(string text); void Hide(); } public struct BadgePosition { public float X; public float Y; } public static class MonsterBadgeLayout { public const float AlertGap = 14f; public const float VerticalGap = 4f; private static float Safe(float value) { if (!float.IsNaN(value) && !float.IsInfinity(value) && !(value < 0f)) { return value; } return 0f; } public static float FontSize(float nameSize) { return Math.Max(10f, Math.Min(14f, Safe(nameSize) * 0.6f)); } public static BadgePosition AboveLeft(float nameWidth, float nameHeight, float badgeWidth) { return new BadgePosition { X = 0f - Math.Max(Safe(nameWidth) * 0.5f, Safe(badgeWidth) + 14f), Y = Safe(nameHeight) * 0.5f + 4f }; } } public sealed class MonsterBadge : IMonsterBadge, IDisposable { private static int themeRevision; private static bool subscribed; private readonly TMP_Text source; private readonly GameObject root; private readonly RectTransform rect; private readonly TextMeshProUGUI text; private readonly Image background; private string sourceText; private string badgeText; private TMP_FontAsset sourceFont; private float sourceSize = -1f; private int appliedTheme = -1; public bool Alive { get { if (Object.op_Implicit((Object)(object)source) && Object.op_Implicit((Object)(object)root)) { return Object.op_Implicit((Object)(object)text); } return false; } } public static void Configure() { themeRevision++; if (!subscribed) { Themes.Changed += ThemeChanged; subscribed = true; } } private static void ThemeChanged() { themeRevision++; } public static void Shutdown() { if (subscribed) { Themes.Changed -= ThemeChanged; subscribed = false; } themeRevision++; } [MethodImpl(MethodImplOptions.NoInlining)] public static IMonsterBadge Create(TMP_Text name) { return new MonsterBadge(name); } private MonsterBadge(TMP_Text name) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) source = name; root = new GameObject("sah.unkilled-badge", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); try { root.layer = ((Component)source).gameObject.layer; rect = root.GetComponent<RectTransform>(); ((Transform)rect).SetParent((Transform)(object)source.rectTransform, false); RectTransform obj = rect; Vector2 anchorMin = (rect.anchorMax = new Vector2(0.5f, 0.5f)); obj.anchorMin = anchorMin; rect.pivot = Vector2.zero; background = root.GetComponent<Image>(); ((Graphic)background).raycastTarget = false; GameObject val2 = new GameObject("label", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }); val2.layer = root.layer; RectTransform component = val2.GetComponent<RectTransform>(); ((Transform)component).SetParent((Transform)(object)rect, false); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = new Vector2(5f, 2f); component.offsetMax = new Vector2(-5f, -2f); text = val2.GetComponent<TextMeshProUGUI>(); ((Graphic)text).raycastTarget = false; ((TMP_Text)text).richText = false; ((TMP_Text)text).enableAutoSizing = false; ((TMP_Text)text).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)text).overflowMode = (TextOverflowModes)0; ((TMP_Text)text).fontStyle = (FontStyles)1; ((TMP_Text)text).alignment = (TextAlignmentOptions)514; root.SetActive(false); } catch { Object.Destroy((Object)(object)root); throw; } } public void Show(string value) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: 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) //IL_01ed: 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_0213: Unknown result type (might be due to invalid IL or missing references) //IL_021d: 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_0246: Unknown result type (might be due to invalid IL or missing references) if (Alive) { if (badgeText != value || sourceText != source.text || (Object)(object)sourceFont != (Object)(object)source.font || sourceSize != source.fontSize || appliedTheme != themeRevision) { badgeText = value; sourceText = source.text; sourceFont = source.font; sourceSize = source.fontSize; appliedTheme = themeRevision; ((TMP_Text)text).font = source.font; AddonTheme.Font((TMP_Text)(object)text); ((TMP_Text)text).fontSize = MonsterBadgeLayout.FontSize(source.fontSize); ((Graphic)text).color = AddonTheme.Warning; ((TMP_Text)text).text = value; Sprite val = (Api.Has("theme") ? Themes.Sprite((ThemePart)16) : null); background.sprite = val; background.type = (Type)(Object.op_Implicit((Object)(object)val) ? 1 : 0); ((Graphic)background).color = (Color)(Object.op_Implicit((Object)(object)val) ? Color.white : new Color(0.06f, 0.07f, 0.08f, 0.96f)); Canvas componentInParent = ((Component)source).GetComponentInParent<Canvas>(); float num = (Api.Has("theme") ? Themes.PixelsPerUnit : 0f); background.pixelsPerUnitMultiplier = ((Object.op_Implicit((Object)(object)componentInParent) && num > 0f) ? (componentInParent.referencePixelsPerUnit / num) : 1f); Vector2 preferredValues = ((TMP_Text)text).GetPreferredValues(value); Vector2 preferredValues2 = source.GetPreferredValues(source.text); float num2 = preferredValues.x + 10f; float num3 = preferredValues.y + 4f; rect.sizeDelta = new Vector2(num2, num3); BadgePosition badgePosition = MonsterBadgeLayout.AboveLeft(preferredValues2.x, preferredValues2.y, num2); rect.anchoredPosition = new Vector2(badgePosition.X, badgePosition.Y); } if (!root.activeSelf) { root.SetActive(true); } } } public void Hide() { if (Object.op_Implicit((Object)(object)root) && root.activeSelf) { root.SetActive(false); } } public void Dispose() { if (Object.op_Implicit((Object)(object)root)) { root.SetActive(false); Object.Destroy((Object)(object)root); } } } public enum HintLanguage { Auto, Korean, English } public static class ApiSettings { public static void Register(ConfigEntry<bool> food, ConfigEntry<bool> craft, ConfigEntry<bool> monsters, ConfigEntry<HintLanguage> language) { if (Api.Has("settings")) { ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)food, (Func<string>)(() => (!Progress.Korean) ? "Food history" : "음식 섭취 기록"), (Func<string>)(() => (!Progress.Korean) ? "Eaten / not eaten in food tooltips and crafting details" : "음식 툴팁과 제작 상세에 섭취 여부 표시")); ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)craft, (Func<string>)(() => (!Progress.Korean) ? "Crafting history" : "아이템 제작 기록"), (Func<string>)(() => (!Progress.Korean) ? "Crafted / not crafted in item tooltips and crafting details" : "아이템 툴팁과 제작 상세에 제작 여부 표시")); ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)monsters, (Func<string>)(() => (!Progress.Korean) ? "Unkilled creatures" : "몬스터 미처치 표시"), (Func<string>)(() => (!Progress.Korean) ? "A separate badge above-left of creature names without a first kill recorded" : "첫 처치 기록이 없는 야생 생물 이름 왼쪽 위에 별도 배지 표시")); ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)language, (Func<string>)(() => (!Progress.Korean) ? "Language" : "표시 언어"), (Func<string>)(() => "Auto / Korean / English")).Options = () => Progress.Korean ? new string[3] { "게임 언어", "한국어", "English" } : new string[3] { "Game language", "Korean", "English" }; } } public static void RegisterExtras(ConfigEntry<bool> filters, ConfigEntry<bool> badges, ConfigEntry<bool> details) { if (Api.Has("settings")) { ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)filters, (Func<string>)(() => (!Progress.Korean) ? "Recipe history filters" : "제작 기록 필터"), (Func<string>)(() => (!Progress.Korean) ? "All history / not crafted / not eaten filters in crafting" : "제작창에 기록 전체 / 미제작 / 미섭취 필터 표시")); ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)badges, (Func<string>)(() => (!Progress.Korean) ? "Uneaten food badges" : "미섭취 음식 표식"), (Func<string>)(() => (!Progress.Korean) ? "A yellow ! on the right side of uneaten food slots" : "슬롯 오른쪽 가운데 노란 ! 표시")); ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)details, (Func<string>)(() => (!Progress.Korean) ? "Achievement details" : "업적 상세 도우미"), (Func<string>)(() => (!Progress.Korean) ? "Named targets, search, completion, biome and crafting-station filters" : "대상 이름, 검색, 완료 여부·지역·제작대 필터")); } } public static void RegisterPrivacy(ConfigEntry<bool> spoilers) { if (Api.Has("settings")) { ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)spoilers, (Func<string>)(() => (!Progress.Korean) ? "Hide undiscovered targets" : "미확인 항목 숨김"), (Func<string>)(() => (!Progress.Korean) ? "Show ??? for unknown targets and hide icons, search and categories. Creatures use kill history; items/builds use unlocks or action history. On by default." : "모르는 대상은 ???로 표시. 이름·아이콘·검색·분류를 함께 숨깁니다. 생물은 처치 기록, 아이템·건설은 해금/행동 기록 기준. 기본 켬.")); } } public static void RegisterPreview(ConfigEntry<bool> preview, ConfigEntry<PreviewQuality> quality) { if (Api.Has("settings")) { ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)preview, (Func<string>)(() => (!Progress.Korean) ? "Creature previews" : "생물 미리보기"), (Func<string>)(() => (!Progress.Korean) ? "Capture visible creatures once and cache their portraits. Unavailable models retain original icons. Disable to restore original icons." : "보이는 생물만 한 번 촬영해 저장합니다. 실패하면 기존 아이콘 사용. 끄면 원래 아이콘으로 복귀")); ModSettings.Add("local.senealachievementhints", "Achievement Hints SeneaL UI", (ConfigEntryBase)(object)quality, (Func<string>)(() => (!Progress.Korean) ? "Creature preview quality" : "생물 미리보기 품질"), (Func<string>)(() => (!Progress.Korean) ? "Standard 256px / Low 128px. Changes apply to the open list." : "기본 256px / 저사양용 128px. 변경하면 열린 목록에도 반영됩니다.")).Options = () => Progress.Korean ? new string[2] { "기본 · 256px", "저사양 · 128px" } : new string[2] { "Standard · 256px", "Low · 128px" }; } } public static void Remove() { if (Api.Has("settings")) { ModSettings.RemoveAll("local.senealachievementhints"); } } } public enum RecipeHistoryFilter { All, Uncrafted, Uneaten } public enum CompletionFilter { All, Incomplete, Complete } public static class ProgressFilters { public static bool MissingFood(ItemData item, PlayerStats stats) { SharedData val = item?.m_shared; Dictionary<string, float> dictionary = stats?.m_foodEatenStats; if (val != null && dictionary != null && !string.IsNullOrEmpty(val.m_name) && FoodEligibility.Tracks(item)) { if (dictionary.TryGetValue(val.m_name, out var value)) { return value <= 0f; } return true; } return false; } public static bool Allows(ItemHistory state, RecipeHistoryFilter filter) { return filter switch { RecipeHistoryFilter.Uncrafted => state.Craft == History.Missing, RecipeHistoryFilter.Uneaten => state.Food == History.Missing, _ => true, }; } public static bool Allows(bool complete, CompletionFilter filter) { if (filter != CompletionFilter.All) { return complete == (filter == CompletionFilter.Complete); } return true; } public static bool GoalMet(bool recorded, float current, float target, RequirementOperator comparison) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected I4, but got Unknown if (!recorded) { return false; } return (int)comparison switch { 0 => current >= target, 1 => current <= target, 2 => current == target, 3 => current != target, _ => false, }; } } public static class CraftFilters { private sealed class View { public WeakReference Owner; public ChipBar Bar; public RecipeHistoryFilter Mode; public int Signature; public float Next; public bool Active; public bool Korean; } private static ConditionalWeakTable<object, View> views = new ConditionalWeakTable<object, View>(); private static readonly List<View> live = new List<View>(); public static Func<bool> Enabled = () => true; private static FieldInfo panel; private static FieldInfo width; private static FieldInfo bodyTop; private static FieldInfo rows; private static FieldInfo rowRecipe; private static FieldInfo rowItem; private static FieldInfo rowVisible; private static FieldInfo shown; private static readonly List<FieldInfo> shifted = new List<FieldInfo>(); private static MethodInfo layout; private static MethodInfo resize; private static MethodInfo filter; private static bool failed; public static void Prepare(Type type) { panel = RuntimeStructure.Field(type, "_panel", typeof(RectTransform)); width = RuntimeStructure.Field(type, "_w", typeof(float)); bodyTop = RuntimeStructure.Field(type, "_bodyTop", typeof(float)); rows = Required(type, "_rows"); shown = RuntimeStructure.Field(type, "_shown", typeof(bool)); if (!rows.FieldType.IsGenericType || rows.FieldType.GetGenericTypeDefinition() != typeof(List<>)) { throw new NotSupportedException("Craft filter rows must be a typed List."); } Type type2 = rows.FieldType.GetGenericArguments()[0]; rowRecipe = RuntimeStructure.Field(type2, "Recipe", typeof(Recipe)); rowItem = RuntimeStructure.Field(type2, "Item", typeof(ItemData)); rowVisible = RuntimeStructure.Field(type2, "Visible", typeof(bool)); shifted.Clear(); string[] array = new string[5] { "_listView", "_listBar", "_divider", "_detailView", "_detailBar" }; foreach (string text in array) { FieldInfo fieldInfo = Required(type, text); if (!typeof(RectTransform).IsAssignableFrom(fieldInfo.FieldType) && !typeof(Component).IsAssignableFrom(fieldInfo.FieldType)) { throw new NotSupportedException("Craft filter layout field changed: " + text); } shifted.Add(fieldInfo); } layout = RuntimeStructure.Method(type, "LayoutFrame", false, typeof(void)); resize = RuntimeStructure.Method(type, "Resize", false, typeof(void)); filter = RuntimeStructure.Method(type, "Filter", false, typeof(void)); } private static FieldInfo Required(Type type, string name) { FieldInfo fieldInfo = AccessTools.Field(type, name); if (fieldInfo == null || fieldInfo.IsStatic) { throw new MissingFieldException(type.FullName, name); } return fieldInfo; } private static View Get(object owner) { return views.GetValue(owner, delegate(object key) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown View entry = new View { Owner = new WeakReference(key) }; entry.Bar = new ChipBar((Transform)(RectTransform)panel.GetValue(key), "achievement-history-filter", new string[3], (Action<int>)delegate(int mode) { object target = entry.Owner.Target; if (target == null) { return; } try { entry.Mode = (RecipeHistoryFilter)mode; entry.Bar.SetOn(mode); entry.Signature = Signature(target, entry.Mode); filter.Invoke(target, null); } catch (Exception error) { Fail(error); } }); live.Add(entry); return entry; }); } public static void Layout(object __instance) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) if (failed) { return; } try { View view = Get(__instance); view.Active = Enabled(); view.Korean = Progress.Korean; ((Component)view.Bar.Root).gameObject.SetActive(view.Active); if (!view.Active) { return; } view.Bar.MaxWidth = (float)width.GetValue(__instance) - 44f; view.Bar.SetLabels((!view.Korean) ? new string[3] { "All history", "Not crafted", "Not eaten" } : new string[3] { "기록 전체", "미제작", "미섭취" }); view.Bar.SetOn((int)view.Mode); float num = (float)bodyTop.GetValue(__instance); view.Bar.Root.anchoredPosition = new Vector2(22f, 0f - num); float num2 = view.Bar.Height + 10f; bodyTop.SetValue(__instance, num + num2); foreach (FieldInfo item in shifted) { object value = item.GetValue(__instance); object obj = ((value is RectTransform) ? value : null); if (obj == null) { object obj2 = ((value is Component) ? value : null); Transform obj3 = ((obj2 != null) ? ((Component)obj2).transform : null); obj = ((obj3 is RectTransform) ? obj3 : null); } RectTransform val = (RectTransform)obj; if (Object.op_Implicit((Object)(object)val)) { val.anchoredPosition -= new Vector2(0f, num2); } } resize.Invoke(__instance, null); } catch (Exception error) { Fail(error); } } public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Expected O, but got Unknown List<CodeInstruction> list = instructions.ToList(); int num = 0; for (int i = 0; i < list.Count; i++) { if (!(list[i].opcode != OpCodes.Stfld) && object.Equals(list[i].operand, rowVisible)) { list[i].opcode = OpCodes.Ldarg_0; list[i].operand = null; list.Insert(++i, new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(CraftFilters), "SetVisible", (Type[])null, (Type[])null))); num++; } } if (num != 1) { throw new NotSupportedException("Expected one native recipe visibility assignment."); } return list; } private static ItemData Output(object row) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown ItemData val = (ItemData)rowItem.GetValue(row); if (val != null) { return val; } Recipe val2 = (Recipe)rowRecipe.GetValue(row); if (!Object.op_Implicit((Object)(object)val2)) { return null; } return val2.m_item?.m_itemData; } public static void SetVisible(object row, bool nativeVisible, object owner) { bool flag = nativeVisible; try { if (!failed && nativeVisible && Enabled() && views.TryGetValue(owner, out var value) && value.Mode != RecipeHistoryFilter.All && Progress.CurrentStats() != null) { flag = ProgressFilters.Allows(Progress.Item(Output(row), recipeOutput: true), value.Mode); } } catch (Exception error) { Fail(error); } rowVisible.SetValue(row, flag); } private static int Signature(object owner, RecipeHistoryFilter mode) { int num = 17; foreach (object item in (IEnumerable)rows.GetValue(owner)) { num = num * 31 + (ProgressFilters.Allows(Progress.Item(Output(item), recipeOutput: true), mode) ? 1 : 0); } return num; } public static void Tick(object __instance) { if (failed || !views.TryGetValue(__instance, out var value)) { return; } try { if (!(bool)shown.GetValue(__instance)) { return; } if (value.Active != Enabled() || value.Korean != Progress.Korean) { layout.Invoke(__instance, null); filter.Invoke(__instance, null); } if (value.Active && value.Mode != RecipeHistoryFilter.All && !(Time.unscaledTime < value.Next)) { value.Next = Time.unscaledTime + 0.35f; int num = Signature(__instance, value.Mode); if (num != value.Signature) { value.Signature = num; filter.Invoke(__instance, null); } } } catch (Exception error) { Fail(error); } } private static void Fail(Exception error) { if (!failed) { Progress.Warning("Recipe history filters disabled: " + (error.InnerException ?? error).Message); } failed = true; foreach (View item in live) { if (Object.op_Implicit((Object)(object)item.Bar?.Root)) { ((Component)item.Bar.Root).gameObject.SetActive(false); } } } public static void Reset() { foreach (View item in live) { object target = item.Owner.Target; if (target != null && Object.op_Implicit((Object)(object)item.Bar?.Root)) { try { layout?.Invoke(target, null); filter?.Invoke(target, null); } catch { } } if (Object.op_Implicit((Object)(object)item.Bar?.Root)) { Object.Destroy((Object)(object)((Component)item.Bar.Root).gameObject); } } live.Clear(); views = new ConditionalWeakTable<object, View>(); failed = false; } } public static class FoodBadges { private sealed class Badge { public WeakReference Slot; public ItemData Item; public RectTransform Root; public TextMeshProUGUI Text; } public static Func<bool> Enabled = () => true; private static ConditionalWeakTable<Slot, Badge> badges = new ConditionalWeakTable<Slot, Badge>(); private static readonly List<Badge> live = new List<Badge>(); private static FieldInfo overlay; private static float next; private static bool failed; public static void Prepare() { overlay = RuntimeStructure.Field(typeof(Slot), "_ovHost", typeof(RectTransform)); } private static bool Missing(ItemData item) { if (Enabled()) { return ProgressFilters.MissingFood(item, Progress.CurrentStats()); } return false; } public static void Set(Slot __instance, ItemData __0) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) if (failed) { return; } try { bool flag = Missing(__0); if (!badges.TryGetValue(__instance, out var value)) { if (!flag) { return; } RectTransform val = (RectTransform)overlay.GetValue(__instance); value = new Badge { Slot = new WeakReference(__instance) }; value.Root = Ui.Node((Transform)(object)val, "achievement-uneaten"); RectTransform root = value.Root; RectTransform root2 = value.Root; Vector2 val2 = (value.Root.pivot = new Vector2(1f, 0.5f)); Vector2 anchorMin = (root2.anchorMax = val2); root.anchorMin = anchorMin; value.Root.anchoredPosition = new Vector2(-5f, 0f); value.Root.sizeDelta = new Vector2(18f, 20f); value.Text = Ui.Label((Transform)(object)value.Root, "mark", TextStyles.ChipText, 0.5f, 0f, 18f, 18f, (TextAlignmentOptions)514); RectTransform rectTransform = ((TMP_Text)value.Text).rectTransform; RectTransform rectTransform2 = ((TMP_Text)value.Text).rectTransform; val2 = (((TMP_Text)value.Text).rectTransform.pivot = new Vector2(0.5f, 0.5f)); anchorMin = (rectTransform2.anchorMax = val2); rectTransform.anchorMin = anchorMin; ((TMP_Text)value.Text).rectTransform.anchoredPosition = Vector2.zero; ((TMP_Text)value.Text).rectTransform.sizeDelta = new Vector2(18f, 22f); ((TMP_Text)value.Text).text = "!"; ((TMP_Text)value.Text).fontSize = 21f; ((TMP_Text)value.Text).fontStyle = (FontStyles)1; ((Graphic)value.Text).color = AddonTheme.Warning; ((Graphic)value.Text).raycastTarget = false; badges.Add(__instance, value); live.Add(value); } value.Item = __0; ((Component)value.Root).gameObject.SetActive(flag); } catch (Exception error) { Fail(error); } } public static void Clear(Slot __instance) { if (badges.TryGetValue(__instance, out var value)) { value.Item = null; if (Object.op_Implicit((Object)(object)value.Root)) { ((Component)value.Root).gameObject.SetActive(false); } } } public static void Tick() { if (failed || Time.unscaledTime < next) { return; } next = Time.unscaledTime + 0.35f; try { for (int num = live.Count - 1; num >= 0; num--) { Badge badge = live[num]; if (!Object.op_Implicit((Object)(object)badge.Root) || !badge.Slot.IsAlive) { live.RemoveAt(num); } else { bool flag = Missing(badge.Item); if (((Component)badge.Root).gameObject.activeSelf != flag) { ((Component)badge.Root).gameObject.SetActive(flag); } } } } catch (Exception error) { Fail(error); } } private static void Fail(Exception error) { failed = true; foreach (Badge item in live) { if (Object.op_Implicit((Object)(object)item.Root)) { ((Component)item.Root).gameObject.SetActive(false); } } Progress.Warning("Uneaten food badges disabled: " + error.Message); } public static void Reset() { foreach (Badge item in live) { if (Object.op_Implicit((Object)(object)item.Root)) { Object.Destroy((Object)(object)((Component)item.Root).gameObject); } } live.Clear(); badges = new ConditionalWeakTable<Slot, Badge>(); failed = false; next = 0f; } } public enum TargetKind { Stat, Enemy, Food, Craft, Pickup, Pickable, Build, Command, WorldKey, World, Achievement } public sealed class AchievementTarget { public TargetKind Kind; public string Key; public PlayerStatType Stat; public int Modifier; public float Required; public float Current; public bool Recorded; public bool Available; public bool Complete; public RequirementOperator Comparison; public Achievement Dependency; public string ProgressText => (Available ? Current.ToString("0.##", CultureInfo.CurrentCulture) : "—") + " / " + Required.ToString("0.##", CultureInfo.CurrentCulture); public void Refresh(PlayerStats stats) { //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) Current = 0f; Recorded = false; Available = stats != null; if (Kind == TargetKind.Achievement) { Available = Object.op_Implicit((Object)(object)Dependency); Recorded = Object.op_Implicit((Object)(object)Dependency); Current = ((Object.op_Implicit((Object)(object)Dependency) && Dependency.m_unlocked) ? 1 : 0); } else if (stats != null && Kind == TargetKind.Stat) { Available = stats.m_stats != null; Recorded = Available && stats.m_stats.TryGetValue(Stat, out Current); } else if (stats != null) { Dictionary<string, float> dictionary = null; switch (Kind) { case TargetKind.Enemy: if (stats.m_enemyStats != null && Modifier >= 0 && Modifier < stats.m_enemyStats.Length) { dictionary = stats.m_enemyStats[Modifier]; } break; case TargetKind.Food: dictionary = stats.m_foodEatenStats; break; case TargetKind.Craft: dictionary = stats.m_itemCraftStats; break; case TargetKind.Pickup: dictionary = stats.m_itemPickupStats; break; case TargetKind.Pickable: dictionary = stats.m_pickableStats; break; case TargetKind.Build: dictionary = stats.m_piecesPlacedStats; break; case TargetKind.Command: dictionary = stats.m_knownCommands; break; case TargetKind.WorldKey: dictionary = stats.m_knownWorldKeys; break; case TargetKind.World: dictionary = stats.m_knownWorlds; break; } Available = dictionary != null; Recorded = dictionary != null && !string.IsNullOrEmpty(Key) && dictionary.TryGetValue(Key, out Current); } Complete = Available && ProgressFilters.GoalMet(Recorded, Current, Required, Comparison); } } public static class AchievementData { public unsafe static List<AchievementTarget> Read(Achievement achievement, PlayerStats stats) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0062: 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_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Expected I4, but got Unknown //IL_01d3: Unknown result type (might be due to invalid IL or missing references) List<AchievementTarget> list = new List<AchievementTarget>(); if (!Object.op_Implicit((Object)(object)achievement)) { return list; } if (achievement.m_statTrigger != null) { foreach (PlayerStatRequirement item in achievement.m_statTrigger) { AchievementTarget obj = new AchievementTarget { Kind = TargetKind.Stat }; PlayerStatType stat = item.m_stat; obj.Key = ((object)(*(PlayerStatType*)(&stat))/*cast due to .constrained prefix*/).ToString(); obj.Stat = item.m_stat; obj.Required = item.m_amountAboveEquals; obj.Comparison = (RequirementOperator)0; list.Add(obj); } } if (achievement.m_enemyStatsTriggers != null) { foreach (EnemyStatRequirement enemyStatsTrigger in achievement.m_enemyStatsTriggers) { list.Add(new AchievementTarget { Kind = TargetKind.Enemy, Key = enemyStatsTrigger.m_stat, Required = enemyStatsTrigger.m_amount, Comparison = enemyStatsTrigger.m_operator, Modifier = (int)enemyStatsTrigger.m_modifier }); } } Add(list, achievement.m_foodEatenTriggers, TargetKind.Food); Add(list, achievement.m_itemCraftTriggers, TargetKind.Craft); Add(list, achievement.m_itemPickupTriggers, TargetKind.Pickup); Add(list, achievement.m_pickableTriggers, TargetKind.Pickable); Add(list, achievement.m_piecePlacedTriggers, TargetKind.Build); Add(list, achievement.m_knownCommandsTriggers, TargetKind.Command); Add(list, achievement.m_knownWorldKeysTriggers, TargetKind.WorldKey); Add(list, achievement.m_knownWorldTriggers, TargetKind.World); if (achievement.m_otherAchievementTriggers != null) { foreach (Achievement otherAchievementTrigger in achievement.m_otherAchievementTriggers) { if (Object.op_Implicit((Object)(object)otherAchievementTrigger)) { list.Add(new AchievementTarget { Kind = TargetKind.Achievement, Key = otherAchievementTrigger.m_id, Dependency = otherAchievementTrigger, Required = 1f, Comparison = (RequirementOperator)0 }); } } } foreach (AchievementTarget item2 in list) { item2.Refresh(stats); } return list; } private static void Add(List<AchievementTarget> result, List<DictStatRequirement> values, TargetKind kind) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: 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_002f: 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_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) if (values == null) { return; } foreach (DictStatRequirement value in values) { result.Add(new AchievementTarget { Kind = kind, Key = value.m_stat, Required = value.m_amount, Comparison = value.m_operator }); } } public static string KindName(TargetKind kind) { if (!Progress.Korean) { return kind.ToString(); } return kind switch { TargetKind.Stat => "통계", TargetKind.Enemy => "처치", TargetKind.Food => "섭취", TargetKind.Craft => "제작", TargetKind.Pickup => "획득", TargetKind.Pickable => "채집·낚시", TargetKind.Build => "건설", TargetKind.Command => "명령", TargetKind.WorldKey => "월드 조건", TargetKind.World => "월드", _ => "업적", }; } } public sealed class TargetInfo { public string Key; public string Name; public ItemData Item; public Sprite Icon; public GameObject PreviewPrefab; public int Biomes; public HashSet<string> Trophies; public HashSet<string> Baits; public bool IconRead; } public static class TargetCatalog { public const int Other = 1048576; public static readonly int[] Biomes = new int[12] { 1, 8, 2, 4, 16, 512, 32, 64, 256, 2097152, 1048576, 4194304 }; private static readonly Dictionary<string, TargetInfo> items = new Dictionary<string, TargetInfo>(StringComparer.Ordinal); private static readonly Dictionary<string, TargetInfo> creatures = new Dictionary<string, TargetInfo>(StringComparer.Ordinal); private static readonly Dictionary<string, TargetInfo> pieces = new Dictionary<string, TargetInfo>(StringComparer.Ordinal); private static readonly Dictionary<TargetInfo, List<Recipe>> recipes = new Dictionary<TargetInfo, List<Recipe>>(); private static readonly HashSet<TargetInfo> classified = new HashSet<TargetInfo>(); private static ObjectDB database; private static ZNetScene scene; private static ZoneSystem world; private static string language; private static int count; public static string Localize(string key) { if (string.IsNullOrEmpty(key)) { if (!Progress.Korean) { return "Unnamed"; } return "이름 미등록"; } Localization instance = Localization.instance; string text = ((instance != null) ? instance.Localize(key) : null); if (!string.IsNullOrEmpty(text) && !(text == key) && !(text == "[" + key.TrimStart(new char[1] { '$' }) + "]")) { return text; } return key.TrimStart(new char[1] { '$' }); } public static string BiomeName(int biome) { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) switch (biome) { case 4194304: if (!Progress.Korean) { return "Undiscovered"; } return "미확인"; case 0: case 1048576: if (!Progress.Korean) { return "Other / unclassified"; } return "기타·분류 없음"; case 2097152: if (!Progress.Korean) { return "Summoned / bred / built"; } return "소환·사육·설치"; default: { int num = Array.IndexOf(Biomes, biome); string[] array = new string[9] { "meadows", "blackforest", "swamp", "mountain", "plains", "mistlands", "ashlands", "deepnorth", "ocean" }; if (num >= 0 && num < array.Length) { return Localize("$biome_" + array[num]); } return Localize(((object)(Biome)biome/*cast due to .constrained prefix*/).ToString()); } } } public static string BiomeText(int mask) { return string.Join(" · ", Biomes.Where((int b) => b != 1048576 && (mask & b) != 0).Select(BiomeName).DefaultIfEmpty(BiomeName(1048576)) .ToArray()); } public static void Sync() { //IL_03bf: Unknown result type (might be due to invalid IL or missing references) //IL_03c9: Expected I4, but got Unknown ObjectDB instance = ObjectDB.instance; ZNetScene instance2 = ZNetScene.instance; ZoneSystem instance3 = ZoneSystem.instance; int num = (Object.op_Implicit((Object)(object)instance) ? (instance.m_recipes?.Count ?? 0) : 0); Localization instance4 = Localization.instance; string text = ((instance4 != null) ? instance4.GetSelectedLanguage() : null) + "/" + Progress.Korean; if (instance == database && instance2 == scene && instance3 == world && language == text && count == num) { CreatureCatalog.Enrich(creatures); TrophyBiomes.Apply(items, creatures.Values); return; } database = instance; scene = instance2; world = instance3; language = text; count = num; items.Clear(); creatures.Clear(); pieces.Clear(); recipes.Clear(); classified.Clear(); FishingCatalog.Reset(); if (Object.op_Implicit((Object)(object)instance) && instance.m_items != null) { foreach (GameObject item in instance.m_items) { if (!Object.op_Implicit((Object)(object)item)) { continue; } ItemData val = item.GetComponent<ItemDrop>()?.m_itemData; if (val?.m_shared != null && !string.IsNullOrEmpty(val.m_shared.m_name)) { if (!items.TryGetValue(val.m_shared.m_name, out var value)) { Dictionary<string, TargetInfo> dictionary = items; string name = val.m_shared.m_name; TargetInfo obj = new TargetInfo { Key = val.m_shared.m_name, Name = Localize(val.m_shared.m_name), Item = val }; value = obj; dictionary[name] = obj; } if (!items.ContainsKey(((Object)item).name)) { items[((Object)item).name] = value; } } } } if (Object.op_Implicit((Object)(object)instance2) && instance2.m_prefabs != null) { foreach (GameObject prefab in instance2.m_prefabs) { if (!Object.op_Implicit((Object)(object)prefab)) { continue; } CreatureCatalog.Add(creatures, prefab, 0); Piece component = prefab.GetComponent<Piece>(); if (Object.op_Implicit((Object)(object)component) && !string.IsNullOrEmpty(component.m_name)) { if (!pieces.ContainsKey(component.m_name)) { pieces[component.m_name] = new TargetInfo { Key = component.m_name, Name = Localize(component.m_name), Icon = component.m_icon }; } if (!string.IsNullOrEmpty(((Object)prefab).name) && !pieces.ContainsKey(((Object)prefab).name)) { pieces[((Object)prefab).name] = pieces[component.m_name]; } } } } SpawnSystemList[] array = Resources.FindObjectsOfTypeAll<SpawnSystemList>(); foreach (SpawnSystemList val2 in array) { if (!Object.op_Implicit((Object)(object)val2) || val2.m_spawners == null) { continue; } foreach (SpawnData spawner in val2.m_spawners) { if (!((Object)(object)spawner?.m_prefab == (Object)null) && spawner.m_enabled && !spawner.m_devDisabled) { CreatureCatalog.Add(creatures, spawner.m_prefab, (int)spawner.m_biome); } } } CreatureCatalog.Enrich(creatures); if (Object.op_Implicit((Object)(object)instance) && instance.m_recipes != null) { foreach (Recipe recipe in instance.m_recipes) { string text2 = ((!Object.op_Implicit((Object)(object)recipe) || !Object.op_Implicit((Object)(object)recipe.m_item)) ? null : recipe.m_item.m_itemData?.m_shared?.m_name); if (text2 != null && items.TryGetValue(text2, out var value2)) { if (!recipes.TryGetValue(value2, out var value3)) { value3 = (recipes[value2] = new List<Recipe>()); } value3.Add(recipe); } } } TrophyBiomes.Apply(items, creatures.Values); } public static Sprite IconFor(TargetInfo info, Achievement achievement) { if (Object.op_Implicit((Object)(object)info?.Icon)) { return info.Icon; } if (info?.Item != null && !info.IconRead) { info.IconRead = true; try { info.Icon = info.Item.GetIcon(); } catch { } if (Object.op_Implicit((Object)(object)info.Icon)) { return info.Icon; } } if (Object.op_Implicit((Object)(object)achievement) && Object.op_Implicit((Object)(object)achievement.m_icon)) { return achievement.m_icon; } if (!Object.op_Implicit((Object)(object)achievement)) { return null; } return achievement.m_iconLocked; } public static void Classify(TargetInfo info) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Invalid comparison between Unknown and I4 if (info == null) { return; } ItemData item = info.Item; if ((item != null && (int)(item.m_shared?.m_itemType).GetValueOrDefault() == 13) || !classified.Add(info) || !recipes.TryGetValue(info, out var value)) { return; } foreach (Recipe item2 in value) { int num = Biomes.Of(item2); if (num >= 0 && num <= 6) { info.Biomes |= Biomes[num]; } } } public static TargetInfo Resolve(AchievementTarget entry, bool classify = true) { if (entry.Kind == TargetKind.Stat) { return new TargetInfo { Key = entry.Key, Name = Localize("$stat_" + entry.Key) }; } if (entry.Kind == TargetKind.Achievement && Object.op_Implicit((Object)(object)entry.Dependency)) { return new TargetInfo { Key = entry.Key, Name = Localize(entry.Dependency.m_name), Icon = entry.Dependency.m_icon }; } if (entry.Kind == TargetKind.Pickable || entry.Kind == TargetKind.Pickup) { TargetInfo targetInfo = FishingCatalog.Resolve(entry.Key); if (targetInfo != null) { return targetInfo; } } Dictionary<string, TargetInfo> dictionary = ((entry.Kind == TargetKind.Enemy) ? creatures : ((entry.Kind == TargetKind.Build) ? pieces : items)); if (!string.IsNullOrEmpty(entry.Key) && dictionary.TryGetValue(entry.Key, out var value)) { if (classify) { Classify(value); } return value; } return new TargetInfo { Key = entry.Key, Name = Localize(entry.Key), Biomes = ((entry.Kind == TargetKind.Enemy) ? BiomeSupplement.For(null, entry.Key) : 0) }; } public static int Mask(TargetInfo info) { if (info.Biomes != 0) { return info.Biomes; } return 1048576; } public static void Reset() { CreaturePreviews.Clear(); items.Clear(); creatures.Clear(); pieces.Clear(); recipes.Clear(); classified.Clear(); FishingCatalog.Reset(); database = null; scene = null; world = null; language = null; count = -1; } } public static class AchievementDetails { private sealed class Context { public Transform Parent; public bool Active; } public static Func<bool> Enabled = () => true; public static Action<string> Timing = delegate { }; private static ConditionalWeakTable<AchievementsGui, Context> contexts = new ConditionalWeakTable<AchievementsGui, Context>(); private static FieldInfo nativeDialog; private static FieldInfo nativeGui; private static FieldInfo nativeApplied; private static DetailsWindow window; private static bool failed; public static bool PreviewActive { get { if (!failed && Enabled() && window != null && window.Opened) { return window.Valid; } return false; } } public static void Prepare(Type type) { nativeDialog = AccessTools.Field(type, "_dlg"); nativeGui = AccessTools.Field(type, "_gui"); nativeApplied = AccessTools.Field(type, "_applied"); if (nativeDialog?.FieldType != typeof(Dialog) || nativeDialog.IsStatic || nativeGui?.FieldType != typeof(InventoryGui) || nativeGui.IsStatic || nativeApplied?.FieldType != typeof(bool) || nativeApplied.IsStatic) { throw new NotSupportedException("SeneaL achievement window bindings changed."); } } public static void Capture(object __instance) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown if (failed) { return; } try { InventoryGui val = (InventoryGui)nativeGui.GetValue(__instance); Dialog val2 = (Dialog)nativeDialog.GetValue(__instance); if (Object.op_Implicit((Object)(object)val) && !((Object)(object)val2?.Root == (Object)null)) { Context value = contexts.GetValue(val.m_achievementsPanel, (AchievementsGui _) => new Context()); value.Parent = ((Transform)val2.Root).parent; value.Active = (bool)nativeApplied.GetValue(__instance); } } catch (Exception error) { Fail(error); } } public static bool Open(AchievementsGui __instance, Achievement __0, bool __1) { if (failed || !Enabled() || !Object.op_Implicit((Object)(object)__0) || !__1 || Progress.CurrentStats() == null || !contexts.TryGetValue(__instance, out var value) || !value.Active || !Object.op_Implicit((Object)(object)value.Parent)) { return true; } try { if (window != null && (Object)(object)window.Owner != (Object)(object)__instance) { window.Dispose(); window = null; } if (window == null) { window = new DetailsWindow(__instance, value.Parent); } window.Open(__0); return false; } catch (Exception error) { Fail(error); return true; } } public static void Closing(AchievementsGui __instance) { if ((Object)(object)window?.Owner == (Object)(object)__instance) { window.Close(); } } public static bool Navigate(AchievementsGui __instance) { if (!((Object)(object)window?.Owner != (Object)(object)__instance)) { return !window.Opened; } return true; } public static void Tick() { if (failed || window == null) { return; } try { if (!window.Valid) { window.Dispose(); window = null; TargetCatalog.Reset(); CraftingStations.Reset(); BuildingFilters.Reset(); } else { window.Tick(); } } catch (Exception error) { Fail(error); } } internal static void Fail(Exception error) { failed = true; window?.Dispose(); window = null; Progress.Warning("SeneaL achievement details disabled: " + (error.InnerException ?? error).Message); } public static void Reset() { window?.Dispose(); window = null; contexts = new ConditionalWeakTable<AchievementsGui, Context>(); failed = false; TargetCatalog.Reset(); CraftingStations.Reset(); BuildingFilters.Reset(); } } public sealed class DetailsWindow { private sealed class FilterStrip { private readonly ScrollBox scroll; private readonly string id; private readonly Action changed; private readonly Func<string, string> label; private ChipBar bar; private string[] keys = Array.Empty<string>(); public string Selected = ""; public bool Active { get { if (bar != null) { return keys.Length > 1; } return false; } } public FilterStrip(Transform parent, string id, Func<string, string> label, Action changed) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown this.id = id; this.label = label; this.changed = changed; scroll = new ScrollBox(parent, id, 1152f, 28f, 34f); scroll.SetActive(false); } public void Options(IEnumerable<string> options) { //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Expected O, but got Unknown //IL_0140: Unknown result type (might be due to invalid IL or missing references) if (bar != null) { ((Component)bar.Root).gameObject.SetActive(false); Object.Destroy((Object)(object)((Component)bar.Root).gameObject); bar = null; } keys = new string[1] { "" }.Concat(options.Distinct<string>(StringComparer.Ordinal).OrderBy<string, string>(label, StringComparer.CurrentCulture).ThenBy<string, string>((string value) => value, StringComparer.Ordinal)).ToArray(); if (Array.IndexOf(keys, Selected) < 0) { Selected = ""; } scroll.SetActive(keys.Length > 1); if (keys.Length > 1) {