Decompiled source of SenpaisChest v2.7.0

SenpaisChest.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PSS;
using SenpaisChest.ChestLabels;
using SenpaisChest.ChestLabels.Extensions;
using SenpaisChest.Config;
using SenpaisChest.Data;
using SenpaisChest.Integration;
using SenpaisChest.UI;
using SunHavenMuseumUtilityTracker;
using SunHavenMuseumUtilityTracker.Data;
using SunhavenMods.Shared;
using SunhavenTodo;
using SunhavenTodo.Data;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Wish;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("SenpaisChest")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+871217776acdadbaabcc0e4af36feeef0cd58101")]
[assembly: AssemblyProduct("SenpaisChest")]
[assembly: AssemblyTitle("SenpaisChest")]
[assembly: InternalsVisibleTo("HavenDevTools")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SunhavenMods.Shared
{
	public static class ConfigFileHelper
	{
		public static ConfigFile CreateNamedConfig(string pluginGuid, string configFileName, Action<string> logWarning = null)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			string text = Path.Combine(Paths.ConfigPath, configFileName);
			string text2 = Path.Combine(Paths.ConfigPath, pluginGuid + ".cfg");
			try
			{
				if (!File.Exists(text) && File.Exists(text2))
				{
					File.Copy(text2, text);
				}
			}
			catch (Exception ex)
			{
				logWarning?.Invoke("[Config] Migration to " + configFileName + " failed: " + ex.Message);
			}
			return new ConfigFile(text, true);
		}

		public static bool ReplacePluginConfig(BaseUnityPlugin plugin, ConfigFile newConfig, Action<string> logWarning = null)
		{
			if ((Object)(object)plugin == (Object)null || newConfig == null)
			{
				return false;
			}
			try
			{
				Type typeFromHandle = typeof(BaseUnityPlugin);
				PropertyInfo property = typeFromHandle.GetProperty("Config", BindingFlags.Instance | BindingFlags.Public);
				if (property != null && property.CanWrite)
				{
					property.SetValue(plugin, newConfig, null);
					return true;
				}
				FieldInfo field = typeFromHandle.GetField("<Config>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null)
				{
					field.SetValue(plugin, newConfig);
					return true;
				}
				FieldInfo[] fields = typeFromHandle.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.FieldType == typeof(ConfigFile))
					{
						fieldInfo.SetValue(plugin, newConfig);
						return true;
					}
				}
			}
			catch (Exception ex)
			{
				logWarning?.Invoke("[Config] ReplacePluginConfig failed: " + ex.Message);
			}
			return false;
		}
	}
	public static class VersionChecker
	{
		public class VersionCheckResult
		{
			public bool Success { get; set; }

			public bool UpdateAvailable { get; set; }

			public string CurrentVersion { get; set; }

			public string LatestVersion { get; set; }

			public string ModName { get; set; }

			public string NexusUrl { get; set; }

			public string Changelog { get; set; }

			public string ErrorMessage { get; set; }
		}

		public class ModHealthSnapshot
		{
			public string PluginGuid { get; set; }

			public DateTime LastCheckUtc { get; set; }

			public int ExceptionCount { get; set; }

			public string LastError { get; set; }
		}

		private class VersionCheckRunner : MonoBehaviour
		{
			private ManualLogSource _pluginLog;

			public void StartCheck(string pluginGuid, string currentVersion, ManualLogSource pluginLog, Action<VersionCheckResult> onComplete)
			{
				_pluginLog = pluginLog;
				((MonoBehaviour)this).StartCoroutine(CheckVersionCoroutine(pluginGuid, currentVersion, onComplete));
			}

			private void LogInfo(string message)
			{
				ManualLogSource pluginLog = _pluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogInfo((object)("[VersionChecker] " + message));
				}
			}

			private void LogWarningMsg(string message)
			{
				ManualLogSource pluginLog = _pluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogWarning((object)("[VersionChecker] " + message));
				}
			}

			private void LogErrorMsg(string message)
			{
				ManualLogSource pluginLog = _pluginLog;
				if (pluginLog != null)
				{
					pluginLog.LogError((object)("[VersionChecker] " + message));
				}
			}

			private IEnumerator CheckVersionCoroutine(string pluginGuid, string currentVersion, Action<VersionCheckResult> onComplete)
			{
				VersionCheckResult result = new VersionCheckResult
				{
					CurrentVersion = currentVersion
				};
				UnityWebRequest www = UnityWebRequest.Get("https://azraelgodking.github.io/SunhavenMod/versions.json");
				try
				{
					www.timeout = 10;
					yield return www.SendWebRequest();
					if ((int)www.result == 2 || (int)www.result == 3)
					{
						result.Success = false;
						result.ErrorMessage = "Network error: " + www.error;
						RecordHealthError(pluginGuid, result.ErrorMessage);
						LogWarningMsg(result.ErrorMessage);
						onComplete?.Invoke(result);
						Object.Destroy((Object)(object)((Component)this).gameObject);
						yield break;
					}
					try
					{
						string text = www.downloadHandler.text;
						Match match = GetModPattern(pluginGuid).Match(text);
						if (!match.Success)
						{
							result.Success = false;
							result.ErrorMessage = "Mod '" + pluginGuid + "' not found in versions.json";
							RecordHealthError(pluginGuid, result.ErrorMessage);
							LogWarningMsg(result.ErrorMessage);
							onComplete?.Invoke(result);
							Object.Destroy((Object)(object)((Component)this).gameObject);
							yield break;
						}
						string value = match.Groups[1].Value;
						result.LatestVersion = ExtractJsonString(value, "version");
						result.ModName = ExtractJsonString(value, "name");
						result.NexusUrl = ExtractJsonString(value, "nexus");
						result.Changelog = ExtractJsonString(value, "changelog");
						if (string.IsNullOrEmpty(result.LatestVersion))
						{
							result.Success = false;
							result.ErrorMessage = "Could not parse version from response";
							RecordHealthError(pluginGuid, result.ErrorMessage);
							LogWarningMsg(result.ErrorMessage);
							onComplete?.Invoke(result);
							Object.Destroy((Object)(object)((Component)this).gameObject);
							yield break;
						}
						result.Success = true;
						result.UpdateAvailable = CompareVersions(currentVersion, result.LatestVersion) < 0;
						if (result.UpdateAvailable)
						{
							LogInfo("Update available for " + result.ModName + ": " + currentVersion + " -> " + result.LatestVersion);
						}
						else
						{
							LogInfo(result.ModName + " is up to date (v" + currentVersion + ")");
						}
					}
					catch (Exception ex)
					{
						result.Success = false;
						result.ErrorMessage = "Parse error: " + ex.Message;
						RecordHealthError(pluginGuid, result.ErrorMessage);
						LogErrorMsg(result.ErrorMessage);
					}
				}
				finally
				{
					((IDisposable)www)?.Dispose();
				}
				onComplete?.Invoke(result);
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}

			private string ExtractJsonString(string json, string key)
			{
				Match match = ExtractFieldRegex.Match(json);
				while (match.Success)
				{
					if (string.Equals(match.Groups["key"].Value, key, StringComparison.Ordinal))
					{
						return match.Groups["value"].Value;
					}
					match = match.NextMatch();
				}
				return null;
			}
		}

		private const string VersionsUrl = "https://azraelgodking.github.io/SunhavenMod/versions.json";

		private static readonly Dictionary<string, ModHealthSnapshot> HealthByPluginGuid = new Dictionary<string, ModHealthSnapshot>(StringComparer.OrdinalIgnoreCase);

		private static readonly object HealthLock = new object();

		private static readonly Dictionary<string, Regex> ModPatternCache = new Dictionary<string, Regex>(StringComparer.Ordinal);

		private static readonly object ModPatternCacheLock = new object();

		private static readonly Regex ExtractFieldRegex = new Regex("\"(?<key>[^\"]+)\"\\s*:\\s*(?:\"(?<value>[^\"]*)\"|null)", RegexOptions.Compiled);

		public static void CheckForUpdate(string pluginGuid, string currentVersion, ManualLogSource logger = null, Action<VersionCheckResult> onComplete = null)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			TouchHealth(pluginGuid);
			VersionCheckRunner versionCheckRunner = new GameObject("VersionChecker").AddComponent<VersionCheckRunner>();
			Object.DontDestroyOnLoad((Object)(object)((Component)versionCheckRunner).gameObject);
			SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(((Component)versionCheckRunner).gameObject);
			versionCheckRunner.StartCheck(pluginGuid, currentVersion, logger, onComplete);
		}

		public static ModHealthSnapshot GetHealthSnapshot(string pluginGuid)
		{
			if (string.IsNullOrWhiteSpace(pluginGuid))
			{
				return null;
			}
			lock (HealthLock)
			{
				if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value))
				{
					return null;
				}
				return new ModHealthSnapshot
				{
					PluginGuid = value.PluginGuid,
					LastCheckUtc = value.LastCheckUtc,
					ExceptionCount = value.ExceptionCount,
					LastError = value.LastError
				};
			}
		}

		public static int CompareVersions(string v1, string v2)
		{
			if (string.IsNullOrEmpty(v1) || string.IsNullOrEmpty(v2))
			{
				return 0;
			}
			v1 = v1.TrimStart('v', 'V');
			v2 = v2.TrimStart('v', 'V');
			int num = v1.IndexOfAny(new char[2] { '-', '+' });
			if (num >= 0)
			{
				v1 = v1.Substring(0, num);
			}
			int num2 = v2.IndexOfAny(new char[2] { '-', '+' });
			if (num2 >= 0)
			{
				v2 = v2.Substring(0, num2);
			}
			string[] array = v1.Split(new char[1] { '.' });
			string[] array2 = v2.Split(new char[1] { '.' });
			int num3 = Math.Max(array.Length, array2.Length);
			for (int i = 0; i < num3; i++)
			{
				int result;
				int num4 = ((i < array.Length && int.TryParse(array[i], out result)) ? result : 0);
				int result2;
				int num5 = ((i < array2.Length && int.TryParse(array2[i], out result2)) ? result2 : 0);
				if (num4 < num5)
				{
					return -1;
				}
				if (num4 > num5)
				{
					return 1;
				}
			}
			return 0;
		}

		private static void TouchHealth(string pluginGuid)
		{
			if (string.IsNullOrWhiteSpace(pluginGuid))
			{
				return;
			}
			lock (HealthLock)
			{
				if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value))
				{
					value = new ModHealthSnapshot
					{
						PluginGuid = pluginGuid
					};
					HealthByPluginGuid[pluginGuid] = value;
				}
				value.LastCheckUtc = DateTime.UtcNow;
			}
		}

		private static void RecordHealthError(string pluginGuid, string errorMessage)
		{
			if (string.IsNullOrWhiteSpace(pluginGuid))
			{
				return;
			}
			lock (HealthLock)
			{
				if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value))
				{
					value = new ModHealthSnapshot
					{
						PluginGuid = pluginGuid
					};
					HealthByPluginGuid[pluginGuid] = value;
				}
				value.LastCheckUtc = DateTime.UtcNow;
				value.ExceptionCount++;
				value.LastError = errorMessage;
			}
		}

		private static Regex GetModPattern(string pluginGuid)
		{
			lock (ModPatternCacheLock)
			{
				if (!ModPatternCache.TryGetValue(pluginGuid, out Regex value))
				{
					value = new Regex("\"" + Regex.Escape(pluginGuid) + "\"\\s*:\\s*\\{([^}]+)\\}", RegexOptions.Compiled | RegexOptions.Singleline);
					ModPatternCache[pluginGuid] = value;
				}
				return value;
			}
		}
	}
	public static class VersionCheckerExtensions
	{
		public static void NotifyUpdateAvailable(this VersionChecker.VersionCheckResult result, ManualLogSource logger = null)
		{
			if (!result.UpdateAvailable)
			{
				return;
			}
			string text = result.ModName + " update available: v" + result.LatestVersion;
			try
			{
				Type type = ReflectionHelper.FindWishType("NotificationStack");
				if (type != null)
				{
					Type type2 = ReflectionHelper.FindType("SingletonBehaviour`1", "Wish");
					if (type2 != null)
					{
						object obj = type2.MakeGenericType(type).GetProperty("Instance")?.GetValue(null);
						if (obj != null)
						{
							MethodInfo method = type.GetMethod("SendNotification", new Type[5]
							{
								typeof(string),
								typeof(int),
								typeof(int),
								typeof(bool),
								typeof(bool)
							});
							if (method != null)
							{
								method.Invoke(obj, new object[5] { text, 0, 1, false, true });
								return;
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				if (logger != null)
				{
					logger.LogWarning((object)("Failed to send native notification: " + ex.Message));
				}
			}
			if (logger != null)
			{
				logger.LogWarning((object)("[UPDATE AVAILABLE] " + text));
			}
			if (!string.IsNullOrEmpty(result.NexusUrl) && logger != null)
			{
				logger.LogWarning((object)("Download at: " + result.NexusUrl));
			}
		}
	}
	public static class SceneRootSurvivor
	{
		private static readonly object Lock = new object();

		private static readonly List<string> NoKillSubstrings = new List<string>();

		private static Harmony _harmony;

		public static void TryRegisterPersistentRunnerGameObject(GameObject go)
		{
			if (!((Object)(object)go == (Object)null))
			{
				TryAddNoKillListSubstring(((Object)go).name);
			}
		}

		public static void TryAddNoKillListSubstring(string nameSubstring)
		{
			if (string.IsNullOrEmpty(nameSubstring))
			{
				return;
			}
			lock (Lock)
			{
				bool flag = false;
				for (int i = 0; i < NoKillSubstrings.Count; i++)
				{
					if (string.Equals(NoKillSubstrings[i], nameSubstring, StringComparison.OrdinalIgnoreCase))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					NoKillSubstrings.Add(nameSubstring);
				}
			}
			EnsurePatched();
		}

		private static void EnsurePatched()
		{
			//IL_0078: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_00a3: Expected O, but got Unknown
			if (_harmony != null)
			{
				return;
			}
			lock (Lock)
			{
				if (_harmony == null)
				{
					MethodInfo methodInfo = AccessTools.Method(typeof(Scene), "GetRootGameObjects", Type.EmptyTypes, (Type[])null);
					if (!(methodInfo == null))
					{
						string text = typeof(SceneRootSurvivor).Assembly.GetName().Name ?? "Unknown";
						Harmony val = new Harmony("SunhavenMods.SceneRootSurvivor." + text);
						val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SceneRootSurvivor), "OnGetRootGameObjectsPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						_harmony = val;
					}
				}
			}
		}

		private static void OnGetRootGameObjectsPostfix(ref GameObject[] __result)
		{
			if (__result == null || __result.Length == 0)
			{
				return;
			}
			List<string> list;
			lock (Lock)
			{
				if (NoKillSubstrings.Count == 0)
				{
					return;
				}
				list = new List<string>(NoKillSubstrings);
			}
			List<GameObject> list2 = new List<GameObject>(__result);
			for (int i = 0; i < list.Count; i++)
			{
				string noKill = list[i];
				list2.RemoveAll((GameObject a) => (Object)(object)a != (Object)null && ((Object)a).name.IndexOf(noKill, StringComparison.OrdinalIgnoreCase) >= 0);
			}
			__result = list2.ToArray();
		}
	}
	public static class ReflectionHelper
	{
		public static readonly BindingFlags AllBindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

		public static Type FindType(string typeName, params string[] namespaces)
		{
			string typeName2 = typeName;
			Type type = AccessTools.TypeByName(typeName2);
			if (type != null)
			{
				return type;
			}
			for (int i = 0; i < namespaces.Length; i++)
			{
				type = AccessTools.TypeByName(namespaces[i] + "." + typeName2);
				if (type != null)
				{
					return type;
				}
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == typeName2 || t.FullName == typeName2);
					if (type != null)
					{
						return type;
					}
				}
				catch (ReflectionTypeLoadException)
				{
				}
			}
			return null;
		}

		public static Type FindWishType(string typeName)
		{
			return FindType(typeName, "Wish");
		}

		public static object GetStaticValue(Type type, string memberName)
		{
			if (type == null)
			{
				return null;
			}
			try
			{
				PropertyInfo property = type.GetProperty(memberName, AllBindingFlags);
				if (property != null && property.GetMethod != null && property.GetIndexParameters().Length == 0)
				{
					return property.GetValue(null);
				}
			}
			catch (AmbiguousMatchException)
			{
				return null;
			}
			FieldInfo field = type.GetField(memberName, AllBindingFlags);
			if (field != null)
			{
				return field.GetValue(null);
			}
			return null;
		}

		public static object GetSingletonInstance(Type type)
		{
			if (type == null)
			{
				return null;
			}
			string[] array = new string[5] { "Instance", "instance", "_instance", "Singleton", "singleton" };
			foreach (string memberName in array)
			{
				object staticValue = GetStaticValue(type, memberName);
				if (staticValue != null)
				{
					return staticValue;
				}
			}
			return null;
		}

		public static object GetInstanceValue(object instance, string memberName)
		{
			if (instance == null)
			{
				return null;
			}
			Type type = instance.GetType();
			while (type != null)
			{
				PropertyInfo property = type.GetProperty(memberName, AllBindingFlags);
				if (property != null && property.GetMethod != null)
				{
					return property.GetValue(instance);
				}
				FieldInfo field = type.GetField(memberName, AllBindingFlags);
				if (field != null)
				{
					return field.GetValue(instance);
				}
				type = type.BaseType;
			}
			return null;
		}

		public static bool SetInstanceValue(object instance, string memberName, object value)
		{
			if (instance == null)
			{
				return false;
			}
			Type type = instance.GetType();
			while (type != null)
			{
				PropertyInfo property = type.GetProperty(memberName, AllBindingFlags);
				if (property != null && property.SetMethod != null)
				{
					property.SetValue(instance, value);
					return true;
				}
				FieldInfo field = type.GetField(memberName, AllBindingFlags);
				if (field != null)
				{
					field.SetValue(instance, value);
					return true;
				}
				type = type.BaseType;
			}
			return false;
		}

		public static object InvokeMethod(object instance, string methodName, params object[] args)
		{
			if (instance == null)
			{
				return null;
			}
			Type type = instance.GetType();
			Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes;
			MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null);
			if (methodInfo == null)
			{
				methodInfo = type.GetMethod(methodName, AllBindingFlags);
			}
			if (methodInfo == null)
			{
				return null;
			}
			return methodInfo.Invoke(instance, args);
		}

		public static object InvokeStaticMethod(Type type, string methodName, params object[] args)
		{
			if (type == null)
			{
				return null;
			}
			Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes;
			MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null);
			if (methodInfo == null)
			{
				methodInfo = type.GetMethod(methodName, AllBindingFlags);
			}
			if (methodInfo == null)
			{
				return null;
			}
			return methodInfo.Invoke(null, args);
		}

		public static FieldInfo[] GetAllFields(Type type)
		{
			if (type == null)
			{
				return Array.Empty<FieldInfo>();
			}
			FieldInfo[] fields = type.GetFields(AllBindingFlags);
			IEnumerable<FieldInfo> second;
			if (!(type.BaseType != null) || !(type.BaseType != typeof(object)))
			{
				second = Enumerable.Empty<FieldInfo>();
			}
			else
			{
				IEnumerable<FieldInfo> allFields = GetAllFields(type.BaseType);
				second = allFields;
			}
			return fields.Concat(second).Distinct().ToArray();
		}

		public static PropertyInfo[] GetAllProperties(Type type)
		{
			if (type == null)
			{
				return Array.Empty<PropertyInfo>();
			}
			PropertyInfo[] properties = type.GetProperties(AllBindingFlags);
			IEnumerable<PropertyInfo> second;
			if (!(type.BaseType != null) || !(type.BaseType != typeof(object)))
			{
				second = Enumerable.Empty<PropertyInfo>();
			}
			else
			{
				IEnumerable<PropertyInfo> allProperties = GetAllProperties(type.BaseType);
				second = allProperties;
			}
			return (from p in properties.Concat(second)
				group p by p.Name into g
				select g.First()).ToArray();
		}

		public static T TryGetValue<T>(object instance, string memberName, T defaultValue = default(T))
		{
			try
			{
				object instanceValue = GetInstanceValue(instance, memberName);
				if (instanceValue is T result)
				{
					return result;
				}
				if (instanceValue != null && typeof(T).IsAssignableFrom(instanceValue.GetType()))
				{
					return (T)instanceValue;
				}
				return defaultValue;
			}
			catch
			{
				return defaultValue;
			}
		}
	}
	public static class ItemSearch
	{
		private static readonly ManualLogSource _log = Logger.CreateLogSource("ItemSearch");

		private static object _dbInstance;

		private static FieldInfo _dictField;

		public static string FormatDisplay(string name, int itemId)
		{
			if (string.IsNullOrEmpty(name))
			{
				return $"#{itemId}";
			}
			return $"{name} (#{itemId})";
		}

		public static List<KeyValuePair<int, string>> SearchItems(string query, int maxResults = 50)
		{
			List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();
			if (string.IsNullOrEmpty(query) || query.Trim().Length < 2)
			{
				return list;
			}
			try
			{
				Dictionary<int, ItemSellInfo> itemDictionary = GetItemDictionary();
				if (itemDictionary == null)
				{
					return list;
				}
				string text = query.Trim().ToLowerInvariant();
				int result;
				bool flag = int.TryParse(query.Trim(), out result);
				List<KeyValuePair<int, string>> list2 = new List<KeyValuePair<int, string>>();
				List<KeyValuePair<int, string>> list3 = new List<KeyValuePair<int, string>>();
				List<KeyValuePair<int, string>> list4 = new List<KeyValuePair<int, string>>();
				foreach (KeyValuePair<int, ItemSellInfo> item2 in itemDictionary)
				{
					int key = item2.Key;
					string text2 = item2.Value?.name;
					if (!string.IsNullOrEmpty(text2))
					{
						KeyValuePair<int, string> item = new KeyValuePair<int, string>(key, text2);
						string text3 = text2.ToLowerInvariant();
						if (flag && key == result)
						{
							list2.Add(item);
						}
						else if (text3 == text)
						{
							list2.Add(item);
						}
						else if (text3.StartsWith(text))
						{
							list3.Add(item);
						}
						else if (text3.Contains(text))
						{
							list4.Add(item);
						}
						else if (flag && key.ToString().Contains(query.Trim()))
						{
							list4.Add(item);
						}
					}
				}
				list3.Sort((KeyValuePair<int, string> a, KeyValuePair<int, string> b) => string.Compare(a.Value, b.Value, StringComparison.OrdinalIgnoreCase));
				list4.Sort((KeyValuePair<int, string> a, KeyValuePair<int, string> b) => string.Compare(a.Value, b.Value, StringComparison.OrdinalIgnoreCase));
				list.AddRange(list2);
				list.AddRange(list3);
				list.AddRange(list4);
				if (list.Count > maxResults)
				{
					list.RemoveRange(maxResults, list.Count - maxResults);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)("[ItemSearch] SearchItems error: " + ex.Message));
				}
			}
			return list;
		}

		public static string GetItemName(int itemId)
		{
			try
			{
				Dictionary<int, ItemSellInfo> itemDictionary = GetItemDictionary();
				if (itemDictionary == null || !itemDictionary.ContainsKey(itemId))
				{
					return null;
				}
				return itemDictionary[itemId]?.name;
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)$"[ItemSearch] GetItemName({itemId}): {ex.Message}");
				}
				return null;
			}
		}

		public static ItemSellInfo GetItemSellInfo(int itemId)
		{
			try
			{
				Dictionary<int, ItemSellInfo> itemDictionary = GetItemDictionary();
				if (itemDictionary != null && itemDictionary.TryGetValue(itemId, out var value))
				{
					return value;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)$"[ItemSearch] GetItemSellInfo({itemId}): {ex.Message}");
				}
			}
			return null;
		}

		public static List<KeyValuePair<int, string>> GetAllItems()
		{
			List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();
			try
			{
				Dictionary<int, ItemSellInfo> itemDictionary = GetItemDictionary();
				if (itemDictionary == null)
				{
					return list;
				}
				foreach (KeyValuePair<int, ItemSellInfo> item in itemDictionary)
				{
					string value = item.Value?.name;
					if (!string.IsNullOrEmpty(value))
					{
						list.Add(new KeyValuePair<int, string>(item.Key, value));
					}
				}
				list.Sort((KeyValuePair<int, string> a, KeyValuePair<int, string> b) => string.Compare(a.Value, b.Value, StringComparison.OrdinalIgnoreCase));
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)("[ItemSearch] GetAllItems: " + ex.Message));
				}
			}
			return list;
		}

		private static Dictionary<int, ItemSellInfo> GetItemDictionary()
		{
			try
			{
				if (_dbInstance != null)
				{
					object dbInstance = _dbInstance;
					Object val = (Object)((dbInstance is Object) ? dbInstance : null);
					if (val == null || !(val == (Object)null))
					{
						goto IL_005b;
					}
				}
				_dbInstance = null;
				_dictField = null;
				_dbInstance = GetSingletonInstance("Wish.ItemInfoDatabase");
				if (_dbInstance != null)
				{
					_dictField = _dbInstance.GetType().GetField("allItemSellInfos", BindingFlags.Instance | BindingFlags.Public);
				}
				goto IL_005b;
				IL_005b:
				if (_dbInstance == null || _dictField == null)
				{
					return null;
				}
				return _dictField.GetValue(_dbInstance) as Dictionary<int, ItemSellInfo>;
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)("[ItemSearch] GetItemDictionary: " + ex.Message));
				}
				return null;
			}
		}

		private static object GetSingletonInstance(string typeName)
		{
			try
			{
				Type type = AccessTools.TypeByName("Wish.SingletonBehaviour`1");
				if (type == null)
				{
					return null;
				}
				Type type2 = AccessTools.TypeByName(typeName);
				if (type2 == null)
				{
					return null;
				}
				return type.MakeGenericType(type2).GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy)?.GetValue(null);
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)("[ItemSearch] GetSingletonInstance(" + typeName + "): " + ex.Message));
				}
				return null;
			}
		}
	}
	public static class TextInputFocusGuard
	{
		private const float DefaultPollIntervalSeconds = 0.25f;

		private static float _nextPollTime = -1f;

		private static bool _cachedDefer;

		private static bool _tmpTypeLookupDone;

		private static Type _tmpInputFieldType;

		private static bool _qcLookupDone;

		private static Type _qcType;

		private static PropertyInfo _qcInstanceProp;

		private static PropertyInfo _qcIsActiveProp;

		private static FieldInfo _qcIsActiveField;

		public static bool ShouldDeferModHotkeys(ManualLogSource debugLog = null, float pollIntervalSeconds = 0.25f)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup < _nextPollTime)
			{
				return _cachedDefer;
			}
			_nextPollTime = realtimeSinceStartup + Mathf.Max(0.05f, pollIntervalSeconds);
			bool flag = false;
			try
			{
				if (GUIUtility.keyboardControl != 0)
				{
					flag = true;
				}
				if (!flag)
				{
					EventSystem current = EventSystem.current;
					GameObject val = ((current != null) ? current.currentSelectedGameObject : null);
					if ((Object)(object)val != (Object)null)
					{
						if ((Object)(object)val.GetComponent<InputField>() != (Object)null)
						{
							flag = true;
						}
						else if (TryGetTmpInputField(val))
						{
							flag = true;
						}
					}
				}
				if (!flag && IsQuantumConsoleActive(debugLog))
				{
					flag = true;
				}
			}
			catch (Exception ex)
			{
				if (debugLog != null)
				{
					debugLog.LogDebug((object)("[TextInputFocusGuard] " + ex.Message));
				}
			}
			_cachedDefer = flag;
			return flag;
		}

		private static bool TryGetTmpInputField(GameObject go)
		{
			if (!_tmpTypeLookupDone)
			{
				_tmpTypeLookupDone = true;
				_tmpInputFieldType = AccessTools.TypeByName("TMPro.TMP_InputField");
			}
			if (_tmpInputFieldType == null)
			{
				return false;
			}
			return (Object)(object)go.GetComponent(_tmpInputFieldType) != (Object)null;
		}

		private static bool IsQuantumConsoleActive(ManualLogSource debugLog)
		{
			try
			{
				if (!_qcLookupDone)
				{
					_qcLookupDone = true;
					_qcType = AccessTools.TypeByName("QFSW.QC.QuantumConsole");
					if (_qcType != null)
					{
						_qcInstanceProp = AccessTools.Property(_qcType, "Instance");
						_qcIsActiveProp = AccessTools.Property(_qcType, "IsActive");
						_qcIsActiveField = AccessTools.Field(_qcType, "isActive") ?? AccessTools.Field(_qcType, "_isActive");
					}
				}
				if (_qcType == null)
				{
					return false;
				}
				object obj = _qcInstanceProp?.GetValue(null);
				if (obj == null)
				{
					return false;
				}
				if (_qcIsActiveProp != null && _qcIsActiveProp.PropertyType == typeof(bool))
				{
					return (bool)_qcIsActiveProp.GetValue(obj);
				}
				if (_qcIsActiveField != null && _qcIsActiveField.FieldType == typeof(bool))
				{
					return (bool)_qcIsActiveField.GetValue(obj);
				}
			}
			catch (Exception ex)
			{
				if (debugLog != null)
				{
					debugLog.LogDebug((object)("[TextInputFocusGuard] Quantum Console focus check failed: " + ex.Message));
				}
			}
			return false;
		}
	}
	internal static class MinimalJsonParser
	{
		internal static void WriteJsonString(StringBuilder sb, string value)
		{
			sb.Append('"');
			if (value != null)
			{
				foreach (char c in value)
				{
					switch (c)
					{
					case '"':
						sb.Append("\\\"");
						break;
					case '\\':
						sb.Append("\\\\");
						break;
					case '\n':
						sb.Append("\\n");
						break;
					case '\r':
						sb.Append("\\r");
						break;
					case '\t':
						sb.Append("\\t");
						break;
					case '\b':
						sb.Append("\\b");
						break;
					case '\f':
						sb.Append("\\f");
						break;
					default:
						sb.Append(c);
						break;
					}
				}
			}
			sb.Append('"');
		}

		internal static void SkipWhitespace(string json, ref int pos)
		{
			while (pos < json.Length && char.IsWhiteSpace(json[pos]))
			{
				pos++;
			}
		}

		internal static object ParseValue(string json, ref int pos)
		{
			SkipWhitespace(json, ref pos);
			if (pos >= json.Length)
			{
				return null;
			}
			char c = json[pos];
			switch (c)
			{
			case '"':
				return ParseString(json, ref pos);
			case '{':
				return ParseObject(json, ref pos);
			case '[':
				return ParseArray(json, ref pos);
			case 't':
				return ParseLiteral(json, ref pos, "true", true);
			case 'f':
				return ParseLiteral(json, ref pos, "false", false);
			case 'n':
				return ParseLiteral(json, ref pos, "null", null);
			default:
				if (!char.IsDigit(c))
				{
					return null;
				}
				goto case '-';
			case '-':
				return ParseNumber(json, ref pos);
			}
		}

		internal static Dictionary<string, object> ParseObject(string json, ref int pos)
		{
			SkipWhitespace(json, ref pos);
			if (pos >= json.Length || json[pos] != '{')
			{
				return null;
			}
			pos++;
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			SkipWhitespace(json, ref pos);
			if (pos < json.Length && json[pos] == '}')
			{
				pos++;
				return dictionary;
			}
			while (pos < json.Length)
			{
				SkipWhitespace(json, ref pos);
				string text = ParseString(json, ref pos);
				if (text == null)
				{
					break;
				}
				SkipWhitespace(json, ref pos);
				if (pos >= json.Length || json[pos] != ':')
				{
					break;
				}
				pos++;
				SkipWhitespace(json, ref pos);
				dictionary[text] = ParseValue(json, ref pos);
				SkipWhitespace(json, ref pos);
				if (pos >= json.Length || json[pos] != ',')
				{
					break;
				}
				pos++;
			}
			SkipWhitespace(json, ref pos);
			if (pos < json.Length && json[pos] == '}')
			{
				pos++;
			}
			return dictionary;
		}

		internal static List<object> ParseArray(string json, ref int pos)
		{
			SkipWhitespace(json, ref pos);
			if (pos >= json.Length || json[pos] != '[')
			{
				return null;
			}
			pos++;
			List<object> list = new List<object>();
			SkipWhitespace(json, ref pos);
			if (pos < json.Length && json[pos] == ']')
			{
				pos++;
				return list;
			}
			while (pos < json.Length)
			{
				SkipWhitespace(json, ref pos);
				list.Add(ParseValue(json, ref pos));
				SkipWhitespace(json, ref pos);
				if (pos >= json.Length || json[pos] != ',')
				{
					break;
				}
				pos++;
			}
			SkipWhitespace(json, ref pos);
			if (pos < json.Length && json[pos] == ']')
			{
				pos++;
			}
			return list;
		}

		internal static string ParseString(string json, ref int pos)
		{
			SkipWhitespace(json, ref pos);
			if (pos >= json.Length || json[pos] != '"')
			{
				return null;
			}
			pos++;
			StringBuilder stringBuilder = new StringBuilder();
			while (pos < json.Length)
			{
				char c = json[pos];
				if (c == '\\' && pos + 1 < json.Length)
				{
					pos++;
					switch (json[pos])
					{
					case '"':
						stringBuilder.Append('"');
						break;
					case '\\':
						stringBuilder.Append('\\');
						break;
					case '/':
						stringBuilder.Append('/');
						break;
					case 'n':
						stringBuilder.Append('\n');
						break;
					case 'r':
						stringBuilder.Append('\r');
						break;
					case 't':
						stringBuilder.Append('\t');
						break;
					case 'b':
						stringBuilder.Append('\b');
						break;
					case 'f':
						stringBuilder.Append('\f');
						break;
					case 'u':
					{
						if (pos + 4 < json.Length && ushort.TryParse(json.Substring(pos + 1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result))
						{
							pos += 4;
							if (result >= 55296 && result <= 56319 && pos + 5 < json.Length && json[pos] == '\\' && json[pos + 1] == 'u' && ushort.TryParse(json.Substring(pos + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result2) && result2 >= 56320 && result2 <= 57343)
							{
								stringBuilder.Append(char.ConvertFromUtf32(char.ConvertToUtf32((char)result, (char)result2)));
								pos += 6;
							}
							else
							{
								stringBuilder.Append((char)result);
							}
						}
						else
						{
							stringBuilder.Append('u');
						}
						break;
					}
					default:
						stringBuilder.Append(json[pos]);
						break;
					}
					pos++;
				}
				else
				{
					if (c == '"')
					{
						pos++;
						return stringBuilder.ToString();
					}
					stringBuilder.Append(c);
					pos++;
				}
			}
			return stringBuilder.ToString();
		}

		internal static object ParseNumber(string json, ref int pos)
		{
			int num = pos;
			bool flag = false;
			if (pos < json.Length && json[pos] == '-')
			{
				pos++;
			}
			while (pos < json.Length && char.IsDigit(json[pos]))
			{
				pos++;
			}
			if (pos < json.Length && json[pos] == '.')
			{
				flag = true;
				pos++;
				while (pos < json.Length && char.IsDigit(json[pos]))
				{
					pos++;
				}
			}
			if (pos < json.Length && (json[pos] == 'e' || json[pos] == 'E'))
			{
				flag = true;
				pos++;
				if (pos < json.Length && (json[pos] == '+' || json[pos] == '-'))
				{
					pos++;
				}
				while (pos < json.Length && char.IsDigit(json[pos]))
				{
					pos++;
				}
			}
			string s = json.Substring(num, pos - num);
			if (flag && double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return result;
			}
			if (!flag && long.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
			{
				return result2;
			}
			return 0L;
		}

		internal static object ParseLiteral(string json, ref int pos, string literal, object result)
		{
			if (pos + literal.Length <= json.Length && json.Substring(pos, literal.Length) == literal)
			{
				pos += literal.Length;
				return result;
			}
			pos++;
			return null;
		}

		internal static int ToInt(object val)
		{
			if (val is long num)
			{
				return (int)num;
			}
			if (val is double num2)
			{
				return (int)num2;
			}
			if (val is int)
			{
				return (int)val;
			}
			return 0;
		}
	}
}
namespace SenpaisChest
{
	[BepInPlugin("com.azraelgodking.senpaischest", "Senpai's Chest", "2.7.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		private static SmartChestManager _staticManager;

		private static SmartChestSaveSystem _staticSaveSystem;

		private static SmartChestUI _staticUI;

		private static SmartChestConfig _staticConfig;

		private static MuseumTodoIntegration _museumTodoIntegration;

		internal static Chest CurrentInteractingChest;

		private static string _gameplaySessionCharacter;

		private static bool _applicationQuitting;

		private static readonly Dictionary<int, float> _sceneDiscardSuppressByHandle = new Dictionary<int, float>();

		private const float SceneDiscardSuppressSeconds = 20f;

		private Harmony _harmony;

		private SmartChestManager _manager;

		private SmartChestSaveSystem _saveSystem;

		private SmartChestUI _ui;

		private SmartChestConfig _config;

		private static GameObject _persistentRunner;

		private static SmartChestPersistentRunner _updateRunner;

		private bool _wasInMenuScene = true;

		private static GameObject _pendingChestPanel;

		private static Chest _pendingChest;

		private static int _pendingChestButtonFrames;

		private const string SenpaisChestConfigPanelName = "SenpaisChest_ConfigPanel";

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		public static ConfigFile ConfigFile { get; private set; }

		private void Awake()
		{
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Expected O, but got Unknown
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigFile = CreateNamedConfig();
			ConfigFileHelper.ReplacePluginConfig((BaseUnityPlugin)(object)this, ConfigFile, (Action<string>)Log.LogWarning);
			Log.LogInfo((object)"Loading Senpai's Chest v2.7.0");
			CreatePersistentRunner();
			try
			{
				_config = new SmartChestConfig();
				_config.Initialize(ConfigFile);
				_staticConfig = _config;
				_config.UIScale.SettingChanged += delegate
				{
					float scale = Mathf.Clamp(_config.UIScale.Value, 0.5f, 2.5f);
					_staticUI?.SetScale(scale);
				};
				_manager = new SmartChestManager();
				_saveSystem = new SmartChestSaveSystem(_manager);
				_staticManager = _manager;
				_staticSaveSystem = _saveSystem;
				GameObject val = new GameObject("SenpaisChest_UI");
				Object.DontDestroyOnLoad((Object)(object)val);
				_ui = val.AddComponent<SmartChestUI>();
				_ui.Initialize(_manager);
				_ui.SetScale(Mathf.Clamp(_config.UIScale.Value, 0.5f, 2.5f));
				_staticUI = _ui;
				_harmony = new Harmony("com.azraelgodking.senpaischest");
				ApplyPatches();
				InitializeIntegrations();
				SceneManager.sceneLoaded += OnSceneLoaded;
				SceneManager.activeSceneChanged += OnActiveSceneChanged;
				if (_config.CheckForUpdates.Value)
				{
					VersionChecker.CheckForUpdate("com.azraelgodking.senpaischest", "2.7.0", Log, delegate(VersionChecker.VersionCheckResult result)
					{
						result.NotifyUpdateAvailable(Log);
					});
				}
				Log.LogInfo((object)"Senpai's Chest loaded successfully!");
				Log.LogInfo((object)string.Format("Press {0}{1} to configure a chest while interacting with it", _config.RequireCtrlModifier.Value ? "Ctrl+" : "", _config.ToggleKey.Value));
				if (_config.EnableChestLabels.Value)
				{
					Log.LogInfo((object)"Chest Labels enabled - labels shown above Chest and BankChest (not Hoppers/Animal Feeders)");
				}
			}
			catch (Exception arg)
			{
				Log.LogError((object)string.Format("Failed to load {0}: {1}", "Senpai's Chest", arg));
			}
		}

		private void CreatePersistentRunner()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			if ((Object)(object)_persistentRunner != (Object)null)
			{
				Log.LogInfo((object)"PersistentRunner already exists");
				return;
			}
			_persistentRunner = new GameObject("SenpaisChest_PersistentRunner");
			Object.DontDestroyOnLoad((Object)(object)_persistentRunner);
			((Object)_persistentRunner).hideFlags = (HideFlags)61;
			SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner);
			_updateRunner = _persistentRunner.AddComponent<SmartChestPersistentRunner>();
			Log.LogInfo((object)"Created hidden PersistentRunner");
		}

		private static ConfigFile CreateNamedConfig()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			string text = Path.Combine(Paths.ConfigPath, "SenpaisChest.cfg");
			string text2 = Path.Combine(Paths.ConfigPath, "com.azraelgodking.senpaischest.cfg");
			try
			{
				if (!File.Exists(text) && File.Exists(text2))
				{
					File.Copy(text2, text);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("[Config] Migration to SenpaisChest.cfg failed: " + ex.Message));
				}
			}
			return new ConfigFile(text, true);
		}

		public static void EnsureUIComponentsExist()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			try
			{
				if ((Object)(object)_persistentRunner == (Object)null || (Object)(object)_updateRunner == (Object)null)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)"[EnsureUI] Recreating PersistentRunner...");
					}
					_persistentRunner = new GameObject("SenpaisChest_PersistentRunner");
					Object.DontDestroyOnLoad((Object)(object)_persistentRunner);
					((Object)_persistentRunner).hideFlags = (HideFlags)61;
					SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner);
					_updateRunner = _persistentRunner.AddComponent<SmartChestPersistentRunner>();
				}
				if ((Object)(object)_staticUI == (Object)null)
				{
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogInfo((object)"[EnsureUI] Recreating SmartChestUI...");
					}
					GameObject val = new GameObject("SenpaisChest_UI");
					Object.DontDestroyOnLoad((Object)val);
					_staticUI = val.AddComponent<SmartChestUI>();
					_staticUI.Initialize(_staticManager);
					_staticUI.SetScale(Mathf.Clamp(_staticConfig.UIScale.Value, 0.5f, 2.5f));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log3 = Log;
				if (log3 != null)
				{
					log3.LogError((object)("[EnsureUI] Error recreating UI: " + ex.Message));
				}
			}
		}

		private void InitializeIntegrations()
		{
			try
			{
				_museumTodoIntegration?.Dispose();
				_museumTodoIntegration = null;
				Dictionary<string, PluginInfo> pluginInfos = Chainloader.PluginInfos;
				bool flag = pluginInfos.ContainsKey("com.azraelgodking.sunhavenmuseumutilitytracker");
				bool flag2 = pluginInfos.ContainsKey("com.azraelgodking.sunhaventodo");
				if (flag && flag2)
				{
					_museumTodoIntegration = new MuseumTodoIntegration();
					return;
				}
				if (!flag)
				{
					Log.LogInfo((object)"[Integrations] S.M.U.T. not found");
				}
				if (!flag2)
				{
					Log.LogInfo((object)"[Integrations] SunhavenTodo not found");
				}
				Log.LogInfo((object)"[Integrations] Museum todo integration disabled (requires both S.M.U.T. and Todo)");
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("[Integrations] Error initializing: " + ex.Message));
			}
		}

		private void ApplyPatches()
		{
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Expected O, but got Unknown
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Expected O, but got Unknown
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Expected O, but got Unknown
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Expected O, but got Unknown
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Expected O, but got Unknown
			//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ff: Expected O, but got Unknown
			try
			{
				PatchMethod(typeof(Player), "InitializeAsOwner", typeof(Plugin), "OnPlayerInitialized");
				PatchMethod(typeof(Chest), "Interact", typeof(Plugin), "OnChestInteract", new Type[1] { typeof(int) });
				MethodInfo methodInfo = AccessTools.Method(typeof(Chest), "EndInteract", new Type[1] { typeof(int) }, (Type[])null);
				if (methodInfo != null)
				{
					HarmonyMethod val = new HarmonyMethod(AccessTools.Method(typeof(Plugin), "OnChestEndInteract_Prefix", (Type[])null, (Type[])null));
					_harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"Patched Chest.EndInteract (prefix)");
				}
				else
				{
					Log.LogWarning((object)"Could not find method Chest.EndInteract");
				}
				Type type = AccessTools.TypeByName("Wish.UIHandler");
				Type type2 = AccessTools.TypeByName("Wish.IExternalUIHandler");
				if (type != null && type2 != null)
				{
					MethodInfo methodInfo2 = AccessTools.Method(type, "OpenUI", new Type[5]
					{
						typeof(GameObject),
						typeof(Transform),
						typeof(bool),
						typeof(bool),
						type2
					}, (Type[])null);
					if (methodInfo2 != null)
					{
						HarmonyMethod val2 = new HarmonyMethod(AccessTools.Method(typeof(Plugin), "OnUIHandlerOpenUI_Postfix", (Type[])null, (Type[])null));
						_harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Log.LogInfo((object)"Patched UIHandler.OpenUI (postfix)");
					}
					else
					{
						Log.LogWarning((object)"Could not find method UIHandler.OpenUI");
					}
				}
				else
				{
					Log.LogWarning((object)"Could not find Wish.UIHandler or Wish.IExternalUIHandler for chest button integration");
				}
				MethodInfo methodInfo3 = AccessTools.Method(typeof(Input), "GetKeyDown", new Type[1] { typeof(KeyCode) }, (Type[])null);
				if (methodInfo3 != null)
				{
					_harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(AccessTools.Method(typeof(Plugin), "OnInputGetKeyDown_Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"Patched Input.GetKeyDown (prefix)");
				}
				MethodInfo methodInfo4 = AccessTools.Method(typeof(Input), "GetKey", new Type[1] { typeof(KeyCode) }, (Type[])null);
				if (methodInfo4 != null)
				{
					_harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(AccessTools.Method(typeof(Plugin), "OnInputGetKey_Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"Patched Input.GetKey (prefix)");
				}
				MethodInfo methodInfo5 = AccessTools.Method(typeof(Input), "GetButtonDown", new Type[1] { typeof(string) }, (Type[])null);
				if (methodInfo5 != null)
				{
					_harmony.Patch((MethodBase)methodInfo5, new HarmonyMethod(AccessTools.Method(typeof(Plugin), "OnInputGetButtonDown_Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"Patched Input.GetButtonDown (prefix)");
				}
				Type type3 = AccessTools.TypeByName("Wish.PlayerInput");
				if (type3 != null)
				{
					MethodInfo methodInfo6 = AccessTools.Method(type3, "GetButtonDown", new Type[1] { typeof(string) }, (Type[])null);
					if (methodInfo6 != null)
					{
						_harmony.Patch((MethodBase)methodInfo6, new HarmonyMethod(AccessTools.Method(typeof(Plugin), "OnPlayerInputGetButtonDown_Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Log.LogInfo((object)"Patched PlayerInput.GetButtonDown(string) (prefix)");
					}
					else
					{
						Log.LogWarning((object)"Could not find method PlayerInput.GetButtonDown(string)");
					}
				}
				else
				{
					Log.LogWarning((object)"Could not find Wish.PlayerInput type for UICancel blocking");
				}
				MethodInfo methodInfo7 = AccessTools.Method(typeof(Chest), "OnDisable", (Type[])null, (Type[])null);
				if (methodInfo7 != null)
				{
					_harmony.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(Plugin), "OnChestOnDisable_Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"Patched Chest.OnDisable (cleanup when chest removed from world)");
				}
				ApplyChestLabelPatches();
				Log.LogInfo((object)"Harmony patches applied successfully");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to apply patches: {arg}");
			}
		}

		private void ApplyChestLabelPatches()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Expected O, but got Unknown
			Type typeFromHandle = typeof(ChestLabelPatch);
			MethodInfo methodInfo = AccessTools.Method(typeFromHandle, "SetMeta_Postfix", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeFromHandle, "OnEnable_Postfix", (Type[])null, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeFromHandle, "InteractionPoint_Postfix", (Type[])null, (Type[])null);
			string[] array = new string[3] { "SetMeta", "ReceiveNewMeta", "SaveMeta" };
			foreach (string text in array)
			{
				MethodInfo methodInfo4 = AccessTools.Method(typeof(Chest), text, (Type[])null, (Type[])null);
				if (methodInfo4 != null)
				{
					_harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)("Patched Chest." + text + " (labels)"));
				}
			}
			MethodInfo methodInfo5 = AccessTools.Method(typeof(Chest), "OnEnable", (Type[])null, (Type[])null);
			if (methodInfo5 != null)
			{
				_harmony.Patch((MethodBase)methodInfo5, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)"Patched Chest.OnEnable (labels)");
			}
			MethodInfo methodInfo6 = AccessTools.Method(typeof(Chest), "get_InteractionPoint", (Type[])null, (Type[])null);
			if (methodInfo6 != null)
			{
				_harmony.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)"Patched Chest.get_InteractionPoint (labels)");
			}
		}

		private void PatchMethod(Type targetType, string methodName, Type patchType, string patchMethodName, Type[] parameters = null)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			MethodInfo methodInfo = ((parameters != null) ? AccessTools.Method(targetType, methodName, parameters, (Type[])null) : AccessTools.Method(targetType, methodName, (Type[])null, (Type[])null));
			if (methodInfo == null)
			{
				Log.LogWarning((object)("Could not find method " + targetType.Name + "." + methodName));
				return;
			}
			MethodInfo methodInfo2 = AccessTools.Method(patchType, patchMethodName, (Type[])null, (Type[])null);
			_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			Log.LogInfo((object)("Patched " + targetType.Name + "." + methodName));
		}

		private static void OnPlayerInitialized(Player __instance)
		{
			try
			{
				if ((Object)(object)__instance != (Object)(object)Player.Instance)
				{
					return;
				}
				EnsureUIComponentsExist();
				string text = null;
				CharacterData currentCharacter = GameSave.CurrentCharacter;
				if (currentCharacter != null)
				{
					text = currentCharacter.characterName;
				}
				if (string.IsNullOrEmpty(text))
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)"Player initialized but no character name found");
					}
					return;
				}
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("Player initialized: " + text));
				}
				_staticManager?.SetCharacterName(text);
				_museumTodoIntegration?.Reset();
				SmartChestSaveData data = _staticSaveSystem?.Load(text);
				_staticManager?.LoadData(data);
				_gameplaySessionCharacter = text;
			}
			catch (Exception arg)
			{
				ManualLogSource log3 = Log;
				if (log3 != null)
				{
					log3.LogError((object)$"Error in OnPlayerInitialized: {arg}");
				}
			}
		}

		private static void OnChestInteract(Chest __instance, int interactType)
		{
			if (interactType != 0)
			{
				return;
			}
			CurrentInteractingChest = __instance;
			try
			{
				FieldInfo field = ((object)__instance).GetType().GetField("ui", BindingFlags.Instance | BindingFlags.NonPublic);
				if (!(field != null))
				{
					return;
				}
				object? value = field.GetValue(__instance);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				if (val != null && (Object)(object)val != (Object)null)
				{
					_pendingChestPanel = val;
					_pendingChest = __instance;
					_pendingChestButtonFrames = 3;
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)$"[SenpaisChest] Scheduled embedded panel from Chest.Interact (will add in 3 frames) - UI active: {val.activeInHierarchy}");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogDebug((object)("[SenpaisChest] Could not schedule chest button: " + ex.Message));
				}
			}
		}

		private static void OnChestOnDisable_Postfix(Chest __instance)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (_applicationQuitting || !Application.isPlaying || (Object)(object)__instance == (Object)null)
				{
					return;
				}
				GameObject gameObject = ((Component)__instance).gameObject;
				if ((Object)(object)gameObject == (Object)null)
				{
					return;
				}
				Scene scene = gameObject.scene;
				if (!((Scene)(ref scene)).IsValid())
				{
					return;
				}
				if (IsDiscardedSceneSuppressed(scene))
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogDebug((object)("[SenpaisChest] OnDisable: skip rule removal (scene '" + ((Scene)(ref scene)).name + "' discarded)"));
					}
					return;
				}
				string chestId = SmartChestManager.GetChestId(__instance);
				if (!string.IsNullOrEmpty(chestId) && _staticManager != null && _staticManager.RemoveSmartChest(chestId))
				{
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogInfo((object)("[SenpaisChest] Removed smart chest rules (chest left world): " + chestId));
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log3 = Log;
				if (log3 != null)
				{
					log3.LogError((object)("[SenpaisChest] OnChestOnDisable_Postfix: " + ex.Message));
				}
			}
		}

		private static bool OnInputGetKeyDown_Prefix(KeyCode key, ref bool __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)key != 8)
			{
				return true;
			}
			if (!SmartChestConfig.StaticBlockInputWhenTyping)
			{
				return true;
			}
			if (!SmartChestUI.ShouldBlockGameInput(_staticUI))
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static bool OnInputGetKey_Prefix(KeyCode key, ref bool __result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			if ((int)key != 8)
			{
				return true;
			}
			if (!SmartChestConfig.StaticBlockInputWhenTyping)
			{
				return true;
			}
			if (!SmartChestUI.ShouldBlockGameInput(_staticUI))
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static bool OnInputGetButtonDown_Prefix(string buttonName, ref bool __result)
		{
			if (string.IsNullOrEmpty(buttonName))
			{
				return true;
			}
			if (!buttonName.Equals("Cancel", StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
			if (!SmartChestConfig.StaticBlockInputWhenTyping)
			{
				return true;
			}
			if (!SmartChestUI.ShouldBlockGameInput(_staticUI))
			{
				return true;
			}
			__result = false;
			return false;
		}

		private static bool OnPlayerInputGetButtonDown_Prefix(string button, ref bool __result)
		{
			if (!SmartChestConfig.StaticBlockInputWhenTyping)
			{
				return true;
			}
			if (!SmartChestUI.ShouldBlockGameInput(_staticUI))
			{
				return true;
			}
			if (button == "UICancel" || button == "Close")
			{
				__result = false;
				return false;
			}
			return true;
		}

		private static bool OnChestEndInteract_Prefix(Chest __instance, int interactType)
		{
			if ((Object)(object)CurrentInteractingChest == (Object)(object)__instance && (Object)(object)_staticUI != (Object)null && _staticUI.IsVisible && !_staticUI.IsEmbedded)
			{
				return false;
			}
			if ((Object)(object)CurrentInteractingChest == (Object)(object)__instance)
			{
				CurrentInteractingChest = null;
				_staticUI?.Hide();
			}
			return true;
		}

		private static void OnUIHandlerOpenUI_Postfix(object __instance, GameObject ui, Transform parent, bool playAudio, bool animate, object externalUIHandler)
		{
			if ((Object)(object)ui == (Object)null || externalUIHandler == null)
			{
				return;
			}
			Chest val = (Chest)((externalUIHandler is Chest) ? externalUIHandler : null);
			if (val == null)
			{
				return;
			}
			try
			{
				_pendingChestPanel = ui;
				_pendingChest = val;
				_pendingChestButtonFrames = 3;
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)$"[SenpaisChest] Scheduled embedded panel (will add in 3 frames) - UI active: {ui.activeInHierarchy}");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[SenpaisChest] Failed to schedule embedded panel: " + ex.Message));
				}
			}
		}

		internal static void ProcessPendingChestButton()
		{
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			if (_pendingChestButtonFrames <= 0 || (Object)(object)_pendingChestPanel == (Object)null || (Object)(object)_pendingChest == (Object)null)
			{
				return;
			}
			_pendingChestButtonFrames--;
			if (_pendingChestButtonFrames != 0)
			{
				return;
			}
			GameObject pendingChestPanel = _pendingChestPanel;
			Chest pendingChest = _pendingChest;
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)$"[SenpaisChest] Processing pending panel: panel={(Object)(object)pendingChestPanel != (Object)null}, active={((pendingChestPanel != null) ? new bool?(pendingChestPanel.activeInHierarchy) : null)}, chest={(Object)(object)pendingChest != (Object)null}");
			}
			if ((Object)(object)pendingChestPanel == (Object)null || !pendingChestPanel.activeInHierarchy)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"[SenpaisChest] Skip add panel: panel null or inactive (panel={(Object)(object)pendingChestPanel != (Object)null}, active={((pendingChestPanel != null) ? new bool?(pendingChestPanel.activeInHierarchy) : null)})");
				}
				_pendingChestPanel = null;
				_pendingChest = null;
				return;
			}
			Canvas componentInParent = pendingChestPanel.GetComponentInParent<Canvas>();
			ManualLogSource log3 = Log;
			if (log3 != null)
			{
				log3.LogInfo((object)$"[SenpaisChest] Chest UI state - Canvas: {(Object)(object)componentInParent != (Object)null}, Canvas active: {((componentInParent != null) ? new bool?(((Component)componentInParent).gameObject.activeInHierarchy) : null)}, RenderMode: {((componentInParent != null) ? new RenderMode?(componentInParent.renderMode) : null)}");
			}
			try
			{
				AddSenpaisChestNotePanel(pendingChestPanel, pendingChest);
			}
			catch (Exception ex)
			{
				ManualLogSource log4 = Log;
				if (log4 != null)
				{
					log4.LogWarning((object)("[SenpaisChest] Failed to add embedded panel: " + ex.Message + "\n" + ex.StackTrace));
				}
			}
			finally
			{
				_pendingChestPanel = null;
				_pendingChest = null;
			}
		}

		private static void AddSenpaisChestNotePanel(GameObject chestUiRoot, Chest chest)
		{
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Expected O, but got Unknown
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0252: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)chestUiRoot == (Object)null || !chestUiRoot.activeInHierarchy)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)$"[SenpaisChest] Cannot add panel: chestUiRoot is null or inactive (null={(Object)(object)chestUiRoot == (Object)null}, active={((chestUiRoot != null) ? new bool?(chestUiRoot.activeInHierarchy) : null)})");
				}
				return;
			}
			Transform val = chestUiRoot.transform.Find("SenpaisChest_ConfigPanel");
			if ((Object)(object)val != (Object)null)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogInfo((object)$"[SenpaisChest] Panel already exists, reusing - active: {((Component)val).gameObject.activeInHierarchy}");
				}
				SmartChestUI uI = GetUI();
				if ((Object)(object)uI != (Object)null)
				{
					uI.ShowNoteForChest(chest, ((Component)val).gameObject);
				}
				return;
			}
			if ((Object)(object)chestUiRoot.GetComponentInParent<Canvas>() == (Object)null)
			{
				ManualLogSource log3 = Log;
				if (log3 != null)
				{
					log3.LogWarning((object)"[SenpaisChest] Chest UI root is not under a Canvas, cannot create embedded panel");
				}
				return;
			}
			Transform transform = chestUiRoot.transform;
			try
			{
				GameObject val2 = new GameObject("SenpaisChest_ConfigPanel");
				val2.transform.SetParent(transform, false);
				val2.transform.SetAsLastSibling();
				val2.SetActive(true);
				RectTransform val3 = val2.AddComponent<RectTransform>();
				val3.anchorMin = new Vector2(0.5f, 0f);
				val3.anchorMax = new Vector2(0.5f, 0f);
				val3.pivot = new Vector2(0.5f, 0f);
				val3.anchoredPosition = Vector2.zero;
				val3.sizeDelta = new Vector2(340f, 34f);
				ManualLogSource log4 = Log;
				if (log4 != null)
				{
					log4.LogInfo((object)$"[SenpaisChest] Panel RectTransform - anchorMin={val3.anchorMin}, anchorMax={val3.anchorMax}, sizeDelta={val3.sizeDelta}, rect={val3.rect}");
				}
				CanvasGroup obj = val2.AddComponent<CanvasGroup>();
				obj.blocksRaycasts = false;
				obj.interactable = false;
				obj.alpha = 1f;
				LayoutRebuilder.ForceRebuildLayoutImmediate(val3);
				Rect rect = val3.rect;
				if (!(((Rect)(ref rect)).width <= 0f))
				{
					rect = val3.rect;
					if (!(((Rect)(ref rect)).height <= 0f))
					{
						SmartChestUI uI2 = GetUI();
						if ((Object)(object)uI2 != (Object)null)
						{
							uI2.ShowNoteForChest(chest, val2);
							ManualLogSource log5 = Log;
							if (log5 != null)
							{
								log5.LogInfo((object)$"[SenpaisChest] Added note panel (Press F9 to configure) - chestUiRoot active: {chestUiRoot.activeInHierarchy}, panel active: {val2.activeInHierarchy}, rect: {val3.rect}");
							}
						}
						else
						{
							ManualLogSource log6 = Log;
							if (log6 != null)
							{
								log6.LogWarning((object)"[SenpaisChest] UI component is null, cannot show embedded panel");
							}
						}
						return;
					}
				}
				ManualLogSource log7 = Log;
				if (log7 != null)
				{
					rect = val3.rect;
					object arg = ((Rect)(ref rect)).width;
					rect = val3.rect;
					log7.LogWarning((object)$"[SenpaisChest] Panel rect is invalid (width={arg}, height={((Rect)(ref rect)).height}), waiting one more frame");
				}
				_pendingChestPanel = chestUiRoot;
				_pendingChest = chest;
				_pendingChestButtonFrames = 1;
			}
			catch (Exception arg2)
			{
				ManualLogSource log8 = Log;
				if (log8 != null)
				{
					log8.LogError((object)$"[SenpaisChest] Exception creating embedded panel: {arg2}");
				}
			}
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			bool flag = ((Scene)(ref scene)).name == "MainMenu" || ((Scene)(ref scene)).name == "Menu";
			if (flag && !_wasInMenuScene)
			{
				Log.LogInfo((object)"Returned to menu, saving data...");
				_staticSaveSystem?.Save();
				_gameplaySessionCharacter = null;
			}
			else if (!flag)
			{
				string currentCharacterName = GetCurrentCharacterName();
				if (!string.IsNullOrEmpty(currentCharacterName))
				{
					if (string.Equals(_gameplaySessionCharacter, currentCharacterName, StringComparison.Ordinal))
					{
						ManualLogSource log = Log;
						if (log != null)
						{
							log.LogDebug((object)("[SenpaisChest] Skip disk reload on scene '" + ((Scene)(ref scene)).name + "' — '" + currentCharacterName + "' already active"));
						}
					}
					else
					{
						_staticManager?.SetCharacterName(currentCharacterName);
						SmartChestSaveData data = _staticSaveSystem?.Load(currentCharacterName);
						_staticManager?.LoadData(data);
						_gameplaySessionCharacter = currentCharacterName;
						ManualLogSource log2 = Log;
						if (log2 != null)
						{
							log2.LogDebug((object)("[SenpaisChest] Loaded smart chest data from disk for '" + currentCharacterName + "' on scene load"));
						}
					}
				}
			}
			_wasInMenuScene = flag;
		}

		private void OnDestroy()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
			SceneManager.activeSceneChanged -= OnActiveSceneChanged;
			_museumTodoIntegration?.Dispose();
			_museumTodoIntegration = null;
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"Plugin OnDestroy called — static references preserved");
			}
			_staticSaveSystem?.Save();
		}

		private void OnApplicationQuit()
		{
			_applicationQuitting = true;
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"Application quitting — saving data");
			}
			_staticSaveSystem?.Save();
		}

		private static bool IsDiscardedSceneSuppressed(Scene scene)
		{
			if (!((Scene)(ref scene)).IsValid())
			{
				return false;
			}
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			CleanupDiscardSuppressionMap(realtimeSinceStartup);
			if (_sceneDiscardSuppressByHandle.TryGetValue(((Scene)(ref scene)).handle, out var value))
			{
				return realtimeSinceStartup < value;
			}
			return false;
		}

		private static void CleanupDiscardSuppressionMap(float now)
		{
			int[] array = null;
			int num = 0;
			foreach (KeyValuePair<int, float> item in _sceneDiscardSuppressByHandle)
			{
				if (!(item.Value > now))
				{
					if (array == null)
					{
						array = new int[_sceneDiscardSuppressByHandle.Count];
					}
					array[num++] = item.Key;
				}
			}
			for (int i = 0; i < num; i++)
			{
				_sceneDiscardSuppressByHandle.Remove(array[i]);
			}
		}

		private static void OnActiveSceneChanged(Scene previous, Scene next)
		{
			if (((Scene)(ref previous)).IsValid())
			{
				CleanupDiscardSuppressionMap(Time.realtimeSinceStartup);
				_sceneDiscardSuppressByHandle[((Scene)(ref previous)).handle] = Time.realtimeSinceStartup + 20f;
			}
		}

		internal static SmartChestManager GetManager()
		{
			return _staticManager;
		}

		internal static SmartChestSaveSystem GetSaveSystem()
		{
			return _staticSaveSystem;
		}

		internal static string GetCurrentCharacterName()
		{
			try
			{
				CharacterData currentCharacter = GameSave.CurrentCharacter;
				return (currentCharacter != null) ? currentCharacter.characterName : null;
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogDebug((object)("[SenpaisChest] GetCurrentCharacterName: " + ex.Message));
				}
				return null;
			}
		}

		internal static SmartChestUI GetUI()
		{
			return _staticUI;
		}

		internal static SmartChestConfig GetConfig()
		{
			return _staticConfig;
		}

		internal static MuseumTodoIntegration GetMuseumTodoIntegration()
		{
			return _museumTodoIntegration;
		}
	}
	public class SmartChestPersistentRunner : MonoBehaviour
	{
		private float _scanTimer;

		private float _autoSaveTimer;

		private float _chestLabelScanTimer;

		private const float AUTO_SAVE_INTERVAL = 300f;

		private const float CHEST_LABEL_SCAN_INTERVAL = 2f;

		private int _lastCountdownSecond = -1;

		private void Update()
		{
			Plugin.ProcessPendingChestButton();
			SmartChestConfig config = Plugin.GetConfig();
			if (config != null && config.EnableChestLabels.Value)
			{
				_chestLabelScanTimer += Time.unscaledDeltaTime;
				if (_chestLabelScanTimer >= 2f)
				{
					_chestLabelScanTimer = 0f;
					Chest[] array = Object.FindObjectsOfType<Chest>();
					foreach (Chest val in array)
					{
						if ((Object)(object)val != (Object)null && (Object)(object)((Component)val).gameObject != (Object)null)
						{
							ChestLabelPatch.EnsureLabel(val);
						}
					}
				}
			}
			SmartChestManager manager = Plugin.GetManager();
			if (config == null || manager == null)
			{
				return;
			}
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			float scanInterval = config.GetScanInterval();
			_scanTimer += unscaledDeltaTime;
			int num = (int)Math.Ceiling(scanInterval - _scanTimer);
			if (num >= 1 && num <= 10 && num != _lastCountdownSecond)
			{
				_lastCountdownSecond = num;
				if (SmartChestConfig.StaticEnableScanCountdownDebug)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogDebug((object)$"[Scan] Next scan in {num}...");
					}
				}
			}
			if (_scanTimer >= scanInterval)
			{
				_scanTimer = 0f;
				_lastCountdownSecond = -1;
				try
				{
					manager.ExecuteScan(config.MaxItemsPerScan.Value, config.EnableNotifications.Value);
					Plugin.GetMuseumTodoIntegration()?.OnScanComplete();
				}
				catch (Exception arg)
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogError((object)$"Error during scan: {arg}");
					}
				}
			}
			_autoSaveTimer += unscaledDeltaTime;
			if (_autoSaveTimer >= 300f)
			{
				_autoSaveTimer = 0f;
				if (manager.IsDirty)
				{
					Plugin.GetSaveSystem()?.Save();
				}
			}
			DetectHotkey(config);
		}

		private void DetectHotkey(SmartChestConfig config)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (TextInputFocusGuard.ShouldDeferModHotkeys(Plugin.Log))
			{
				return;
			}
			KeyCode staticToggleKey = SmartChestConfig.StaticToggleKey;
			bool staticRequireCtrl = SmartChestConfig.StaticRequireCtrl;
			if (Input.GetKeyDown(staticToggleKey) && (!staticRequireCtrl || Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)))
			{
				SmartChestUI uI = Plugin.GetUI();
				if (!((Object)(object)uI == (Object)null) && (Object)(object)Plugin.CurrentInteractingChest != (Object)null)
				{
					uI.ToggleForChest(Plugin.CurrentInteractingChest);
				}
			}
		}

		private void OnDestroy()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			string text = (((Scene)(ref activeScene)).name ?? string.Empty).ToLowerInvariant();
			if (!Application.isPlaying || text.Contains("menu") || text.Contains("title"))
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[PersistentRunner] OnDestroy during app quit/menu unload (expected).");
				}
			}
			else
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)"[PersistentRunner] OnDestroy outside quit/menu (unexpected).");
				}
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.azraelgodking.senpaischest";

		public const string PLUGIN_NAME = "Senpai's Chest";

		public const string PLUGIN_VERSION = "2.7.0";
	}
}
namespace SenpaisChest.UI
{
	[DefaultExecutionOrder(-30000)]
	public class SmartChestUI : MonoBehaviour
	{
		private const string PAUSE_ID = "SenpaisChest_Config";

