using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SunHavenMuseumUtilityTracker.Data; using SunHavenMuseumUtilityTracker.Patches; using SunHavenMuseumUtilityTracker.UI; using SunhavenMods.Shared; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; using Wish; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("SunHavenMuseumUtilityTracker")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+5c08b5aa5d0be9c4b93df77f697dc55d5ac97088")] [assembly: AssemblyProduct("SunHavenMuseumUtilityTracker")] [assembly: AssemblyTitle("SunHavenMuseumUtilityTracker")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SunhavenMods.Shared { public static class ConfigFileHelper { public static ConfigFile CreateNamedConfig(string pluginGuid, string configFileName, Action logWarning = null) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, configFileName); string text2 = Path.Combine(Paths.ConfigPath, pluginGuid + ".cfg"); try { if (!File.Exists(text) && File.Exists(text2)) { File.Copy(text2, text); } } catch (Exception ex) { logWarning?.Invoke("[Config] Migration to " + configFileName + " failed: " + ex.Message); } return new ConfigFile(text, true); } public static bool ReplacePluginConfig(BaseUnityPlugin plugin, ConfigFile newConfig, Action logWarning = null) { if ((Object)(object)plugin == (Object)null || newConfig == null) { return false; } try { Type typeFromHandle = typeof(BaseUnityPlugin); PropertyInfo property = typeFromHandle.GetProperty("Config", BindingFlags.Instance | BindingFlags.Public); if (property != null && property.CanWrite) { property.SetValue(plugin, newConfig, null); return true; } FieldInfo field = typeFromHandle.GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(plugin, newConfig); return true; } FieldInfo[] fields = typeFromHandle.GetFields(BindingFlags.Instance | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(ConfigFile)) { fieldInfo.SetValue(plugin, newConfig); return true; } } } catch (Exception ex) { logWarning?.Invoke("[Config] ReplacePluginConfig failed: " + ex.Message); } return false; } } public static class VersionChecker { public class VersionCheckResult { public bool Success { get; set; } public bool UpdateAvailable { get; set; } public string CurrentVersion { get; set; } public string LatestVersion { get; set; } public string ModName { get; set; } public string NexusUrl { get; set; } public string Changelog { get; set; } public string ErrorMessage { get; set; } } public class ModHealthSnapshot { public string PluginGuid { get; set; } public DateTime LastCheckUtc { get; set; } public int ExceptionCount { get; set; } public string LastError { get; set; } } private class VersionCheckRunner : MonoBehaviour { private ManualLogSource _pluginLog; public void StartCheck(string pluginGuid, string currentVersion, ManualLogSource pluginLog, Action onComplete) { _pluginLog = pluginLog; ((MonoBehaviour)this).StartCoroutine(CheckVersionCoroutine(pluginGuid, currentVersion, onComplete)); } private void LogInfo(string message) { ManualLogSource pluginLog = _pluginLog; if (pluginLog != null) { pluginLog.LogInfo((object)("[VersionChecker] " + message)); } } private void LogWarningMsg(string message) { ManualLogSource pluginLog = _pluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[VersionChecker] " + message)); } } private void LogErrorMsg(string message) { ManualLogSource pluginLog = _pluginLog; if (pluginLog != null) { pluginLog.LogError((object)("[VersionChecker] " + message)); } } private IEnumerator CheckVersionCoroutine(string pluginGuid, string currentVersion, Action onComplete) { VersionCheckResult result = new VersionCheckResult { CurrentVersion = currentVersion }; UnityWebRequest www = UnityWebRequest.Get("https://azraelgodking.github.io/SunhavenMod/versions.json"); try { www.timeout = 10; yield return www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { result.Success = false; result.ErrorMessage = "Network error: " + www.error; RecordHealthError(pluginGuid, result.ErrorMessage); LogWarningMsg(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } try { string text = www.downloadHandler.text; Match match = GetModPattern(pluginGuid).Match(text); if (!match.Success) { result.Success = false; result.ErrorMessage = "Mod '" + pluginGuid + "' not found in versions.json"; RecordHealthError(pluginGuid, result.ErrorMessage); LogWarningMsg(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } string value = match.Groups[1].Value; result.LatestVersion = ExtractJsonString(value, "version"); result.ModName = ExtractJsonString(value, "name"); result.NexusUrl = ExtractJsonString(value, "nexus"); result.Changelog = ExtractJsonString(value, "changelog"); if (string.IsNullOrEmpty(result.LatestVersion)) { result.Success = false; result.ErrorMessage = "Could not parse version from response"; RecordHealthError(pluginGuid, result.ErrorMessage); LogWarningMsg(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } result.Success = true; result.UpdateAvailable = CompareVersions(currentVersion, result.LatestVersion) < 0; if (result.UpdateAvailable) { LogInfo("Update available for " + result.ModName + ": " + currentVersion + " -> " + result.LatestVersion); } else { LogInfo(result.ModName + " is up to date (v" + currentVersion + ")"); } } catch (Exception ex) { result.Success = false; result.ErrorMessage = "Parse error: " + ex.Message; RecordHealthError(pluginGuid, result.ErrorMessage); LogErrorMsg(result.ErrorMessage); } } finally { ((IDisposable)www)?.Dispose(); } onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); } private string ExtractJsonString(string json, string key) { Match match = ExtractFieldRegex.Match(json); while (match.Success) { if (string.Equals(match.Groups["key"].Value, key, StringComparison.Ordinal)) { return match.Groups["value"].Value; } match = match.NextMatch(); } return null; } } private const string VersionsUrl = "https://azraelgodking.github.io/SunhavenMod/versions.json"; private static readonly Dictionary HealthByPluginGuid = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly object HealthLock = new object(); private static readonly Dictionary ModPatternCache = new Dictionary(StringComparer.Ordinal); private static readonly object ModPatternCacheLock = new object(); private static readonly Regex ExtractFieldRegex = new Regex("\"(?[^\"]+)\"\\s*:\\s*(?:\"(?[^\"]*)\"|null)", RegexOptions.Compiled); public static void CheckForUpdate(string pluginGuid, string currentVersion, ManualLogSource logger = null, Action onComplete = null) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) TouchHealth(pluginGuid); VersionCheckRunner versionCheckRunner = new GameObject("VersionChecker").AddComponent(); Object.DontDestroyOnLoad((Object)(object)((Component)versionCheckRunner).gameObject); SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(((Component)versionCheckRunner).gameObject); versionCheckRunner.StartCheck(pluginGuid, currentVersion, logger, onComplete); } public static ModHealthSnapshot GetHealthSnapshot(string pluginGuid) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return null; } lock (HealthLock) { if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value)) { return null; } return new ModHealthSnapshot { PluginGuid = value.PluginGuid, LastCheckUtc = value.LastCheckUtc, ExceptionCount = value.ExceptionCount, LastError = value.LastError }; } } public static int CompareVersions(string v1, string v2) { if (string.IsNullOrEmpty(v1) || string.IsNullOrEmpty(v2)) { return 0; } v1 = v1.TrimStart('v', 'V'); v2 = v2.TrimStart('v', 'V'); int num = v1.IndexOfAny(new char[2] { '-', '+' }); if (num >= 0) { v1 = v1.Substring(0, num); } int num2 = v2.IndexOfAny(new char[2] { '-', '+' }); if (num2 >= 0) { v2 = v2.Substring(0, num2); } string[] array = v1.Split(new char[1] { '.' }); string[] array2 = v2.Split(new char[1] { '.' }); int num3 = Math.Max(array.Length, array2.Length); for (int i = 0; i < num3; i++) { int result; int num4 = ((i < array.Length && int.TryParse(array[i], out result)) ? result : 0); int result2; int num5 = ((i < array2.Length && int.TryParse(array2[i], out result2)) ? result2 : 0); if (num4 < num5) { return -1; } if (num4 > num5) { return 1; } } return 0; } private static void TouchHealth(string pluginGuid) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return; } lock (HealthLock) { if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value)) { value = new ModHealthSnapshot { PluginGuid = pluginGuid }; HealthByPluginGuid[pluginGuid] = value; } value.LastCheckUtc = DateTime.UtcNow; } } private static void RecordHealthError(string pluginGuid, string errorMessage) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return; } lock (HealthLock) { if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value)) { value = new ModHealthSnapshot { PluginGuid = pluginGuid }; HealthByPluginGuid[pluginGuid] = value; } value.LastCheckUtc = DateTime.UtcNow; value.ExceptionCount++; value.LastError = errorMessage; } } private static Regex GetModPattern(string pluginGuid) { lock (ModPatternCacheLock) { if (!ModPatternCache.TryGetValue(pluginGuid, out Regex value)) { value = new Regex("\"" + Regex.Escape(pluginGuid) + "\"\\s*:\\s*\\{([^}]+)\\}", RegexOptions.Compiled | RegexOptions.Singleline); ModPatternCache[pluginGuid] = value; } return value; } } } public static class VersionCheckerExtensions { public static void NotifyUpdateAvailable(this VersionChecker.VersionCheckResult result, ManualLogSource logger = null) { if (!result.UpdateAvailable) { return; } string text = result.ModName + " update available: v" + result.LatestVersion; try { Type type = ReflectionHelper.FindWishType("NotificationStack"); if (type != null) { Type type2 = ReflectionHelper.FindType("SingletonBehaviour`1", "Wish"); if (type2 != null) { object obj = type2.MakeGenericType(type).GetProperty("Instance")?.GetValue(null); if (obj != null) { MethodInfo method = type.GetMethod("SendNotification", new Type[5] { typeof(string), typeof(int), typeof(int), typeof(bool), typeof(bool) }); if (method != null) { method.Invoke(obj, new object[5] { text, 0, 1, false, true }); return; } } } } } catch (Exception ex) { if (logger != null) { logger.LogWarning((object)("Failed to send native notification: " + ex.Message)); } } if (logger != null) { logger.LogWarning((object)("[UPDATE AVAILABLE] " + text)); } if (!string.IsNullOrEmpty(result.NexusUrl) && logger != null) { logger.LogWarning((object)("Download at: " + result.NexusUrl)); } } } public static class SceneRootSurvivor { private static readonly object Lock = new object(); private static readonly List NoKillSubstrings = new List(); private static Harmony _harmony; public static void TryRegisterPersistentRunnerGameObject(GameObject go) { if (!((Object)(object)go == (Object)null)) { TryAddNoKillListSubstring(((Object)go).name); } } public static void TryAddNoKillListSubstring(string nameSubstring) { if (string.IsNullOrEmpty(nameSubstring)) { return; } lock (Lock) { bool flag = false; for (int i = 0; i < NoKillSubstrings.Count; i++) { if (string.Equals(NoKillSubstrings[i], nameSubstring, StringComparison.OrdinalIgnoreCase)) { flag = true; break; } } if (!flag) { NoKillSubstrings.Add(nameSubstring); } } EnsurePatched(); } private static void EnsurePatched() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00a3: Expected O, but got Unknown if (_harmony != null) { return; } lock (Lock) { if (_harmony == null) { MethodInfo methodInfo = AccessTools.Method(typeof(Scene), "GetRootGameObjects", Type.EmptyTypes, (Type[])null); if (!(methodInfo == null)) { string text = typeof(SceneRootSurvivor).Assembly.GetName().Name ?? "Unknown"; Harmony val = new Harmony("SunhavenMods.SceneRootSurvivor." + text); val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SceneRootSurvivor), "OnGetRootGameObjectsPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony = val; } } } } private static void OnGetRootGameObjectsPostfix(ref GameObject[] __result) { if (__result == null || __result.Length == 0) { return; } List list; lock (Lock) { if (NoKillSubstrings.Count == 0) { return; } list = new List(NoKillSubstrings); } List list2 = new List(__result); for (int i = 0; i < list.Count; i++) { string noKill = list[i]; list2.RemoveAll((GameObject a) => (Object)(object)a != (Object)null && ((Object)a).name.IndexOf(noKill, StringComparison.OrdinalIgnoreCase) >= 0); } __result = list2.ToArray(); } } public static class ReflectionHelper { public static readonly BindingFlags AllBindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; public static Type FindType(string typeName, params string[] namespaces) { string typeName2 = typeName; Type type = AccessTools.TypeByName(typeName2); if (type != null) { return type; } for (int i = 0; i < namespaces.Length; i++) { type = AccessTools.TypeByName(namespaces[i] + "." + typeName2); if (type != null) { return type; } } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == typeName2 || t.FullName == typeName2); if (type != null) { return type; } } catch (ReflectionTypeLoadException) { } } return null; } public static Type FindWishType(string typeName) { return FindType(typeName, "Wish"); } public static object GetStaticValue(Type type, string memberName) { if (type == null) { return null; } try { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.GetMethod != null && property.GetIndexParameters().Length == 0) { return property.GetValue(null); } } catch (AmbiguousMatchException) { return null; } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { return field.GetValue(null); } return null; } public static object GetSingletonInstance(Type type) { if (type == null) { return null; } string[] array = new string[5] { "Instance", "instance", "_instance", "Singleton", "singleton" }; foreach (string memberName in array) { object staticValue = GetStaticValue(type, memberName); if (staticValue != null) { return staticValue; } } return null; } public static object GetInstanceValue(object instance, string memberName) { if (instance == null) { return null; } Type type = instance.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.GetMethod != null) { return property.GetValue(instance); } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { return field.GetValue(instance); } type = type.BaseType; } return null; } public static bool SetInstanceValue(object instance, string memberName, object value) { if (instance == null) { return false; } Type type = instance.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.SetMethod != null) { property.SetValue(instance, value); return true; } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { field.SetValue(instance, value); return true; } type = type.BaseType; } return false; } public static object InvokeMethod(object instance, string methodName, params object[] args) { if (instance == null) { return null; } Type type = instance.GetType(); Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes; MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null); if (methodInfo == null) { methodInfo = type.GetMethod(methodName, AllBindingFlags); } if (methodInfo == null) { return null; } return methodInfo.Invoke(instance, args); } public static object InvokeStaticMethod(Type type, string methodName, params object[] args) { if (type == null) { return null; } Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes; MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null); if (methodInfo == null) { methodInfo = type.GetMethod(methodName, AllBindingFlags); } if (methodInfo == null) { return null; } return methodInfo.Invoke(null, args); } public static FieldInfo[] GetAllFields(Type type) { if (type == null) { return Array.Empty(); } FieldInfo[] fields = type.GetFields(AllBindingFlags); IEnumerable second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty(); } else { IEnumerable 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[] properties = type.GetProperties(AllBindingFlags); IEnumerable second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty(); } else { IEnumerable 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(object instance, string memberName, T defaultValue = default(T)) { try { object instanceValue = GetInstanceValue(instance, memberName); if (instanceValue is T result) { return result; } if (instanceValue != null && typeof(T).IsAssignableFrom(instanceValue.GetType())) { return (T)instanceValue; } return defaultValue; } catch { return defaultValue; } } } public static class IconCache { private struct CachedIcon { public Texture2D Texture; public bool OwnsTexture; } private static readonly Dictionary _iconCache = new Dictionary(); private const int MaxCacheSize = 200; private static readonly HashSet _loadingItems = new HashSet(); private static readonly HashSet _failedItems = new HashSet(); private static readonly Dictionary _currencyToItemId = new Dictionary(); private static Texture2D _fallbackTexture; private static ManualLogSource _log; private static Type _databaseType; private static Type _itemDataType; private static MethodInfo _getDataMethod; private static bool _reflectionInitialized; private static bool _initialized; private static bool _iconsLoaded; private static int[] _pendingPreloadItemIds; public static void Initialize(ManualLogSource log, int[] preloadItemIds = null) { _log = log; if (_initialized) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogDebug((object)"[IconCache] Already initialized"); } return; } _initialized = true; ManualLogSource log3 = _log; if (log3 != null) { log3.LogInfo((object)"[IconCache] Initializing icon cache..."); } _fallbackTexture = CreateFallbackTexture(); ManualLogSource log4 = _log; if (log4 != null) { log4.LogInfo((object)"[IconCache] Created fallback texture"); } if (preloadItemIds != null && preloadItemIds.Length != 0) { _pendingPreloadItemIds = (int[])preloadItemIds.Clone(); ManualLogSource log5 = _log; if (log5 != null) { log5.LogInfo((object)$"[IconCache] Preload: {_pendingPreloadItemIds.Length} item ID(s) will queue with LoadAllIcons"); } } } public static void RegisterCurrency(string currencyId, int itemId) { _currencyToItemId[currencyId] = itemId; } public static void LoadAllIcons() { if (_iconsLoaded) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)"[IconCache] Icons already loaded, skipping"); } return; } if (!InitializeReflection()) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogError((object)"[IconCache] Failed to initialize reflection, cannot load icons"); } _iconsLoaded = true; return; } foreach (KeyValuePair item in _currencyToItemId) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogDebug((object)$"[IconCache] Queuing load for: {item.Key} (ItemID: {item.Value})"); } LoadIcon(item.Value); } if (_pendingPreloadItemIds != null) { int[] pendingPreloadItemIds = _pendingPreloadItemIds; foreach (int num in pendingPreloadItemIds) { if (num > 0) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogDebug((object)$"[IconCache] Queuing preload item ID: {num}"); } LoadIcon(num); } } } _iconsLoaded = true; ManualLogSource log5 = _log; if (log5 != null) { log5.LogInfo((object)$"[IconCache] Queued {_currencyToItemId.Count} currency icon(s); preload queue processed."); } } public static Texture2D GetIconForCurrency(string currencyId) { if (_currencyToItemId.TryGetValue(currencyId, out var value)) { return GetIcon(value); } return GetFallbackTexture(); } public static Texture2D GetIcon(int itemId) { if (itemId <= 0) { return GetFallbackTexture(); } if (_iconCache.TryGetValue(itemId, out var value)) { return value.Texture; } if (!_loadingItems.Contains(itemId) && !_failedItems.Contains(itemId)) { LoadIcon(itemId); } return GetFallbackTexture(); } private static Texture2D GetFallbackTexture() { if ((Object)(object)_fallbackTexture == (Object)null) { _fallbackTexture = CreateFallbackTexture(); } return _fallbackTexture; } public static bool IsIconLoaded(int itemId) { return _iconCache.ContainsKey(itemId); } public static bool IsIconLoaded(string currencyId) { if (_currencyToItemId.TryGetValue(currencyId, out var value)) { return IsIconLoaded(value); } return false; } public static int GetItemIdForCurrency(string currencyId) { if (!_currencyToItemId.TryGetValue(currencyId, out var value)) { return -1; } return value; } private static bool InitializeReflection() { if (_reflectionInitialized) { if (_databaseType != null && _itemDataType != null) { return _getDataMethod != null; } return false; } _reflectionInitialized = true; try { string[] array = new string[4] { "Database", "Wish.Database", "PSS.Database", "SunHaven.Database" }; for (int i = 0; i < array.Length; i++) { _databaseType = AccessTools.TypeByName(array[i]); if (_databaseType != null) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)("[IconCache] Found Database type: " + _databaseType.FullName)); } break; } } MethodInfo[] methods; if (_databaseType == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type[] types = assembly.GetTypes(); foreach (Type type in types) { if (!(type.Name == "Database") || type.IsNested) { continue; } methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "GetData" && methodInfo.IsGenericMethod) { _databaseType = type; ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)("[IconCache] Found Database type: " + type.FullName)); } break; } } if (_databaseType != null) { break; } } if (_databaseType != null) { break; } } catch (Exception ex) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogDebug((object)("[IconCache] Skipping assembly " + assembly.GetName().Name + ": " + ex.Message)); } } } } if (_databaseType == null) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogError((object)"[IconCache] Could not find Database type"); } return false; } _itemDataType = AccessTools.TypeByName("Wish.ItemData"); if (_itemDataType == null) { ManualLogSource log5 = _log; if (log5 != null) { log5.LogError((object)"[IconCache] Could not find Wish.ItemData type"); } return false; } methods = _databaseType.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name == "GetData") || !methodInfo2.IsGenericMethod) { continue; } ParameterInfo[] parameters = methodInfo2.GetParameters(); if (methodInfo2.GetGenericArguments().Length == 1 && parameters.Length == 3 && parameters[0].ParameterType == typeof(int)) { _getDataMethod = methodInfo2.MakeGenericMethod(_itemDataType); ManualLogSource log6 = _log; if (log6 != null) { log6.LogInfo((object)"[IconCache] Found Database.GetData method"); } break; } } if (_getDataMethod == null) { ManualLogSource log7 = _log; if (log7 != null) { log7.LogError((object)"[IconCache] Could not find Database.GetData method"); } return false; } return true; } catch (Exception ex2) { ManualLogSource log8 = _log; if (log8 != null) { log8.LogError((object)("[IconCache] Error initializing reflection: " + ex2.Message)); } return false; } } private static void LoadIcon(int itemId) { if (itemId <= 0 || _loadingItems.Contains(itemId) || _iconCache.ContainsKey(itemId)) { return; } _loadingItems.Add(itemId); try { if (!InitializeReflection() || _getDataMethod == null) { _failedItems.Add(itemId); _loadingItems.Remove(itemId); return; } Type delegateType = typeof(Action<>).MakeGenericType(_itemDataType); ParameterExpression parameterExpression = Expression.Parameter(_itemDataType, "itemData"); ConstantExpression arg = Expression.Constant(itemId); MethodCallExpression body = Expression.Call(typeof(IconCache).GetMethod("OnIconLoadedInternal", BindingFlags.Static | BindingFlags.NonPublic), arg, Expression.Convert(parameterExpression, typeof(object))); Delegate @delegate = Expression.Lambda(delegateType, body, parameterExpression).Compile(); Action action = Expression.Lambda(Expression.Call(typeof(IconCache).GetMethod("OnIconLoadFailed", BindingFlags.Static | BindingFlags.NonPublic), arg), Array.Empty()).Compile(); _getDataMethod.Invoke(null, new object[3] { itemId, @delegate, action }); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)$"[IconCache] Error loading icon {itemId}: {ex.Message}"); } _failedItems.Add(itemId); _loadingItems.Remove(itemId); } } private static void OnIconLoadedInternal(int itemId, object itemData) { _loadingItems.Remove(itemId); if (itemData == null) { _failedItems.Add(itemId); return; } try { Type type = itemData.GetType(); object obj = null; BindingFlags[] array = new BindingFlags[4] { BindingFlags.Instance | BindingFlags.Public, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy, BindingFlags.Instance | BindingFlags.NonPublic, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy }; BindingFlags[] array2 = array; foreach (BindingFlags bindingAttr in array2) { PropertyInfo property = type.GetProperty("icon", bindingAttr); if (property != null) { obj = property.GetValue(itemData); break; } } if (obj == null) { array2 = array; foreach (BindingFlags bindingAttr2 in array2) { FieldInfo field = type.GetField("icon", bindingAttr2); if (field != null) { obj = field.GetValue(itemData); break; } } } if (obj == null) { Type type2 = type; while (type2 != null && obj == null) { PropertyInfo[] properties = type2.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.Name.Equals("icon", StringComparison.OrdinalIgnoreCase)) { obj = propertyInfo.GetValue(itemData); break; } } if (obj == null) { FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.Name.Equals("icon", StringComparison.OrdinalIgnoreCase)) { obj = fieldInfo.GetValue(itemData); break; } } } type2 = type2.BaseType; } } Sprite val = (Sprite)((obj is Sprite) ? obj : null); if (val != null) { CacheSprite(itemId, val); } else { _failedItems.Add(itemId); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)$"[IconCache] Error processing icon {itemId}: {ex.Message}"); } _failedItems.Add(itemId); } } private static void OnIconLoadFailed(int itemId) { _loadingItems.Remove(itemId); _failedItems.Add(itemId); } private static void CacheSprite(int itemId, Sprite sprite) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sprite == (Object)null || (Object)(object)sprite.texture == (Object)null) { _failedItems.Add(itemId); return; } try { Rect rect = sprite.rect; Texture2D val; bool ownsTexture; if (((Rect)(ref rect)).width == (float)((Texture)sprite.texture).width) { rect = sprite.rect; if (((Rect)(ref rect)).height == (float)((Texture)sprite.texture).height) { val = sprite.texture; ownsTexture = false; goto IL_0077; } } val = ExtractSpriteTexture(sprite); ownsTexture = (Object)(object)val != (Object)null; goto IL_0077; IL_0077: if ((Object)(object)val != (Object)null) { _iconCache[itemId] = new CachedIcon { Texture = val, OwnsTexture = ownsTexture }; if (_iconCache.Count <= 200) { return; } int num = -1; int num2 = -1; foreach (int key in _iconCache.Keys) { if (num2 < 0) { num2 = key; } if (!_loadingItems.Contains(key) && !_failedItems.Contains(key)) { num = key; break; } } if (num < 0) { num = num2; } if (num >= 0 && _iconCache.TryGetValue(num, out var value)) { if (value.OwnsTexture && (Object)(object)value.Texture != (Object)null) { Object.Destroy((Object)(object)value.Texture); } _iconCache.Remove(num); } } else { _failedItems.Add(itemId); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)$"[IconCache] Error caching sprite {itemId}: {ex.Message}"); } _failedItems.Add(itemId); } } private static Texture2D ExtractSpriteTexture(Sprite sprite) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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_0066: Expected O, but got Unknown try { Rect rect = sprite.rect; int num = (int)((Rect)(ref rect)).width; int num2 = (int)((Rect)(ref rect)).height; if (!((Texture)sprite.texture).isReadable) { return CopyTextureViaRenderTexture(sprite); } Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false); Color[] pixels = sprite.texture.GetPixels((int)((Rect)(ref rect)).x, (int)((Rect)(ref rect)).y, num, num2); val.SetPixels(pixels); val.Apply(); return val; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("[IconCache] Error extracting sprite texture: " + ex.Message)); } return null; } } private static Texture2D CopyTextureViaRenderTexture(Sprite sprite) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown RenderTexture val = null; RenderTexture active = RenderTexture.active; try { Rect rect = sprite.rect; int num = (int)((Rect)(ref rect)).width; int num2 = (int)((Rect)(ref rect)).height; val = RenderTexture.GetTemporary(((Texture)sprite.texture).width, ((Texture)sprite.texture).height, 0, (RenderTextureFormat)0); Graphics.Blit((Texture)(object)sprite.texture, val); RenderTexture.active = val; Texture2D val2 = new Texture2D(num, num2, (TextureFormat)4, false); val2.ReadPixels(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, (float)num, (float)num2), 0, 0); val2.Apply(); return val2; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("[IconCache] Error copying texture via RenderTexture: " + ex.Message)); } return null; } finally { RenderTexture.active = active; if ((Object)(object)val != (Object)null) { RenderTexture.ReleaseTemporary(val); } } } private static Texture2D CreateFallbackTexture() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) int num = 32; Texture2D val = new Texture2D(num, num); Color val2 = default(Color); ((Color)(ref val2))..ctor(0.3f, 0.3f, 0.4f, 0.8f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.5f, 0.5f, 0.6f, 1f); for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { if (j == 0 || j == num - 1 || i == 0 || i == num - 1) { val.SetPixel(j, i, val3); } else { val.SetPixel(j, i, val2); } } } val.Apply(); return val; } public static void Clear() { foreach (KeyValuePair item in _iconCache) { if (item.Value.OwnsTexture && (Object)(object)item.Value.Texture != (Object)null) { Object.Destroy((Object)(object)item.Value.Texture); } } _iconCache.Clear(); _loadingItems.Clear(); _failedItems.Clear(); _initialized = false; _iconsLoaded = false; _pendingPreloadItemIds = null; } public static (int loaded, int loading, int failed) GetStats() { return (_iconCache.Count, _loadingItems.Count, _failedItems.Count); } public static void LogStatus() { (int, int, int) stats = GetStats(); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[IconCache] Loaded: {stats.Item1}, Loading: {stats.Item2}, Failed: {stats.Item3}"); } } } public static class TextInputFocusGuard { private const float DefaultPollIntervalSeconds = 0.25f; private static float _nextPollTime = -1f; private static bool _cachedDefer; private static bool _tmpTypeLookupDone; private static Type _tmpInputFieldType; private static bool _qcLookupDone; private static Type _qcType; private static PropertyInfo _qcInstanceProp; private static PropertyInfo _qcIsActiveProp; private static FieldInfo _qcIsActiveField; public static bool ShouldDeferModHotkeys(ManualLogSource debugLog = null, float pollIntervalSeconds = 0.25f) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextPollTime) { return _cachedDefer; } _nextPollTime = realtimeSinceStartup + Mathf.Max(0.05f, pollIntervalSeconds); bool flag = false; try { if (GUIUtility.keyboardControl != 0) { flag = true; } if (!flag) { EventSystem current = EventSystem.current; GameObject val = ((current != null) ? current.currentSelectedGameObject : null); if ((Object)(object)val != (Object)null) { if ((Object)(object)val.GetComponent() != (Object)null) { flag = true; } else if (TryGetTmpInputField(val)) { flag = true; } } } if (!flag && IsQuantumConsoleActive(debugLog)) { flag = true; } } catch (Exception ex) { if (debugLog != null) { debugLog.LogDebug((object)("[TextInputFocusGuard] " + ex.Message)); } } _cachedDefer = flag; return flag; } private static bool TryGetTmpInputField(GameObject go) { if (!_tmpTypeLookupDone) { _tmpTypeLookupDone = true; _tmpInputFieldType = AccessTools.TypeByName("TMPro.TMP_InputField"); } if (_tmpInputFieldType == null) { return false; } return (Object)(object)go.GetComponent(_tmpInputFieldType) != (Object)null; } private static bool IsQuantumConsoleActive(ManualLogSource debugLog) { try { if (!_qcLookupDone) { _qcLookupDone = true; _qcType = AccessTools.TypeByName("QFSW.QC.QuantumConsole"); if (_qcType != null) { _qcInstanceProp = AccessTools.Property(_qcType, "Instance"); _qcIsActiveProp = AccessTools.Property(_qcType, "IsActive"); _qcIsActiveField = AccessTools.Field(_qcType, "isActive") ?? AccessTools.Field(_qcType, "_isActive"); } } if (_qcType == null) { return false; } object obj = _qcInstanceProp?.GetValue(null); if (obj == null) { return false; } if (_qcIsActiveProp != null && _qcIsActiveProp.PropertyType == typeof(bool)) { return (bool)_qcIsActiveProp.GetValue(obj); } if (_qcIsActiveField != null && _qcIsActiveField.FieldType == typeof(bool)) { return (bool)_qcIsActiveField.GetValue(obj); } } catch (Exception ex) { if (debugLog != null) { debugLog.LogDebug((object)("[TextInputFocusGuard] Quantum Console focus check failed: " + ex.Message)); } } return false; } } } namespace SunHavenMuseumUtilityTracker { [BepInPlugin("com.azraelgodking.sunhavenmuseumutilitytracker", "Sun Haven Museum Utility Tracker", "2.4.2")] public class Plugin : BaseUnityPlugin { private static DonationManager _staticDonationManager; private static DonationSaveSystem _staticSaveSystem; private static MuseumTrackerUI _staticTrackerUI; private static GameObject _persistentRunner; private static PersistentRunner _persistentRunnerComponent; private DonationManager _donationManager; private DonationSaveSystem _saveSystem; private MuseumTrackerUI _trackerUI; private Harmony _harmony; private bool _wasInMenuScene = true; private static bool _applicationQuitting; private ConfigEntry _toggleKey; private ConfigEntry _requireCtrl; private ConfigEntry _altToggleKey; private ConfigEntry _checkForUpdates; private ConfigEntry _uiScale; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static ConfigFile ConfigFile { get; private set; } public static KeyCode StaticToggleKey { get; private set; } public static bool StaticRequireCtrl { get; private set; } public static KeyCode StaticAltToggleKey { get; private set; } public static float StaticUIScale { get; private set; } = 1f; public DonationManager DonationManager => _donationManager; private void Awake() { //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigFile = CreateNamedConfig(); ConfigFileHelper.ReplacePluginConfig((BaseUnityPlugin)(object)this, ConfigFile, (Action)Log.LogWarning); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Sun Haven Museum Utility Tracker v2.4.2 loading..."); BindConfiguration(); CreatePersistentRunner(); _donationManager = new DonationManager(); _staticDonationManager = _donationManager; _saveSystem = new DonationSaveSystem(_donationManager); _staticSaveSystem = _saveSystem; CreateUIComponents(); ApplyPatches(); SceneManager.sceneLoaded += OnSceneLoaded; if (_checkForUpdates.Value) { VersionChecker.CheckForUpdate("com.azraelgodking.sunhavenmuseumutilitytracker", "2.4.2", Log, delegate(VersionChecker.VersionCheckResult result) { result.NotifyUpdateAvailable(Log); }); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"Sun Haven Museum Utility Tracker loaded successfully!"); ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("Press {0}{1} or {2} to open the tracker", _requireCtrl.Value ? "Ctrl+" : "", _toggleKey.Value, _altToggleKey.Value)); } private void BindConfiguration() { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) _toggleKey = ConfigFile.Bind("Hotkeys", "ToggleKey", (KeyCode)99, "Key to toggle the Museum Tracker window"); _requireCtrl = ConfigFile.Bind("Hotkeys", "RequireCtrl", true, "Require Ctrl to be held when pressing the toggle key"); _altToggleKey = ConfigFile.Bind("Hotkeys", "AltToggleKey", (KeyCode)288, "Alternative key to toggle the Museum Tracker (no modifier required). Useful for Steam Deck."); _checkForUpdates = ConfigFile.Bind("Updates", "CheckForUpdates", true, "Check for mod updates on startup"); _uiScale = ConfigFile.Bind("Display", "UIScale", 1f, new ConfigDescription("Scale factor for the tracker window (1.0 = default, 1.5 = 50% larger)", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 2.5f), Array.Empty())); StaticToggleKey = _toggleKey.Value; StaticRequireCtrl = _requireCtrl.Value; StaticAltToggleKey = _altToggleKey.Value; StaticUIScale = Mathf.Clamp(_uiScale.Value, 0.5f, 2.5f); _toggleKey.SettingChanged += delegate { //IL_0006: 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) StaticToggleKey = _toggleKey.Value; _trackerUI?.SetToggleKey(_toggleKey.Value, _requireCtrl.Value); }; _requireCtrl.SettingChanged += delegate { //IL_0021: Unknown result type (might be due to invalid IL or missing references) StaticRequireCtrl = _requireCtrl.Value; _trackerUI?.SetToggleKey(_toggleKey.Value, _requireCtrl.Value); }; _altToggleKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) StaticAltToggleKey = _altToggleKey.Value; }; _uiScale.SettingChanged += delegate { StaticUIScale = Mathf.Clamp(_uiScale.Value, 0.5f, 2.5f); _trackerUI?.SetScale(StaticUIScale); }; } private static ConfigFile CreateNamedConfig() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "SunHavenMuseumUtilityTracker.cfg"); string text2 = Path.Combine(Paths.ConfigPath, "com.azraelgodking.sunhavenmuseumutilitytracker.cfg"); try { if (!File.Exists(text) && File.Exists(text2)) { File.Copy(text2, text); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[Config] Migration to SunHavenMuseumUtilityTracker.cfg failed: " + ex.Message)); } } return new ConfigFile(text, true); } private void CreatePersistentRunner() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (!((Object)(object)_persistentRunner != (Object)null) || !((Object)(object)_persistentRunnerComponent != (Object)null)) { _persistentRunner = new GameObject("MuseumTracker_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _persistentRunnerComponent = _persistentRunner.AddComponent(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[PersistentRunner] Created"); } } private void CreateUIComponents() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("MuseumTracker_UI"); Object.DontDestroyOnLoad((Object)(object)val); _trackerUI = val.AddComponent(); _trackerUI.Initialize(_donationManager); _trackerUI.SetScale(StaticUIScale); _trackerUI.SetToggleKey(_toggleKey.Value, _requireCtrl.Value); _staticTrackerUI = _trackerUI; val.AddComponent(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"UI components created"); } 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown //IL_00e1: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_persistentRunner == (Object)null || (Object)(object)_persistentRunnerComponent == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[EnsureUI] Recreating PersistentRunner..."); } _persistentRunner = new GameObject("MuseumTracker_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _persistentRunnerComponent = _persistentRunner.AddComponent(); ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"[EnsureUI] PersistentRunner recreated"); } } if ((Object)(object)_staticTrackerUI == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)"[EnsureUI] Recreating TrackerUI..."); } GameObject val = new GameObject("MuseumTracker_UI"); Object.DontDestroyOnLoad((Object)val); _staticTrackerUI = val.AddComponent(); _staticTrackerUI.Initialize(_staticDonationManager); _staticTrackerUI.SetScale(StaticUIScale); _staticTrackerUI.SetToggleKey(StaticToggleKey, StaticRequireCtrl); ManualLogSource log4 = Log; if (log4 != null) { log4.LogInfo((object)"[EnsureUI] TrackerUI recreated"); } } } catch (Exception ex) { ManualLogSource log5 = Log; if (log5 != null) { log5.LogError((object)("[EnsureUI] Error recreating UI: " + ex.Message)); } } } private void ApplyPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown try { _harmony = new Harmony("com.azraelgodking.sunhavenmuseumutilitytracker"); MethodInfo methodInfo = AccessTools.Method(typeof(Player), "InitializeAsOwner", (Type[])null, (Type[])null); if (methodInfo != null) { MethodInfo methodInfo2 = AccessTools.Method(typeof(PlayerPatches), "OnPlayerInitialized", (Type[])null, (Type[])null); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched Player.InitializeAsOwner"); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)"Could not find Player.InitializeAsOwner"); } MethodInfo methodInfo3 = AccessTools.Method(typeof(GameSave), "LoadCharacter", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo3 != null) { MethodInfo methodInfo4 = AccessTools.Method(typeof(GameSavePatches), "OnLoadCharacter", (Type[])null, (Type[])null); _harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Patched GameSave.LoadCharacter"); } HungryMonsterPatches.ApplyPatches(_harmony); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Harmony patches applied"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Error applying patches: " + ex.Message)); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("[SceneChange] Scene loaded: '" + ((Scene)(ref scene)).name + "'")); string text = ((Scene)(ref scene)).name.ToLowerInvariant(); if (text.Contains("menu") || text.Contains("title") || text.Contains("mainmenu")) { if (!_wasInMenuScene) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Menu scene transition detected: " + ((Scene)(ref scene)).name)); PlayerPatches.SaveAndReset(); } _wasInMenuScene = true; } else { _wasInMenuScene = false; } } private void OnApplicationQuit() { _applicationQuitting = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"Application quitting, saving data..."); _saveSystem?.ForceSave(); } private void OnDestroy() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) SceneManager.sceneLoaded -= OnSceneLoaded; Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name ?? string.Empty; string text2 = text.ToLowerInvariant(); if (_applicationQuitting || !Application.isPlaying || text2.Contains("menu") || text2.Contains("title")) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Plugin OnDestroy during expected teardown (scene: " + text + ")")); } else { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[CRITICAL] Plugin OnDestroy outside expected teardown (scene: " + text + ")")); } _saveSystem?.ForceSave(); } public static DonationManager GetDonationManager() { return _staticDonationManager; } public static MuseumTrackerUI GetTrackerUI() { return _staticTrackerUI; } public static void SaveData() { _staticSaveSystem?.ForceSave(); } public static void LoadDataForPlayer(string playerName) { DonationData data = _staticSaveSystem?.Load(playerName); _staticDonationManager?.LoadForCharacter(playerName, data); } public static void ToggleUI() { _staticTrackerUI?.Toggle(); } internal static void TickAutoSave() { if (PlayerPatches.IsDataLoaded) { _staticSaveSystem?.CheckAutoSave(); } } } public class PersistentRunner : MonoBehaviour { private string _lastScene = ""; private float _worldSyncTimer; private const float WORLD_SYNC_INTERVAL = 5f; private void Update() { Plugin.TickAutoSave(); CheckHotkeys(); CheckSceneChange(); SyncWorldProgress(); } private void CheckHotkeys() { //IL_002c: 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) if (!TextInputFocusGuard.ShouldDeferModHotkeys(Plugin.Log)) { bool flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); bool keyDown = Input.GetKeyDown(Plugin.StaticToggleKey); bool keyDown2 = Input.GetKeyDown(Plugin.StaticAltToggleKey); if (keyDown && flag == Plugin.StaticRequireCtrl) { Plugin.ToggleUI(); } else if (keyDown2) { Plugin.ToggleUI(); } } } private void CheckSceneChange() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (name != _lastScene) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[PersistentRunner] Scene changed: '" + _lastScene + "' -> '" + name + "'")); } _lastScene = name; } } private void SyncWorldProgress() { _worldSyncTimer += Time.unscaledDeltaTime; if (_worldSyncTimer < 5f) { return; } _worldSyncTimer = 0f; if (!PlayerPatches.IsDataLoaded) { return; } DonationManager donationManager = Plugin.GetDonationManager(); if (donationManager == null || !donationManager.IsLoaded) { return; } int item = donationManager.GetOverallStats().donated; MuseumPatches.SyncWithGameProgress(verboseLogging: false); int item2 = donationManager.GetOverallStats().donated; if (item2 > item) { Plugin.SaveData(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[PersistentRunner] Background world sync marked {item2 - item} items"); } } } private void OnDestroy() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string text = (((Scene)(ref activeScene)).name ?? string.Empty).ToLowerInvariant(); if (!Application.isPlaying || text.Contains("menu") || text.Contains("title")) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PersistentRunner] OnDestroy during app quit/menu unload (expected)."); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[PersistentRunner] OnDestroy outside quit/menu (unexpected)."); } } } } public static class PluginInfo { public const string PLUGIN_GUID = "com.azraelgodking.sunhavenmuseumutilitytracker"; public const string PLUGIN_NAME = "Sun Haven Museum Utility Tracker"; public const string PLUGIN_VERSION = "2.4.2"; } } namespace SunHavenMuseumUtilityTracker.UI { public class DebugMode : MonoBehaviour { private bool _debugEnabled; private void Update() { if (Input.GetKeyDown((KeyCode)291)) { _debugEnabled = !_debugEnabled; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[DebugMode] Debug mode: " + (_debugEnabled ? "ENABLED" : "DISABLED"))); } if (_debugEnabled) { MuseumPatches.DiscoverProgressKeyPatterns(); MuseumPatches.LogAllMappings(); } } if (_debugEnabled && Input.GetKeyDown((KeyCode)292)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[DebugMode] Force syncing with game progress..."); } MuseumPatches.SyncWithGameProgress(); } } } public class MuseumTrackerUI : MonoBehaviour { private const float BASE_WINDOW_WIDTH = 640f; private const float BASE_WINDOW_HEIGHT = 700f; private const float BASE_ICON_SIZE = 34f; private const float BASE_HEADER_HEIGHT = 80f; private float _scale = 1f; private DonationManager _donationManager; private bool _isVisible; private Rect _windowRect; private Vector2 _scrollPosition; private int _windowId; private KeyCode _toggleKey = (KeyCode)109; private bool _requireCtrl = true; private int _selectedSectionIndex; private HashSet _expandedBundles = new HashSet(); private bool _showOnlyNeeded; private string _searchQuery = ""; private float _openAnimation; private string _syncStatusMessage = ""; private float _syncStatusTimer; private Dictionary _cachedGameDonationCounts = new Dictionary(); private Dictionary _cachedGameCompleteStatus = new Dictionary(); private Coroutine _cacheRefreshCoroutine; private bool _isCacheRefreshing; private bool _stylesInitialized; private GUIStyle _windowStyle; private GUIStyle _titleStyle; private GUIStyle _subtitleStyle; private GUIStyle _sectionTabStyle; private GUIStyle _sectionTabActiveStyle; private GUIStyle _bundleHeaderStyle; private GUIStyle _bundleHeaderCompleteStyle; private GUIStyle _itemRowStyle; private GUIStyle _itemNameStyle; private GUIStyle _buttonStyle; private GUIStyle _closeButtonStyle; private GUIStyle _labelStyle; private GUIStyle _checkmarkStyle; private GUIStyle _toggleStyle; private GUIStyle _searchStyle; private GUIStyle _statsStyle; private GUIStyle _footerStyle; private GUIStyle _syncButtonStyle; private GUIStyle _syncStatusStyle; private GUIStyle _headerBoxStyle; private GUIStyle _headerCountStyle; private GUIStyle _searchLabelStyle; private GUIStyle _scrollStyle; private GUIStyle _emptyStyle; private GUIStyle _progressLabelStyle; private GUIStyle _itemNameDonatedStyle; private GUIStyle _rarityLabelStyleCached; private GUIStyle _checkStyleCached; private GUIStyle _neededStyleCached; private Texture2D _windowBackground; private Texture2D _headerBackground; private Texture2D _tabNormal; private Texture2D _tabHover; private Texture2D _tabActive; private Texture2D _bundleNormal; private Texture2D _bundleHover; private Texture2D _bundleComplete; private Texture2D _itemEven; private Texture2D _itemOdd; private Texture2D _itemDonated; private Texture2D _buttonNormal; private Texture2D _buttonHover; private Texture2D _progressBg; private Texture2D _searchBg; private Texture2D _dividerTex; private Texture2D _itemPlaceholderIcon; private readonly Color _parchmentLight = new Color(0.96f, 0.93f, 0.86f, 0.98f); private readonly Color _parchment = new Color(0.92f, 0.87f, 0.78f, 0.97f); private readonly Color _parchmentDark = new Color(0.85f, 0.78f, 0.65f, 0.95f); private readonly Color _parchmentDarker = new Color(0.75f, 0.67f, 0.52f, 0.92f); private readonly Color _woodDark = new Color(0.35f, 0.25f, 0.15f); private readonly Color _woodMedium = new Color(0.5f, 0.38f, 0.25f); private readonly Color _woodLight = new Color(0.65f, 0.52f, 0.38f); private readonly Color _leather = new Color(0.55f, 0.4f, 0.28f); private readonly Color _goldRich = new Color(0.85f, 0.68f, 0.2f); private readonly Color _goldBright = new Color(0.95f, 0.8f, 0.3f); private readonly Color _goldPale = new Color(1f, 0.92f, 0.7f); private readonly Color _forestGreen = new Color(0.3f, 0.55f, 0.3f); private readonly Color _skyBlue = new Color(0.45f, 0.65f, 0.85f); private readonly Color _coralWarm = new Color(0.85f, 0.5f, 0.4f); private readonly Color _textDark = new Color(0.18f, 0.14f, 0.1f); private readonly Color _textMedium = new Color(0.32f, 0.28f, 0.22f); private readonly Color _textLight = new Color(0.48f, 0.42f, 0.34f); private readonly Color _textMuted = new Color(0.58f, 0.52f, 0.44f); private readonly Color _successGreen = new Color(0.35f, 0.65f, 0.35f); private readonly Color _successGreenLight = new Color(0.5f, 0.75f, 0.45f); private readonly Color _neededOrange = new Color(0.85f, 0.55f, 0.25f); private readonly Color _borderDark = new Color(0.45f, 0.35f, 0.22f, 0.8f); private readonly Color _borderGold = new Color(0.75f, 0.6f, 0.25f, 0.7f); private readonly Dictionary _rarityColors = new Dictionary { { ItemRarity.Common, new Color(0.5f, 0.45f, 0.38f) }, { ItemRarity.Uncommon, new Color(0.35f, 0.6f, 0.35f) }, { ItemRarity.Rare, new Color(0.35f, 0.55f, 0.8f) }, { ItemRarity.Epic, new Color(0.65f, 0.4f, 0.75f) }, { ItemRarity.Legendary, new Color(0.9f, 0.7f, 0.2f) } }; private readonly Dictionary _sectionThemes = new Dictionary { { "hall_of_gems", (new Color(0.6f, 0.5f, 0.75f), new Color(0.75f, 0.65f, 0.9f)) }, { "hall_of_culture", (new Color(0.7f, 0.55f, 0.35f), new Color(0.85f, 0.7f, 0.45f)) }, { "aquarium", (new Color(0.4f, 0.6f, 0.75f), new Color(0.55f, 0.75f, 0.9f)) } }; public bool IsVisible => _isVisible; private float WindowWidth => 640f * _scale; private float WindowHeight => 700f * _scale; private float HeaderHeight => 80f * _scale; private float IconSize => 34f * _scale; private int ScaledFont(int baseSize) { return Mathf.Max(8, Mathf.RoundToInt((float)baseSize * _scale)); } private float Scaled(float value) { return value * _scale; } private int ScaledInt(float value) { return Mathf.RoundToInt(value * _scale); } public void SetScale(float scale) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) _scale = Mathf.Clamp(scale, 0.5f, 2.5f); _stylesInitialized = false; float windowWidth = WindowWidth; float windowHeight = WindowHeight; _windowRect = new Rect(((float)Screen.width - windowWidth) / 2f, ((float)Screen.height - windowHeight) / 2f, windowWidth, windowHeight); } public void Initialize(DonationManager donationManager) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) _donationManager = donationManager; _isVisible = false; _windowId = ((object)this).GetHashCode(); float windowWidth = WindowWidth; float windowHeight = WindowHeight; float num = ((float)Screen.width - windowWidth) / 2f; float num2 = ((float)Screen.height - windowHeight) / 2f; _windowRect = new Rect(num, num2, windowWidth, windowHeight); IconCache.Initialize(Plugin.Log); MuseumContent.ApplyResolvedAquariumFishIfNeeded(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"MuseumTrackerUI initialized"); } } public void SetToggleKey(KeyCode key, bool requireCtrl) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _toggleKey = key; _requireCtrl = requireCtrl; } public void Toggle() { if (!PlayerPatches.IsDataLoaded) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Cannot toggle UI: data not loaded"); } } else if (_isVisible) { Hide(); } else { Show(); } } public void Show() { _isVisible = true; _openAnimation = 0f; MuseumContent.ApplyResolvedAquariumFishIfNeeded(); StartCacheRefresh(); if ((Object)(object)Player.Instance != (Object)null) { Player.Instance.AddPauseObject("MuseumTracker_UI"); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Museum Tracker UI opened"); } } private void StartCacheRefresh() { if (_cacheRefreshCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_cacheRefreshCoroutine); } _cacheRefreshCoroutine = ((MonoBehaviour)this).StartCoroutine(RefreshGameProgressCacheCoroutine()); } private IEnumerator RefreshGameProgressCacheCoroutine() { _isCacheRefreshing = true; _cachedGameDonationCounts.Clear(); _cachedGameCompleteStatus.Clear(); List allBundleIds = MuseumContent.GetAllBundleIds(); int processedCount = 0; foreach (string item in allBundleIds) { string progressKeyForBundle = MuseumContent.GetProgressKeyForBundle(item); if (!string.IsNullOrEmpty(progressKeyForBundle)) { _cachedGameDonationCounts[item] = MuseumPatches.GetBundleDonationCount(progressKeyForBundle); _cachedGameCompleteStatus[item] = MuseumPatches.IsBundleCompleteInGame(progressKeyForBundle); } processedCount++; if (processedCount % 3 == 0) { yield return null; } } _isCacheRefreshing = false; _cacheRefreshCoroutine = null; } private void RefreshGameProgressCacheImmediate() { if (_cacheRefreshCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(_cacheRefreshCoroutine); _cacheRefreshCoroutine = null; } _isCacheRefreshing = false; _cachedGameDonationCounts.Clear(); _cachedGameCompleteStatus.Clear(); foreach (string allBundleId in MuseumContent.GetAllBundleIds()) { string progressKeyForBundle = MuseumContent.GetProgressKeyForBundle(allBundleId); if (!string.IsNullOrEmpty(progressKeyForBundle)) { _cachedGameDonationCounts[allBundleId] = MuseumPatches.GetBundleDonationCount(progressKeyForBundle); _cachedGameCompleteStatus[allBundleId] = MuseumPatches.IsBundleCompleteInGame(progressKeyForBundle); } } } public void Hide() { _isVisible = false; if ((Object)(object)Player.Instance != (Object)null) { Player.Instance.RemovePauseObject("MuseumTracker_UI"); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Museum Tracker UI closed"); } } private void Update() { if (_isVisible && Input.GetKeyDown((KeyCode)27)) { Hide(); } if (!_isVisible) { return; } _openAnimation = Mathf.MoveTowards(_openAnimation, 1f, Time.unscaledDeltaTime * 6f); if (_syncStatusTimer > 0f) { _syncStatusTimer -= Time.unscaledDeltaTime; if (_syncStatusTimer <= 0f) { _syncStatusMessage = ""; } } } private void OnGUI() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (_isVisible && _donationManager != null && PlayerPatches.IsDataLoaded) { InitializeStyles(); float openAnimation = _openAnimation; GUI.color = new Color(1f, 1f, 1f, openAnimation); _windowRect = GUI.Window(_windowId, _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); GUI.color = Color.white; } } private void InitializeStyles() { if (!_stylesInitialized) { CreateTextures(); CreateStyles(); _stylesInitialized = true; } } private void CreateTextures() { //IL_000a: 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_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_026b: 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) _windowBackground = MakeParchmentTexture(32, 128, _parchment, _parchmentLight, _borderDark, 4); _headerBackground = MakeGradientTex(8, 64, _parchmentDark, _parchment); _tabNormal = MakeRoundedRect(8, 8, _parchmentDark, _borderDark, 2); _tabHover = MakeRoundedRect(8, 8, _parchmentDarker, _woodMedium, 2); _tabActive = MakeRoundedRect(8, 8, _goldPale, _goldRich, 3); _bundleNormal = MakeRoundedRect(6, 6, _parchmentDark, _borderDark, 2); _bundleHover = MakeRoundedRect(6, 6, _parchmentDarker, _woodMedium, 2); _bundleComplete = MakeRoundedRect(6, 6, new Color(_successGreenLight.r, _successGreenLight.g, _successGreenLight.b, 0.3f), _successGreen, 2); _itemEven = MakeTex(1, 1, new Color(_parchmentLight.r, _parchmentLight.g, _parchmentLight.b, 0.72f)); _itemOdd = MakeTex(1, 1, new Color(_parchment.r, _parchment.g, _parchment.b, 0.68f)); _itemDonated = MakeTex(1, 1, new Color(_successGreenLight.r, _successGreenLight.g, _successGreenLight.b, 0.35f)); _buttonNormal = MakeRoundedRect(6, 6, _woodMedium, _woodDark, 2); _buttonHover = MakeRoundedRect(6, 6, _woodLight, _woodMedium, 2); _progressBg = MakeTex(1, 1, new Color(_woodDark.r, _woodDark.g, _woodDark.b, 0.4f)); _searchBg = MakeRoundedRect(6, 6, _parchmentLight, _borderDark, 1); _dividerTex = MakeTex(1, 1, _borderDark); _itemPlaceholderIcon = MakeRoundedRect(32, 32, new Color(0.88f, 0.82f, 0.72f, 1f), _borderDark, 2); } private void CreateStyles() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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_004d: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0061: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00b2: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: 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_00f4: Expected O, but got Unknown //IL_00ff: 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_0115: 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_0126: 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_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Expected O, but got Unknown //IL_01e5: Expected O, but got Unknown //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Expected O, but got Unknown //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Expected O, but got Unknown //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Expected O, but got Unknown //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_033d: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Expected O, but got Unknown //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_036a: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_03a1: Expected O, but got Unknown //IL_03a1: Unknown result type (might be due to invalid IL or missing references) //IL_03a6: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Expected O, but got Unknown //IL_03b5: Expected O, but got Unknown //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03c5: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_03f0: Expected O, but got Unknown //IL_03fb: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_0411: Unknown result type (might be due to invalid IL or missing references) //IL_0418: Unknown result type (might be due to invalid IL or missing references) //IL_0422: Unknown result type (might be due to invalid IL or missing references) //IL_0433: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Unknown result type (might be due to invalid IL or missing references) //IL_0443: 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_045a: Unknown result type (might be due to invalid IL or missing references) //IL_0464: Unknown result type (might be due to invalid IL or missing references) //IL_0472: Unknown result type (might be due to invalid IL or missing references) //IL_0479: Unknown result type (might be due to invalid IL or missing references) //IL_04a6: Unknown result type (might be due to invalid IL or missing references) //IL_04b0: Expected O, but got Unknown //IL_04b5: Expected O, but got Unknown //IL_04bc: Unknown result type (might be due to invalid IL or missing references) //IL_04c1: Unknown result type (might be due to invalid IL or missing references) //IL_04cf: Unknown result type (might be due to invalid IL or missing references) //IL_04d6: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Unknown result type (might be due to invalid IL or missing references) //IL_04f4: Unknown result type (might be due to invalid IL or missing references) //IL_0504: Unknown result type (might be due to invalid IL or missing references) //IL_050a: 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_052c: Unknown result type (might be due to invalid IL or missing references) //IL_0532: Unknown result type (might be due to invalid IL or missing references) //IL_0542: Unknown result type (might be due to invalid IL or missing references) //IL_0548: Unknown result type (might be due to invalid IL or missing references) //IL_0557: Expected O, but got Unknown //IL_0562: Unknown result type (might be due to invalid IL or missing references) //IL_0567: Unknown result type (might be due to invalid IL or missing references) //IL_0575: Unknown result type (might be due to invalid IL or missing references) //IL_057c: Unknown result type (might be due to invalid IL or missing references) //IL_058b: Expected O, but got Unknown //IL_0596: Unknown result type (might be due to invalid IL or missing references) //IL_059b: Unknown result type (might be due to invalid IL or missing references) //IL_05a9: Unknown result type (might be due to invalid IL or missing references) //IL_05b0: Unknown result type (might be due to invalid IL or missing references) //IL_05b7: Unknown result type (might be due to invalid IL or missing references) //IL_05c1: Unknown result type (might be due to invalid IL or missing references) //IL_05cd: Expected O, but got Unknown //IL_05d8: Unknown result type (might be due to invalid IL or missing references) //IL_05dd: Unknown result type (might be due to invalid IL or missing references) //IL_05eb: Unknown result type (might be due to invalid IL or missing references) //IL_05f2: Unknown result type (might be due to invalid IL or missing references) //IL_05fc: Unknown result type (might be due to invalid IL or missing references) //IL_0603: Unknown result type (might be due to invalid IL or missing references) //IL_0612: Expected O, but got Unknown //IL_061d: Unknown result type (might be due to invalid IL or missing references) //IL_0622: Unknown result type (might be due to invalid IL or missing references) //IL_0630: Unknown result type (might be due to invalid IL or missing references) //IL_0641: Unknown result type (might be due to invalid IL or missing references) //IL_0648: Unknown result type (might be due to invalid IL or missing references) //IL_0652: Unknown result type (might be due to invalid IL or missing references) //IL_0663: Unknown result type (might be due to invalid IL or missing references) //IL_066a: Unknown result type (might be due to invalid IL or missing references) //IL_0674: Unknown result type (might be due to invalid IL or missing references) //IL_06a1: Unknown result type (might be due to invalid IL or missing references) //IL_06ab: Expected O, but got Unknown //IL_06b0: Expected O, but got Unknown //IL_06bb: Unknown result type (might be due to invalid IL or missing references) //IL_06c0: Unknown result type (might be due to invalid IL or missing references) //IL_06ce: 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_06dc: Unknown result type (might be due to invalid IL or missing references) //IL_06e3: Unknown result type (might be due to invalid IL or missing references) //IL_06f2: Expected O, but got Unknown //IL_06fd: Unknown result type (might be due to invalid IL or missing references) //IL_0702: Unknown result type (might be due to invalid IL or missing references) //IL_0710: Unknown result type (might be due to invalid IL or missing references) //IL_0717: Unknown result type (might be due to invalid IL or missing references) //IL_071e: Unknown result type (might be due to invalid IL or missing references) //IL_0725: Unknown result type (might be due to invalid IL or missing references) //IL_0734: Expected O, but got Unknown //IL_073f: Unknown result type (might be due to invalid IL or missing references) //IL_0744: Unknown result type (might be due to invalid IL or missing references) //IL_075c: Unknown result type (might be due to invalid IL or missing references) //IL_0770: Unknown result type (might be due to invalid IL or missing references) //IL_0780: Unknown result type (might be due to invalid IL or missing references) //IL_0787: Unknown result type (might be due to invalid IL or missing references) //IL_0791: Unknown result type (might be due to invalid IL or missing references) //IL_07a9: Unknown result type (might be due to invalid IL or missing references) //IL_07bd: Unknown result type (might be due to invalid IL or missing references) //IL_07cd: Unknown result type (might be due to invalid IL or missing references) //IL_07d3: Unknown result type (might be due to invalid IL or missing references) //IL_07dd: Unknown result type (might be due to invalid IL or missing references) //IL_07f5: Unknown result type (might be due to invalid IL or missing references) //IL_0809: Unknown result type (might be due to invalid IL or missing references) //IL_0819: Unknown result type (might be due to invalid IL or missing references) //IL_081f: Unknown result type (might be due to invalid IL or missing references) //IL_0829: Unknown result type (might be due to invalid IL or missing references) //IL_0837: Unknown result type (might be due to invalid IL or missing references) //IL_083e: Unknown result type (might be due to invalid IL or missing references) //IL_086b: Unknown result type (might be due to invalid IL or missing references) //IL_0875: Expected O, but got Unknown //IL_087a: Expected O, but got Unknown //IL_0885: Unknown result type (might be due to invalid IL or missing references) //IL_088a: Unknown result type (might be due to invalid IL or missing references) //IL_0898: Unknown result type (might be due to invalid IL or missing references) //IL_089f: Unknown result type (might be due to invalid IL or missing references) //IL_08a6: Unknown result type (might be due to invalid IL or missing references) //IL_08ad: Unknown result type (might be due to invalid IL or missing references) //IL_08bc: Expected O, but got Unknown //IL_08c7: Unknown result type (might be due to invalid IL or missing references) //IL_08cc: Unknown result type (might be due to invalid IL or missing references) //IL_08fb: Unknown result type (might be due to invalid IL or missing references) //IL_0901: Unknown result type (might be due to invalid IL or missing references) //IL_0911: Unknown result type (might be due to invalid IL or missing references) //IL_093e: Unknown result type (might be due to invalid IL or missing references) //IL_0948: Expected O, but got Unknown //IL_094d: Expected O, but got Unknown //IL_0954: Unknown result type (might be due to invalid IL or missing references) //IL_0959: Unknown result type (might be due to invalid IL or missing references) //IL_0960: Unknown result type (might be due to invalid IL or missing references) //IL_096e: Unknown result type (might be due to invalid IL or missing references) //IL_0975: Unknown result type (might be due to invalid IL or missing references) //IL_097c: Unknown result type (might be due to invalid IL or missing references) //IL_098b: Expected O, but got Unknown //IL_0992: Unknown result type (might be due to invalid IL or missing references) //IL_0997: Unknown result type (might be due to invalid IL or missing references) //IL_09a3: Expected O, but got Unknown //IL_09ae: Unknown result type (might be due to invalid IL or missing references) //IL_09b8: Expected O, but got Unknown //IL_09bf: Unknown result type (might be due to invalid IL or missing references) //IL_09c4: Unknown result type (might be due to invalid IL or missing references) //IL_09cb: Unknown result type (might be due to invalid IL or missing references) //IL_09d9: Unknown result type (might be due to invalid IL or missing references) //IL_09e0: Unknown result type (might be due to invalid IL or missing references) //IL_09e7: Unknown result type (might be due to invalid IL or missing references) //IL_09f6: Expected O, but got Unknown //IL_09fd: Unknown result type (might be due to invalid IL or missing references) //IL_0a02: Unknown result type (might be due to invalid IL or missing references) //IL_0a09: Unknown result type (might be due to invalid IL or missing references) //IL_0a17: Unknown result type (might be due to invalid IL or missing references) //IL_0a23: Expected O, but got Unknown //IL_0a2a: Unknown result type (might be due to invalid IL or missing references) //IL_0a2f: Unknown result type (might be due to invalid IL or missing references) //IL_0a3d: Unknown result type (might be due to invalid IL or missing references) //IL_0a44: Unknown result type (might be due to invalid IL or missing references) //IL_0a59: Unknown result type (might be due to invalid IL or missing references) //IL_0a68: Expected O, but got Unknown //IL_0a6f: Unknown result type (might be due to invalid IL or missing references) //IL_0a74: Unknown result type (might be due to invalid IL or missing references) //IL_0a82: Unknown result type (might be due to invalid IL or missing references) //IL_0a89: Unknown result type (might be due to invalid IL or missing references) //IL_0a95: Expected O, but got Unknown //IL_0a9c: Unknown result type (might be due to invalid IL or missing references) //IL_0aa1: Unknown result type (might be due to invalid IL or missing references) //IL_0ab4: Expected O, but got Unknown //IL_0abb: Unknown result type (might be due to invalid IL or missing references) //IL_0ac0: Unknown result type (might be due to invalid IL or missing references) //IL_0ac7: Unknown result type (might be due to invalid IL or missing references) //IL_0ad1: Unknown result type (might be due to invalid IL or missing references) //IL_0ae4: Expected O, but got Unknown int num = ScaledInt(16f); GUIStyle val = new GUIStyle(GUI.skin.window); val.normal.background = _windowBackground; val.normal.textColor = _textDark; val.padding = new RectOffset(0, 0, 0, 0); val.border = new RectOffset(num, num, num, num); _windowStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(26), fontStyle = (FontStyle)1, alignment = (TextAnchor)3 }; val2.normal.textColor = _woodDark; val2.padding = new RectOffset(0, 0, 0, 0); _titleStyle = val2; GUIStyle val3 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(13), fontStyle = (FontStyle)0, alignment = (TextAnchor)3 }; val3.normal.textColor = _textMedium; _subtitleStyle = val3; GUIStyle val4 = new GUIStyle(GUI.skin.button); val4.normal.background = _tabNormal; val4.normal.textColor = _textDark; val4.hover.background = _tabHover; val4.hover.textColor = _woodDark; val4.active.background = _tabHover; val4.active.textColor = _woodDark; val4.fontSize = ScaledFont(13); val4.fontStyle = (FontStyle)1; val4.alignment = (TextAnchor)4; val4.padding = new RectOffset(ScaledInt(14f), ScaledInt(10f), ScaledInt(12f), ScaledInt(10f)); val4.margin = new RectOffset(ScaledInt(4f), ScaledInt(4f), 0, 0); _sectionTabStyle = val4; GUIStyle val5 = new GUIStyle(_sectionTabStyle); val5.normal.background = _tabActive; val5.normal.textColor = _woodDark; val5.hover.background = _tabActive; val5.hover.textColor = _woodDark; _sectionTabActiveStyle = val5; GUIStyle val6 = new GUIStyle(GUI.skin.button); val6.normal.background = _bundleNormal; val6.normal.textColor = _textDark; val6.hover.background = _bundleHover; val6.hover.textColor = _woodDark; val6.active.background = _bundleHover; val6.active.textColor = _woodDark; val6.fontSize = ScaledFont(14); val6.fontStyle = (FontStyle)1; val6.alignment = (TextAnchor)3; val6.padding = new RectOffset(ScaledInt(18f), ScaledInt(14f), ScaledInt(14f), ScaledInt(14f)); val6.wordWrap = true; _bundleHeaderStyle = val6; GUIStyle val7 = new GUIStyle(_bundleHeaderStyle); val7.normal.background = _bundleComplete; val7.normal.textColor = _successGreen; val7.hover.background = _bundleComplete; val7.hover.textColor = _forestGreen; _bundleHeaderCompleteStyle = val7; _itemRowStyle = new GUIStyle { padding = new RectOffset(ScaledInt(16f), ScaledInt(10f), ScaledInt(8f), ScaledInt(8f)), margin = new RectOffset(0, 0, 2, 1) }; GUIStyle val8 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(14) }; val8.normal.textColor = _textDark; val8.wordWrap = true; _itemNameStyle = val8; GUIStyle val9 = new GUIStyle(GUI.skin.button); val9.normal.background = _buttonNormal; val9.normal.textColor = _parchmentLight; val9.hover.background = _buttonHover; val9.hover.textColor = Color.white; val9.active.background = _buttonHover; val9.active.textColor = Color.white; val9.fontSize = ScaledFont(12); val9.fontStyle = (FontStyle)1; val9.padding = new RectOffset(ScaledInt(14f), ScaledInt(14f), ScaledInt(8f), ScaledInt(8f)); _buttonStyle = val9; GUIStyle val10 = new GUIStyle(_buttonStyle) { fontSize = ScaledFont(18), fontStyle = (FontStyle)1 }; val10.normal.background = MakeRoundedRect(6, 6, _coralWarm, new Color(0.7f, 0.35f, 0.25f), 2); val10.normal.textColor = Color.white; val10.hover.background = MakeRoundedRect(6, 6, new Color(0.95f, 0.55f, 0.45f), _coralWarm, 2); val10.hover.textColor = Color.white; _closeButtonStyle = val10; GUIStyle val11 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(12) }; val11.normal.textColor = _textDark; _labelStyle = val11; GUIStyle val12 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(16), fontStyle = (FontStyle)1 }; val12.normal.textColor = _successGreen; val12.alignment = (TextAnchor)4; _checkmarkStyle = val12; GUIStyle val13 = new GUIStyle(GUI.skin.toggle) { fontSize = ScaledFont(12) }; val13.normal.textColor = _textMedium; val13.hover.textColor = _textDark; _toggleStyle = val13; GUIStyle val14 = new GUIStyle(GUI.skin.textField) { fontSize = ScaledFont(14) }; val14.normal.background = _searchBg; val14.normal.textColor = _textDark; val14.focused.background = _searchBg; val14.focused.textColor = _textDark; val14.padding = new RectOffset(ScaledInt(14f), ScaledInt(10f), ScaledInt(10f), ScaledInt(10f)); _searchStyle = val14; GUIStyle val15 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(22), fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; val15.normal.textColor = _woodDark; _statsStyle = val15; GUIStyle val16 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(12), fontStyle = (FontStyle)0, alignment = (TextAnchor)4 }; val16.normal.textColor = _textLight; _footerStyle = val16; GUIStyle val17 = new GUIStyle(GUI.skin.button); val17.normal.background = MakeRoundedRect(6, 6, new Color(0.3f, 0.55f, 0.5f), new Color(0.2f, 0.4f, 0.35f), 2); val17.normal.textColor = _parchmentLight; val17.hover.background = MakeRoundedRect(6, 6, new Color(0.4f, 0.65f, 0.6f), new Color(0.3f, 0.5f, 0.45f), 2); val17.hover.textColor = Color.white; val17.active.background = MakeRoundedRect(6, 6, new Color(0.35f, 0.6f, 0.55f), new Color(0.25f, 0.45f, 0.4f), 2); val17.active.textColor = Color.white; val17.fontSize = ScaledFont(11); val17.fontStyle = (FontStyle)1; val17.padding = new RectOffset(ScaledInt(10f), ScaledInt(10f), ScaledInt(6f), ScaledInt(6f)); _syncButtonStyle = val17; GUIStyle val18 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(11), fontStyle = (FontStyle)2, alignment = (TextAnchor)5 }; val18.normal.textColor = _successGreen; _syncStatusStyle = val18; GUIStyle val19 = new GUIStyle(GUI.skin.box); val19.normal.background = MakeRoundedRect(6, 6, new Color(_goldPale.r, _goldPale.g, _goldPale.b, 0.65f), _goldRich, 2); val19.padding = new RectOffset(ScaledInt(18f), ScaledInt(12f), ScaledInt(10f), ScaledInt(10f)); _headerBoxStyle = val19; GUIStyle val20 = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4, fontSize = ScaledFont(12), fontStyle = (FontStyle)1 }; val20.normal.textColor = _textDark; _headerCountStyle = val20; _searchLabelStyle = new GUIStyle(_labelStyle) { fontStyle = (FontStyle)1 }; _scrollStyle = new GUIStyle(GUI.skin.scrollView); GUIStyle val21 = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4, fontSize = ScaledFont(15), fontStyle = (FontStyle)0 }; val21.normal.textColor = _textMedium; _emptyStyle = val21; _progressLabelStyle = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4, fontSize = ScaledFont(13), fontStyle = (FontStyle)1 }; GUIStyle val22 = new GUIStyle(_itemNameStyle) { fontSize = ScaledFont(14), fontStyle = (FontStyle)0 }; val22.normal.textColor = new Color(0.22f, 0.52f, 0.28f); _itemNameDonatedStyle = val22; _rarityLabelStyleCached = new GUIStyle(_labelStyle) { fontSize = ScaledFont(12), fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; _checkStyleCached = new GUIStyle(_checkmarkStyle) { fontSize = ScaledFont(14) }; GUIStyle val23 = new GUIStyle(_checkmarkStyle); val23.normal.textColor = _neededOrange; val23.fontSize = ScaledFont(12); _neededStyleCached = val23; } 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 MakeGradientTex(int width, int height, Color topColor, Color bottomColor) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_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_0034: 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) Texture2D val = new Texture2D(width, height); Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < height; i++) { float num = (float)i / (float)(height - 1); Color val2 = Color.Lerp(topColor, bottomColor, num); for (int j = 0; j < width; j++) { array[i * width + j] = val2; } } val.SetPixels(array); val.Apply(); return val; } private Texture2D MakeRoundedRect(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 Texture2D MakeParchmentTexture(int width, int height, Color baseColor, Color lightColor, 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_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009b: 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_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0064: 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++) { float num = (float)i / (float)(height - 1); Color val2 = Color.Lerp(lightColor, baseColor, num * 0.3f); for (int j = 0; j < width; j++) { if (j < borderWidth || j >= width - borderWidth || i < borderWidth || i >= height - borderWidth) { array[i * width + j] = borderColor; continue; } float num2 = (float)((j + i) % 3) * 0.01f; array[i * width + j] = new Color(val2.r + num2, val2.g + num2 * 0.8f, val2.b + num2 * 0.5f, val2.a); } } val.SetPixels(array); val.Apply(); return val; } private void DrawWindow(int windowId) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(Array.Empty()); DrawHeader(); DrawGoldDivider(); GUILayout.Space(Scaled(14f)); DrawSearchBar(); GUILayout.Space(Scaled(14f)); DrawSectionTabs(); GUILayout.Space(Scaled(16f)); DrawContent(); GUILayout.Space(Scaled(10f)); DrawFooter(); GUILayout.EndVertical(); GUI.DragWindow(new Rect(0f, 0f, WindowWidth, HeaderHeight)); } private void DrawHeader() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(0f, HeaderHeight, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.DrawTexture(rect, (Texture)(object)_headerBackground, (ScaleMode)0); float num = Scaled(25f); GUILayout.BeginArea(new Rect(((Rect)(ref rect)).x + num, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width - num * 2f, ((Rect)(ref rect)).height)); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(Array.Empty()); GUILayout.Space(Scaled(12f)); GUILayout.Label("S.M.U.T.", _titleStyle, Array.Empty()); GUILayout.Label("Sun Haven Museum Utility Tracker", _subtitleStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(Array.Empty()); GUILayout.Space(12f); (int donated, int total) overallStats = _donationManager.GetOverallStats(); int item = overallStats.donated; int item2 = overallStats.total; float overallCompletionPercent = _donationManager.GetOverallCompletionPercent(); GUILayout.BeginVertical(_headerBoxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(100f)) }); GUILayout.Label($"{overallCompletionPercent:F0}%", _statsStyle, Array.Empty()); GUILayout.Label($"{item}/{item2}", _headerCountStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndVertical(); GUILayout.Space(Scaled(10f)); GUILayout.BeginVertical(Array.Empty()); GUILayout.Space(Scaled(20f)); if (GUILayout.Button("X", _closeButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(38f)), GUILayout.Height(Scaled(38f)) })) { Hide(); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndArea(); } private void DrawGoldDivider() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: 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_00ee: 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) Rect rect = GUILayoutUtility.GetRect(0f, Scaled(5f), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); float num = Scaled(24f); ((Rect)(ref rect)).x = ((Rect)(ref rect)).x + num; ((Rect)(ref rect)).width = ((Rect)(ref rect)).width - num * 2f; GUI.color = _borderDark; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.color = _goldRich; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 1f, ((Rect)(ref rect)).width, 2f), (Texture)(object)Texture2D.whiteTexture); GUI.color = _goldPale; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + 3f, ((Rect)(ref rect)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.color = Color.white; } private void DrawSearchBar() { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Space(Scaled(20f)); GUILayout.Label("Search", _searchLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(52f)) }); _searchQuery = GUILayout.TextField(_searchQuery, _searchStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(180f)), GUILayout.Height(Scaled(28f)) }); GUILayout.Space(Scaled(15f)); _showOnlyNeeded = GUILayout.Toggle(_showOnlyNeeded, " Needed only", _toggleStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(115f)) }); GUILayout.FlexibleSpace(); if (!string.IsNullOrEmpty(_syncStatusMessage)) { GUILayout.Label(_syncStatusMessage, _syncStatusStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(150f)) }); GUILayout.Space(Scaled(8f)); } if (!string.IsNullOrEmpty(_searchQuery)) { if (GUILayout.Button("Clear", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(55f)), GUILayout.Height(Scaled(28f)) })) { _searchQuery = ""; } GUILayout.Space(Scaled(8f)); } if (GUILayout.Button("Sync with Game", _syncButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(105f)), GUILayout.Height(Scaled(28f)) })) { PerformGameSync(); } GUILayout.Space(Scaled(20f)); GUILayout.EndHorizontal(); } private void PerformGameSync() { try { int item = _donationManager.GetOverallStats().donated; MuseumPatches.SyncWithGameProgress(); RefreshGameProgressCacheImmediate(); int item2 = _donationManager.GetOverallStats().donated; int num = item2 - item; if (num > 0) { Plugin.SaveData(); _syncStatusMessage = $"Synced {num} items!"; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[UI] Synced {num} items from game progress"); } } else { _syncStatusMessage = "Already in sync!"; } _syncStatusTimer = 4f; } catch (Exception ex) { _syncStatusMessage = "Sync failed"; _syncStatusTimer = 4f; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("[UI] Sync failed: " + ex.Message)); } } } private void DrawSectionTabs() { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) List allSections = MuseumContent.GetAllSections(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Space(Scaled(20f)); for (int i = 0; i < allSections.Count; i++) { MuseumSection museumSection = allSections[i]; (int, int) sectionStats = _donationManager.GetSectionStats(museumSection); bool num = _donationManager.IsSectionComplete(museumSection); bool num2 = i == _selectedSectionIndex; if (!_sectionThemes.TryGetValue(museumSection.Id, out (Color, Color) _)) { _ = _woodMedium; _ = _woodLight; } GUIStyle val = (num2 ? _sectionTabActiveStyle : _sectionTabStyle); string text = (num ? " *" : ""); if (GUILayout.Button($"{museumSection.Name}{text}\n{sectionStats.Item1}/{sectionStats.Item2}", val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(185f)), GUILayout.Height(Scaled(52f)) })) { _selectedSectionIndex = i; _scrollPosition = Vector2.zero; } if (i < allSections.Count - 1) { GUILayout.Space(Scaled(8f)); } } GUILayout.Space(Scaled(20f)); GUILayout.EndHorizontal(); } private void DrawContent() { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0079: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) List allSections = MuseumContent.GetAllSections(); if (_selectedSectionIndex >= allSections.Count) { _selectedSectionIndex = 0; } MuseumSection museumSection = allSections[_selectedSectionIndex]; (Color, Color) value; (Color, Color) tuple = (_sectionThemes.TryGetValue(museumSection.Id, out value) ? value : (_woodMedium, _woodLight)); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Space(Scaled(20f)); GUILayout.BeginVertical(Array.Empty()); DrawSectionProgress(museumSection, tuple.Item1); GUILayout.Space(Scaled(10f)); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, _scrollStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true) }); bool flag = false; foreach (MuseumBundle bundle in museumSection.Bundles) { if (DrawBundle(bundle, tuple.Item1)) { flag = true; GUILayout.Space(Scaled(10f)); } } if (!flag) { GUILayout.Space(Scaled(60f)); if (!string.IsNullOrEmpty(_searchQuery)) { GUILayout.Label("No items match \"" + _searchQuery + "\"", _emptyStyle, Array.Empty()); } else if (_showOnlyNeeded) { GUILayout.Label("All items in this section are donated!", _emptyStyle, Array.Empty()); } } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.Space(Scaled(20f)); GUILayout.EndHorizontal(); } private void DrawSectionProgress(MuseumSection section, Color sectionColor) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: 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_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Expected O, but got Unknown //IL_01cb: Unknown result type (might be due to invalid IL or missing references) (int, int) sectionStats = _donationManager.GetSectionStats(section); float sectionCompletionPercent = _donationManager.GetSectionCompletionPercent(section); Rect rect = GUILayoutUtility.GetRect(0f, Scaled(32f), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); float num = Scaled(3f); GUI.DrawTexture(rect, (Texture)(object)_progressBg); float num2 = Mathf.Max(0f, (((Rect)(ref rect)).width - num * 2f) * (sectionCompletionPercent / 100f)); Rect val = new Rect(((Rect)(ref rect)).x + num, ((Rect)(ref rect)).y + num, num2, ((Rect)(ref rect)).height - num * 2f); GUI.color = sectionColor; GUI.DrawTexture(val, (Texture)(object)Texture2D.whiteTexture); GUI.color = Color.white; GUI.color = _borderDark; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y + ((Rect)(ref rect)).height - 1f, ((Rect)(ref rect)).width, 1f), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, 1f, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + ((Rect)(ref rect)).width - 1f, ((Rect)(ref rect)).y, 1f, ((Rect)(ref rect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.color = Color.white; GUIStyle val2 = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4, fontSize = ScaledFont(14), fontStyle = (FontStyle)1 }; val2.normal.textColor = _textDark; GUIStyle val3 = val2; GUI.Label(rect, $"{section.Name}: {sectionStats.Item1} / {sectionStats.Item2} ({sectionCompletionPercent:F0}%)", val3); } private bool DrawBundle(MuseumBundle bundle, Color sectionColor) { bool flag = _donationManager.IsBundleComplete(bundle); bool flag2 = _expandedBundles.Contains(bundle.Id); (int, int) bundleStats = _donationManager.GetBundleStats(bundle); List visibleItems = GetVisibleItems(bundle); if (visibleItems.Count == 0 && (_showOnlyNeeded || !string.IsNullOrEmpty(_searchQuery))) { return false; } if (_showOnlyNeeded && flag && string.IsNullOrEmpty(_searchQuery)) { return false; } int value; int num = (_cachedGameDonationCounts.TryGetValue(bundle.Id, out value) ? value : (-1)); bool value2; bool flag3 = _cachedGameCompleteStatus.TryGetValue(bundle.Id, out value2) && value2; GUIStyle val = ((flag || flag3) ? _bundleHeaderCompleteStyle : _bundleHeaderStyle); string text = (flag2 ? "[-]" : "[+]"); string text2 = ((flag || flag3) ? " COMPLETE" : ""); string text3 = ((num < 0) ? $"{text} {bundle.Name}{text2} ({bundleStats.Item1}/{bundleStats.Item2})" : $"{text} {bundle.Name}{text2} ({bundleStats.Item1}/{bundleStats.Item2}) [Game: {num}]"); if (GUILayout.Button(text3, val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(Scaled(44f)) })) { if (flag2) { _expandedBundles.Remove(bundle.Id); } else { _expandedBundles.Add(bundle.Id); } } if (flag2) { DrawBundleItems(visibleItems); } return true; } private List GetVisibleItems(MuseumBundle bundle) { List list = new List(); string value = _searchQuery?.ToLower() ?? ""; foreach (MuseumItem item in bundle.Items) { bool flag = _donationManager.HasDonated(item.Id); if (!(_showOnlyNeeded && flag) && (string.IsNullOrEmpty(value) || item.Name.ToLower().Contains(value) || item.Rarity.ToString().ToLower().Contains(value) || bundle.Name.ToLower().Contains(value))) { list.Add(item); } } return list; } private void DrawBundleItems(List visibleItems) { int num = 0; foreach (MuseumItem visibleItem in visibleItems) { DrawItemRow(visibleItem, num); num++; } } private void DrawItemRow(MuseumItem item, int index) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Invalid comparison between Unknown and I4 //IL_0071: 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_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0177: 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_017c: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) bool flag = _donationManager.HasDonated(item.Id); Texture2D val = (flag ? _itemDonated : ((index % 2 == 0) ? _itemEven : _itemOdd)); GUILayout.BeginHorizontal(_itemRowStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Scaled(44f)) }); Rect lastRect = GUILayoutUtility.GetLastRect(); if ((int)Event.current.type == 7 && (Object)(object)val != (Object)null) { GUI.DrawTexture(lastRect, (Texture)(object)val); } GUILayout.Space(Scaled(10f)); if (GUILayout.Toggle(flag, "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(24f)) }) != flag) { _donationManager.ToggleDonated(item.Id); } GUILayout.Space(Scaled(8f)); Texture2D val2 = ((item.GameItemId > 0) ? IconCache.GetIcon(item.GameItemId) : _itemPlaceholderIcon); if ((Object)(object)val2 != (Object)null) { float iconSize = IconSize; GUI.DrawTexture(GUILayoutUtility.GetRect(iconSize, iconSize, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(iconSize), GUILayout.Height(iconSize) }), (Texture)(object)val2, (ScaleMode)2); GUILayout.Space(Scaled(10f)); } else { GUILayout.Space(IconSize + Scaled(10f)); } Color value; Color val3 = (_rarityColors.TryGetValue(item.Rarity, out value) ? value : _textDark); Color contentColor = GUI.contentColor; GUI.contentColor = (flag ? _successGreen : val3); GUILayout.Label(item.Name, flag ? _itemNameDonatedStyle : _itemNameStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.contentColor = val3; GUILayout.Label(item.Rarity.ToString(), _rarityLabelStyleCached, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(80f)) }); if (flag) { GUI.contentColor = _successGreen; GUILayout.Label("Donated", _checkStyleCached, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(60f)) }); } else { GUI.contentColor = _neededOrange; GUILayout.Label("Needed", _neededStyleCached, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(60f)) }); } GUI.contentColor = contentColor; GUILayout.Space(Scaled(10f)); GUILayout.EndHorizontal(); } private void DrawFooter() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label(string.Format("Press {0}{1} to open/close · ESC to close · Sync copies completed bundles from game", _requireCtrl ? "Ctrl+" : "", _toggleKey), _footerStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(Scaled(12f)); } } } namespace SunHavenMuseumUtilityTracker.Patches { public static class MuseumPatches { private static Dictionary _progressKeyToItemId = new Dictionary(StringComparer.OrdinalIgnoreCase); private static Dictionary _gameIdToProgressKey = new Dictionary(); private static bool _gameDataDiscovered = false; private static bool _debugLogging = true; private static Type _gameSaveRuntimeType; private static Func _gameSaveInstanceGetter; private static Func _currentWorldChecker; private static Func _getProgressIntWorld; private static Func _getProgressBoolWorld; public static void DiscoverProgressKeyPatterns() { if (_gameDataDiscovered) { return; } _gameDataDiscovered = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DISCOVER] ======= Starting discovery of game progress key patterns ======="); } try { Type type = null; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[DISCOVER] Searching for MuseumCurator type in all assemblies..."); } int num = 0; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { num++; try { type = assembly.GetType("MuseumCurator"); if (type == null) { type = assembly.GetType("Wish.MuseumCurator"); } if (type != null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("[DISCOVER] FOUND MuseumCurator in assembly: " + assembly.GetName().Name)); } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("[DISCOVER] Full type name: " + type.FullName)); } break; } } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogDebug((object)("[MuseumPatches] Skipping assembly for MuseumCurator search: " + ex.Message)); } } } ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[DISCOVER] Searched {num} assemblies"); } if (type == null) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogWarning((object)"[DISCOVER] Could not find MuseumCurator - trying AccessTools..."); } type = AccessTools.TypeByName("MuseumCurator") ?? AccessTools.TypeByName("Wish.MuseumCurator"); } if (type == null) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogError((object)"[DISCOVER] MuseumCurator type NOT FOUND in any assembly!"); } ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)"[DISCOVER] Searching for any types containing 'Museum'..."); } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { try { Type[] types = assembly2.GetTypes(); foreach (Type type2 in types) { if (type2.Name.Contains("Museum")) { ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogInfo((object)("[DISCOVER] Found type: " + type2.FullName)); } } } } catch (Exception ex2) { ManualLogSource log11 = Plugin.Log; if (log11 != null) { log11.LogDebug((object)("[MuseumPatches] Skipping assembly for Museum types: " + ex2.Message)); } } } return; } ManualLogSource log12 = Plugin.Log; if (log12 != null) { log12.LogInfo((object)"[DISCOVER] Listing all static fields on MuseumCurator:"); } FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { ManualLogSource log13 = Plugin.Log; if (log13 != null) { log13.LogInfo((object)("[DISCOVER] Field: " + fieldInfo.Name + " (" + fieldInfo.FieldType.Name + ")")); } } string[] obj = new string[3] { "culturalMuseumProgress", "aquaticMuseumProgress", "miningMuseumProgress" }; int num2 = 0; string[] array = obj; foreach (string text in array) { ManualLogSource log14 = Plugin.Log; if (log14 != null) { log14.LogInfo((object)("[DISCOVER] Looking for field: " + text)); } FieldInfo field = type.GetField(text, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field == null) { ManualLogSource log15 = Plugin.Log; if (log15 != null) { log15.LogWarning((object)("[DISCOVER] Field '" + text + "' NOT FOUND on MuseumCurator")); } continue; } ManualLogSource log16 = Plugin.Log; if (log16 != null) { log16.LogInfo((object)("[DISCOVER] Found field: " + text + " (Type: " + field.FieldType.FullName + ")")); } object value = field.GetValue(null); if (value == null) { ManualLogSource log17 = Plugin.Log; if (log17 != null) { log17.LogWarning((object)("[DISCOVER] Field '" + text + "' is NULL")); } continue; } ManualLogSource log18 = Plugin.Log; if (log18 != null) { log18.LogInfo((object)("[DISCOVER] Processing " + text + " (contains data)...")); } if (!(value is IEnumerable enumerable)) { continue; } int num3 = 0; foreach (object item in enumerable) { num3++; try { Type type3 = item.GetType(); FieldInfo field2 = type3.GetField("Item1"); FieldInfo field3 = type3.GetField("Item2"); if (field2 == null || field3 == null) { continue; } string text2 = field2.GetValue(item) as string; int num4 = (int)field3.GetValue(item); ManualLogSource log19 = Plugin.Log; if (log19 != null) { log19.LogInfo((object)$"[DISCOVER] Entry: progressKey='{text2}', requiredOrId={num4}"); } if (string.IsNullOrEmpty(text2)) { continue; } MuseumItem museumItem = MuseumContent.FindByGameItemId(num4); if (museumItem != null) { _progressKeyToItemId[text2] = museumItem.Id; _gameIdToProgressKey[num4] = text2; num2++; ManualLogSource log20 = Plugin.Log; if (log20 != null) { log20.LogInfo((object)("[DISCOVER] MAPPED: '" + text2 + "' -> " + museumItem.Id + " (" + museumItem.Name + ")")); } } else { ManualLogSource log21 = Plugin.Log; if (log21 != null) { log21.LogInfo((object)$"[DISCOVER] (No single-item map) key='{text2}', value={num4}"); } } } catch (Exception ex3) { ManualLogSource log22 = Plugin.Log; if (log22 != null) { log22.LogError((object)$"[DISCOVER] Error processing item {num3}: {ex3.Message}"); } } } ManualLogSource log23 = Plugin.Log; if (log23 != null) { log23.LogInfo((object)$"[DISCOVER] {text} had {num3} entries"); } } ManualLogSource log24 = Plugin.Log; if (log24 != null) { log24.LogInfo((object)$"[MuseumPatches] Discovered {num2} progress key mappings"); } ManualLogSource log25 = Plugin.Log; if (log25 != null) { log25.LogInfo((object)"[DISCOVER] NOTE: The game only tracks BUNDLE completion, not individual items."); } ManualLogSource log26 = Plugin.Log; if (log26 != null) { log26.LogInfo((object)"[DISCOVER] Use manual tracking in the UI to mark individual donations."); } } catch (Exception ex4) { ManualLogSource log27 = Plugin.Log; if (log27 != null) { log27.LogError((object)("[MuseumPatches] Error discovering patterns: " + ex4.Message + "\n" + ex4.StackTrace)); } } } public static void SyncWithGameProgress(bool verboseLogging = true) { try { DonationManager donationManager = Plugin.GetDonationManager(); if (donationManager == null) { if (verboseLogging) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[SYNC] DonationManager is null"); } } return; } if (verboseLogging) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[SYNC] ======= Starting sync with game progress ======="); } } if (!TryGetGameSaveForWorldProgress(out object _)) { if (verboseLogging) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[SYNC] Game progress not available - cannot sync (world not loaded yet)"); } } return; } if (verboseLogging) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"[SYNC] NOTE: The game only stores BUNDLE completion, not individual items."); } ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)"[SYNC] This will mark ALL items in completed bundles as donated."); } } int num = SyncCompletedBundles(donationManager, verboseLogging); if (verboseLogging) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[SYNC] Synced {num} completed bundles"); } ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)"[SYNC] ======= Sync complete ======="); } } } catch (Exception ex) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogError((object)("[SYNC] Error syncing with game progress: " + ex.Message)); } } } public static int GetBundleDonationCount(string progressKey) { try { if (!TryGetGameSaveForWorldProgress(out object gs)) { return -1; } return _getProgressIntWorld(gs, progressKey + "complete"); } catch { return -1; } } public static bool IsBundleCompleteInGame(string progressKey) { List itemsInBundle = MuseumContent.GetItemsInBundle(progressKey); return IsBundleCompleteInGame(progressKey, itemsInBundle.Count); } private static bool IsBundleCompleteInGame(string progressKey, int requiredSlots) { try { if (!TryGetGameSaveForWorldProgress(out object gs)) { return false; } if (_getProgressBoolWorld(gs, progressKey)) { return true; } if (requiredSlots <= 0) { return false; } return _getProgressIntWorld(gs, progressKey + "complete") >= requiredSlots; } catch (Exception ex) { if (_debugLogging) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[MuseumPatches] IsBundleCompleteInGame(" + progressKey + "): " + ex.Message)); } } return false; } } private static bool TryGetGameSaveForWorldProgress(out object gs) { gs = null; if (!EnsureGameSaveProgressDelegates()) { return false; } try { gs = _gameSaveInstanceGetter?.Invoke(); if (gs == null || !_currentWorldChecker(gs)) { gs = null; return false; } return true; } catch (Exception ex) { if (_debugLogging) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[MuseumPatches] TryGetGameSaveForWorldProgress: " + ex.Message)); } } gs = null; return false; } } private static bool EnsureGameSaveProgressDelegates() { if (_getProgressIntWorld != null && _getProgressBoolWorld != null && _gameSaveInstanceGetter != null && _currentWorldChecker != null) { return true; } try { _gameSaveRuntimeType = Type.GetType("Wish.GameSave, SunHaven.Core") ?? AccessTools.TypeByName("Wish.GameSave") ?? AccessTools.TypeByName("GameSave"); if (_gameSaveRuntimeType == null) { return false; } _gameSaveInstanceGetter = CreateGameSaveInstanceGetter(_gameSaveRuntimeType); if (_gameSaveInstanceGetter == null) { return false; } PropertyInfo currentWorldProp = _gameSaveRuntimeType.GetProperty("CurrentWorld", BindingFlags.Instance | BindingFlags.Public); if (currentWorldProp == null) { return false; } _currentWorldChecker = (object gs) => gs != null && currentWorldProp.GetValue(gs) != null; MethodInfo intM = _gameSaveRuntimeType.GetMethod("GetProgressIntWorld", new Type[1] { typeof(string) }); MethodInfo boolM = _gameSaveRuntimeType.GetMethod("GetProgressBoolWorld", new Type[1] { typeof(string) }); if (intM == null || boolM == null) { return false; } _getProgressIntWorld = (object obj, string key) => (int)intM.Invoke(obj, new object[1] { key }); _getProgressBoolWorld = (object obj, string key) => (bool)boolM.Invoke(obj, new object[1] { key }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[MuseumPatches] GameSave world progress delegates ready"); } return true; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("[MuseumPatches] EnsureGameSaveProgressDelegates: " + ex.Message)); } return false; } } private static Func CreateGameSaveInstanceGetter(Type gameSaveType) { PropertyInfo inst = gameSaveType.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy); if (inst != null) { return () => inst.GetValue(null); } Type type = AccessTools.TypeByName("SingletonBehaviour`1"); if (type == null) { return null; } Type type2 = type.MakeGenericType(gameSaveType); PropertyInfo p = type2.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public); if (p == null) { return null; } return () => p.GetValue(null); } private static int SyncCompletedBundles(DonationManager manager, bool verboseLogging) { int num = 0; List allBundleIds = MuseumContent.GetAllBundleIds(); if (verboseLogging) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[SYNC] Checking {allBundleIds.Count} bundles for completion..."); } } foreach (string item in allBundleIds) { try { string progressKeyForBundle = MuseumContent.GetProgressKeyForBundle(item); if (string.IsNullOrEmpty(progressKeyForBundle)) { continue; } List itemsInBundle = MuseumContent.GetItemsInBundle(item); int bundleDonationCount = GetBundleDonationCount(progressKeyForBundle); bool flag = IsBundleCompleteInGame(progressKeyForBundle, itemsInBundle.Count); if (verboseLogging) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)$"[SYNC] Bundle '{item}' ({progressKeyForBundle}): {bundleDonationCount}/{itemsInBundle.Count} donated, complete={flag}"); } } if (flag) { if (verboseLogging) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("[SYNC] Bundle '" + item + "' is COMPLETE, marking all items...")); } } int num2 = 0; foreach (MuseumItem item2 in itemsInBundle) { if (!manager.HasDonated(item2.Id)) { manager.MarkDonated(item2.Id); num2++; } } if (num2 <= 0) { continue; } if (verboseLogging) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[SYNC] Marked {num2} items from bundle '{item}'"); } } num++; continue; } if (bundleDonationCount > 0 && verboseLogging) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)$"[SYNC] Bundle '{item}' has {bundleDonationCount}/{itemsInBundle.Count} donations (not complete)"); } } } catch (Exception ex) { if (verboseLogging) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogDebug((object)("[SYNC] Error checking bundle '" + item + "': " + ex.Message)); } } } } return num; } public static bool IsGameProgressAvailable() { object gs; return TryGetGameSaveForWorldProgress(out gs); } public static void ResetReflectionCache() { } public static void LogAllMappings() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[MuseumPatches] Current mappings ({_progressKeyToItemId.Count} total):"); } foreach (KeyValuePair item in _progressKeyToItemId) { MuseumItem museumItem = MuseumContent.FindById(item.Value); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)(" '" + item.Key + "' -> " + item.Value + " (" + (museumItem?.Name ?? "unknown") + ")")); } } } public static void SetDebugLogging(bool enabled) { _debugLogging = enabled; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[MuseumPatches] Debug logging: {enabled}"); } } } public static class HungryMonsterPatches { private static bool _isHooked; public static void ApplyPatches(Harmony harmony) { //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown if (_isHooked) { return; } try { if ((AccessTools.TypeByName("Wish.HungryMonster") ?? AccessTools.TypeByName("HungryMonster")) == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[HungryMonsterPatches] Could not find HungryMonster type"); } return; } Type type = AccessTools.TypeByName("Wish.Chest") ?? AccessTools.TypeByName("Chest"); if (type != null) { MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "SaveInventory", (Type[])null, (Type[])null); if (methodInfo == null) { methodInfo = AccessTools.Method(type, "SaveInventory", (Type[])null, (Type[])null); } if (methodInfo != null) { MethodInfo methodInfo2 = AccessTools.Method(typeof(HungryMonsterPatches), "OnSaveInventory", (Type[])null, (Type[])null); harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[HungryMonsterPatches] Patched Chest.SaveInventory (HungryMonster filter in postfix)"); } } else { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[HungryMonsterPatches] Could not find Chest.SaveInventory"); } } } else { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)"[HungryMonsterPatches] Could not find Chest type"); } } _isHooked = true; } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)("[HungryMonsterPatches] Error applying patches: " + ex.Message)); } } } public static void OnSaveInventory(object __instance) { try { Type type = __instance.GetType(); FieldInfo field = type.GetField("bundleType", BindingFlags.Instance | BindingFlags.Public); if (field == null) { return; } object value = field.GetValue(__instance); if (value == null || (int)value != 3) { return; } FieldInfo field2 = type.GetField("progressTokenWhenFull", BindingFlags.Instance | BindingFlags.Public); if (field2 == null) { return; } object value2 = field2.GetValue(__instance); if (value2 == null) { return; } FieldInfo field3 = value2.GetType().GetField("progressID", BindingFlags.Instance | BindingFlags.Public); if (field3 == null) { return; } string text = field3.GetValue(value2) as string; if (string.IsNullOrEmpty(text)) { return; } FieldInfo field4 = type.GetField("sellingInventory", BindingFlags.Instance | BindingFlags.Public); if (field4 == null) { return; } object value3 = field4.GetValue(__instance); if (value3 == null) { return; } PropertyInfo property = value3.GetType().GetProperty("Items", BindingFlags.Instance | BindingFlags.Public); if (property == null || !(property.GetValue(value3) is IEnumerable enumerable)) { return; } MuseumBundle museumBundle = MuseumContent.FindBundleByProgressKey(text); if (museumBundle == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[HungryMonsterPatches] No bundle found for progress key: " + text)); } return; } DonationManager donationManager = Plugin.GetDonationManager(); if (donationManager == null) { return; } int num = 0; foreach (object item in enumerable) { FieldInfo field5 = item.GetType().GetField("item", BindingFlags.Instance | BindingFlags.Public); if (field5 == null) { continue; } object value4 = field5.GetValue(item); if (value4 == null) { continue; } MethodInfo method = value4.GetType().GetMethod("ID", BindingFlags.Instance | BindingFlags.Public); if (method == null) { continue; } int num2 = (int)method.Invoke(value4, null); MuseumItem museumItem = MuseumContent.FindByGameItemIdInBundle(num2, text); if (museumItem != null && !donationManager.HasDonated(museumItem.Id)) { donationManager.MarkDonated(museumItem.Id); num++; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[HungryMonsterPatches] Auto-tracked donation: {museumItem.Name} (ID: {num2})"); } } } if (num > 0) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[HungryMonsterPatches] Tracked {num} new donations for bundle '{museumBundle.Name}'"); } } } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogDebug((object)("[HungryMonsterPatches] Error tracking donation: " + ex.Message)); } } } } public static class PlayerPatches { private static bool _isDataLoaded = false; private static string _loadedCharacterName = null; private static string _lastCharacterSourceLog = null; private static string _lastCharacterNameLog = null; private static float _lastSaveAndResetRealtime = -100f; private const float SaveAndResetDedupSeconds = 1.5f; public static bool IsDataLoaded => _isDataLoaded; public static string LoadedCharacterName => _loadedCharacterName; public static void OnPlayerInitialized(Player __instance) { try { Plugin.EnsureUIComponentsExist(); string currentCharacterName = GetCurrentCharacterName(); if (string.IsNullOrEmpty(currentCharacterName) || currentCharacterName == "default") { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Could not determine character name on player init"); } return; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Player initialized: " + currentCharacterName)); } if (_isDataLoaded && _loadedCharacterName == currentCharacterName) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Data already loaded for " + currentCharacterName)); } return; } if (_isDataLoaded && _loadedCharacterName != currentCharacterName) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("Switching from " + _loadedCharacterName + " to " + currentCharacterName)); } Plugin.SaveData(); ResetState(); } LoadDataForCharacter(currentCharacterName); } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)("Error in OnPlayerInitialized: " + ex.Message)); } } } private static void LoadDataForCharacter(string characterName) { try { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Loading donation data for: " + characterName)); } Plugin.LoadDataForPlayer(characterName); _isDataLoaded = true; _loadedCharacterName = characterName; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Donation data loaded successfully for " + characterName)); } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Error loading donation data: " + ex.Message)); } } } private static string GetCurrentCharacterName() { try { string lastLoadedCharacterName = GameSavePatches.LastLoadedCharacterName; if (!string.IsNullOrEmpty(lastLoadedCharacterName)) { string text = SanitizeFileName(lastLoadedCharacterName); LogCharacterSourceOnce("lastLoaded", text); return text; } CharacterData currentCharacter = GameSave.CurrentCharacter; if (currentCharacter != null && !string.IsNullOrEmpty(currentCharacter.characterName)) { string text2 = SanitizeFileName(currentCharacter.characterName); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("GetCurrentCharacterName: FALLBACK to CurrentCharacter = '" + text2 + "'")); } return text2; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"GetCurrentCharacterName: Could not determine character name"); } return "default"; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Error getting character name: " + ex.Message)); } return "default"; } } private static string SanitizeFileName(string name) { if (string.IsNullOrEmpty(name)) { return "default"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } name = name.Trim(); if (!string.IsNullOrEmpty(name)) { return name; } return "default"; } private static void LogCharacterSourceOnce(string source, string characterName) { if (!string.Equals(_lastCharacterSourceLog, source, StringComparison.Ordinal) || !string.Equals(_lastCharacterNameLog, characterName, StringComparison.Ordinal)) { _lastCharacterSourceLog = source; _lastCharacterNameLog = characterName; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("GetCurrentCharacterName: Using " + source + " character name = '" + characterName + "'")); } } } public static void ResetState() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Resetting data state"); } _isDataLoaded = false; _loadedCharacterName = null; _lastCharacterSourceLog = null; _lastCharacterNameLog = null; GameSavePatches.ResetLastLoadedSlot(); } public static void SaveAndReset() { try { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup - _lastSaveAndResetRealtime < 1.5f) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)$"SaveAndReset deduplicated (last run {realtimeSinceStartup - _lastSaveAndResetRealtime:0.00}s ago)"); } return; } _lastSaveAndResetRealtime = realtimeSinceStartup; if (_isDataLoaded) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Saving data for " + _loadedCharacterName + " before menu")); } Plugin.SaveData(); } ResetState(); } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Error in SaveAndReset: " + ex.Message)); } } } } public static class GameSavePatches { public static int LastLoadedSlot { get; private set; } = -1; public static string LastLoadedCharacterName { get; private set; } = null; public static void ResetLastLoadedSlot() { LastLoadedSlot = -1; LastLoadedCharacterName = null; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"GameSavePatches: Reset LastLoadedSlot and LastLoadedCharacterName"); } } public static void OnLoadCharacter(int characterNumber) { try { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"GameSave.LoadCharacter: slot {characterNumber}"); } LastLoadedSlot = characterNumber; GameSave instance = SingletonBehaviour.Instance; if (((instance != null) ? instance.Saves : null) == null || characterNumber < 0 || characterNumber >= SingletonBehaviour.Instance.Saves.Count) { return; } GameSaveData val = SingletonBehaviour.Instance.Saves[characterNumber]; if (val == null) { return; } string characterNameFromSaveData = GetCharacterNameFromSaveData(val); if (!string.IsNullOrEmpty(characterNameFromSaveData)) { LastLoadedCharacterName = characterNameFromSaveData; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"GameSavePatches: Extracted character name '{characterNameFromSaveData}' from slot {characterNumber}"); } } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Error in OnLoadCharacter: " + ex.Message)); } } } private static string GetCharacterNameFromSaveData(object saveData) { if (saveData == null) { return null; } Type type = saveData.GetType(); string[] array = new string[6] { "characterName", "CharacterName", "name", "Name", "playerName", "PlayerName" }; string[] array2 = array; foreach (string name in array2) { PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(string)) { string text = property.GetValue(saveData) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } PropertyInfo property2 = type.GetProperty("characterData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property2 != null) { object value = property2.GetValue(saveData); if (value != null) { Type type2 = value.GetType(); array2 = array; foreach (string name2 in array2) { PropertyInfo property3 = type2.GetProperty(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property3 != null && property3.PropertyType == typeof(string)) { string text2 = property3.GetValue(value) as string; if (!string.IsNullOrEmpty(text2)) { return text2; } } } } } return null; } } } namespace SunHavenMuseumUtilityTracker.Data { public class DonationManager { private static Dictionary> _museumIdsByGameItemId; private DonationData _donationData; private string _currentCharacter; private bool _isDirty; public bool IsDirty => _isDirty; public bool IsLoaded => _donationData != null; public string CurrentCharacter => _currentCharacter; public event Action OnDonationsChanged; public event Action OnDataLoaded; public DonationManager() { _donationData = null; _currentCharacter = null; } public void LoadForCharacter(string characterName, DonationData data) { _currentCharacter = characterName; _donationData = data ?? new DonationData(characterName); _isDirty = false; this.OnDataLoaded?.Invoke(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"DonationManager: Loaded data for {characterName} with {_donationData.DonatedItemIds.Count} donated items"); } } public DonationData GetData() { return _donationData; } public void ClearDirty() { _isDirty = false; } public bool HasDonated(string itemId) { return _donationData?.HasDonated(itemId) ?? false; } public bool HasDonatedByGameId(int gameItemId) { List museumItemIdsForGameItem = GetMuseumItemIdsForGameItem(gameItemId); if (museumItemIdsForGameItem.Count == 0) { return false; } foreach (string item in museumItemIdsForGameItem) { if (!HasDonated(item)) { return false; } } return true; } public void MarkDonated(string itemId) { if (_donationData != null && !_donationData.HasDonated(itemId)) { _donationData.MarkDonated(itemId); _isDirty = true; this.OnDonationsChanged?.Invoke(); MuseumItem museumItem = MuseumContent.FindById(itemId); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("DonationManager: Marked " + (museumItem?.Name ?? itemId) + " as donated")); } } } public void MarkDonatedByGameId(int gameItemId) { foreach (string item in GetMuseumItemIdsForGameItem(gameItemId)) { MarkDonated(item); } } private static List GetMuseumItemIdsForGameItem(int gameItemId) { if (_museumIdsByGameItemId == null) { _museumIdsByGameItemId = (from item in MuseumContent.GetAllItems() group item by item.GameItemId).ToDictionary((IGrouping group) => group.Key, (IGrouping group) => group.Select((MuseumItem item) => item.Id).ToList()); } if (!_museumIdsByGameItemId.TryGetValue(gameItemId, out List value)) { return new List(); } return value; } public void ToggleDonated(string itemId) { if (_donationData != null) { if (_donationData.HasDonated(itemId)) { _donationData.UnmarkDonated(itemId); } else { _donationData.MarkDonated(itemId); } _isDirty = true; this.OnDonationsChanged?.Invoke(); } } public (int donated, int total) GetSectionStats(MuseumSection section) { int num = 0; int num2 = 0; foreach (MuseumBundle bundle in section.Bundles) { (int, int) bundleStats = GetBundleStats(bundle); num += bundleStats.Item2; num2 += bundleStats.Item1; } return (num2, num); } public (int donated, int total) GetBundleStats(MuseumBundle bundle) { int count = bundle.Items.Count; return (bundle.Items.Count((MuseumItem item) => HasDonated(item.Id)), count); } public (int donated, int total) GetOverallStats() { List allItems = MuseumContent.GetAllItems(); return new ValueTuple(item2: allItems.Count, item1: allItems.Count((MuseumItem item) => HasDonated(item.Id))); } public List GetNeededItems(MuseumBundle bundle) { return bundle.Items.Where((MuseumItem item) => !HasDonated(item.Id)).ToList(); } public List GetAllNeededItems() { return (from item in MuseumContent.GetAllItems() where !HasDonated(item.Id) select item).ToList(); } public bool IsBundleComplete(MuseumBundle bundle) { return bundle.Items.All((MuseumItem item) => HasDonated(item.Id)); } public bool IsSectionComplete(MuseumSection section) { return section.Bundles.All(IsBundleComplete); } public bool IsMuseumComplete() { return MuseumContent.GetAllSections().All(IsSectionComplete); } public float GetBundleCompletionPercent(MuseumBundle bundle) { (int, int) bundleStats = GetBundleStats(bundle); if (bundleStats.Item2 <= 0) { return 0f; } return (float)bundleStats.Item1 / (float)bundleStats.Item2 * 100f; } public float GetSectionCompletionPercent(MuseumSection section) { (int, int) sectionStats = GetSectionStats(section); if (sectionStats.Item2 <= 0) { return 0f; } return (float)sectionStats.Item1 / (float)sectionStats.Item2 * 100f; } public float GetOverallCompletionPercent() { (int, int) overallStats = GetOverallStats(); if (overallStats.Item2 <= 0) { return 0f; } return (float)overallStats.Item1 / (float)overallStats.Item2 * 100f; } } public class DonationSaveSystem { private readonly string _saveFolder; private readonly DonationManager _manager; private float _lastSaveCheck; private const float SAVE_INTERVAL = 30f; public DonationSaveSystem(DonationManager manager) { _manager = manager; _saveFolder = Path.Combine(Paths.ConfigPath, "SunHavenMuseumUtilityTracker", "Saves"); if (!Directory.Exists(_saveFolder)) { Directory.CreateDirectory(_saveFolder); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Created save folder: " + _saveFolder)); } } } private string GetSaveFilePath(string characterName) { string text = SanitizeFileName(characterName); return Path.Combine(_saveFolder, text + "_donations.json"); } private string SanitizeFileName(string name) { char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } return name; } public DonationData Load(string characterName) { string saveFilePath = GetSaveFilePath(characterName); if (!File.Exists(saveFilePath)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("No save file found for " + characterName + ", creating new data")); } return new DonationData(characterName); } try { DonationDataWrapper donationDataWrapper = JsonUtility.FromJson(File.ReadAllText(saveFilePath)); if (donationDataWrapper != null) { DonationData donationData = donationDataWrapper.ToData(); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"Loaded {donationData.DonatedItemIds.Count} donated items for {characterName}"); } return donationData; } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Failed to load donation data for " + characterName + ": " + ex.Message)); } string path = saveFilePath + ".bak"; if (File.Exists(path)) { try { DonationDataWrapper donationDataWrapper2 = JsonUtility.FromJson(File.ReadAllText(path)); if (donationDataWrapper2 != null) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("Restored from backup for " + characterName)); } return donationDataWrapper2.ToData(); } } catch (Exception ex2) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)("Failed to load backup for " + characterName + ": " + ex2.Message)); } } } } return new DonationData(characterName); } public bool Save(string characterName, DonationData data) { if (data == null || string.IsNullOrEmpty(characterName)) { return false; } string saveFilePath = GetSaveFilePath(characterName); string text = saveFilePath + ".tmp"; string text2 = saveFilePath + ".bak"; try { string contents = JsonUtility.ToJson((object)new DonationDataWrapper(data), true); File.WriteAllText(text, contents); if (File.Exists(saveFilePath)) { if (File.Exists(text2)) { File.Delete(text2); } File.Move(saveFilePath, text2); } File.Move(text, saveFilePath); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Saved {data.DonatedItemIds.Count} donated items for {characterName}"); } return true; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Failed to save donation data for " + characterName + ": " + ex.Message)); } if (File.Exists(text)) { try { File.Delete(text); } catch (Exception ex2) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("[DonationSave] Cleanup temp file failed: " + ex2.Message)); } } } return false; } } public void CheckAutoSave() { if (_manager.IsLoaded && _manager.IsDirty && !(Time.time - _lastSaveCheck < 30f)) { _lastSaveCheck = Time.time; if (Save(_manager.CurrentCharacter, _manager.GetData())) { _manager.ClearDirty(); } } } public bool ForceSave() { if (!_manager.IsLoaded) { return false; } bool num = Save(_manager.CurrentCharacter, _manager.GetData()); if (num) { _manager.ClearDirty(); } return num; } } public static class MuseumContent { private static List _sections; public static List GetAllSections() { if (_sections == null) { _sections = BuildMuseumContent(); } return _sections; } private static List BuildMuseumContent() { List list = new List(); MuseumSection museumSection = new MuseumSection("hall_of_gems", "The Hall of Gems", "A dazzling collection of precious gems and minerals."); MuseumBundle museumBundle = new MuseumBundle("ManaBundle", "Mana Bundle", "hall_of_gems", "Collect 20 Mana Drops."); museumBundle.Items.AddRange(new MuseumItem[1] { new MuseumItem("mana_drop", "Mana Drop (x20)", "ManaBundle", 60234, ItemRarity.Rare, "A shimmering drop of pure mana. Donate 20.") }); museumSection.Bundles.Add(museumBundle); MuseumBundle museumBundle2 = new MuseumBundle("MoneyBundle", "Money Bundle", "hall_of_gems", "Wealth and currency collection."); museumBundle2.Items.AddRange(new MuseumItem[3] { new MuseumItem("coins", "Coins (x25,000)", "MoneyBundle", 60000, ItemRarity.Common, "Gold coins. Donate 25,000."), new MuseumItem("mana_orbs", "Mana Orbs (x1,000)", "MoneyBundle", 60001, ItemRarity.Uncommon, "Magical mana orbs. Donate 1,000."), new MuseumItem("tickets", "Tickets (x1,000)", "MoneyBundle", 60002, ItemRarity.Uncommon, "Event tickets. Donate 1,000.") }); museumSection.Bundles.Add(museumBundle2); MuseumBundle museumBundle3 = new MuseumBundle("GoldenBundle", "Golden Bundle", "hall_of_gems", "Rare golden items collection."); museumBundle3.Items.AddRange(new MuseumItem[13] { new MuseumItem("golden_milk", "Golden Milk", "GoldenBundle", 2920, ItemRarity.Rare, "Milk with a golden hue."), new MuseumItem("golden_egg", "Golden Egg", "GoldenBundle", 3052, ItemRarity.Rare, "A shimmering golden egg."), new MuseumItem("golden_wool", "Golden Wool", "GoldenBundle", 2113, ItemRarity.Rare, "Luxurious golden wool."), new MuseumItem("golden_pomegranate", "Golden Pomegranate", "GoldenBundle", 3053, ItemRarity.Rare, "A golden pomegranate."), new MuseumItem("golden_log", "Golden Log", "GoldenBundle", 2114, ItemRarity.Rare, "A log of golden wood."), new MuseumItem("golden_feather", "Golden Feather", "GoldenBundle", 2115, ItemRarity.Rare, "A brilliant golden feather."), new MuseumItem("golden_silk", "Golden Silk", "GoldenBundle", 2116, ItemRarity.Rare, "Fine golden silk."), new MuseumItem("golden_apple", "Golden Apple", "GoldenBundle", 3054, ItemRarity.Rare, "A golden apple."), new MuseumItem("golden_orange", "Golden Orange", "GoldenBundle", 3055, ItemRarity.Rare, "A golden orange."), new MuseumItem("golden_strawberry", "Golden Strawberry", "GoldenBundle", 3057, ItemRarity.Rare, "A golden strawberry."), new MuseumItem("golden_blueberry", "Golden Blueberry", "GoldenBundle", 3056, ItemRarity.Rare, "A golden blueberry."), new MuseumItem("golden_peach", "Golden Peach", "GoldenBundle", 3058, ItemRarity.Rare, "A golden peach."), new MuseumItem("golden_raspberry", "Golden Raspberry", "GoldenBundle", 3059, ItemRarity.Rare, "A golden raspberry.") }); museumSection.Bundles.Add(museumBundle3); MuseumBundle museumBundle4 = new MuseumBundle("BarsBundle", "Bars Bundle", "hall_of_gems", "Metal bars collection."); museumBundle4.Items.AddRange(new MuseumItem[8] { new MuseumItem("copper_bar", "Copper Bar", "BarsBundle", 1200, ItemRarity.Common, "A bar of copper."), new MuseumItem("iron_bar", "Iron Bar", "BarsBundle", 1201, ItemRarity.Common, "A bar of iron."), new MuseumItem("adamant_bar", "Adamant Bar", "BarsBundle", 1202, ItemRarity.Uncommon, "A bar of adamant."), new MuseumItem("mithril_bar", "Mithril Bar", "BarsBundle", 1203, ItemRarity.Rare, "A bar of mithril."), new MuseumItem("sunite_bar", "Sunite Bar", "BarsBundle", 1204, ItemRarity.Epic, "A bar of sunite."), new MuseumItem("gold_bar", "Gold Bar", "BarsBundle", 1205, ItemRarity.Rare, "A bar of gold."), new MuseumItem("glorite_bar", "Glorite Bar", "BarsBundle", 1206, ItemRarity.Legendary, "A bar of glorite."), new MuseumItem("elven_steel_bar", "Elven Steel Bar", "BarsBundle", 1207, ItemRarity.Epic, "A bar of elven steel.") }); museumSection.Bundles.Add(museumBundle4); MuseumBundle museumBundle5 = new MuseumBundle("GemBundle", "Gems Bundle", "hall_of_gems", "Precious gems collection."); museumBundle5.Items.AddRange(new MuseumItem[7] { new MuseumItem("sapphire", "Sapphire", "GemBundle", 1000, ItemRarity.Rare, "A brilliant blue sapphire."), new MuseumItem("ruby", "Ruby", "GemBundle", 1001, ItemRarity.Rare, "A deep red ruby."), new MuseumItem("amethyst", "Amethyst", "GemBundle", 1002, ItemRarity.Uncommon, "A purple amethyst."), new MuseumItem("diamond", "Diamond", "GemBundle", 1003, ItemRarity.Epic, "A sparkling diamond."), new MuseumItem("havenite", "Havenite", "GemBundle", 1004, ItemRarity.Legendary, "A rare havenite gem."), new MuseumItem("black_diamond", "Black Diamond", "GemBundle", 1005, ItemRarity.Legendary, "A mysterious black diamond."), new MuseumItem("dizzite", "Dizzite", "GemBundle", 10620, ItemRarity.Rare, "A shimmering dizzite gem.") }); museumSection.Bundles.Add(museumBundle5); MuseumBundle museumBundle6 = new MuseumBundle("NelvariMinesBundle", "Nel'Vari Mines Bundle", "hall_of_gems", "Treasures from the Nel'Vari Mines."); museumBundle6.Items.AddRange(new MuseumItem[4] { new MuseumItem("mana_shard", "Mana Shard (x5)", "NelvariMinesBundle", 18015, ItemRarity.Rare, "A shard of crystallized mana. Donate 5."), new MuseumItem("sparkling_dragon_scale", "Sparkling Dragon Scale (x5)", "NelvariMinesBundle", 1115, ItemRarity.Epic, "A sparkling dragon scale. Donate 5."), new MuseumItem("sharp_dragon_scale", "Sharp Dragon Scale (x5)", "NelvariMinesBundle", 1116, ItemRarity.Epic, "A sharp dragon scale. Donate 5."), new MuseumItem("tough_dragon_scale", "Tough Dragon Scale (x5)", "NelvariMinesBundle", 1114, ItemRarity.Epic, "A tough dragon scale. Donate 5.") }); museumSection.Bundles.Add(museumBundle6); MuseumBundle museumBundle7 = new MuseumBundle("WithergateMinesBundle", "Withergate Mines Bundle", "hall_of_gems", "Sweet treasures from the Withergate Mines."); museumBundle7.Items.AddRange(new MuseumItem[4] { new MuseumItem("candy_corn_pieces", "Candy Corn Pieces (x5)", "WithergateMinesBundle", 18016, ItemRarity.Rare, "Candy corn pieces. Donate 5."), new MuseumItem("rock_candy_gem", "Rock Candy Gem (x5)", "WithergateMinesBundle", 3759, ItemRarity.Rare, "A rock candy gem. Donate 5."), new MuseumItem("jawbreaker_gem", "Jawbreaker Gem (x5)", "WithergateMinesBundle", 3761, ItemRarity.Rare, "A jawbreaker gem. Donate 5."), new MuseumItem("hard_butterscotch_gem", "Hard Butterscotch Gem (x5)", "WithergateMinesBundle", 3760, ItemRarity.Rare, "A hard butterscotch gem. Donate 5.") }); museumSection.Bundles.Add(museumBundle7); list.Add(museumSection); MuseumSection museumSection2 = new MuseumSection("hall_of_culture", "The Hall of Culture", "Crops, flowers, foraging, combat, alchemy, exploration, and farming from all regions."); MuseumBundle museumBundle8 = new MuseumBundle("WinterCropsBundle", "Winter Crops", "hall_of_culture", "Crops harvested in winter."); museumBundle8.Items.AddRange(new MuseumItem[15] { new MuseumItem("wc_tea_leaves", "Tea Leaves", "WinterCropsBundle", 11048), new MuseumItem("wc_turnip", "Turnip", "WinterCropsBundle", 11040), new MuseumItem("wc_purple_eggplant", "Purple Eggplant", "WinterCropsBundle", 12081), new MuseumItem("wc_heat_fruit", "Heat Fruit", "WinterCropsBundle", 11041), new MuseumItem("wc_marshmallow_bean", "Marshmallow Bean", "WinterCropsBundle", 11042), new MuseumItem("wc_brr_nana", "Brr-Nana", "WinterCropsBundle", 12082), new MuseumItem("wc_star_fruit", "Star Fruit", "WinterCropsBundle", 11066), new MuseumItem("wc_hexagon_berry", "Hexagon Berry", "WinterCropsBundle", 11065), new MuseumItem("wc_snow_pea", "Snow Pea", "WinterCropsBundle", 11078), new MuseumItem("wc_snow_ball_crop", "Snow Ball (Crop)", "WinterCropsBundle", 11067), new MuseumItem("wc_balloon_fruit", "Balloon Fruit", "WinterCropsBundle", 11063), new MuseumItem("wc_blizzard_berry", "Blizzard Berry", "WinterCropsBundle", 11068), new MuseumItem("wc_pythagorean_berry", "Pythagorean Berry", "WinterCropsBundle", 11069), new MuseumItem("wc_blue_moon_fruit", "Blue Moon Fruit", "WinterCropsBundle", 11064), new MuseumItem("wc_candy_cane", "Candy Cane", "WinterCropsBundle", 11077) }); museumSection2.Bundles.Add(museumBundle8); MuseumBundle museumBundle9 = new MuseumBundle("FlowersBundle", "Flowers", "hall_of_culture", "Flowers grown year-round."); museumBundle9.Items.AddRange(new MuseumItem[11] { new MuseumItem("fl_red_rose", "Red Rose", "FlowersBundle", 11107), new MuseumItem("fl_blue_rose", "Blue Rose", "FlowersBundle", 11108), new MuseumItem("fl_daisy", "Daisy", "FlowersBundle", 11130), new MuseumItem("fl_orchid", "Orchid", "FlowersBundle", 11105), new MuseumItem("fl_tulip", "Tulip", "FlowersBundle", 11109), new MuseumItem("fl_hibiscus", "Hibiscus", "FlowersBundle", 11103), new MuseumItem("fl_lavender", "Lavender", "FlowersBundle", 11102), new MuseumItem("fl_sunflower", "Sunflower", "FlowersBundle", 11106), new MuseumItem("fl_lily", "Lily", "FlowersBundle", 11104), new MuseumItem("fl_lotus", "Lotus", "FlowersBundle", 11110), new MuseumItem("fl_honey_flower", "Honey Flower", "FlowersBundle", 11101) }); museumSection2.Bundles.Add(museumBundle9); MuseumBundle museumBundle10 = new MuseumBundle("SpringCropsBundle", "Spring Crops", "hall_of_culture", "Crops harvested in spring."); museumBundle10.Items.AddRange(new MuseumItem[13] { new MuseumItem("sp_grapes", "Grapes", "SpringCropsBundle", 11022), new MuseumItem("sp_wheat", "Wheat", "SpringCropsBundle", 11000), new MuseumItem("sp_tomato", "Tomato", "SpringCropsBundle", 11003), new MuseumItem("sp_corn", "Corn", "SpringCropsBundle", 11001), new MuseumItem("sp_onion", "Onion", "SpringCropsBundle", 11009), new MuseumItem("sp_potato", "Potato", "SpringCropsBundle", 11002), new MuseumItem("sp_greenroot", "Greenroot", "SpringCropsBundle", 11010), new MuseumItem("sp_carrot", "Carrot", "SpringCropsBundle", 11006), new MuseumItem("sp_kale", "Kale", "SpringCropsBundle", 11050), new MuseumItem("sp_lettuce", "Lettuce", "SpringCropsBundle", 11013), new MuseumItem("sp_cinnaberry", "Cinnaberry", "SpringCropsBundle", 11012), new MuseumItem("sp_pepper", "Pepper", "SpringCropsBundle", 11008), new MuseumItem("sp_shimmeroot", "Shimmeroot", "SpringCropsBundle", 11007) }); museumSection2.Bundles.Add(museumBundle10); MuseumBundle museumBundle11 = new MuseumBundle("FallCropsBundle", "Fall Crops", "hall_of_culture", "Crops harvested in fall."); museumBundle11.Items.AddRange(new MuseumItem[9] { new MuseumItem("fa_garlic", "Garlic", "FallCropsBundle", 11060), new MuseumItem("fa_yam", "Yam", "FallCropsBundle", 11047), new MuseumItem("fa_soda_pop_crop", "Soda Pop Crop", "FallCropsBundle", 11038), new MuseumItem("fa_fizzy_fruit", "Fizzy Fruit", "FallCropsBundle", 11039), new MuseumItem("fa_cranberry", "Cranberry", "FallCropsBundle", 11070), new MuseumItem("fa_barley", "Barley", "FallCropsBundle", 11045), new MuseumItem("fa_pumpkin", "Pumpkin", "FallCropsBundle", 11036), new MuseumItem("fa_ghost_pepper", "Ghost Pepper", "FallCropsBundle", 11049), new MuseumItem("fa_butternut", "Butternut", "FallCropsBundle", 11044) }); museumSection2.Bundles.Add(museumBundle11); MuseumBundle museumBundle12 = new MuseumBundle("NelvariTempleBooks", "Nel'Vari Temple Books", "hall_of_culture", "Mine book series from Sun Haven, Nel'Vari, and Withergate."); museumBundle12.Items.AddRange(new MuseumItem[15] { new MuseumItem("book_nivara_i", "Origins of the Grand Tree and Nivara, Book I", "NelvariTempleBooks", 6500, ItemRarity.Rare), new MuseumItem("book_nivara_ii", "Origins of the Grand Tree and Nivara, Book II", "NelvariTempleBooks", 6501, ItemRarity.Rare), new MuseumItem("book_nivara_iii", "Origins of the Grand Tree and Nivara, Book III", "NelvariTempleBooks", 6502, ItemRarity.Rare), new MuseumItem("book_nivara_iv", "Origins of the Grand Tree and Nivara, Book IV", "NelvariTempleBooks", 6503, ItemRarity.Rare), new MuseumItem("book_nivara_v", "Origins of the Grand Tree and Nivara, Book V", "NelvariTempleBooks", 6504, ItemRarity.Rare), new MuseumItem("book_elios_i", "Origins of Sun Haven and Elios, Book I", "NelvariTempleBooks", 6505, ItemRarity.Rare), new MuseumItem("book_elios_ii", "Origins of Sun Haven and Elios, Book II", "NelvariTempleBooks", 6506, ItemRarity.Rare), new MuseumItem("book_elios_iii", "Origins of Sun Haven and Elios, Book III", "NelvariTempleBooks", 6507, ItemRarity.Rare), new MuseumItem("book_elios_iv", "Origins of Sun Haven and Elios, Book IV", "NelvariTempleBooks", 6508, ItemRarity.Rare), new MuseumItem("book_elios_v", "Origins of Sun Haven and Elios, Book V", "NelvariTempleBooks", 6509, ItemRarity.Rare), new MuseumItem("book_dynus_i", "Origins of Dynus and Shadows, Book I", "NelvariTempleBooks", 6510, ItemRarity.Rare), new MuseumItem("book_dynus_ii", "Origins of Dynus and Shadows, Book II", "NelvariTempleBooks", 6511, ItemRarity.Rare), new MuseumItem("book_dynus_iii", "Origins of Dynus and Shadows, Book III", "NelvariTempleBooks", 6512, ItemRarity.Rare), new MuseumItem("book_dynus_iv", "Origins of Dynus and Shadows, Book IV", "NelvariTempleBooks", 6513, ItemRarity.Rare), new MuseumItem("book_dynus_v", "Origins of Dynus and Shadows, Book V", "NelvariTempleBooks", 6514, ItemRarity.Rare) }); museumSection2.Bundles.Add(museumBundle12); MuseumBundle museumBundle13 = new MuseumBundle("SummerCropsBundle", "Summer Crops", "hall_of_culture", "Crops harvested in summer."); museumBundle13.Items.AddRange(new MuseumItem[10] { new MuseumItem("su_armoranth", "Armoranth", "SummerCropsBundle", 11053), new MuseumItem("su_guava_berry", "Guava Berry", "SummerCropsBundle", 11035), new MuseumItem("su_beet", "Beet", "SummerCropsBundle", 12080), new MuseumItem("su_lemon", "Lemon", "SummerCropsBundle", 11052), new MuseumItem("su_chocoberry", "Chocoberry", "SummerCropsBundle", 11054), new MuseumItem("su_pineapple", "Pineapple", "SummerCropsBundle", 11056), new MuseumItem("su_pepper", "Pepper", "SummerCropsBundle", 11008), new MuseumItem("su_melon", "Melon", "SummerCropsBundle", 11057), new MuseumItem("su_stormelon", "Stormelon", "SummerCropsBundle", 11051), new MuseumItem("su_durian", "Durian", "SummerCropsBundle", 11062) }); museumSection2.Bundles.Add(museumBundle13); MuseumBundle museumBundle14 = new MuseumBundle("ForagingBundle", "Foraging", "hall_of_culture", "Foraged goods from trees and the beach."); museumBundle14.Items.AddRange(new MuseumItem[12] { new MuseumItem("fo_apple", "Apple", "ForagingBundle", 3044), new MuseumItem("fo_berry", "Berry", "ForagingBundle", 16500), new MuseumItem("fo_blueberry", "Blueberry", "ForagingBundle", 3046), new MuseumItem("fo_mushroom", "Mushroom", "ForagingBundle", 3001), new MuseumItem("fo_orange", "Orange", "ForagingBundle", 3045), new MuseumItem("fo_peach", "Peach", "ForagingBundle", 3048), new MuseumItem("fo_raspberry", "Raspberry", "ForagingBundle", 3049), new MuseumItem("fo_sand_dollar", "Sand Dollar", "ForagingBundle", 2102), new MuseumItem("fo_seaweed", "Seaweed", "ForagingBundle", 3002), new MuseumItem("fo_starfish", "Starfish", "ForagingBundle", 2103), new MuseumItem("fo_strawberry", "Strawberry", "ForagingBundle", 3047), new MuseumItem("fo_log", "Log", "ForagingBundle", 2002) }); museumSection2.Bundles.Add(museumBundle14); MuseumBundle museumBundle15 = new MuseumBundle("CombatBundle", "Combat", "hall_of_culture", "Monster trinkets and ancient swords."); museumBundle15.Items.AddRange(new MuseumItem[14] { new MuseumItem("co_leafie_trinket", "Leafie Trinket", "CombatBundle", 20103, ItemRarity.Uncommon), new MuseumItem("co_elite_leafie_trinket", "Elite Leafie Trinket", "CombatBundle", 20104, ItemRarity.Uncommon), new MuseumItem("co_centipillar_trinket", "Centipillar Trinket", "CombatBundle", 20105, ItemRarity.Uncommon), new MuseumItem("co_peppinch_green_trinket", "Peppinch - Green Trinket", "CombatBundle", 20106, ItemRarity.Uncommon), new MuseumItem("co_scorpepper_trinket", "Scorpepper Trinket", "CombatBundle", 20107, ItemRarity.Uncommon), new MuseumItem("co_elite_scorpepper_trinket", "Elite Scorpepper Trinket", "CombatBundle", 20108, ItemRarity.Uncommon), new MuseumItem("co_hat_crab_trinket", "Hat Crab Trinket", "CombatBundle", 20109, ItemRarity.Uncommon), new MuseumItem("co_floaty_crab_trinket", "Floaty Crab Trinket", "CombatBundle", 20110, ItemRarity.Uncommon), new MuseumItem("co_bucket_crab_trinket", "Bucket Crab Trinket", "CombatBundle", 20111, ItemRarity.Uncommon), new MuseumItem("co_umbrella_crab_trinket", "Umbrella Crab Trinket", "CombatBundle", 20112, ItemRarity.Uncommon), new MuseumItem("co_chimchuck_trinket", "Chimchuck Trinket", "CombatBundle", 20113, ItemRarity.Uncommon), new MuseumItem("co_ancient_sun_haven_sword", "Ancient Sun Haven Sword", "CombatBundle", 20100, ItemRarity.Rare), new MuseumItem("co_ancient_nelvarian_sword", "Ancient Nel'Varian Sword", "CombatBundle", 20101, ItemRarity.Rare), new MuseumItem("co_ancient_withergate_sword", "Ancient Withergate Sword", "CombatBundle", 20102, ItemRarity.Rare) }); museumSection2.Bundles.Add(museumBundle15); MuseumBundle museumBundle16 = new MuseumBundle("AlchemyBundle", "Alchemy", "hall_of_culture", "Combat potions from alchemy."); museumBundle16.Items.AddRange(new MuseumItem[11] { new MuseumItem("al_mana_potion", "Mana Potion", "AlchemyBundle", 3080), new MuseumItem("al_health_potion", "Health Potion", "AlchemyBundle", 3081), new MuseumItem("al_attack_potion", "Attack Potion", "AlchemyBundle", 3082), new MuseumItem("al_speed_potion", "Speed Potion", "AlchemyBundle", 3083), new MuseumItem("al_defense_potion", "Defense Potion", "AlchemyBundle", 3084), new MuseumItem("al_advanced_attack", "Advanced Attack Potion", "AlchemyBundle", 3085, ItemRarity.Uncommon), new MuseumItem("al_advanced_defense", "Advanced Defense Potion", "AlchemyBundle", 3086, ItemRarity.Uncommon), new MuseumItem("al_advanced_spell_damage", "Advanced Spell Damage Potion", "AlchemyBundle", 3087, ItemRarity.Uncommon), new MuseumItem("al_incredible_attack", "Incredible Attack Potion", "AlchemyBundle", 3767, ItemRarity.Rare), new MuseumItem("al_incredible_defense", "Incredible Defense Potion", "AlchemyBundle", 3768, ItemRarity.Rare), new MuseumItem("al_incredible_spell_damage", "Incredible Spell Damage Potion", "AlchemyBundle", 3766, ItemRarity.Rare) }); museumSection2.Bundles.Add(museumBundle16); MuseumBundle museumBundle17 = new MuseumBundle("ExplorationBundle", "Exploration", "hall_of_culture", "Rare drops from trees and exploration."); museumBundle17.Items.AddRange(new MuseumItem[10] { new MuseumItem("ex_petrified_log", "Petrified Log", "ExplorationBundle", 20200, ItemRarity.Uncommon), new MuseumItem("ex_phoenix_feather", "Phoenix Feather", "ExplorationBundle", 20201, ItemRarity.Uncommon), new MuseumItem("ex_fairy_wings", "Fairy Wings", "ExplorationBundle", 20202, ItemRarity.Uncommon), new MuseumItem("ex_griffon_egg", "Griffon Egg", "ExplorationBundle", 20203, ItemRarity.Uncommon), new MuseumItem("ex_mana_sap", "Mana Sap", "ExplorationBundle", 20204, ItemRarity.Uncommon), new MuseumItem("ex_pumice_stone", "Pumice Stone", "ExplorationBundle", 20205, ItemRarity.Uncommon), new MuseumItem("ex_mysterious_antler", "Mysterious Antler", "ExplorationBundle", 20206, ItemRarity.Uncommon), new MuseumItem("ex_dragon_fang", "Dragon Fang", "ExplorationBundle", 20207, ItemRarity.Uncommon), new MuseumItem("ex_monster_candy", "Monster Candy", "ExplorationBundle", 20208, ItemRarity.Uncommon), new MuseumItem("ex_unicorn_hair_tuft", "Unicorn Hair Tuft", "ExplorationBundle", 20209, ItemRarity.Uncommon) }); museumSection2.Bundles.Add(museumBundle17); MuseumBundle museumBundle18 = new MuseumBundle("WithergateFarmingBundle", "Withergate Farming", "hall_of_culture", "Crops from Withergate."); museumBundle18.Items.AddRange(new MuseumItem[8] { new MuseumItem("wg_kraken_kale", "Kraken Kale", "WithergateFarmingBundle", 11016), new MuseumItem("wg_tombmelon", "Tombmelon", "WithergateFarmingBundle", 11019), new MuseumItem("wg_suckerstem", "Suckerstem", "WithergateFarmingBundle", 11018), new MuseumItem("wg_razorstalk", "Razorstalk", "WithergateFarmingBundle", 11020), new MuseumItem("wg_snappy_plant", "Snappy Plant", "WithergateFarmingBundle", 11005), new MuseumItem("wg_moonplant", "Moonplant", "WithergateFarmingBundle", 11017), new MuseumItem("wg_eggplant", "Eggplant", "WithergateFarmingBundle", 11015), new MuseumItem("wg_demon_orb", "Demon Orb", "WithergateFarmingBundle", 11004) }); museumSection2.Bundles.Add(museumBundle18); MuseumBundle museumBundle19 = new MuseumBundle("NelvariFarmingBundle", "Nel'Vari Farming", "hall_of_culture", "Crops from Nel'Vari."); museumBundle19.Items.AddRange(new MuseumItem[11] { new MuseumItem("nv_acorn", "Acorn", "NelvariFarmingBundle", 11031), new MuseumItem("nv_rock_fruit", "Rock Fruit", "NelvariFarmingBundle", 11025), new MuseumItem("nv_water_fruit", "Water Fruit", "NelvariFarmingBundle", 11023), new MuseumItem("nv_fire_fruit", "Fire Fruit", "NelvariFarmingBundle", 11024), new MuseumItem("nv_walk_choy", "Walk Choy", "NelvariFarmingBundle", 11028), new MuseumItem("nv_wind_chime", "Wind Chime", "NelvariFarmingBundle", 11026), new MuseumItem("nv_shiiwalki_mushroom", "Shiiwalki Mushroom", "NelvariFarmingBundle", 11029), new MuseumItem("nv_dragon_fruit", "Dragon Fruit", "NelvariFarmingBundle", 11027), new MuseumItem("nv_mana_gem", "Mana Gem", "NelvariFarmingBundle", 11033), new MuseumItem("nv_cat_tail", "Cat Tail", "NelvariFarmingBundle", 11032), new MuseumItem("nv_indiglow", "Indiglow", "NelvariFarmingBundle", 11030) }); museumSection2.Bundles.Add(museumBundle19); list.Add(museumSection2); MuseumSection museumSection3 = new MuseumSection("aquarium", "Aquarium", "Fish and aquatic life from all waters and seasons."); MuseumBundle museumBundle20 = new MuseumBundle("FishingBundle", "Fishing (Relics)", "aquarium", "Rare fishing relics and treasures."); museumBundle20.Items.AddRange(new MuseumItem[11] { new MuseumItem("fishing_relic_20150", "Handmade Bobber", "FishingBundle", 20150, ItemRarity.Rare, "A handmade fishing bobber."), new MuseumItem("fishing_relic_20151", "Ancient Magic Staff", "FishingBundle", 20151, ItemRarity.Rare, "An ancient magic staff."), new MuseumItem("fishing_relic_20152", "Bronze Dragon Relic", "FishingBundle", 20152, ItemRarity.Rare, "A bronze dragon relic."), new MuseumItem("fishing_relic_20153", "Old Sword Hilt", "FishingBundle", 20153, ItemRarity.Rare, "An old sword hilt."), new MuseumItem("fishing_relic_20154", "Ancient Almari Totem", "FishingBundle", 20154, ItemRarity.Rare, "An ancient Almari totem."), new MuseumItem("fishing_relic_20155", "Ancient Angel Quill", "FishingBundle", 20155, ItemRarity.Rare, "An ancient angel quill."), new MuseumItem("fishing_relic_20156", "Ancient Elven Headdress", "FishingBundle", 20156, ItemRarity.Rare, "An ancient elven headdress."), new MuseumItem("fishing_relic_20157", "Ancient Naga Crook", "FishingBundle", 20157, ItemRarity.Rare, "An ancient Naga crook."), new MuseumItem("fishing_relic_20158", "Nel'Varian Runestone", "FishingBundle", 20158, ItemRarity.Rare, "A Nel'Varian runestone."), new MuseumItem("fishing_relic_20159", "Old Mayoral Painting", "FishingBundle", 20159, ItemRarity.Rare, "An old mayoral painting."), new MuseumItem("fishing_relic_20160", "Tentacle Monster Emblem", "FishingBundle", 20160, ItemRarity.Rare, "A tentacle monster emblem.") }); museumSection3.Bundles.Add(museumBundle20); MuseumBundle museumBundle21 = new MuseumBundle("MuseumAquariumBigTank", "Large Tank (Sun Haven)", "aquarium", "Year-round Sun Haven fish."); museumBundle21.Items.AddRange(new MuseumItem[26] { new MuseumItem("bt_pygmy_tuna", "Pygmy Tuna", "MuseumAquariumBigTank", 15023), new MuseumItem("bt_catfish", "Catfish", "MuseumAquariumBigTank", 15018), new MuseumItem("bt_gold_fish", "Gold Fish", "MuseumAquariumBigTank", 15008), new MuseumItem("bt_streamline_cod", "Streamline Cod", "MuseumAquariumBigTank", 15014), new MuseumItem("bt_salmon", "Salmon", "MuseumAquariumBigTank", 15085), new MuseumItem("bt_clown_fish", "Clown Fish", "MuseumAquariumBigTank", 15083), new MuseumItem("bt_black_bass", "Black Bass", "MuseumAquariumBigTank", 15084), new MuseumItem("bt_rainbow_trout", "Rainbow Trout", "MuseumAquariumBigTank", 15004), new MuseumItem("bt_popeye_goldfish", "Popeye Goldfish", "MuseumAquariumBigTank", 15082), new MuseumItem("bt_pufferfish", "Pufferfish", "MuseumAquariumBigTank", 15007), new MuseumItem("bt_ironhead_sturgeon", "Ironhead Sturgeon", "MuseumAquariumBigTank", 15024), new MuseumItem("bt_cuddlefish", "Cuddlefish", "MuseumAquariumBigTank", 15022), new MuseumItem("bt_lobster", "Lobster", "MuseumAquariumBigTank", 15088), new MuseumItem("bt_silver_carp", "Silver Carp", "MuseumAquariumBigTank", 15012), new MuseumItem("bt_tuna", "Tuna", "MuseumAquariumBigTank", 15087), new MuseumItem("bt_blunted_swordfish", "Blunted Swordfish", "MuseumAquariumBigTank", 15017), new MuseumItem("bt_ribbon_eel", "Ribbon Eel", "MuseumAquariumBigTank", 15089), new MuseumItem("bt_tiger_trout", "Tiger Trout", "MuseumAquariumBigTank", 15086), new MuseumItem("bt_eel", "Eel", "MuseumAquariumBigTank", 15002), new MuseumItem("bt_red_snapper", "Red Snapper", "MuseumAquariumBigTank", 15011), new MuseumItem("bt_carp", "Carp", "MuseumAquariumBigTank", 15010), new MuseumItem("bt_redeye_piranha", "Redeye Piranha", "MuseumAquariumBigTank", 15016), new MuseumItem("bt_angel_fish", "Angel Fish", "MuseumAquariumBigTank", 15005), new MuseumItem("bt_whitebelly_shark", "Whitebelly Shark", "MuseumAquariumBigTank", 15013), new MuseumItem("bt_koi_fish", "Koi Fish", "MuseumAquariumBigTank", 15090), new MuseumItem("bt_sandstone_fish", "Sandstone Fish", "MuseumAquariumBigTank", 2118) }); museumSection3.Bundles.Add(museumBundle21); MuseumBundle museumBundle22 = new MuseumBundle("MuseumAquariumSpring", "Spring Tank", "aquarium", "Spring season fish in Sun Haven."); museumBundle22.Items.AddRange(new MuseumItem[9] { new MuseumItem("aq_sp_butterflyfish", "Butterflyfish", "MuseumAquariumSpring", 15117), new MuseumItem("aq_sp_sunfish", "Sunfish", "MuseumAquariumSpring", 15116), new MuseumItem("aq_sp_flower_flounder", "Flower Flounder", "MuseumAquariumSpring", 15114), new MuseumItem("aq_sp_raincloud_ray", "Raincloud Ray", "MuseumAquariumSpring", 15118), new MuseumItem("aq_sp_floral_trout", "Floral Trout", "MuseumAquariumSpring", 15119), new MuseumItem("aq_sp_neon_tetra", "Neon Tetra", "MuseumAquariumSpring", 15121), new MuseumItem("aq_sp_seahorse", "Seahorse", "MuseumAquariumSpring", 15122), new MuseumItem("aq_sp_painted_egg", "Painted Egg", "MuseumAquariumSpring", 15123), new MuseumItem("aq_sp_tadpole", "Tadpole", "MuseumAquariumSpring", 15124) }); museumSection3.Bundles.Add(museumBundle22); MuseumBundle museumBundle23 = new MuseumBundle("MuseumAquariumSummer", "Summer Tank", "aquarium", "Summer season fish in Sun Haven."); museumBundle23.Items.AddRange(new MuseumItem[9] { new MuseumItem("aq_su_blazeel", "Blazeel", "MuseumAquariumSummer", 15104), new MuseumItem("aq_su_hearth_angler", "Hearth Angler", "MuseumAquariumSummer", 15106), new MuseumItem("aq_su_scorching_squid", "Scorching Squid", "MuseumAquariumSummer", 15107), new MuseumItem("aq_su_magma_star", "Magma Star", "MuseumAquariumSummer", 15108), new MuseumItem("aq_su_tinder_turtle", "Tinder Turtle", "MuseumAquariumSummer", 15109), new MuseumItem("aq_su_pyrelus", "Pyrelus", "MuseumAquariumSummer", 15110), new MuseumItem("aq_su_flame_ray", "Flame Ray", "MuseumAquariumSummer", 15111), new MuseumItem("aq_su_molten_slug", "Molten Slug", "MuseumAquariumSummer", 15112), new MuseumItem("aq_su_searback", "Searback", "MuseumAquariumSummer", 15113) }); museumSection3.Bundles.Add(museumBundle23); MuseumBundle museumBundle24 = new MuseumBundle("MuseumAquariumFall", "Fall Tank", "aquarium", "Fall season fish in Sun Haven."); museumBundle24.Items.AddRange(new MuseumItem[9] { new MuseumItem("aq_fa_coducopia", "Coducopia", "MuseumAquariumFall", 15125), new MuseumItem("aq_fa_king_salmon", "King Salmon", "MuseumAquariumFall", 15126), new MuseumItem("aq_fa_hayfish", "Hayfish", "MuseumAquariumFall", 15127), new MuseumItem("aq_fa_acorn_anchovy", "Acorn Anchovy", "MuseumAquariumFall", 15128), new MuseumItem("aq_fa_vampire_piranha", "Vampire Piranha", "MuseumAquariumFall", 15131), new MuseumItem("aq_fa_ghostfish", "Ghostfish", "MuseumAquariumFall", 15132), new MuseumItem("aq_fa_pumpkin_jelly", "Pumpkin Jelly", "MuseumAquariumFall", 15133), new MuseumItem("aq_fa_pirate_perch", "Pirate Perch", "MuseumAquariumFall", 15134), new MuseumItem("aq_fa_autumn_leaf_sole", "Autumn Leaf Sole", "MuseumAquariumFall", 15135) }); museumSection3.Bundles.Add(museumBundle24); MuseumBundle museumBundle25 = new MuseumBundle("MuseumAquariumWinter", "Winter Tank", "aquarium", "Winter season fish in Sun Haven."); museumBundle25.Items.AddRange(new MuseumItem[9] { new MuseumItem("aq_wi_frostfin", "Frostfin", "MuseumAquariumWinter", 15094), new MuseumItem("aq_wi_christmas_lightfish", "Christmas Lightfish", "MuseumAquariumWinter", 15095), new MuseumItem("aq_wi_holly_carp", "Holly Carp", "MuseumAquariumWinter", 15096), new MuseumItem("aq_wi_jingle_bass", "Jingle Bass", "MuseumAquariumWinter", 15097), new MuseumItem("aq_wi_frozen_tuna", "Frozen Tuna", "MuseumAquariumWinter", 15098), new MuseumItem("aq_wi_scarffish", "Scarffish", "MuseumAquariumWinter", 15099), new MuseumItem("aq_wi_heatfin", "Heatfin", "MuseumAquariumWinter", 15100), new MuseumItem("aq_wi_icicle_carp", "Icicle Carp", "MuseumAquariumWinter", 15101), new MuseumItem("aq_wi_blazing_herring", "Blazing Herring", "MuseumAquariumWinter", 15102) }); museumSection3.Bundles.Add(museumBundle25); MuseumBundle museumBundle26 = new MuseumBundle("MuseumAquariumNelvari", "Nel'Vari Tank", "aquarium", "Fish from Nel'Vari waters."); museumBundle26.Items.AddRange(new MuseumItem[14] { new MuseumItem("aq_nv_robed_parrotfish", "Robed Parrotfish", "MuseumAquariumNelvari", 15041), new MuseumItem("aq_nv_axolotl", "Axolotl", "MuseumAquariumNelvari", 15055), new MuseumItem("aq_nv_frilled_betta", "Frilled Betta", "MuseumAquariumNelvari", 15054), new MuseumItem("aq_nv_horsefish", "Horsefish", "MuseumAquariumNelvari", 15053), new MuseumItem("aq_nv_flamefish", "Flamefish", "MuseumAquariumNelvari", 15036), new MuseumItem("aq_nv_dragon_gulper", "Dragon Gulper", "MuseumAquariumNelvari", 15040), new MuseumItem("aq_nv_neapolitan_fish", "Neapolitan Fish", "MuseumAquariumNelvari", 15056), new MuseumItem("aq_nv_snobfish", "Snobfish", "MuseumAquariumNelvari", 15042), new MuseumItem("aq_nv_kelp_eel", "Kelp Eel", "MuseumAquariumNelvari", 15045), new MuseumItem("aq_nv_princely_frog", "Princely Frog", "MuseumAquariumNelvari", 15058), new MuseumItem("aq_nv_angelfin", "Angelfin", "MuseumAquariumNelvari", 15059), new MuseumItem("aq_nv_bubblefish", "Bubblefish", "MuseumAquariumNelvari", 15060), new MuseumItem("aq_nv_crystal_tetra", "Crystal Tetra", "MuseumAquariumNelvari", 15044), new MuseumItem("aq_nv_sky_ray", "Sky Ray", "MuseumAquariumNelvari", 15046) }); museumSection3.Bundles.Add(museumBundle26); MuseumBundle museumBundle27 = new MuseumBundle("MuseumAquariumWithergate", "Withergate Tank", "aquarium", "Fish from Withergate waters."); museumBundle27.Items.AddRange(new MuseumItem[22] { new MuseumItem("aq_wg_kraken", "Kraken", "MuseumAquariumWithergate", 15070), new MuseumItem("aq_wg_water_bear", "Water Bear", "MuseumAquariumWithergate", 15065), new MuseumItem("aq_wg_bonemouth_bass", "Bonemouth Bass", "MuseumAquariumWithergate", 15028), new MuseumItem("aq_wg_mummy_trout", "Mummy Trout", "MuseumAquariumWithergate", 15069), new MuseumItem("aq_wg_deadeye_shrimp", "Deadeye Shrimp", "MuseumAquariumWithergate", 15033), new MuseumItem("aq_wg_electric_eel", "Electric Eel", "MuseumAquariumWithergate", 15066), new MuseumItem("aq_wg_brain_jelly", "Brain Jelly", "MuseumAquariumWithergate", 15068), new MuseumItem("aq_wg_redfinned_pincher", "Redfinned Pincher", "MuseumAquariumWithergate", 15067), new MuseumItem("aq_wg_sea_bat", "Sea Bat", "MuseumAquariumWithergate", 15071), new MuseumItem("aq_wg_ghosthead_tuna", "Ghosthead Tuna", "MuseumAquariumWithergate", 15073), new MuseumItem("aq_wg_globfish", "Globfish", "MuseumAquariumWithergate", 15072), new MuseumItem("aq_wg_living_jelly", "Living Jelly", "MuseumAquariumWithergate", 15031), new MuseumItem("aq_wg_purrmaid", "Purrmaid", "MuseumAquariumWithergate", 15037), new MuseumItem("aq_wg_slime_leech", "Slime Leech", "MuseumAquariumWithergate", 15035), new MuseumItem("aq_wg_goblin_shark", "Goblin Shark", "MuseumAquariumWithergate", 15074), new MuseumItem("aq_wg_moonfish", "Moonfish", "MuseumAquariumWithergate", 15076), new MuseumItem("aq_wg_toothy_angler", "Toothy Angler", "MuseumAquariumWithergate", 15030), new MuseumItem("aq_wg_vampire_squid", "Vampire Squid", "MuseumAquariumWithergate", 15075), new MuseumItem("aq_wg_viperfish", "Viperfish", "MuseumAquariumWithergate", 15077), new MuseumItem("aq_wg_albino_squid", "Albino Squid", "MuseumAquariumWithergate", 15079), new MuseumItem("aq_wg_devilfin", "Devilfin", "MuseumAquariumWithergate", 15080), new MuseumItem("aq_wg_shadow_tuna", "Shadow Tuna", "MuseumAquariumWithergate", 15029) }); museumSection3.Bundles.Add(museumBundle27); list.Add(museumSection3); return list; } public static void ApplyResolvedAquariumFishIfNeeded() { } private static void AddPlaceholderBundle(MuseumSection section, string bundleId, string displayName, int slotCount) { MuseumBundle museumBundle = new MuseumBundle(bundleId, displayName, section.Id, $"Donate all {slotCount} items to complete this bundle."); for (int i = 1; i <= slotCount; i++) { museumBundle.Items.Add(new MuseumItem(bundleId + "_slot_" + i, "Item " + i, bundleId, -1, ItemRarity.Common, "Slot " + i + " (see in-game museum for exact item).")); } section.Bundles.Add(museumBundle); } public static List GetAllItems() { List list = new List(); foreach (MuseumSection allSection in GetAllSections()) { foreach (MuseumBundle bundle in allSection.Bundles) { list.AddRange(bundle.Items); } } return list; } public static List GetAllBundleIds() { List list = new List(); foreach (MuseumSection allSection in GetAllSections()) { foreach (MuseumBundle bundle in allSection.Bundles) { list.Add(bundle.Id); } } return list; } public static string GetProgressKeyForBundle(string bundleId) { foreach (MuseumSection allSection in GetAllSections()) { foreach (MuseumBundle bundle in allSection.Bundles) { if (bundle.Id == bundleId) { return bundle.Id; } } } return null; } public static List GetItemsInBundle(string bundleId) { foreach (MuseumSection allSection in GetAllSections()) { foreach (MuseumBundle bundle in allSection.Bundles) { if (bundle.Id == bundleId) { return bundle.Items; } } } return new List(); } public static MuseumBundle FindBundleByProgressKey(string progressKey) { foreach (MuseumSection allSection in GetAllSections()) { foreach (MuseumBundle bundle in allSection.Bundles) { if (bundle.Id == progressKey) { return bundle; } } } return null; } public static MuseumItem FindByGameItemId(int gameItemId) { foreach (MuseumSection allSection in GetAllSections()) { foreach (MuseumBundle bundle in allSection.Bundles) { foreach (MuseumItem item in bundle.Items) { if (item.GameItemId == gameItemId) { return item; } } } } return null; } public static MuseumItem FindByGameItemIdInBundle(int gameItemId, string bundleId) { if (string.IsNullOrEmpty(bundleId)) { return null; } foreach (MuseumItem item in GetItemsInBundle(bundleId)) { if (item.GameItemId == gameItemId) { return item; } } return null; } public static List<(int GameItemId, string Name)> GetResolvedAquariumItems(string bundleId) { List itemsInBundle = GetItemsInBundle(bundleId); if (itemsInBundle == null || itemsInBundle.Count == 0) { return null; } List<(int, string)> list = new List<(int, string)>(); foreach (MuseumItem item in itemsInBundle) { if (item.GameItemId > 0) { list.Add((item.GameItemId, item.Name)); } } if (list.Count <= 0) { return null; } return list; } public static MuseumItem FindById(string itemId) { foreach (MuseumSection allSection in GetAllSections()) { foreach (MuseumBundle bundle in allSection.Bundles) { foreach (MuseumItem item in bundle.Items) { if (item.Id == itemId) { return item; } } } } return null; } } [Serializable] public class MuseumSection { public string Id { get; set; } public string Name { get; set; } public string Description { get; set; } public List Bundles { get; set; } = new List(); public MuseumSection() { } public MuseumSection(string id, string name, string description = "") { Id = id; Name = name; Description = description; } } [Serializable] public class MuseumBundle { public string Id { get; set; } public string Name { get; set; } public string Description { get; set; } public string SectionId { get; set; } public List Items { get; set; } = new List(); public MuseumBundle() { } public MuseumBundle(string id, string name, string sectionId, string description = "") { Id = id; Name = name; SectionId = sectionId; Description = description; } } [Serializable] public class MuseumItem { public string Id { get; set; } public string Name { get; set; } public string BundleId { get; set; } public int GameItemId { get; set; } public string Description { get; set; } public ItemRarity Rarity { get; set; } public MuseumItem() { } public MuseumItem(string id, string name, string bundleId, int gameItemId, ItemRarity rarity = ItemRarity.Common, string description = "") { Id = id; Name = name; BundleId = bundleId; GameItemId = gameItemId; Rarity = rarity; Description = description; } } public enum ItemRarity { Common, Uncommon, Rare, Epic, Legendary } [Serializable] public class DonationData { public string CharacterName { get; set; } public HashSet DonatedItemIds { get; set; } = new HashSet(); public DateTime LastUpdated { get; set; } = DateTime.Now; public DonationData() { } public DonationData(string characterName) { CharacterName = characterName; } public bool HasDonated(string itemId) { return DonatedItemIds.Contains(itemId); } public bool HasDonated(MuseumItem item) { return HasDonated(item.Id); } public void MarkDonated(string itemId) { DonatedItemIds.Add(itemId); LastUpdated = DateTime.Now; } public void MarkDonated(MuseumItem item) { MarkDonated(item.Id); } public void UnmarkDonated(string itemId) { DonatedItemIds.Remove(itemId); LastUpdated = DateTime.Now; } } [Serializable] public class DonationDataWrapper { public string CharacterName; public List DonatedItemIds = new List(); public string LastUpdated; public DonationDataWrapper() { } public DonationDataWrapper(DonationData data) { CharacterName = data.CharacterName; DonatedItemIds = new List(data.DonatedItemIds); LastUpdated = data.LastUpdated.ToString("o"); } public DonationData ToData() { DonationData donationData = new DonationData { CharacterName = CharacterName, DonatedItemIds = new HashSet(DonatedItemIds) }; if (DateTime.TryParse(LastUpdated, out var result)) { donationData.LastUpdated = result; } return donationData; } } }