using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; 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; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SunhavenMods.Shared; using SunhavenTodo.Data; using SunhavenTodo.UI; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; 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("SunhavenTodo")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+42b06f35515a4a0432ef2920414ed2b88e58ab88")] [assembly: AssemblyProduct("SunhavenTodo")] [assembly: AssemblyTitle("SunhavenTodo")] [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 OvernightHookUtility { public static bool TryHookOvernightEvent(ref bool overnightHooked, ref UnityAction overnightCallback, UnityAction callback, Func singletonResolver, Action logInfo = null, Action logWarning = null) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Expected O, but got Unknown //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Expected O, but got Unknown if (overnightHooked) { return true; } try { Type type = AccessTools.TypeByName("Wish.DayCycle"); if (type != null) { FieldInfo fieldInfo = AccessTools.Field(type, "OnDayStart"); if (fieldInfo != null) { object? value = fieldInfo.GetValue(null); UnityAction val = (UnityAction)((value is UnityAction) ? value : null); overnightCallback = callback; if (val != null) { val = (UnityAction)Delegate.Remove((Delegate?)(object)val, (Delegate?)(object)overnightCallback); val = (UnityAction)Delegate.Combine((Delegate?)(object)val, (Delegate?)(object)overnightCallback); fieldInfo.SetValue(null, val); } else { fieldInfo.SetValue(null, overnightCallback); } overnightHooked = true; logInfo?.Invoke("Hooked into DayCycle.OnDayStart"); return true; } } Type type2 = AccessTools.TypeByName("Wish.UIHandler"); if (type2 == null) { return false; } object obj = singletonResolver?.Invoke(type2); if (obj == null) { return false; } FieldInfo fieldInfo2 = AccessTools.Field(type2, "OnCompleteOvernight"); if (fieldInfo2 == null) { return false; } object? value2 = fieldInfo2.GetValue(obj); UnityAction val2 = (UnityAction)((value2 is UnityAction) ? value2 : null); overnightCallback = callback; if (val2 != null) { val2 = (UnityAction)Delegate.Remove((Delegate?)(object)val2, (Delegate?)(object)overnightCallback); val2 = (UnityAction)Delegate.Combine((Delegate?)(object)val2, (Delegate?)(object)overnightCallback); fieldInfo2.SetValue(obj, val2); } else { fieldInfo2.SetValue(obj, overnightCallback); } overnightHooked = true; logInfo?.Invoke("Hooked into UIHandler.OnCompleteOvernight"); return true; } catch (Exception ex) { logWarning?.Invoke("Failed to hook overnight event: " + ex.Message); return false; } } } 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; } } internal static class MinimalJsonParser { internal static void WriteJsonString(StringBuilder sb, string value) { sb.Append('"'); if (value != null) { foreach (char c in value) { switch (c) { case '"': sb.Append("\\\""); break; case '\\': sb.Append("\\\\"); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; case '\b': sb.Append("\\b"); break; case '\f': sb.Append("\\f"); break; default: sb.Append(c); break; } } } sb.Append('"'); } internal static void SkipWhitespace(string json, ref int pos) { while (pos < json.Length && char.IsWhiteSpace(json[pos])) { pos++; } } internal static object ParseValue(string json, ref int pos) { SkipWhitespace(json, ref pos); if (pos >= json.Length) { return null; } char c = json[pos]; switch (c) { case '"': return ParseString(json, ref pos); case '{': return ParseObject(json, ref pos); case '[': return ParseArray(json, ref pos); case 't': return ParseLiteral(json, ref pos, "true", true); case 'f': return ParseLiteral(json, ref pos, "false", false); case 'n': return ParseLiteral(json, ref pos, "null", null); default: if (!char.IsDigit(c)) { return null; } goto case '-'; case '-': return ParseNumber(json, ref pos); } } internal static Dictionary ParseObject(string json, ref int pos) { SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != '{') { return null; } pos++; Dictionary dictionary = new Dictionary(); SkipWhitespace(json, ref pos); if (pos < json.Length && json[pos] == '}') { pos++; return dictionary; } while (pos < json.Length) { SkipWhitespace(json, ref pos); string text = ParseString(json, ref pos); if (text == null) { break; } SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != ':') { break; } pos++; SkipWhitespace(json, ref pos); dictionary[text] = ParseValue(json, ref pos); SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != ',') { break; } pos++; } SkipWhitespace(json, ref pos); if (pos < json.Length && json[pos] == '}') { pos++; } return dictionary; } internal static List ParseArray(string json, ref int pos) { SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != '[') { return null; } pos++; List list = new List(); SkipWhitespace(json, ref pos); if (pos < json.Length && json[pos] == ']') { pos++; return list; } while (pos < json.Length) { SkipWhitespace(json, ref pos); list.Add(ParseValue(json, ref pos)); SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != ',') { break; } pos++; } SkipWhitespace(json, ref pos); if (pos < json.Length && json[pos] == ']') { pos++; } return list; } internal static string ParseString(string json, ref int pos) { SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != '"') { return null; } pos++; StringBuilder stringBuilder = new StringBuilder(); while (pos < json.Length) { char c = json[pos]; if (c == '\\' && pos + 1 < json.Length) { pos++; switch (json[pos]) { case '"': stringBuilder.Append('"'); break; case '\\': stringBuilder.Append('\\'); break; case '/': stringBuilder.Append('/'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'u': { if (pos + 4 < json.Length && ushort.TryParse(json.Substring(pos + 1, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result)) { pos += 4; if (result >= 55296 && result <= 56319 && pos + 5 < json.Length && json[pos] == '\\' && json[pos + 1] == 'u' && ushort.TryParse(json.Substring(pos + 2, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result2) && result2 >= 56320 && result2 <= 57343) { stringBuilder.Append(char.ConvertFromUtf32(char.ConvertToUtf32((char)result, (char)result2))); pos += 6; } else { stringBuilder.Append((char)result); } } else { stringBuilder.Append('u'); } break; } default: stringBuilder.Append(json[pos]); break; } pos++; } else { if (c == '"') { pos++; return stringBuilder.ToString(); } stringBuilder.Append(c); pos++; } } return stringBuilder.ToString(); } internal static object ParseNumber(string json, ref int pos) { int num = pos; bool flag = false; if (pos < json.Length && json[pos] == '-') { pos++; } while (pos < json.Length && char.IsDigit(json[pos])) { pos++; } if (pos < json.Length && json[pos] == '.') { flag = true; pos++; while (pos < json.Length && char.IsDigit(json[pos])) { pos++; } } if (pos < json.Length && (json[pos] == 'e' || json[pos] == 'E')) { flag = true; pos++; if (pos < json.Length && (json[pos] == '+' || json[pos] == '-')) { pos++; } while (pos < json.Length && char.IsDigit(json[pos])) { pos++; } } string s = json.Substring(num, pos - num); if (flag && double.TryParse(s, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } if (!flag && long.TryParse(s, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { return result2; } return 0L; } internal static object ParseLiteral(string json, ref int pos, string literal, object result) { if (pos + literal.Length <= json.Length && json.Substring(pos, literal.Length) == literal) { pos += literal.Length; return result; } pos++; return null; } internal static int ToInt(object val) { if (val is long num) { return (int)num; } if (val is double num2) { return (int)num2; } if (val is int) { return (int)val; } return 0; } } } namespace SunhavenTodo { [BepInPlugin("com.azraelgodking.sunhaventodo", "Sunhaven Todo", "1.4.3")] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static UnityAction <0>__OnNewDay; } private static TodoManager _staticTodoManager; private static TodoSaveSystem _staticSaveSystem; private static TodoUI _staticTodoUI; private static TodoHUD _staticTodoHUD; private static GameObject _persistentRunner; private static PersistentRunner _persistentRunnerComponent; private static KeyCode _staticToggleKey = (KeyCode)116; private static bool _staticRequireCtrl = true; private static bool _staticAutoSave = true; private static float _staticAutoSaveInterval = 60f; private static bool _staticHUDEnabled = true; private static float _staticHUDPositionX = -1f; private static float _staticHUDPositionY = -1f; private static KeyCode _staticHUDToggleKey = (KeyCode)104; private static float _staticUIScale = 1f; private ConfigEntry _toggleKey; private ConfigEntry _requireCtrl; private ConfigEntry _autoSave; private ConfigEntry _autoSaveInterval; private ConfigEntry _hudEnabled; private ConfigEntry _hudPositionX; private ConfigEntry _hudPositionY; private ConfigEntry _hudToggleKey; private ConfigEntry _checkForUpdates; private ConfigEntry _uiScale; private TodoManager _todoManager; private TodoSaveSystem _saveSystem; private TodoUI _todoUI; private TodoHUD _todoHUD; private Harmony _harmony; private static float _lastAutoSaveTime; private bool _isDataLoaded; private string _loadedCharacterName; private bool _wasInMenuScene = true; private static bool _applicationQuitting; private static bool _overnightHooked; private static UnityAction _overnightCallback; 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 => _staticToggleKey; public static bool StaticRequireCtrl => _staticRequireCtrl; public static KeyCode StaticHUDToggleKey => _staticHUDToggleKey; public static bool StaticHUDEnabled => _staticHUDEnabled; public static string GetOpenListShortcutDisplay() { //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) KeyCode staticToggleKey = StaticToggleKey; string text = ((object)(KeyCode)(ref staticToggleKey)).ToString(); if (!StaticRequireCtrl) { return text; } return "Ctrl+" + text; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigFile = CreateNamedConfig(); ConfigFileHelper.ReplacePluginConfig((BaseUnityPlugin)(object)this, ConfigFile, (Action)Log.LogWarning); Log.LogInfo((object)"Loading Sunhaven Todo v1.4.3"); IconCache.Initialize(Log); BindConfiguration(); CreatePersistentRunner(); InitializeManagers(); ApplyPatches(); SceneManager.sceneLoaded += OnSceneLoaded; if (_checkForUpdates.Value) { VersionChecker.CheckForUpdate("com.azraelgodking.sunhaventodo", "1.4.3", Log, delegate(VersionChecker.VersionCheckResult result) { result.NotifyUpdateAvailable(Log); }); } Log.LogInfo((object)"Sunhaven Todo loaded successfully!"); } private void BindConfiguration() { //IL_0027: 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_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Expected O, but got Unknown _toggleKey = ConfigFile.Bind("Hotkeys", "ToggleKey", (KeyCode)116, "Key to toggle the Todo List window"); _staticToggleKey = _toggleKey.Value; _toggleKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) _staticToggleKey = _toggleKey.Value; }; _requireCtrl = ConfigFile.Bind("Hotkeys", "RequireCtrl", true, "Require Ctrl to be held when pressing the toggle key"); _staticRequireCtrl = _requireCtrl.Value; _requireCtrl.SettingChanged += delegate { _staticRequireCtrl = _requireCtrl.Value; }; _autoSave = ConfigFile.Bind("Saving", "AutoSave", true, "Automatically save the todo list periodically"); _staticAutoSave = _autoSave.Value; _autoSave.SettingChanged += delegate { _staticAutoSave = _autoSave.Value; }; _autoSaveInterval = ConfigFile.Bind("Saving", "AutoSaveInterval", 60f, "Auto-save interval in seconds"); _staticAutoSaveInterval = _autoSaveInterval.Value; _autoSaveInterval.SettingChanged += delegate { _staticAutoSaveInterval = _autoSaveInterval.Value; }; _hudEnabled = ConfigFile.Bind("HUD", "Enabled", true, "Show the movable HUD panel with top 5 urgent tasks"); _staticHUDEnabled = _hudEnabled.Value; _hudEnabled.SettingChanged += delegate { _staticHUDEnabled = _hudEnabled.Value; _staticTodoHUD?.SetEnabled(_staticHUDEnabled); }; _hudPositionX = ConfigFile.Bind("HUD", "PositionX", -1f, "HUD X position (-1 for default)"); _staticHUDPositionX = _hudPositionX.Value; _hudPositionY = ConfigFile.Bind("HUD", "PositionY", -1f, "HUD Y position (-1 for default)"); _staticHUDPositionY = _hudPositionY.Value; _hudToggleKey = ConfigFile.Bind("Hotkeys", "HUDToggleKey", (KeyCode)104, "Key to toggle the HUD panel (with Ctrl if RequireCtrl is enabled)"); _staticHUDToggleKey = _hudToggleKey.Value; _hudToggleKey.SettingChanged += delegate { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) _staticHUDToggleKey = _hudToggleKey.Value; }; _checkForUpdates = ConfigFile.Bind("Updates", "CheckForUpdates", true, "Check for mod updates on startup"); _uiScale = ConfigFile.Bind("Display", "UIScale", 1f, new ConfigDescription("Scale factor for the Todo list and HUD (1.0 = default, 1.5 = 50% larger)", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 2.5f), Array.Empty())); _staticUIScale = Mathf.Clamp(_uiScale.Value, 0.5f, 2.5f); _uiScale.SettingChanged += delegate { _staticUIScale = Mathf.Clamp(_uiScale.Value, 0.5f, 2.5f); _staticTodoUI?.SetScale(_staticUIScale); _staticTodoHUD?.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, "SunhavenTodo.cfg"); string text2 = Path.Combine(Paths.ConfigPath, "com.azraelgodking.sunhaventodo.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 SunhavenTodo.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("SunhavenTodo_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _persistentRunnerComponent = _persistentRunner.AddComponent(); Log.LogInfo((object)"[PersistentRunner] Created"); } } private void InitializeManagers() { _todoManager = new TodoManager(); _staticTodoManager = _todoManager; _saveSystem = new TodoSaveSystem(_todoManager); _staticSaveSystem = _saveSystem; _todoManager.OnTodosChanged += OnTodosChanged; } 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown _harmony = new Harmony("com.azraelgodking.sunhaventodo"); try { Type type = AccessTools.TypeByName("Wish.Player"); if (type != null) { MethodInfo methodInfo = AccessTools.Method(type, "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); Log.LogInfo((object)"Applied player initialization patch"); } } } catch (Exception ex) { Log.LogWarning((object)("Failed to apply patches: " + ex.Message)); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (((Scene)(ref scene)).name == "MainMenu" || ((Scene)(ref scene)).name == "Bootstrap") { _overnightHooked = false; _overnightCallback = null; if (!_wasInMenuScene) { SaveData(); PlayerPatches.ResetForMenu(); } _wasInMenuScene = true; } else { _wasInMenuScene = false; EnsureUIComponentsExist(); } } 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_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown 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("SunhavenTodo_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)_staticTodoUI == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)"[EnsureUI] Recreating TodoUI..."); } GameObject val = new GameObject("SunhavenTodo_UI"); Object.DontDestroyOnLoad((Object)val); _staticTodoUI = val.AddComponent(); _staticTodoUI.Initialize(_staticTodoManager); _staticTodoUI.SetScale(_staticUIScale); ManualLogSource log4 = Log; if (log4 != null) { log4.LogInfo((object)"[EnsureUI] TodoUI recreated"); } } if ((Object)(object)_staticTodoHUD == (Object)null) { ManualLogSource log5 = Log; if (log5 != null) { log5.LogInfo((object)"[EnsureUI] Recreating TodoHUD..."); } GameObject val2 = new GameObject("SunhavenTodo_HUD"); Object.DontDestroyOnLoad((Object)val2); _staticTodoHUD = val2.AddComponent(); _staticTodoHUD.Initialize(_staticTodoManager); _staticTodoHUD.SetScale(_staticUIScale); _staticTodoHUD.SetEnabled(_staticHUDEnabled); if (_staticHUDPositionX >= 0f && _staticHUDPositionY >= 0f) { _staticTodoHUD.SetPosition(_staticHUDPositionX, _staticHUDPositionY); } _staticTodoHUD.OnPositionChanged = delegate(float x, float y) { _staticHUDPositionX = x; _staticHUDPositionY = y; Plugin instance = Instance; if (instance != null) { ConfigEntry hudPositionX = instance._hudPositionX; if (hudPositionX != null) { ((ConfigEntryBase)hudPositionX).SetSerializedValue(x.ToString()); } } Plugin instance2 = Instance; if (instance2 != null) { ConfigEntry hudPositionY = instance2._hudPositionY; if (hudPositionY != null) { ((ConfigEntryBase)hudPositionY).SetSerializedValue(y.ToString()); } } }; ManualLogSource log6 = Log; if (log6 != null) { log6.LogInfo((object)"[EnsureUI] TodoHUD recreated"); } } if ((Object)(object)Instance != (Object)null) { Instance._todoUI = _staticTodoUI; Instance._todoHUD = _staticTodoHUD; } } catch (Exception ex) { ManualLogSource log7 = Log; if (log7 != null) { log7.LogError((object)("[EnsureUI] Error: " + ex.Message)); } } } private void OnTodosChanged() { _lastAutoSaveTime = Time.unscaledTime - _staticAutoSaveInterval + 5f; } public void LoadDataForCharacter(string characterName) { if (string.IsNullOrEmpty(characterName)) { Log.LogWarning((object)"Cannot load data: No character name"); return; } if (_isDataLoaded && _loadedCharacterName != characterName) { SaveData(); } TodoListData data = _staticSaveSystem.Load(characterName); _staticTodoManager.LoadForCharacter(characterName, data); _isDataLoaded = true; _loadedCharacterName = characterName; Log.LogInfo((object)("Loaded todo list for character: " + characterName)); TryHookOvernight(); } public static void SaveData() { if (_staticTodoManager != null && _staticTodoManager.IsDirty) { _staticSaveSystem?.Save(); } } public static void TryHookOvernight() { //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_0025: Expected O, but got Unknown object obj = <>O.<0>__OnNewDay; if (obj == null) { UnityAction val = OnNewDay; <>O.<0>__OnNewDay = val; obj = (object)val; } OvernightHookUtility.TryHookOvernightEvent(ref _overnightHooked, ref _overnightCallback, (UnityAction)obj, delegate(Type type) { try { Type type2 = AccessTools.TypeByName("Wish.SingletonBehaviour`1"); if (type2 != null) { return type2.MakeGenericType(type).GetProperty("Instance")?.GetValue(null); } } catch (Exception) { } return null; }, delegate(string msg) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)msg); } }, delegate(string msg) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)msg); } }); } private static void OnNewDay() { if (_staticTodoManager == null) { return; } _staticTodoManager.ResetRecurringTodos(RecurInterval.Daily); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[Todo] Reset daily recurring tasks."); } try { Type type = AccessTools.TypeByName("Wish.DayCycle"); if (type != null) { PropertyInfo propertyInfo = AccessTools.Property(type, "MonthDay"); int num = ((propertyInfo != null) ? ((int)propertyInfo.GetValue(null)) : 0); if (num > 0) { if ((num - 1) % 7 == 0) { _staticTodoManager.ResetRecurringTodos(RecurInterval.Weekly); ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"[Todo] Reset weekly recurring tasks."); } } if (num == 1) { _staticTodoManager.ResetRecurringTodos(RecurInterval.Seasonal); ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)"[Todo] Reset seasonal recurring tasks."); } } } } } catch (Exception) { } SaveData(); } public static void ToggleUI() { EnsureUIComponentsExist(); _staticTodoUI?.Toggle(); } public static void ShowUI() { EnsureUIComponentsExist(); _staticTodoUI?.Show(); } public static void HideUI() { _staticTodoUI?.Hide(); } public static TodoManager GetTodoManager() { return _staticTodoManager; } public static TodoUI GetTodoUI() { return _staticTodoUI; } public static TodoHUD GetTodoHUD() { return _staticTodoHUD; } public static void ToggleHUD() { EnsureUIComponentsExist(); _staticTodoHUD?.Toggle(); } public static void ShowHUD() { EnsureUIComponentsExist(); if (_staticHUDEnabled && !((Object)(object)_staticTodoHUD == (Object)null)) { _staticTodoHUD.SetEnabled(enabled: true); } } internal static void TickAutoSave() { if (_staticAutoSave && _staticTodoManager != null && _staticTodoManager.IsDirty && !(Time.unscaledTime - _lastAutoSaveTime < _staticAutoSaveInterval)) { SaveData(); _lastAutoSaveTime = Time.unscaledTime; } } private void OnDestroy() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) SceneManager.sceneLoaded -= OnSceneLoaded; if (_todoManager != null) { _todoManager.OnTodosChanged -= OnTodosChanged; } 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")) { Log.LogInfo((object)("Plugin OnDestroy during expected teardown (scene: " + text + ")")); } else { Log.LogWarning((object)("[CRITICAL] Plugin OnDestroy outside expected teardown (scene: " + text + ")")); } SaveData(); } private void OnApplicationQuit() { _applicationQuitting = true; SaveData(); } } public class PersistentRunner : MonoBehaviour { private void Update() { CheckHotkeys(); Plugin.TickAutoSave(); } private void CheckHotkeys() { //IL_0045: 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) TodoUI todoUI = Plugin.GetTodoUI(); bool flag = (Object)(object)todoUI != (Object)null && todoUI.IsVisible; if (!TextInputFocusGuard.ShouldDeferModHotkeys(Plugin.Log)) { bool flag2 = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); bool keyDown = Input.GetKeyDown(Plugin.StaticToggleKey); bool keyDown2 = Input.GetKeyDown(Plugin.StaticHUDToggleKey); if (!flag && keyDown && flag2 == Plugin.StaticRequireCtrl) { Plugin.ToggleUI(); } if (keyDown2 && flag2 == Plugin.StaticRequireCtrl) { Plugin.ToggleHUD(); } } } 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 PlayerPatches { private static bool _isDataLoaded; private static string _loadedCharacterName; public static void OnPlayerInitialized(object __instance) { try { Plugin.EnsureUIComponentsExist(); string currentCharacterName = GetCurrentCharacterName(__instance); if (_isDataLoaded && _loadedCharacterName != currentCharacterName) { Plugin.SaveData(); ResetState(); } if (!string.IsNullOrEmpty(currentCharacterName)) { Plugin.Instance?.LoadDataForCharacter(currentCharacterName); _isDataLoaded = true; _loadedCharacterName = currentCharacterName; return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Skipping todo load because character name is unavailable."); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnPlayerInitialized: " + ex.Message)); } } } private static string GetCurrentCharacterName(object player) { try { Type type = AccessTools.TypeByName("Wish.GameSave"); if (type != null) { PropertyInfo propertyInfo = AccessTools.Property(type, "CurrentCharacter"); if (propertyInfo != null) { object value = propertyInfo.GetValue(null); if (value != null) { PropertyInfo propertyInfo2 = AccessTools.Property(value.GetType(), "characterName"); if (propertyInfo2 != null) { string text = propertyInfo2.GetValue(value) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } } object obj = AccessTools.Property(type, "Instance")?.GetValue(null); if (obj != null) { object obj2 = AccessTools.Property(type, "CurrentSave")?.GetValue(obj); if (obj2 != null) { object obj3 = AccessTools.Property(obj2.GetType(), "characterData")?.GetValue(obj2); if (obj3 != null) { string text2 = AccessTools.Property(obj3.GetType(), "characterName")?.GetValue(obj3) as string; if (!string.IsNullOrEmpty(text2)) { return text2; } } } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Failed to get character name: " + ex.Message)); } } if (!string.IsNullOrEmpty(_loadedCharacterName)) { return _loadedCharacterName; } return null; } private static void ResetState() { _isDataLoaded = false; _loadedCharacterName = null; } internal static void ResetForMenu() { ResetState(); } } public static class PluginInfo { public const string PLUGIN_GUID = "com.azraelgodking.sunhaventodo"; public const string PLUGIN_NAME = "Sunhaven Todo"; public const string PLUGIN_VERSION = "1.4.3"; } } namespace SunhavenTodo.UI { public class TodoHUD : MonoBehaviour { private const int WINDOW_ID = 98766; private const float BASE_WINDOW_WIDTH = 280f; private const float BASE_MIN_HEIGHT = 100f; private const float BASE_MAX_HEIGHT = 300f; private const float BASE_HEADER_HEIGHT = 28f; private const float BASE_ITEM_HEIGHT = 24f; private const float BASE_ICON_SIZE = 16f; private const int MAX_ITEMS = 5; private float _scale = 1f; private TodoManager _manager; private bool _isEnabled = true; private Rect _windowRect; public Action OnPositionChanged; private List _cachedItems = new List(); private float _lastUpdateTime; private const float UPDATE_INTERVAL = 1f; private readonly Color _parchmentLight = new Color(0.96f, 0.93f, 0.86f, 0.95f); private readonly Color _parchment = new Color(0.92f, 0.87f, 0.78f, 0.95f); private readonly Color _parchmentDark = new Color(0.85f, 0.78f, 0.65f, 0.95f); private readonly Color _woodDark = new Color(0.35f, 0.25f, 0.15f); private readonly Color _woodMedium = new Color(0.5f, 0.38f, 0.25f); private readonly Color _goldRich = new Color(0.85f, 0.68f, 0.2f); private readonly Color _goldBright = new Color(0.95f, 0.8f, 0.3f); private readonly Color _textDark = new Color(0.25f, 0.2f, 0.15f); private readonly Color _borderDark = new Color(0.4f, 0.3f, 0.2f, 0.8f); private readonly Dictionary _priorityColors = new Dictionary { { TodoPriority.Low, new Color(0.5f, 0.6f, 0.7f) }, { TodoPriority.Normal, new Color(0.45f, 0.55f, 0.45f) }, { TodoPriority.High, new Color(0.85f, 0.65f, 0.25f) }, { TodoPriority.Urgent, new Color(0.8f, 0.3f, 0.25f) } }; private readonly Dictionary _categoryColors = new Dictionary { { TodoCategory.General, new Color(0.55f, 0.5f, 0.45f) }, { TodoCategory.Farming, new Color(0.45f, 0.65f, 0.35f) }, { TodoCategory.Mining, new Color(0.5f, 0.45f, 0.55f) }, { TodoCategory.Fishing, new Color(0.4f, 0.6f, 0.75f) }, { TodoCategory.Combat, new Color(0.75f, 0.35f, 0.35f) }, { TodoCategory.Crafting, new Color(0.65f, 0.55f, 0.4f) }, { TodoCategory.Social, new Color(0.7f, 0.5f, 0.6f) }, { TodoCategory.Quests, new Color(0.8f, 0.7f, 0.3f) }, { TodoCategory.Collection, new Color(0.55f, 0.7f, 0.65f) } }; private bool _stylesInitialized; private GUIStyle _windowStyle; private GUIStyle _headerStyle; private GUIStyle _itemStyle; private GUIStyle _priorityStyle; private GUIStyle _categoryStyle; private GUIStyle _titleStyle; private GUIStyle _emptyStyle; private GUIStyle _closeButtonStyle; private Texture2D _windowBackground; private Texture2D _headerBackground; private Texture2D _itemEven; private Texture2D _itemOdd; private Texture2D _closeBtnNormal; private Texture2D _closeBtnHover; private Texture2D _closeBtnActive; private float WindowWidth => 280f * _scale; private float MinHeight => 100f * _scale; private float MaxHeight => 300f * _scale; private float HeaderHeight => 28f * _scale; private float ItemHeight => 24f * _scale; private float IconSize => 16f * _scale; public bool IsEnabled => _isEnabled; 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 Initialize(TodoManager manager) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) _manager = manager; _windowRect = new Rect((float)Screen.width - WindowWidth - Scaled(20f), Scaled(100f), WindowWidth, MinHeight); if (_manager != null) { _manager.OnTodosChanged += RefreshCache; _manager.OnDataLoaded += RefreshCache; } } public void SetPosition(float x, float y) { ((Rect)(ref _windowRect)).x = Mathf.Clamp(x, 0f, (float)Screen.width - ((Rect)(ref _windowRect)).width); ((Rect)(ref _windowRect)).y = Mathf.Clamp(y, 0f, (float)Screen.height - ((Rect)(ref _windowRect)).height); } public (float x, float y) GetPosition() { return (((Rect)(ref _windowRect)).x, ((Rect)(ref _windowRect)).y); } public void SetEnabled(bool enabled) { _isEnabled = enabled; } public void SetScale(float scale) { _scale = Mathf.Clamp(scale, 0.5f, 2.5f); _stylesInitialized = false; } public void Toggle() { _isEnabled = !_isEnabled; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("TodoHUD toggled: " + (_isEnabled ? "ON" : "OFF"))); } } private void Update() { if (_isEnabled && _manager != null && Time.unscaledTime - _lastUpdateTime > 1f) { RefreshCache(); _lastUpdateTime = Time.unscaledTime; } } private void RefreshCache() { if (_manager != null) { _cachedItems = (from t in _manager.GetActiveTodos() orderby (int)t.Priority descending, t.CreatedAt descending select t).Take(5).ToList(); } } private void OnGUI() { //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown //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) if (!_isEnabled || _manager == null) { return; } TodoUI todoUI = Plugin.GetTodoUI(); if (((Object)(object)todoUI != (Object)null && todoUI.IsVisible) || string.IsNullOrEmpty(_manager.CurrentCharacter)) { return; } InitializeStyles(); float num = HeaderHeight + Scaled(8f); if (_cachedItems.Count == 0) { num += Scaled(40f); } else { for (int i = 0; i < _cachedItems.Count; i++) { num += GetItemRowHeight(_cachedItems[i]); } num += Scaled(4f); } ((Rect)(ref _windowRect)).width = WindowWidth; ((Rect)(ref _windowRect)).height = Mathf.Clamp(num, MinHeight, MaxHeight); Rect windowRect = _windowRect; GUI.depth = -500; _windowRect = GUI.Window(98766, _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); ((Rect)(ref _windowRect)).x = Mathf.Clamp(((Rect)(ref _windowRect)).x, 0f, (float)Screen.width - ((Rect)(ref _windowRect)).width); ((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, 0f, (float)Screen.height - ((Rect)(ref _windowRect)).height); if (Math.Abs(((Rect)(ref _windowRect)).x - ((Rect)(ref windowRect)).x) > 0.1f || Math.Abs(((Rect)(ref _windowRect)).y - ((Rect)(ref windowRect)).y) > 0.1f) { OnPositionChanged?.Invoke(((Rect)(ref _windowRect)).x, ((Rect)(ref _windowRect)).y); } } private void DrawWindow(int windowId) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(Array.Empty()); DrawHeader(); DrawItems(); GUILayout.EndVertical(); float num = Scaled(42f); GUI.DragWindow(new Rect(0f, 0f, Mathf.Max(Scaled(48f), ((Rect)(ref _windowRect)).width - num), HeaderHeight)); } private void DrawHeader() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: 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_00f9: Expected O, but got Unknown Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref _windowRect)).width, HeaderHeight); if ((Object)(object)_headerBackground != (Object)null) { GUI.DrawTexture(val, (Texture)(object)_headerBackground); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Space(Scaled(8f)); int num = _manager?.GetActiveTodos().Count() ?? 0; GUILayout.Label($"Todo ({num})", _headerStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(HeaderHeight) }); GUILayout.FlexibleSpace(); GUIStyle val2 = new GUIStyle(_headerStyle) { fontSize = ScaledFont(9), fontStyle = (FontStyle)2 }; val2.normal.textColor = new Color(_textDark.r, _textDark.g, _textDark.b, 0.5f); GUIStyle val3 = val2; GUILayout.Label(Plugin.GetOpenListShortcutDisplay() + " to open", val3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(HeaderHeight) }); GUILayout.FlexibleSpace(); GUILayout.Label("drag to move", val3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(HeaderHeight) }); GUILayout.Space(Scaled(4f)); if (GUILayout.Button("X", _closeButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(26f)), GUILayout.Height(Scaled(22f)) })) { _isEnabled = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"TodoHUD hidden via close button (open Todo list → Sticky, or use HUD toggle hotkey)"); } } GUILayout.Space(Scaled(8f)); GUILayout.EndHorizontal(); } private void DrawItems() { GUILayout.Space(Scaled(4f)); if (_cachedItems.Count == 0) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("No active tasks", _emptyStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } else { for (int i = 0; i < _cachedItems.Count; i++) { DrawItem(_cachedItems[i], i); } } GUILayout.Space(Scaled(4f)); } private void DrawItem(TodoItem item, int index) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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) //IL_0050: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_00ea: Expected O, but got Unknown //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: 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) //IL_0135: Expected O, but got Unknown //IL_017e: 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_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Expected O, but got Unknown //IL_0235: Unknown result type (might be due to invalid IL or missing references) Texture2D background = ((index % 2 == 0) ? _itemEven : _itemOdd); string text = (string.IsNullOrWhiteSpace(item.Title) ? "(No title)" : item.Title); GUIStyle val = new GUIStyle(_titleStyle) { wordWrap = true, clipping = (TextClipping)0, alignment = (TextAnchor)0 }; float titleAvailableWidth = GetTitleAvailableWidth(item); float num = Mathf.Ceil(val.CalcHeight(new GUIContent(text), titleAvailableWidth)); float num2 = Mathf.Max(ItemHeight, num + Scaled(6f)); GUIStyle val2 = new GUIStyle(_itemStyle); val2.normal.background = background; val2.padding = new RectOffset(ScaledInt(6f), ScaledInt(6f), ScaledInt(2f), ScaledInt(2f)); GUILayout.BeginHorizontal(val2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(num2) }); GUILayout.Space(Scaled(6f)); Color value; Color textColor = (_priorityColors.TryGetValue(item.Priority, out value) ? value : _textDark); GUIStyle val3 = new GUIStyle(_priorityStyle); val3.normal.textColor = textColor; GUIStyle val4 = val3; GUILayout.Label(GetPriorityIcon(item.Priority), val4, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(16f)) }); Color value2; Color textColor2 = (_categoryColors.TryGetValue(item.Category, out value2) ? value2 : _textDark); GUIStyle val5 = new GUIStyle(_categoryStyle); val5.normal.textColor = textColor2; GUIStyle val6 = val5; GUILayout.Label("[" + GetCategoryShort(item.Category) + "]", val6, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(36f)) }); GUILayout.Space(Scaled(4f)); if (item.IconItemId > 0) { Texture2D icon = IconCache.GetIcon(item.IconItemId); if ((Object)(object)icon != (Object)null) { GUI.DrawTexture(GUILayoutUtility.GetRect(IconSize, IconSize, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(IconSize), GUILayout.Height(IconSize) }), (Texture)(object)icon, (ScaleMode)2); } else { GUILayout.Space(IconSize); } GUILayout.Space(Scaled(4f)); } GUILayout.Label(text, val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(titleAvailableWidth), GUILayout.MinHeight(num) }); GUILayout.FlexibleSpace(); GUILayout.Space(Scaled(6f)); GUILayout.EndHorizontal(); } private float GetItemRowHeight(TodoItem item) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown if (item == null) { return ItemHeight; } string text = (string.IsNullOrWhiteSpace(item.Title) ? "(No title)" : item.Title); GUIStyle val = new GUIStyle(_titleStyle) { wordWrap = true, clipping = (TextClipping)0, alignment = (TextAnchor)0 }; float titleAvailableWidth = GetTitleAvailableWidth(item); float num = Mathf.Ceil(val.CalcHeight(new GUIContent(text), titleAvailableWidth)); return Mathf.Max(ItemHeight, num + Scaled(6f)); } private float GetTitleAvailableWidth(TodoItem item) { float num = Scaled(68f); if (item != null && item.IconItemId > 0) { num += IconSize + Scaled(4f); } return Mathf.Max(Scaled(100f), WindowWidth - num); } private string GetPriorityIcon(TodoPriority priority) { return priority switch { TodoPriority.Low => "-", TodoPriority.Normal => "o", TodoPriority.High => "!", TodoPriority.Urgent => "!!", _ => "o", }; } private string GetCategoryShort(TodoCategory category) { return category switch { TodoCategory.General => "GEN", TodoCategory.Farming => "FRM", TodoCategory.Mining => "MIN", TodoCategory.Fishing => "FSH", TodoCategory.Combat => "CMB", TodoCategory.Crafting => "CRF", TodoCategory.Social => "SOC", TodoCategory.Quests => "QST", TodoCategory.Collection => "COL", _ => "GEN", }; } private void InitializeStyles() { if (!_stylesInitialized) { CreateTextures(); CreateStyles(); _stylesInitialized = true; } } private void CreateTextures() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) _windowBackground = MakeParchmentTexture(16, 64, _parchment, _parchmentLight, _borderDark, 2); _headerBackground = MakeGradientTex(8, Mathf.Max(1, ScaledInt(HeaderHeight)), _parchmentDark, _parchment); _itemEven = MakeTex(1, 1, new Color(_parchmentLight.r, _parchmentLight.g, _parchmentLight.b, 0.3f)); _itemOdd = MakeTex(1, 1, new Color(_parchment.r, _parchment.g, _parchment.b, 0.3f)); _closeBtnNormal = MakeTex(2, 2, _parchmentDark); _closeBtnHover = MakeTex(2, 2, new Color(_goldRich.r * 0.85f, _goldRich.g * 0.85f, _goldRich.b * 0.85f, 1f)); _closeBtnActive = MakeTex(2, 2, _woodMedium); } private void CreateStyles() { //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_0017: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_009b: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_010a: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Expected O, but got Unknown //IL_016b: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_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_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Expected O, but got Unknown //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: 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_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Expected O, but got Unknown //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Expected O, but got Unknown //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_021f: 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_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Expected O, but got Unknown //IL_02ad: Expected O, but got Unknown //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02fb: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0362: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Expected O, but got Unknown //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03a3: Expected O, but got Unknown //IL_03a8: Expected O, but got Unknown GUIStyle val = new GUIStyle(); val.normal.background = _windowBackground; val.normal.textColor = _textDark; val.padding = new RectOffset(ScaledInt(4f), ScaledInt(4f), ScaledInt(4f), ScaledInt(4f)); val.border = new RectOffset(ScaledInt(4f), ScaledInt(4f), ScaledInt(4f), ScaledInt(4f)); _windowStyle = val; GUIStyle val2 = new GUIStyle { fontSize = ScaledFont(13), fontStyle = (FontStyle)1 }; val2.normal.textColor = _woodDark; val2.alignment = (TextAnchor)3; val2.padding = new RectOffset(ScaledInt(2f), ScaledInt(2f), ScaledInt(2f), ScaledInt(2f)); _headerStyle = val2; GUIStyle val3 = new GUIStyle { fontSize = ScaledFont(11) }; val3.normal.textColor = _textDark; val3.padding = new RectOffset(ScaledInt(2f), ScaledInt(2f), ScaledInt(2f), ScaledInt(2f)); _itemStyle = val3; GUIStyle val4 = new GUIStyle { fontSize = ScaledFont(11), fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; val4.normal.textColor = _textDark; _priorityStyle = val4; GUIStyle val5 = new GUIStyle { fontSize = ScaledFont(8), fontStyle = (FontStyle)1, alignment = (TextAnchor)3 }; val5.normal.textColor = _textDark; _categoryStyle = val5; GUIStyle val6 = new GUIStyle { fontSize = ScaledFont(11) }; val6.normal.textColor = _textDark; val6.alignment = (TextAnchor)3; val6.clipping = (TextClipping)0; val6.wordWrap = true; _titleStyle = val6; GUIStyle val7 = new GUIStyle { fontSize = ScaledFont(11), fontStyle = (FontStyle)2 }; val7.normal.textColor = new Color(_textDark.r, _textDark.g, _textDark.b, 0.6f); val7.alignment = (TextAnchor)4; val7.padding = new RectOffset(ScaledInt(10f), ScaledInt(10f), ScaledInt(10f), ScaledInt(10f)); _emptyStyle = val7; GUIStyle val8 = new GUIStyle { fontSize = ScaledFont(11), fontStyle = (FontStyle)1 }; val8.normal.background = _closeBtnNormal; val8.normal.textColor = _textDark; val8.hover.background = _closeBtnHover; val8.hover.textColor = _woodDark; val8.active.background = _closeBtnActive; val8.active.textColor = _woodDark; val8.alignment = (TextAnchor)4; val8.padding = new RectOffset(ScaledInt(2f), ScaledInt(2f), ScaledInt(2f), ScaledInt(2f)); val8.border = new RectOffset(ScaledInt(2f), ScaledInt(2f), ScaledInt(2f), ScaledInt(2f)); _closeButtonStyle = val8; } 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 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_0029: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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) Texture2D val = new Texture2D(width, height); Color[] array = (Color[])(object)new Color[width * height]; Random random = new Random(42); 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)random.NextDouble() * 0.02f - 0.01f; array[i * width + j] = new Color(Mathf.Clamp01(val2.r + num2), Mathf.Clamp01(val2.g + num2), Mathf.Clamp01(val2.b + num2), val2.a); } } val.SetPixels(array); val.Apply(); return val; } private void OnDestroy() { if (_manager != null) { _manager.OnTodosChanged -= RefreshCache; _manager.OnDataLoaded -= RefreshCache; } } } public class TodoUI : MonoBehaviour { private const int WINDOW_ID = 98765; private const float BASE_WINDOW_WIDTH = 520f; private const float BASE_WINDOW_HEIGHT = 600f; private const float BASE_HEADER_HEIGHT = 50f; private const float BASE_ITEM_HEIGHT = 36f; private const float BASE_ICON_SIZE = 24f; private float _scale = 1f; private const string PAUSE_ID = "SunhavenTodo_UI"; private bool _isVisible; private Rect _windowRect; private Vector2 _scrollPosition; private float _openAnimation; private string _newTodoTitle = ""; private string _newTodoDescription = ""; private int _selectedPriority = 1; private int _selectedCategory; private bool _showAddForm; private string _editingItemId; private bool _newTodoIsRecurring; private int _newTodoRecurInterval; private int _selectedCategoryFilter = -1; private bool _showCompletedItems = true; private string _searchQuery = ""; private readonly List _cachedDrawList = new List(); private bool _cachedDrawListDirty = true; private int _cachedFilterCategory = -2; private bool _cachedFilterShowCompleted = true; private string _cachedFilterSearch; private TodoManager _manager; private readonly Color _parchmentLight = new Color(0.96f, 0.93f, 0.86f, 0.98f); private readonly Color _parchment = new Color(0.92f, 0.87f, 0.78f, 0.97f); private readonly Color _parchmentDark = new Color(0.85f, 0.78f, 0.65f, 0.95f); private readonly Color _woodDark = new Color(0.35f, 0.25f, 0.15f); private readonly Color _woodMedium = new Color(0.5f, 0.38f, 0.25f); private readonly Color _woodLight = new Color(0.65f, 0.52f, 0.38f); private readonly Color _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(0.98f, 0.95f, 0.85f); private readonly Color _forestGreen = new Color(0.3f, 0.55f, 0.3f); private readonly Color _successGreen = new Color(0.35f, 0.65f, 0.35f); private readonly Color _skyBlue = new Color(0.45f, 0.65f, 0.85f); private readonly Color _urgentRed = new Color(0.8f, 0.3f, 0.25f); private readonly Color _textDark = new Color(0.25f, 0.2f, 0.15f); private readonly Color _textLight = new Color(0.95f, 0.92f, 0.88f); private readonly Color _borderDark = new Color(0.4f, 0.3f, 0.2f, 0.8f); private readonly Dictionary _priorityColors = new Dictionary { { TodoPriority.Low, new Color(0.5f, 0.6f, 0.7f) }, { TodoPriority.Normal, new Color(0.45f, 0.55f, 0.45f) }, { TodoPriority.High, new Color(0.85f, 0.65f, 0.25f) }, { TodoPriority.Urgent, new Color(0.8f, 0.3f, 0.25f) } }; private readonly Dictionary _categoryColors = new Dictionary { { TodoCategory.General, new Color(0.55f, 0.5f, 0.45f) }, { TodoCategory.Farming, new Color(0.45f, 0.65f, 0.35f) }, { TodoCategory.Mining, new Color(0.5f, 0.45f, 0.55f) }, { TodoCategory.Fishing, new Color(0.4f, 0.6f, 0.75f) }, { TodoCategory.Combat, new Color(0.75f, 0.35f, 0.35f) }, { TodoCategory.Crafting, new Color(0.65f, 0.55f, 0.4f) }, { TodoCategory.Social, new Color(0.7f, 0.5f, 0.6f) }, { TodoCategory.Quests, new Color(0.8f, 0.7f, 0.3f) }, { TodoCategory.Collection, new Color(0.55f, 0.7f, 0.65f) } }; private bool _stylesInitialized; private GUIStyle _windowStyle; private GUIStyle _titleStyle; private GUIStyle _headerStyle; private GUIStyle _labelStyle; private GUIStyle _labelBoldStyle; private GUIStyle _buttonStyle; private GUIStyle _textFieldStyle; private GUIStyle _textAreaStyle; private GUIStyle _itemStyle; private GUIStyle _itemCompletedStyle; private GUIStyle _tabStyle; private GUIStyle _tabActiveStyle; private GUIStyle _statsStyle; private GUIStyle _categoryLabelStyle; private GUIStyle _priorityLabelStyle; private GUIStyle _footerStyle; private GUIStyle _completedTitleStyle; private GUIStyle _wrappedActiveTitleStyle; private GUIStyle _wrappedCompletedTitleStyle; private GUIStyle _rowStyle; private Dictionary _priorityStyleCache; private Dictionary _categoryStyleCache; private Texture2D _windowBackground; private Texture2D _headerBackground; private Texture2D _buttonNormal; private Texture2D _buttonHover; private Texture2D _buttonActive; private Texture2D _itemEven; private Texture2D _itemOdd; private Texture2D _itemCompleted; private Texture2D _tabNormal; private Texture2D _tabActive; private Texture2D _textFieldBg; private Texture2D _goldLine; private Texture2D _progressBg; private Texture2D _progressFill; private float WindowWidth => 520f * _scale; private float WindowHeight => 600f * _scale; private float HeaderHeight => 50f * _scale; private float ItemHeight => 36f * _scale; private float IconSize => 24f * _scale; public bool IsVisible => _isVisible; 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 Initialize(TodoManager manager) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) _manager = manager; float windowWidth = WindowWidth; float windowHeight = WindowHeight; _windowRect = new Rect(((float)Screen.width - windowWidth) / 2f, ((float)Screen.height - windowHeight) / 2f, windowWidth, windowHeight); } public void Show() { _isVisible = true; _openAnimation = 0f; PauseGame(pause: true); } public void Hide() { _isVisible = false; _showAddForm = false; _editingItemId = null; PauseGame(pause: false); } private void PauseGame(bool pause) { try { if ((Object)(object)Player.Instance != (Object)null) { if (pause) { Player.Instance.AddPauseObject("SunhavenTodo_UI"); } else { Player.Instance.RemovePauseObject("SunhavenTodo_UI"); } } Type type = Type.GetType("PlayerInput, Assembly-CSharp"); if (type != null) { type.GetMethod(pause ? "DisableInput" : "EnableInput", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null)?.Invoke(null, new object[1] { "SunhavenTodo_UI" }); } } catch (Exception ex) { Debug.LogWarning((object)("[SunhavenTodo] Input blocking failed: " + ex.Message)); } } public void Toggle() { if (_isVisible) { Hide(); } else { Show(); } } public void SetScale(float scale) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) _scale = Mathf.Clamp(scale, 0.5f, 2.5f); _stylesInitialized = false; _windowRect = new Rect(((float)Screen.width - WindowWidth) / 2f, ((float)Screen.height - WindowHeight) / 2f, WindowWidth, WindowHeight); } private void Update() { if (_isVisible && Input.GetKeyDown((KeyCode)27)) { if (_showAddForm) { _showAddForm = false; _editingItemId = null; } else { Hide(); } } if (_isVisible) { _openAnimation = Mathf.MoveTowards(_openAnimation, 1f, Time.unscaledDeltaTime * 8f); } } private void OnGUI() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_0085: 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_00f3: Unknown result type (might be due to invalid IL or missing references) if (_isVisible && _manager != null) { InitializeStyles(); ((Rect)(ref _windowRect)).width = WindowWidth; ((Rect)(ref _windowRect)).height = WindowHeight; GUI.color = new Color(1f, 1f, 1f, _openAnimation); GUI.depth = -1000; _windowRect = GUI.Window(98765, _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); ((Rect)(ref _windowRect)).x = Mathf.Clamp(((Rect)(ref _windowRect)).x, 0f, (float)Screen.width - ((Rect)(ref _windowRect)).width); ((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, 0f, (float)Screen.height - ((Rect)(ref _windowRect)).height); GUI.color = Color.white; } } private void DrawWindow(int windowId) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(Array.Empty()); DrawHeader(); DrawGoldDivider(); DrawStats(); DrawFilterBar(); if (_showAddForm) { DrawAddForm(); } else { DrawCategoryTabs(); DrawTodoList(); } DrawFooter(); GUILayout.EndVertical(); GUI.DragWindow(new Rect(0f, 0f, WindowWidth, HeaderHeight)); } private void DrawHeader() { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Todo List", _titleStyle, Array.Empty()); GUILayout.FlexibleSpace(); TodoHUD todoHUD = Plugin.GetTodoHUD(); if (Plugin.StaticHUDEnabled && (Object)(object)todoHUD != (Object)null && !todoHUD.IsEnabled) { if (GUILayout.Button(new GUIContent("Sticky", "Show the movable sticky task panel (top tasks)"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(64f), GUILayout.Height(28f) })) { Plugin.ShowHUD(); } GUILayout.Space(8f); } if (GUILayout.Button(new GUIContent(_showAddForm ? "Cancel" : "+ Add"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(70f), GUILayout.Height(28f) })) { _showAddForm = !_showAddForm; if (!_showAddForm) { ResetAddForm(); } } GUILayout.Space(8f); if (GUILayout.Button("X", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(28f), GUILayout.Height(28f) })) { Hide(); } GUILayout.EndHorizontal(); } private void DrawGoldDivider() { //IL_0027: 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_0038: Invalid comparison between Unknown and I4 //IL_0048: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(4f); Rect rect = GUILayoutUtility.GetRect(WindowWidth - Scaled(40f), Scaled(3f)); if ((int)Event.current.type == 7 && (Object)(object)_goldLine != (Object)null) { GUI.DrawTexture(rect, (Texture)(object)_goldLine); } GUILayout.Space(4f); } private void DrawStats() { //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Invalid comparison between Unknown and I4 //IL_0127: 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) (int total, int completed, int active) stats = _manager.GetStats(); int item = stats.total; int item2 = stats.completed; int item3 = stats.active; float completionPercent = _manager.GetCompletionPercent(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label($"Active: {item3}", _statsStyle, Array.Empty()); GUILayout.Space(20f); GUILayout.Label($"Completed: {item2}", _statsStyle, Array.Empty()); GUILayout.Space(20f); GUILayout.Label($"Total: {item}", _statsStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(4f); Rect rect = GUILayoutUtility.GetRect(WindowWidth - Scaled(60f), Scaled(12f)); ((Rect)(ref rect)).x = ((Rect)(ref rect)).x + 10f; ((Rect)(ref rect)).width = ((Rect)(ref rect)).width - 20f; if ((int)Event.current.type == 7) { if ((Object)(object)_progressBg != (Object)null) { GUI.DrawTexture(rect, (Texture)(object)_progressBg); } if ((Object)(object)_progressFill != (Object)null && completionPercent > 0f) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, (((Rect)(ref rect)).width - 4f) * (completionPercent / 100f), ((Rect)(ref rect)).height - 4f), (Texture)(object)_progressFill); } } GUILayout.Space(8f); } private void DrawFilterBar() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Expected O, but got Unknown GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Search:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); _searchQuery = GUILayout.TextField(_searchQuery, _textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(24f) }); GUILayout.Space(10f); if (GUILayout.Button(new GUIContent(_showCompletedItems ? "[v] Show Done" : "[ ] Show Done"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(100f), GUILayout.Height(24f) })) { _showCompletedItems = !_showCompletedItems; } GUILayout.FlexibleSpace(); if (_manager.GetCompletedTodos().Any() && GUILayout.Button("Clear Done", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(80f), GUILayout.Height(24f) })) { _manager.ClearCompleted(); } GUILayout.EndHorizontal(); GUILayout.Space(8f); } private void DrawCategoryTabs() { GUILayout.BeginHorizontal(Array.Empty()); GUIStyle val = ((_selectedCategoryFilter == -1) ? _tabActiveStyle : _tabStyle); if (GUILayout.Button("All", val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) })) { _selectedCategoryFilter = -1; } Dictionary countsByCategory = _manager.GetCountsByCategory(); foreach (TodoCategory value2 in Enum.GetValues(typeof(TodoCategory))) { int value; int num = (countsByCategory.TryGetValue(value2, out value) ? value : 0); GUIStyle val2 = ((value2 == (TodoCategory)_selectedCategoryFilter) ? _tabActiveStyle : _tabStyle); if (GUILayout.Button((num > 0) ? $"{value2} ({num})" : value2.ToString(), val2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) })) { _selectedCategoryFilter = (int)value2; } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(8f); } private void DrawTodoList() { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) if (_cachedFilterCategory != _selectedCategoryFilter || _cachedFilterShowCompleted != _showCompletedItems || _cachedFilterSearch != _searchQuery) { _cachedDrawListDirty = true; } if (_cachedDrawListDirty) { _cachedDrawListDirty = false; _cachedFilterCategory = _selectedCategoryFilter; _cachedFilterShowCompleted = _showCompletedItems; _cachedFilterSearch = _searchQuery; _cachedDrawList.Clear(); foreach (TodoItem filteredTodo in GetFilteredTodos()) { _cachedDrawList.Add(filteredTodo); } _cachedDrawList.Sort(delegate(TodoItem a, TodoItem b) { if (a.IsCompleted != b.IsCompleted) { return a.IsCompleted.CompareTo(b.IsCompleted); } int num = ((int)b.Priority).CompareTo((int)a.Priority); return (num != 0) ? num : b.CreatedAt.CompareTo(a.CreatedAt); }); } _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); if (_cachedDrawList.Count == 0) { GUILayout.Space(20f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("No tasks to show", _labelStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(20f); } else { for (int i = 0; i < _cachedDrawList.Count; i++) { DrawTodoItem(_cachedDrawList[i], i); } } GUILayout.EndScrollView(); } private void DrawTodoItem(TodoItem item, int index) { //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown //IL_026d: Unknown result type (might be due to invalid IL or missing references) Texture2D background = (item.IsCompleted ? _itemCompleted : ((index % 2 == 0) ? _itemEven : _itemOdd)); string text = (string.IsNullOrWhiteSpace(item.Title) ? "(No title)" : item.Title); if (item.IsRecurring) { text = "[" + GetRecurIcon(item.RecurInterval) + "] " + text; } GUIStyle val = (item.IsCompleted ? _wrappedCompletedTitleStyle : _wrappedActiveTitleStyle); float num = Scaled(143f); if (item.IconItemId > 0) { num += IconSize + Scaled(4f); } float num2 = Scaled(32f); float num3 = Mathf.Max(Scaled(120f), WindowWidth - num - num2); float num4 = Mathf.Ceil(val.CalcHeight(new GUIContent(text), num3)); float num5 = Mathf.Max(ItemHeight, num4 + Scaled(10f)); _rowStyle.normal.background = background; GUILayout.BeginHorizontal(_rowStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(num5) }); if (GUILayout.Toggle(item.IsCompleted, "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(20f) }) != item.IsCompleted) { _manager.ToggleComplete(item.Id); _cachedDrawListDirty = true; } GUILayout.Space(4f); GUIStyle value; GUIStyle val2 = (GUIStyle)(_priorityStyleCache.TryGetValue(item.Priority, out value) ? ((object)value) : ((object)_priorityLabelStyle)); GUILayout.Label(GetPriorityIcon(item.Priority), val2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(20f) }); GUIStyle value2; GUIStyle val3 = (GUIStyle)(_categoryStyleCache.TryGetValue(item.Category, out value2) ? ((object)value2) : ((object)_categoryLabelStyle)); GUILayout.Label("[" + GetCategoryShort(item.Category) + "]", val3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(45f) }); GUILayout.Space(4f); if (item.IconItemId > 0) { Texture2D icon = IconCache.GetIcon(item.IconItemId); if ((Object)(object)icon != (Object)null) { GUI.DrawTexture(GUILayoutUtility.GetRect(IconSize, IconSize, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(IconSize), GUILayout.Height(IconSize) }), (Texture)(object)icon, (ScaleMode)2); } else { GUILayout.Space(IconSize); } GUILayout.Space(4f); } GUILayout.Label(text, val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num3), GUILayout.MinHeight(num4) }); GUILayout.FlexibleSpace(); if (GUILayout.Button("✎", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(22f), GUILayout.Height(22f) })) { _editingItemId = item.Id; _newTodoTitle = item.Title ?? ""; _newTodoDescription = item.Description ?? ""; _selectedPriority = (int)item.Priority; _selectedCategory = (int)item.Category; _newTodoIsRecurring = item.IsRecurring; _newTodoRecurInterval = (int)item.RecurInterval; _showAddForm = true; } GUILayout.Space(2f); if (GUILayout.Button("x", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(22f), GUILayout.Height(22f) })) { _manager.RemoveTodo(item.Id); _cachedDrawListDirty = true; } GUILayout.EndHorizontal(); } private void DrawAddForm() { GUILayout.Space(10f); GUILayout.BeginVertical(_windowStyle, Array.Empty()); GUILayout.Label((_editingItemId != null) ? "Edit Task" : "Add New Task", _headerStyle, Array.Empty()); GUILayout.Space(8f); GUILayout.Label("Title:", _labelStyle, Array.Empty()); _newTodoTitle = GUILayout.TextField(_newTodoTitle, _textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) }); GUILayout.Space(6f); GUILayout.Label("Description (optional):", _labelStyle, Array.Empty()); _newTodoDescription = GUILayout.TextArea(_newTodoDescription, _textAreaStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(50f) }); GUILayout.Space(6f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Priority:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); string[] names = Enum.GetNames(typeof(TodoPriority)); for (int i = 0; i < names.Length; i++) { GUIStyle val = ((i == _selectedPriority) ? _tabActiveStyle : _tabStyle); if (GUILayout.Button(names[i], val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) })) { _selectedPriority = i; } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(6f); GUILayout.Label("Category:", _labelStyle, Array.Empty()); string[] names2 = Enum.GetNames(typeof(TodoCategory)); for (int j = 0; j < names2.Length; j += 5) { GUILayout.BeginHorizontal(Array.Empty()); for (int k = 0; k < 5 && j + k < names2.Length; k++) { int num = j + k; GUIStyle val2 = ((num == _selectedCategory) ? _tabActiveStyle : _tabStyle); if (GUILayout.Button(names2[num], val2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) })) { _selectedCategory = num; } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Space(6f); GUILayout.BeginHorizontal(Array.Empty()); _newTodoIsRecurring = GUILayout.Toggle(_newTodoIsRecurring, " Recurring task", _labelStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (_newTodoIsRecurring) { GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Resets:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); string[] names3 = Enum.GetNames(typeof(RecurInterval)); for (int l = 0; l < names3.Length; l++) { GUIStyle val3 = ((l == _newTodoRecurInterval) ? _tabActiveStyle : _tabStyle); if (GUILayout.Button(names3[l], val3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(24f) })) { _newTodoRecurInterval = l; } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Cancel", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(80f), GUILayout.Height(28f) })) { _showAddForm = false; ResetAddForm(); } GUILayout.Space(10f); GUI.enabled = !string.IsNullOrWhiteSpace(_newTodoTitle); if (GUILayout.Button((_editingItemId != null) ? "Update" : "Add Task", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(100f), GUILayout.Height(28f) })) { SaveNewTodo(); } GUI.enabled = true; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawFooter() { GUILayout.Space(4f); DrawGoldDivider(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("Press ESC to close | Drag header to move", _footerStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private IEnumerable GetFilteredTodos() { List list = _manager.GetAllTodos().ToList(); if (!_showCompletedItems) { list = list.Where((TodoItem t) => !t.IsCompleted).ToList(); } if (_selectedCategoryFilter >= 0) { TodoCategory category2 = (TodoCategory)_selectedCategoryFilter; list = list.Where((TodoItem t) => t.Category == category2).ToList(); } if (!string.IsNullOrWhiteSpace(_searchQuery)) { string lowerQuery = _searchQuery.ToLowerInvariant(); list = list.Where((TodoItem t) => t.Title.ToLowerInvariant().Contains(lowerQuery) || (!string.IsNullOrEmpty(t.Description) && t.Description.ToLowerInvariant().Contains(lowerQuery))).ToList(); if (!_showCompletedItems) { list = list.Where((TodoItem t) => !t.IsCompleted).ToList(); } if (_selectedCategoryFilter >= 0) { TodoCategory category = (TodoCategory)_selectedCategoryFilter; list = list.Where((TodoItem t) => t.Category == category).ToList(); } } return list; } private void SaveNewTodo() { if (string.IsNullOrWhiteSpace(_newTodoTitle)) { return; } TodoCategory selectedCategory = (TodoCategory)_selectedCategory; TodoItem todoItem = new TodoItem(_newTodoTitle.Trim(), _newTodoDescription?.Trim() ?? "", (TodoPriority)_selectedPriority, selectedCategory) { IsRecurring = _newTodoIsRecurring, RecurInterval = (RecurInterval)_newTodoRecurInterval }; if (_editingItemId != null) { todoItem.Id = _editingItemId; TodoItem todo = _manager.GetTodo(_editingItemId); if (todo != null) { todoItem.CreatedAt = todo.CreatedAt; todoItem.IsCompleted = todo.IsCompleted; todoItem.CompletedAt = todo.CompletedAt; todoItem.IconItemId = todo.IconItemId; todoItem.MuseumDestination = todo.MuseumDestination; } _manager.UpdateTodo(todoItem); } else { _manager.AddTodo(todoItem); _selectedCategoryFilter = (int)selectedCategory; } _cachedDrawListDirty = true; _showAddForm = false; ResetAddForm(); } private void ResetAddForm() { _newTodoTitle = ""; _newTodoDescription = ""; _selectedPriority = 1; _selectedCategory = 0; _editingItemId = null; _newTodoIsRecurring = false; _newTodoRecurInterval = 0; } private string GetPriorityIcon(TodoPriority priority) { return priority switch { TodoPriority.Low => "-", TodoPriority.Normal => "o", TodoPriority.High => "!", TodoPriority.Urgent => "!!", _ => "o", }; } private string GetRecurIcon(RecurInterval interval) { return interval switch { RecurInterval.Daily => "D", RecurInterval.Weekly => "W", RecurInterval.Seasonal => "S", _ => "R", }; } private string GetCategoryShort(TodoCategory category) { return category switch { TodoCategory.General => "GEN", TodoCategory.Farming => "FRM", TodoCategory.Mining => "MIN", TodoCategory.Fishing => "FSH", TodoCategory.Combat => "CMB", TodoCategory.Crafting => "CRF", TodoCategory.Social => "SOC", TodoCategory.Quests => "QST", TodoCategory.Collection => "COL", _ => "GEN", }; } private void InitializeStyles() { if (!_stylesInitialized) { CreateTextures(); CreateStyles(); _stylesInitialized = true; } } private void CreateTextures() { //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_001c: 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_0038: 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_0052: 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_006d: 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_0088: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0134: 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_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) DestroyTextures(); _windowBackground = MakeParchmentTexture(32, 128, _parchment, _parchmentLight, _borderDark, 4); _headerBackground = MakeGradientTex(8, 32, _parchmentDark, _parchment); _buttonNormal = MakeRoundedRect(8, 8, _parchmentDark, _borderDark, 2); _buttonHover = MakeRoundedRect(8, 8, _woodLight, _borderDark, 2); _buttonActive = MakeRoundedRect(8, 8, _goldPale, _goldRich, 2); _itemEven = MakeTex(1, 1, new Color(_parchmentLight.r, _parchmentLight.g, _parchmentLight.b, 0.4f)); _itemOdd = MakeTex(1, 1, new Color(_parchment.r, _parchment.g, _parchment.b, 0.4f)); _itemCompleted = MakeTex(1, 1, new Color(_successGreen.r, _successGreen.g, _successGreen.b, 0.15f)); _tabNormal = MakeRoundedRect(8, 8, _parchmentDark, _borderDark, 1); _tabActive = MakeRoundedRect(8, 8, _goldPale, _goldRich, 2); _textFieldBg = MakeTex(1, 1, new Color(1f, 1f, 1f, 0.9f)); _goldLine = MakeGradientTex(64, 3, _goldBright, _goldRich); _progressBg = MakeTex(1, 1, new Color(_woodDark.r, _woodDark.g, _woodDark.b, 0.4f)); _progressFill = MakeGradientTex(8, 8, _goldBright, _goldRich); } private void DestroyTextures() { if ((Object)(object)_windowBackground != (Object)null) { Object.Destroy((Object)(object)_windowBackground); } if ((Object)(object)_headerBackground != (Object)null) { Object.Destroy((Object)(object)_headerBackground); } if ((Object)(object)_buttonNormal != (Object)null) { Object.Destroy((Object)(object)_buttonNormal); } if ((Object)(object)_buttonHover != (Object)null) { Object.Destroy((Object)(object)_buttonHover); } if ((Object)(object)_buttonActive != (Object)null) { Object.Destroy((Object)(object)_buttonActive); } if ((Object)(object)_itemEven != (Object)null) { Object.Destroy((Object)(object)_itemEven); } if ((Object)(object)_itemOdd != (Object)null) { Object.Destroy((Object)(object)_itemOdd); } if ((Object)(object)_itemCompleted != (Object)null) { Object.Destroy((Object)(object)_itemCompleted); } if ((Object)(object)_tabNormal != (Object)null) { Object.Destroy((Object)(object)_tabNormal); } if ((Object)(object)_tabActive != (Object)null) { Object.Destroy((Object)(object)_tabActive); } if ((Object)(object)_textFieldBg != (Object)null) { Object.Destroy((Object)(object)_textFieldBg); } if ((Object)(object)_goldLine != (Object)null) { Object.Destroy((Object)(object)_goldLine); } if ((Object)(object)_progressBg != (Object)null) { Object.Destroy((Object)(object)_progressBg); } if ((Object)(object)_progressFill != (Object)null) { Object.Destroy((Object)(object)_progressFill); } } private void CreateStyles() { //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_0017: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_009b: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_010a: Expected O, but got Unknown //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0125: 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) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_0174: Expected O, but got Unknown //IL_0179: Expected O, but got Unknown //IL_017a: 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_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Expected O, but got Unknown //IL_01e1: Expected O, but got Unknown //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Expected O, but got Unknown //IL_0200: Unknown result type (might be due to invalid IL or missing references) //IL_0205: 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_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Expected O, but got Unknown //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_025c: 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) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Expected O, but got Unknown //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0326: Expected O, but got Unknown //IL_032b: Expected O, but got Unknown //IL_032c: Unknown result type (might be due to invalid IL or missing references) //IL_0331: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_0357: Unknown result type (might be due to invalid IL or missing references) //IL_0361: Unknown result type (might be due to invalid IL or missing references) //IL_0372: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) //IL_0383: Unknown result type (might be due to invalid IL or missing references) //IL_03b0: Unknown result type (might be due to invalid IL or missing references) //IL_03ba: Expected O, but got Unknown //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03e7: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Expected O, but got Unknown //IL_03f6: Expected O, but got Unknown //IL_03fd: Unknown result type (might be due to invalid IL or missing references) //IL_0402: Unknown result type (might be due to invalid IL or missing references) //IL_040e: Expected O, but got Unknown //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_0414: 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_0429: 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_0460: Unknown result type (might be due to invalid IL or missing references) //IL_046a: Expected O, but got Unknown //IL_046f: Expected O, but got Unknown //IL_0476: Unknown result type (might be due to invalid IL or missing references) //IL_047b: Unknown result type (might be due to invalid IL or missing references) //IL_0490: Unknown result type (might be due to invalid IL or missing references) //IL_049f: Expected O, but got Unknown //IL_04a0: Unknown result type (might be due to invalid IL or missing references) //IL_04a5: Unknown result type (might be due to invalid IL or missing references) //IL_04b3: Unknown result type (might be due to invalid IL or missing references) //IL_04c4: Unknown result type (might be due to invalid IL or missing references) //IL_04cb: Unknown result type (might be due to invalid IL or missing references) //IL_04d5: Unknown result type (might be due to invalid IL or missing references) //IL_04e6: Unknown result type (might be due to invalid IL or missing references) //IL_04ed: Unknown result type (might be due to invalid IL or missing references) //IL_04f7: Unknown result type (might be due to invalid IL or missing references) //IL_0508: Unknown result type (might be due to invalid IL or missing references) //IL_050f: Unknown result type (might be due to invalid IL or missing references) //IL_0519: Unknown result type (might be due to invalid IL or missing references) //IL_0520: Unknown result type (might be due to invalid IL or missing references) //IL_054d: Unknown result type (might be due to invalid IL or missing references) //IL_0557: Expected O, but got Unknown //IL_0557: Unknown result type (might be due to invalid IL or missing references) //IL_0570: Unknown result type (might be due to invalid IL or missing references) //IL_057a: Expected O, but got Unknown //IL_057a: Unknown result type (might be due to invalid IL or missing references) //IL_05a7: Unknown result type (might be due to invalid IL or missing references) //IL_05b1: Expected O, but got Unknown //IL_05b6: Expected O, but got Unknown //IL_05bd: Unknown result type (might be due to invalid IL or missing references) //IL_05c2: Unknown result type (might be due to invalid IL or missing references) //IL_05d3: Unknown result type (might be due to invalid IL or missing references) //IL_05da: Unknown result type (might be due to invalid IL or missing references) //IL_05e4: Unknown result type (might be due to invalid IL or missing references) //IL_05f0: Expected O, but got Unknown //IL_05f7: 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_060a: Unknown result type (might be due to invalid IL or missing references) //IL_0616: 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_0637: Unknown result type (might be due to invalid IL or missing references) //IL_0643: Expected O, but got Unknown //IL_064a: Unknown result type (might be due to invalid IL or missing references) //IL_064f: Unknown result type (might be due to invalid IL or missing references) //IL_065d: Unknown result type (might be due to invalid IL or missing references) //IL_0664: Unknown result type (might be due to invalid IL or missing references) //IL_0690: Unknown result type (might be due to invalid IL or missing references) //IL_069f: Expected O, but got Unknown //IL_06a6: Unknown result type (might be due to invalid IL or missing references) //IL_06ab: 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_06ca: Unknown result type (might be due to invalid IL or missing references) //IL_06d6: Expected O, but got Unknown //IL_06dd: Unknown result type (might be due to invalid IL or missing references) //IL_06e2: Unknown result type (might be due to invalid IL or missing references) //IL_06e9: Unknown result type (might be due to invalid IL or missing references) //IL_06f0: Unknown result type (might be due to invalid IL or missing references) //IL_06fc: Expected O, but got Unknown //IL_0703: Unknown result type (might be due to invalid IL or missing references) //IL_0708: Unknown result type (might be due to invalid IL or missing references) //IL_070f: Unknown result type (might be due to invalid IL or missing references) //IL_0716: Unknown result type (might be due to invalid IL or missing references) //IL_0722: Expected O, but got Unknown //IL_0729: Unknown result type (might be due to invalid IL or missing references) //IL_072e: Unknown result type (might be due to invalid IL or missing references) //IL_0733: Unknown result type (might be due to invalid IL or missing references) //IL_073d: Expected O, but got Unknown //IL_0742: Expected O, but got Unknown //IL_0788: Unknown result type (might be due to invalid IL or missing references) //IL_0781: Unknown result type (might be due to invalid IL or missing references) //IL_0789: Unknown result type (might be due to invalid IL or missing references) //IL_0797: Unknown result type (might be due to invalid IL or missing references) //IL_079c: Unknown result type (might be due to invalid IL or missing references) //IL_07a2: Unknown result type (might be due to invalid IL or missing references) //IL_07ad: Expected O, but got Unknown //IL_0813: Unknown result type (might be due to invalid IL or missing references) //IL_080c: Unknown result type (might be due to invalid IL or missing references) //IL_0815: Unknown result type (might be due to invalid IL or missing references) //IL_0825: Unknown result type (might be due to invalid IL or missing references) //IL_082a: Unknown result type (might be due to invalid IL or missing references) //IL_0830: Unknown result type (might be due to invalid IL or missing references) //IL_083c: Expected O, but got Unknown GUIStyle val = new GUIStyle(); val.normal.background = _windowBackground; val.normal.textColor = _textDark; val.padding = new RectOffset(ScaledInt(15f), ScaledInt(15f), ScaledInt(15f), ScaledInt(15f)); val.border = new RectOffset(ScaledInt(8f), ScaledInt(8f), ScaledInt(8f), ScaledInt(8f)); _windowStyle = val; GUIStyle val2 = new GUIStyle { fontSize = ScaledFont(22), fontStyle = (FontStyle)1 }; val2.normal.textColor = _woodDark; val2.alignment = (TextAnchor)3; val2.padding = new RectOffset(ScaledInt(5f), ScaledInt(5f), ScaledInt(5f), ScaledInt(5f)); _titleStyle = val2; GUIStyle val3 = new GUIStyle { fontSize = ScaledFont(16), fontStyle = (FontStyle)1 }; val3.normal.textColor = _woodDark; val3.alignment = (TextAnchor)4; val3.padding = new RectOffset(ScaledInt(5f), ScaledInt(5f), ScaledInt(5f), ScaledInt(5f)); _headerStyle = val3; GUIStyle val4 = new GUIStyle { fontSize = ScaledFont(12) }; val4.normal.textColor = _textDark; val4.alignment = (TextAnchor)3; val4.padding = new RectOffset(ScaledInt(2f), ScaledInt(2f), ScaledInt(2f), ScaledInt(2f)); _labelStyle = val4; _labelBoldStyle = new GUIStyle(_labelStyle) { fontStyle = (FontStyle)1 }; GUIStyle val5 = new GUIStyle(_labelStyle) { fontSize = ScaledFont(11), alignment = (TextAnchor)4 }; val5.normal.textColor = _woodMedium; _statsStyle = val5; GUIStyle val6 = new GUIStyle { fontSize = ScaledFont(11), fontStyle = (FontStyle)1 }; val6.normal.background = _buttonNormal; val6.normal.textColor = _textDark; val6.hover.background = _buttonHover; val6.hover.textColor = _textDark; val6.active.background = _buttonActive; val6.active.textColor = _woodDark; val6.alignment = (TextAnchor)4; val6.padding = new RectOffset(ScaledInt(8f), ScaledInt(8f), ScaledInt(4f), ScaledInt(4f)); val6.border = new RectOffset(ScaledInt(4f), ScaledInt(4f), ScaledInt(4f), ScaledInt(4f)); _buttonStyle = val6; GUIStyle val7 = new GUIStyle { fontSize = ScaledFont(12) }; val7.normal.background = _textFieldBg; val7.normal.textColor = _textDark; val7.focused.background = _textFieldBg; val7.focused.textColor = _textDark; val7.padding = new RectOffset(ScaledInt(6f), ScaledInt(6f), ScaledInt(4f), ScaledInt(4f)); val7.border = new RectOffset(ScaledInt(2f), ScaledInt(2f), ScaledInt(2f), ScaledInt(2f)); _textFieldStyle = val7; _textAreaStyle = new GUIStyle(_textFieldStyle) { wordWrap = true }; GUIStyle val8 = new GUIStyle { fontSize = ScaledFont(12) }; val8.normal.textColor = _textDark; val8.padding = new RectOffset(ScaledInt(8f), ScaledInt(8f), ScaledInt(4f), ScaledInt(4f)); _itemStyle = val8; GUIStyle val9 = new GUIStyle(_itemStyle); val9.normal.textColor = new Color(0.5f, 0.5f, 0.5f); _itemCompletedStyle = val9; GUIStyle val10 = new GUIStyle { fontSize = ScaledFont(10) }; val10.normal.background = _tabNormal; val10.normal.textColor = _textDark; val10.hover.background = _buttonHover; val10.hover.textColor = _textDark; val10.active.background = _tabActive; val10.active.textColor = _woodDark; val10.alignment = (TextAnchor)4; val10.padding = new RectOffset(ScaledInt(6f), ScaledInt(6f), ScaledInt(3f), ScaledInt(3f)); val10.margin = new RectOffset(ScaledInt(2f), ScaledInt(2f), 0, 0); val10.border = new RectOffset(ScaledInt(4f), ScaledInt(4f), ScaledInt(4f), ScaledInt(4f)); _tabStyle = val10; GUIStyle val11 = new GUIStyle(_tabStyle); val11.normal.background = _tabActive; val11.normal.textColor = _woodDark; val11.fontStyle = (FontStyle)1; _tabActiveStyle = val11; _categoryLabelStyle = new GUIStyle(_labelStyle) { fontSize = ScaledFont(9), fontStyle = (FontStyle)1 }; _priorityLabelStyle = new GUIStyle(_labelStyle) { fontSize = ScaledFont(12), fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; GUIStyle val12 = new GUIStyle(_labelStyle) { fontSize = ScaledFont(10), fontStyle = (FontStyle)2 }; val12.normal.textColor = new Color(_textDark.r, _textDark.g, _textDark.b, 0.6f); _footerStyle = val12; GUIStyle val13 = new GUIStyle(_labelBoldStyle); val13.normal.textColor = new Color(0.5f, 0.5f, 0.5f); val13.fontStyle = (FontStyle)2; _completedTitleStyle = val13; _wrappedActiveTitleStyle = new GUIStyle(_labelBoldStyle) { wordWrap = true, clipping = (TextClipping)0, alignment = (TextAnchor)0 }; _wrappedCompletedTitleStyle = new GUIStyle(_completedTitleStyle) { wordWrap = true, clipping = (TextClipping)0, alignment = (TextAnchor)0 }; _rowStyle = new GUIStyle(_itemStyle) { padding = new RectOffset(8, 8, 4, 4) }; _priorityStyleCache = new Dictionary(); foreach (TodoPriority value3 in Enum.GetValues(typeof(TodoPriority))) { Color value; Color textColor = (_priorityColors.TryGetValue(value3, out value) ? value : _textDark); Dictionary priorityStyleCache = _priorityStyleCache; GUIStyle val14 = new GUIStyle(_priorityLabelStyle); val14.normal.textColor = textColor; priorityStyleCache[value3] = val14; } _categoryStyleCache = new Dictionary(); foreach (TodoCategory value4 in Enum.GetValues(typeof(TodoCategory))) { Color value2; Color textColor2 = (_categoryColors.TryGetValue(value4, out value2) ? value2 : _textDark); Dictionary categoryStyleCache = _categoryStyleCache; GUIStyle val15 = new GUIStyle(_categoryLabelStyle); val15.normal.textColor = textColor2; categoryStyleCache[value4] = val15; } } private Texture2D MakeTex(int width, int height, Color color) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < array.Length; i++) { array[i] = color; } Texture2D val = new Texture2D(width, height); val.SetPixels(array); val.Apply(); return val; } private Texture2D 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_0029: 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_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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) Texture2D val = new Texture2D(width, height); Color[] array = (Color[])(object)new Color[width * height]; Random random = new Random(42); 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)random.NextDouble() * 0.03f - 0.015f; array[i * width + j] = new Color(Mathf.Clamp01(val2.r + num2), Mathf.Clamp01(val2.g + num2), Mathf.Clamp01(val2.b + num2), val2.a); } } val.SetPixels(array); val.Apply(); return val; } } } namespace SunhavenTodo.Data { public enum TodoPriority { Low, Normal, High, Urgent } public enum TodoCategory { General, Farming, Mining, Fishing, Combat, Crafting, Social, Quests, Collection } public enum RecurInterval { Daily, Weekly, Seasonal } [Serializable] public class TodoItem { public string Id { get; set; } public string Title { get; set; } public string Description { get; set; } public int IconItemId { get; set; } public string MuseumDestination { get; set; } public TodoPriority Priority { get; set; } public TodoCategory Category { get; set; } public bool IsCompleted { get; set; } public DateTime CreatedAt { get; set; } public DateTime? CompletedAt { get; set; } public bool IsRecurring { get; set; } public RecurInterval RecurInterval { get; set; } public TodoItem() { Id = Guid.NewGuid().ToString(); Priority = TodoPriority.Normal; Category = TodoCategory.General; IconItemId = -1; MuseumDestination = ""; CreatedAt = DateTime.Now; IsCompleted = false; } public TodoItem(string title, string description = "", TodoPriority priority = TodoPriority.Normal, TodoCategory category = TodoCategory.General) : this() { Title = title; Description = description; Priority = priority; Category = category; } } [Serializable] public class TodoListData { public string CharacterName { get; set; } public List Items { get; set; } public DateTime LastUpdated { get; set; } public TodoListData() { Items = new List(); LastUpdated = DateTime.Now; } public TodoListData(string characterName) : this() { CharacterName = characterName; } } internal static class TodoJson { internal static string Serialize(TodoListData data) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("{"); stringBuilder.Append(" \"CharacterName\": "); MinimalJsonParser.WriteJsonString(stringBuilder, data.CharacterName); stringBuilder.AppendLine(","); stringBuilder.Append(" \"LastUpdated\": "); MinimalJsonParser.WriteJsonString(stringBuilder, data.LastUpdated.ToString("o")); stringBuilder.AppendLine(","); stringBuilder.AppendLine(" \"Items\": ["); for (int i = 0; i < data.Items.Count; i++) { TodoItem todoItem = data.Items[i]; stringBuilder.AppendLine(" {"); stringBuilder.Append(" \"Id\": "); MinimalJsonParser.WriteJsonString(stringBuilder, todoItem.Id ?? ""); stringBuilder.AppendLine(","); stringBuilder.Append(" \"Title\": "); MinimalJsonParser.WriteJsonString(stringBuilder, todoItem.Title ?? ""); stringBuilder.AppendLine(","); stringBuilder.Append(" \"Description\": "); MinimalJsonParser.WriteJsonString(stringBuilder, todoItem.Description ?? ""); stringBuilder.AppendLine(","); stringBuilder.AppendLine($" \"IconItemId\": {todoItem.IconItemId},"); stringBuilder.Append(" \"MuseumDestination\": "); MinimalJsonParser.WriteJsonString(stringBuilder, todoItem.MuseumDestination ?? ""); stringBuilder.AppendLine(","); stringBuilder.AppendLine($" \"Priority\": {(int)todoItem.Priority},"); stringBuilder.AppendLine($" \"Category\": {(int)todoItem.Category},"); stringBuilder.AppendLine(" \"IsCompleted\": " + (todoItem.IsCompleted ? "true" : "false") + ","); stringBuilder.Append(" \"CreatedAt\": "); MinimalJsonParser.WriteJsonString(stringBuilder, todoItem.CreatedAt.ToString("o")); stringBuilder.AppendLine(","); stringBuilder.Append(" \"CompletedAt\": "); MinimalJsonParser.WriteJsonString(stringBuilder, todoItem.CompletedAt?.ToString("o") ?? ""); stringBuilder.AppendLine(","); stringBuilder.AppendLine(" \"IsRecurring\": " + (todoItem.IsRecurring ? "true" : "false") + ","); stringBuilder.AppendLine($" \"RecurInterval\": {(int)todoItem.RecurInterval}"); stringBuilder.Append(" }"); if (i < data.Items.Count - 1) { stringBuilder.AppendLine(","); } else { stringBuilder.AppendLine(); } } stringBuilder.AppendLine(" ]"); stringBuilder.Append("}"); return stringBuilder.ToString(); } internal static TodoListData Deserialize(string json) { if (string.IsNullOrEmpty(json)) { return null; } try { return DeserializeCore(json); } catch (Exception) { return null; } } private static TodoListData DeserializeCore(string json) { int pos = 0; Dictionary dictionary = MinimalJsonParser.ParseObject(json, ref pos); if (dictionary == null) { return null; } TodoListData todoListData = new TodoListData(); if (dictionary.TryGetValue("CharacterName", out var value)) { todoListData.CharacterName = (value as string) ?? ""; } if (dictionary.TryGetValue("LastUpdated", out var value2) && value2 is string s) { todoListData.LastUpdated = (DateTime.TryParse(s, null, DateTimeStyles.RoundtripKind, out var result) ? result : DateTime.Now); } if (dictionary.TryGetValue("Items", out var value3) && value3 is List list) { bool flag = default(bool); bool flag2 = default(bool); foreach (object item in list) { if (!(item is Dictionary dictionary2)) { continue; } TodoItem todoItem = new TodoItem(); if (dictionary2.TryGetValue("Id", out var value4)) { todoItem.Id = (value4 as string) ?? Guid.NewGuid().ToString(); } if (dictionary2.TryGetValue("Title", out var value5)) { todoItem.Title = (value5 as string) ?? ""; } if (dictionary2.TryGetValue("Description", out var value6)) { todoItem.Description = (value6 as string) ?? ""; } if (dictionary2.TryGetValue("IconItemId", out var value7)) { todoItem.IconItemId = MinimalJsonParser.ToInt(value7); } else { todoItem.IconItemId = -1; } if (dictionary2.TryGetValue("MuseumDestination", out var value8)) { todoItem.MuseumDestination = (value8 as string) ?? ""; } else { todoItem.MuseumDestination = ""; } if (dictionary2.TryGetValue("Priority", out var value9)) { todoItem.Priority = (TodoPriority)MinimalJsonParser.ToInt(value9); } if (dictionary2.TryGetValue("Category", out var value10)) { todoItem.Category = (TodoCategory)MinimalJsonParser.ToInt(value10); } if (dictionary2.TryGetValue("IsCompleted", out var value11)) { int num; if (value11 is bool) { flag = (bool)value11; num = 1; } else { num = 0; } todoItem.IsCompleted = (byte)((uint)num & (flag ? 1u : 0u)) != 0; } if (dictionary2.TryGetValue("CreatedAt", out var value12) && value12 is string s2) { todoItem.CreatedAt = (DateTime.TryParse(s2, null, DateTimeStyles.RoundtripKind, out var result2) ? result2 : DateTime.Now); } if (dictionary2.TryGetValue("CompletedAt", out var value13) && value13 is string text && !string.IsNullOrEmpty(text)) { todoItem.CompletedAt = (DateTime.TryParse(text, null, DateTimeStyles.RoundtripKind, out var result3) ? new DateTime?(result3) : null); } if (dictionary2.TryGetValue("IsRecurring", out var value14)) { int num2; if (value14 is bool) { flag2 = (bool)value14; num2 = 1; } else { num2 = 0; } todoItem.IsRecurring = (byte)((uint)num2 & (flag2 ? 1u : 0u)) != 0; } if (dictionary2.TryGetValue("RecurInterval", out var value15)) { todoItem.RecurInterval = (RecurInterval)MinimalJsonParser.ToInt(value15); } todoListData.Items.Add(todoItem); } } return todoListData; } } public class TodoManager { private TodoListData _todoData; private string _currentCharacter; private bool _isDirty; private readonly Dictionary _todoById = new Dictionary(); private bool _queryCacheDirty = true; private readonly List _activeTodosCache = new List(); private readonly List _completedTodosCache = new List(); private readonly Dictionary> _todosByCategoryCache = new Dictionary>(); private readonly Dictionary> _todosByPriorityCache = new Dictionary>(); public bool IsDirty => _isDirty; public string CurrentCharacter => _currentCharacter; public event Action OnTodosChanged; public event Action OnDataLoaded; public void LoadForCharacter(string characterName, TodoListData data) { _currentCharacter = characterName; _todoData = data ?? new TodoListData(characterName); _isDirty = false; RebuildTodoIndex(); InvalidateQueryCaches(); this.OnDataLoaded?.Invoke(); } public void ClearData() { _todoData = null; _currentCharacter = null; _isDirty = false; _todoById.Clear(); InvalidateQueryCaches(); } public TodoListData GetData() { return _todoData; } public void MarkClean() { _isDirty = false; } public void AddTodo(TodoItem item) { if (_todoData != null) { _todoData.Items.Add(item); if (!string.IsNullOrEmpty(item.Id)) { _todoById[item.Id] = item; } _todoData.LastUpdated = DateTime.Now; _isDirty = true; InvalidateQueryCaches(); this.OnTodosChanged?.Invoke(); } } public void AddTodo(string title, string description = "", TodoPriority priority = TodoPriority.Normal, TodoCategory category = TodoCategory.General) { TodoItem item = new TodoItem(title, description, priority, category); AddTodo(item); } public TodoItem GetTodo(string id) { _todoById.TryGetValue(id, out TodoItem value); return value; } public void UpdateTodo(TodoItem item) { TodoItem item2 = item; if (_todoData == null) { return; } int num = _todoData.Items.FindIndex((TodoItem i) => i.Id == item2.Id); if (num >= 0) { _todoData.Items[num] = item2; if (!string.IsNullOrEmpty(item2.Id)) { _todoById[item2.Id] = item2; } _todoData.LastUpdated = DateTime.Now; _isDirty = true; InvalidateQueryCaches(); this.OnTodosChanged?.Invoke(); } } public void RemoveTodo(string itemId) { string itemId2 = itemId; if (_todoData != null && _todoData.Items.RemoveAll((TodoItem i) => i.Id == itemId2) > 0) { _todoById.Remove(itemId2); _todoData.LastUpdated = DateTime.Now; _isDirty = true; InvalidateQueryCaches(); this.OnTodosChanged?.Invoke(); } } public void ToggleComplete(string itemId) { if (_todoData != null) { TodoItem todoById = GetTodoById(itemId); if (todoById != null) { todoById.IsCompleted = !todoById.IsCompleted; todoById.CompletedAt = (todoById.IsCompleted ? new DateTime?(DateTime.Now) : null); _todoData.LastUpdated = DateTime.Now; _isDirty = true; InvalidateQueryCaches(); this.OnTodosChanged?.Invoke(); } } } public void ResetRecurringTodos(RecurInterval interval) { if (_todoData == null) { return; } bool flag = false; foreach (TodoItem item in _todoData.Items) { if (item.IsCompleted && item.IsRecurring && item.RecurInterval == interval) { item.IsCompleted = false; item.CompletedAt = null; flag = true; } } if (flag) { _todoData.LastUpdated = DateTime.Now; _isDirty = true; InvalidateQueryCaches(); this.OnTodosChanged?.Invoke(); } } public void ClearCompleted() { if (_todoData != null && _todoData.Items.RemoveAll((TodoItem i) => i.IsCompleted && !i.IsRecurring) > 0) { RebuildTodoIndex(); _todoData.LastUpdated = DateTime.Now; _isDirty = true; InvalidateQueryCaches(); this.OnTodosChanged?.Invoke(); } } public IEnumerable GetAllTodos() { IEnumerable enumerable = _todoData?.Items; return enumerable ?? Enumerable.Empty(); } public IReadOnlyList GetTodosByCategory(TodoCategory category) { EnsureQueryCaches(); if (!_todosByCategoryCache.TryGetValue(category, out List value)) { return Array.Empty(); } return value; } public IReadOnlyList GetTodosByPriority(TodoPriority priority) { EnsureQueryCaches(); if (!_todosByPriorityCache.TryGetValue(priority, out List value)) { return Array.Empty(); } return value; } public IReadOnlyList GetActiveTodos() { EnsureQueryCaches(); return _activeTodosCache; } public IReadOnlyList GetCompletedTodos() { EnsureQueryCaches(); return _completedTodosCache; } public IEnumerable SearchTodos(string query) { if (string.IsNullOrWhiteSpace(query)) { return GetAllTodos(); } string lowerQuery = query.ToLower(); return from i in GetAllTodos() where i.Title.ToLower().Contains(lowerQuery) || (i.Description != null && i.Description.ToLower().Contains(lowerQuery)) select i; } public TodoItem GetTodoById(string id) { if (string.IsNullOrEmpty(id)) { return null; } if (!_todoById.TryGetValue(id, out TodoItem value)) { return null; } return value; } public (int total, int completed, int active) GetStats() { List list = GetAllTodos().ToList(); int num = list.Count((TodoItem i) => i.IsCompleted); return (list.Count, num, list.Count - num); } public Dictionary GetCountsByCategory() { Dictionary dictionary = new Dictionary(); foreach (TodoCategory value in Enum.GetValues(typeof(TodoCategory))) { dictionary[value] = GetTodosByCategory(value).Count((TodoItem i) => !i.IsCompleted); } return dictionary; } public float GetCompletionPercent() { var (num, num2, _) = GetStats(); if (num != 0) { return (float)num2 / (float)num * 100f; } return 0f; } private void RebuildTodoIndex() { _todoById.Clear(); if (_todoData?.Items == null) { return; } foreach (TodoItem item in _todoData.Items) { if (item != null && !string.IsNullOrEmpty(item.Id)) { _todoById[item.Id] = item; } } } private void InvalidateQueryCaches() { _queryCacheDirty = true; } private void EnsureQueryCaches() { if (!_queryCacheDirty) { return; } _activeTodosCache.Clear(); _completedTodosCache.Clear(); _todosByCategoryCache.Clear(); _todosByPriorityCache.Clear(); if (_todoData?.Items != null) { foreach (TodoItem item in _todoData.Items) { if (item != null) { if (item.IsCompleted) { _completedTodosCache.Add(item); } else { _activeTodosCache.Add(item); } if (!_todosByCategoryCache.TryGetValue(item.Category, out List value)) { value = new List(); _todosByCategoryCache[item.Category] = value; } value.Add(item); if (!_todosByPriorityCache.TryGetValue(item.Priority, out List value2)) { value2 = new List(); _todosByPriorityCache[item.Priority] = value2; } value2.Add(item); } } } _queryCacheDirty = false; } } public class TodoSaveSystem { private readonly TodoManager _manager; private string _savePath; public TodoSaveSystem(TodoManager manager) { _manager = manager; _savePath = Path.Combine(Paths.ConfigPath, "com.azraelgodking.sunhaventodo"); if (!Directory.Exists(_savePath)) { Directory.CreateDirectory(_savePath); } } private string GetSaveFilePath(string characterName) { string text = SanitizeFileName(characterName); return Path.Combine(_savePath, text + "_todos.json"); } private string SanitizeFileName(string name) { if (string.IsNullOrEmpty(name)) { return "unknown"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } return name; } public void Save() { TodoListData data = _manager.GetData(); if (data == null || string.IsNullOrEmpty(data.CharacterName)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Cannot save: No data or character name"); } return; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"[Save] Saving {data.Items.Count} todo(s) for '{data.CharacterName}'"); } try { string text = SerializeToJson(data); string saveFilePath = GetSaveFilePath(data.CharacterName); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogDebug((object)$"[Save] Writing to: {saveFilePath} ({text.Length} chars)"); } string text2 = saveFilePath + ".tmp"; try { File.WriteAllText(text2, text); if (File.Exists(saveFilePath)) { string text3 = saveFilePath + ".bak"; if (File.Exists(text3)) { File.Delete(text3); } File.Move(saveFilePath, text3); } File.Move(text2, saveFilePath); } finally { if (File.Exists(text2)) { File.Delete(text2); } } _manager.MarkClean(); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("[Save] Saved successfully: " + saveFilePath)); } } catch (Exception arg) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)$"[Save] Failed to save: {arg}"); } } } public TodoListData Load(string characterName) { if (string.IsNullOrEmpty(characterName)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Cannot load: No character name"); } return null; } string saveFilePath = GetSaveFilePath(characterName); string text = saveFilePath + ".bak"; if (File.Exists(saveFilePath)) { TodoListData todoListData = TryLoadFromFile(saveFilePath, characterName); if (todoListData != null) { return todoListData; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Main save file corrupted for " + characterName + ", trying backup...")); } } if (File.Exists(text)) { TodoListData todoListData2 = TryLoadFromFile(text, characterName); if (todoListData2 != null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Loaded from backup for " + characterName)); } return todoListData2; } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Backup file also corrupted for " + characterName)); } } ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)("No valid save file found for " + characterName + ", creating new todo list")); } return new TodoListData(characterName); } private TodoListData TryLoadFromFile(string filePath, string characterName) { try { string text = File.ReadAllText(filePath); if (string.IsNullOrWhiteSpace(text) || !text.TrimStart(Array.Empty()).StartsWith("{")) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("File " + filePath + " does not contain valid JSON")); } return null; } TodoListData todoListData = DeserializeFromJson(text); if (todoListData == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Failed to deserialize " + filePath)); } return null; } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"Loaded todo list for {characterName}: {todoListData.Items.Count} item(s)"); } return todoListData; } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("Error loading " + filePath + ": " + ex.Message)); } return null; } } public void Delete(string characterName) { if (string.IsNullOrEmpty(characterName)) { return; } string saveFilePath = GetSaveFilePath(characterName); if (!File.Exists(saveFilePath)) { return; } try { File.Delete(saveFilePath); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Deleted todo list for " + characterName)); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Failed to delete todo list: " + ex.Message)); } } } private static string SerializeToJson(TodoListData data) { return TodoJson.Serialize(data); } private static TodoListData DeserializeFromJson(string json) { return TodoJson.Deserialize(json); } } }