		private SmartChestManager _manager;

		private bool _isVisible;

		private Chest _currentChest;

		private SmartChestData _currentData;

		private string _chestId;

		private bool _confirmCopyRules;

		private float _confirmCopyRulesUntil;

		private float _scale = 1f;

		private const float BASE_WINDOW_WIDTH = 420f;

		private const float BASE_WINDOW_HEIGHT = 500f;

		private const float BASE_GROUPS_WIDTH = 400f;

		private const float BASE_GROUPS_HEIGHT = 450f;

		private Rect _windowRect = new Rect(100f, 100f, 420f, 500f);

		private GameObject _notePanelRoot;

		private Rect _noteRect;

		private bool _noteRectDirty = true;

		private float _contentHeight = 500f;

		private int _selectedRuleType;

		private string _itemIdInput = "";

		private int _selectedCategory;

		private int _selectedItemType;

		private int _selectedProperty;

		private int _selectedGroup;

		private string _lastSearchQuery = "";

		private List<KeyValuePair<int, string>> _searchResults = new List<KeyValuePair<int, string>>();

		private Vector2 _searchScrollPos;

		private int _selectedItemId = -1;

		private string _selectedItemName = "";

		private bool _groupsWindowVisible;

		private Rect _groupsWindowRect;

		private ItemGroup _editingGroup;

