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;
}
}
}
}