Decompiled source of PeakAdminToolkit v1.0.1

BepInEx/plugins/PeakAdminToolkit/PeakAdminToolkit.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Peak;
using PeakAdminToolkit.Core;
using PeakAdminToolkit.Items;
using PeakAdminToolkit.Players;
using PeakAdminToolkit.Self;
using PeakAdminToolkit.UI;
using PeakAdminToolkit.World;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: AssemblyDescription("PEAK item catalog and administration toolkit")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: ComVisible(false)]
[assembly: AssemblyProduct("PEAK Admin Toolkit")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyTitle("PEAK Admin Toolkit")]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("1.0.1.0")]
namespace PeakAdminToolkit
{
	[BepInPlugin("com.oldip.peakadmintoolkit", "PEAK Admin Toolkit", "1.0.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.oldip.peakadmintoolkit";

		public const string Name = "PEAK Admin Toolkit";

		public const string Version = "1.0.1";

		private ToolkitConfig config;

		private Compatibility compatibility;

		private PeakApi api;

		private MainWindow window;

		private SelfTools selfTools;

		private HornFuelHud hornFuelHud;

		private DroppedItemHistory droppedItems;

		private DroppedItemCapture droppedItemCapture;

		private bool hasFocus = true;

		private double nextLanguagePoll;

		private bool guiFailureReported;

		private bool WindowIsOpen
		{
			get
			{
				if (window != null)
				{
					return window.IsOpen;
				}
				return false;
			}
		}

		private void Awake()
		{
			config = new ToolkitConfig(((BaseUnityPlugin)this).Config);
			compatibility = new Compatibility(() => WindowIsOpen, ((BaseUnityPlugin)this).Logger);
			api = new PeakApi(compatibility, new ItemDescriptions(delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			}), delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			});
			selfTools = new SelfTools(new SelfApi(), delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			}, () => WindowIsOpen || !hasFocus || !config.Enabled.Value);
			droppedItems = new DroppedItemHistory();
			window = new MainWindow(config, api, selfTools, droppedItems, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			});
			droppedItemCapture = new DroppedItemCapture(droppedItems, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			});
			hornFuelHud = new HornFuelHud(null, delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			}, () => ((Behaviour)this).isActiveAndEnabled && config.Enabled.Value);
			SceneManager.sceneLoaded += OnSceneLoaded;
			SceneManager.sceneUnloaded += OnSceneUnloaded;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("PEAK Admin Toolkit 1.0.1 loaded. Window hooks: " + compatibility.WindowHooksReady));
		}

		private void Update()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			if (config == null || window == null)
			{
				return;
			}
			if (!config.Enabled.Value)
			{
				window.Close();
				selfTools.Reset();
				return;
			}
			selfTools.Poll();
			if ((int)config.ToggleKey.Value != 0 && Input.GetKeyDown(config.ToggleKey.Value))
			{
				if (WindowIsOpen)
				{
					window.Close();
				}
				else if (api.CanOpenWindow)
				{
					window.Open();
				}
			}
			if (Time.unscaledTimeAsDouble >= nextLanguagePoll)
			{
				nextLanguagePoll = Time.unscaledTimeAsDouble + 1.0;
				window.RefreshLanguage(api.ReadLanguage());
			}
		}

		private void OnGUI()
		{
			if (window == null)
			{
				return;
			}
			try
			{
				window.Draw();
			}
			catch (Exception ex)
			{
				window.Close();
				if (!guiFailureReported)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Admin window closed after a UI error: " + ex.Message));
				}
				guiFailureReported = true;
			}
		}

		private void OnApplicationFocus(bool hasFocus)
		{
			this.hasFocus = hasFocus;
			if (!hasFocus && window != null)
			{
				window.Close();
			}
		}

		private void OnDisable()
		{
			if (selfTools != null)
			{
				selfTools.Reset();
			}
			if (window != null)
			{
				window.Close();
			}
		}

		private void OnSceneUnloaded(Scene scene)
		{
			if (selfTools != null)
			{
				selfTools.Reset();
			}
			if (droppedItems != null)
			{
				droppedItems.Clear();
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			if (selfTools != null)
			{
				selfTools.Reset();
			}
			if (droppedItems != null)
			{
				droppedItems.Clear();
			}
			if (window != null)
			{
				window.Close();
				window.InvalidateItemCatalog();
			}
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			SceneManager.sceneUnloaded -= OnSceneUnloaded;
			if (selfTools != null)
			{
				selfTools.Dispose();
			}
			if (hornFuelHud != null)
			{
				hornFuelHud.Dispose();
			}
			if (droppedItemCapture != null)
			{
				droppedItemCapture.Dispose();
			}
			if (window != null)
			{
				window.Dispose();
			}
			if (compatibility != null)
			{
				compatibility.Dispose();
			}
			window = null;
			api = null;
			compatibility = null;
			selfTools = null;
			hornFuelHud = null;
		}
	}
}
namespace PeakAdminToolkit.Core
{
	internal sealed class ToolkitConfig
	{
		public readonly ConfigEntry<bool> Enabled;

		public readonly ConfigEntry<KeyCode> ToggleKey;

		public readonly ConfigEntry<string> Language;

		public ToolkitConfig(ConfigFile file)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			Enabled = file.Bind<bool>("General", "Enabled", true, new ConfigDescription("Enable the toolkit. Disabling closes the window, resets self tools and stops the horn HUD correction. / 停用时关闭窗口并恢复自身工具 / 停用時關閉視窗並恢復自身工具", (AcceptableValueBase)null, new object[0]));
			ToggleKey = file.Bind<KeyCode>("Interface", "ToggleKey", (KeyCode)289, new ConfigDescription("Open or close the window. None disables the shortcut. / 窗口快捷键 / 視窗快捷鍵", (AcceptableValueBase)null, new object[0]));
			Language = file.Bind<string>("Interface", "Language", "Auto", new ConfigDescription("UI language. Auto follows PEAK; unsupported languages use English. / 界面语言 / 介面語言", (AcceptableValueBase)(object)new AcceptableValueList<string>(new string[4] { "Auto", "en", "zh-CN", "zh-TW" }), new object[0]));
		}
	}
	internal sealed class Localization
	{
		private static readonly Dictionary<string, string> English = Read("en");

		private static readonly Dictionary<string, string> Simplified = Read("zh-CN");

		private static readonly Dictionary<string, string> Traditional = Read("zh-TW");

		public string Language { get; private set; }

		public static IEnumerable<string> Keys => English.Keys;

		public Localization()
		{
			Language = "en";
		}

		public static string Resolve(string preference, string gameLanguage)
		{
			switch (preference)
			{
			case "en":
			case "zh-CN":
			case "zh-TW":
				return preference;
			default:
				if (gameLanguage == "SimplifiedChinese")
				{
					return "zh-CN";
				}
				if (gameLanguage == "TraditionalChinese")
				{
					return "zh-TW";
				}
				return "en";
			}
		}

		public void Update(string preference, string gameLanguage)
		{
			Language = Resolve(preference, gameLanguage);
		}

		public string Text(string key)
		{
			Dictionary<string, string> dictionary = ((Language == "zh-CN") ? Simplified : ((Language == "zh-TW") ? Traditional : English));
			if (dictionary.TryGetValue(key, out var value) || English.TryGetValue(key, out value))
			{
				return value;
			}
			return key;
		}

		public string Format(string key, params object[] values)
		{
			try
			{
				return string.Format(Text(key), values);
			}
			catch (FormatException)
			{
				if (English.TryGetValue(key, out var value))
				{
					try
					{
						return string.Format(value, values);
					}
					catch (FormatException)
					{
					}
				}
				return key;
			}
		}

		private static Dictionary<string, string> Read(string language)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Invalid comparison between Unknown and I4
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			foreach (JProperty item in EmbeddedJson.Read("Locales." + language + ".json").Properties())
			{
				if ((int)item.Value.Type == 8)
				{
					string value = (string)item.Value;
					if (!string.IsNullOrWhiteSpace(value))
					{
						dictionary[item.Name] = value;
					}
				}
			}
			return dictionary;
		}
	}
	internal static class EmbeddedJson
	{
		public static JObject Read(string name, Action<string> warning = null)
		{
			//IL_005f: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			using Stream stream = typeof(EmbeddedJson).Assembly.GetManifestResourceStream("PeakAdminToolkit." + name);
			if (stream == null)
			{
				warning?.Invoke("Missing embedded data: " + name);
				return new JObject();
			}
			try
			{
				using StreamReader streamReader = new StreamReader(stream);
				return JObject.Parse(streamReader.ReadToEnd());
			}
			catch (JsonException ex)
			{
				JsonException ex2 = ex;
				warning?.Invoke("Invalid embedded data " + name + ": " + ((Exception)(object)ex2).Message);
				return new JObject();
			}
		}
	}
	internal sealed class Compatibility : IDisposable
	{
		private readonly Harmony harmony;

		private readonly ManualLogSource log;

		private readonly FieldInfo languageField;

		private readonly FieldInfo guiInstance;

		private readonly PropertyInfo cursorProperty;

		private static Func<bool> windowOpen;

		private bool reportedLanguageFailure;

		public bool WindowHooksReady { get; private set; }

		public Compatibility(Func<bool> isWindowOpen, ManualLogSource logger)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Expected O, but got Unknown
			log = logger;
			harmony = new Harmony("com.oldip.peakadmintoolkit");
			Type type = FindGameType("LocalizedText");
			Type type2 = FindGameType("GUIManager");
			if (type != null)
			{
				languageField = type.GetField("CURRENT_LANGUAGE", BindingFlags.Static | BindingFlags.Public);
			}
			if (type2 != null)
			{
				guiInstance = type2.GetField("instance", BindingFlags.Static | BindingFlags.Public);
				cursorProperty = type2.GetProperty("windowShowingCursor", BindingFlags.Instance | BindingFlags.Public);
			}
			PropertyInfo propertyInfo = ((type2 == null) ? null : type2.GetProperty("windowBlockingInput", BindingFlags.Instance | BindingFlags.Public));
			if (!IsBooleanGetter(propertyInfo) || !IsBooleanGetter(cursorProperty) || guiInstance == null)
			{
				log.LogWarning((object)"Window unavailable: PEAK input/cursor API is incompatible. Expected GUIManager.windowBlockingInput and windowShowingCursor.");
				return;
			}
			windowOpen = isWindowOpen;
			try
			{
				HarmonyMethod val = new HarmonyMethod(typeof(Compatibility).GetMethod("IncludeToolkitWindow", BindingFlags.Static | BindingFlags.NonPublic));
				harmony.Patch((MethodBase)propertyInfo.GetGetMethod(), (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				harmony.Patch((MethodBase)cursorProperty.GetGetMethod(), (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				WindowHooksReady = true;
			}
			catch (Exception ex)
			{
				Dispose();
				log.LogWarning((object)("Window unavailable: input hooks could not be installed. " + ex.Message));
			}
		}

		private static Type FindGameType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (assembly.GetName().Name == "Assembly-CSharp")
				{
					return assembly.GetType(name, throwOnError: false);
				}
			}
			return null;
		}

		private static bool IsBooleanGetter(PropertyInfo property)
		{
			if (property != null && property.PropertyType == typeof(bool))
			{
				return property.GetGetMethod() != null;
			}
			return false;
		}

		private static void IncludeToolkitWindow(ref bool __result)
		{
			__result = __result || (windowOpen != null && windowOpen());
		}

		public string ReadGameLanguage()
		{
			try
			{
				object obj = ((languageField == null) ? null : languageField.GetValue(null));
				if (obj != null)
				{
					return obj.ToString();
				}
			}
			catch (Exception ex)
			{
				if (!reportedLanguageFailure)
				{
					log.LogWarning((object)("PEAK language read failed: " + ex.Message));
				}
			}
			if (!reportedLanguageFailure)
			{
				log.LogWarning((object)"PEAK language unavailable; Auto uses English. An explicit UI language still works.");
			}
			reportedLanguageFailure = true;
			return null;
		}

		public bool GameWantsCursor()
		{
			if (guiInstance == null || cursorProperty == null)
			{
				return false;
			}
			try
			{
				object value = guiInstance.GetValue(null);
				Object val = (Object)((value is Object) ? value : null);
				if (val == (Object)null)
				{
					return false;
				}
				return (bool)cursorProperty.GetValue(value, null);
			}
			catch (Exception)
			{
				return false;
			}
		}

		public void Dispose()
		{
			WindowHooksReady = false;
			windowOpen = null;
			harmony.UnpatchSelf();
		}
	}
	internal sealed class PeakApi
	{
		private readonly Compatibility compatibility;

		private readonly ItemCatalogApi catalog;

		private readonly ItemSpawnApi spawning = new ItemSpawnApi();

		public bool CanOpenWindow => compatibility.WindowHooksReady;

		public bool CanSpawnItems => spawning.CanSpawnItems;

		public PeakApi(Compatibility compatibility, ItemDescriptions descriptions = null, Action<string> diagnosticLog = null)
		{
			this.compatibility = compatibility;
			catalog = new ItemCatalogApi(descriptions, diagnosticLog);
		}

		public string ReadLanguage()
		{
			return compatibility.ReadGameLanguage();
		}

		public bool GameWantsCursor()
		{
			return compatibility.GameWantsCursor();
		}

		public List<ItemCatalogEntry> ReadItemCatalog(bool includeSpecial = false)
		{
			return catalog.ReadItemCatalog(includeSpecial);
		}

		public bool SpawnItem(ItemCatalogEntry entry, bool includeSpecial = false)
		{
			return spawning.SpawnItem(entry, includeSpecial);
		}

		public bool MatchesItem(string query, ItemCatalogEntry item)
		{
			if (item != null)
			{
				return ItemSearch.Matches(query, item.CurrentName, item.EnglishName, item.SimplifiedName, item.TraditionalName);
			}
			return false;
		}
	}
}
namespace PeakAdminToolkit.Items
{
	internal static class ItemApiAccess
	{
		internal const BindingFlags StaticFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