		private string _newGroupName = "";

		private string _groupItemSearch = "";

		private string _groupItemSearchLast = "";

		private string _groupPatternInput = "";

		private List<KeyValuePair<int, string>> _groupSearchResults = new List<KeyValuePair<int, string>>();

		private Vector2 _groupSearchScroll;

		private Vector2 _groupListScroll;

		private Vector2 _groupItemsScroll;

		private static readonly string[] RuleTypeNames = new string[6] { "By Item", "By Category", "By Item Type", "By Property", "By Group", "By wildcard name" };

		private string _wildcardPatternInput = "";

		private static readonly string[] BaseCategoryNames = new string[6] { "Equip", "Use", "Craftable", "Monster", "Furniture", "Quest" };

		private static readonly string[] CategoryNamesWithMuseum = new string[7] { "Equip", "Use", "Craftable", "Monster", "Furniture", "Quest", "Undonated Items" };

		private static readonly string[] ItemTypeNames = new string[9] { "Normal", "Armor", "Food", "Fish", "Crop", "WateringCan", "Animal", "Pet", "Tool" };

		private static readonly string[] PropertyNames = new string[8] { "isGem", "isForageable", "isAnimalProduct", "isMeal", "isFruit", "isArtisanryItem", "isPotion", "isNotDonated" };

