Decompiled source of Achievement Hints SeneaL UI v1.6.4

plugins/AchievementHintsSeneaLUI/SeneaLAchievementHints.dll

Decompiled 8 hours ago
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)
				{