		internal const BindingFlags InstanceFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

		internal static bool IsValidToSpawn(object item)
		{
			if (TryIsValidToSpawn(item, out var valid, out var _))
			{
				return valid;
			}
			return false;
		}

		internal static bool TryIsValidToSpawn(object item, out bool valid, out string reason)
		{
			valid = false;
			reason = null;
			if (item == null)
			{
				reason = "item unavailable";
				return false;
			}
			try
			{
				MethodInfo method = item.GetType().GetMethod("IsValidToSpawn", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, Type.EmptyTypes, null);
				if (method == null)
				{
					reason = "IsValidToSpawn missing";
					return false;
				}
				if (method.ReturnType != typeof(bool))
				{
					reason = "IsValidToSpawn must return Boolean";
					return false;
				}
				valid = (bool)method.Invoke(item, null);
				return true;
			}
			catch (Exception ex)
			{
				TargetInvocationException ex2 = ex as TargetInvocationException;
				reason = ((ex2 != null && ex2.InnerException != null) ? ex2.InnerException : ex).GetType().Name;
				return false;
			}
		}

		internal static string InvokeStringMethod(object target, string name)
		{
			MethodInfo method = target.GetType().GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, Type.EmptyTypes, null);
			if (method == null || method.ReturnType != typeof(string))
			{
				return string.Empty;
			}
			try
			{
				return (method.Invoke(target, null) as string) ?? string.Empty;
			}
			catch (Exception)
			{
				return string.Empty;
			}
		}

		internal static bool ReadStaticBoolean(Type type, string name)
		{
			object obj = ReadStaticMember(type, name);
			if (obj is bool)
			{
				return (bool)obj;
			}
			return false;
		}