		private static readonly string[] PropertyDisplayNames = new string[8] { "Gems", "Forageables", "Animal Products", "Meals", "Fruits", "Artisanry Items", "Potions", "Museum (Not Donated)" };

		internal const string SearchFieldControlName = "SmartChestItemSearch";

		internal const string GroupSearchFieldControlName = "SmartChestGroupItemSearch";

		internal const string WildcardPatternControlName = "SmartChestWildcardPattern";

		private readonly Color _bgDark = new Color(0.15f, 0.16f, 0.24f, 1f);

		private readonly Color _borderGold = new Color(0.75f, 0.65f, 0.3f, 1f);

		private readonly Color _goldText = new Color(0.95f, 0.85f, 0.35f, 1f);

		private readonly Color _whiteText = new Color(0.95f, 0.95f, 0.95f, 1f);

		private readonly Color _dimText = new Color(0.6f, 0.6f, 0.7f, 1f);

		private readonly Color _greenActive = new Color(0.2f, 0.55f, 0.45f, 1f);

		private readonly Color _greenHover = new Color(0.25f, 0.65f, 0.52f, 1f);

		private readonly Color _greenBright = new Color(0.3f, 0.7f, 0.55f, 1f);

		private readonly Color _redDanger = new Color(0.75f, 0.2f, 0.2f, 1f);

