Decompiled source of WhereIsMyAmulet v1.0.4

WhereIsMyAmulet.dll

Decompiled a week 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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Peak;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("WhereIsMyAmulet")]
[assembly: AssemblyDescription("Shows the distance to dropped amulet fragments in PEAK.")]
[assembly: AssemblyProduct("WhereIsMyAmulet")]
[assembly: ComVisible(false)]
[assembly: Guid("ce0f5e2c-08a1-4f5f-8da4-a91c5c43a5a7")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.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 WhereIsMyAmulet
{
	internal enum AmuletLabelFont
	{
		Auto,
		GameDefault,
		TmpDefault,
		KoreanBinggrae,
		Crazk
	}
	internal static class FontHelper
	{
		public static TMP_FontAsset GetLabelFont(AmuletLabelFont choice)
		{
			string source;
			TMP_FontAsset val = GetFontForChoice(choice, out source);
			if ((Object)(object)val == (Object)null)
			{
				val = GetGameDefaultFont();
				source += " -> GameDefault fallback";
			}
			return val;
		}

		private static TMP_FontAsset GetFontForChoice(AmuletLabelFont choice, out string source)
		{
			switch (choice)
			{
			case AmuletLabelFont.TmpDefault:
				source = "TmpDefault";
				return TMP_Settings.defaultFontAsset;
			case AmuletLabelFont.KoreanBinggrae:
				source = "KoreanBinggrae";
				return FindFontAsset("Korean Binggrae-Bold SDF");
			case AmuletLabelFont.Crazk:
				source = "Crazk";
				return FindFontAsset("CRAZK___ SDF");
			case AmuletLabelFont.Auto:
				source = "Auto(GameDefault)";
				return GetGameDefaultFont() ?? TMP_Settings.defaultFontAsset;
			default:
				source = "GameDefault";
				return GetGameDefaultFont();
			}
		}

		private static TMP_FontAsset GetGameDefaultFont()
		{
			if (!((Object)(object)FontFallbackSwapper.instance == (Object)null))
			{
				return FontFallbackSwapper.instance.mainBaseFont;
			}
			return null;
		}

		private static TMP_FontAsset FindFontAsset(string assetName)
		{
			IEnumerable<TMP_FontAsset> first = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			IEnumerable<TMP_FontAsset> second;
			try
			{
				second = Resources.LoadAll<TMP_FontAsset>(string.Empty);
			}
			catch
			{
				second = Enumerable.Empty<TMP_FontAsset>();
			}
			return (from font in first.Concat(second)
				where (Object)(object)font != (Object)null
				select font).Distinct().FirstOrDefault((Func<TMP_FontAsset, bool>)((TMP_FontAsset font) => string.Equals(NormalizeFontName(((Object)font).name), NormalizeFontName(assetName), StringComparison.OrdinalIgnoreCase)));
		}

		private static string NormalizeFontName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return string.Empty;
			}
			int num = name.IndexOf(" (Clone)", StringComparison.OrdinalIgnoreCase);
			return ((num < 0) ? name : name.Substring(0, num)).Trim();
		}
	}
	internal static class ModConfigLocalization
	{
		private const string ConfigFileName = "com.wuyachiyu.WhereIsMyAmulet.cfg";

		private static readonly FieldInfo DescriptionBackingField = typeof(ConfigDescription).GetField("<Description>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);

		private static bool IsChinese
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Invalid comparison between Unknown and I4
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Invalid comparison between Unknown and I4
				if ((int)LocalizedText.CURRENT_LANGUAGE != 9)
				{
					return (int)LocalizedText.CURRENT_LANGUAGE == 10;
				}
				return true;
			}
		}

		public static void PatchDisplayNames(Harmony harmony)
		{
			ModConfigUiAdapter.Install(harmony, "com.wuyachiyu.WhereIsMyAmulet.cfg", "WhereIsMyAmulet", (ConfigEntryBase entry) => GetLocalizedConfigText(entry.Definition.Section, entry.Definition.Key), GetLocalizedToken, delegate(string message)
			{
				Debug.LogWarning((object)("[WhereIsMyAmulet] " + message));
			});
		}

		public static void RefreshVisibleUi()
		{
			ModConfigUiAdapter.RefreshVisibleUi();
		}

		public static void Shutdown()
		{
			ModConfigUiAdapter.Shutdown();
		}

		public static void ApplyLocalizedDescriptions(IEnumerable<ConfigEntryBase> entries)
		{
			if (entries == null)
			{
				return;
			}
			foreach (ConfigEntryBase item in entries.Where((ConfigEntryBase value) => value != null))
			{
				SetDescription(item, GetLocalizedDescription(item.Definition.Section, item.Definition.Key));
			}
		}

		private static string GetLocalizedDescription(string section, string key)
		{
			return GetDescription(section, key, IsChinese);
		}

		private static string GetLocalizedConfigText(string section, string key)
		{
			if (!IsKnownConfigKey(section, key))
			{
				return null;
			}
			return GetLocalizedToken(key);
		}

		private static bool IsKnownConfigKey(string section, string key)
		{
			if (section == "General")
			{
				switch (key)
				{
				default:
					return key == "DisplayDurationSeconds";
				case "Enabled":
				case "ScanKey":
				case "ScanMode":
					return true;
				}
			}
			if (section == "Display")
			{
				switch (key)
				{
				default:
					return key == "ShowOffscreenDirection";
				case "MaxDistance":
				case "FontSize":
				case "LabelFont":
				case "ShowStatueFragments":
					return true;
				}
			}
			return false;
		}

		private static string GetDescription(string section, string key, bool chinese)
		{
			if (section == "General")
			{
				switch (key)
				{
				case "Enabled":
					if (!chinese)
					{
						return "Enable or disable WhereIsMyAmulet.";
					}
					return "启用或关闭 WhereIsMyAmulet。";
				case "ScanKey":
					if (!chinese)
					{
						return "Key used to scan dropped amulets and statue fragments.";
					}
					return "扫描掉落护符和雕像碎片的快捷键。";
				case "ScanMode":
					if (!chinese)
					{
						return "Keep labels visible, or hide them automatically after the configured duration.";
					}
					return "选择标签常驻显示,或在扫描后按设定时间自动隐藏。";
				case "DisplayDurationSeconds":
					if (!chinese)
					{
						return "Number of seconds labels remain visible in Timed mode.";
					}
					return "定时模式下标签持续显示的秒数。";
				}
			}
			if (section == "Display")
			{
				switch (key)
				{
				case "MaxDistance":
					if (!chinese)
					{
						return "Maximum label distance in metres. Set to 0 for unlimited.";
					}
					return "标签显示的最大距离,单位米。0 表示不限制。";
				case "FontSize":
					if (!chinese)
					{
						return "Font size used by location labels.";
					}
					return "位置标签的字号。";
				case "LabelFont":
					if (!chinese)
					{
						return "Choose a font for English location labels only. Chinese text may display as tofu boxes.";
					}
					return "选择仅用于英文位置标签的字体;中文可能显示为口口口。";
				case "ShowStatueFragments":
					if (!chinese)
					{
						return "Show amulet fragments held by amulet statues and fragments mounted on Scout Statues.";
					}
					return "显示场景雕像手中的护符碎片,以及祭坛上已经镶嵌的护符碎片。";
				case "ShowOffscreenDirection":
					if (!chinese)
					{
						return "Show a direction indicator when a target is off-screen.";
					}
					return "目标在屏幕外时显示方向提示。";
				}
			}
			return string.Empty;
		}

		private static string GetLocalizedToken(string key)
		{
			bool isChinese = IsChinese;
			switch (key)
			{
			case "WhereIsMyAmulet":
				if (!isChinese)
				{
					return "WhereIsMyAmulet";
				}
				return "护符在哪里";
			case "General":
				if (!isChinese)
				{
					return "General";
				}
				return "常规";
			case "Display":
				if (!isChinese)
				{
					return "Display";
				}
				return "显示";
			case "Enabled":
				if (!isChinese)
				{
					return "Enabled";
				}
				return "启用 MOD";
			case "ScanKey":
				if (!isChinese)
				{
					return "Scan Key";
				}
				return "扫描快捷键";
			case "ScanMode":
				if (!isChinese)
				{
					return "Scan Mode";
				}
				return "显示模式";
			case "DisplayDurationSeconds":
				if (!isChinese)
				{
					return "Display Duration";
				}
				return "定时显示秒数";
			case "MaxDistance":
				if (!isChinese)
				{
					return "Max Distance";
				}
				return "最大距离";
			case "FontSize":
				if (!isChinese)
				{
					return "Label Font Size";
				}
				return "标签字号";
			case "LabelFont":
				if (!isChinese)
				{
					return "Label Font";
				}
				return "标签字体";
			case "ShowStatueFragments":
				if (!isChinese)
				{
					return "Show Statue Fragments";
				}
				return "显示雕像碎片";
			case "ShowOffscreenDirection":
				if (!isChinese)
				{
					return "Off-screen Direction";
				}
				return "屏外方向提示";
			case "Auto":
				if (!isChinese)
				{
					return "Auto";
				}
				return "自动";
			case "GameDefault":
				if (!isChinese)
				{
					return "Game Default";
				}
				return "游戏默认";
			case "TmpDefault":
				if (!isChinese)
				{
					return "TMP Default";
				}
				return "TMP 默认字体";
			case "Persistent":
				if (!isChinese)
				{
					return "Persistent";
				}
				return "常驻";
			case "Timed":
				if (!isChinese)
				{
					return "Timed";
				}
				return "定时";
			case "KoreanBinggrae":
				if (!isChinese)
				{
					return "KoreanBinggrae";
				}
				return "KoreanBinggrae";
			case "Crazk":
				if (!isChinese)
				{
					return "Crazk";
				}
				return "Crazk";
			default:
				return null;
			}
		}

		private static void SetDescription(ConfigEntryBase entry, string text)
		{
			if (!ModConfigUiAdapter.Owns(entry, "com.wuyachiyu.WhereIsMyAmulet.cfg") || entry.Description == null || DescriptionBackingField == null || string.IsNullOrEmpty(text))
			{
				return;
			}
			try
			{
				DescriptionBackingField.SetValue(entry.Description, text);
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("[WhereIsMyAmulet] Description localization skipped: " + ex.Message));
			}
		}
	}
	internal static class ModConfigUiAdapter
	{
		internal sealed class ActionFieldSubscription : IDisposable
		{
			private FieldInfo _field;

			private Action _handler;

			internal void Subscribe(FieldInfo field, Action handler)
			{
				if (!(field == null) && !(field.FieldType != typeof(Action)) && field.IsStatic && handler != null && (!(_field == field) || _handler != handler))
				{
					Dispose();
					field.SetValue(null, (Action)Delegate.Combine((Action)field.GetValue(null), handler));
					_field = field;
					_handler = handler;
				}
			}

			public void Dispose()
			{
				if (_field != null)
				{
					_field.SetValue(null, (Action)Delegate.Remove((Action)_field.GetValue(null), _handler));
				}
				_field = null;
				_handler = null;
			}
		}

		private const string Guid = "com.github.PEAKModding.PEAKLib.ModConfig";

		private const BindingFlags Members = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static readonly HashSet<MethodBase> Patched = new HashSet<MethodBase>();

		private static readonly HashSet<string> Warnings = new HashSet<string>();

		private static readonly Dictionary<Tuple<Type, string>, MemberInfo> MemberCache = new Dictionary<Tuple<Type, string>, MemberInfo>();

		private static string _fileName;

		private static string _modName;

		private static Func<ConfigEntryBase, string> _name;

		private static Func<string, string> _token;

		private static Action<string> _warn;

		private static Type _inputCell;

		private static Type _row;

		private static Type _tab;

		private static MonoBehaviour _menu;

		private static Coroutine _refresh;

		internal static void Install(Harmony harmony, string fileName, string modName, Func<ConfigEntryBase, string> name, Func<string, string> token, Action<string> warn)
		{
			_fileName = fileName;
			_modName = modName;
			_name = name;
			_token = token;
			_warn = warn;
			if (harmony == null || !Chainloader.PluginInfos.TryGetValue("com.github.PEAKModding.PEAKLib.ModConfig", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null)
			{
				return;
			}
			Assembly assembly = ((object)value.Instance).GetType().Assembly;
			foreach (Type item in from t in assembly.GetTypes()
				where t.FullName != null && t.FullName.StartsWith("PEAKLib.ModConfig.SettingOptions.BepInEx", StringComparison.Ordinal) && !t.ContainsGenericParameters && !t.IsAbstract
				select t)
			{
				Patch(harmony, DeclaredImplementation(item, "GetDisplayName", Type.EmptyTypes), "DisplayNamePostfix");
			}
			Type type = assembly.GetType("PEAKLib.ModConfig.Components.ModSettingsMenu") ?? assembly.GetType("PEAKLib.ModConfig.Components.ModdedSettingsMenu");
			if (type == null)
			{
				Warn("menu", "Unsupported ModConfig menu type.");
				return;
			}
			string[] array = new string[4] { "OnEnable", "ShowSettings", "SetSection", "UpdateSectionTabs" };
			foreach (string text in array)
			{
				Patch(harmony, DeclaredImplementation(type, text, (!(text == "SetSection") && !(text == "UpdateSectionTabs")) ? Type.EmptyTypes : new Type[1] { typeof(string) }), "MenuPostfix");
			}
			array = new string[2] { "OnDisable", "OnDestroy" };
			foreach (string name2 in array)
			{
				MethodInfo methodInfo = DeclaredImplementation(type, name2, Type.EmptyTypes);
				if (methodInfo != null)
				{
					Patch(harmony, methodInfo, "MenuClosedPostfix");
				}
			}
			_inputCell = AccessTools.TypeByName("Zorro.Settings.SettingInputUICell");
			_row = AccessTools.TypeByName("SettingsUICell");
			_tab = assembly.GetType("PEAKLib.ModConfig.Components.ModdedTABSButton");
			Type type2 = AccessTools.TypeByName("Zorro.Settings.UI.EnumSettingUI");
			Patch(harmony, DeclaredImplementation(type2, "RefreshText", Type.EmptyTypes), "ControlPostfix");
			foreach (Type item2 in from t in assembly.GetTypes()
				where _inputCell != null && _inputCell.IsAssignableFrom(t) && !t.IsAbstract
				select t)
			{
				foreach (MethodInfo item3 in from m in item2.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
					where m.Name == "Setup" && m.GetParameters().Length == 2
					select m)
				{
					Patch(harmony, item3, "ControlPostfix");
				}
			}
		}

		internal static MethodInfo DeclaredImplementation(Type type, string name, Type[] parameters)
		{
			while (type != null)
			{
				MethodInfo method = type.GetMethod(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameters, null);
				if (method != null)
				{
					if (!method.ContainsGenericParameters && !method.IsAbstract)
					{
						return method;
					}
					return null;
				}
				type = type.BaseType;
			}
			return null;
		}

		private static void Patch(Harmony harmony, MethodInfo method, string callback)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			if (method == null)
			{
				Warn(callback, "A ModConfig hook is unavailable: " + callback);
			}
			else if (!Patched.Contains(method))
			{
				try
				{
					harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(typeof(ModConfigUiAdapter), callback, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Patched.Add(method);
				}
				catch (Exception ex)
				{
					Warn(method.ToString(), "ModConfig hook skipped: " + method?.ToString() + ": " + ex.Message);
				}
			}
		}

		internal static bool Owns(ConfigEntryBase entry, string fileName)
		{
			if (((entry != null) ? entry.ConfigFile : null) != null && !string.IsNullOrEmpty(fileName))
			{
				return string.Equals(Path.GetFileName(entry.ConfigFile.ConfigFilePath), fileName, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		internal static ConfigEntryBase Entry(object setting)
		{
			if (setting == null)
			{
				return null;
			}
			Type[] interfaces = setting.GetType().GetInterfaces();
			for (int i = 0; i < interfaces.Length; i++)
			{
				PropertyInfo property = interfaces[i].GetProperty("ConfigBase", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null && typeof(ConfigEntryBase).IsAssignableFrom(property.PropertyType))
				{
					object? value = property.GetValue(setting, null);
					return (ConfigEntryBase)((value is ConfigEntryBase) ? value : null);
				}
			}
			object obj = Read(setting, "ConfigBase");
			return (ConfigEntryBase)((obj is ConfigEntryBase) ? obj : null);
		}

		private static void DisplayNamePostfix(object __instance, ref string __result)
		{
			try
			{
				ConfigEntryBase val = Entry(__instance);
				if (Owns(val, _fileName))
				{
					string text = _name(val);
					if (!string.IsNullOrEmpty(text))
					{
						__result = text;
					}
				}
			}
			catch (Exception ex)
			{
				Warn("name", ex.Message);
			}
		}

		private static void MenuPostfix(MonoBehaviour __instance, MethodBase __originalMethod)
		{
			if ((Object)(object)_menu != (Object)(object)__instance || __originalMethod.Name == "OnEnable")
			{
				StopRefresh();
			}
			_menu = __instance;
			RefreshVisibleUi();
		}

		private static void MenuClosedPostfix(MonoBehaviour __instance)
		{
			if (!((Object)(object)_menu != (Object)(object)__instance))
			{
				StopRefresh();
				_menu = null;
			}
		}

		internal static void RefreshVisibleUi()
		{
			if ((Object)(object)_menu == (Object)null || !((Behaviour)_menu).isActiveAndEnabled)
			{
				StopRefresh();
				_menu = null;
			}
			else if (_refresh == null)
			{
				_refresh = _menu.StartCoroutine(DeferredRefresh(_menu));
			}
		}

		private static IEnumerator DeferredRefresh(MonoBehaviour owner)
		{
			yield return null;
			try
			{
				if (!((Object)(object)owner == (Object)null) && !((Object)(object)owner != (Object)(object)_menu) && ((Behaviour)owner).isActiveAndEnabled)
				{
					RefreshMenu(owner);
				}
			}
			finally
			{
				if ((Object)(object)owner == (Object)(object)_menu)
				{
					_refresh = null;
				}
			}
		}

		private static void RefreshMenu(MonoBehaviour owner)
		{
			try
			{
				RefreshTabs(owner, "ModTabController", sections: false);
				if (SameMod(Read(owner, "selectedMod") as string, _modName))
				{
					RefreshTabs(owner, "SectionTabController", sections: true);
				}
				object obj = Read(owner, "Content");
				Transform val = (Transform)((obj is Transform) ? obj : null);
				if (!((Object)(object)val == (Object)null) && !(_inputCell == null))
				{
					Component[] componentsInChildren = ((Component)val).GetComponentsInChildren(_inputCell, false);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						RefreshControl(componentsInChildren[i]);
					}
				}
			}
			catch (Exception ex)
			{
				Warn("menu-refresh", ex.Message);
			}
		}

		internal static bool SameMod(string category, string modName)
		{
			if (!string.IsNullOrEmpty(category) && !string.IsNullOrEmpty(modName))
			{
				return string.Equals(category.Replace(" ", ""), modName.Replace(" ", ""), StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static void RefreshTabs(MonoBehaviour menu, string controllerName, bool sections)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			try
			{
				if (_tab == null || !(Read(Read(menu, controllerName), "Tabs") is IEnumerable enumerable))
				{
					return;
				}
				foreach (GameObject item in enumerable)
				{
					GameObject val = item;
					if (!((Object)(object)val == (Object)null))
					{
						Component component = val.GetComponent(_tab);
						string text = Read(component, "category") as string;
						if (sections || SameMod(text, _modName))
						{
							SetText(Read(component, "text"), _token(sections ? text : _modName));
						}
					}
				}
			}
			catch (Exception ex)
			{
				Warn("tabs", ex.Message);
			}
		}

		private static void ControlPostfix(Component __instance)
		{
			RefreshControl(__instance);
		}

		private static void RefreshControl(Component control)
		{
			try
			{
				if ((Object)(object)control == (Object)null || !control.gameObject.activeInHierarchy)
				{
					return;
				}
				object obj = Read(control, "KeySetting") ?? Read(control, "_listeningSetting");
				ConfigEntryBase val = Entry(obj);
				if (!Owns(val, _fileName))
				{
					return;
				}
				if (_row != null)
				{
					Component componentInParent = control.GetComponentInParent(_row);
					if ((Object)(object)componentInParent != (Object)null)
					{
						string text = _name(val);
						if (!string.IsNullOrEmpty(text))
						{
							object obj2 = Read(componentInParent, "localizedText");
							if (obj2 != null)
							{
								Write(obj2, "autoSet", false);
							}
							SetText(Read(componentInParent, "m_text"), text);
						}
					}
				}
				if (!val.SettingType.IsEnum && !(val.Description.AcceptableValues is AcceptableValueList<string>))
				{
					return;
				}
				object obj3 = Read(control, "dropdown");
				if (obj3 == null || !(Read(obj3, "options") is IList list) || !(DeclaredImplementation(obj.GetType(), "GetUnlocalizedChoices", Type.EmptyTypes)?.Invoke(obj, null) is IEnumerable<string> source))
				{
					return;
				}
				string[] array = source.ToArray();
				if (array.Length != list.Count)
				{
					return;
				}
				ApplyOptionText(list, array, _token);
				DeclaredImplementation(obj3.GetType(), "RefreshShownValue", Type.EmptyTypes)?.Invoke(obj3, null);
				if (Read(obj3, "m_Items") is IList list2 && list2.Count == array.Length)
				{
					for (int i = 0; i < list2.Count; i++)
					{
						SetText(Read(list2[i], "text"), Read(list[i], "text") as string);
					}
				}
			}
			catch (Exception ex)
			{
				Warn("control", ex.Message);
			}
		}

		internal static void ApplyOptionText(IList options, string[] raw, Func<string, string> translate)
		{
			if (options != null && raw != null && options.Count == raw.Length)
			{
				for (int i = 0; i < raw.Length; i++)
				{
					SetText(options[i], translate(raw[i]) ?? raw[i]);
				}
			}
		}

		private static void SetText(object target, string text)
		{
			if (target != null && !string.IsNullOrEmpty(text) && !object.Equals(Read(target, "text"), text))
			{
				Write(target, "text", text);
			}
		}

		private static MemberInfo Member(Type type, string name)
		{
			Tuple<Type, string> key = Tuple.Create(type, name);
			if (MemberCache.TryGetValue(key, out var value))
			{
				return value;
			}
			Type type2 = type;
			while (type2 != null)
			{
				value = (MemberInfo)(((object)type2.GetProperty(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)) ?? ((object)type2.GetField(name, BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)));
				if (value != null)
				{
					break;
				}
				type2 = type2.BaseType;
			}
			MemberCache[key] = value;
			return value;
		}

		private static object Read(object target, string name)
		{
			if (target == null)
			{
				return null;
			}
			MemberInfo memberInfo = Member(target.GetType(), name);
			if (!(memberInfo is PropertyInfo propertyInfo))
			{
				return (memberInfo as FieldInfo)?.GetValue(target);
			}
			return propertyInfo.GetValue(target, null);
		}

		private static void Write(object target, string name, object value)
		{
			MemberInfo memberInfo = Member(target.GetType(), name);
			if (memberInfo is PropertyInfo propertyInfo)
			{
				propertyInfo.SetValue(target, value, null);
			}
			else if (memberInfo is FieldInfo fieldInfo)
			{
				fieldInfo.SetValue(target, value);
			}
		}

		private static void StopRefresh()
		{
			if (_refresh != null && _menu != null && (Object)(object)_menu != (Object)null)
			{
				_menu.StopCoroutine(_refresh);
			}
			_refresh = null;
		}

		internal static void Shutdown()
		{
			StopRefresh();
			_menu = null;
			Patched.Clear();
			Warnings.Clear();
			MemberCache.Clear();
			_name = null;
			_token = null;
			_warn = null;
		}

		private static void Warn(string key, string message)
		{
			if (Warnings.Add(key))
			{
				_warn?.Invoke("ModConfig localization: " + message);
			}
		}
	}
	internal enum AmuletDisplayMode
	{
		Persistent,
		Timed
	}
	[BepInPlugin("com.wuyachiyu.WhereIsMyAmulet", "WhereIsMyAmulet", "1.0.4")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class WhereIsMyAmuletPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.wuyachiyu.WhereIsMyAmulet";

		public const string PluginName = "WhereIsMyAmulet";

		public const string PluginVersion = "1.0.4";

		private readonly Dictionary<string, AmuletLabel> _labels = new Dictionary<string, AmuletLabel>();

		private readonly Dictionary<int, Item> _amuletDefinitions = new Dictionary<int, Item>();

		private readonly List<string> _labelsToRemove = new List<string>();

		private static readonly FieldInfo ScoutStatueAmuletsField = typeof(ScoutStatue).GetField("hasAmulets", BindingFlags.Instance | BindingFlags.NonPublic);

		private ManualLogSource _log;

		private Canvas _canvas;

		private TMP_FontAsset _font;

		private Material _labelTitleMaterial;

		private Material _labelDetailMaterial;

		private Camera _mainCamera;

		private Harmony _harmony;

		private bool _modConfigRefreshScheduled;

		private AmuletLabelFont _lastFontChoice;

		private float _lastFontSize;

		private float _hideAt = float.PositiveInfinity;

		private ConfigEntry<bool> _enabled;

		private ConfigEntry<KeyCode> _scanKey;

		private ConfigEntry<AmuletDisplayMode> _scanMode;

		private ConfigEntry<float> _displayDuration;

		private ConfigEntry<float> _maxDistance;

		private ConfigEntry<float> _fontSize;

		private ConfigEntry<AmuletLabelFont> _fontChoice;

		private ConfigEntry<bool> _showStatueFragments;

		private ConfigEntry<bool> _showOffscreen;

		private void Awake()
		{
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Expected O, but got Unknown
			_log = ((BaseUnityPlugin)this).Logger;
			_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Show labels for dropped amulet fragments.");
			_scanKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ScanKey", (KeyCode)99, "Press this key to scan for dropped amulet fragments.");
			_scanMode = ((BaseUnityPlugin)this).Config.Bind<AmuletDisplayMode>("General", "ScanMode", AmuletDisplayMode.Persistent, "Keep labels visible or hide them automatically after a timed scan.");
			_displayDuration = ((BaseUnityPlugin)this).Config.Bind<float>("General", "DisplayDurationSeconds", 8f, "Timed display duration in seconds.");
			_maxDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "MaxDistance", 500f, "Maximum distance in metres for a label. Set to 0 for unlimited.");
			_fontSize = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "FontSize", 22f, "Distance label font size.");
			_fontChoice = ((BaseUnityPlugin)this).Config.Bind<AmuletLabelFont>("Display", "LabelFont", AmuletLabelFont.GameDefault, "Font used by English location labels. Chinese text may display as tofu boxes.");
			_showStatueFragments = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowStatueFragments", true, "Show amulet fragments held by amulet statues.");
			_showOffscreen = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowOffscreenDirection", true, "Show a direction marker on the screen edge for offscreen fragments.");
			_lastFontChoice = _fontChoice.Value;
			_lastFontSize = _fontSize.Value;
			ModConfigLocalization.ApplyLocalizedDescriptions(GetConfigEntries());
			_harmony = new Harmony("com.wuyachiyu.WhereIsMyAmulet.ModConfigLocalization");
			try
			{
				ModConfigLocalization.PatchDisplayNames(_harmony);
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("ModConfig localization patch skipped: " + ex.Message));
			}
			LocalizedText.OnLangugageChanged = (Action)Delegate.Combine(LocalizedText.OnLangugageChanged, new Action(OnGameLanguageChanged));
			SceneManager.sceneLoaded += OnSceneLoaded;
			CreateCanvas();
		}

		private void Start()
		{
			ScheduleModConfigRefresh();
		}

		private void Update()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (!_enabled.Value)
			{
				ClearLabels();
				return;
			}
			UpdateLabelStyleIfChanged();
			if (Input.GetKeyDown(_scanKey.Value))
			{
				ScanItems();
			}
			if (_scanMode.Value == AmuletDisplayMode.Timed && Time.unscaledTime >= _hideAt)
			{
				ClearLabels();
				return;
			}
			if ((Object)(object)_mainCamera == (Object)null || !((Behaviour)_mainCamera).isActiveAndEnabled)
			{
				_mainCamera = Camera.main;
			}
			_labelsToRemove.Clear();
			foreach (AmuletLabel value in _labels.Values)
			{
				if (value.IsValid)
				{
					value.Update(_mainCamera, _maxDistance.Value, _showOffscreen.Value);
				}
				else
				{
					_labelsToRemove.Add(value.InstanceId);
				}
			}
			RemovePendingLabels();
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			LocalizedText.OnLangugageChanged = (Action)Delegate.Remove(LocalizedText.OnLangugageChanged, new Action(OnGameLanguageChanged));
			ModConfigLocalization.Shutdown();
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
			ClearLabels();
			ReleaseLabelMaterials();
			if ((Object)(object)_canvas != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_canvas).gameObject);
			}
		}

		private void CreateCanvas()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("WhereIsMyAmuletCanvas");
			Object.DontDestroyOnLoad((Object)(object)val);
			val.layer = 5;
			_canvas = val.AddComponent<Canvas>();
			_canvas.renderMode = (RenderMode)0;
			_canvas.sortingOrder = 20000;
			CanvasScaler obj = val.AddComponent<CanvasScaler>();
			obj.uiScaleMode = (ScaleMode)1;
			obj.referenceResolution = new Vector2(1920f, 1080f);
			_font = null;
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			ClearLabels();
			_amuletDefinitions.Clear();
			_font = null;
			ReleaseLabelMaterials();
			_mainCamera = null;
		}

		private void ScanItems()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			if (!EnsureFont(logResolution: true))
			{
				return;
			}
			RefreshAmuletDefinitions();
			Item[] array = Object.FindObjectsByType<Item>((FindObjectsSortMode)0);
			HashSet<string> hashSet = new HashSet<string>();
			Item[] array2 = array;
			foreach (Item val in array2)
			{
				if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy || (int)val.itemState != 0)
				{
					continue;
				}
				if (TryGetAmuletIndex(val, out var _))
				{
					string text = "item:" + ((Object)((Component)val).gameObject).GetInstanceID();
					hashSet.Add(text);
					Item captured = val;
					AddLabel(text, captured.transform, () => GetItemName(captured), () => (Object)(object)captured != (Object)null && ((Component)captured).gameObject.activeInHierarchy && (int)captured.itemState == 0 && TryGetAmuletIndex(captured, out var _));
				}
				Backpack val2 = (Backpack)(object)((val is Backpack) ? val : null);
				if ((Object)(object)val2 != (Object)null && HasAmulet(((Item)val2).data))
				{
					string text2 = "backpack:" + ((Object)((Component)val2).gameObject).GetInstanceID();
					hashSet.Add(text2);
					Backpack captured2 = val2;
					AddLabel(text2, ((Item)captured2).transform, () => GetBackpackTitle(((Item)captured2).data), () => (Object)(object)captured2 != (Object)null && ((Component)captured2).gameObject.activeInHierarchy && (int)((Item)captured2).itemState == 0 && HasAmulet(((Item)captured2).data));
				}
			}
			if (_showStatueFragments.Value)
			{
				PropSpawner_AmuletStatues[] array3 = Resources.FindObjectsOfTypeAll<PropSpawner_AmuletStatues>();
				HashSet<int> hashSet2 = new HashSet<int>();
				PropSpawner_AmuletStatues[] array4 = array3;
				foreach (PropSpawner_AmuletStatues val3 in array4)
				{
					if ((Object)(object)val3 == (Object)null)
					{
						continue;
					}
					Scene scene = ((Component)val3).gameObject.scene;
					if (!((Scene)(ref scene)).IsValid() || !((Component)val3).gameObject.activeInHierarchy || !hashSet2.Add(((Object)((Component)val3).gameObject).GetInstanceID()) || ((Component)val3).transform.childCount == 0)
					{
						continue;
					}
					GameObject gameObject = ((Component)((Component)val3).transform.GetChild(0)).gameObject;
					if ((Object)(object)gameObject == (Object)null || !gameObject.activeInHierarchy)
					{
						continue;
					}
					FakeItem val4 = FindAmuletStatueFakeItem(gameObject);
					if (!((Object)(object)val4 == (Object)null))
					{
						string text3 = "amulet-statue:" + ((Object)((Component)val3).gameObject).GetInstanceID();
						hashSet.Add(text3);
						PropSpawner_AmuletStatues capturedStatue = val3;
						GameObject capturedStatueObject = gameObject;
						FakeItem capturedStatueFragment = val4;
						Transform capturedTarget;
						AddLabel(text3, capturedTarget = ((Component)capturedStatueFragment).transform, () => GetAmuletStatueTitle(capturedStatueFragment), () => IsAmuletStatueValid(capturedStatue, capturedStatueObject, capturedTarget, capturedStatueFragment));
					}
				}
				ScanScoutStatues(hashSet);
			}
			_labelsToRemove.Clear();
			foreach (string key in _labels.Keys)
			{
				if (!hashSet.Contains(key))
				{
					_labelsToRemove.Add(key);
				}
			}
			RemovePendingLabels();
			_hideAt = ((_scanMode.Value == AmuletDisplayMode.Timed) ? (Time.unscaledTime + Mathf.Max(0.5f, _displayDuration.Value)) : float.PositiveInfinity);
		}

		private static Transform FindAmuletStatueTarget(Transform root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return null;
			}
			Queue<Transform> queue = new Queue<Transform>();
			queue.Enqueue(root);
			while (queue.Count > 0)
			{
				Transform val = queue.Dequeue();
				string text = ((Object)((Component)val).gameObject).name.ToLowerInvariant();
				if (text.Contains("amulet") || text.Contains("gem") || text.Contains("fragment") || text.Contains("shard"))
				{
					return val;
				}
				for (int i = 0; i < val.childCount; i++)
				{
					queue.Enqueue(val.GetChild(i));
				}
			}
			return root;
		}

		private string GetAmuletStatueTitle(FakeItem statueFragment)
		{
			if ((Object)(object)statueFragment != (Object)null && TryGetAmuletIndex(statueFragment.realItemPrefab, out var index))
			{
				return GetStatueAmuletTitle(index);
			}
			if (!IsChineseLanguage())
			{
				return "Statue Fragment\nOn Statue";
			}
			return "雕像碎片\n雕像上";
		}

		private string GetStatueAmuletTitle(int index)
		{
			string amuletName = GetAmuletName(index);
			if (!IsChineseLanguage())
			{
				return amuletName + "\nOn Statue";
			}
			return amuletName + "\n雕像上";
		}

		private static FakeItem FindAmuletStatueFakeItem(GameObject statueObject)
		{
			if ((Object)(object)statueObject == (Object)null)
			{
				return null;
			}
			FakeItem val = null;
			FakeItem[] componentsInChildren = statueObject.GetComponentsInChildren<FakeItem>(true);
			FakeItem[] array = componentsInChildren;
			foreach (FakeItem val2 in array)
			{
				if (!((Object)(object)val2 == (Object)null) && ((Component)val2).gameObject.activeInHierarchy)
				{
					if ((Object)(object)val == (Object)null)
					{
						val = val2;
					}
					if (TryGetAmuletIndex(val2.realItemPrefab, out var _))
					{
						return val2;
					}
				}
			}
			if (componentsInChildren.Length != 1)
			{
				return null;
			}
			return val;
		}

		private static bool IsAmuletStatueValid(PropSpawner_AmuletStatues statue, GameObject statueObject, Transform statueTarget, FakeItem statueFragment)
		{
			if ((Object)(object)statue != (Object)null && ((Component)statue).gameObject.activeInHierarchy && ((Component)statue).transform.childCount > 0 && (Object)(object)statueObject != (Object)null && statueObject.activeInHierarchy && (Object)(object)((Component)((Component)statue).transform.GetChild(0)).gameObject == (Object)(object)statueObject)
			{
				return IsFakeStatueFragmentVisible(statueTarget, statueFragment);
			}
			return false;
		}

		private static bool IsFakeStatueFragmentVisible(Transform fragmentTarget, FakeItem fragmentItem)
		{
			if ((Object)(object)fragmentTarget != (Object)null && (Object)(object)fragmentItem != (Object)null && ((Component)fragmentTarget).gameObject.activeInHierarchy && ((Component)fragmentItem).gameObject.activeInHierarchy)
			{
				return !fragmentItem.pickedUp;
			}
			return false;
		}

		private static bool IsStatueFragmentVisible(Transform fragmentTarget, Item fragmentItem)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Invalid comparison between Unknown and I4
			if ((Object)(object)fragmentTarget == (Object)null || !((Component)fragmentTarget).gameObject.activeInHierarchy)
			{
				return false;
			}
			if ((Object)(object)fragmentItem == (Object)null)
			{
				return true;
			}
			if (((Component)fragmentItem).gameObject.activeInHierarchy)
			{
				return (int)fragmentItem.itemState == 0;
			}
			return false;
		}

		private void ScanScoutStatues(HashSet<string> seen)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			ScoutStatue[] array = Resources.FindObjectsOfTypeAll<ScoutStatue>();
			HashSet<int> hashSet = new HashSet<int>();
			ScoutStatue[] array2 = array;
			foreach (ScoutStatue val in array2)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Scene scene = ((Component)val).gameObject.scene;
				if (!((Scene)(ref scene)).IsValid() || !((Component)val).gameObject.activeInHierarchy || !hashSet.Add(((Object)((Component)val).gameObject).GetInstanceID()) || val.amuletObjects == null || !TryGetScoutAmuletTypes(val, out var types))
				{
					continue;
				}
				for (int j = 0; j < types.Length; j++)
				{
					int num = types[j];
					if (num < 0 || num >= val.amuletObjects.Length)
					{
						continue;
					}
					GameObject val2 = val.amuletObjects[num];
					if (!((Object)(object)val2 == (Object)null) && val2.activeInHierarchy)
					{
						string text = "statue:" + ((Object)((Component)val).gameObject).GetInstanceID() + ":" + j;
						seen.Add(text);
						int capturedSlot = j;
						int capturedType = num;
						ScoutStatue capturedStatue = val;
						GameObject capturedFragment = val2;
						Item capturedFragmentItem = val2.GetComponentInChildren<Item>(true);
						AddLabel(text, capturedFragment.transform, () => GetStatueAmuletTitle(capturedType), () => IsScoutStatueFragmentValid(capturedStatue, capturedSlot, capturedType, capturedFragment, capturedFragmentItem));
					}
				}
			}
		}

		private static bool TryGetScoutAmuletTypes(ScoutStatue statue, out int[] types)
		{
			types = null;
			if ((Object)(object)statue == (Object)null || ScoutStatueAmuletsField == null)
			{
				return false;
			}
			try
			{
				if (!(ScoutStatueAmuletsField.GetValue(statue) is int[] array))
				{
					return false;
				}
				types = (int[])array.Clone();
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static bool IsScoutStatueFragmentValid(ScoutStatue statue, int slot, int type, GameObject fragment, Item fragmentItem)
		{
			if ((Object)(object)statue == (Object)null || !((Component)statue).gameObject.activeInHierarchy || (Object)(object)fragment == (Object)null || statue.amuletObjects == null || type < 0 || type >= statue.amuletObjects.Length || (Object)(object)statue.amuletObjects[type] != (Object)(object)fragment || !TryGetScoutAmuletTypes(statue, out var types) || slot < 0 || slot >= types.Length || types[slot] != type)
			{
				return false;
			}
			return IsStatueFragmentVisible(fragment.transform, fragmentItem);
		}

		private static bool TryGetAmuletIndex(Item item, out int index)
		{
			index = -1;
			AmuletBase val = default(AmuletBase);
			if ((Object)(object)item == (Object)null || !((Component)item).TryGetComponent<AmuletBase>(ref val))
			{
				return false;
			}
			index = val.amuletIndex;
			if (index >= 0)
			{
				return index < 4;
			}
			return false;
		}

		private void AddLabel(string id, Transform target, Func<string> titleProvider, Func<bool> isValid)
		{
			if (_labels.TryGetValue(id, out var value))
			{
				value.RefreshContent();
			}
			else
			{
				_labels.Add(id, new AmuletLabel(id, ((Component)_canvas).transform, target, titleProvider, isValid, _font, _labelTitleMaterial, _labelDetailMaterial, _fontSize.Value));
			}
		}

		private static bool HasAmulet(ItemInstanceData instanceData)
		{
			BackpackData val = default(BackpackData);
			if (instanceData == null || !instanceData.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val) || val == null || val.itemSlots == null)
			{
				return false;
			}
			ItemSlot[] itemSlots = val.itemSlots;
			foreach (ItemSlot val2 in itemSlots)
			{
				if (val2 != null && !val2.IsEmpty() && (Object)(object)val2.prefab != (Object)null && TryGetAmuletIndex(val2.prefab, out var _))
				{
					return true;
				}
			}
			return false;
		}

		private void RefreshAmuletDefinitions()
		{
			if (_amuletDefinitions.Count == 4)
			{
				return;
			}
			try
			{
				ItemDatabase instance = SingletonAsset<ItemDatabase>.Instance;
				if ((Object)(object)instance == (Object)null || instance.itemLookup == null)
				{
					return;
				}
				foreach (Item value in instance.itemLookup.Values)
				{
					if (TryGetAmuletIndex(value, out var index) && !_amuletDefinitions.ContainsKey(index))
					{
						_amuletDefinitions.Add(index, value);
					}
				}
			}
			catch (Exception ex)
			{
				_log.LogDebug((object)("WhereIsMyAmulet: amulet definition lookup unavailable: " + ex.Message));
			}
		}

		private string GetItemName(Item item)
		{
			try
			{
				if ((Object)(object)item != (Object)null)
				{
					string name = item.GetName();
					if (IsUsableLocalizedName(name))
					{
						return name;
					}
				}
			}
			catch
			{
			}
			if (!TryGetAmuletIndex(item, out var index))
			{
				return "Amulet";
			}
			return GetAmuletName(index);
		}

		private string GetAmuletName(int index)
		{
			if (_amuletDefinitions.TryGetValue(index, out var value))
			{
				string itemNameFromDefinition = GetItemNameFromDefinition(value);
				if (IsUsableLocalizedName(itemNameFromDefinition))
				{
					return itemNameFromDefinition;
				}
			}
			string text;
			switch (index)
			{
			case 0:
				text = "NAME_AMULET_DOUBLEJUMP";
				break;
			case 1:
				text = "NAME_AMULET_INFINITESTAM";
				break;
			case 2:
				text = "NAME_AMULET_HEALING";
				break;
			case 3:
				text = "NAME_AMULET_CLONE";
				break;
			default:
				return "Amulet";
			}
			try
			{
				string text2 = LocalizedText.GetText(text, true);
				if (IsUsableLocalizedName(text2))
				{
					return text2;
				}
			}
			catch
			{
			}
			return index switch
			{
				0 => "Scout's Initiative", 
				1 => "Scout's Ambition", 
				2 => "Scout's Tenacity", 
				3 => "Scout's Generosity", 
				_ => "Amulet", 
			};
		}

		private static string GetItemNameFromDefinition(Item definition)
		{
			try
			{
				return ((Object)(object)definition == (Object)null) ? null : definition.GetName();
			}
			catch
			{
				return null;
			}
		}

		private string GetBackpackTitle(ItemInstanceData data)
		{
			List<string> list = new List<string>();
			BackpackData val = default(BackpackData);
			if (data != null && data.TryGetDataEntry<BackpackData>((DataEntryKey)7, ref val) && val != null && val.itemSlots != null)
			{
				ItemSlot[] itemSlots = val.itemSlots;
				foreach (ItemSlot val2 in itemSlots)
				{
					if (val2 != null && !val2.IsEmpty() && !((Object)(object)val2.prefab == (Object)null) && TryGetAmuletIndex(val2.prefab, out var _))
					{
						string itemName = GetItemName(val2.prefab);
						if (!list.Contains(itemName))
						{
							list.Add(itemName);
						}
					}
				}
			}
			return string.Join("\n", list.ToArray()) + "\n" + (IsChineseLanguage() ? "背包内" : "In backpack");
		}

		private static bool IsUsableLocalizedName(string value)
		{
			if (!string.IsNullOrWhiteSpace(value))
			{
				return !value.StartsWith("LOC: ", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool IsChineseLanguage()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if ((int)LocalizedText.CURRENT_LANGUAGE != 9)
			{
				return (int)LocalizedText.CURRENT_LANGUAGE == 10;
			}
			return true;
		}

		private void RemoveLabel(string id)
		{
			if (_labels.TryGetValue(id, out var value))
			{
				value.Dispose();
				_labels.Remove(id);
			}
		}

		private void ClearLabels()
		{
			foreach (AmuletLabel value in _labels.Values)
			{
				value.Dispose();
			}
			_labels.Clear();
		}

		private void RemovePendingLabels()
		{
			for (int i = 0; i < _labelsToRemove.Count; i++)
			{
				RemoveLabel(_labelsToRemove[i]);
			}
			_labelsToRemove.Clear();
		}

		private IEnumerable<ConfigEntryBase> GetConfigEntries()
		{
			return (IEnumerable<ConfigEntryBase>)(object)new ConfigEntryBase[9]
			{
				(ConfigEntryBase)_enabled,
				(ConfigEntryBase)_scanKey,
				(ConfigEntryBase)_scanMode,
				(ConfigEntryBase)_displayDuration,
				(ConfigEntryBase)_maxDistance,
				(ConfigEntryBase)_fontSize,
				(ConfigEntryBase)_fontChoice,
				(ConfigEntryBase)_showStatueFragments,
				(ConfigEntryBase)_showOffscreen
			};
		}

		private void UpdateLabelStyleIfChanged()
		{
			if (_fontChoice.Value != _lastFontChoice || !Mathf.Approximately(_fontSize.Value, _lastFontSize))
			{
				_lastFontChoice = _fontChoice.Value;
				_lastFontSize = _fontSize.Value;
				_font = null;
				ReleaseLabelMaterials();
				ApplyLabelStyleToExisting(logResolution: true);
			}
		}

		private bool EnsureFont(bool logResolution)
		{
			if ((Object)(object)_font != (Object)null)
			{
				EnsureLabelMaterials();
				return true;
			}
			_font = FontHelper.GetLabelFont(_fontChoice.Value);
			if ((Object)(object)_font == (Object)null)
			{
				if (logResolution)
				{
					_log.LogWarning((object)"No TMP font is currently available for amulet labels");
				}
				return false;
			}
			EnsureLabelMaterials();
			return true;
		}

		private void EnsureLabelMaterials()
		{
			if ((!((Object)(object)_labelTitleMaterial != (Object)null) || !((Object)(object)_labelDetailMaterial != (Object)null)) && !((Object)(object)_font == (Object)null) && !((Object)(object)((TMP_Asset)_font).material == (Object)null))
			{
				ReleaseLabelMaterials();
				_labelTitleMaterial = CreateLabelMaterial("WhereIsMyAmulet Label Title", 0.055f, 0.8f, 0.35f, 0.05f, 0.05f);
				_labelDetailMaterial = CreateLabelMaterial("WhereIsMyAmulet Label Detail", 0.035f, 0.72f, 0.3f, 0f, 0.08f);
			}
		}

		private Material CreateLabelMaterial(string name, float outlineWidth, float underlayAlpha, float underlayOffset, float underlayDilate, float underlaySoftness)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(((TMP_Asset)_font).material)
			{
				name = name,
				hideFlags = (HideFlags)61
			};
			if (val.HasProperty("_OutlineColor") && val.HasProperty("_OutlineWidth"))
			{
				val.EnableKeyword("OUTLINE_ON");
				val.SetColor("_OutlineColor", new Color(0f, 0f, 0f, 0.88f));
				val.SetFloat("_OutlineWidth", outlineWidth);
			}
			if (val.HasProperty("_UnderlayColor") && val.HasProperty("_UnderlayOffsetX") && val.HasProperty("_UnderlayOffsetY") && val.HasProperty("_UnderlayDilate") && val.HasProperty("_UnderlaySoftness"))
			{
				val.EnableKeyword("UNDERLAY_ON");
				val.SetColor("_UnderlayColor", new Color(0f, 0f, 0f, underlayAlpha));
				val.SetFloat("_UnderlayOffsetX", underlayOffset);
				val.SetFloat("_UnderlayOffsetY", 0f - underlayOffset);
				val.SetFloat("_UnderlayDilate", underlayDilate);
				val.SetFloat("_UnderlaySoftness", underlaySoftness);
			}
			return val;
		}

		private void ReleaseLabelMaterials()
		{
			if ((Object)(object)_labelTitleMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_labelTitleMaterial);
				_labelTitleMaterial = null;
			}
			if ((Object)(object)_labelDetailMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_labelDetailMaterial);
				_labelDetailMaterial = null;
			}
		}

		private void ApplyLabelStyleToExisting(bool logResolution)
		{
			if (!EnsureFont(logResolution))
			{
				return;
			}
			foreach (AmuletLabel value in _labels.Values)
			{
				value.ApplyStyle(_font, _labelTitleMaterial, _labelDetailMaterial, _fontSize.Value);
			}
		}

		private void OnGameLanguageChanged()
		{
			_amuletDefinitions.Clear();
			_font = null;
			ReleaseLabelMaterials();
			ApplyLabelStyleToExisting(logResolution: true);
			foreach (AmuletLabel value in _labels.Values)
			{
				value.RefreshContent();
			}
			ModConfigLocalization.ApplyLocalizedDescriptions(GetConfigEntries());
			ModConfigLocalization.RefreshVisibleUi();
			ScheduleModConfigRefresh();
		}

		private void ScheduleModConfigRefresh()
		{
			if (!_modConfigRefreshScheduled)
			{
				_modConfigRefreshScheduled = true;
				((MonoBehaviour)this).StartCoroutine(DeferredModConfigRefresh());
			}
		}

		private IEnumerator DeferredModConfigRefresh()
		{
			yield return null;
			ModConfigLocalization.ApplyLocalizedDescriptions(GetConfigEntries());
			ModConfigLocalization.RefreshVisibleUi();
			_modConfigRefreshScheduled = false;
		}

		private static TMP_FontAsset FindFont()
		{
			try
			{
				if ((Object)(object)GUIManager.instance != (Object)null)
				{
					AscentUI componentInChildren = ((Component)GUIManager.instance).GetComponentInChildren<AscentUI>(true);
					if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.text != (Object)null && (Object)(object)((TMP_Text)componentInChildren.text).font != (Object)null)
					{
						return ((TMP_Text)componentInChildren.text).font;
					}
					if ((Object)(object)GUIManager.instance.heroDayText != (Object)null && (Object)(object)((TMP_Text)GUIManager.instance.heroDayText).font != (Object)null)
					{
						return ((TMP_Text)GUIManager.instance.heroDayText).font;
					}
				}
				TextMeshProUGUI val = Object.FindAnyObjectByType<TextMeshProUGUI>();
				if ((Object)(object)val != (Object)null && (Object)(object)((TMP_Text)val).font != (Object)null)
				{
					return ((TMP_Text)val).font;
				}
				if ((Object)(object)TMP_Settings.defaultFontAsset != (Object)null)
				{
					return TMP_Settings.defaultFontAsset;
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("WhereIsMyAmulet: failed to find a game font: " + ex.Message));
			}
			return null;
		}
	}
	internal sealed class AmuletLabel
	{
		private readonly string _instanceId;

		private readonly Transform _target;

		private readonly Func<bool> _isValid;

		private readonly Func<string> _titleProvider;

		private readonly GameObject _root;

		private readonly CanvasGroup _group;

		private readonly TextMeshProUGUI _titleText;

		private readonly TextMeshProUGUI _distanceText;

		private readonly TextMeshProUGUI _arrow;

		private float _fontSize;

		private string _cachedTitle = string.Empty;

		private string _cachedDistance = string.Empty;

		private string _lastTitle = string.Empty;

		private string _lastDistance = string.Empty;

		private int _lastDistanceMeters = int.MinValue;

		private bool _hasTextState;

		private bool _visible;

		private bool _arrowVisible;

		public string InstanceId => _instanceId;

		public bool IsValid
		{
			get
			{
				if ((Object)(object)_target != (Object)null && _isValid != null)
				{
					return _isValid();
				}
				return false;
			}
		}

		public AmuletLabel(string id, Transform canvas, Transform target, Func<string> titleProvider, Func<bool> isValid, TMP_FontAsset font, Material titleMaterial, Material detailMaterial, float fontSize)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Expected O, but got Unknown
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Expected O, but got Unknown
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			_instanceId = id ?? string.Empty;
			_target = target;
			_titleProvider = titleProvider;
			_isValid = isValid;
			_fontSize = Mathf.Clamp(fontSize, 10f, 64f);
			_root = new GameObject("AmuletDistanceLabel");
			_root.transform.SetParent(canvas, false);
			_root.layer = 5;
			_group = _root.AddComponent<CanvasGroup>();
			_group.blocksRaycasts = false;
			_group.interactable = false;
			_group.alpha = 0f;
			_titleText = CreateText("TitleText", font, titleMaterial, _fontSize, new Vector2(0f, 10f));
			((TMP_Text)_titleText).fontStyle = (FontStyles)1;
			((Graphic)_titleText).color = new Color(0.875f, 0.855f, 0.761f, 1f);
			_distanceText = CreateText("DistanceText", font, detailMaterial, 18f, new Vector2(0f, -40f));
			((Graphic)_distanceText).color = new Color(0.875f, 0.855f, 0.761f, 1f);
			GameObject val = new GameObject("OffscreenDirection");
			val.transform.SetParent(_root.transform, false);
			_arrow = val.AddComponent<TextMeshProUGUI>();
			val.layer = 5;
			ApplyFont(_arrow, font, ((TMP_Asset)font).material, 22f);
			((TMP_Text)_arrow).text = "^";
			((TMP_Text)_arrow).alignment = (TextAlignmentOptions)514;
			((Graphic)_arrow).color = new Color(1f, 0.85f, 0.2f, 1f);
			((Graphic)_arrow).raycastTarget = false;
			((TMP_Text)_arrow).rectTransform.sizeDelta = new Vector2(18f, 18f);
			((TMP_Text)_arrow).rectTransform.anchoredPosition = new Vector2(0f, -28f);
			((Behaviour)_arrow).enabled = false;
			RefreshContent();
		}

		public void ApplyStyle(TMP_FontAsset font, Material titleMaterial, Material detailMaterial, float fontSize)
		{
			if (!((Object)(object)font == (Object)null))
			{
				_fontSize = Mathf.Clamp(fontSize, 10f, 64f);
				ApplyFont(_titleText, font, titleMaterial, _fontSize);
				ApplyFont(_distanceText, font, detailMaterial, 18f);
				ApplyFont(_arrow, font, ((TMP_Asset)font).material, 22f);
			}
		}

		public void RefreshContent()
		{
			_cachedTitle = ((_titleProvider == null) ? "Amulet" : (_titleProvider() ?? string.Empty));
			_hasTextState = false;
		}

		private static void ApplyFont(TextMeshProUGUI text, TMP_FontAsset font, Material material, float size)
		{
			((TMP_Text)text).enableAutoSizing = false;
			((TMP_Text)text).font = font;
			((TMP_Text)text).fontSharedMaterial = material;
			((TMP_Text)text).UpdateMeshPadding();
			((TMP_Text)text).fontSize = Mathf.Clamp(size, 10f, 64f);
		}

		private TextMeshProUGUI CreateText(string name, TMP_FontAsset font, Material material, float size, Vector2 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(_root.transform, false);
			val.layer = 5;
			TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
			((Graphic)obj).raycastTarget = false;
			((TMP_Text)obj).overflowMode = (TextOverflowModes)0;
			((TMP_Text)obj).richText = false;
			ApplyFont(obj, font, material, size);
			((TMP_Text)obj).rectTransform.sizeDelta = new Vector2(420f, 180f);
			((TMP_Text)obj).rectTransform.anchoredPosition = position;
			return obj;
		}

		public void Update(Camera camera, float maxDistance, bool showOffscreen)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)camera == (Object)null)
			{
				SetVisible(visible: false);
				return;
			}
			Vector3 val = _target.position + Vector3.up * 0.8f;
			Vector3 val2 = camera.WorldToViewportPoint(val);
			float num = Vector3.Distance(((Component)camera).transform.position, _target.position);
			bool flag = maxDistance <= 0f || num <= maxDistance;
			bool flag2 = val2.z > 0f;
			if (flag2 && val2.x >= 0f && val2.x <= 1f && val2.y >= 0f && val2.y <= 1f && flag)
			{
				Vector3 position = camera.WorldToScreenPoint(val);
				position.z = 0f;
				_root.transform.position = position;
				SetText(_cachedTitle, GetDistanceText(num));
				SetArrowVisible(visible: false);
				SetVisible(visible: true);
				return;
			}
			if (!showOffscreen || !flag)
			{
				SetVisible(visible: false);
				return;
			}
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(val2.x - 0.5f, val2.y - 0.5f);
			if (!flag2)
			{
				val3 = -val3;
			}
			if (((Vector2)(ref val3)).sqrMagnitude < 0.001f)
			{
				val3 = Vector2.up;
			}
			((Vector2)(ref val3)).Normalize();
			Vector2 val4 = default(Vector2);
			((Vector2)(ref val4))..ctor((float)Screen.width * 0.5f, (float)Screen.height * 0.5f);
			float num2 = (float)Mathf.Min(Screen.width, Screen.height) * 0.42f;
			_root.transform.position = Vector2.op_Implicit(val4 + val3 * num2);
			SetText(string.Empty, GetDistanceText(num));
			SetArrowVisible(visible: true);
			((Transform)((TMP_Text)_arrow).rectTransform).localRotation = Quaternion.Euler(0f, 0f, Mathf.Atan2(val3.y, val3.x) * 57.29578f - 90f);
			SetVisible(visible: true);
		}

		public void Dispose()
		{
			if ((Object)(object)_root != (Object)null)
			{
				Object.Destroy((Object)(object)_root);
			}
		}

		private void SetVisible(bool visible)
		{
			if (_visible != visible)
			{
				_visible = visible;
				_group.alpha = (visible ? 1f : 0f);
			}
		}

		private void SetText(string title, string distance)
		{
			if (!_hasTextState || !string.Equals(_lastTitle, title, StringComparison.Ordinal) || !string.Equals(_lastDistance, distance, StringComparison.Ordinal))
			{
				_hasTextState = true;
				_lastTitle = title;
				_lastDistance = distance;
				((TMP_Text)_titleText).text = title;
				((TMP_Text)_distanceText).text = distance;
			}
		}

		private string GetDistanceText(float distance)
		{
			int num = Mathf.RoundToInt(distance);
			if (num != _lastDistanceMeters)
			{
				_lastDistanceMeters = num;
				_cachedDistance = num.ToString(CultureInfo.InvariantCulture) + "m";
			}
			return _cachedDistance;
		}

		private void SetArrowVisible(bool visible)
		{
			if (_arrowVisible != visible)
			{
				_arrowVisible = visible;
				((Behaviour)_arrow).enabled = visible;
			}
		}
	}
}