Decompiled source of SenpaisChest v1.1.0
SenpaisChest.dll
Decompiled 13 hours ago
The result has been truncated due to the large size, download it to view full contents!
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 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 UnityEngine; using UnityEngine.Networking; using UnityEngine.SceneManagement; 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+b72ac30fec0dbf108a60f7a3222b24b27660f6bf")] [assembly: AssemblyProduct("SenpaisChest")] [assembly: AssemblyTitle("SenpaisChest")] [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.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 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; } } private class VersionCheckRunner : MonoBehaviour { public void StartCheck(string pluginGuid, string currentVersion, Action<VersionCheckResult> onComplete) { ((MonoBehaviour)this).StartCoroutine(CheckVersionCoroutine(pluginGuid, currentVersion, onComplete)); } 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; LogWarning(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } try { string text = www.downloadHandler.text; string pattern = "\"" + Regex.Escape(pluginGuid) + "\"\\s*:\\s*\\{([^}]+)\\}"; Match match = Regex.Match(text, pattern, RegexOptions.Singleline); if (!match.Success) { result.Success = false; result.ErrorMessage = "Mod '" + pluginGuid + "' not found in versions.json"; LogWarning(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"; LogWarning(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) { Log("Update available for " + result.ModName + ": " + currentVersion + " -> " + result.LatestVersion); } else { Log(result.ModName + " is up to date (v" + currentVersion + ")"); } } catch (Exception ex) { result.Success = false; result.ErrorMessage = "Parse error: " + ex.Message; LogError(result.ErrorMessage); } } finally { ((IDisposable)www)?.Dispose(); } onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); } private string ExtractJsonString(string json, string key) { string pattern = "\"" + key + "\"\\s*:\\s*(?:\"([^\"]*)\"|null)"; Match match = Regex.Match(json, pattern); if (!match.Success) { return null; } return match.Groups[1].Value; } } private const string VersionsUrl = "https://azraelgodking.github.io/SunhavenMod/versions.json"; private static ManualLogSource _logger; 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) _logger = logger; VersionCheckRunner versionCheckRunner = new GameObject("VersionChecker").AddComponent<VersionCheckRunner>(); Object.DontDestroyOnLoad((Object)(object)((Component)versionCheckRunner).gameObject); versionCheckRunner.StartCheck(pluginGuid, currentVersion, onComplete); } 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'); string[] array = v1.Split(new char[1] { '.' }); string[] array2 = v2.Split(new char[1] { '.' }); int num = Math.Max(array.Length, array2.Length); for (int i = 0; i < num; i++) { int result; int num2 = ((i < array.Length && int.TryParse(array[i], out result)) ? result : 0); int result2; int num3 = ((i < array2.Length && int.TryParse(array2[i], out result2)) ? result2 : 0); if (num2 < num3) { return -1; } if (num2 > num3) { return 1; } } return 0; } internal static void Log(string message) { ManualLogSource logger = _logger; if (logger != null) { logger.LogInfo((object)("[VersionChecker] " + message)); } } internal static void LogWarning(string message) { ManualLogSource logger = _logger; if (logger != null) { logger.LogWarning((object)("[VersionChecker] " + message)); } } internal static void LogError(string message) { ManualLogSource logger = _logger; if (logger != null) { logger.LogError((object)("[VersionChecker] " + message)); } } } 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 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) { Type type = AccessTools.TypeByName(typeName); if (type != null) { return type; } for (int i = 0; i < namespaces.Length; i++) { type = AccessTools.TypeByName(namespaces[i] + "." + typeName); if (type != null) { return type; } } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == typeName || t.FullName == typeName); 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; } PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.GetMethod != null) { return property.GetValue(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; } } } } namespace SenpaisChest { [BepInPlugin("com.azraelgodking.senpaischest", "Senpai's Chest", "1.1.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 Harmony _harmony; private SmartChestManager _manager; private SmartChestSaveSystem _saveSystem; private SmartChestUI _ui; private SmartChestConfig _config; private static GameObject _persistentRunner; private static SmartChestPersistentRunner _updateRunner; private string _lastKnownScene = ""; private bool _wasInMenuScene = true; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; Log.LogInfo((object)"Loading Senpai's Chest v1.1.0"); CreatePersistentRunner(); try { _config = new SmartChestConfig(); _config.Initialize(((BaseUnityPlugin)this).Config); _staticConfig = _config; _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); _staticUI = _ui; _harmony = new Harmony("com.azraelgodking.senpaischest"); ApplyPatches(); InitializeIntegrations(); SceneManager.sceneLoaded += OnSceneLoaded; if (_config.CheckForUpdates.Value) { VersionChecker.CheckForUpdate("com.azraelgodking.senpaischest", "1.1.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)); } 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; _updateRunner = _persistentRunner.AddComponent<SmartChestPersistentRunner>(); Log.LogInfo((object)"Created hidden PersistentRunner"); } 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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; _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); } } catch (Exception ex) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogError((object)("[EnsureUI] Error recreating UI: " + ex.Message)); } } } private void InitializeIntegrations() { try { 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_00aa: 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"); } Log.LogInfo((object)"Harmony patches applied successfully"); } catch (Exception arg) { Log.LogError((object)$"Failed to apply patches: {arg}"); } } 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); } 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) { CurrentInteractingChest = __instance; } } private static bool OnChestEndInteract_Prefix(Chest __instance, int interactType) { if ((Object)(object)CurrentInteractingChest == (Object)(object)__instance && (Object)(object)_staticUI != (Object)null && _staticUI.IsVisible) { return false; } if ((Object)(object)CurrentInteractingChest == (Object)(object)__instance) { CurrentInteractingChest = null; _staticUI?.Hide(); } return true; } 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(); } _wasInMenuScene = flag; _lastKnownScene = ((Scene)(ref scene)).name; } private void OnDestroy() { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"Plugin OnDestroy called — static references preserved"); } _staticSaveSystem?.Save(); } private void OnApplicationQuit() { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"Application quitting — saving data"); } _staticSaveSystem?.Save(); } internal static SmartChestManager GetManager() { return _staticManager; } internal static SmartChestSaveSystem GetSaveSystem() { return _staticSaveSystem; } 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 const float AUTO_SAVE_INTERVAL = 300f; private int _lastCountdownSecond = -1; private void Update() { SmartChestConfig config = Plugin.GetConfig(); 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; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((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_0000: Unknown result type (might be due to invalid IL or missing references) 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() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[PersistentRunner] OnDestroy called — this should NOT happen!"); } } } 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 = "1.1.0"; } } namespace SenpaisChest.UI { public class SmartChestUI : MonoBehaviour { private SmartChestManager _manager; private bool _isVisible; private Chest _currentChest; private SmartChestData _currentData; private string _chestId; private Rect _windowRect = new Rect(100f, 100f, 420f, 500f); private Vector2 _rulesScrollPos; private float _contentHeight = 500f; private int _selectedRuleType; private string _itemIdInput = ""; private int _selectedCategory; private int _selectedItemType; private int _selectedProperty; 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 static readonly string[] RuleTypeNames = new string[4] { "By Item", "By Category", "By Item Type", "By Property" }; private static readonly string[] CategoryNames = new string[6] { "Equip", "Use", "Craftable", "Monster", "Furniture", "Quest" }; 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)" }; 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); private readonly Color _whiteText = new Color(0.95f, 0.95f, 0.95f); private readonly Color _dimText = new Color(0.6f, 0.6f, 0.7f); 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 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 _separatorTex; private GUIStyle _windowStyle; private GUIStyle _titleStyle; 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 bool _stylesInitialized; public bool IsVisible => _isVisible; public void Initialize(SmartChestManager manager) { _manager = manager; } public void Show() { _isVisible = true; } public void Hide() { _isVisible = false; Chest currentChest = _currentChest; _currentChest = null; _currentData = null; Plugin.CurrentInteractingChest = null; SaveIfDirty(); if ((Object)(object)currentChest != (Object)null) { try { currentChest.EndInteract(0); } catch { } } } 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) { Hide(); 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; _isVisible = true; } private string GetChestName(Chest chest) { try { FieldInfo field = typeof(Chest).GetField("data", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { object? value = field.GetValue(chest); ChestData val = (ChestData)((value is ChestData) ? value : null); if (val != null && !string.IsNullOrEmpty(val.name)) { return val.name; } } } catch { } return "Chest"; } private void OnGUI() { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) if (_isVisible && _currentData != null) { if (!_stylesInitialized) { InitializeStyles(); } float num = (float)Screen.height - 40f; ((Rect)(ref _windowRect)).height = Mathf.Clamp(_contentHeight, 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), "", _windowStyle); } } private void DrawWindow(int windowId) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0181: 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_019a: Unknown result type (might be due to invalid IL or missing references) //IL_041d: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Invalid comparison between Unknown and I4 //IL_0459: Unknown result type (might be due to invalid IL or missing references) //IL_0425: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, ((Rect)(ref _windowRect)).height), (Texture)(object)_solidBg); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Smart Chest Config - " + _currentData.ChestName, _titleStyle, Array.Empty<GUILayoutOption>()); GUILayout.FlexibleSpace(); if (GUILayout.Button("X", _closeButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(26f), GUILayout.Height(22f) })) { Hide(); } GUILayout.EndHorizontal(); GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); bool flag = GUILayout.Toggle(_currentData.IsEnabled, " Smart Chest Enabled", _toggleStyle, Array.Empty<GUILayoutOption>()); if (flag != _currentData.IsEnabled) { _currentData.IsEnabled = flag; _manager.MarkDirty(); SaveIfDirty(); } GUILayout.EndHorizontal(); GUILayout.Space(8f); GUILayout.Label("Item Rules:", _sectionHeaderStyle, Array.Empty<GUILayoutOption>()); GUILayout.Space(4f); if (_currentData.Rules.Count == 0) { GUILayout.Label(" No rules configured. Add rules below.", _labelDimStyle, Array.Empty<GUILayoutOption>()); } else { float num = 36f; float num2 = Mathf.Min((float)_currentData.Rules.Count * num, 180f); _rulesScrollPos = GUILayout.BeginScrollView(_rulesScrollPos, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num2) }); int num3 = -1; for (int i = 0; i < _currentData.Rules.Count; i++) { GUILayout.BeginHorizontal(_ruleBoxStyle, Array.Empty<GUILayoutOption>()); GUILayout.Label($"{i + 1}. {GetRuleDisplayText(_currentData.Rules[i])}", _ruleTextStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (GUILayout.Button("X", _removeRuleBtnStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(26f), GUILayout.Height(22f) })) { num3 = i; } GUILayout.EndHorizontal(); GUILayout.Space(2f); } GUILayout.EndScrollView(); if (num3 >= 0) { _currentData.Rules.RemoveAt(num3); _manager.MarkDirty(); SaveIfDirty(); } } GUILayout.Space(10f); GUILayout.Label("Add New Rule:", _sectionHeaderStyle, Array.Empty<GUILayoutOption>()); GUILayout.Space(6f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); DrawSelectorButton(0, GUILayout.Height(28f)); DrawSelectorButton(1, GUILayout.Height(28f)); GUILayout.EndHorizontal(); GUILayout.Space(2f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); DrawSelectorButton(2, GUILayout.Height(28f)); DrawSelectorButton(3, GUILayout.Height(28f)); GUILayout.EndHorizontal(); GUILayout.Space(8f); DrawRuleInput(); GUILayout.Space(8f); if (GUILayout.Button("Add Rule", _addButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { AddRule(); } GUILayout.Space(12f); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Remove Smart Chest", _dangerButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) })) { _manager.RemoveSmartChest(_chestId); SaveIfDirty(); Hide(); } GUILayout.Space(8f); if (GUILayout.Button("Close", _closeBottomButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) })) { Hide(); } GUILayout.EndHorizontal(); if ((int)Event.current.type == 7) { Rect lastRect = GUILayoutUtility.GetLastRect(); _contentHeight = ((Rect)(ref lastRect)).yMax + 24f; } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 28f)); } private void DrawSelectorButton(int index, params GUILayoutOption[] options) { GUIStyle val = ((index == _selectedRuleType) ? _selectorActiveStyle : _selectorStyle); if (GUILayout.Button(RuleTypeNames[index], val, options)) { _selectedRuleType = index; } } private void DrawRuleInput() { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) switch (_selectedRuleType) { case 0: GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Search:", _labelBoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); _itemIdInput = GUILayout.TextField(_itemIdInput, _textFieldStyle, Array.Empty<GUILayoutOption>()); GUILayout.EndHorizontal(); if (_itemIdInput != _lastSearchQuery) { _lastSearchQuery = _itemIdInput; _searchResults = SmartChestManager.SearchItems(_itemIdInput); _searchScrollPos = Vector2.zero; } if (_selectedItemId > 0) { GUILayout.Space(2f); GUILayout.Label($"Selected: {_selectedItemName} ({_selectedItemId})", _labelStyle, Array.Empty<GUILayoutOption>()); } if (_searchResults.Count > 0) { GUILayout.Space(4f); _searchScrollPos = GUILayout.BeginScrollView(_searchScrollPos, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(120f) }); foreach (KeyValuePair<int, string> searchResult in _searchResults) { GUIStyle val = ((searchResult.Key == _selectedItemId) ? _searchResultSelectedStyle : _searchResultStyle); if (GUILayout.Button($"{searchResult.Value} ({searchResult.Key})", val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) })) { _selectedItemId = searchResult.Key; _selectedItemName = searchResult.Value; } } GUILayout.EndScrollView(); } else if (_itemIdInput.Length >= 2) { GUILayout.Space(2f); GUILayout.Label(" No items found.", _labelDimStyle, Array.Empty<GUILayoutOption>()); } break; case 1: GUILayout.Label("Category:", _labelBoldStyle, Array.Empty<GUILayoutOption>()); DrawOptionGrid(CategoryNames, ref _selectedCategory, 3); break; case 2: GUILayout.Label("Item Type:", _labelBoldStyle, Array.Empty<GUILayoutOption>()); DrawOptionGrid(ItemTypeNames, ref _selectedItemType, 3); break; case 3: GUILayout.Label("Property:", _labelBoldStyle, Array.Empty<GUILayoutOption>()); DrawOptionGrid(PropertyDisplayNames, ref _selectedProperty, 2); break; } } private void DrawOptionGrid(string[] options, ref int selected, int columns) { int num = (options.Length + columns - 1) / columns; for (int i = 0; i < num; i++) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); for (int j = 0; j < columns; j++) { int num2 = i * columns + j; if (num2 < options.Length) { GUIStyle val = ((num2 == selected) ? _selectorActiveStyle : _selectorStyle); if (GUILayout.Button(options[num2], val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) })) { selected = num2; } } else { GUILayout.FlexibleSpace(); } } GUILayout.EndHorizontal(); GUILayout.Space(2f); } } private string GetRuleDisplayText(SmartChestRule rule) { if (rule.Type == RuleType.ByItemId) { string itemName = SmartChestManager.GetItemName(rule.ItemId); if (!string.IsNullOrEmpty(itemName)) { return $"{itemName} ({rule.ItemId})"; } return $"Item ID: {rule.ItemId}"; } return rule.GetDisplayText(); } private void AddRule() { SmartChestRule smartChestRule = null; switch (_selectedRuleType) { case 0: if (_selectedItemId > 0) { smartChestRule = new SmartChestRule { Type = RuleType.ByItemId, ItemId = _selectedItemId }; _itemIdInput = ""; _lastSearchQuery = ""; _searchResults.Clear(); _selectedItemId = -1; _selectedItemName = ""; } else { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Select an item from the search results first"); } } break; case 1: if (_selectedCategory >= 0 && _selectedCategory < CategoryNames.Length) { smartChestRule = new SmartChestRule { Type = RuleType.ByCategory, CategoryName = CategoryNames[_selectedCategory] }; } break; case 2: if (_selectedItemType >= 0 && _selectedItemType < ItemTypeNames.Length) { smartChestRule = new SmartChestRule { Type = RuleType.ByItemType, ItemTypeName = ItemTypeNames[_selectedItemType] }; } break; case 3: if (_selectedProperty >= 0 && _selectedProperty < PropertyNames.Length) { smartChestRule = new SmartChestRule { Type = RuleType.ByProperty, PropertyName = PropertyNames[_selectedProperty] }; } break; } if (smartChestRule == null) { return; } foreach (SmartChestRule rule in _currentData.Rules) { if (rule.Type == smartChestRule.Type && rule.ItemId == smartChestRule.ItemId && rule.CategoryName == smartChestRule.CategoryName && rule.ItemTypeName == smartChestRule.ItemTypeName && rule.PropertyName == smartChestRule.PropertyName) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Rule already exists, skipping duplicate"); } return; } } _currentData.Rules.Add(smartChestRule); _manager.MarkDirty(); SaveIfDirty(); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Added rule: " + smartChestRule.GetDisplayText())); } } private void SaveIfDirty() { SmartChestSaveSystem saveSystem = Plugin.GetSaveSystem(); if (saveSystem == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[UI] SaveIfDirty: SaveSystem is null!"); } } else { saveSystem.Save(); } } private void InitializeStyles() { if (!_stylesInitialized) { CreateTextures(); CreateStyles(); _stylesInitialized = true; } } private void CreateTextures() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_009a: 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_00c2: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) _solidBg = MakeTex(4, 4, _bgDark); _windowBg = MakeBorderedTex(16, 16, _bgDark, _borderGold, 2); _ruleBg = MakeTex(1, 1, _ruleBoxColor); _btnInactiveTex = MakeTex(1, 1, _btnInactive); _btnHoverTex = MakeTex(1, 1, _btnHover); _btnActiveTex = MakeTex(1, 1, _greenActive); _btnActiveHoverTex = MakeTex(1, 1, _greenHover); _redBtnTex = MakeTex(1, 1, _redDanger); _redBtnHoverTex = MakeTex(1, 1, _redHover); _greenBtnTex = MakeTex(1, 1, _greenActive); _greenBtnHoverTex = MakeTex(1, 1, _greenBright); _closeBtnTex = MakeTex(1, 1, _redDanger); _closeBtnHoverTex = MakeTex(1, 1, _redHover); _fieldBgTex = MakeTex(1, 1, _fieldBg); _separatorTex = MakeTex(1, 1, _borderGold); } private void CreateStyles() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0037: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Expected O, but got Unknown //IL_00d0: Expected O, but got Unknown //IL_00d1: 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_00de: 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_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Expected O, but got Unknown //IL_0111: Expected O, but got Unknown //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Expected O, but got Unknown //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Expected O, but got Unknown //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Expected O, but got Unknown //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Expected O, but got Unknown //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Expected O, but got Unknown //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d1: Expected O, but got Unknown //IL_01d6: Expected O, but got Unknown //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Expected O, but got Unknown //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01fd: 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_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Expected O, but got Unknown //IL_0285: Expected O, but got Unknown //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Expected O, but got Unknown //IL_031b: Expected O, but got Unknown //IL_0326: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) //IL_0338: Expected O, but got Unknown //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_0370: Unknown result type (might be due to invalid IL or missing references) //IL_0386: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03d1: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Expected O, but got Unknown //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Expected O, but got Unknown //IL_0405: Expected O, but got Unknown //IL_0406: Unknown result type (might be due to invalid IL or missing references) //IL_040b: Unknown result type (might be due to invalid IL or missing references) //IL_0413: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Unknown result type (might be due to invalid IL or missing references) //IL_043c: Unknown result type (might be due to invalid IL or missing references) //IL_044d: Unknown result type (might be due to invalid IL or missing references) //IL_0454: Unknown result type (might be due to invalid IL or missing references) //IL_045e: Unknown result type (might be due to invalid IL or missing references) //IL_046f: Unknown result type (might be due to invalid IL or missing references) //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_0480: Unknown result type (might be due to invalid IL or missing references) //IL_0487: Unknown result type (might be due to invalid IL or missing references) //IL_048c: Unknown result type (might be due to invalid IL or missing references) //IL_0496: Expected O, but got Unknown //IL_0496: Unknown result type (might be due to invalid IL or missing references) //IL_049b: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Expected O, but got Unknown //IL_04aa: Expected O, but got Unknown //IL_04b1: Unknown result type (might be due to invalid IL or missing references) //IL_04b6: Unknown result type (might be due to invalid IL or missing references) //IL_04c7: Unknown result type (might be due to invalid IL or missing references) //IL_04ce: Unknown result type (might be due to invalid IL or missing references) //IL_04d8: Unknown result type (might be due to invalid IL or missing references) //IL_04e9: Unknown result type (might be due to invalid IL or missing references) //IL_04f0: Unknown result type (might be due to invalid IL or missing references) //IL_04ff: Expected O, but got Unknown //IL_0500: Unknown result type (might be due to invalid IL or missing references) //IL_0505: Unknown result type (might be due to invalid IL or missing references) //IL_050d: Unknown result type (might be due to invalid IL or missing references) //IL_0514: Unknown result type (might be due to invalid IL or missing references) //IL_0525: Unknown result type (might be due to invalid IL or missing references) //IL_052c: Unknown result type (might be due to invalid IL or missing references) //IL_0536: Unknown result type (might be due to invalid IL or missing references) //IL_0547: Unknown result type (might be due to invalid IL or missing references) //IL_054e: Unknown result type (might be due to invalid IL or missing references) //IL_0558: Unknown result type (might be due to invalid IL or missing references) //IL_0569: Unknown result type (might be due to invalid IL or missing references) //IL_0570: Unknown result type (might be due to invalid IL or missing references) //IL_057a: Unknown result type (might be due to invalid IL or missing references) //IL_0581: Unknown result type (might be due to invalid IL or missing references) //IL_0588: Unknown result type (might be due to invalid IL or missing references) //IL_0592: Expected O, but got Unknown //IL_0597: Expected O, but got Unknown //IL_0598: Unknown result type (might be due to invalid IL or missing references) //IL_059d: Unknown result type (might be due to invalid IL or missing references) //IL_05a5: Unknown result type (might be due to invalid IL or missing references) //IL_05ac: Unknown result type (might be due to invalid IL or missing references) //IL_05bd: Unknown result type (might be due to invalid IL or missing references) //IL_05c4: Unknown result type (might be due to invalid IL or missing references) //IL_05ce: Unknown result type (might be due to invalid IL or missing references) //IL_05df: Unknown result type (might be due to invalid IL or missing references) //IL_05e6: Unknown result type (might be due to invalid IL or missing references) //IL_05f0: Unknown result type (might be due to invalid IL or missing references) //IL_0601: Unknown result type (might be due to invalid IL or missing references) //IL_0608: Unknown result type (might be due to invalid IL or missing references) //IL_0612: Unknown result type (might be due to invalid IL or missing references) //IL_0619: Unknown result type (might be due to invalid IL or missing references) //IL_0620: Unknown result type (might be due to invalid IL or missing references) //IL_062a: Expected O, but got Unknown //IL_062f: Expected O, but got Unknown //IL_0630: Unknown result type (might be due to invalid IL or missing references) //IL_0635: Unknown result type (might be due to invalid IL or missing references) //IL_063d: Unknown result type (might be due to invalid IL or missing references) //IL_0644: Unknown result type (might be due to invalid IL or missing references) //IL_0655: Unknown result type (might be due to invalid IL or missing references) //IL_065c: Unknown result type (might be due to invalid IL or missing references) //IL_0666: Unknown result type (might be due to invalid IL or missing references) //IL_0677: Unknown result type (might be due to invalid IL or missing references) //IL_067e: Unknown result type (might be due to invalid IL or missing references) //IL_0688: Unknown result type (might be due to invalid IL or missing references) //IL_0699: Unknown result type (might be due to invalid IL or missing references) //IL_06a0: Unknown result type (might be due to invalid IL or missing references) //IL_06aa: Unknown result type (might be due to invalid IL or missing references) //IL_06b1: Unknown result type (might be due to invalid IL or missing references) //IL_06b8: Unknown result type (might be due to invalid IL or missing references) //IL_06c2: Expected O, but got Unknown //IL_06c7: Expected O, but got Unknown //IL_06c8: Unknown result type (might be due to invalid IL or missing references) //IL_06cd: Unknown result type (might be due to invalid IL or missing references) //IL_06d5: Unknown result type (might be due to invalid IL or missing references) //IL_06e6: Unknown result type (might be due to invalid IL or missing references) //IL_06ed: Unknown result type (might be due to invalid IL or missing references) //IL_06f7: Unknown result type (might be due to invalid IL or missing references) //IL_0708: Unknown result type (might be due to invalid IL or missing references) //IL_070f: Unknown result type (might be due to invalid IL or missing references) //IL_0719: Unknown result type (might be due to invalid IL or missing references) //IL_072a: Unknown result type (might be due to invalid IL or missing references) //IL_0731: Unknown result type (might be due to invalid IL or missing references) //IL_073b: Unknown result type (might be due to invalid IL or missing references) //IL_0742: Unknown result type (might be due to invalid IL or missing references) //IL_0747: Unknown result type (might be due to invalid IL or missing references) //IL_0751: Expected O, but got Unknown //IL_0751: Unknown result type (might be due to invalid IL or missing references) //IL_0756: Unknown result type (might be due to invalid IL or missing references) //IL_0760: Expected O, but got Unknown //IL_0765: Expected O, but got Unknown //IL_076c: Unknown result type (might be due to invalid IL or missing references) //IL_0771: Unknown result type (might be due to invalid IL or missing references) //IL_0778: Unknown result type (might be due to invalid IL or missing references) //IL_0789: Unknown result type (might be due to invalid IL or missing references) //IL_0790: Unknown result type (might be due to invalid IL or missing references) //IL_079a: Unknown result type (might be due to invalid IL or missing references) //IL_07ab: Unknown result type (might be due to invalid IL or missing references) //IL_07b2: Unknown result type (might be due to invalid IL or missing references) //IL_07c1: Expected O, but got Unknown _windowStyle = new GUIStyle(GUI.skin.window) { padding = new RectOffset(14, 14, 12, 12), border = new RectOffset(2, 2, 2, 2) }; _windowStyle.normal.background = _windowBg; _windowStyle.normal.textColor = _whiteText; _windowStyle.onNormal.background = _windowBg; _windowStyle.onNormal.textColor = _whiteText; GUIStyle val = new GUIStyle { fontSize = 15, fontStyle = (FontStyle)1 }; val.normal.textColor = _whiteText; val.alignment = (TextAnchor)3; val.padding = new RectOffset(2, 2, 2, 2); _titleStyle = val; GUIStyle val2 = new GUIStyle { fontSize = 15, fontStyle = (FontStyle)1 }; val2.normal.textColor = _goldText; val2.alignment = (TextAnchor)4; val2.padding = new RectOffset(4, 4, 2, 2); _sectionHeaderStyle = val2; GUIStyle val3 = new GUIStyle { fontSize = 13 }; val3.normal.textColor = _whiteText; val3.alignment = (TextAnchor)3; val3.padding = new RectOffset(2, 2, 2, 2); val3.wordWrap = true; _labelStyle = val3; _labelBoldStyle = new GUIStyle(_labelStyle) { fontStyle = (FontStyle)1 }; GUIStyle val4 = new GUIStyle(_labelStyle) { fontSize = 12, fontStyle = (FontStyle)2 }; val4.normal.textColor = _dimText; _labelDimStyle = val4; GUIStyle val5 = new GUIStyle(); val5.normal.background = _ruleBg; val5.padding = new RectOffset(10, 8, 6, 6); val5.margin = new RectOffset(4, 4, 2, 2); _ruleBoxStyle = val5; _ruleTextStyle = new GUIStyle(_labelStyle) { fontSize = 13 }; GUIStyle val6 = new GUIStyle { fontSize = 13, fontStyle = (FontStyle)1 }; val6.normal.background = _redBtnTex; val6.normal.textColor = _whiteText; val6.hover.background = _redBtnHoverTex; val6.hover.textColor = _whiteText; val6.active.background = _redBtnHoverTex; val6.active.textColor = _whiteText; val6.alignment = (TextAnchor)4; val6.padding = new RectOffset(2, 2, 2, 2); _removeRuleBtnStyle = val6; GUIStyle val7 = new GUIStyle { fontSize = 14, fontStyle = (FontStyle)1 }; val7.normal.background = _closeBtnTex; val7.normal.textColor = _whiteText; val7.hover.background = _closeBtnHoverTex; val7.hover.textColor = _whiteText; val7.active.background = _closeBtnHoverTex; val7.active.textColor = _whiteText; val7.alignment = (TextAnchor)4; val7.padding = new RectOffset(2, 2, 1, 1); _closeButtonStyle = val7; _toggleStyle = new GUIStyle(GUI.skin.toggle) { fontSize = 13 }; _toggleStyle.normal.textColor = _whiteText; _toggleStyle.onNormal.textColor = _whiteText; _toggleStyle.hover.textColor = _whiteText; _toggleStyle.onHover.textColor = _whiteText; GUIStyle val8 = new GUIStyle { fontSize = 13 }; val8.normal.background = _fieldBgTex; val8.normal.textColor = _whiteText; val8.focused.background = _fieldBgTex; val8.focused.textColor = _whiteText; val8.padding = new RectOffset(8, 8, 5, 5); val8.border = new RectOffset(2, 2, 2, 2); _textFieldStyle = val8; GUIStyle val9 = new GUIStyle { fontSize = 12, fontStyle = (FontStyle)1 }; val9.normal.background = _btnInactiveTex; val9.normal.textColor = _whiteText; val9.hover.background = _btnHoverTex; val9.hover.textColor = _whiteText; val9.active.background = _btnActiveTex; val9.active.textColor = _whiteText; val9.alignment = (TextAnchor)4; val9.padding = new RectOffset(8, 8, 5, 5); val9.margin = new RectOffset(2, 2, 1, 1); _selectorStyle = val9; GUIStyle val10 = new GUIStyle(_selectorStyle); val10.normal.background = _btnActiveTex; val10.normal.textColor = _whiteText; val10.hover.background = _btnActiveHoverTex; val10.hover.textColor = _whiteText; _selectorActiveStyle = val10; GUIStyle val11 = new GUIStyle { fontSize = 14, fontStyle = (FontStyle)1 }; val11.normal.background = _greenBtnTex; val11.normal.textColor = _whiteText; val11.hover.background = _greenBtnHoverTex; val11.hover.textColor = _whiteText; val11.active.background = _greenBtnHoverTex; val11.active.textColor = _whiteText; val11.alignment = (TextAnchor)4; val11.padding = new RectOffset(10, 10, 5, 5); _addButtonStyle = val11; GUIStyle val12 = new GUIStyle { fontSize = 12, fontStyle = (FontStyle)1 }; val12.normal.background = _redBtnTex; val12.normal.textColor = _whiteText; val12.hover.background = _redBtnHoverTex; val12.hover.textColor = _whiteText; val12.active.background = _redBtnHoverTex; val12.active.textColor = _whiteText; val12.alignment = (TextAnchor)4; val12.padding = new RectOffset(10, 10, 4, 4); _dangerButtonStyle = val12; GUIStyle val13 = new GUIStyle { fontSize = 12, fontStyle = (FontStyle)1 }; val13.normal.background = _btnInactiveTex; val13.normal.textColor = _whiteText; val13.hover.background = _btnHoverTex; val13.hover.textColor = _whiteText; val13.active.background = _btnHoverTex; val13.active.textColor = _whiteText; val13.alignment = (TextAnchor)4; val13.padding = new RectOffset(10, 10, 4, 4); _closeBottomButtonStyle = val13; GUIStyle val14 = new GUIStyle { fontSize = 12 }; val14.normal.background = _ruleBg; val14.normal.textColor = _whiteText; val14.hover.background = _btnHoverTex; val14.hover.textColor = _whiteText; val14.active.background = _btnActiveTex; val14.active.textColor = _whiteText; val14.alignment = (TextAnchor)3; val14.padding = new RectOffset(8, 8, 3, 3); val14.margin = new RectOffset(0, 0, 1, 1); _searchResultStyle = val14; GUIStyle val15 = new GUIStyle(_searchResultStyle) { fontStyle = (FontStyle)1 }; val15.normal.background = _btnActiveTex; val15.normal.textColor = _whiteText; val15.hover.background = _btnActiveHoverTex; val15.hover.textColor = _whiteText; _searchResultSelectedStyle = val15; } private Texture2D MakeTex(int width, int height, Color color) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < array.Length; i++) { array[i] = color; } Texture2D val = new Texture2D(width, height); val.SetPixels(array); val.Apply(); return val; } private Texture2D MakeBorderedTex(int width, int height, Color fillColor, Color borderColor, int borderWidth) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(width, height); Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { bool flag = j < borderWidth || j >= width - borderWidth || i < borderWidth || i >= height - borderWidth; array[i * width + j] = (flag ? borderColor : fillColor); } } val.SetPixels(array); val.Apply(); return val; } private void OnDestroy() { if ((Object)(object)_solidBg != (Object)null) { Object.Destroy((Object)(object)_solidBg); } if ((Object)(object)_windowBg != (Object)null) { Object.Destroy((Object)(object)_windowBg); } if ((Object)(object)_ruleBg != (Object)null) { Object.Destroy((Object)(object)_ruleBg); } if ((Object)(object)_btnInactiveTex != (Object)null) { Object.Destroy((Object)(object)_btnInactiveTex); } if ((Object)(object)_btnHoverTex != (Object)null) { Object.Destroy((Object)(object)_btnHoverTex); } if ((Object)(object)_btnActiveTex != (Object)null) { Object.Destroy((Object)(object)_btnActiveTex); } if ((Object)(object)_btnActiveHoverTex != (Object)null) { Object.Destroy((Object)(object)_btnActiveHoverTex); } if ((Object)(object)_redBtnTex != (Object)null) { Object.Destroy((Object)(object)_redBtnTex); } if ((Object)(object)_redBtnHoverTex != (Object)null) { Object.Destroy((Object)(object)_redBtnHoverTex); } if ((Object)(object)_greenBtnTex != (Object)null) { Object.Destroy((Object)(object)_greenBtnTex); } if ((Object)(object)_greenBtnHoverTex != (Object)null) { Object.Destroy((Object)(object)_greenBtnHoverTex); } if ((Object)(object)_closeBtnTex != (Object)null) { Object.Destroy((Object)(object)_closeBtnTex); } if ((Object)(object)_closeBtnHoverTex != (Object)null) { Object.Destroy((Object)(object)_closeBtnHoverTex); } if ((Object)(object)_fieldBgTex != (Object)null) { Object.Destroy((Object)(object)_fieldBgTex); } if ((Object)(object)_separatorTex != (Object)null) { Object.Destroy((Object)(object)_separatorTex); } } } } namespace SenpaisChest.Integration { public class MuseumTodoIntegration { private readonly Dictionary<int, string> _museumTodoIds = new Dictionary<int, string>(); private int _scanCounter; private const int SCAN_INTERVAL = 3; public MuseumTodoIntegration() { DonationManager donationManager = Plugin.GetDonationManager(); if (donationManager != null) { donationManager.OnDonationsChanged += OnDonationsChanged; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[MuseumTodoIntegration] Initialized - museum item todos will sync with S.M.U.T. and Todo"); } } public void OnScanComplete() { _scanCounter++; if (_scanCounter < 3) { return; } _scanCounter = 0; try { DonationManager donationManager = Plugin.GetDonationManager(); TodoManager todoManager = Plugin.GetTodoManager(); if (donationManager == null || !donationManager.IsLoaded || todoManager == null) { return; } HashSet<Inventory> inventories = ChestManager.inventories; if (inventories == null || inventories.Count == 0) { return; } foreach (Inventory item in inventories) { if ((Object)(object)item == (Object)null) { continue; } List<SlotItemData> items = item.Items; if (items == null) { continue; } int num = Math.Min(item.maxSlots, items.Count); for (int i = 0; i < num; i++) { SlotItemData val = items[i]; if (val.id > 0 && val.amount > 0) { CheckMuseumItem(val.id, donationManager, todoManager); } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[MuseumTodoIntegration] Error during scan: " + ex.Message)); } } } private void CheckMuseumItem(int gameItemId, DonationManager donationManager, TodoManager todoManager) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown if (_museumTodoIds.ContainsKey(gameItemId)) { return; } MuseumItem val = MuseumContent.FindByGameItemId(gameItemId); if (val != null && !donationManager.HasDonatedByGameId(gameItemId)) { string text = "Donate " + val.Name + " to the museum"; string text2 = "Found in a chest - this item is still needed for the museum!"; TodoItem val2 = new TodoItem(text, text2, (TodoPriority)2, (TodoCategory)8); todoManager.AddTodo(val2); _museumTodoIds[gameItemId] = val2.Id; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[MuseumTodoIntegration] Created todo for museum item: {val.Name} (ID: {gameItemId})"); } } } private void OnDonationsChanged() { try { DonationManager donationManager = Plugin.GetDonationManager(); TodoManager todoManager = Plugin.GetTodoManager(); if (donationManager == null || todoManager == null) { return; } List<int> list = new List<int>(); foreach (KeyValuePair<int, string> museumTodoId in _museumTodoIds) { if (donationManager.HasDonatedByGameId(museumTodoId.Key)) { list.Add(museumTodoId.Key); } } foreach (int item in list) { if (!_museumTodoIds.TryGetValue(item, out var todoId)) { continue; } TodoItem val = todoManager.GetAllTodos().FirstOrDefault((Func<TodoItem, bool>)((TodoItem t) => t.Id == todoId)); if (val != null && !val.IsCompleted) { todoManager.ToggleComplete(todoId); MuseumItem val2 = MuseumContent.FindByGameItemId(item); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[MuseumTodoIntegration] Completed todo for donated item: " + (((val2 != null) ? val2.Name : null) ?? item.ToString()))); } } _museumTodoIds.Remove(item); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[MuseumTodoIntegration] Error processing donation change: " + ex.Message)); } } } public void Reset() { _museumTodoIds.Clear(); _scanCounter = 0; } } } namespace SenpaisChest.Data { public enum RuleType { ByItemId, ByCategory, ByItemType, ByProperty } [Serializable] public class SmartChestRule { public RuleType Type; public int ItemId; public string CategoryName = ""; public string ItemTypeName = ""; public string PropertyName = ""; public string GetDisplayText() { return Type switch { RuleType.ByItemId => $"Item ID: {ItemId}", RuleType.ByCategory => "Category: " + CategoryName, RuleType.ByItemType => "Type: " + ItemTypeName, RuleType.ByProperty => "Property: " + PropertyName, _ => "Unknown Rule", }; } } [Serializable] public class SmartChestData { public string ChestId = ""; public string ChestName = ""; public bool IsEnabled = true; public List<SmartChestRule> Rules = new List<SmartChestRule>(); public SmartChestData() { } public SmartChestData(string chestId, string chestName) { ChestId = chestId; ChestName = chestName; IsEnabled = true; Rules = new List<SmartChestRule>(); } } [Serializable] public class SmartChestSaveData { public string CharacterName = ""; public List<SmartChestData> Chests = new List<SmartChestData>(); public SmartChestSaveData() { } public SmartChestSaveData(string characterName) { CharacterName = characterName; Chests = new List<SmartChestData>(); } } public class SmartChestManager { private readonly Dictionary<string, SmartChestData> _smartChests = new Dictionary<string, SmartChestData>(); private string _characterName = ""; private bool _isDirty; private readonly Dictionary<int, string> _categoryCache = new Dictionary<int, string>(); private static FieldInfo _chestDataField; private static object _itemInfoDbInstance; private static FieldInfo _allItemSellInfosField; private static object _notificationStackInstance; private static MethodInfo _sendNotificationMethod; private static MethodInfo _databaseGetDataMethod; private static bool _reflectionInitialized; private static bool _museumReflectionInitialized; private static bool _museumModAvailable; private static MethodInfo _getDonationManagerMethod; private static MethodInfo _hasDonatedByGameIdMethod; private static MethodInfo _findByGameItemIdMethod; private static PropertyInfo _isLoadedProperty; public bool IsDirty => _isDirty; public void LoadData(SmartChestSaveData data) { _smartChests.Clear(); _categoryCache.Clear(); if (data == null) { return; } _characterName = data.CharacterName; foreach (SmartChestData chest in data.Chests) { _smartChests[chest.ChestId] = chest; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Loaded {_smartChests.Count} smart chest configurations"); } } public SmartChestSaveData GetSaveData() { return new SmartChestSaveData { CharacterName = _characterName, Chests = _smartChests.Values.ToList() }; } public void SetCharacterName(string name) { _characterName = name; } public void MarkClean() { _isDirty = false; } public void MarkDirty() { _isDirty = true; } public SmartChestData GetOrCreateSmartChest(string chestId, string chestName) { if (_smartChests.TryGetValue(chestId, out var value)) { value.ChestName = chestName; return value; } SmartChestData smartChestData = new SmartChestData(chestId, chestName); _smartChests[chestId] = smartChestData; _isDirty = true; return smartChestData; } public SmartChestData GetSmartChest(string chestId) { _smartChests.TryGetValue(chestId, out var value); return value; } public bool IsSmartChest(string chestId) { if (_smartChests.ContainsKey(chestId) && _smartChests[chestId].IsEnabled) { return _smartChests[chestId].Rules.Count > 0; } return false; } public void RemoveSmartChest(string chestId) { if (_smartChests.Remove(chestId)) { _isDirty = true; } } public static string GetChestId(Chest chest) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)chest == (Object)null) { return null; } if ((Object)(object)chest == (Object)null) { return null; } Vector3Int position = ((Decoration)chest).Position; return $"{((Vector3Int)(ref position)).x}_{((Vector3Int)(ref position)).y}_{((Vector3Int)(ref position)).z}"; } private static void InitReflection() { if (_reflectionInitialized) { return; } try { _chestDataField = typeof(Chest).GetField("data", BindingFlags.Instance | BindingFlags.NonPublic); Type type = AccessTools.TypeByName("Wish.Database") ?? AccessTools.TypeByName("Database"); if (type != null) { MethodInfo methodInfo = type.GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "GetData" && m.IsGenericMethod && m.GetParameters().Length == 3); if (methodInfo != null) { _databaseGetDataMethod = methodInfo.MakeGenericMethod(typeof(ItemData)); } } _reflectionInitialized = true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Failed to initialize reflection: " + ex.Message)); } } } 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 { return null; } } private static ItemSellInfo GetItemSellInfo(int itemId) { try { if (_itemInfoDbInstance != null) { object itemInfoDbInstance = _itemInfoDbInstance; Object val = (Object)((itemInfoDbInstance is Object) ? itemInfoDbInstance : null); if (val == null || !(val == (Object)null)) { goto IL_004f; } } _itemInfoDbInstance = GetSingletonInstance("Wish.ItemInfoDatabase"); if (_itemInfoDbInstance != null) { _allItemSellInfosField = _itemInfoDbInstance.GetType().GetField("allItemSellInfos", BindingFlags.Instance | BindingFlags.Public); } goto IL_004f; IL_004f: if (_itemInfoDbInstance == null || _allItemSellInfosField == null) { return null; } if (_allItemSellInfosField.GetValue(_itemInfoDbInstance) is Dictionary<int, ItemSellInfo> dictionary && dictionary.ContainsKey(itemId)) { return dictionary[itemId]; } } catch { } return null; } private static void SendNotification(string message) { try { if (_notificationStackInstance != null) { object notificationStackInstance = _notificationStackInstance; Object val = (Object)((notificationStackInstance is Object) ? notificationStackInstance : null); if (val == null || !(val == (Object)null)) { goto IL_0094; } } _notificationStackInstance = GetSingletonInstance("Wish.NotificationStack"); if (_notificationStackInstance != null) { _sendNotificationMethod = _notificationStackInstance.GetType().GetMethod("SendNotification", new Type[5] { typeof(string), typeof(int), typeof(int), typeof(bool), typeof(bool) }); } goto IL_0094; IL_0094: _sendNotificationMethod?.Invoke(_notificationStackInstance, new object[5] { message, 0, 0, true, false }); } catch { } } private bool IsChestInUse(Chest chest) { try { InitReflection(); if (_chestDataField != null) { object? value = _chestDataField.GetValue(chest); object? obj = ((value is ChestData) ? value : null); return obj != null && ((ChestData)obj).inUse; } } catch { } return false; } public static List<KeyValuePair<int, string>> SearchItems(string query, int maxResults = 20) { List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>(); if (string.IsNullOrEmpty(query) || query.Length < 2) { return list; } try { if (_itemInfoDbInstance != null) { object itemInfoDbInstance = _itemInfoDbInstance; Object val = (Object)((itemInfoDbInstance is Object) ? itemInfoDbInstance : null); if (val == null || !(val == (Object)null)) { goto IL_0069; } } _itemInfoDbInstance = GetSingletonInstance("Wish.ItemInfoDatabase"); if (_itemInfoDbInstance != null) { _allItemSellInfosField = _itemInfoDbInstance.GetType().GetField("allItemSellInfos", BindingFlags.Instance | BindingFlags.Public); } goto IL_0069; IL_0069: if (_itemInfoDbInstance == null || _allItemSellInfosField == null) { return list; } if (!(_allItemSellInfosField.GetValue(_itemInfoDbInstance) is Dictionary<int, ItemSellInfo> dictionary)) { return list; } string text = query.ToLowerInvariant(); int result; bool flag = int.TryParse(query, 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 dictionary) { if (item2.Value != null && !string.IsNullOrEmpty(item2.Value.name)) { KeyValuePair<int, string> item = new KeyValuePair<int, string>(item2.Key, item2.Value.name); string text2 = item2.Value.name.ToLowerInvariant(); if (flag && item2.Key == result) { list2.Add(item); } else if (text2 == text) { list2.Add(item); } else if (text2.StartsWith(text)) { list3.Add(item); } else if (text2.Contains(text)) { list4.Add(item); } else if (flag && item2.Key.ToString().Contains(query)) { 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 = Plugin.Log; if (log != null) { log.LogWarning((object)("Error searching items: " + ex.Message)); } } return list; } public static string GetItemName(int itemId) { return GetItemSellInfo(itemId)?.name; } public void ExecuteScan(int maxItemsPerScan, bool enableNotifications) { if (_smartChests.Count == 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)"[Scan] No smart chests configured, skipping scan"); } return; } InitReflection(); HashSet<Inventory> inventories = ChestManager.inventories; Dictionary<Inventory, Chest> associatedChests = ChestManager.associatedChests; if (inventories == null || inventories.Count == 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)"[Scan] No inventories loaded, skipping scan"); } return; } if (associatedChests == null || associatedChests.Count == 0) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogDebug((object)"[Scan] No associated chests found, skipping scan"); } return; } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[Scan] Starting scan: {_smartChests.Count} smart chest(s), {associatedChests.Count} associated chest(s)"); } int num = 0; HashSet<Chest> hashSet = new HashSet<Chest>(); Dictionary<string, KeyValuePair<Inventory, Chest>> dictionary = new Dictionary<string, KeyValuePair<Inventory, Chest>>(); foreach (KeyValuePair<Inventory, Chest> item in associatedChests) { string chestId = GetChestId(item.Value); if (chestId != null && !dictionary.ContainsKey(chestId)) { dictionary[chestId] = new KeyValuePair<Inventory, Chest>(item.Key, item.Value); } } ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogDebug((object)$"[Scan] Built chest lookup: {dictionary.Count} unique chests"); } foreach (KeyValuePair<string, SmartChestData> smartChest in _smartChests) { if (num >= maxItemsPerScan) { break; } SmartChestData value = smartChest.Value; if (!value.IsEnabled || value.Rules.Count == 0) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogDebug((object)$"[Scan] Skipping '{value.ChestName}' (enabled={value.IsEnabled}, rules={value.Rules.Count})"); } continue; } if (!dictionary.TryGetValue(value.ChestId, out var value2)) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogWarning((object)("[Scan] Smart chest '" + value.ChestName + "' (id=" + value.ChestId + ") not found in loaded chests")); } continue; } Inventory key = value2.Key; Chest value3 = value2.Value; if (IsChestInUse(value3)) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogDebug((object)("[Scan] Target chest '" + value.ChestName + "' is in use, skipping")); } continue; } ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)$"[Scan] Scanning for items matching '{value.ChestName}' ({value.Rules.Count} rules)"); } int num2 = 0; foreach (KeyValuePair<string, KeyValuePair<Inventory, Chest>> item2 in dictionary) { if (num >= maxItemsPerScan) { break; } if (item2.Key == value.ChestId) { continue; } Inventory key2 = item2.Value.Key; Chest value4 = item2.Value.Value; if (!IsChestInUse(value4) && !IsSmartChest(item2.Key)) { num2++; int num3 = TransferMatchingItems(key2, value4, key, value3, value.Rules, maxItemsPerScan - num); if (num3 > 0) { num += num3; hashSet.Add(value4); hashSet.Add(value3); } } } ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogDebug((object)$"[Scan] Scanned {num2} source chests for '{value.ChestName}'"); } } int num5 = default(int); int num6 = default(int); foreach (KeyValuePair<string, SmartChestData> smartChest2 in _smartChests) { if (num >= maxItemsPerScan) { break; } SmartChestData value5 = smartChest2.Value; if (!value5.IsEnabled || value5.Rules.Count == 0 || !dictionary.TryGetValue(value5.ChestId, out var value6)) { continue; } Inventory key3 = value6.Key; Chest value7 = value6.Value; if (IsChestInUse(value7)) { continue; } List<SlotItemData> items = key3.Items; if (items == null) { continue; } int num4 = Mathf.Min(key3.maxSlots, items.Count) - 1; while (num4 >= 0 && num < maxItemsPerScan) { SlotItemData val = items[num4]; if (val.id > 0 && val.amount > 0 && !MatchesAnyRule(val.id, value5.Rules)) { string text = GetItemSellInfo(val.id)?.name ?? $"Item {val.id}"; bool flag = false; foreach (KeyValuePair<string, SmartChestData> smartChest3 in _smartChests) { if (!(smartChest3.Key == value5.ChestId) && smartChest3.Value.IsEnabled && smartChest3.Value.Rules.Count != 0 && MatchesAnyRule(val.id, smartChest3.Value.Rules) && dictionary.TryGetValue(smartChest3.Value.ChestId, out var value8) && !IsChestInUse(value8.Value) && value8.Key.CanAcceptItem(val.item, val.amount, ref num5) && num5 > 0) { ManualLogSource log11 = Plugin.Log; if (log11 != null) { log11.LogInfo((object)$"[Scan] Ejecting {text} x{num5} from '{value5.ChestName}' to smart chest '{smartChest3.Value.ChestName}'"); } TransferItemData(key3, num4, value8.Key, val.item, num5); hashSet.Add(value7); hashSet.Add(value8.Value); num++; flag = true; break; } } if (!flag) { foreach (KeyValuePair<string, KeyValuePair<Inventory, Chest>> item3 in dictionary) { if (!IsSmartChest(item3.Key) && !IsChestInUse(item3.Value.Value) && item3.Value.Key.CanAcceptItem(val.item, val.amount, ref num6) && num6 > 0) { ManualLogSource log12 = Plugin.Log; if (log12 != null) { log12.LogInfo((object)$"[Scan] Ejecting {text} x{num6} from '{value5.ChestName}' to normal chest"); } TransferItemData(key3, num4, item3.Value.Key, val.item, num6); hashSet.Add(value7); hashSet.Add(item3.Value.Value); num++; break; } } } } num4--; } } foreach (Chest item4 in hashSet) { try { ((Decoration)item4).SaveMeta(); ((Decoration)item4).SendNewMeta(((Decoration)item4).meta); } catch (Exception ex) { ManualLogSource log13 = Plugin.Log; if (log13 != null) { log13.LogError((object)("Error saving chest meta: " + ex.Message)); } } } if (num > 0) { ManualLogSource log14 = Plugin.Log; if (log14 != null) { log14.LogInfo((object)$"[Scan] Complete: sorted {num} item stack(s)"); } if (enableNotifications) { SendNotification("Senpai's Chest: Sorted"); } } else { ManualLogSource log15 = Plugin.Log; if (log15 != null) { log15.LogInfo((object)"[Scan] Complete: no items to sort"); } } } private int TransferMatchingItems(Inventory sourceInv, Chest sourceChest, Inventory targetInv, Chest targetChest, List<SmartChestRule> rules, int maxItems) { int num = 0; List<SlotItemData> items = sourceInv.Items; if (items == null) { return 0; } int num2 = Mathf.Min(sourceInv.maxSlots, items.Count) - 1; int num3 = default(int); while (num2 >= 0 && num < maxItems) { SlotItemData val = items[num2]; if (val.id > 0 && val.amount > 0 && MatchesAnyRule(val.id, rules)) { if (!targetInv.CanAcceptItem(val.item, val.amount, ref num3)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)$"[Scan] Target cannot accept item {val.id} (amount={val.amount})"); } } else if (num3 > 0) { string arg = GetItemSellInfo(val.id)?.name ?? $"Item {val.id}"; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[Scan] Moving {arg} x{num3}"); } TransferItemData(sourceInv, num2, targetInv, val.item, num3); num++; } } num2--; } return num; } private void TransferItemData(Inventory sourceInv, int sourceSlot, Inventory targetInv, Item item, int amount) { targetInv.AddItem(item, amount, false); sourceInv.RemoveItemAt(sourceSlot, amount); } private bool MatchesAnyRule(int itemId, List<SmartChestRule> rules) { foreach (SmartChestRule rule in rules) { if (MatchesRule(itemId, rule)) { return true; } } return false; } private bool MatchesRule(int itemId, SmartChestRule rule) { switch (rule.Type) { case RuleType.ByItemId: return itemId == rule.ItemId; case RuleType.ByItemType: { ItemSellInfo itemSellInfo2 = GetItemSellInfo(itemId); if (itemSellInfo2 == null) { return false; } return ((object)(ItemType)(ref itemSellInfo2.itemType)).ToString() == rule.ItemTypeName; } case RuleType.ByProperty: { if (rule.PropertyName == "isNotDonated") { return IsMuseumItemNotDonated(itemId); } ItemSellInfo itemSellInfo = GetItemSellInfo(itemId); if (itemSellInfo == null) { return false; } return rule.PropertyName switch { "isGem" => itemSellInfo.isGem, "isForageable" => itemSellInfo.isForageable, "isAnimalProduct" => itemSellInfo.isAnimalProduct, "isMeal" => itemSellInfo.isMeal, "isFruit" => itemSellInfo.isFruit, "isArtisanryItem" => itemSellInfo.isArtisanryItem, "isPotion" => itemSellInfo.isPotion, _ => false, }; } case RuleType.ByCategory: return MatchesCategory(itemId, rule.CategoryName); default: return false; } } private bool MatchesCategory(int itemId, string categoryName) { if (_categoryCache.TryGetValue(itemId, out var value)) { return value == categoryName; } bool matched = false; try { if (_databaseGetDataMethod != null) { Action<ItemData> action = delegate(ItemData itemData) { if ((Object)(object)itemData != (Object)null) { _categoryCache[itemId] = ((object)(ItemCategory)(ref itemData.category)).ToString(); matched = ((object)(ItemCategory)(ref itemData.category)).ToString() == categoryName; } }; _databaseGetDataMethod.Invoke(null, new object[3] { itemId, action, null }); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"Failed to lookup category for item {itemId}: {ex.Message}"); } } return matched; } private static bool IsMuseumItemNotDonated(int itemId) { if (!_museumReflectionInitialized) { _museumReflectionInitialized = true; try { Type type = AccessTools.TypeByName("SunHavenMuseumUtilityTracker.Plugin"); if (type != null) { _getDonationManagerMethod = type.GetMethod("GetDonationManager", BindingFlags.Static | BindingFlags.Public); Type type2 = AccessTools.TypeByName("SunHavenMuseumUtilityTracker.Data.MuseumContent"); if (type2 != null) { _findByGameItemIdMethod = type2.GetMethod("FindByGameItemId", BindingFlags.Static | BindingFlags.Public); } _museumModAvailable = _getDonationManagerMethod != null && _findByGameItemIdMethod != null;