		private readonly Color _redHover = new Color(0.85f, 0.28f, 0.28f, 1f);

		private readonly Color _btnInactive = new Color(0.22f, 0.24f, 0.34f, 1f);

		private readonly Color _btnHover = new Color(0.3f, 0.32f, 0.44f, 1f);

		private readonly Color _ruleBoxColor = new Color(0.18f, 0.19f, 0.28f, 1f);

		private readonly Color _fieldBg = new Color(0.12f, 0.13f, 0.22f, 1f);

		private readonly Color _museumHighlight = new Color(0.35f, 0.65f, 0.85f, 1f);

		private readonly Color _parchmentLight = new Color(0.96f, 0.93f, 0.86f, 0.98f);

		private readonly Color _parchment = new Color(0.92f, 0.87f, 0.78f, 0.97f);

		private readonly Color _parchmentDark = new Color(0.85f, 0.78f, 0.65f, 0.95f);

		private readonly Color _woodDark = new Color(0.35f, 0.25f, 0.15f);

		private readonly Color _woodMedium = new Color(0.5f, 0.38f, 0.25f);

		private readonly Color _woodLight = new Color(0.65f, 0.52f, 0.38f);

		private readonly Color _goldPale = new Color(0.92f, 0.82f, 0.55f);

		private readonly Color _goldRich = new Color(0.72f, 0.55f, 0.2f);