		internal static object ReadMember(object target, string name)
		{
			if (target == null)
			{
				return null;
			}
			Type type = target.GetType();
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return property.GetValue(target, null);
				}
				catch (Exception)
				{
					return null;
				}
			}
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (!(field == null))
			{
				return field.GetValue(target);
			}
			return null;
		}

		internal static object ReadStaticMember(Type type, string name)
		{
			if (type == null)
			{
				return null;
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (property != null && property.GetIndexParameters().Length == 0)
			{
				try
				{
					return property.GetValue(null, null);
				}
				catch (Exception)
				{
					return null;
				}
			}
			FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (!(field == null))
			{
				return field.GetValue(null);
			}
			return null;
		}

		internal static Type FindGameType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (!(assembly.GetName().Name != "Assembly-CSharp"))
				{
					return assembly.GetType(name, throwOnError: false);
				}
			}
			return null;
		}

		internal static Type FindLoadedType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type type = assembly.GetType(name, throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
			return null;
		}

		internal static bool HasStaticBooleanMember(Type type, string name)
		{
			if (type == null)
			{
				return false;
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (property != null && property.PropertyType == typeof(bool) && property.GetIndexParameters().Length == 0)
			{
				MethodInfo getMethod = property.GetGetMethod(nonPublic: true);
				if (getMethod != null && getMethod.IsStatic)
				{
					return true;
				}
			}
			FieldInfo field = type.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (field != null && field.FieldType == typeof(bool))
			{
				return field.IsStatic;
			}
			return false;
		}
	}
	internal sealed class ItemValidityDiagnostics
	{
		private readonly Action<string> log;

		private readonly HashSet<string> seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

		private int validCount;

		private int invalidCount;

		private int unknownCount;

		public ItemValidityDiagnostics(Action<string> log)
		{
			this.log = log;
		}

		public void Record(string spawnName, bool evaluated, bool valid, string reason)
		{
			if (SpecialItems.Classify(spawnName) != SpecialItemKind.None && seen.Add(spawnName))
			{
				if (!evaluated)
				{
					unknownCount++;
					details.Add("unknown: " + spawnName + " (" + reason + ")");
				}
				else if (valid)
				{
					validCount++;
				}
				else
				{
					invalidCount++;
					details.Add("false: " + spawnName);
				}
			}
		}

		public void Write(string failure = null)
		{
			try
			{
				log("[ItemValidity] status=" + ((failure == null) ? "complete" : ("incomplete reason=" + failure)) + " registered=" + seen.Count + " true=" + validCount + " false=" + invalidCount + " unknown=" + unknownCount);
				foreach (string detail in details)
				{
					log("[ItemValidity] " + detail);
				}
			}
			catch (Exception)
			{
			}
		}
	}
	internal sealed class HornFuelHud : IDisposable
	{
		private const BindingFlags Members = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static HornFuelHud current;

		private readonly Harmony harmony = new Harmony("com.oldip.peakadmintoolkit.hornfuelhud");

		private readonly Func<string, Type> resolve;

		private readonly Action<string> log;

		private readonly Func<bool> enabled;

		private bool disposed;

		private string frameFailure;

		internal bool Available { get; private set; }

		internal HornFuelHud(Func<string, Type> resolve, Action<string> log, Func<bool> enabled)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			this.resolve = resolve ?? new Func<string, Type>(FindGameType);
			this.log = log;
			this.enabled = enabled;
			try
			{
				Type type = this.resolve("GUIManager");
				if (type == null || this.resolve("Character") == null || this.resolve("MagicBugle") == null)
				{
					throw new MissingMemberException("Horn HUD game types");
				}
				MethodInfo method = type.GetMethod("UpdateItems", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method == null || method.ReturnType != typeof(void))
				{
					throw new MissingMethodException("GUIManager", "UpdateItems");
				}
				harmony.Patch((MethodBase)method, (HarmonyMethod)null, new HarmonyMethod(typeof(HornFuelHud).GetMethod("Postfix", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				current = this;
				Available = true;
			}
			catch (Exception ex)
			{
				Fail(ex);
			}
		}

		private static void Postfix(object __instance)
		{
			HornFuelHud hornFuelHud = current;
			if (hornFuelHud == null || !hornFuelHud.Available || (hornFuelHud.enabled != null && !hornFuelHud.enabled()))
			{
				return;
			}
			try
			{
				Apply(__instance, hornFuelHud.resolve);
				hornFuelHud.frameFailure = null;
			}
			catch (Exception innerException)
			{
				while (innerException is TargetInvocationException && innerException.InnerException != null)
				{
					innerException = innerException.InnerException;
				}
				string text = innerException.GetType().Name + ": " + innerException.Message;
				if (hornFuelHud.frameFailure != text && hornFuelHud.log != null)
				{
					hornFuelHud.log("Horn HUD frame skipped: " + text);
				}
				hornFuelHud.frameFailure = text;
			}
		}

		internal static void Apply(object gui, Func<string, Type> resolve)
		{
			if (gui == null || resolve == null)
			{
				return;
			}
			Type type = resolve("Character");
			Type type2 = resolve("MagicBugle");
			if (type == null || type2 == null)
			{
				return;
			}
			object obj = ReadStatic(type, "localCharacter");
			if (obj == null || !object.ReferenceEquals(obj, ReadStatic(type, "observedCharacter")))
			{
				return;
			}
			object obj2 = Read(Read(obj, "data"), "currentItem");
			if (obj2 == null)
			{
				return;
			}
			MethodInfo method = obj2.GetType().GetMethod("GetComponent", new Type[1] { typeof(Type) });
			if (method == null)
			{
				return;
			}
			object obj3 = method.Invoke(obj2, new object[1] { type2 });
			if (obj3 == null)
			{
				return;
			}
			object obj4 = Read(Read(Read(obj, "refs"), "items"), "currentSelectedSlot");
			if (obj4 == null || !Convert.ToBoolean(Read(obj4, "IsSome")))
			{
				return;
			}
			int num = Convert.ToInt32(Read(obj4, "Value"));
			if (!(Read(gui, "items") is Array array) || num < 0 || num >= array.Length)
			{
				return;
			}
			object value = array.GetValue(num);
			object obj5 = Read(value, "fuelBar");
			object obj6 = Read(value, "fuelBarFill");
			if (obj5 == null || obj6 == null)
			{
				return;
			}
			float num2 = Convert.ToSingle(Read(obj3, "totalTootTime"));
			float num3 = Convert.ToSingle(Read(obj3, "currentFuel"));
			if (!(num2 <= 0f) && !float.IsNaN(num2) && !float.IsNaN(num3))
			{
				PropertyInfo property = obj6.GetType().GetProperty("fillAmount", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				MethodInfo method2 = obj5.GetType().GetMethod("SetActive", new Type[1] { typeof(bool) });
				if (!(property == null) && property.CanWrite && !(method2 == null))
				{
					float num4 = Math.Max(0f, Math.Min(1f, num3 / num2));
					method2.Invoke(obj5, new object[1] { true });
					property.SetValue(obj6, num4, null);
				}
			}
		}

		private static object Read(object target, string name)
		{
			if (target == null)
			{
				return null;
			}
			Type type = target.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(target);
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(property == null))
			{
				return property.GetValue(target, null);
			}
			return null;
		}

		private static object ReadStatic(Type type, string name)
		{
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(null);
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(property == null))
			{
				return property.GetValue(null, null);
			}
			return null;
		}

		private static Type FindGameType(string name)
		{
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				if (assembly.GetName().Name == "Assembly-CSharp")
				{
					return assembly.GetType(name, throwOnError: false);
				}
			}
			return null;
		}

		private void Fail(Exception ex)
		{
			while (ex is TargetInvocationException && ex.InnerException != null)
			{
				ex = ex.InnerException;
			}
			Available = false;
			if (object.ReferenceEquals(current, this))
			{
				current = null;
			}
			try
			{
				harmony.UnpatchSelf();
			}
			catch
			{
			}
			if (log != null)
			{
				log("Horn fuel HUD unavailable: " + ex.Message);
			}
		}

		public void Dispose()
		{
			if (!disposed)
			{
				disposed = true;
				Available = false;
				if (object.ReferenceEquals(current, this))
				{
					current = null;
				}
				harmony.UnpatchSelf();
			}
		}
	}
	internal sealed class ItemCatalogApi
	{
		private readonly ItemDescriptions descriptions;

		private readonly Action<string> diagnosticLog;

		public ItemCatalogApi(ItemDescriptions descriptions, Action<string> diagnosticLog = null)
		{
			this.descriptions = descriptions;
			this.diagnosticLog = diagnosticLog;
		}

		public List<ItemCatalogEntry> ReadItemCatalog(bool includeSpecial = false)
		{
			ItemValidityDiagnostics itemValidityDiagnostics = ((includeSpecial && diagnosticLog != null) ? new ItemValidityDiagnostics(diagnosticLog) : null);
			try
			{
				return ReadCatalog(includeSpecial, itemValidityDiagnostics);
			}
			catch (Exception ex)
			{
				itemValidityDiagnostics?.Write("catalog read failed: " + ex.GetType().Name);
				return new List<ItemCatalogEntry>();
			}
		}

		private List<ItemCatalogEntry> ReadCatalog(bool includeSpecial, ItemValidityDiagnostics diagnostics)
		{
			List<ItemCatalogEntry> list = new List<ItemCatalogEntry>();
			Type type = ItemApiAccess.FindGameType("ItemDatabase");
			if (type == null)
			{
				diagnostics?.Write("ItemDatabase type unavailable");
				return list;
			}
			object target = ItemApiAccess.ReadStaticMember(type, "Instance");
			object obj = ItemApiAccess.ReadMember(target, "itemLookup");
			if (!(obj is IDictionary dictionary))
			{
				diagnostics?.Write("ItemDatabase lookup unavailable");
				return list;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (DictionaryEntry item in dictionary)
			{
				try
				{
					ItemCatalogEntry itemCatalogEntry = CreateCatalogEntry(item.Value, item.Key as string, includeSpecial, diagnostics);
					if (itemCatalogEntry != null && hashSet.Add(itemCatalogEntry.SpawnName))
					{
						list.Add(itemCatalogEntry);
					}
				}
				catch (Exception ex)
				{
					diagnostics?.Record(item.Key as string, evaluated: false, valid: false, "candidate read failed: " + ex.GetType().Name);
				}
			}
			list.Sort(delegate(ItemCatalogEntry left, ItemCatalogEntry right)
			{
				int num = StringComparer.CurrentCultureIgnoreCase.Compare(left.CurrentName, right.CurrentName);
				return (num == 0) ? StringComparer.OrdinalIgnoreCase.Compare(left.SpawnName, right.SpawnName) : num;
			});
			diagnostics?.Write();
			return list;
		}

		private ItemCatalogEntry CreateCatalogEntry(object item, string registeredName, bool includeSpecial, ItemValidityDiagnostics diagnostics)
		{
			string spawnName = GetSpawnName(item);
			if (string.IsNullOrEmpty(spawnName))
			{
				diagnostics?.Record(registeredName, evaluated: false, valid: false, "prefab or name unavailable");
				return null;
			}
			bool valid;
			string reason;
			bool flag = ItemApiAccess.TryIsValidToSpawn(item, out valid, out reason);
			diagnostics?.Record(spawnName, flag, valid, reason);
			bool duplicatePrefab = ItemApiAccess.ReadMember(item, "isSecretlyOtherItemPrefab") != null;
			if (!flag)
			{
				return null;
			}
			bool flag2 = valid || ItemManualSpawnPolicy.AllowsSoloOverride(item, spawnName);
			if (!ItemVisibility.ShouldShow(spawnName, registered: true, flag2, duplicatePrefab, includeSpecial))
			{
				return null;
			}
			string text = ItemApiAccess.InvokeStringMethod(item, "GetName");
			if (string.IsNullOrWhiteSpace(text))
			{
				text = spawnName;
			}
			string localizedItemKey = GetLocalizedItemKey(item, "GetNameIndex");
			string text2 = GetLocalizedText(localizedItemKey, "English");
			string localizedText = GetLocalizedText(localizedItemKey, "SimplifiedChinese");
			string localizedText2 = GetLocalizedText(localizedItemKey, "TraditionalChinese");
			if (string.IsNullOrWhiteSpace(text2))
			{
				text2 = spawnName;
			}
			object obj = ItemApiAccess.ReadMember(item, "UIData");
			string text3 = (ItemApiAccess.ReadMember(obj, "itemName") as string) ?? spawnName;
			object obj2 = ItemApiAccess.ReadMember(item, "itemTags");
			string nativeTags = ((obj2 == null) ? string.Empty : obj2.ToString());
			string localizedItemKey2 = GetLocalizedItemKey(item, "GetDescriptionIndex");
			SpecialItemKind specialItemKind = SpecialItems.Classify(spawnName);
			if (specialItemKind != SpecialItemKind.None)
			{
				Localization localization = new Localization();
				localization.Update("en", null);
				Localization localization2 = new Localization();
				localization2.Update("zh-CN", null);
				Localization localization3 = new Localization();
				localization3.Update("zh-TW", null);
				ItemCategory itemCategory = ItemCategory.Other;
				ConsumableKind kinds = ConsumableKind.None;
				if (specialItemKind == SpecialItemKind.MultiplayerItems)
				{
					itemCategory |= ItemClassification.Categories(text3, nativeTags);
					kinds = ItemClassification.Kinds(text3, nativeTags);
				}
				string text4 = SpecialItems.DisplayName(spawnName, localization);
				string text5 = SpecialItems.DisplayName(spawnName, localization2);
				string text6 = SpecialItems.DisplayName(spawnName, localization3);
				string text7 = SpecialItems.Description(spawnName, localization);
				string text8 = SpecialItems.Description(spawnName, localization2);
				string text9 = SpecialItems.Description(spawnName, localization3);
				if (string.IsNullOrEmpty(text7))
				{
					text7 = ReadDescription(localizedItemKey2, "en", text3, text2, spawnName);
				}
				if (string.IsNullOrEmpty(text8))
				{
					text8 = ReadDescription(localizedItemKey2, "zh-CN", text3, text2, spawnName);
				}
				if (string.IsNullOrEmpty(text9))
				{
					text9 = ReadDescription(localizedItemKey2, "zh-TW", text3, text2, spawnName);
				}
				return new ItemCatalogEntry(spawnName, text.Trim(), string.IsNullOrEmpty(text4) ? text2.Trim() : text4, string.IsNullOrEmpty(text5) ? localizedText : text5, string.IsNullOrEmpty(text6) ? localizedText2 : text6, ReadItemIcon(obj), itemCategory, kinds, text7, text8, text9, valid, flag2);
			}
			return new ItemCatalogEntry(spawnName, text.Trim(), text2.Trim(), localizedText, localizedText2, ReadItemIcon(obj), ItemClassification.Categories(text3, nativeTags), ItemClassification.Kinds(text3, nativeTags), ReadDescription(localizedItemKey2, "en", text3, text2, spawnName), ReadDescription(localizedItemKey2, "zh-CN", text3, text2, spawnName), ReadDescription(localizedItemKey2, "zh-TW", text3, text2, spawnName));
		}

		private string ReadDescription(string key, string language, params string[] names)
		{
			string text = ((descriptions == null) ? string.Empty : descriptions.Get(language, names));
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return GetLocalizedText(key, (language == "zh-TW") ? "TraditionalChinese" : ((language == "zh-CN") ? "SimplifiedChinese" : "English"));
		}

		private static Texture2D ReadItemIcon(object uiData)
		{
			if (uiData == null)
			{
				return null;
			}
			MethodInfo method = uiData.GetType().GetMethod("GetIcon", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, Type.EmptyTypes, null);
			if (method != null && method.ReturnType == typeof(Texture2D))
			{
				try
				{
					object? obj = method.Invoke(uiData, null);
					Texture2D val = (Texture2D)((obj is Texture2D) ? obj : null);
					if ((Object)(object)val != (Object)null)
					{
						return val;
					}
				}
				catch (Exception)
				{
				}
			}
			object obj2 = ItemApiAccess.ReadMember(uiData, "icon");
			return (Texture2D)((obj2 is Texture2D) ? obj2 : null);
		}

		private static string GetSpawnName(object item)
		{
			object obj = ItemApiAccess.ReadMember(item, "name");
			string text = obj as string;
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text.Replace("(Clone)", string.Empty).Trim();
			}
			return string.Empty;
		}

		private static string GetLocalizedItemKey(object item, string methodName)
		{
			object target = ItemApiAccess.ReadMember(item, "UIData");
			string text = ItemApiAccess.ReadMember(target, "itemName") as string;
			if (string.IsNullOrWhiteSpace(text))
			{
				return string.Empty;
			}
			Type type = ItemApiAccess.FindGameType("LocalizedText");
			if (type == null)
			{
				return string.Empty;
			}
			MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[1] { typeof(string) }, null);
			if (method == null)
			{
				return string.Empty;
			}
			try
			{
				return (method.Invoke(null, new object[1] { text }) as string) ?? string.Empty;
			}
			catch (Exception)
			{
				return string.Empty;
			}
		}

		private static string GetLocalizedText(string key, string language)
		{
			if (string.IsNullOrWhiteSpace(key))
			{
				return string.Empty;
			}
			Type type = ItemApiAccess.FindGameType("LocalizedText");
			if (type == null)
			{
				return string.Empty;
			}
			FieldInfo field = type.GetField("CURRENT_LANGUAGE", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			if (field == null || !field.FieldType.IsEnum)
			{
				return string.Empty;
			}
			MethodInfo method = type.GetMethod("GetText", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[2]
			{
				typeof(string),
				field.FieldType
			}, null);
			if (method == null)
			{
				return string.Empty;
			}
			try
			{
				object obj = Enum.Parse(field.FieldType, language);
				return (method.Invoke(null, new object[2] { key, obj }) as string) ?? string.Empty;
			}
			catch (Exception)
			{
				return string.Empty;
			}
		}
	}
	internal sealed class ItemSpawnApi
	{
		public bool CanSpawnItems
		{
			get
			{
				try
				{
					object localCharacterItems = GetLocalCharacterItems();
					return IsInRoom() && HasSpawnCapability(localCharacterItems);
				}
				catch (Exception)
				{
					return false;
				}
			}
		}

		public bool SpawnItem(ItemCatalogEntry entry, bool includeSpecial = false)
		{
			try
			{
				return RequestSpawn(entry, includeSpecial);
			}
			catch (Exception)
			{
				return false;
			}
		}

		private bool RequestSpawn(ItemCatalogEntry entry, bool includeSpecial)
		{
			if (entry == null || !entry.CanSpawnManually || !CanSpawnItems)
			{
				return false;
			}
			if (!TryGetDatabaseItem(entry.SpawnName, out var item))
			{
				return false;
			}
			if (!ItemApiAccess.TryIsValidToSpawn(item, out var valid, out var _))
			{
				return false;
			}
			bool flag = valid || ItemManualSpawnPolicy.AllowsSoloOverride(item, entry.SpawnName);
			if (!flag)
			{
				return false;
			}
			bool duplicatePrefab = ItemApiAccess.ReadMember(item, "isSecretlyOtherItemPrefab") != null;
			if (!ItemVisibility.ShouldShow(entry.SpawnName, registered: true, flag, duplicatePrefab, includeSpecial))
			{
				return false;
			}
			object localCharacterItems = GetLocalCharacterItems();
			if (localCharacterItems == null)
			{
				return false;
			}
			MethodInfo methodInfo = FindSpawnMethod(localCharacterItems.GetType());
			if (methodInfo == null)
			{
				return false;
			}
			try
			{
				methodInfo.Invoke(localCharacterItems, new object[1] { entry.SpawnName });
				return true;
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static bool HasSpawnCapability(object characterItems)
		{
			if (characterItems == null)
			{
				return false;
			}
			Type type = ItemApiAccess.FindLoadedType("Photon.Pun.PhotonNetwork");
			if (!ItemApiAccess.HasStaticBooleanMember(type, "InRoom"))
			{
				return false;
			}
			Type type2 = ItemApiAccess.FindGameType("ItemDatabase");
			Type type3 = ItemApiAccess.FindGameType("Item");
			if (type2 == null || type3 == null || ItemApiAccess.ReadStaticMember(type2, "Instance") == null)
			{
				return false;
			}
			if (FindTryGetItemMethod(type2, type3) == null || FindSpawnMethod(characterItems.GetType()) == null)
			{
				return false;
			}
			MethodInfo method = type3.GetMethod("IsValidToSpawn", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, Type.EmptyTypes, null);
			if (method != null)
			{
				return method.ReturnType == typeof(bool);
			}
			return false;
		}

		private static bool TryGetDatabaseItem(string spawnName, out object item)
		{
			item = null;
			Type type = ItemApiAccess.FindGameType("ItemDatabase");
			Type type2 = ItemApiAccess.FindGameType("Item");
			if (type == null || type2 == null)
			{
				return false;
			}
			MethodInfo methodInfo = FindTryGetItemMethod(type, type2);
			if (methodInfo == null)
			{
				return false;
			}
			object[] array = new object[2] { spawnName, null };
			try
			{
				if (!(bool)methodInfo.Invoke(null, array))
				{
					return false;
				}
				item = array[1];
				return item != null;
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static MethodInfo FindTryGetItemMethod(Type databaseType, Type itemType)
		{
			MethodInfo[] methods = databaseType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != "TryGetItem") && !(methodInfo.ReturnType != typeof(bool)))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 2 && parameters[0].ParameterType == typeof(string) && parameters[1].ParameterType.IsByRef && parameters[1].ParameterType.GetElementType() == itemType)
					{
						return methodInfo;
					}
				}
			}
			return null;
		}

		private static bool IsInRoom()
		{
			Type type = ItemApiAccess.FindLoadedType("Photon.Pun.PhotonNetwork");
			return ItemVisibility.CanRequestSpawn(ItemApiAccess.ReadStaticBoolean(type, "InRoom"));
		}

		private static object GetLocalCharacterItems()
		{
			Type type = ItemApiAccess.FindGameType("Character");
			object target = ItemApiAccess.ReadStaticMember(type, "localCharacter");
			object target2 = ItemApiAccess.ReadMember(target, "refs");
			return ItemApiAccess.ReadMember(target2, "items");
		}

		private static MethodInfo FindSpawnMethod(Type type)
		{
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!(methodInfo.Name != "SpawnItemInHand") && !(methodInfo.ReturnType != typeof(void)))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
					{
						return methodInfo;
					}
				}
			}
			return null;
		}
	}
	internal static class ItemManualSpawnPolicy
	{
		internal static bool AllowsSoloOverride(object item, string spawnName)
		{
			if (item == null || SpecialItems.Classify(spawnName) != SpecialItemKind.MultiplayerItems)
			{
				return false;
			}
			try
			{
				Type type = ItemApiAccess.FindLoadedType("Photon.Pun.PhotonNetwork");
				if (!ItemApiAccess.HasStaticBooleanMember(type, "OfflineMode") || !ItemApiAccess.HasStaticBooleanMember(type, "InRoom"))
				{
					return false;
				}
				bool flag = ItemApiAccess.ReadStaticBoolean(type, "OfflineMode");
				if (!flag && ItemApiAccess.ReadStaticBoolean(type, "InRoom"))
				{
					object target = ItemApiAccess.ReadStaticMember(type, "CurrentRoom");
					object obj = ItemApiAccess.ReadMember(target, "PlayerCount");
					flag = obj is int && (int)obj == 1;
				}
				if (!flag)
				{
					return false;
				}
				Type type2 = ItemApiAccess.FindGameType("LootData");
				MethodInfo method = item.GetType().GetMethod("GetComponent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[1] { typeof(Type) }, null);
				if (type2 == null || method == null)
				{
					return false;
				}
				object obj2 = method.Invoke(item, new object[1] { type2 });
				if (obj2 == null)
				{
					return false;
				}
				object obj3 = ItemApiAccess.ReadMember(obj2, "banInSolo");
				object obj4 = ItemApiAccess.ReadMember(obj2, "excludeBasedOnCustomRunSetting");
				if (!(obj3 is bool) || !(bool)obj3 || !(obj4 is bool) || (bool)obj4)
				{
					return false;
				}
				FieldInfo field = type2.GetField("useOtherItemForSpawningValidity", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy);
				if (field == null || field.GetValue(obj2) != null)
				{
					return false;
				}
				Type type3 = ItemApiAccess.FindGameType("RunSettings");
				if (type3 == null)
				{
					return false;
				}
				MethodInfo method2 = type3.GetMethod("IsItemEnabled", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[1] { typeof(string) }, null);
				MethodInfo method3 = type3.GetMethod("IsItemEnabled", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy, null, new Type[1] { typeof(ushort) }, null);
				object obj5 = ItemApiAccess.ReadMember(item, "itemID");
				if (method2 == null || method3 == null || method2.ReturnType != typeof(bool) || method3.ReturnType != typeof(bool) || !(obj5 is ushort))
				{
					return false;
				}
				return (bool)method2.Invoke(null, new object[1] { spawnName }) && (bool)method3.Invoke(null, new object[1] { obj5 });
			}
			catch (Exception)
			{
				return false;
			}
		}
	}
	internal sealed class ItemCatalogEntry
	{
		public readonly string SpawnName;

		public readonly string CurrentName;

		public readonly string EnglishName;

		public readonly string SimplifiedName;

		public readonly string TraditionalName;

		public readonly Texture2D Icon;

		public readonly ItemCategory Categories;

		public readonly ConsumableKind Kinds;

		public readonly bool ValidToSpawn;

		public readonly bool CanSpawnManually;

		private readonly string englishDescription;

		private readonly string simplifiedDescription;

		private readonly string traditionalDescription;

		public string DisplayName(string language)
		{
			string text = language switch
			{
				"en" => EnglishName, 
				"zh-CN" => SimplifiedName, 
				"zh-TW" => TraditionalName, 
				_ => CurrentName, 
			};
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			return CurrentName;
		}

		public string DisplayDescription(string language)
		{
			string text = ((language == "zh-TW") ? traditionalDescription : ((language == "zh-CN") ? simplifiedDescription : englishDescription));
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text.Trim();
			}
			return string.Empty;
		}

		public ItemCatalogEntry(string spawnName, string currentName, string englishName, string simplifiedName, string traditionalName, Texture2D icon, ItemCategory categories, ConsumableKind kinds, string englishDescription = null, string simplifiedDescription = null, string traditionalDescription = null, bool validToSpawn = true, bool? canSpawnManually = null)
		{
			SpawnName = spawnName;
			CurrentName = currentName;
			EnglishName = englishName;
			SimplifiedName = simplifiedName;
			TraditionalName = traditionalName;
			Icon = icon;
			Categories = categories;
			Kinds = kinds;
			ValidToSpawn = validToSpawn;
			CanSpawnManually = canSpawnManually ?? validToSpawn;
			this.englishDescription = englishDescription;
			this.simplifiedDescription = simplifiedDescription;
			this.traditionalDescription = traditionalDescription;
		}
	}
	[Flags]
	internal enum ItemCategory
	{
		None = 0,
		Mystical = 1,
		Consumable = 2,
		Deployable = 4,
		Equipment = 8,
		Other = 0x10
	}
	[Flags]
	internal enum ConsumableKind
	{
		None = 0,
		Food = 1,
		Healing = 2,
		Recovery = 4,
		Buff = 8,
		Revive = 0x10
	}
	internal static class ItemClassification
	{
		private static readonly Dictionary<string, ItemCategory> Known = BuildCategories();

		public static ItemCategory Categories(string uiName, string nativeTags)
		{
			ItemCategory value;
			bool flag = Known.TryGetValue((uiName ?? string.Empty).Trim(), out value);
			if (HasTag(nativeTags, "Mystical") || HasTag(nativeTags, "ScoutAmulet") || HasTag(nativeTags, "GoldenIdol") || HasTag(nativeTags, "BookOfBones"))
			{
				value |= ItemCategory.Mystical;
			}
			if (!flag && IsFoodTag(nativeTags))
			{
				value |= ItemCategory.Consumable;
			}
			if (value != ItemCategory.None)
			{
				return value;
			}
			return ItemCategory.Other;
		}

		public static ConsumableKind Kinds(string uiName, string nativeTags)
		{
			if ((Categories(uiName, nativeTags) & ItemCategory.Consumable) == 0)
			{
				return ConsumableKind.None;
			}
			ConsumableKind consumableKind = ConsumableKind.None;
			switch ((uiName ?? string.Empty).Trim().ToLowerInvariant())
			{
			case "medicinal root":
			case "medicinalroot":
				consumableKind = ConsumableKind.Food | ConsumableKind.Healing | ConsumableKind.Recovery;
				break;
			case "napberry":
				consumableKind = ConsumableKind.Food | ConsumableKind.Healing | ConsumableKind.Recovery;
				break;
			case "froglegs":
				consumableKind = ConsumableKind.Food;
				break;
			case "mandrake":
				consumableKind = ConsumableKind.Food | ConsumableKind.Recovery;
				break;
			case "bandages":
				consumableKind = ConsumableKind.Healing;
				break;
			case "first aid kit":
			case "firstaidkit":
			case "cure-all":
			case "remedy fungus":
				consumableKind = ConsumableKind.Healing | ConsumableKind.Recovery;
				break;
			case "antidote":
			case "heat pack":
				consumableKind = ConsumableKind.Recovery;
				break;
			case "sunscreen":
				consumableKind = ConsumableKind.Buff;
				break;
			case "energy drink":
			case "sports drink":
				consumableKind = ConsumableKind.Buff;
				break;
			case "fortified milk":
			case "fortifiedmilk":
				consumableKind = ConsumableKind.Healing | ConsumableKind.Buff;
				break;
			case "scout effigy":
			case "scouteffigy":
				consumableKind = ConsumableKind.Revive;
				break;
			}
			if (IsFoodTag(nativeTags))
			{
				consumableKind |= ConsumableKind.Food;
			}
			return consumableKind;
		}

		public static bool Matches(ItemCategory categories, ConsumableKind kinds, ItemCategory category, ConsumableKind kind)
		{
			if (category == ItemCategory.None || (categories & category) != ItemCategory.None)
			{
				if (kind != ConsumableKind.None)
				{
					return (kinds & kind) != 0;
				}
				return true;
			}
			return false;
		}

		public static ConsumableKind[] AvailableKinds(IEnumerable<ItemCatalogEntry> catalog)
		{
			ConsumableKind consumableKind = ConsumableKind.None;
			foreach (ItemCatalogEntry item in catalog)
			{
				if ((item.Categories & ItemCategory.Consumable) != ItemCategory.None)
				{
					consumableKind |= item.Kinds;
				}
			}
			List<ConsumableKind> list = new List<ConsumableKind>();
			list.Add(ConsumableKind.None);
			List<ConsumableKind> list2 = list;
			ConsumableKind[] array = new ConsumableKind[5]
			{
				ConsumableKind.Food,
				ConsumableKind.Healing,
				ConsumableKind.Recovery,
				ConsumableKind.Buff,
				ConsumableKind.Revive
			};
			foreach (ConsumableKind consumableKind2 in array)
			{
				if ((consumableKind & consumableKind2) != ConsumableKind.None)
				{
					list2.Add(consumableKind2);
				}
			}
			return list2.ToArray();
		}

		private static bool IsFoodTag(string tags)
		{
			if (!HasTag(tags, "PackagedFood") && !HasTag(tags, "Berry") && !HasTag(tags, "Mushroom") && !HasTag(tags, "GourmandRequirement"))
			{
				return HasTag(tags, "Bird");
			}
			return true;
		}

		private static bool HasTag(string tags, string expected)
		{
			string[] array = (tags ?? string.Empty).Split(new char[1] { ',' });
			foreach (string text in array)
			{
				if (string.Equals(text.Trim(), expected, StringComparison.Ordinal))
				{
					return true;
				}
			}
			return false;
		}

		private static Dictionary<string, ItemCategory> BuildCategories()
		{
			Dictionary<string, ItemCategory> dictionary = new Dictionary<string, ItemCategory>(StringComparer.OrdinalIgnoreCase);
			Add(dictionary, ItemCategory.Mystical, "AMULET_CLONE|AMULET_DOUBLEJUMP|AMULET_HEALING|AMULET_INFINITESTAM|Ancient Idol|anti-rope cannon|anti-rope spool|Bugle of Friendship|Cursed Skull|Faerie Lantern|RitualDagger|Scout Effigy|ScoutEffigy|Scoutmaster's Bugle|SCOUTMASTERSOUL|ScoutsHonor|Strange Gem|THEBOOKOFBONES|VOIDLAUNCHER|Warp Compass|Warp Fungus");
			Add(dictionary, ItemCategory.Consumable, "Airline Food|Aloe Vera|Antidote|Balloon|Balloon Bunch|Bandages|Big Egg|Big Lollipop|Bird|Black Clusterberry|Blue Berrynana|Blue Shroomberry|Brown Berrynana|Bugle Shroom|Button Shroom|Cactus|Chubby Shroom|Cluster Shroom|Coconut|Coconut Half|Cure-All|EARLYWORM|Egg|Energy Drink|First Aid Kit|FirstAidKit|Fortified Milk|FortifiedMilk|FrogLegs|Gold Prickleberry|Granola Bar|Green Clusterberry|Green Crispberry|Green Kingberry|Green Shroomberry|Heat Pack|Honeycomb|Hot Dog|Mandrake|Marshmallow|Medicinal Root|MedicinalRoot|Napberry|Orange Winterberry|Pandora's Lunchbox|Pink Berrynana|Purple Kingberry|Purple Shroomberry|Red Clusterberry|Red Crispberry|Red Prickleberry|Red Shroomberry|Remedy Fungus|Scorchberry|Scout Cookies|Scout Effigy|ScoutEffigy|Small Egg|Sports Drink|Sunscreen|Tick|Trail Mix|Yellow Berrynana|Yellow Clusterberry|Yellow Crispberry|Yellow Kingberry|Yellow Shroomberry|Yellow Winterberry");
			Add(dictionary, ItemCategory.Deployable, "Magic Bean");
			Add(dictionary, ItemCategory.Deployable, "anti-rope cannon|anti-rope spool|Bounce Fungus|Chain Launcher|Checkpoint Flag|Cloud Fungus|Piton|Portable Stove|rope cannon|rope spool|Scout Cannon|Shelf Fungus|VOIDLAUNCHER");
			Add(dictionary, ItemCategory.Equipment, "Backpack|Binoculars|Bugle|Candlestick|Compass|Fannypack|Flare|Glider|Guidebook|Jetpack|Lantern|Megaphone|Parasol|Pirate's Compass|Rescue Claw|Rocketpack|Torch");
			Add(dictionary, ItemCategory.Other, "Beehive|Berrynana Peel|Bing Bong|Blowgun|Conch|Dynamite|Frisbee|Frog|Scorpion|Scroll|SNOWBALL|Stick|Stone|Torn Page");
			return dictionary;
		}

		private static void Add(Dictionary<string, ItemCategory> map, ItemCategory category, string names)
		{
			string[] array = names.Split(new char[1] { '|' });
			foreach (string key in array)
			{
				map.TryGetValue(key, out var value);
				map[key] = value | category;
			}
		}
	}
	internal static class ChineseScript
	{
		public static string ToSimplified(string value)
		{
			return Convert(value, "zh-CN", 33554432u);
		}

		public static string ToTraditional(string value)
		{
			return Convert(value, "zh-TW", 67108864u);
		}

		private static string Convert(string value, string locale, uint flags)
		{
			if (string.IsNullOrEmpty(value) || Environment.OSVersion.Platform != PlatformID.Win32NT)
			{
				return value;
			}
			try
			{
				int num = LCMapStringEx(locale, flags, value, -1, null, 0, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);
				if (num == 0)
				{
					return value;
				}
				StringBuilder stringBuilder = new StringBuilder(num);
				return (LCMapStringEx(locale, flags, value, -1, stringBuilder, num, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero) == 0) ? value : stringBuilder.ToString();
			}
			catch (DllNotFoundException)
			{
				return value;
			}
			catch (EntryPointNotFoundException)
			{
				return value;
			}
		}

		[DllImport("kernel32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
		private static extern int LCMapStringEx(string locale, uint flags, string source, int sourceLength, StringBuilder destination, int destinationLength, IntPtr version, IntPtr reserved, IntPtr sortHandle);
	}
	internal sealed class ItemDescriptions
	{
		private readonly Dictionary<string, string> english;

		private readonly Dictionary<string, string> simplified;

		public ItemDescriptions(Action<string> warning = null)
		{
			english = Read("peak-item-tooltip.descriptions.json", warning);
			simplified = Read("peak-item-tooltip.descriptions.zh-CN.json", warning);
		}

		public string Get(string language, params string[] names)
		{
			string text = Find((language == "zh-CN" || language == "zh-TW") ? simplified : english, names);
			if (!(language == "zh-TW"))
			{
				return text;
			}
			return ChineseScript.ToTraditional(text);
		}

		private static string Find(Dictionary<string, string> table, string[] names)
		{
			foreach (string text in names)
			{
				if (!string.IsNullOrWhiteSpace(text) && table.TryGetValue(text.Trim(), out var value))
				{
					return value;
				}
			}
			return string.Empty;
		}

		private static Dictionary<string, string> Read(string fileName, Action<string> warning)
		{
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Invalid comparison between Unknown and I4
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			JToken obj = EmbeddedJson.Read("Descriptions." + fileName, warning)["items"];
			JObject val = (JObject)(object)((obj is JObject) ? obj : null);
			if (val == null)
			{
				return dictionary;
			}
			foreach (JProperty item in val.Properties())
			{
				JToken value = item.Value;
				JObject val2 = (JObject)(object)((value is JObject) ? value : null);
				JToken val3 = ((val2 == null) ? null : val2["description"]);
				if (val3 != null && (int)val3.Type == 8)
				{
					string text = (string)val3;
					if (!string.IsNullOrWhiteSpace(text))
					{
						dictionary[item.Name.Trim()] = text.Trim();
					}
				}
			}
			return dictionary;
		}
	}
	internal static class ItemSearch
	{
		public static bool Matches(string query, string currentName, string englishName, string simplifiedName, string traditionalName)
		{
			string text = Normalize(query);
			if (text.Length == 0)
			{
				return true;
			}
			string[] array = new string[4]
			{
				currentName ?? string.Empty,
				englishName ?? string.Empty,
				simplifiedName ?? string.Empty,
				traditionalName ?? string.Empty
			};
			string[] array2 = array;
			foreach (string value in array2)
			{
				if (Contains(value, text))
				{
					return true;
				}
			}
			bool flag = false;
			string[] array3 = array;
			foreach (string value2 in array3)
			{
				if (Contains(value2, "glider") || Contains(value2, "滑翔翼"))
				{
					flag = true;
					break;
				}
			}
			if (flag)
			{
				if (!Contains("傘 umbrella", text))
				{
					return Contains("umbrella", text);
				}
				return true;
			}
			return false;
		}

		private static bool Contains(string value, string query)
		{
			return Normalize(value).IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static string Normalize(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder(value.Length);
			foreach (char c in value)
			{
				if (!char.IsWhiteSpace(c))
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return ChineseScript.ToSimplified(stringBuilder.ToString());
		}
	}
	internal static class ItemVisibility
	{
		private static readonly HashSet<string> HiddenPrefabs = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"ClimbingChalk", "Climbing Chalk", "Clusterberry_UNUSED", "FireWood", "GuidebookPage_13_FirstTeams", "Megaphone", "Mushroom Glow", "Weird Shroom", "Stone", "Skull",
			"Warpsketball", "Basketball", "Cheat Compass", "Cheat Compass 1", "BingBong_Prop Variant", "Binoculars_Prop", "Bugle_Prop Variant", "GuidebookPage", "Lollipop_Prop", "Mandrake_Hidden",
			"Parasol_Roots Variant", "Parachute", "AUTOPARACHUTE", "ScoutCookies_Vanilla", "Warp Compass", "RescueHook_Infinite", "ScoutmasterSoul"
		};

		public static bool ShouldShow(string spawnName, bool registered, bool canSpawn, bool duplicatePrefab, bool includeSpecial = false)
		{
			if (string.IsNullOrWhiteSpace(spawnName) || !registered || duplicatePrefab)
			{
				return false;
			}
			SpecialItemKind specialItemKind = SpecialItems.Classify(spawnName);
			if (specialItemKind == SpecialItemKind.MultiplayerItems)
			{
				if (!canSpawn)
				{
					return includeSpecial;
				}
				return true;
			}
			if (!canSpawn)
			{
				return false;
			}
			if (specialItemKind != SpecialItemKind.None)
			{
				return includeSpecial;
			}
			string text = NormalizePrefabName(spawnName);
			if (!HiddenPrefabs.Contains(text) && !text.StartsWith("C_", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("GuidebookPage", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("Basketball", StringComparison.OrdinalIgnoreCase) && text.IndexOf("_Prop", StringComparison.OrdinalIgnoreCase) < 0 && !text.StartsWith("Cheat ", StringComparison.OrdinalIgnoreCase) && !text.EndsWith("_UNUSED", StringComparison.OrdinalIgnoreCase) && !text.EndsWith("_TEMP", StringComparison.OrdinalIgnoreCase))
			{
				return !text.EndsWith("_Hidden", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		public static bool CanRequestSpawn(bool inRoom)
		{
			return inRoom;
		}

		private static string NormalizePrefabName(string value)
		{
			return (value ?? string.Empty).Replace("(Clone)", string.Empty).Trim();
		}
	}
	internal enum SpecialItemKind
	{
		None,
		SpecialVariants,
		UnusedItems,
		MultiplayerItems,
		LobbyToys,
		MiscellaneousItems
	}
	internal static class SpecialItems
	{
		private static readonly HashSet<string> Chess = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"C_King B", "C_King W", "C_Queen B", "C_Queen W", "C_Rook B", "C_Rook W", "C_Bishop B", "C_Bishop W", "C_Knight B", "C_Knight W",
			"C_Pawn B", "C_Pawn W"
		};

		private static readonly HashSet<string> LobbyProps = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Binoculars_Prop", "Lollipop_Prop", "Bugle_Prop Variant", "BingBong_Prop Variant", "Passport" };

		private static readonly HashSet<string> Multiplayer = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Fannypack", "Cursed Skull", "Bugle_Magic", "ScoutEffigy", "HealingDart Variant", "RitualDagger" };

		private static readonly HashSet<string> Miscellaneous = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
		{
			"GuidebookPage", "GuidebookPageScroll Variant", "GuidebookPage_0_Intro", "GuidebookPage_10_Sleepy", "GuidebookPage_11_Awake", "GuidebookPage_12_Crashout", "GuidebookPage_13_FirstTeams", "GuidebookPage_1_Mushrooms", "GuidebookPage_2_Campfire", "GuidebookPage_3_Revival",
			"GuidebookPage_4_Poison", "GuidebookPage_5_Zombies", "GuidebookPage_6_BodyHeat", "GuidebookPage_7_BurningSun", "GuidebookPage_8_Magma", "GuidebookPage_9_Gloom"
		};

		public static SpecialItemKind Classify(string spawnName)
		{
			string text = Normalize(spawnName);
			if (EqualsName(text, "RescueHook_Infinite") || EqualsName(text, "ScoutCookies_Vanilla"))
			{
				return SpecialItemKind.SpecialVariants;
			}
			if (EqualsName(text, "Parachute"))
			{
				return SpecialItemKind.UnusedItems;
			}
			if (LobbyProps.Contains(text) || Chess.Contains(text) || EqualsName(text, "Basketball") || EqualsName(text, "Warpsketball"))
			{
				return SpecialItemKind.LobbyToys;
			}
			if (Miscellaneous.Contains(text))
			{
				return SpecialItemKind.MiscellaneousItems;
			}
			if (Multiplayer.Contains(text))
			{
				return SpecialItemKind.MultiplayerItems;
			}
			return SpecialItemKind.None;
		}

		public static SpecialItemKind[] AvailableKinds(IEnumerable<ItemCatalogEntry> catalog)
		{
			HashSet<SpecialItemKind> hashSet = new HashSet<SpecialItemKind>();
			foreach (ItemCatalogEntry item2 in catalog)
			{
				hashSet.Add(Classify(item2.SpawnName));
			}
			List<SpecialItemKind> list = new List<SpecialItemKind>();
			list.Add(SpecialItemKind.None);
			List<SpecialItemKind> list2 = list;
			SpecialItemKind[] array = new SpecialItemKind[5]
			{
				SpecialItemKind.SpecialVariants,
				SpecialItemKind.UnusedItems,
				SpecialItemKind.MultiplayerItems,
				SpecialItemKind.LobbyToys,
				SpecialItemKind.MiscellaneousItems
			};
			foreach (SpecialItemKind item in array)
			{
				if (hashSet.Contains(item))
				{
					list2.Add(item);
				}
			}
			return list2.ToArray();
		}

		public static string DisplayName(string spawnName, Localization loc)
		{
			string text = Normalize(spawnName);
			if (EqualsName(text, "RescueHook_Infinite"))
			{
				return loc.Text("InfiniteHookName");
			}
			if (EqualsName(text, "ScoutCookies_Vanilla"))
			{
				return loc.Text("VanillaCookiesName");
			}
			if (EqualsName(text, "Parachute"))
			{
				return loc.Text("ParachuteName");
			}
			if (EqualsName(text, "Basketball"))
			{
				return loc.Text("BasketballName");
			}
			if (EqualsName(text, "Warpsketball"))
			{
				return loc.Text("WarpsketballName");
			}
			if (EqualsName(text, "GuidebookPageScroll Variant"))
			{
				return loc.Text("ScrollName");
			}
			if (EqualsName(text, "Binoculars_Prop"))
			{
				return loc.Text("LobbyBinocularsName");
			}
			if (EqualsName(text, "Lollipop_Prop"))
			{
				return loc.Text("LobbyLollipopName");
			}
			if (EqualsName(text, "Bugle_Prop Variant"))
			{
				return loc.Text("LobbyBugleName");
			}
			if (EqualsName(text, "BingBong_Prop Variant"))
			{
				return loc.Text("LobbyBingBongName");
			}
			if (EqualsName(text, "Passport"))
			{
				return loc.Text("LobbyPassportName");
			}
			if (EqualsName(text, "Cursed Skull"))
			{
				return loc.Text("CursedSkullName");
			}
			if (EqualsName(text, "Bugle_Magic"))
			{
				return loc.Text("FriendshipBugleName");
			}
			if (EqualsName(text, "ScoutEffigy"))
			{
				return loc.Text("ScoutEffigyName");
			}
			if (EqualsName(text, "HealingDart Variant"))
			{
				return loc.Text("BlowgunName");
			}
			if (EqualsName(text, "RitualDagger"))
			{
				return loc.Text("RitualDaggerName");
			}
			if (EqualsName(text, "Fannypack"))
			{
				return loc.Text("FannypackName");
			}
			if (!Chess.Contains(text))
			{
				return string.Empty;
			}
			string text2 = text.Substring(2).Split(' ', '_')[0].ToLowerInvariant();
			string key = (text.EndsWith(" B", StringComparison.OrdinalIgnoreCase) ? "ChessBlack" : "ChessWhite");
			return loc.Format(key, loc.Text("Chess_" + text2));
		}

		public static string Description(string spawnName, Localization loc)
		{
			string text = Normalize(spawnName);
			if (EqualsName(text, "RescueHook_Infinite"))
			{
				return loc.Text("InfiniteHookDescription");
			}
			if (EqualsName(text, "ScoutCookies_Vanilla"))
			{
				return loc.Text("VanillaCookiesDescription");
			}
			if (EqualsName(text, "Parachute"))
			{
				return loc.Text("ParachuteDescription");
			}
			if (EqualsName(text, "Basketball"))
			{
				return loc.Text("BasketballDescription");
			}
			if (EqualsName(text, "Warpsketball"))
			{
				return loc.Text("WarpsketballDescription");
			}
			if (!Chess.Contains(text))
			{
				return string.Empty;
			}
			return loc.Text("ChessDescription");
		}

		private static bool EqualsName(string name, string expected)
		{
			return string.Equals(name, expected, StringComparison.OrdinalIgnoreCase);
		}

		private static string Normalize(string name)
		{
			return (name ?? string.Empty).Replace("(Clone)", string.Empty).Trim();
		}
	}
}
namespace PeakAdminToolkit.UI
{
	internal sealed class SpecialItemFilter
	{
		private ItemCategory category;

		public bool Enabled { get; private set; }

		public SpecialItemKind SelectedKind { get; private set; }

		public void SetCategory(ItemCategory value)
		{
			if (category != value)
			{
				Reset();
			}
			category = value;
		}

		public void SetEnabled(bool value)
		{
			Enabled = value && category == ItemCategory.Other;
			SelectedKind = SpecialItemKind.None;
		}

		public void SelectKind(SpecialItemKind kind)
		{
			SelectedKind = (Enabled ? kind : SpecialItemKind.None);
		}

		public void Reset()
		{
			Enabled = false;
			SelectedKind = SpecialItemKind.None;
		}

		public bool Matches(ItemCatalogEntry item)
		{
			SpecialItemKind specialItemKind = SpecialItems.Classify(item.SpawnName);
			if (specialItemKind != SpecialItemKind.None && (specialItemKind != SpecialItemKind.MultiplayerItems || !item.CanSpawnManually) && (!Enabled || category != ItemCategory.Other))
			{
				return false;
			}
			if (SelectedKind != SpecialItemKind.None)
			{
				return SelectedKind == specialItemKind;
			}
			return true;
		}
	}
	internal sealed class CursorLease
	{
		private bool acquired;

		private CursorLockMode previousLock;

		private bool previousVisibility;

		public void Acquire()
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!acquired)
			{
				previousLock = Cursor.lockState;
				previousVisibility = Cursor.visible;
				acquired = true;
			}
			Cursor.lockState = (CursorLockMode)0;
			Cursor.visible = true;
		}

		public void Release(bool anotherWindowNeedsCursor)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (acquired)
			{
				acquired = false;
				if (!anotherWindowNeedsCursor)
				{
					Cursor.lockState = previousLock;
					Cursor.visible = previousVisibility;
				}
			}
		}
	}
	internal static class WindowBounds
	{
		private const float ScreenMargin = 16f;

		public static float ScaleFor(float screenWidth, float screenHeight)
		{
			if (screenWidth <= 0f || screenHeight <= 0f)
			{
				return 1f;
			}
			return Mathf.Min(screenWidth / 1920f, screenHeight / 1080f);
		}

		public static Rect Centered(float screenWidth, float screenHeight)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			float num = ScaleFor(screenWidth, screenHeight);
			return Clamp(new Rect((screenWidth / num - 1200f) / 2f, (screenHeight / num - 800f) / 2f, 1200f, 800f), screenWidth / num, screenHeight / num);
		}

		public static Rect Clamp(Rect window, float screenWidth, float screenHeight)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			((Rect)(ref window)).width = Mathf.Min(((Rect)(ref window)).width, Mathf.Max(1f, screenWidth - 16f));
			((Rect)(ref window)).height = Mathf.Min(((Rect)(ref window)).height, Mathf.Max(1f, screenHeight - 16f));
			((Rect)(ref window)).x = Mathf.Clamp(((Rect)(ref window)).x, 0f, Mathf.Max(0f, screenWidth - ((Rect)(ref window)).width));
			((Rect)(ref window)).y = Mathf.Clamp(((Rect)(ref window)).y, 0f, Mathf.Max(0f, screenHeight - ((Rect)(ref window)).height));
			return window;
		}
	}
	internal static class ItemGridLayout
	{
		private const float Gap = 8f;

		private const float MinWidth = 140f;

		private const float Height = 142f;

		public static int Columns(float width)
		{
			return Math.Max(1, (int)((width + 8f) / 148f));
		}

		public static Rect CardRect(int index, float width)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			int num = Columns(width);
			float num2 = (width - 8f * (float)(num - 1)) / (float)num;
			return new Rect((float)(index % num) * (num2 + 8f), (float)(index / num) * 150f, num2, 142f);
		}

		public static float ContentHeight(int count, float width)
		{
			if (count != 0)
			{
				return (float)((count - 1) / Columns(width) + 1) * 150f - 8f;
			}
			return 0f;
		}

		public static float ContentWidth(float viewportWidth)
		{
			return Math.Max(1f, viewportWidth - 24f);
		}

		public static Rect StatusRect(Rect card, float measuredHeight)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			float num = ((measuredHeight > 0f) ? Math.Max(28f, (float)Math.Ceiling(measuredHeight) + 4f) : 0f);
			return new Rect(((Rect)(ref card)).x + 8f, ((Rect)(ref card)).y + 92f - num, ((Rect)(ref card)).width - 16f, num);
		}

		public static Rect IconRect(Rect card, float statusHeight)
		{
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			float num = ((statusHeight > 0f) ? Math.Min(80f, 80f - statusHeight) : 80f);
			return new Rect(((Rect)(ref card)).x + (((Rect)(ref card)).width - num) / 2f, ((Rect)(ref card)).y + 8f, num, num);
		}

		public static int HitTest(Rect viewport, Vector2 scroll, Vector2 pointer, int count)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			float num = ContentWidth(((Rect)(ref viewport)).width);
			if (pointer.x < ((Rect)(ref viewport)).x || pointer.x >= ((Rect)(ref viewport)).x + num || pointer.y < ((Rect)(ref viewport)).y || pointer.y >= ((Rect)(ref viewport)).y + ((Rect)(ref viewport)).height)
			{
				return -1;
			}
			float num2 = pointer.x - ((Rect)(ref viewport)).x + scroll.x;
			float num3 = pointer.y - ((Rect)(ref viewport)).y + scroll.y;
			for (int i = 0; i < count; i++)
			{
				Rect val = CardRect(i, num);
				if (num2 >= ((Rect)(ref val)).x && num2 < ((Rect)(ref val)).x + ((Rect)(ref val)).width && num3 >= ((Rect)(ref val)).y && num3 < ((Rect)(ref val)).y + ((Rect)(ref val)).height)
				{
					return i;
				}
			}
			return -1;
		}
	}
	internal sealed class Theme : IDisposable
	{
		private bool ready;

		private Font font;

		private Texture2D windowBackground;

		private Texture2D panelBackground;

		private Texture2D buttonBackground;

		private Texture2D buttonHoverBackground;

		private Texture2D buttonActiveBackground;

		private Texture2D buttonSelectedHoverBackground;

		public GUIStyle Window { get; private set; }

		public GUIStyle Heading { get; private set; }

		public GUIStyle Body { get; private set; }

		public GUIStyle Small { get; private set; }

		public GUIStyle Button { get; private set; }

		public GUIStyle Toggle { get; private set; }

		public GUIStyle Card { get; private set; }

		public GUIStyle CardName { get; private set; }

		public GUIStyle Tooltip { get; private set; }

		public GUIStyle TextField { get; private set; }

		public void Ensure()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Expected O, but got Unknown
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Expected O, but got Unknown
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Expected O, but got Unknown
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Expected O, but got Unknown
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: Expected O, but got Unknown
			//IL_0264: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Expected O, but got Unknown
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Expected O, but got Unknown
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ac: Expected O, but got Unknown
			//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c4: Expected O, but got Unknown
			//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d0: Expected O, but got Unknown
			//IL_03de: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: Expected O, but got Unknown
			//IL_0400: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Expected O, but got Unknown
			//IL_0424: Unknown result type (might be due to invalid IL or missing references)
			//IL_042b: Expected O, but got Unknown
			//IL_0448: Unknown result type (might be due to invalid IL or missing references)
			//IL_0452: Expected O, but got Unknown
			//IL_0473: Unknown result type (might be due to invalid IL or missing references)
			//IL_0488: Unknown result type (might be due to invalid IL or missing references)
			//IL_048f: Expected O, but got Unknown
			//IL_04b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c0: Expected O, but got Unknown
			//IL_04f2: Unknown result type (might be due to invalid IL or missing references)
			if (!ready)
			{
				font = Font.CreateDynamicFontFromOSFont(new string[4] { "Microsoft JhengHei", "Microsoft YaHei", "Arial Unicode MS", "Arial" }, 20);
				windowBackground = CreateTexture(new Color(0.075f, 0.09f, 0.105f, 0.98f));
				panelBackground = CreateTexture(new Color(0.12f, 0.15f, 0.17f, 0.99f));
				buttonBackground = CreateTexture(new Color(0.22f, 0.27f, 0.3f, 1f));
				buttonHoverBackground = CreateTexture(new Color(0.3f, 0.39f, 0.43f, 1f));
				buttonActiveBackground = CreateTexture(new Color(0.1f, 0.39f, 0.36f, 1f));
				buttonSelectedHoverBackground = CreateTexture(new Color(0.15f, 0.5f, 0.45f, 1f));
				GUIStyle val = new GUIStyle(GUI.skin.box);
				val.fontSize = 18;
				val.fontStyle = (FontStyle)1;
				val.alignment = (TextAnchor)1;
				val.padding = new RectOffset(16, 16, 16, 16);
				val.contentOffset = Vector2.zero;
				Window = val;
				SetBackground(Window, windowBackground);
				SetTextColor(Window, new Color(0.96f, 0.98f, 1f, 1f));
				GUIStyle val2 = new GUIStyle(GUI.skin.label);
				val2.wordWrap = true;
				val2.richText = true;
				val2.fontSize = 20;
				Body = val2;
				SetTextColor(Body, new Color(0.94f, 0.96f, 0.98f, 1f));
				GUIStyle val3 = new GUIStyle(Body);
				val3.fontSize = 26;
				val3.fontStyle = (FontStyle)1;
				val3.wordWrap = false;
				val3.alignment = (TextAnchor)3;
				Heading = val3;
				GUIStyle val4 = new GUIStyle(GUI.skin.label);
				val4.fontSize = 16;
				val4.wordWrap = true;
				val4.richText = true;
				Small = val4;
				SetTextColor(Small, new Color(0.82f, 0.87f, 0.9f, 1f));
				GUIStyle val5 = new GUIStyle(GUI.skin.button);
				val5.fixedHeight = 38f;
				val5.fontSize = 18;
				Button = val5;
				SetButtonState(Button.normal, buttonBackground);
				SetButtonState(Button.hover, buttonHoverBackground);
				SetButtonState(Button.focused, buttonHoverBackground);
				SetButtonState(Button.active, buttonActiveBackground);
				SetButtonState(Button.onNormal, buttonActiveBackground);
				SetButtonState(Button.onHover, buttonSelectedHoverBackground);
				SetButtonState(Button.onFocused, buttonSelectedHoverBackground);
				SetButtonState(Button.onActive, buttonActiveBackground);
				GUIStyle val6 = new GUIStyle(GUI.skin.toggle);
				val6.fontSize = 18;
				val6.fixedHeight = 32f;
				Toggle = val6;
				SetTextColor(Toggle, new Color(0.94f, 0.96f, 0.98f, 1f));
				GUIStyle val7 = new GUIStyle(Button);
				val7.fixedHeight = 0f;
				val7.margin = new RectOffset();
				val7.padding = new RectOffset();
				Card = val7;
				GUIStyle val8 = new GUIStyle(Body);
				val8.fontSize = 18;
				val8.richText = false;
				val8.alignment = (TextAnchor)4;
				val8.padding = new RectOffset();
				val8.clipping = (TextClipping)1;
				CardName = val8;
				GUIStyle val9 = new GUIStyle(GUI.skin.textField);
				val9.fontSize = 20;
				val9.fixedHeight = 42f;
				val9.padding = new RectOffset(10, 10, 7, 7);
				TextField = val9;
				SetTextColor(TextField, new Color(0.96f, 0.98f, 1f, 1f));
				GUIStyle val10 = new GUIStyle(GUI.skin.box);
				val10.alignment = (TextAnchor)0;
				val10.wordWrap = true;
				val10.richText = true;
				val10.fontSize = 16;
				val10.padding = new RectOffset(8, 8, 6, 6);
				Tooltip = val10;
				SetBackground(Tooltip, panelBackground);
				SetTextColor(Tooltip, new Color(0.94f, 0.96f, 0.98f, 1f));
				if ((Object)(object)font != (Object)null)
				{
					GUIStyle window = Window;
					GUIStyle heading = Heading;
					GUIStyle body = Body;
					GUIStyle small = Small;
					GUIStyle button = Button;
					GUIStyle toggle = Toggle;
					GUIStyle card = Card;
					GUIStyle cardName = CardName;
					GUIStyle tooltip = Tooltip;
					Font val11 = (TextField.font = font);
					Font val13 = (tooltip.font = val11);
					Font val15 = (cardName.font = val13);
					Font val17 = (card.font = val15);
					Font val19 = (toggle.font = val17);
					Font val21 = (button.font = val19);
					Font val23 = (small.font = val21);
					Font val25 = (body.font = val23);
					Font val27 = (heading.font = val25);
					window.font = val27;
				}
				ready = true;
			}
		}

		private static Texture2D CreateTexture(Color color)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Expected O, but got Unknown
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false);
			((Object)val).hideFlags = (HideFlags)61;
			val.SetPixel(0, 0, color);
			val.Apply();
			return val;
		}

		private static void SetBackground(GUIStyle style, Texture2D texture)
		{
			style.normal.background = texture;
			style.hover.background = texture;
			style.active.background = texture;
			style.focused.background = texture;
			style.onNormal.background = texture;
			style.onHover.background = texture;
			style.onActive.background = texture;
			style.onFocused.background = texture;
		}

		private static void SetTextColor(GUIStyle style, Color color)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			style.normal.textColor = color;
			style.hover.textColor = color;
			style.active.textColor = color;
			style.focused.textColor = color;
			style.onNormal.textColor = color;
			style.onHover.textColor = color;
			style.onActive.textColor = color;
			style.onFocused.textColor = color;
		}

		private static void SetButtonState(GUIStyleState state, Texture2D background)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			state.background = background;
			state.textColor = new Color(0.97f, 0.98f, 1f, 1f);
		}

		private static void DestroyTexture(Texture2D texture)
		{
			if ((Object)(object)texture != (Object)null)
			{
				Object.Destroy((Object)(object)texture);
			}
		}

		public void Dispose()
		{
			Window = null;
			Heading = null;
			Body = null;
			Small = null;
			Button = null;
			Card = null;
			CardName = null;
			Tooltip = null;
			TextField = null;
			if ((Object)(object)font != (Object)null)
			{
				Object.Destroy((Object)(object)font);
			}
			font = null;
			DestroyTexture(windowBackground);
			DestroyTexture(panelBackground);
			DestroyTexture(buttonBackground);
			DestroyTexture(buttonHoverBackground);
			DestroyTexture(buttonActiveBackground);
			DestroyTexture(buttonSelectedHoverBackground);
			windowBackground = null;
			panelBackground = null;
			buttonBackground = null;
			buttonHoverBackground = null;
			buttonActiveBackground = null;
			buttonSelectedHoverBackground = null;
			ready = false;
		}
	}
	internal sealed class Toast
	{
		private string message;

		private double expires;

		public void Show(string text, double now, double duration)
		{
			message = text;
			expires = now + duration;
		}

		public string Current(double now)
		{
			if (!(now < expires))
			{
				return null;
			}
			return message;
		}

		public void Clear()
		{
			message = null;
		}
	}
	internal sealed class Tooltip
	{
		private string target;

		private string content;

		private double started;

		public string Observe(string id, string text, double now)
		{
			if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(text))
			{
				Clear();
				return null;
			}
			if (target != id || content != text)
			{
				target = id;
				content = text;
				started = now;
			}
			if (!(now - started >= 1.0))
			{
				return null;
			}
			return content;
		}

		public void Clear()
		{
			target = null;
			content = null;
		}

		public static Rect Bounds(Vector2 pointer, float width, float height, float windowWidth, float windowHeight)
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			width = Mathf.Min(width, windowWidth - 24f);
			height = Mathf.Min(height, windowHeight - 24f);
			float num = pointer.x + 14f;
			float num2 = pointer.y + 14f;
			if (num + width > windowWidth - 12f)
			{
				num = pointer.x - width - 14f;
			}
			if (num2 + height > windowHeight - 12f)
			{
				num2 = pointer.y - height - 14f;
			}
			return new Rect(Mathf.Clamp(num, 12f, windowWidth - width - 12f), Mathf.Clamp(num2, 12f, windowHeight - height - 12f), width, height);
		}
	}
	internal static class ItemTooltip
	{
		public static string Format(ItemCatalogEntry item, Localization localization)
		{
			List<string> list = new List<string>();
			foreach (ItemCategory value in Enum.GetValues(typeof(ItemCategory)))
			{
				if (value != ItemCategory.None && (item.Categories & value) != ItemCategory.None)
				{
					list.Add(localization.Text(value.ToString()));
				}
			}
			foreach (ConsumableKind value2 in Enum.GetValues(typeof(ConsumableKind)))
			{
				if (value2 != ConsumableKind.None && (item.Kinds & value2) != ConsumableKind.None)
				{
					list.Add(localization.Text(value2.ToString()));
				}
			}
			SpecialItemKind specialItemKind = SpecialItems.Classify(item.SpawnName);
			if (specialItemKind != SpecialItemKind.None)
			{
				list.Add(localization.Text(specialItemKind.ToString()));
			}
			string text = item.DisplayName(localization.Language) + "\n" + localization.Format("ItemCategory", string.Join(" · ", list.ToArray()));
			if (!item.CanSpawnManually)
			{
				string text2 = text;
				text = text2 + "\n" + localization.Text("ItemUnavailable") + "\n" + localization.Text("ItemUnavailableReason");
			}
			else if (!item.ValidToSpawn)
			{
				string text3 = text;
				text = text3 + "\n" + localization.Text("SoloManualSpawn") + "\n" + localization.Text("SoloManualSpawnReason");
			}
			string text4 = item.DisplayDescription(localization.Language);
			if (!string.IsNullOrEmpty(text4))
			{
				return text + "\n\n" + text4;
			}
			return text;
		}
	}
	internal sealed class MainWindow : IDisposable
	{
		private const int WindowId = 5259604;

		private static readonly ItemCategory[] Categories = new ItemCategory[6]
		{
			ItemCategory.None,
			ItemCategory.Mystical,
			ItemCategory.Consumable,
			ItemCategory.Deployable,
			ItemCategory.Equipment,
			ItemCategory.Other
		};

		private ConsumableKind[] availableKinds = new ConsumableKind[1];

		private readonly ToolkitConfig config;

		private readonly PeakApi api;

		private readonly SelfTools selfTools;

		private readonly SelfToolsPanel selfPanel = new SelfToolsPanel();

		private readonly WorldPanel worldPanel = new WorldPanel();

		private readonly PlayerPanel playerPanel;

		private readonly RoomRoleReader roomRole = new RoomRoleReader();

		private readonly Localization localization = new Localization();

		private readonly CursorLease cursor = new CursorLease();

		private readonly Theme theme = new Theme();

		private readonly Toast toast = new Toast();

		private readonly Tooltip tooltip = new Tooltip();

		private readonly SpecialItemFilter specialFilter = new SpecialItemFilter();

		private readonly List<ItemCatalogEntry> catalog = new List<ItemCatalogEntry>();

		private readonly List<ItemCatalogEntry> visibleItems = new List<ItemCatalogEntry>();

		private Rect windowRect;

		private float uiScale = 1f;

		private int screenWidth;

		private int screenHeight;

		private bool open;

		private bool catalogLoaded;

		private bool languageUnavailable;

		private int activeTab = 1;

		private int categoryIndex;

		private int kindIndex;

		private string itemQuery = string.Empty;

		private Vector2 itemScroll = Vector2.zero;

		private string lastGameLanguage;

		private string lastPreference;

		public bool IsOpen => open;

		public MainWindow(ToolkitConfig config, PeakApi api, SelfTools selfTools, DroppedItemHistory history, Action<string> log)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			playerPanel = new PlayerPanel(history, log);
			this.config = config;
			this.api = api;
			this.selfTools = selfTools;
			lastPreference = config.Language.Value;
			localization.Update(lastPreference, null);
		}

		public void Open()
		{
			if (!open && api.CanOpenWindow)
			{
				specialFilter.Reset();
				specialFilter.SetCategory(Categories[categoryIndex]);
				open = true;
				CenterOnScreen();
				InvalidateItemCatalog();
				cursor.Acquire();
				toast.Clear();
				tooltip.Clear();
			}
		}

		public void Close()
		{
			if (open)
			{
				open = false;
				specialFilter.Reset();
				cursor.Release(api.GameWantsCursor());
				toast.Clear();
				tooltip.Clear();
			}
		}

		public void InvalidateItemCatalog()
		{
			tooltip.Clear();
			catalogLoaded = false;
			catalog.Clear();
			visibleItems.Clear();
		}

		public void RefreshLanguage(string gameLanguage)
		{
			bool flag = !string.Equals(gameLanguage, lastGameLanguage, StringComparison.Ordinal);
			languageUnavailable = string.IsNullOrEmpty(gameLanguage) && config.Language.Value == "Auto";
			if (!(gameLanguage == lastGameLanguage) || !(config.Language.Value == lastPreference))
			{
				lastGameLanguage = gameLanguage;
				lastPreference = config.Language.Value;
				localization.Update(lastPreference, gameLanguage);
				tooltip.Clear();
				if (flag && catalogLoaded)
				{
					RefreshItemCatalog();
				}
			}
		}

		public void Draw()
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			if (!open)
			{
				return;
			}
			if (screenWidth != Screen.width || screenHeight != Screen.height)
			{
				CenterOnScreen();
			}
			theme.Ensure();
			Matrix4x4 matrix = GUI.matrix;
			try
			{
				GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(uiScale, uiScale, 1f));
				windowRect = GUI.Window(5259604, windowRect, new WindowFunction(DrawContents), string.Empty, theme.Window);
				windowRect = WindowBounds.Clamp(windowRect, (float)screenWidth / uiScale, (float)screenHeight / uiScale);
			}
			finally
			{
				GUI.matrix = matrix;
			}
		}

		private void DrawContents(int id)
		{
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Invalid comparison between Unknown and I4
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_0372: Expected O, but got Unknown
			//IL_0379: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) });
			GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(42f) });
			GUILayout.Label(localization.Text("Heading"), theme.Heading, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Label(localization.Text("Foundation"), theme.Small, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) });
			GUILayout.FlexibleSpace();
			int num = GUILayout.SelectionGrid(activeTab, new string[5]
			{
				localization.Text("Overview"),
				localization.Text("Items"),
				localization.Text("SelfTools"),
				localization.Text("Players"),
				localization.Text("World")
			}, 5, theme.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(640f) });
			if (num != activeTab)
			{
				activeTab = num;
				specialFilter.Reset();
				InvalidateItemCatalog();
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(10f);
			ItemCatalogEntry itemCatalogEntry = null;
			if (activeTab == 1)
			{
				itemCatalogEntry = DrawItems();
			}
			else if (activeTab == 2)
			{
				selfPanel.Draw(selfTools, theme, localization, toast);
			}
			else if (activeTab == 3)
			{
				playerPanel.Draw(theme, localization, toast);
			}
			else if (activeTab == 4)
			{
				worldPanel.Draw(theme, localization, toast);
			}
			else
			{
				DrawOverview();
				GUILayout.FlexibleSpace();
			}
			GUILayout.Label(toast.Current(Time.unscaledTimeAsDouble) ?? string.Empty, theme.Small, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) });
			GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Label(localization.Text("CloseHelp"), theme.Small, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			if (GUILayout.Button(localization.Text("Close"), theme.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }))
			{
				Close();
			}
			GUILayout.EndHorizontal();
			GUILayout.EndVertical();
			GUI.DragWindow(new Rect(12f, 12f, ((Rect)(ref windowRect)).width - 680f, 42f));
			if ((int)Event.current.type == 7)
			{
				string text = null;
				if (open && itemCatalogEntry != null)
				{
					text = ItemTooltip.Format(itemCatalogEntry, localization);
				}
				string text2 = tooltip.Observe((text == null) ? null : itemCatalogEntry.SpawnName, text, Time.unscaledTimeAsDouble);
				if (!string.IsNullOrEmpty(text2))
				{
					float num2 = Mathf.Min(380f, ((Rect)(ref windowRect)).width - 24f);
					float height = theme.Tooltip.CalcHeight(new GUIContent(text2), num2);
					GUI.Box(Tooltip.Bounds(Event.current.mousePosition, num2, height, ((Rect)(ref windowRect)).width, ((Rect)(ref windowRect)).height), text2, theme.Tooltip);
				}
			}
		}

		private void DrawOverview()
		{
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Expected O, but got Unknown
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Expected O, but got Unknown
			GUILayout.Label(localization.Text("Scope"), theme.Body, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			string key = roomRole.Read() switch
			{
				RoomRole.OutsideRoom => "RoomRoleOutside", 
				RoomRole.Client => "RoomRoleClient", 
				RoomRole.Host => "RoomRoleHost", 
				_ => "RoomRoleUnavailable", 
			};
			GUILayout.Label(localization.Format("RoomRoleLabel", localization.Text(key)), theme.Body, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Space(16f);
			GUILayout.Label(localization.Text("Language"), theme.Body, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUIContent[] array = (GUIContent[])(object)new GUIContent[4]
			{
				new GUIContent(localization.Text("Auto")),
				new GUIContent(localization.Text("English")),
				new GUIContent(localization.Text("SimplifiedChinese")),
				new GUIContent(localization.Text("TraditionalChinese"))
			};
			int num = ((config.Language.Value == "en") ? 1 : ((config.Language.Value == "zh-CN") ? 2 : ((config.Language.Value == "zh-TW") ? 3 : 0)));
			int num2 = GUILayout.SelectionGrid(num, array, 4, theme.Button, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			if (num2 != num)
			{
				config.Language.Value = num2 switch
				{
					3 => "zh-TW", 
					2 => "zh-CN", 
					1 => "en", 
					_ => "Auto", 
				};
				RefreshLanguage(lastGameLanguage);
				toast.Show(localization.Text("Saved"), Time.unscaledTimeAsDouble, 2.0);
			}
			GUILayout.Label(localization.Text("AutoHelp"), theme.Small, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Label(localization.Format("ActiveLanguage", LanguageName()), theme.Small, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			if (languageUnavailable)
			{
				GUILayout.Label(localization.Text("LanguageUnavailable"), theme.Small, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			}
		}

		private ItemCatalogEntry DrawItems()
		{
			//IL_040c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0411: Unknown result type (might be due to invalid IL or missing references)
			//IL_0435: Unknown result type (might be due to invalid IL or missing references)
			//IL_0438: Unknown result type (might be due to invalid IL or missing references)
			//IL_0455: Unknown result type (might be due to invalid IL or missing references)
			//IL_045c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0461: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Unknown result type (might be due to invalid IL or missing references)
			//IL_0466: Unknown result type (might be due to invalid IL or missing references)
			//IL_047e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0480: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_06c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_06cc: Invalid comparison between Unknown and I4
			//IL_06d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_06d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_06de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0530: Unknown result type (might be due to invalid IL or missing references)
			//IL_05bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0604: Unknown result type (might be due to invalid IL or missing references)
			//IL_0606: Unknown result type (might be due to invalid IL or missing references)
			//IL_060f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0614: Unknown result type (might be due to invalid IL or missing references)
			//IL_0625: Unknown result type (might be due to invalid IL or missing references)
			//IL_0683: Unknown result type (might be due to invalid IL or missing references)
			//IL_0643: Unknown result type (might be due to invalid IL or missing references)
			if (!catalogLoaded)
			{
				RefreshItemCatalog();
			}
			GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[0]);
			GUILayout.Label(localization.Text("SearchItems"), theme.Body, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) });
			string text = GUILayout.TextField(itemQuery, theme.TextField, (GUILayoutOption[])(object)new GUILayoutOption[0]);
			if (text != itemQuery)
			{
				itemQuery = text;
				ApplyFilter();
			}
			if (GUILayout.Button(localization.Text("RefreshItems"), theme.Button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }))
			{
				RefreshItemCatalog();
			}
			GUILayout.EndHorizontal();
			GUILayout.Space(6f);
			string[] array = new strin