		private readonly Color _borderWood = new Color(0.45f, 0.35f, 0.22f, 0.9f);

		private readonly Color _chestTextDark = new Color(0.08f, 0.06f, 0.05f, 1f);

		private readonly Color _chestTextDim = new Color(0.14f, 0.11f, 0.09f, 1f);

		private readonly Color _crimsonTitle = new Color(0.52f, 0.18f, 0.12f, 1f);

		private readonly Color _crimsonTitleLight = new Color(0.58f, 0.22f, 0.16f, 1f);

		private readonly Color _innerBorderCrimson = new Color(0.42f, 0.18f, 0.14f, 1f);

		private Texture2D _solidBg;

		private Texture2D _windowBg;

		private Texture2D _ruleBg;

		private Texture2D _btnInactiveTex;

		private Texture2D _btnHoverTex;

		private Texture2D _btnActiveTex;

		private Texture2D _btnActiveHoverTex;

		private Texture2D _redBtnTex;

		private Texture2D _redBtnHoverTex;

		private Texture2D _greenBtnTex;

		private Texture2D _greenBtnHoverTex;

		private Texture2D _closeBtnTex;

		private Texture2D _closeBtnHoverTex;

		private Texture2D _fieldBgTex;

		private Texture2D _chestParchmentBg;

		private Texture2D _chestWoodBorderTex;

		private Texture2D _chestRuleBoxTex;

		private Texture2D _chestSelectorTex;

		private Texture2D _chestSelectorHoverTex;

		private Texture2D _chestSelectorActiveTex;

		private Texture2D _chestAddBtnTex;

		private Texture2D _chestAddBtnHoverTex;

		private Texture2D _chestWoodBtnTex;

		private Texture2D _chestWoodBtnHoverTex;

		private Texture2D _chestDangerBtnTex;

		private Texture2D _chestDangerBtnHoverTex;

		private Texture2D _chestFieldBgTex;

		private Texture2D _chestGoldLineTex;

		private Texture2D _noteBannerBg;

		private Texture2D _noteBannerBorderTex;

		private Texture2D _configTitleBarTex;

		private Texture2D _configContentBg;

		private Texture2D _configGoldSeparatorTex;

		private Texture2D _configSectionHeaderTex;

		private Texture2D _configSearchFieldTex;

		private Texture2D _configRemoveBtnTex;

		private Texture2D _configRemoveBtnHoverTex;

		private Texture2D _configCloseBtnTex;

		private Texture2D _configCloseBtnHoverTex;

		private Texture2D _configWindowBg;

		private Texture2D _configInnerBorderTex;

		private GUIStyle _windowStyle;

		private GUIStyle _titleStyle;

		private GUIStyle _chestTitleStyle;

		private GUIStyle _chestLabelStyle;

		private GUIStyle _chestLabelBoldStyle;

		private GUIStyle _chestLabelDimStyle;

		private GUIStyle _chestSectionHeaderStyle;

		private GUIStyle _chestToggleStyle;

		private GUIStyle _sectionHeaderStyle;

		private GUIStyle _labelStyle;

		private GUIStyle _labelBoldStyle;

		private GUIStyle _labelDimStyle;

		private GUIStyle _ruleBoxStyle;

		private GUIStyle _ruleTextStyle;

		private GUIStyle _removeRuleBtnStyle;

		private GUIStyle _closeButtonStyle;

		private GUIStyle _toggleStyle;

		private GUIStyle _textFieldStyle;

		private GUIStyle _selectorStyle;

		private GUIStyle _selectorActiveStyle;

		private GUIStyle _addButtonStyle;

		private GUIStyle _dangerButtonStyle;

		private GUIStyle _closeBottomButtonStyle;

		private GUIStyle _searchResultStyle;

		private GUIStyle _searchResultSelectedStyle;

		private GUIStyle _chestRuleBoxStyle;

		private GUIStyle _chestRuleTextStyle;

		private GUIStyle _chestSelectorStyle;

		private GUIStyle _chestSelectorActiveStyle;

		private GUIStyle _chestAddButtonStyle;

		private GUIStyle _chestDangerButtonStyle;

		private GUIStyle _chestCloseButtonStyle;

		private GUIStyle _chestSearchFieldStyle;

		private GUIStyle _chestRemoveRuleBtnStyle;

		private GUIStyle _chestSearchResultStyle;

		private GUIStyle _chestSearchResultSelectedStyle;

		private GUIStyle _noteBannerStyle;

		private GUIStyle _configTitleStyle;

		private GUIStyle _configSectionHeaderBoxStyle;

		private GUIStyle _configSearchFieldStyle;

		private GUIStyle _configRemoveButtonStyle;

		private GUIStyle _configCloseBottomStyle;

		private GUIStyle _configWindowStyle;

		private bool _stylesDirty = true;

		private float WindowWidth => 420f * _scale;

		private float WindowHeight => 500f * _scale;

		private float GroupsWindowWidth => 400f * _scale;

		private float GroupsWindowHeight => 450f * _scale;

		public bool IsVisible => _isVisible;

		public bool IsEmbedded => false;

		private float Scaled(float value)
		{
			return value * _scale;
		}

		private int ScaledFont(int baseSize)
		{
			return Mathf.Max(8, Mathf.RoundToInt((float)baseSize * _scale));
		}

		private int ScaledInt(float value)
		{
			return Mathf.RoundToInt(value * _scale);
		}

		internal static bool ShouldBlockGameInput(SmartChestUI ui)
		{
			if ((Object)(object)ui == (Object)null || !ui.IsVisible)
			{
				return false;
			}
			string nameOfFocusedControl = GUI.GetNameOfFocusedControl();
			if (!(nameOfFocusedControl == "SmartChestItemSearch") && !(nameOfFocusedControl == "SmartChestGroupItemSearch"))
			{
				return nameOfFocusedControl == "SmartChestWildcardPattern";
			}
			return true;
		}

		public void Initialize(SmartChestManager manager)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			_manager = manager;
			_windowRect = new Rect(100f, 100f, WindowWidth, WindowHeight);
			_groupsWindowRect = new Rect(150f, 150f, GroupsWindowWidth, GroupsWindowHeight);
		}

		public void SetScale(float scale)
		{
			_scale = Mathf.Clamp(scale, 0.5f, 2.5f);
			_stylesDirty = true;
			_configWindowStyle = null;
			((Rect)(ref _windowRect)).width = WindowWidth;
			((Rect)(ref _windowRect)).height = WindowHeight;
			((Rect)(ref _groupsWindowRect)).width = GroupsWindowWidth;
			((Rect)(ref _groupsWindowRect)).height = GroupsWindowHeight;
		}

		public void Show()
		{
			_isVisible = true;
		}

		public void Hide()
		{
			_isVisible = false;
			_confirmCopyRules = false;
			BlockGameInput(block: false);
			Chest currentChest = _currentChest;
			_currentChest = null;
			_currentData = null;
			_notePanelRoot = null;
			_noteRectDirty = true;
			Plugin.CurrentInteractingChest = null;
			SaveIfDirty();
			if (!((Object)(object)currentChest != (Object)null))
			{
				return;
			}
			try
			{
				currentChest.EndInteract(0);
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("[SmartChestUI] EndInteract on close: " + ex.Message));
				}
			}
		}

		public void HideConfig()
		{
			SaveIfDirty();
			_isVisible = false;
			_confirmCopyRules = false;
			_groupsWindowVisible = false;
			BlockGameInput(block: false);
		}

		private void BlockGameInput(bool block)
		{
			try
			{
				if ((Object)(object)Player.Instance != (Object)null)
				{
					if (block)
					{
						Player.Instance.AddPauseObject("SenpaisChest_Config");
					}
					else
					{
						Player.Instance.RemovePauseObject("SenpaisChest_Config");
					}
				}
				Type type = Type.GetType("PlayerInput, Assembly-CSharp");
				if (type != null)
				{
					type.GetMethod(block ? "DisableInput" : "EnableInput", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null)?.Invoke(null, new object[1] { "SenpaisChest_Config" });
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogDebug((object)("[SmartChestUI] BlockGameInput failed: " + ex.Message));
				}
			}
		}

		public void Toggle()
		{
			if (_isVisible)
			{
				Hide();
			}
			else
			{
				Show();
			}
		}

		public void ToggleForChest(Chest chest)
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if (_isVisible && (Object)(object)_currentChest == (Object)(object)chest)
			{
				HideConfig();
				return;
			}
			_currentChest = chest;
			_chestId = SmartChestManager.GetChestId(chest);
			if (string.IsNullOrEmpty(_chestId))
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"Cannot configure chest: no valid ID");
				}
				return;
			}
			string chestName = GetChestName(chest);
			_currentData = _manager.GetOrCreateSmartChest(_chestId, chestName);
			_selectedRuleType = 0;
			_itemIdInput = "";
			_lastSearchQuery = "";
			_searchResults.Clear();
			_selectedItemId = -1;
			_selectedItemName = "";
			_searchScrollPos = Vector2.zero;
			_selectedCategory = 0;
			_selectedItemType = 0;
			_selectedProperty = 0;
			_wildcardPatternInput = "";
			_confirmCopyRules = false;
			_isVisible = true;
			BlockGameInput(block: true);
			PositionWindowNextToChestPanel();
		}

		public void ShowNoteForChest(Chest chest, GameObject panelRoot)
		{
			_currentChest = chest;
			_chestId = SmartChestManager.GetChestId(chest);
			if (string.IsNullOrEmpty(_chestId))
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"[SmartChestUI] Cannot show note: no valid chest ID");
				}
				return;
			}
			_currentData = null;
			_notePanelRoot = panelRoot;
			_noteRectDirty = true;
			_isVisible = false;
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogDebug((object)$"[SmartChestUI] ShowNoteForChest: chest={(Object)(object)chest != (Object)null}, panelRoot={(Object)(object)panelRoot != (Object)null}");
			}
		}

		private void PositionWindowNextToChestPanel()
		{
			if (!((Object)(object)_currentChest == (Object)null))
			{
				float num = Scaled(8f);
				float height = Mathf.Min(Scaled(400f), (float)Screen.height - num);
				((Rect)(ref _windowRect)).width = WindowWidth;
				((Rect)(ref _windowRect)).height = height;
				((Rect)(ref _windowRect)).x = Mathf.Clamp(((float)Screen.width - ((Rect)(ref _windowRect)).width) * 0.5f, 0f, (float)Screen.width - ((Rect)(ref _windowRect)).width);
				((Rect)(ref _windowRect)).y = num;
			}
		}

		private string GetChestName(Chest chest)
		{
			return SmartChestManager.GetChestName(chest);
		}

		private void OnGUI()
		{
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Expected O, but got Unknown
			//IL_01ef: 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_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Expected O, but got Unknown
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Invalid comparison between Unknown and I4
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			if (_stylesDirty || _configWindowStyle == null)
			{
				InitializeStyles();
			}
			if (_configWindowStyle == null)
			{
				return;
			}
			if ((Object)(object)_notePanelRoot != (Object)null && !_isVisible)
			{
				if (!_notePanelRoot.activeInHierarchy)
				{
					_notePanelRoot = null;
					return;
				}
				if (_noteRectDirty || (int)Event.current.type == 8)
				{
					_noteRect = GetScreenRectFromTransform(_notePanelRoot.transform);
					_noteRectDirty = false;
				}
				Rect val = _noteRect;
				if (((Rect)(ref val)).width <= 0f || ((Rect)(ref val)).height <= 0f)
				{
					((Rect)(ref val))..ctor(((float)Screen.width - Scaled(340f)) * 0.5f, Scaled(8f), Scaled(340f), Scaled(34f));
				}
				val = ClampRectToScreen(val);
				GUI.BeginGroup(val);
				DrawNoteContent(((Rect)(ref val)).width, ((Rect)(ref val)).height);
				GUI.EndGroup();
			}
			else if (_isVisible && _currentData != null)
			{
				float num = (float)Screen.height - Scaled(40f);
				((Rect)(ref _windowRect)).width = WindowWidth;
				((Rect)(ref _windowRect)).height = Mathf.Clamp(_contentHeight, Scaled(300f), num);
				((Rect)(ref _windowRect)).x = Mathf.Clamp(((Rect)(ref _windowRect)).x, 0f, (float)Screen.width - ((Rect)(ref _windowRect)).width);
				((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, 0f, (float)Screen.height - ((Rect)(ref _windowRect)).height);
				_windowRect = GUI.Window("com.azraelgodking.senpaischest".GetHashCode(), _windowRect, new WindowFunction(DrawWindow), "", _configWindowStyle);
				if (_groupsWindowVisible)
				{
					_groupsWindowRect = GUI.Window("com.azraelgodking.senpaischest".GetHashCode() + 1, _groupsWindowRect, new WindowFunction(DrawGroupsWindow), "Manage Groups", _configWindowStyle);
				}
			}
		}

		private static Rect GetScreenRectFromTransform(Transform transform)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
			if ((Object)(object)val == (Object)null)
			{
				return new Rect(0f, 0f, 400f, 400f);
			}
			Canvas componentInParent = ((Component)val).GetComponentInParent<Canvas>();
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			val.GetWorldCorners(array);
			Vector2 val2 = default(Vector2);
			Vector2 val3 = default(Vector2);
			if ((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode == 0)
			{
				((Vector2)(ref val2))..ctor(array[0].x, array[0].y);
				((Vector2)(ref val3))..ctor(array[2].x, array[2].y);
			}
			else
			{
				Camera obj = (((Object)(object)componentInParent != (Object)null) ? componentInParent.worldCamera : Camera.main);
				val2 = RectTransformUtility.WorldToScreenPoint(obj, array[0]);
				val3 = RectTransformUtility.WorldToScreenPoint(obj, array[2]);
			}
			float x = val2.x;
			float num = (float)Screen.height - val3.y;
			float num2 = val3.x - val2.x;
			float num3 = val3.y - val2.y;
			return new Rect(x, num, num2, num3);
		}

		private static Rect ClampRectToScreen(Rect rect)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Min(((Rect)(ref rect)).width, (float)Screen.width);
			float num2 = Mathf.Min(((Rect)(ref rect)).height, (float)Screen.height);
			float num3 = Mathf.Clamp(((Rect)(ref rect)).x, 0f, (float)Screen.width - num);
			float num4 = Mathf.Clamp(((Rect)(ref rect)).y, 0f, (float)Screen.height - num2);
			return new Rect(num3, num4, num, num2);
		}

		private void DrawWindow(int windowId)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			DrawConfigContent(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, ((Rect)(ref _windowRect)).height), withWindowChrome: true);
		}

		private void DrawConfigContent(Rect rect, bool withWindowChrome)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Invalid comparison between Unknown and I4
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0325: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ad: 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_0627: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0934: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b47: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c65: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c6b: Invalid comparison between Unknown and I4
			//IL_0c12: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c18: Invalid comparison between Unknown and I4
			//IL_0c72: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c78: Invalid comparison between Unknown and I4
			//IL_0c56: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c1a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0c1f: Unknown result type (might be due to invalid IL or missing references)
			if (