using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using BirthdayReminder; using BirthdayReminder.Data; using CropOptimizer.Data; using GiftingAssistant.Data; using HarmonyLib; using HavenDevTools; using HavensAlmanac.Config; using HavensAlmanac.Data; using HavensAlmanac.Integration; using HavensAlmanac.Services; using HavensAlmanac.UI; using HavensBirthright; using I2.Loc; using Microsoft.CodeAnalysis; using SunHavenMuseumUtilityTracker; using SunHavenMuseumUtilityTracker.Data; using SunhavenMods.Shared; using SunhavenTodo; using SunhavenTodo.Data; using TheVault; using TheVault.Vault; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; [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("HavensAlmanac")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+79057c8571b97b27a9323b455b8f88a51829b688")] [assembly: AssemblyProduct("HavensAlmanac")] [assembly: AssemblyTitle("HavensAlmanac")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [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] [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] [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 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 void TryUnhookOvernightEvent(ref bool overnightHooked, ref UnityAction overnightCallback, Func singletonResolver = null, Action logInfo = null, Action logWarning = null) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown UnityAction val = overnightCallback; try { if (val != null) { try { Type type = AccessTools.TypeByName("Wish.DayCycle"); FieldInfo fieldInfo = ((type != null) ? AccessTools.Field(type, "OnDayStart") : null); if (fieldInfo != null) { object? value = fieldInfo.GetValue(null); UnityAction val2 = (UnityAction)((value is UnityAction) ? value : null); if (val2 != null) { val2 = (UnityAction)Delegate.Remove((Delegate?)(object)val2, (Delegate?)(object)val); fieldInfo.SetValue(null, val2); } } } catch (Exception ex) { logWarning?.Invoke("Failed to unhook DayCycle.OnDayStart: " + ex.Message); } try { Type type2 = AccessTools.TypeByName("Wish.UIHandler"); if (type2 != null) { object obj = singletonResolver?.Invoke(type2); FieldInfo fieldInfo2 = AccessTools.Field(type2, "OnCompleteOvernight"); if (obj != null && fieldInfo2 != null) { object? value2 = fieldInfo2.GetValue(obj); UnityAction val3 = (UnityAction)((value2 is UnityAction) ? value2 : null); if (val3 != null) { val3 = (UnityAction)Delegate.Remove((Delegate?)(object)val3, (Delegate?)(object)val); fieldInfo2.SetValue(obj, val3); } } } } catch (Exception ex2) { logWarning?.Invoke("Failed to unhook UIHandler.OnCompleteOvernight: " + ex2.Message); } } logInfo?.Invoke("Overnight hook cleared"); } finally { overnightHooked = false; overnightCallback = null; } } } public static class SharedCodeRevision { public const string Value = "2026.07.28"; } public sealed class ModHealthReport { public string PluginGuid { get; set; } public string DisplayName { get; set; } public string PluginVersion { get; set; } public string SharedCodeRevision { get; set; } public string IntegrationSummary { get; set; } public string Mode { get; set; } public string CharacterName { get; set; } public bool? DataLoaded { get; set; } public string LastPersistenceOutcome { get; set; } public string LastError { get; set; } public DateTime ReportedUtc { get; set; } public ModHealthReport Clone() { return new ModHealthReport { PluginGuid = PluginGuid, DisplayName = DisplayName, PluginVersion = PluginVersion, SharedCodeRevision = SharedCodeRevision, IntegrationSummary = IntegrationSummary, Mode = Mode, CharacterName = CharacterName, DataLoaded = DataLoaded, LastPersistenceOutcome = LastPersistenceOutcome, LastError = LastError, ReportedUtc = ReportedUtc }; } public void AppendDiagnosticDump(StringBuilder sb) { if (sb != null) { sb.AppendLine("guid: " + (PluginGuid ?? "—")); sb.AppendLine("name: " + (DisplayName ?? "—")); sb.AppendLine("version: " + (PluginVersion ?? "—")); sb.AppendLine("shared: " + (SharedCodeRevision ?? "—")); sb.AppendLine("integrations: " + (IntegrationSummary ?? "—")); sb.AppendLine("mode: " + (Mode ?? "—")); sb.AppendLine("character: " + (string.IsNullOrEmpty(CharacterName) ? "—" : CharacterName)); sb.AppendLine("data loaded: " + (DataLoaded.HasValue ? DataLoaded.Value.ToString() : "—")); sb.AppendLine("last save: " + (LastPersistenceOutcome ?? "—")); sb.AppendLine("last error: " + (LastError ?? "—")); sb.AppendLine("reported: " + ((ReportedUtc == default(DateTime)) ? "—" : ReportedUtc.ToLocalTime().ToString("u"))); } } } public static class ModDiagnostics { private static readonly Dictionary ReportsByPluginGuid = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly object Lock = new object(); public static void ReportStartup(ModHealthReport report) { if (report == null || string.IsNullOrWhiteSpace(report.PluginGuid)) { return; } report.ReportedUtc = DateTime.UtcNow; if (string.IsNullOrEmpty(report.SharedCodeRevision)) { report.SharedCodeRevision = "2026.07.28"; } lock (Lock) { ReportsByPluginGuid[report.PluginGuid] = report.Clone(); } } public static void ReportRuntime(string pluginGuid, Action update) { if (string.IsNullOrWhiteSpace(pluginGuid) || update == null) { return; } lock (Lock) { if (!ReportsByPluginGuid.TryGetValue(pluginGuid, out ModHealthReport value)) { value = new ModHealthReport { PluginGuid = pluginGuid }; ReportsByPluginGuid[pluginGuid] = value; } update(value); value.ReportedUtc = DateTime.UtcNow; } } public static ModHealthReport GetReport(string pluginGuid) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return null; } lock (Lock) { ModHealthReport value; return ReportsByPluginGuid.TryGetValue(pluginGuid, out value) ? value.Clone() : null; } } public static IReadOnlyList GetAllReports() { lock (Lock) { return ReportsByPluginGuid.Values.Select((ModHealthReport r) => r.Clone()).ToList(); } } public static string FormatHealthLogLine(ModHealthReport report) { if (report == null) { return "[Health] (empty report)"; } string text = (string.IsNullOrEmpty(report.DisplayName) ? report.PluginGuid : report.DisplayName); string text2 = (string.IsNullOrEmpty(report.PluginVersion) ? "?" : report.PluginVersion); string text3 = (string.IsNullOrEmpty(report.SharedCodeRevision) ? "?" : report.SharedCodeRevision); string text4 = (string.IsNullOrEmpty(report.IntegrationSummary) ? "—" : report.IntegrationSummary); string text5 = (string.IsNullOrEmpty(report.Mode) ? "—" : report.Mode); string text6 = (string.IsNullOrEmpty(report.CharacterName) ? "—" : report.CharacterName); string text7 = ((!report.DataLoaded.HasValue) ? "—" : (report.DataLoaded.Value ? "loaded" : "none")); string text8 = (string.IsNullOrEmpty(report.LastPersistenceOutcome) ? "—" : report.LastPersistenceOutcome); return "[Health] " + text + " v" + text2 + " | shared " + text3 + " | integrations: " + text4 + " | mode: " + text5 + " | character: " + text6 + " | data: " + text7 + " | save: " + text8; } public static void LogStartupHealth(ManualLogSource log, ModHealthReport report) { ReportStartup(report); if (log != null) { log.LogInfo((object)FormatHealthLogLine(report)); } } public static void LogModStartup(ManualLogSource log, string pluginGuid, string displayName, string pluginVersion, string integrationSummary, string mode = "startup", bool? dataLoaded = false, string lastPersistenceOutcome = "—") { LogStartupHealth(log, new ModHealthReport { PluginGuid = pluginGuid, DisplayName = displayName, PluginVersion = pluginVersion, SharedCodeRevision = "2026.07.28", IntegrationSummary = integrationSummary, Mode = mode, DataLoaded = dataLoaded, LastPersistenceOutcome = lastPersistenceOutcome }); } } public static class SuitePluginGuids { public const string DevTools = "com.azraelgodking.havendevtools"; public const string SenpaisChest = "com.azraelgodking.senpaischest"; public const string BirthdayReminder = "com.azraelgodking.squirrelsbirthdayreminder"; public const string HavensBirthright = "com.azraelgodking.havensbirthright"; public const string Smut = "com.azraelgodking.sunhavenmuseumutilitytracker"; public const string SunhavenTodo = "com.azraelgodking.sunhaventodo"; public const string TheVault = "com.azraelgodking.thevault"; public const string HavensAlmanac = "com.azraelgodking.havensalmanac"; public const string FasterRaces = "com.azraelgodking.fasterraces"; public const string TrinketFortune = "com.azraelgodking.trinketfortune"; public const string CropOptimizer = "com.azraelgodking.cropoptimizer"; public const string HavensRespec = "com.azraelgodking.havensrespec"; public const string GiftingAssistant = "com.azraelgodking.giftingassistant"; } public static class ModHealthIntegrationSummary { public static string Build(params (string label, string pluginGuid)[] integrations) { if (integrations == null || integrations.Length == 0) { return "standalone"; } Dictionary pluginInfos = Chainloader.PluginInfos; if (pluginInfos == null) { return "standalone"; } List list = new List(integrations.Length); for (int i = 0; i < integrations.Length; i++) { var (text, text2) = integrations[i]; if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(text2) && pluginInfos.ContainsKey(text2)) { list.Add(text); } } if (list.Count != 0) { return string.Join(", ", list); } return "standalone"; } } public static class ModHealthAggregator { public sealed class MergedModHealthRow { public string PluginGuid { get; set; } public string DisplayName { get; set; } public string InstalledVersion { get; set; } public string SharedCodeRevision { get; set; } public string IntegrationSummary { get; set; } public string Mode { get; set; } public string CharacterName { get; set; } public bool? DataLoaded { get; set; } public string LastPersistenceOutcome { get; set; } public DateTime LastCheckUtc { get; set; } public int ExceptionCount { get; set; } public string LastError { get; set; } public DateTime ReportedUtc { get; set; } public bool HasDiagnosticsReport { get; set; } public bool HasIssue { get { if (ExceptionCount <= 0) { return !string.IsNullOrEmpty(LastError); } return true; } } public void AppendDiagnosticDump(StringBuilder sb) { sb.AppendLine("=== " + (DisplayName ?? PluginGuid) + " ==="); sb.AppendLine("installed: " + (InstalledVersion ?? "—")); sb.AppendLine("shared code: " + (SharedCodeRevision ?? "—")); sb.AppendLine(string.Format("version check: {0}, exceptions {1}", (LastCheckUtc == default(DateTime)) ? "never" : LastCheckUtc.ToLocalTime().ToString("u"), ExceptionCount)); if (!string.IsNullOrEmpty(LastError)) { sb.AppendLine("last error: " + LastError); } sb.AppendLine("integrations: " + (IntegrationSummary ?? "—")); sb.AppendLine("mode: " + (Mode ?? "—")); sb.AppendLine("character: " + (string.IsNullOrEmpty(CharacterName) ? "—" : CharacterName)); sb.AppendLine("data loaded: " + (DataLoaded.HasValue ? DataLoaded.Value.ToString() : "—")); sb.AppendLine("last save: " + (LastPersistenceOutcome ?? "—")); sb.AppendLine("diagnostics reported: " + ((ReportedUtc == default(DateTime)) ? "—" : ReportedUtc.ToLocalTime().ToString("u"))); sb.AppendLine(); } } public sealed class SharedCodeSkewAnalysis { public bool HasSkew { get; set; } public int VersionCheckerCopyCount { get; set; } public int ModDiagnosticsCopyCount { get; set; } public IReadOnlyDictionary> ModsByRevision { get; set; } = new Dictionary>(); } private struct VersionTelemetryEntry { public string PluginGuid; public DateTime LastCheckUtc; public int ExceptionCount; public string LastError; } private struct SnapshotShape { public PropertyInfo LastCheckUtc; public PropertyInfo ExceptionCount; public PropertyInfo LastError; } private struct DiagnosticsShape { public PropertyInfo DisplayName; public PropertyInfo PluginVersion; public PropertyInfo SharedCodeRevision; public PropertyInfo IntegrationSummary; public PropertyInfo Mode; public PropertyInfo CharacterName; public PropertyInfo DataLoaded; public PropertyInfo LastPersistenceOutcome; public PropertyInfo LastError; public PropertyInfo ReportedUtc; } private static readonly object CacheLock = new object(); private static List _cachedVersionCheckerFields; private static List _cachedDiagnosticsFields; private static int _cachedAssemblyCount; private static DateTime _cacheBuiltAt = DateTime.MinValue; private static readonly TimeSpan CacheMaxAge = TimeSpan.FromMinutes(5.0); private static readonly Dictionary VersionSnapshotShapeCache = new Dictionary(); private static readonly Dictionary DiagnosticsShapeCache = new Dictionary(); public static IReadOnlyList CollectMergedRows(Func resolveDisplayName = null, Func resolveInstalledVersion = null) { if (resolveDisplayName == null) { resolveDisplayName = (string _) => (string)null; } if (resolveInstalledVersion == null) { resolveInstalledVersion = (string _) => (string)null; } List list = CollectVersionCheckerTelemetry(); List list2 = CollectDiagnosticsReports(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (VersionTelemetryEntry item in list) { dictionary[item.PluginGuid] = new MergedModHealthRow { PluginGuid = item.PluginGuid, DisplayName = (resolveDisplayName(item.PluginGuid) ?? item.PluginGuid), InstalledVersion = resolveInstalledVersion(item.PluginGuid), LastCheckUtc = item.LastCheckUtc, ExceptionCount = item.ExceptionCount, LastError = item.LastError }; } foreach (ModHealthReport item2 in list2) { if (!string.IsNullOrEmpty(item2.PluginGuid)) { if (!dictionary.TryGetValue(item2.PluginGuid, out var value)) { value = new MergedModHealthRow { PluginGuid = item2.PluginGuid }; dictionary[item2.PluginGuid] = value; } value.HasDiagnosticsReport = true; value.DisplayName = item2.DisplayName ?? value.DisplayName ?? resolveDisplayName(item2.PluginGuid) ?? item2.PluginGuid; value.InstalledVersion = item2.PluginVersion ?? value.InstalledVersion ?? resolveInstalledVersion(item2.PluginGuid); value.SharedCodeRevision = item2.SharedCodeRevision ?? value.SharedCodeRevision; value.IntegrationSummary = item2.IntegrationSummary ?? value.IntegrationSummary; value.Mode = item2.Mode ?? value.Mode; value.CharacterName = item2.CharacterName ?? value.CharacterName; value.DataLoaded = item2.DataLoaded ?? value.DataLoaded; value.LastPersistenceOutcome = item2.LastPersistenceOutcome ?? value.LastPersistenceOutcome; value.ReportedUtc = ((item2.ReportedUtc > value.ReportedUtc) ? item2.ReportedUtc : value.ReportedUtc); if (!string.IsNullOrEmpty(item2.LastError)) { value.LastError = item2.LastError; } } } foreach (MergedModHealthRow value2 in dictionary.Values) { if (string.IsNullOrEmpty(value2.DisplayName)) { value2.DisplayName = resolveDisplayName(value2.PluginGuid) ?? value2.PluginGuid; } if (string.IsNullOrEmpty(value2.InstalledVersion)) { value2.InstalledVersion = resolveInstalledVersion(value2.PluginGuid); } } return dictionary.Values.OrderBy((MergedModHealthRow r) => r.DisplayName, StringComparer.OrdinalIgnoreCase).ToList(); } public static SharedCodeSkewAnalysis AnalyzeSharedCodeSkew(IEnumerable rows) { List cachedDiagnosticsFields = GetCachedDiagnosticsFields(); Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); if (rows != null) { foreach (MergedModHealthRow row in rows) { if (!string.IsNullOrEmpty(row.SharedCodeRevision)) { if (!dictionary.TryGetValue(row.SharedCodeRevision, out var value)) { value = new List(); dictionary[row.SharedCodeRevision] = value; } value.Add(row.DisplayName ?? row.PluginGuid); } } } return new SharedCodeSkewAnalysis { HasSkew = (dictionary.Count > 1), VersionCheckerCopyCount = GetCachedVersionCheckerFields().Count, ModDiagnosticsCopyCount = cachedDiagnosticsFields.Count, ModsByRevision = ((IEnumerable>>)dictionary).ToDictionary((Func>, string>)((KeyValuePair> kvp) => kvp.Key), (Func>, IReadOnlyList>)((KeyValuePair> kvp) => kvp.Value.OrderBy((string n) => n, StringComparer.OrdinalIgnoreCase).ToList())) }; } public static string BuildCombinedDiagnosticDump(IEnumerable rows) { StringBuilder stringBuilder = new StringBuilder(); if (rows == null) { return stringBuilder.ToString(); } foreach (MergedModHealthRow row in rows) { row.AppendDiagnosticDump(stringBuilder); } return stringBuilder.ToString(); } private static List CollectVersionCheckerTelemetry() { List cachedVersionCheckerFields = GetCachedVersionCheckerFields(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (FieldInfo item in cachedVersionCheckerFields) { IDictionary dictionary2; try { dictionary2 = item.GetValue(null) as IDictionary; } catch { continue; } if (dictionary2 == null) { continue; } foreach (DictionaryEntry item2 in dictionary2) { string text = item2.Key as string; if (string.IsNullOrEmpty(text)) { continue; } object value = item2.Value; if (value != null) { VersionTelemetryEntry value2 = ReadVersionSnapshot(text, value); if (!dictionary.TryGetValue(text, out var value3)) { dictionary[text] = value2; continue; } dictionary[text] = new VersionTelemetryEntry { PluginGuid = text, LastCheckUtc = ((value2.LastCheckUtc > value3.LastCheckUtc) ? value2.LastCheckUtc : value3.LastCheckUtc), ExceptionCount = Math.Max(value3.ExceptionCount, value2.ExceptionCount), LastError = (string.IsNullOrEmpty(value2.LastError) ? value3.LastError : value2.LastError) }; } } } return dictionary.Values.ToList(); } private static List CollectDiagnosticsReports() { List cachedDiagnosticsFields = GetCachedDiagnosticsFields(); Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (FieldInfo item in cachedDiagnosticsFields) { IDictionary dictionary2; try { dictionary2 = item.GetValue(null) as IDictionary; } catch { continue; } if (dictionary2 == null) { continue; } foreach (DictionaryEntry item2 in dictionary2) { string text = item2.Key as string; if (string.IsNullOrEmpty(text)) { continue; } object value = item2.Value; if (value != null) { ModHealthReport modHealthReport = ReadDiagnosticsReport(text, value); if (!dictionary.TryGetValue(text, out var value2)) { dictionary[text] = modHealthReport; } else { dictionary[text] = MergeDiagnosticsReports(value2, modHealthReport); } } } } return dictionary.Values.ToList(); } private static ModHealthReport MergeDiagnosticsReports(ModHealthReport a, ModHealthReport b) { ModHealthReport modHealthReport = ((a.ReportedUtc >= b.ReportedUtc) ? a : b); ModHealthReport modHealthReport2 = ((modHealthReport == a) ? b : a); ModHealthReport modHealthReport3 = modHealthReport.Clone(); modHealthReport3.IntegrationSummary = modHealthReport.IntegrationSummary ?? modHealthReport2.IntegrationSummary; modHealthReport3.Mode = modHealthReport.Mode ?? modHealthReport2.Mode; modHealthReport3.CharacterName = modHealthReport.CharacterName ?? modHealthReport2.CharacterName; modHealthReport3.DataLoaded = modHealthReport.DataLoaded ?? modHealthReport2.DataLoaded; modHealthReport3.LastPersistenceOutcome = modHealthReport.LastPersistenceOutcome ?? modHealthReport2.LastPersistenceOutcome; modHealthReport3.SharedCodeRevision = modHealthReport.SharedCodeRevision ?? modHealthReport2.SharedCodeRevision; modHealthReport3.LastError = modHealthReport.LastError ?? modHealthReport2.LastError; modHealthReport3.DisplayName = modHealthReport.DisplayName ?? modHealthReport2.DisplayName; modHealthReport3.PluginVersion = modHealthReport.PluginVersion ?? modHealthReport2.PluginVersion; return modHealthReport3; } private static ModHealthReport ReadDiagnosticsReport(string guid, object reportObj) { DiagnosticsShape diagnosticsShape = GetDiagnosticsShape(reportObj.GetType()); ModHealthReport modHealthReport = new ModHealthReport { PluginGuid = guid }; modHealthReport.DisplayName = diagnosticsShape.DisplayName?.GetValue(reportObj) as string; modHealthReport.PluginVersion = diagnosticsShape.PluginVersion?.GetValue(reportObj) as string; modHealthReport.SharedCodeRevision = diagnosticsShape.SharedCodeRevision?.GetValue(reportObj) as string; modHealthReport.IntegrationSummary = diagnosticsShape.IntegrationSummary?.GetValue(reportObj) as string; modHealthReport.Mode = diagnosticsShape.Mode?.GetValue(reportObj) as string; modHealthReport.CharacterName = diagnosticsShape.CharacterName?.GetValue(reportObj) as string; modHealthReport.LastPersistenceOutcome = diagnosticsShape.LastPersistenceOutcome?.GetValue(reportObj) as string; modHealthReport.LastError = diagnosticsShape.LastError?.GetValue(reportObj) as string; if (diagnosticsShape.DataLoaded?.GetValue(reportObj) is bool value) { modHealthReport.DataLoaded = value; } if (diagnosticsShape.ReportedUtc?.GetValue(reportObj) is DateTime reportedUtc) { modHealthReport.ReportedUtc = reportedUtc; } return modHealthReport; } private static VersionTelemetryEntry ReadVersionSnapshot(string guid, object snap) { Type type = snap.GetType(); VersionTelemetryEntry result = new VersionTelemetryEntry { PluginGuid = guid }; SnapshotShape versionSnapshotShape = GetVersionSnapshotShape(type); if (versionSnapshotShape.LastCheckUtc?.GetValue(snap) is DateTime lastCheckUtc) { result.LastCheckUtc = lastCheckUtc; } if (versionSnapshotShape.ExceptionCount?.GetValue(snap) is int exceptionCount) { result.ExceptionCount = exceptionCount; } result.LastError = versionSnapshotShape.LastError?.GetValue(snap) as string; return result; } private static SnapshotShape GetVersionSnapshotShape(Type snapshotType) { lock (CacheLock) { if (VersionSnapshotShapeCache.TryGetValue(snapshotType, out var value)) { return value; } value = new SnapshotShape { LastCheckUtc = snapshotType.GetProperty("LastCheckUtc"), ExceptionCount = snapshotType.GetProperty("ExceptionCount"), LastError = snapshotType.GetProperty("LastError") }; VersionSnapshotShapeCache[snapshotType] = value; return value; } } private static DiagnosticsShape GetDiagnosticsShape(Type reportType) { lock (CacheLock) { if (DiagnosticsShapeCache.TryGetValue(reportType, out var value)) { return value; } value = new DiagnosticsShape { DisplayName = reportType.GetProperty("DisplayName"), PluginVersion = reportType.GetProperty("PluginVersion"), SharedCodeRevision = reportType.GetProperty("SharedCodeRevision"), IntegrationSummary = reportType.GetProperty("IntegrationSummary"), Mode = reportType.GetProperty("Mode"), CharacterName = reportType.GetProperty("CharacterName"), DataLoaded = reportType.GetProperty("DataLoaded"), LastPersistenceOutcome = reportType.GetProperty("LastPersistenceOutcome"), LastError = reportType.GetProperty("LastError"), ReportedUtc = reportType.GetProperty("ReportedUtc") }; DiagnosticsShapeCache[reportType] = value; return value; } } private static List GetCachedVersionCheckerFields() { return GetCachedStaticDictionaryFields("SunhavenMods.Shared.VersionChecker", "HealthByPluginGuid", ref _cachedVersionCheckerFields); } private static List GetCachedDiagnosticsFields() { return GetCachedStaticDictionaryFields("SunhavenMods.Shared.ModDiagnostics", "ReportsByPluginGuid", ref _cachedDiagnosticsFields); } private static List GetCachedStaticDictionaryFields(string typeName, string fieldName, ref List cache) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); lock (CacheLock) { if (cache != null && assemblies.Length == _cachedAssemblyCount && !(DateTime.UtcNow - _cacheBuiltAt > CacheMaxAge)) { return cache; } List list = new List(); Assembly[] array = assemblies; foreach (Assembly assembly in array) { Type type = null; try { type = assembly.GetType(typeName, throwOnError: false); } catch { } if (!(type == null)) { FieldInfo field = type.GetField(fieldName, BindingFlags.Static | BindingFlags.NonPublic); if (!(field == null)) { list.Add(field); } } } cache = list; _cachedAssemblyCount = assemblies.Length; _cacheBuiltAt = DateTime.UtcNow; return cache; } } } 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) { Assembly[] assemblies; if (namespaces != null && namespaces.Length != 0) { string[] array = namespaces; for (int i = 0; i < array.Length; i++) { Type type = AccessTools.TypeByName(array[i] + "." + typeName); if (type != null) { return type; } } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { array = namespaces; foreach (string text in array) { try { Type type2 = assembly.GetType(text + "." + typeName, throwOnError: false); if (type2 != null) { return type2; } } catch { } } } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { try { Type type3 = assembly2.GetTypes().FirstOrDefault((Type t) => namespaces.Any((string ns) => string.Equals(t.FullName, ns + "." + typeName, StringComparison.Ordinal) || (t.Name == typeName && string.Equals(t.Namespace, ns, StringComparison.Ordinal)))); if (type3 != null) { return type3; } } catch (ReflectionTypeLoadException ex) { Type type4 = ex.Types?.FirstOrDefault((Type t) => t != null && namespaces.Any((string ns) => string.Equals(t.FullName, ns + "." + typeName, StringComparison.Ordinal) || (t.Name == typeName && string.Equals(t.Namespace, ns, StringComparison.Ordinal)))); if (type4 != null) { return type4; } } } return null; } Type type5 = AccessTools.TypeByName(typeName); if (type5 != null) { return type5; } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly3 in assemblies) { try { type5 = assembly3.GetTypes().FirstOrDefault((Type t) => t.Name == typeName || t.FullName == typeName); if (type5 != null) { return type5; } } catch (ReflectionTypeLoadException) { } } return null; } public static Type FindModPlugin(string assemblyName) { if (string.IsNullOrWhiteSpace(assemblyName)) { return null; } Type type = FindType("Plugin", assemblyName); if (type != null && string.Equals(type.Assembly.GetName().Name, assemblyName, StringComparison.OrdinalIgnoreCase)) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (!string.Equals(assembly.GetName().Name, assemblyName, StringComparison.OrdinalIgnoreCase)) { continue; } Type type2 = assembly.GetType(assemblyName + ".Plugin", throwOnError: false); if (type2 != null) { return type2; } try { Type type3 = assembly.GetTypes().FirstOrDefault((Type t) => t.IsClass && !t.IsAbstract && t.Name == "Plugin"); if (type3 != null) { return type3; } } catch (ReflectionTypeLoadException ex) { Type type4 = ex.Types?.FirstOrDefault((Type t) => t != null && t.IsClass && !t.IsAbstract && t.Name == "Plugin"); if (type4 != null) { return type4; } } } return null; } public static MethodInfo GetStaticMethod(Type type, string methodName) { return type?.GetMethod(methodName, AllBindingFlags); } 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 GUIStyleHelper { public static class SunHavenColors { public static readonly Color Parchment = new Color(0.96f, 0.93f, 0.85f); public static readonly Color ParchmentDark = new Color(0.85f, 0.82f, 0.72f); public static readonly Color Wood = new Color(0.45f, 0.32f, 0.22f); public static readonly Color WoodLight = new Color(0.55f, 0.42f, 0.32f); public static readonly Color Gold = new Color(0.85f, 0.65f, 0.13f); public static readonly Color GoldDark = new Color(0.72f, 0.53f, 0.04f); public static readonly Color TextDark = new Color(0.2f, 0.15f, 0.1f); public static readonly Color TextLight = new Color(0.95f, 0.92f, 0.85f); public static readonly Color Success = new Color(0.2f, 0.6f, 0.2f); public static readonly Color Warning = new Color(0.8f, 0.6f, 0.2f); public static readonly Color Error = new Color(0.8f, 0.2f, 0.2f); public static readonly Color TransparentDark = new Color(0f, 0f, 0f, 0.7f); public static readonly Color TransparentLight = new Color(1f, 1f, 1f, 0.1f); } public static Texture2D MakeSolidTexture(Color color) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1, (TextureFormat)4, false); val.SetPixel(0, 0, color); val.Apply(); return val; } public static Texture2D MakeSolidTexture(int width, int height, Color color) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(width, height, (TextureFormat)4, false); Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < array.Length; i++) { array[i] = color; } val.SetPixels(array); val.Apply(); return val; } public static Texture2D MakeGradientTexture(int height, Color topColor, Color bottomColor) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0006: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (height <= 1) { return MakeSolidTexture(1, 1, topColor); } Texture2D val = new Texture2D(1, height, (TextureFormat)4, false); for (int i = 0; i < height; i++) { float num = (float)i / (float)(height - 1); val.SetPixel(0, i, Color.Lerp(bottomColor, topColor, num)); } val.Apply(); return val; } public static Texture2D MakeBorderedTexture(int width, int height, Color fillColor, Color borderColor, int borderWidth = 1) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(width, height, (TextureFormat)4, false); 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; } public static GUIStyle CreateWindowStyle(Texture2D background, int padding = 10) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_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_0036: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0046: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.window); val.normal.background = background; val.onNormal.background = background; val.padding = new RectOffset(padding, padding, padding, padding); val.border = new RectOffset(4, 4, 4, 4); return val; } public static GUIStyle CreateLabelStyle(Color textColor, int fontSize = 14, TextAnchor alignment = (TextAnchor)3, Texture2D background = null) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = fontSize, alignment = alignment, wordWrap = true }; val.normal.textColor = textColor; GUIStyle val2 = val; if ((Object)(object)background != (Object)null) { val2.normal.background = background; } return val2; } public static GUIStyle CreateButtonStyle(Color textColor, Texture2D normalBg, Texture2D hoverBg = null, int fontSize = 14) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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) //IL_003b: 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_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: 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_006b: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.button) { fontSize = fontSize, alignment = (TextAnchor)4 }; val.normal.textColor = textColor; val.normal.background = normalBg; val.hover.textColor = textColor; val.hover.background = hoverBg ?? normalBg; val.active.textColor = textColor; val.active.background = normalBg; return val; } public static GUIStyle CreateTextFieldStyle(Color textColor, Color bgColor, int fontSize = 14, int padding = 4) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //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: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown Texture2D background = MakeSolidTexture(bgColor); GUIStyle val = new GUIStyle(GUI.skin.textField) { fontSize = fontSize, padding = new RectOffset(padding, padding, padding, padding) }; val.normal.textColor = textColor; val.normal.background = background; val.focused.textColor = textColor; val.focused.background = background; val.hover.textColor = textColor; val.hover.background = background; return val; } public static GUIStyle CreateHeaderStyle(Color textColor, int fontSize = 18, TextAnchor alignment = (TextAnchor)4) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.label) { fontSize = fontSize, fontStyle = (FontStyle)1, alignment = alignment }; val.normal.textColor = textColor; return val; } public static GUIStyle CreateScrollViewStyle(Texture2D background) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown GUIStyle val = new GUIStyle(GUI.skin.scrollView); val.normal.background = background; return val; } } 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 && IsQuantumConsoleActiveInternal(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; } public static bool IsQuantumConsoleActive(ManualLogSource debugLog = null) { return IsQuantumConsoleActiveInternal(debugLog); } public static bool IsUnityUiTextInputFocused() { try { EventSystem current = EventSystem.current; GameObject val = ((current != null) ? current.currentSelectedGameObject : null); if ((Object)(object)val == (Object)null) { return false; } if ((Object)(object)val.GetComponent() != (Object)null) { return true; } return TryGetTmpInputField(val); } catch { return false; } } private static bool IsQuantumConsoleActiveInternal(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; } } public static class ModLocalization { private static readonly string[] SupportedLanguageCodes = new string[16] { "en", "da", "de", "es", "fr", "it", "ja", "ko", "nl", "pt", "pt-BR", "ru", "sv", "zh-CN", "zh-TW", "uk" }; private static readonly Dictionary LanguageAlias = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "pt-br", "pt-BR" }, { "pt_br", "pt-BR" }, { "zh-cn", "zh-CN" }, { "zh_cn", "zh-CN" }, { "zh-tw", "zh-TW" }, { "zh_tw", "zh-TW" } }; private static string _modId; private static Dictionary> _tables; private static ManualLogSource _log; private static bool _initialized; private static bool _forceEnglish; public static string CurrentLanguage { get; private set; } = "en"; public static bool ForceEnglish => _forceEnglish; public static bool IsReady { get { if (_initialized && _tables != null) { return _tables.Count > 0; } return false; } } public static event Action LanguageChanged { add { LanguageChangeWatcher.LanguageChanged += value; } remove { LanguageChangeWatcher.LanguageChanged -= value; } } public static void Init(string modId, Dictionary> tables, Harmony harmony, ManualLogSource log) { _modId = modId ?? string.Empty; _tables = tables ?? new Dictionary>(); _log = log; _initialized = true; RefreshCurrentLanguage(); LanguageChangeWatcher.EnsurePatched(harmony); } public static void SetForceEnglish(bool forceEnglish) { _forceEnglish = forceEnglish; ApplyEffectiveLanguage(); } internal static void OnGameLanguageChanged(string languageCode) { if (_tables == null || _forceEnglish) { return; } string text = NormalizeLanguageCode(languageCode); if (!string.Equals(CurrentLanguage, text, StringComparison.OrdinalIgnoreCase)) { CurrentLanguage = text; ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("[" + _modId + "] Language changed to " + CurrentLanguage)); } } } public static void RefreshCurrentLanguage() { ApplyEffectiveLanguage(); } private static void ApplyEffectiveLanguage() { if (_forceEnglish) { CurrentLanguage = "en"; } else { RefreshCurrentLanguageFromGame(); } } private static void RefreshCurrentLanguageFromGame() { try { string currentLanguageCode = LocalizationManager.CurrentLanguageCode; if (!string.IsNullOrWhiteSpace(currentLanguageCode)) { CurrentLanguage = NormalizeLanguageCode(currentLanguageCode); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("[" + _modId + "] Failed to read LocalizationManager.CurrentLanguageCode: " + ex.Message)); } CurrentLanguage = "en"; } } public static string T(string key) { if (!TryT(key, out string value)) { return key; } return value; } public static string T(string key, params object[] args) { string text = T(key); if (args == null || args.Length == 0) { return text; } try { return string.Format(CultureInfo.InvariantCulture, text, args); } catch (FormatException ex) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)("[" + _modId + "] Format failed for key '" + key + "': " + ex.Message)); } return text; } } public static bool TryT(string key, out string value) { value = null; if (string.IsNullOrEmpty(key)) { return false; } if (_tables == null || !_tables.TryGetValue(key, out Dictionary value2) || value2 == null) { return false; } if (TryGetForLanguage(value2, CurrentLanguage, out value)) { return true; } if (!string.Equals(CurrentLanguage, "en", StringComparison.OrdinalIgnoreCase) && TryGetForLanguage(value2, "en", out value)) { return true; } return false; } private static bool TryGetForLanguage(Dictionary translations, string languageCode, out string value) { value = null; if (translations == null) { return false; } string text = NormalizeLanguageCode(languageCode); if (translations.TryGetValue(text, out value) && !string.IsNullOrEmpty(value)) { return true; } foreach (KeyValuePair translation in translations) { if (string.Equals(translation.Key, text, StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(translation.Value)) { value = translation.Value; return true; } } return false; } public static string NormalizeLanguageCode(string code) { if (string.IsNullOrWhiteSpace(code)) { return "en"; } string text = code.Trim(); if (LanguageAlias.TryGetValue(text, out string value)) { return value; } string[] supportedLanguageCodes = SupportedLanguageCodes; foreach (string text2 in supportedLanguageCodes) { if (string.Equals(text2, text, StringComparison.OrdinalIgnoreCase)) { return text2; } } return "en"; } public static Dictionary> ParseStringsJson(string json) { Dictionary> dictionary = new Dictionary>(StringComparer.Ordinal); if (string.IsNullOrWhiteSpace(json)) { return dictionary; } int pos = 0; Dictionary dictionary2 = MinimalJsonParser.ParseObject(json, ref pos); if (dictionary2 == null) { return dictionary; } foreach (KeyValuePair item in dictionary2) { if (!(item.Value is Dictionary dictionary3)) { continue; } Dictionary dictionary4 = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair item2 in dictionary3) { if (item2.Value is string value) { dictionary4[NormalizeLanguageCode(item2.Key)] = value; } } if (dictionary4.Count > 0) { dictionary[item.Key] = dictionary4; } } return dictionary; } public static Dictionary> LoadEmbeddedStrings(Assembly assembly, string resourceName, ManualLogSource log = null) { try { using Stream stream = assembly.GetManifestResourceStream(resourceName); if (stream == null) { if (log != null) { log.LogError((object)("Localization resource not found: " + resourceName)); } return new Dictionary>(); } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8); return ParseStringsJson(streamReader.ReadToEnd()); } catch (Exception ex) { if (log != null) { log.LogError((object)("Failed to load localization resource '" + resourceName + "': " + ex.Message)); } return new Dictionary>(); } } public static void Shutdown() { _log = null; } } public static class LanguageChangeWatcher { private static bool _patched; public static event Action LanguageChanged; public static void EnsurePatched(Harmony harmony) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown if (_patched || harmony == null) { return; } try { MethodInfo methodInfo = AccessTools.Method(typeof(LanguageChangeWatcher), "OnSetLanguageAndCode", (Type[])null, (Type[])null); harmony.Patch((MethodBase)AccessTools.Method(typeof(LocalizationManager), "SetLanguageAndCode", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _patched = true; } catch (Exception innerException) { throw new InvalidOperationException("Failed to patch LocalizationManager.SetLanguageAndCode", innerException); } } private static void OnSetLanguageAndCode(string LanguageName, string LanguageCode) { string text = ModLocalization.NormalizeLanguageCode(string.IsNullOrWhiteSpace(LanguageCode) ? LocalizationManager.CurrentLanguageCode : LanguageCode); ModLocalization.OnGameLanguageChanged(text); LanguageChangeWatcher.LanguageChanged?.Invoke(text); } internal static void RaiseLanguageChanged(string languageCode) { string obj = ModLocalization.NormalizeLanguageCode(languageCode); LanguageChangeWatcher.LanguageChanged?.Invoke(obj); } } public static class LocalizationBootstrap { public static ConfigEntry BindForceEnglish(ConfigFile config) { ConfigEntry entry = config.Bind("Localization", "ForceEnglish", false, "Keep this mod's UI in English and ignore Sun Haven's in-game language setting."); ApplyForceEnglish(entry.Value); entry.SettingChanged += delegate { ApplyForceEnglish(entry.Value); }; return entry; } private static void ApplyForceEnglish(bool forceEnglish) { ModLocalization.SetForceEnglish(forceEnglish); LanguageChangeWatcher.RaiseLanguageChanged(ModLocalization.CurrentLanguage); } public static void Init(string pluginGuid, Harmony harmony, ManualLogSource log, Assembly assembly = null) { if ((object)assembly == null) { assembly = Assembly.GetCallingAssembly(); } Dictionary> tables = ModLocalization.LoadEmbeddedStrings(assembly, pluginGuid + ".Localization.strings.json", log); ModLocalization.Init(pluginGuid, tables, harmony, log); } public static void EnsureInitialized(string pluginGuid, Harmony harmony, ManualLogSource log, Assembly assembly = null) { if (!ModLocalization.IsReady) { Init(pluginGuid, harmony, log, assembly); } } } public static class RelationshipHeartRules { public const int PointsPerHeart = 5; public const int HeartsPerRow = 5; public const int RomanceMilestoneSlotIndex = 14; public static int GetMaxHearts(float points) { if (points >= 75f) { return 20; } if (points >= 50f) { return 15; } return 10; } public static int GetFullHearts(float points, int maxHearts) { return Mathf.Clamp(Mathf.FloorToInt(points / 5f), 0, maxHearts); } public static bool IsMilestoneSlot(int slotIndex, bool romanceable, int maxHearts) { if (romanceable && maxHearts >= 15) { return slotIndex == 14; } return false; } } public readonly struct RelationshipHeartLayout { public float BgWidth { get; } public float BgHeight { get; } public float IconWidth { get; } public float IconHeight { get; } public float FillWidth { get; } public float FillHeight { get; } public float Gutter { get; } public static RelationshipHeartLayout Dashboard { get; } = new RelationshipHeartLayout(16f, 14f, 22f, 19f, 12f, 10f, 2f); public static RelationshipHeartLayout Compact { get; } = new RelationshipHeartLayout(8f, 7f, 12f, 10f, 12f, 10f, 2f); public RelationshipHeartLayout(float bgWidth, float bgHeight, float iconWidth, float iconHeight, float fillWidth, float fillHeight, float gutter) { BgWidth = bgWidth; BgHeight = bgHeight; IconWidth = iconWidth; IconHeight = iconHeight; FillWidth = fillWidth; FillHeight = fillHeight; Gutter = gutter; } } public static class RelationshipHeartAssetLoader { private static Texture2D _bgStandard; private static Texture2D _bgMilestone; private static Texture2D _fillStandard; private static Texture2D _fillMilestone; private static Texture2D _heartIcon; private static bool _loadAttempted; public static bool IsLoaded { get { if ((Object)(object)_heartIcon != (Object)null && (Object)(object)_bgStandard != (Object)null) { return (Object)(object)_bgMilestone != (Object)null; } return false; } } public static Texture2D BgStandard => _bgStandard; public static Texture2D BgMilestone => _bgMilestone; public static Texture2D FillStandard => _fillStandard; public static Texture2D FillMilestone => _fillMilestone; public static Texture2D HeartIcon => _heartIcon; public static void EnsureLoaded() { if (!_loadAttempted) { _loadAttempted = true; string text = ResolveAssetsDirectory(); if (!string.IsNullOrEmpty(text)) { _bgStandard = LoadPng(Path.Combine(text, "hearts_bg_1-20.png")); _bgMilestone = LoadPng(Path.Combine(text, "hearts_bg_21-25.png")); _fillStandard = LoadPng(Path.Combine(text, "hearts_fill_1-10.png")); _fillMilestone = LoadPng(Path.Combine(text, "hearts_fill_21-25.png")); _heartIcon = LoadPng(Path.Combine(text, "heart_icon.png")); } } } private static string ResolveAssetsDirectory() { try { string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); if (string.IsNullOrEmpty(directoryName)) { return null; } string text = Path.Combine(directoryName, "Assets", "Relationships"); return Directory.Exists(text) ? text : null; } catch { return null; } } private static Texture2D LoadPng(string path) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (!File.Exists(path)) { return null; } try { byte[] array = File.ReadAllBytes(path); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { filterMode = (FilterMode)0, wrapMode = (TextureWrapMode)1 }; if (!ImageConversion.LoadImage(val, array)) { Object.Destroy((Object)(object)val); return null; } return val; } catch { return null; } } } public static class RelationshipHeartRenderer { private static GUIStyle _fallbackStyle; public static float GridWidth(int maxHearts, RelationshipHeartLayout layout, float scale, bool singleRow = false) { int num = (singleRow ? maxHearts : Mathf.Min(5, maxHearts)); float num2 = ScaledIconWidth(layout, scale); return (float)num * num2 + (float)Mathf.Max(0, num - 1) * Scaled(layout.Gutter, scale); } public static float GridHeight(int maxHearts, RelationshipHeartLayout layout, float scale, bool singleRow = false) { if (singleRow) { return ScaledIconHeight(layout, scale); } int num = Mathf.Max(1, Mathf.CeilToInt((float)maxHearts / 5f)); float num2 = ScaledIconHeight(layout, scale); return (float)num * num2 + (float)Mathf.Max(0, num - 1) * Scaled(layout.Gutter, scale); } public static void DrawGrid(Rect origin, float points, bool romanceable, float scale, RelationshipHeartLayout layout, bool singleRow = false) { //IL_007e: Unknown result type (might be due to invalid IL or missing references) RelationshipHeartAssetLoader.EnsureLoaded(); int maxHearts = RelationshipHeartRules.GetMaxHearts(points); int fullHearts = RelationshipHeartRules.GetFullHearts(points, maxHearts); float num = ScaledIconWidth(layout, scale); float num2 = ScaledIconHeight(layout, scale); for (int i = 0; i < maxHearts; i++) { int num3 = ((!singleRow) ? (i / 5) : 0); int num4 = (singleRow ? i : (i % 5)); Rect cell = new Rect(((Rect)(ref origin)).x + (float)num4 * (num + Scaled(layout.Gutter, scale)), ((Rect)(ref origin)).y + (float)num3 * (num2 + Scaled(layout.Gutter, scale)), num, num2); bool filled = i < fullHearts; bool milestone = RelationshipHeartRules.IsMilestoneSlot(i, romanceable, maxHearts); DrawSlot(cell, filled, milestone, scale, layout); } } public static float ScaledIconWidth(RelationshipHeartLayout layout, float scale) { return Scaled(layout.IconWidth, scale); } public static float ScaledIconHeight(RelationshipHeartLayout layout, float scale) { return Scaled(layout.IconHeight, scale); } private static void DrawSlot(Rect cell, bool filled, bool milestone, float scale, RelationshipHeartLayout layout) { //IL_0007: 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_0053: Unknown result type (might be due to invalid IL or missing references) if (!RelationshipHeartAssetLoader.IsLoaded) { DrawFallback(cell, filled); return; } Texture2D tex = (milestone ? RelationshipHeartAssetLoader.BgMilestone : RelationshipHeartAssetLoader.BgStandard); DrawTextureCentered(cell, tex, Scaled(layout.BgWidth, scale), Scaled(layout.BgHeight, scale)); if (filled) { Texture2D tex2 = (milestone ? RelationshipHeartAssetLoader.FillMilestone : RelationshipHeartAssetLoader.FillStandard); DrawTextureCentered(cell, tex2, Scaled(layout.FillWidth, scale), Scaled(layout.FillHeight, scale)); } } private static void DrawTextureCentered(Rect cell, Texture2D tex, float w, float h) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)tex == (Object)null)) { GUI.DrawTexture(new Rect(((Rect)(ref cell)).x + (((Rect)(ref cell)).width - w) * 0.5f, ((Rect)(ref cell)).y + (((Rect)(ref cell)).height - h) * 0.5f, w, h), (Texture)(object)tex, (ScaleMode)0, true); } } private static void DrawFallback(Rect cell, bool filled) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown if (_fallbackStyle == null) { _fallbackStyle = new GUIStyle(GUI.skin.label) { alignment = (TextAnchor)4 }; } _fallbackStyle.fontSize = Mathf.Max(10, Mathf.RoundToInt(((Rect)(ref cell)).height * 0.7f)); GUI.Label(cell, filled ? "♥" : "♡", _fallbackStyle); } private static float Scaled(float value, float scale) { return value * scale; } } } namespace HavensAlmanac { [BepInPlugin("com.azraelgodking.havensalmanac", "Haven's Almanac", "2.2.1")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static Func <0>__ResolveSingletonInstance; public static UnityAction <1>__OnOvernightComplete; } private static AlmanacDataAggregator _staticAggregator; private static AlmanacHUD _staticHUD; private static AlmanacDashboard _staticDashboard; private static DailyBriefing _staticBriefing; private static GameObject _persistentRunner; private static AlmanacPersistentRunner _persistentRunnerComponent; private static bool _overnightHooked; private static UnityAction _overnightCallback; private Harmony _harmony; private bool _applicationQuitting; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static ConfigFile ConfigFile { get; private set; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigFile = CreateNamedConfig(); ConfigFileHelper.ReplacePluginConfig((BaseUnityPlugin)(object)this, ConfigFile, (Action)Log.LogWarning); Log.LogInfo((object)"Loading Haven's Almanac v2.2.1"); AlmanacConfig.Initialize(ConfigFile); LocalizationBootstrap.BindForceEnglish(ConfigFile); CreatePersistentRunner(); _staticAggregator = new AlmanacDataAggregator(); InitializeIntegrations(); CreateUIComponents(); ApplyPatches(); SceneManager.sceneLoaded += OnSceneLoaded; if (AlmanacConfig.CheckForUpdates.Value) { VersionChecker.CheckForUpdate("com.azraelgodking.havensalmanac", "2.2.1", Log, delegate(VersionChecker.VersionCheckResult result) { result.NotifyUpdateAvailable(Log); }); } int integrationModCount = _staticAggregator.IntegrationModCount; Log.LogInfo((object)string.Format("{0} loaded with {1} integration{2}", "Haven's Almanac", integrationModCount, (integrationModCount == 1) ? string.Empty : "s")); ModDiagnostics.LogModStartup(Log, "com.azraelgodking.havensalmanac", "Haven's Almanac", "2.2.1", string.Format("{0} providers, {1}", integrationModCount, ModHealthIntegrationSummary.Build(("DevTools", "com.azraelgodking.havendevtools"), ("Todo", "com.azraelgodking.sunhaventodo"), ("Birthday", "com.azraelgodking.squirrelsbirthdayreminder"), ("SMUT", "com.azraelgodking.sunhavenmuseumutilitytracker"), ("SenpaisChest", "com.azraelgodking.senpaischest"), ("Vault", "com.azraelgodking.thevault"), ("Birthright", "com.azraelgodking.havensbirthright"), ("CropOptimizer", "com.azraelgodking.cropoptimizer"), ("GiftingAssistant", "com.azraelgodking.giftingassistant"))), "startup", false); if (integrationModCount == 0) { Log.LogWarning((object)"No supported companion mods detected. Haven's Almanac is most useful alongside SunhavenTodo, Birthday Reminder, Museum Tracker, Senpai's Chest, The Vault, Haven's Birthright, Haven Dev Tools, Crop Optimizer, or Gifting Assistant."); } } private static ConfigFile CreateNamedConfig() { return ConfigFileHelper.CreateNamedConfig("com.azraelgodking.havensalmanac", "HavensAlmanac.cfg", delegate(string message) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)message); } }); } 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("HavensAlmanac_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _persistentRunnerComponent = _persistentRunner.AddComponent(); Log.LogInfo((object)"[PersistentRunner] Created"); } } private void InitializeIntegrations() { Dictionary pluginInfos = Chainloader.PluginInfos; _staticAggregator.RegisterProvider(new RelationshipDataProvider()); if (pluginInfos.ContainsKey("com.azraelgodking.havendevtools")) { _staticAggregator.RegisterProvider(new ModHealthBridgeProvider()); } TryRegisterProvider(pluginInfos, "com.azraelgodking.sunhaventodo", () => new TodoDataProvider(), "SunhavenTodo"); TryRegisterProvider(pluginInfos, "com.azraelgodking.squirrelsbirthdayreminder", () => new BirthdayDataProvider(), "BirthdayReminder"); TryRegisterProvider(pluginInfos, "com.azraelgodking.sunhavenmuseumutilitytracker", () => new MuseumDataProvider(), "S.M.U.T."); TryRegisterProvider(pluginInfos, "com.azraelgodking.senpaischest", () => new ChestDataProvider(), "SenpaisChest"); TryRegisterProvider(pluginInfos, "com.azraelgodking.thevault", () => new VaultDataProvider(), "TheVault"); TryRegisterProvider(pluginInfos, "com.azraelgodking.havensbirthright", () => new BirthrightDataProvider(), "HavensBirthright"); TryRegisterProvider(pluginInfos, "com.azraelgodking.havendevtools", () => new DevToolsDataProvider(), "HavenDevTools"); TryRegisterProvider(pluginInfos, "com.azraelgodking.cropoptimizer", () => new CropOptimizerDataProvider(), "CropOptimizer"); TryRegisterProvider(pluginInfos, "com.azraelgodking.giftingassistant", () => new GiftingAssistantDataProvider(), "GiftingAssistant"); } private void TryRegisterProvider(Dictionary pluginInfos, string guid, Func factory, string displayName) { try { if (pluginInfos.ContainsKey(guid)) { _staticAggregator.RegisterProvider(factory()); Log.LogInfo((object)("[Integration] " + displayName + " detected and registered")); } } catch (Exception ex) { Log.LogWarning((object)("[Integration] Failed to load " + displayName + " provider: " + ex.Message)); } } private void CreateUIComponents() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //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) //IL_004d: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown try { GameObject val = new GameObject("HavensAlmanac_HUD"); Object.DontDestroyOnLoad((Object)val); _staticHUD = val.AddComponent(); _staticHUD.Initialize(_staticAggregator); _staticHUD.SetScale(AlmanacConfig.StaticUIScale); WireHudPositionPersistence(); GameObject val2 = new GameObject("HavensAlmanac_Dashboard"); Object.DontDestroyOnLoad((Object)val2); _staticDashboard = val2.AddComponent(); _staticDashboard.Initialize(_staticAggregator); _staticDashboard.SetScale(AlmanacConfig.StaticUIScale); GameObject val3 = new GameObject("HavensAlmanac_Briefing"); Object.DontDestroyOnLoad((Object)val3); _staticBriefing = val3.AddComponent(); _staticBriefing.Initialize(_staticAggregator); _staticBriefing.SetScale(AlmanacConfig.StaticUIScale); } catch (Exception arg) { Log.LogError((object)$"[UI] Error creating UI components: {arg}"); } } public void ApplyUIScaleToAllUI() { float staticUIScale = AlmanacConfig.StaticUIScale; _staticHUD?.SetScale(staticUIScale); _staticDashboard?.SetScale(staticUIScale); _staticBriefing?.SetScale(staticUIScale); } public static void EnsureUIComponentsExist() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0158: 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("HavensAlmanac_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _persistentRunnerComponent = _persistentRunner.AddComponent(); } if ((Object)(object)_staticHUD == (Object)null) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"[EnsureUI] Recreating HUD..."); } GameObject val = new GameObject("HavensAlmanac_HUD"); Object.DontDestroyOnLoad((Object)val); _staticHUD = val.AddComponent(); _staticHUD.Initialize(_staticAggregator); _staticHUD.SetScale(AlmanacConfig.StaticUIScale); WireHudPositionPersistence(); } if ((Object)(object)_staticDashboard == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)"[EnsureUI] Recreating Dashboard..."); } GameObject val2 = new GameObject("HavensAlmanac_Dashboard"); Object.DontDestroyOnLoad((Object)val2); _staticDashboard = val2.AddComponent(); _staticDashboard.Initialize(_staticAggregator); _staticDashboard.SetScale(AlmanacConfig.StaticUIScale); } if ((Object)(object)_staticBriefing == (Object)null) { ManualLogSource log4 = Log; if (log4 != null) { log4.LogInfo((object)"[EnsureUI] Recreating Briefing..."); } GameObject val3 = new GameObject("HavensAlmanac_Briefing"); Object.DontDestroyOnLoad((Object)val3); _staticBriefing = val3.AddComponent(); _staticBriefing.Initialize(_staticAggregator); _staticBriefing.SetScale(AlmanacConfig.StaticUIScale); } } catch (Exception ex) { ManualLogSource log5 = Log; if (log5 != null) { log5.LogError((object)("[EnsureUI] Error: " + ex.Message)); } } } private void ApplyPatches() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown _harmony = new Harmony("com.azraelgodking.havensalmanac"); LocalizationBootstrap.Init("com.azraelgodking.havensalmanac", _harmony, Log); 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(Plugin), "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 static void OnPlayerInitialized(object __instance) { try { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[Almanac] Player initialized"); } EnsureUIComponentsExist(); _staticAggregator?.RefreshAll(); ResetOvernightHook(); TryHookOvernightEvent(); } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)("Error in OnPlayerInitialized: " + ex.Message)); } } } public static void ResetOvernightHook() { OvernightHookUtility.TryUnhookOvernightEvent(ref _overnightHooked, ref _overnightCallback, ResolveSingletonInstance, delegate(string message) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("[Almanac] " + message)); } }, delegate(string message) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[Almanac] " + message)); } }); } public static void TryHookOvernightEvent() { //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.<1>__OnOvernightComplete; if (obj == null) { UnityAction val = OnOvernightComplete; <>O.<1>__OnOvernightComplete = val; obj = (object)val; } OvernightHookUtility.TryHookOvernightEvent(ref _overnightHooked, ref _overnightCallback, (UnityAction)obj, ResolveSingletonInstance, delegate(string message) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)message); } }, delegate(string message) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)message); } }); } private static void OnOvernightComplete() { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[Almanac] Day started - refreshing data and showing briefing"); } _staticAggregator?.RefreshAll(); _staticBriefing?.ShowBriefing(); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { if (((Scene)(ref scene)).name == "MainMenu" || ((Scene)(ref scene)).name == "Bootstrap") { Log.LogInfo((object)"[Almanac] Main menu detected - hiding UI"); _staticHUD?.Hide(); _staticDashboard?.Hide(); _staticBriefing?.Hide(); ResetOvernightHook(); } else { EnsureUIComponentsExist(); } } private static void WireHudPositionPersistence() { if ((Object)(object)_staticHUD == (Object)null) { return; } _staticHUD.OnPositionChanged = delegate(float x, float y) { AlmanacConfig.StaticHUDPositionX = x; AlmanacConfig.StaticHUDPositionY = y; ConfigEntry hUDPositionX = AlmanacConfig.HUDPositionX; if (hUDPositionX != null) { ((ConfigEntryBase)hUDPositionX).SetSerializedValue(x.ToString()); } ConfigEntry hUDPositionY = AlmanacConfig.HUDPositionY; if (hUDPositionY != null) { ((ConfigEntryBase)hUDPositionY).SetSerializedValue(y.ToString()); } }; } private static object ResolveSingletonInstance(Type targetType) { if (targetType == null) { return null; } Type type = AccessTools.TypeByName("Wish.SingletonBehaviour`1"); if (type == null) { return null; } return AccessTools.Property(type.MakeGenericType(targetType), "Instance")?.GetValue(null); } internal static AlmanacDataAggregator GetDataAggregator() { return _staticAggregator; } internal static AlmanacHUD GetAlmanacHUD() { return _staticHUD; } internal static AlmanacDashboard GetAlmanacDashboard() { return _staticDashboard; } internal static DailyBriefing GetDailyBriefing() { return _staticBriefing; } private void OnDestroy() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) SceneManager.sceneLoaded -= OnSceneLoaded; Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name ?? string.Empty; string text2 = text.ToLowerInvariant(); if (_applicationQuitting || !Application.isPlaying || text2.Contains("menu") || text2.Contains("title")) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)("[Lifecycle] Plugin OnDestroy during expected teardown (scene: " + text + ")")); } } else { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("[Lifecycle] Plugin OnDestroy outside expected teardown (scene: " + text + ")")); } } Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } private void OnApplicationQuit() { _applicationQuitting = true; } } public class AlmanacPersistentRunner : MonoBehaviour { private void Update() { DetectHotkeys(); } private void DetectHotkeys() { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) if (!TextInputFocusGuard.ShouldDeferModHotkeys(Plugin.Log)) { bool flag = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305); if (Input.GetKeyDown(AlmanacConfig.StaticDashboardToggleKey) && (!AlmanacConfig.StaticDashboardRequireCtrl || flag)) { Plugin.EnsureUIComponentsExist(); Plugin.GetAlmanacDashboard()?.Toggle(); } if (Input.GetKeyDown(AlmanacConfig.StaticHUDToggleKey)) { Plugin.EnsureUIComponentsExist(); Plugin.GetAlmanacHUD()?.Toggle(); } } } private void OnDestroy() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) Scene activeScene = SceneManager.GetActiveScene(); string text = (((Scene)(ref activeScene)).name ?? string.Empty).ToLowerInvariant(); if (!Application.isPlaying || text.Contains("menu") || text.Contains("title")) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PersistentRunner] OnDestroy during app quit/menu unload (expected)."); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[PersistentRunner] OnDestroy outside quit/menu (unexpected)."); } } } } public static class PluginInfo { public const string PLUGIN_GUID = "com.azraelgodking.havensalmanac"; public const string PLUGIN_NAME = "Haven's Almanac"; public const string PLUGIN_VERSION = "2.2.1"; } } namespace HavensAlmanac.UI { public class AlmanacDashboard : MonoBehaviour { private const int WINDOW_ID = 98781; private const float BASE_WIDTH = 500f; private const float BASE_HEIGHT = 550f; private float _scale = 1f; private AlmanacDataAggregator _aggregator; private Rect _windowRect; private bool _isVisible; private Vector2 _scrollPosition; private Dictionary _sectionExpanded = new Dictionary(); private bool _stylesInitialized; private GUIStyle _windowStyle; private GUIStyle _titleStyle; private GUIStyle _sectionHeaderStyle; private GUIStyle _sectionHeaderExpandedStyle; private GUIStyle _contentStyle; private GUIStyle _closeButtonStyle; private GUIStyle _noModsStyle; private Texture2D _bgTexture; private Texture2D _sectionBgTexture; private Texture2D _sectionExpandedBgTexture; private float Width => 500f * _scale; private float Height => 550f * _scale; public bool IsVisible => _isVisible; private float Scaled(float value) { return value * _scale; } private int ScaledFont(int baseSize) { return Mathf.Max(8, Mathf.RoundToInt((float)baseSize * _scale)); } private int ScaledInt(float value) { return Mathf.RoundToInt(value * _scale); } public void Initialize(AlmanacDataAggregator aggregator) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) _aggregator = aggregator; _windowRect = new Rect(((float)Screen.width - Width) / 2f, ((float)Screen.height - Height) / 2f, Width, Height); } public void SetScale(float scale) { _scale = Mathf.Clamp(scale, 0.5f, 2.5f); _stylesInitialized = false; } public void Show() { _aggregator?.RefreshAll(); _isVisible = true; } public void Hide() { _isVisible = false; } public void Toggle() { if (_isVisible) { Hide(); } else { Show(); } } private void Update() { if (_isVisible && Input.GetKeyDown((KeyCode)27)) { Hide(); } } private void OnGUI() { //IL_0026: 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_0047: Expected O, but got Unknown //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 (_isVisible && _aggregator != null) { if (!_stylesInitialized) { InitializeStyles(); } _windowRect = GUI.Window(98781, _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); } } private void DrawWindow(int id) { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("almanac.dashboard.title"), _titleStyle, Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("X", _closeButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(24f)), GUILayout.Height(Scaled(24f)) })) { Hide(); } GUILayout.EndHorizontal(); GUILayout.Space(Scaled(6f)); if (!_aggregator.HasDashboardSections) { GUILayout.Label(ModLocalization.T("almanac.dashboard.noMods"), _noModsStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("almanac.dashboard.installHint"), _noModsStyle, Array.Empty()); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, Scaled(30f))); return; } _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, Array.Empty()); foreach (IModDataProvider provider in _aggregator.Providers) { if (!_sectionExpanded.ContainsKey(provider.ModName)) { _sectionExpanded[provider.ModName] = true; } bool flag = _sectionExpanded[provider.ModName]; GUIStyle val = (flag ? _sectionHeaderExpandedStyle : _sectionHeaderStyle); string text = (flag ? "▼" : "▶"); string providerError = _aggregator.GetProviderError(provider); string text2 = ((providerError != null) ? " [!]" : ""); if (GUILayout.Button(text + " " + provider.ModIcon + " " + provider.ModName + text2 + " — " + provider.HudSummary, val, Array.Empty())) { _sectionExpanded[provider.ModName] = !flag; } if (flag) { GUILayout.BeginVertical(_contentStyle, Array.Empty()); try { if (providerError != null) { GUILayout.Label(ModLocalization.T("almanac.dashboard.refreshError", providerError), _contentStyle, Array.Empty()); } provider.DrawDashboardSection(); } catch (Exception ex) { GUILayout.Label(ModLocalization.T("almanac.dashboard.error", ex.Message), Array.Empty()); } GUILayout.EndVertical(); } GUILayout.Space(Scaled(4f)); } GUILayout.EndScrollView(); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, Scaled(30f))); } private void InitializeStyles() { //IL_0085: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Expected O, but got Unknown //IL_012b: Expected O, but got Unknown //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Expected O, but got Unknown //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Expected O, but got Unknown //IL_0201: Expected O, but got Unknown //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_0261: Expected O, but got Unknown //IL_0282: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Expected O, but got Unknown //IL_02d4: Expected O, but got Unknown //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02f6: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Expected O, but got Unknown //IL_0322: Unknown result type (might be due to invalid IL or missing references) //IL_0327: 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_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_034f: Expected O, but got Unknown //IL_0369: Unknown result type (might be due to invalid IL or missing references) _stylesInitialized = true; Color color = default(Color); ((Color)(ref color))..ctor(0.12f, 0.1f, 0.08f, 0.96f); Color color2 = default(Color); ((Color)(ref color2))..ctor(0.18f, 0.15f, 0.11f, 0.9f); Color color3 = default(Color); ((Color)(ref color3))..ctor(0.22f, 0.18f, 0.13f, 0.9f); Color textColor = default(Color); ((Color)(ref textColor))..ctor(0.95f, 0.85f, 0.55f); Color textColor2 = default(Color); ((Color)(ref textColor2))..ctor(0.91f, 0.87f, 0.82f); _bgTexture = GUIStyleHelper.MakeSolidTexture(color); _sectionBgTexture = GUIStyleHelper.MakeSolidTexture(color2); _sectionExpandedBgTexture = GUIStyleHelper.MakeSolidTexture(color3); _windowStyle = new GUIStyle(GUI.skin.window) { padding = new RectOffset(ScaledInt(12f), ScaledInt(12f), ScaledInt(10f), ScaledInt(10f)), border = new RectOffset(ScaledInt(2f), ScaledInt(2f), ScaledInt(2f), ScaledInt(2f)) }; _windowStyle.normal.background = _bgTexture; _windowStyle.onNormal.background = _bgTexture; _titleStyle = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1, fontSize = ScaledFont(16), alignment = (TextAnchor)3 }; _titleStyle.normal.textColor = textColor; _sectionHeaderStyle = new GUIStyle(GUI.skin.button) { fontStyle = (FontStyle)1, fontSize = ScaledFont(13), alignment = (TextAnchor)3, padding = new RectOffset(ScaledInt(8f), ScaledInt(8f), ScaledInt(6f), ScaledInt(6f)) }; _sectionHeaderStyle.normal.background = _sectionBgTexture; _sectionHeaderStyle.normal.textColor = textColor2; _sectionHeaderStyle.hover.background = _sectionExpandedBgTexture; _sectionHeaderStyle.hover.textColor = textColor; _sectionHeaderExpandedStyle = new GUIStyle(_sectionHeaderStyle); _sectionHeaderExpandedStyle.normal.background = _sectionExpandedBgTexture; _sectionHeaderExpandedStyle.normal.textColor = textColor; _contentStyle = new GUIStyle(GUI.skin.box) { padding = new RectOffset(ScaledInt(12f), ScaledInt(12f), ScaledInt(8f), ScaledInt(8f)) }; _contentStyle.normal.textColor = textColor2; _closeButtonStyle = new GUIStyle(GUI.skin.button) { fontStyle = (FontStyle)1, fontSize = ScaledFont(14), alignment = (TextAnchor)4 }; _noModsStyle = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(13), fontStyle = (FontStyle)2, alignment = (TextAnchor)4, wordWrap = true }; _noModsStyle.normal.textColor = new Color(0.7f, 0.6f, 0.5f); } private void OnDestroy() { if ((Object)(object)_bgTexture != (Object)null) { Object.Destroy((Object)(object)_bgTexture); } if ((Object)(object)_sectionBgTexture != (Object)null) { Object.Destroy((Object)(object)_sectionBgTexture); } if ((Object)(object)_sectionExpandedBgTexture != (Object)null) { Object.Destroy((Object)(object)_sectionExpandedBgTexture); } } } public class AlmanacHUD : MonoBehaviour { private const int WINDOW_ID = 98780; private const float BASE_WIDTH = 260f; private const float BASE_MIN_HEIGHT = 60f; private const float REFRESH_INTERVAL = 5f; private float _scale = 1f; private AlmanacDataAggregator _aggregator; private Rect _windowRect; private bool _isVisible = true; private float _refreshTimer; private bool _stylesInitialized; private GUIStyle _windowStyle; private GUIStyle _titleStyle; private GUIStyle _iconStyle; private GUIStyle _summaryStyle; private GUIStyle _noModsStyle; private Texture2D _bgTexture; private Texture2D _headerTexture; public Action OnPositionChanged; private float Width => 260f * _scale; private float MinHeight => 60f * _scale; public bool IsVisible => _isVisible; private float Scaled(float value) { return value * _scale; } private int ScaledFont(int baseSize) { return Mathf.Max(8, Mathf.RoundToInt((float)baseSize * _scale)); } private int ScaledInt(float value) { return Mathf.RoundToInt(value * _scale); } public void Initialize(AlmanacDataAggregator aggregator) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) _aggregator = aggregator; float num = AlmanacConfig.StaticHUDPositionX; float num2 = AlmanacConfig.StaticHUDPositionY; if (num < 0f || num2 < 0f) { num = (float)Screen.width - Width - Scaled(20f); num2 = Scaled(80f); } _windowRect = new Rect(num, num2, Width, MinHeight); } public void SetScale(float scale) { _scale = Mathf.Clamp(scale, 0.5f, 2.5f); _stylesInitialized = false; } public void Show() { _isVisible = true; } public void Hide() { _isVisible = false; } public void Toggle() { _isVisible = !_isVisible; } public void SetPosition(float x, float y) { ((Rect)(ref _windowRect)).x = x; ((Rect)(ref _windowRect)).y = y; } private void Update() { if (_isVisible && _aggregator != null) { _refreshTimer += Time.unscaledDeltaTime; if (_refreshTimer >= 5f) { _refreshTimer = 0f; _aggregator.RefreshAll(); } } } private void OnGUI() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (!_isVisible || !AlmanacConfig.StaticHUDEnabled || _aggregator == null) { return; } AlmanacDashboard almanacDashboard = Plugin.GetAlmanacDashboard(); DailyBriefing dailyBriefing = Plugin.GetDailyBriefing(); if ((!((Object)(object)almanacDashboard != (Object)null) || !almanacDashboard.IsVisible) && (!((Object)(object)dailyBriefing != (Object)null) || !dailyBriefing.IsVisible)) { if (!_stylesInitialized) { InitializeStyles(); } _windowRect = GUI.Window(98780, _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); ClampToScreen(); } } private void DrawWindow(int id) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("almanac.hud.title"), _titleStyle, Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("x", (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(18f)), GUILayout.Height(Scaled(18f)) })) { Hide(); } GUILayout.EndHorizontal(); GUILayout.Space(Scaled(2f)); if (_aggregator.IntegrationModCount == 0) { GUILayout.Label(ModLocalization.T("almanac.hud.noMods"), _noModsStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("almanac.hud.installHint"), _noModsStyle, Array.Empty()); } else { foreach (IModDataProvider provider in _aggregator.Providers) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(provider.ModIcon, _iconStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(22f)) }); GUILayout.Label(provider.ModName + ": " + provider.HudSummary, _summaryStyle, Array.Empty()); GUILayout.EndHorizontal(); } } GUI.DragWindow(); } private void ClampToScreen() { float x = ((Rect)(ref _windowRect)).x; float y = ((Rect)(ref _windowRect)).y; ((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(x - ((Rect)(ref _windowRect)).x) > 1f || Math.Abs(y - ((Rect)(ref _windowRect)).y) > 1f) { OnPositionChanged?.Invoke(((Rect)(ref _windowRect)).x, ((Rect)(ref _windowRect)).y); } } private void InitializeStyles() { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Expected O, but got Unknown //IL_0104: Expected O, but got Unknown //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Expected O, but got Unknown //IL_01a7: 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_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected O, but got Unknown //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: 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_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Expected O, but got Unknown //IL_0222: Unknown result type (might be due to invalid IL or missing references) _stylesInitialized = true; Color color = default(Color); ((Color)(ref color))..ctor(0.18f, 0.14f, 0.1f, 0.92f); Color color2 = default(Color); ((Color)(ref color2))..ctor(0.22f, 0.17f, 0.12f, 0.95f); Color textColor = default(Color); ((Color)(ref textColor))..ctor(0.95f, 0.85f, 0.55f); Color textColor2 = default(Color); ((Color)(ref textColor2))..ctor(0.91f, 0.87f, 0.82f); _bgTexture = GUIStyleHelper.MakeSolidTexture(color); _headerTexture = GUIStyleHelper.MakeSolidTexture(color2); _windowStyle = new GUIStyle(GUI.skin.window) { padding = new RectOffset(ScaledInt(8f), ScaledInt(8f), ScaledInt(6f), ScaledInt(6f)), border = new RectOffset(ScaledInt(2f), ScaledInt(2f), ScaledInt(2f), ScaledInt(2f)) }; _windowStyle.normal.background = _bgTexture; _windowStyle.onNormal.background = _bgTexture; _titleStyle = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1, fontSize = ScaledFont(13), alignment = (TextAnchor)3 }; _titleStyle.normal.textColor = textColor; _iconStyle = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(14), alignment = (TextAnchor)4 }; _summaryStyle = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(12), alignment = (TextAnchor)3 }; _summaryStyle.normal.textColor = textColor2; _noModsStyle = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(11), fontStyle = (FontStyle)2, alignment = (TextAnchor)4 }; _noModsStyle.normal.textColor = new Color(0.7f, 0.6f, 0.5f); } private void OnDestroy() { if ((Object)(object)_bgTexture != (Object)null) { Object.Destroy((Object)(object)_bgTexture); } if ((Object)(object)_headerTexture != (Object)null) { Object.Destroy((Object)(object)_headerTexture); } } } public class DailyBriefing : MonoBehaviour { private const int WINDOW_ID = 98782; private const float BASE_WIDTH = 500f; private const float BASE_MIN_HEIGHT = 250f; private float _scale = 1f; private AlmanacDataAggregator _aggregator; private Rect _windowRect; private bool _isVisible; private float _contentHeight = 250f; private float _visibleSinceUnscaledTime; private float _autoDismissSeconds; private bool _stylesInitialized; private GUIStyle _windowStyle; private GUIStyle _titleStyle; private GUIStyle _sectionTitleStyle; private GUIStyle _contentStyle; private GUIStyle _dismissButtonStyle; private Texture2D _bgTexture; private float Width => 500f * _scale; private float MinHeight => 250f * _scale; public bool IsVisible => _isVisible; private float Scaled(float value) { return value * _scale; } private int ScaledFont(int baseSize) { return Mathf.Max(8, Mathf.RoundToInt((float)baseSize * _scale)); } private int ScaledInt(float value) { return Mathf.RoundToInt(value * _scale); } public void Initialize(AlmanacDataAggregator aggregator) { _aggregator = aggregator; CenterWindow(); } public void ShowBriefing() { if (AlmanacConfig.StaticBriefingEnabled && _aggregator != null && _aggregator.InstalledModCount != 0) { _aggregator.RefreshAll(); if (_aggregator.HasAnyBriefingContent) { CenterWindow(); _isVisible = true; _visibleSinceUnscaledTime = Time.unscaledTime; _autoDismissSeconds = Mathf.Max(0f, AlmanacConfig.StaticBriefingAutoDismiss); } } } public void Hide() { _isVisible = false; } public void SetScale(float scale) { _scale = Mathf.Clamp(scale, 0.5f, 2.5f); _stylesInitialized = false; } private void CenterWindow() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) _windowRect = new Rect(((float)Screen.width - Width) / 2f, ((float)Screen.height - MinHeight) / 2f - Scaled(50f), Width, MinHeight); } private void Update() { if (_isVisible) { if (Input.GetKeyDown((KeyCode)27)) { Hide(); } else if (_autoDismissSeconds > 0f && Time.unscaledTime - _visibleSinceUnscaledTime >= _autoDismissSeconds) { Hide(); } } } private float GetAutoDismissRemaining() { if (_autoDismissSeconds <= 0f) { return -1f; } float num = _autoDismissSeconds - (Time.unscaledTime - _visibleSinceUnscaledTime); return Mathf.Max(0f, num); } private void OnGUI() { //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) if (_isVisible && _aggregator != null) { if (!_stylesInitialized) { InitializeStyles(); } float num = (float)Screen.height - Scaled(60f); ((Rect)(ref _windowRect)).height = Mathf.Clamp(_contentHeight, MinHeight, num); ((Rect)(ref _windowRect)).x = ((float)Screen.width - ((Rect)(ref _windowRect)).width) / 2f; ((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, Scaled(20f), (float)Screen.height - ((Rect)(ref _windowRect)).height - Scaled(20f)); _windowRect = GUI.Window(98782, _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); } } private void DrawWindow(int id) { //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Invalid comparison between Unknown and I4 //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //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) GUILayout.Label(ModLocalization.T("almanac.briefing.title"), _titleStyle, Array.Empty()); GUILayout.Space(Scaled(6f)); bool flag = false; foreach (IModDataProvider provider in _aggregator.Providers) { if (provider.HasBriefingContent && TryDrawProviderSection(provider)) { flag = true; GUILayout.Space(Scaled(6f)); } } if (!flag) { GUILayout.Label(ModLocalization.T("almanac.briefing.empty"), _contentStyle, Array.Empty()); } GUILayout.Space(Scaled(10f)); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button(ModLocalization.T("almanac.briefing.dismiss"), _dismissButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(120f)), GUILayout.Height(Scaled(30f)) })) { Hide(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); float autoDismissRemaining = GetAutoDismissRemaining(); if (autoDismissRemaining >= 0f) { int num = Mathf.CeilToInt(autoDismissRemaining); GUILayout.Space(Scaled(4f)); GUILayout.Label($"Auto-dismissing in {num}s ({_autoDismissSeconds:F0}s total)", _contentStyle, Array.Empty()); } if ((int)Event.current.type == 7) { Rect lastRect = GUILayoutUtility.GetLastRect(); _contentHeight = ((Rect)(ref lastRect)).yMax + Scaled(28f); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, Scaled(30f))); } private bool TryDrawProviderSection(IModDataProvider provider) { bool flag = false; try { GUILayout.BeginVertical(Array.Empty()); flag = true; GUILayout.Label(provider.ModIcon + " " + provider.ModName, _sectionTitleStyle, Array.Empty()); bool result = provider.DrawBriefingSection(); GUILayout.EndVertical(); flag = false; return result; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Briefing] " + provider.ModName + " threw during DrawBriefingSection: " + ex.Message)); } } finally { if (flag) { try { GUILayout.EndVertical(); } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[Briefing] Layout recovery: " + ex2.Message)); } } } } return false; } private void InitializeStyles() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Expected O, but got Unknown //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown //IL_00f2: Expected O, but got Unknown //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Expected O, but got Unknown //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: 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_018a: Expected O, but got Unknown //IL_0195: 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_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Expected O, but got Unknown //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Expected O, but got Unknown _stylesInitialized = true; Color color = default(Color); ((Color)(ref color))..ctor(0.15f, 0.12f, 0.09f, 0.96f); Color textColor = default(Color); ((Color)(ref textColor))..ctor(0.95f, 0.85f, 0.55f); Color textColor2 = default(Color); ((Color)(ref textColor2))..ctor(0.91f, 0.87f, 0.82f); new Color(0.6f, 0.55f, 0.45f); _bgTexture = GUIStyleHelper.MakeSolidTexture(color); _windowStyle = new GUIStyle(GUI.skin.window) { padding = new RectOffset(ScaledInt(16f), ScaledInt(16f), ScaledInt(12f), ScaledInt(12f)), border = new RectOffset(ScaledInt(2f), ScaledInt(2f), ScaledInt(2f), ScaledInt(2f)) }; _windowStyle.normal.background = _bgTexture; _windowStyle.onNormal.background = _bgTexture; _titleStyle = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1, fontSize = ScaledFont(18), alignment = (TextAnchor)4 }; _titleStyle.normal.textColor = textColor; _sectionTitleStyle = new GUIStyle(GUI.skin.label) { fontStyle = (FontStyle)1, fontSize = ScaledFont(13) }; _sectionTitleStyle.normal.textColor = textColor; _contentStyle = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(12), fontStyle = (FontStyle)2, alignment = (TextAnchor)4, wordWrap = true }; _contentStyle.normal.textColor = textColor2; _dismissButtonStyle = new GUIStyle(GUI.skin.button) { fontSize = ScaledFont(13), fontStyle = (FontStyle)1 }; } private void OnDestroy() { if ((Object)(object)_bgTexture != (Object)null) { Object.Destroy((Object)(object)_bgTexture); } } } } namespace HavensAlmanac.Services { internal static class GameRelationshipReader { private static bool _resolved; private static Type _npcManagerType; private static PropertyInfo _npcManagerInstanceProp; private static FieldInfo _npcsDictField; private static Type _gameSaveType; private static PropertyInfo _gameSaveInstanceProp; private static PropertyInfo _currentSaveProp; private static PropertyInfo _characterDataProp; private static PropertyInfo _relationshipsProp; private static MethodInfo _getProgressBoolMethod; private static MethodInfo _getProgressStringMethod; private static FieldInfo _gaveGiftForDayField; private static PropertyInfo _originalNameProp; private static PropertyInfo _localizedNameProp; private static PropertyInfo _romanceableProp; public static bool IsAvailable { get { EnsureResolved(); object save; object characterData; if (TryGetNpcDictionary(out IDictionary _)) { return TryGetSave(out save, out characterData); } return false; } } public static List ReadRows(bool romanceOnly, string nameFilter) { List list = new List(); if (!TryGetNpcDictionary(out IDictionary npcs) || !TryGetSave(out object save, out object characterData)) { return list; } object relationships = _relationshipsProp?.GetValue(characterData); string text = ReadProgressString(save, "MarriedWith") ?? string.Empty; string value = nameFilter?.Trim() ?? string.Empty; foreach (DictionaryEntry item in npcs) { if (item.Value == null) { continue; } object value2 = item.Value; bool flag = ReadBool(value2, _romanceableProp); if (romanceOnly && !flag) { continue; } string text2 = ReadString(value2, _originalNameProp) ?? item.Key?.ToString(); if (!string.IsNullOrEmpty(text2)) { string text3 = ReadString(value2, _localizedNameProp) ?? text2; if (string.IsNullOrEmpty(value) || text2.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0 || text3.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { float points = ReadRelationshipPoints(relationships, text2); list.Add(new RelationshipRow { Key = text2, DisplayName = text3, Points = points, Romanceable = flag, IsDating = ReadProgressBool(save, "Dating" + text2), IsMarriedTo = ReadProgressBool(save, "MarriedTo" + text2), IsPrimarySpouse = (!string.IsNullOrEmpty(text) && text.Equals(text2, StringComparison.Ordinal)), GiftedToday = ReadGiftedToday(value2) }); } } } return list.OrderBy((RelationshipRow r) => r.DisplayName, StringComparer.OrdinalIgnoreCase).ToList(); } private static float ReadRelationshipPoints(object relationships, string key) { if (relationships == null || string.IsNullOrEmpty(key)) { return 0f; } try { if (relationships is IDictionary dictionary && dictionary.Contains(key)) { return Convert.ToSingle(dictionary[key]); } } catch { } return 0f; } private static bool ReadGiftedToday(object npc) { if (npc == null || _gaveGiftForDayField == null) { return false; } try { object value = _gaveGiftForDayField.GetValue(npc); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static bool ReadProgressBool(object save, string key) { if (save == null || _getProgressBoolMethod == null || string.IsNullOrEmpty(key)) { return false; } try { object obj = _getProgressBoolMethod.Invoke(save, new object[1] { key }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static string ReadProgressString(object save, string key) { if (save == null || _getProgressStringMethod == null || string.IsNullOrEmpty(key)) { return null; } try { return _getProgressStringMethod.Invoke(save, new object[1] { key }) as string; } catch { return null; } } private static bool TryGetNpcDictionary(out IDictionary npcs) { npcs = null; EnsureResolved(); if (_npcManagerInstanceProp == null || _npcsDictField == null) { return false; } try { object value = _npcManagerInstanceProp.GetValue(null); if (value == null) { return false; } npcs = _npcsDictField.GetValue(value) as IDictionary; return npcs != null; } catch { return false; } } private static bool TryGetSave(out object save, out object characterData) { save = null; characterData = null; EnsureResolved(); if (_gameSaveInstanceProp == null || _currentSaveProp == null || _characterDataProp == null) { return false; } try { object value = _gameSaveInstanceProp.GetValue(null); if (value == null) { return false; } save = _currentSaveProp.GetValue(value); if (save == null) { return false; } characterData = _characterDataProp.GetValue(save); return characterData != null; } catch { return false; } } private static string ReadString(object instance, PropertyInfo prop) { if (instance == null || prop == null) { return null; } try { return prop.GetValue(instance) as string; } catch { return null; } } private static bool ReadBool(object instance, PropertyInfo prop) { if (instance == null || prop == null) { return false; } try { object value = prop.GetValue(instance); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static void EnsureResolved() { if (_resolved) { return; } _resolved = true; _npcManagerType = AccessTools.TypeByName("Wish.NPCManager"); if (_npcManagerType != null) { _npcManagerInstanceProp = FindProperty(_npcManagerType, "Instance", "instance"); _npcsDictField = AccessTools.Field(_npcManagerType, "_npcs"); } _gameSaveType = AccessTools.TypeByName("Wish.GameSave"); if (_gameSaveType != null) { _gameSaveInstanceProp = FindProperty(_gameSaveType, "Instance", "instance"); Type type = AccessTools.TypeByName("Wish.SaveGame"); if (type != null) { _currentSaveProp = FindProperty(_gameSaveType, "CurrentSave", "currentSave"); _characterDataProp = FindProperty(type, "characterData", "CharacterData"); Type type2 = _characterDataProp?.PropertyType; if (type2 != null) { _relationshipsProp = FindProperty(type2, "Relationships", "relationships"); } } _getProgressBoolMethod = AccessTools.Method(_gameSaveType, "GetProgressBoolCharacter", new Type[1] { typeof(string) }, (Type[])null); _getProgressStringMethod = AccessTools.Method(_gameSaveType, "GetProgressStringCharacter", new Type[1] { typeof(string) }, (Type[])null); } Type type3 = AccessTools.TypeByName("Wish.NPCAI"); if (type3 != null) { _gaveGiftForDayField = AccessTools.Field(type3, "gaveGiftForDay"); _originalNameProp = FindProperty(type3, "OriginalName", "originalName"); _localizedNameProp = FindProperty(type3, "LocalizedActualNPCName", "localizedActualNPCName"); _romanceableProp = FindProperty(type3, "Romanceable", "romanceable"); } } private static PropertyInfo FindProperty(Type type, params string[] names) { if (type == null || names == null) { return null; } for (int i = 0; i < names.Length; i++) { PropertyInfo propertyInfo = AccessTools.Property(type, names[i]); if (propertyInfo != null) { return propertyInfo; } } return null; } } } namespace HavensAlmanac.Integration { public class BirthdayDataProvider : IModDataProvider { private List _birthdays = new List(); private int _ungiftedCount; private string _hudSummary = "Loading..."; private bool _isReady; public string ModName => "Birthday"; public string ModIcon => "★"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent { get { if (_isReady && _birthdays != null) { return _birthdays.Count > 0; } return false; } } public void Refresh() { try { BirthdayManager manager = Plugin.GetManager(); if (manager == null) { _isReady = false; return; } List todaysBirthdays = manager.TodaysBirthdays; _birthdays = ((todaysBirthdays != null) ? new List(todaysBirthdays) : new List()); _ungiftedCount = 0; foreach (BirthdayDisplayInfo birthday in _birthdays) { if (!birthday.HasBeenGifted) { _ungiftedCount++; } } if (_birthdays.Count == 0) { _hudSummary = "No birthdays"; } else { _hudSummary = string.Format("{0} birthday{1} / {2} ungifted", _birthdays.Count, (_birthdays.Count != 1) ? "s" : "", _ungiftedCount); } _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[BirthdayProvider] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { if (_birthdays.Count == 0) { GUILayout.Label(ModLocalization.T("almanac.provider.birthday.noBirthdays"), Array.Empty()); return; } GUILayout.Label(ModLocalization.T("almanac.provider.birthday.todayList", _birthdays.Count), Array.Empty()); GUILayout.Space(4f); foreach (BirthdayDisplayInfo birthday in _birthdays) { string text = (birthday.HasBeenGifted ? ModLocalization.T("almanac.provider.birthday.gifted") : ModLocalization.T("almanac.provider.birthday.notGifted")); GUILayout.Label(" " + birthday.NPCName + text, Array.Empty()); if (!string.IsNullOrEmpty(birthday.GiftHint)) { GUILayout.Label(" " + birthday.GiftHint, Array.Empty()); } } } public bool DrawBriefingSection() { if (_birthdays.Count == 0) { return false; } foreach (BirthdayDisplayInfo birthday in _birthdays) { string text = (birthday.HasBeenGifted ? ModLocalization.T("almanac.provider.birthday.giftedBriefing") : ""); GUILayout.Label(ModLocalization.T("almanac.provider.birthday.briefing", birthday.NPCName, text), Array.Empty()); } if (_ungiftedCount > 0) { GUILayout.Label(ModLocalization.T("almanac.provider.birthday.reminder"), Array.Empty()); } return true; } } public class BirthrightDataProvider : IModDataProvider { private string _raceName = "None"; private int _bonusCount; private List _bonuses = new List(); private string _hudSummary = "Loading..."; private bool _isReady; public string ModName => "Birthright"; public string ModIcon => "⚔"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent => false; public void Refresh() { //IL_0028: 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) try { RacialBonusManager racialBonusManager = Plugin.GetRacialBonusManager(); if (racialBonusManager == null) { _isReady = false; return; } Race? playerRace = racialBonusManager.GetPlayerRace(); if (playerRace.HasValue) { _raceName = ((object)playerRace.Value/*cast due to .constrained prefix*/).ToString(); _bonuses = racialBonusManager.GetCurrentPlayerBonuses() ?? new List(); _bonusCount = _bonuses.Count; _hudSummary = string.Format("{0} ({1} bonus{2})", _raceName, _bonusCount, (_bonusCount != 1) ? "es" : ""); } else { _raceName = "None"; _bonusCount = 0; _bonuses.Clear(); _hudSummary = "No race set"; } _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[BirthrightProvider] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { GUILayout.Label(ModLocalization.T("almanac.provider.birthright.race", _raceName), Array.Empty()); if (_bonusCount == 0) { GUILayout.Label(ModLocalization.T("almanac.provider.birthright.noBonuses"), Array.Empty()); return; } GUILayout.Label(ModLocalization.T("almanac.provider.birthright.activeBonuses", _bonusCount), Array.Empty()); GUILayout.Space(4f); foreach (RacialBonus bonuse in _bonuses) { string formattedValue = bonuse.GetFormattedValue(); GUILayout.Label(" " + bonuse.Description + ": " + formattedValue, Array.Empty()); } } public bool DrawBriefingSection() { return false; } } public class ChestDataProvider : IModDataProvider { private int _smartChestCount; private string _hudSummary = "Loading..."; private bool _isReady; private Type _pluginType; private MethodInfo _getManagerMethod; private Type _managerType; private MethodInfo _getSaveDataMethod; private Type _saveDataType; private PropertyInfo _chestConfigsProperty; private FieldInfo _chestConfigsField; public string ModName => "Chests"; public string ModIcon => "▣"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent => false; public void Refresh() { try { if (!TryResolvePluginType()) { _isReady = false; return; } object managerCached = GetManagerCached(); if (managerCached == null) { _isReady = false; return; } object saveDataCached = GetSaveDataCached(managerCached); _smartChestCount = ((saveDataCached != null) ? ReadChestConfigCount(saveDataCached) : 0); _hudSummary = ((_smartChestCount == 0) ? "No smart chests" : string.Format("{0} smart chest{1}", _smartChestCount, (_smartChestCount != 1) ? "s" : "")); _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[ChestProvider] Refresh error: " + ex.Message)); } } } private bool TryResolvePluginType() { if (_pluginType != null) { return true; } _pluginType = ReflectionHelper.FindType("SenpaisChest.Plugin"); return _pluginType != null; } private object GetManagerCached() { if (_getManagerMethod == null) { _getManagerMethod = _pluginType.GetMethod("GetManager", ReflectionHelper.AllBindingFlags, null, Type.EmptyTypes, null); if (_getManagerMethod == null) { return null; } } return _getManagerMethod.Invoke(null, null); } private object GetSaveDataCached(object manager) { Type type = manager.GetType(); if (_managerType != type || _getSaveDataMethod == null) { _managerType = type; _getSaveDataMethod = _managerType.GetMethod("GetSaveData", ReflectionHelper.AllBindingFlags, null, Type.EmptyTypes, null); } return _getSaveDataMethod?.Invoke(manager, null); } private int ReadChestConfigCount(object saveData) { Type type = saveData.GetType(); if (_saveDataType != type) { _saveDataType = type; _chestConfigsProperty = _saveDataType.GetProperty("ChestConfigs", ReflectionHelper.AllBindingFlags); _chestConfigsField = ((_chestConfigsProperty == null) ? _saveDataType.GetField("ChestConfigs", ReflectionHelper.AllBindingFlags) : null); } if (!(((_chestConfigsProperty != null) ? _chestConfigsProperty.GetValue(saveData) : _chestConfigsField?.GetValue(saveData)) is ICollection collection)) { return 0; } return collection.Count; } public void DrawDashboardSection() { GUILayout.Label(ModLocalization.T("almanac.provider.chest.count", _smartChestCount), Array.Empty()); } public bool DrawBriefingSection() { return false; } } public class CropOptimizerDataProvider : IModDataProvider { private string _hudSummary = "Loading..."; private bool _isReady; private List<(string name, int totalGold, int count)> _topCrops = new List<(string, int, int)>(); public string ModName => "Crop Optimizer"; public string ModIcon => "o"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent => false; public void Refresh() { //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) try { _hudSummary = CropOptimizerDataProvider.GetSummary(); _topCrops.Clear(); string text = default(string); foreach (CropTypeSummary topCrop in CropOptimizerDataProvider.GetTopCrops(5)) { CropTypeSummary current = topCrop; CropOptimizerDataProvider.TryGetCropDisplayName(((CropTypeSummary)(ref current)).ItemId, ref text); _topCrops.Add((text ?? $"Item #{((CropTypeSummary)(ref current)).ItemId}", ((CropTypeSummary)(ref current)).TotalGold, ((CropTypeSummary)(ref current)).CropCount)); } _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[CropOptimizerProvider] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { GUILayout.Label(_hudSummary, Array.Empty()); if (_topCrops.Count > 0) { GUILayout.Space(4f); GUILayout.Label(ModLocalization.T("almanac.provider.crop.topHeader"), Array.Empty()); for (int i = 0; i < _topCrops.Count; i++) { (string name, int totalGold, int count) tuple = _topCrops[i]; string item = tuple.name; int item2 = tuple.totalGold; int item3 = tuple.count; string text = ((item3 == 1) ? ModLocalization.T("almanac.provider.crop.plant") : ModLocalization.T("almanac.provider.crop.plants")); GUILayout.Label(ModLocalization.T("almanac.provider.crop.topRow", i + 1, item, item2, item3, text), Array.Empty()); } } } public bool DrawBriefingSection() { return false; } } public class DevToolsDataProvider : IModDataProvider { private bool _isAuthorized; private string _playerName = ""; private string _hudSummary = "Loading..."; private bool _isReady; public string ModName => "DevTools"; public string ModIcon => "⚒"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent => false; public void Refresh() { try { _isAuthorized = Plugin.IsAuthorized; _playerName = Plugin.CurrentPlayerName ?? ""; _hudSummary = (_isAuthorized ? "Active" : "Inactive"); _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[DevToolsProvider] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { string text = (_isAuthorized ? ModLocalization.T("almanac.provider.devtools.authorized") : ModLocalization.T("almanac.provider.devtools.notAuthorized")); GUILayout.Label(ModLocalization.T("almanac.provider.devtools.status", text), Array.Empty()); if (!string.IsNullOrEmpty(_playerName)) { GUILayout.Label(ModLocalization.T("almanac.provider.devtools.player", _playerName), Array.Empty()); } } public bool DrawBriefingSection() { return false; } } public class GiftingAssistantDataProvider : IModDataProvider { private string _hudSummary = "Loading..."; private bool _isReady; private bool _integrationEnabled; private int _rosterCount; private int _pendingCount; private int _highPriorityPending; private int _urgentPending; private readonly List _pendingEntries = new List(); public string ModName => "Gifting"; public string ModIcon => "♥"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent { get { if (_isReady && _integrationEnabled) { return _pendingCount > 0; } return false; } } public void Refresh() { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Invalid comparison between Unknown and I4 //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Invalid comparison between Unknown and I4 _integrationEnabled = GiftingAssistantAlmanacData.IsIntegrationEnabled; _rosterCount = 0; _pendingCount = 0; _highPriorityPending = 0; _urgentPending = 0; _pendingEntries.Clear(); try { if (!_integrationEnabled) { _hudSummary = ModLocalization.T("almanac.provider.gifting.disabled"); _isReady = true; return; } if (!GiftingAssistantAlmanacData.TryGetSummary(ref _hudSummary, ref _rosterCount, ref _pendingCount)) { _hudSummary = "Not ready"; _isReady = false; return; } foreach (RosterEntrySnapshot sortedRosterEntry in GiftingAssistantAlmanacData.GetSortedRosterEntries(0)) { RosterEntrySnapshot current = sortedRosterEntry; if (!((RosterEntrySnapshot)(ref current)).IsGiftedToday) { _pendingEntries.Add(current); if ((int)((RosterEntrySnapshot)(ref current)).Priority >= 2) { _highPriorityPending++; } if ((int)((RosterEntrySnapshot)(ref current)).Priority >= 3) { _urgentPending++; } } } _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[GiftingProvider] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if (!_integrationEnabled) { GUILayout.Label(ModLocalization.T("almanac.provider.gifting.disabled"), Array.Empty()); return; } if (_rosterCount == 0) { GUILayout.Label(ModLocalization.T("almanac.provider.gifting.noRoster"), Array.Empty()); return; } GUILayout.Label(ModLocalization.T("almanac.provider.gifting.stats", _rosterCount, _pendingCount), Array.Empty()); if (_pendingEntries.Count == 0) { return; } GUILayout.Space(4f); GUILayout.Label(ModLocalization.T("almanac.provider.gifting.pendingHeader"), GUI.skin.label, Array.Empty()); foreach (RosterEntrySnapshot pendingEntry in _pendingEntries) { RosterEntrySnapshot current = pendingEntry; GUILayout.Label(ModLocalization.T("almanac.provider.gifting.priorityRow", ((RosterEntrySnapshot)(ref current)).Priority, ((RosterEntrySnapshot)(ref current)).NpcName, ModLocalization.T("almanac.provider.gifting.notGifted")), Array.Empty()); } } public bool DrawBriefingSection() { if (!_integrationEnabled || _pendingCount == 0) { return false; } string text = ((_pendingCount == 1) ? ModLocalization.T("almanac.provider.gifting.npc") : ModLocalization.T("almanac.provider.gifting.npcs")); GUILayout.Label(ModLocalization.T("almanac.provider.gifting.briefing.remaining", _pendingCount, text), Array.Empty()); if (_highPriorityPending > 0) { GUILayout.Label(ModLocalization.T("almanac.provider.gifting.briefing.high", _highPriorityPending), Array.Empty()); } if (_urgentPending > 0) { GUILayout.Label(ModLocalization.T("almanac.provider.gifting.briefing.urgent", _urgentPending), Array.Empty()); } return true; } } public class ModHealthBridgeProvider : IModDataProvider { private const string DevToolsGuid = "com.azraelgodking.havendevtools"; private string _hudSummary = ""; private string _detailLine = ""; private string _pointerLine = ""; private bool _isReady; public string ModName => ModLocalization.T("almanac.provider.healthbridge.title"); public string ModIcon => "+"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent => false; public void Refresh() { _isReady = false; _hudSummary = ""; _detailLine = ""; _pointerLine = ""; if (!IsDevToolsInstalled()) { return; } try { IReadOnlyList readOnlyList = ModHealthAggregator.CollectMergedRows(ResolveDisplayName, ResolveInstalledVersion); ModHealthAggregator.SharedCodeSkewAnalysis sharedCodeSkewAnalysis = ModHealthAggregator.AnalyzeSharedCodeSkew(readOnlyList); int num = 0; for (int i = 0; i < readOnlyList.Count; i++) { if (readOnlyList[i].HasIssue) { num++; } } _hudSummary = ModLocalization.T("almanac.provider.healthbridge.summary", num.ToString()); _detailLine = (sharedCodeSkewAnalysis.HasSkew ? ModLocalization.T("almanac.provider.healthbridge.skew") : ModLocalization.T("almanac.provider.healthbridge.ok")); _pointerLine = ModLocalization.T("almanac.provider.healthbridge.pointer"); _isReady = true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[ModHealthBridge] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { if (_isReady) { GUILayout.Label(_detailLine, Array.Empty()); GUILayout.Label(_pointerLine, Array.Empty()); } } public bool DrawBriefingSection() { return false; } private static bool IsDevToolsInstalled() { try { return Chainloader.PluginInfos != null && Chainloader.PluginInfos.ContainsKey("com.azraelgodking.havendevtools"); } catch { return false; } } private static string ResolveDisplayName(string pluginGuid) { try { if (Chainloader.PluginInfos != null && Chainloader.PluginInfos.TryGetValue(pluginGuid, out var value) && ((value != null) ? value.Metadata : null) != null && !string.IsNullOrEmpty(value.Metadata.Name)) { return value.Metadata.Name; } } catch { } return null; } private static string ResolveInstalledVersion(string pluginGuid) { try { if (Chainloader.PluginInfos != null && Chainloader.PluginInfos.TryGetValue(pluginGuid, out var value) && ((value != null) ? value.Metadata : null) != null) { return value.Metadata.Version.ToString(); } } catch { } return null; } } public class MuseumDataProvider : IModDataProvider { private int _donated; private int _total; private float _completionPercent; private int _neededCount; private string _hudSummary = "Loading..."; private bool _isReady; private const int BriefingNearCompletionThreshold = 5; public string ModName => "Museum"; public string ModIcon => "⌂"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent { get { if (_isReady && _neededCount > 0) { return _neededCount <= 5; } return false; } } public void Refresh() { try { DonationManager donationManager = Plugin.GetDonationManager(); if (donationManager == null || !donationManager.IsLoaded) { _isReady = false; return; } (int, int) overallStats = donationManager.GetOverallStats(); _donated = overallStats.Item1; _total = overallStats.Item2; _completionPercent = donationManager.GetOverallCompletionPercent(); _neededCount = donationManager.GetAllNeededItems()?.Count ?? 0; _hudSummary = $"{_completionPercent:F0}% ({_donated}/{_total})"; _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[MuseumProvider] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_00af: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label(ModLocalization.T("almanac.provider.museum.donated", _donated, _total), Array.Empty()); Rect rect = GUILayoutUtility.GetRect(0f, 16f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.Box(rect, ""); if (_total > 0) { float num = (float)_donated / (float)_total; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 1f, ((Rect)(ref rect)).y + 1f, (((Rect)(ref rect)).width - 2f) * num, ((Rect)(ref rect)).height - 2f), (Texture)(object)Texture2D.whiteTexture); } GUILayout.Label(ModLocalization.T("almanac.provider.museum.percent", _completionPercent.ToString("F1")), Array.Empty()); if (_neededCount > 0) { GUILayout.Label(ModLocalization.T("almanac.provider.museum.needed", _neededCount), Array.Empty()); } } public bool DrawBriefingSection() { if (!_isReady || _neededCount <= 0 || _neededCount > 5) { return false; } GUILayout.Label(ModLocalization.T("almanac.provider.museum.briefing.summary", _completionPercent, _donated, _total), Array.Empty()); string text = ((_neededCount == 1) ? ModLocalization.T("almanac.provider.museum.item") : ModLocalization.T("almanac.provider.museum.items")); GUILayout.Label(ModLocalization.T("almanac.provider.museum.briefing.left", _neededCount, text), Array.Empty()); return true; } } public class RelationshipDataProvider : IModDataProvider { private readonly List _rows = new List(); private string _hudSummary = "Loading..."; private bool _isReady; private int _ungiftedCount; private string _nameFilter = string.Empty; public string ModName => "Relationships"; public string ModIcon => "♥"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent { get { if (_isReady && AlmanacConfig.RelationshipsEnabled.Value) { return _ungiftedCount > 0; } return false; } } public void Refresh() { _rows.Clear(); _ungiftedCount = 0; if (!AlmanacConfig.RelationshipsEnabled.Value) { _hudSummary = ModLocalization.T("almanac.provider.relationship.disabled"); _isReady = true; return; } try { if (!GameRelationshipReader.IsAvailable) { _hudSummary = ModLocalization.T("almanac.provider.relationship.notReady"); _isReady = false; return; } bool value = AlmanacConfig.RelationshipsRomanceOnly.Value; _rows.AddRange(GameRelationshipReader.ReadRows(value, _nameFilter)); SortRows(); foreach (RelationshipRow row in _rows) { if (!row.GiftedToday) { _ungiftedCount++; } } if (_rows.Count == 0) { _hudSummary = ModLocalization.T("almanac.provider.relationship.noNpcs"); } else { _hudSummary = ModLocalization.T("almanac.provider.relationship.summary", _rows.Count, _ungiftedCount); } _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[RelationshipProvider] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { if (!AlmanacConfig.RelationshipsEnabled.Value) { GUILayout.Label(ModLocalization.T("almanac.provider.relationship.disabled"), Array.Empty()); return; } if (_rows.Count == 0) { GUILayout.Label(ModLocalization.T("almanac.provider.relationship.noNpcs"), Array.Empty()); return; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("almanac.provider.relationship.filter"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }); _nameFilter = GUILayout.TextField(_nameFilter ?? string.Empty, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(120f) }); if (GUILayout.Button(ModLocalization.T("almanac.provider.relationship.apply"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) })) { Refresh(); } GUILayout.EndHorizontal(); GUILayout.Space(4f); float scale = Mathf.Clamp(AlmanacConfig.StaticUIScale, 0.5f, 2.5f); foreach (RelationshipRow row in _rows) { DrawRow(row, scale); GUILayout.Space(6f); } } public bool DrawBriefingSection() { if (!HasBriefingContent) { return false; } int num = 0; foreach (RelationshipRow row in _rows) { if (!row.GiftedToday) { GUILayout.Label(ModLocalization.T("almanac.provider.relationship.briefingRow", row.DisplayName), Array.Empty()); num++; if (num >= 5) { break; } } } if (_ungiftedCount > num) { GUILayout.Label(ModLocalization.T("almanac.provider.relationship.briefingMore", _ungiftedCount - num), Array.Empty()); } return true; } private void DrawRow(RelationshipRow row, float scale) { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) RelationshipHeartLayout dashboard = RelationshipHeartLayout.Dashboard; int maxHearts = RelationshipHeartRules.GetMaxHearts(row.Points); float num = RelationshipHeartRenderer.ScaledIconWidth(dashboard, scale) * 5f + Scaled(dashboard.Gutter, scale) * 4f; float num2 = RelationshipHeartRenderer.GridHeight(maxHearts, dashboard, scale); float num3 = Mathf.Max(num2, GUI.skin.label.lineHeight); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(row.DisplayName, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num3) }); GUILayout.Space(Scaled(8f, scale)); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num), GUILayout.Height(num3) }); GUILayout.FlexibleSpace(); RelationshipHeartRenderer.DrawGrid(GUILayoutUtility.GetRect(num, num2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }), row.Points, row.Romanceable, scale, dashboard); GUILayout.FlexibleSpace(); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); DrawStatusBadges(row); } private static void DrawStatusBadges(RelationshipRow row) { if (row.IsPrimarySpouse) { GUILayout.Label(ModLocalization.T("almanac.provider.relationship.spouse"), Array.Empty()); } else if (row.IsMarriedTo) { GUILayout.Label(ModLocalization.T("almanac.provider.relationship.married"), Array.Empty()); } else if (row.IsDating) { GUILayout.Label(ModLocalization.T("almanac.provider.relationship.dating"), Array.Empty()); } if (!row.GiftedToday && AlmanacConfig.RelationshipsShowGiftBadge.Value) { GUILayout.Label(ModLocalization.T("almanac.provider.relationship.ungifted"), Array.Empty()); } } private void SortRows() { switch (AlmanacConfig.RelationshipsSortMode.Value) { case RelationshipSortMode.HeartsAsc: _rows.Sort((RelationshipRow a, RelationshipRow b) => a.Points.CompareTo(b.Points)); break; case RelationshipSortMode.HeartsDesc: _rows.Sort((RelationshipRow a, RelationshipRow b) => b.Points.CompareTo(a.Points)); break; case RelationshipSortMode.UngiftedFirst: _rows.Sort(delegate(RelationshipRow a, RelationshipRow b) { int num = a.GiftedToday.CompareTo(b.GiftedToday); return (num == 0) ? string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase) : num; }); break; default: _rows.Sort((RelationshipRow a, RelationshipRow b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase)); break; } } private static float Scaled(float value, float scale) { return value * scale; } } public class TodoDataProvider : IModDataProvider { private int _activeCount; private int _totalCount; private int _completedCount; private float _completionPercent; private List _highPriorityItems = new List(); private string _hudSummary = "Loading..."; private bool _isReady; public string ModName => "Todo"; public string ModIcon => "✎"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent { get { if (_isReady) { return _activeCount > 0; } return false; } } public void Refresh() { try { TodoManager todoManager = Plugin.GetTodoManager(); if (todoManager == null) { _isReady = false; return; } (int, int, int) stats = todoManager.GetStats(); _totalCount = stats.Item1; _completedCount = stats.Item2; _activeCount = stats.Item3; _completionPercent = todoManager.GetCompletionPercent(); _highPriorityItems = (from t in todoManager.GetActiveTodos() where (int)t.Priority >= 2 orderby (int)t.Priority descending select t).Take(5).ToList(); _hudSummary = ((_totalCount == 0) ? "No tasks" : $"{_activeCount} active / {_completionPercent:F0}%"); _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[TodoProvider] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Invalid comparison between Unknown and I4 GUILayout.Label(ModLocalization.T("almanac.provider.todo.stats", _totalCount, _completedCount, _activeCount), Array.Empty()); Rect rect = GUILayoutUtility.GetRect(0f, 16f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.Box(rect, ""); if (_totalCount > 0) { GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 1f, ((Rect)(ref rect)).y + 1f, (((Rect)(ref rect)).width - 2f) * (_completionPercent / 100f), ((Rect)(ref rect)).height - 2f), (Texture)(object)Texture2D.whiteTexture); } GUILayout.Label(ModLocalization.T("almanac.provider.todo.percent", _completionPercent.ToString("F0")), Array.Empty()); if (_highPriorityItems.Count <= 0) { return; } GUILayout.Space(4f); GUILayout.Label(ModLocalization.T("almanac.provider.todo.highPriority"), GUI.skin.label, Array.Empty()); foreach (TodoItem highPriorityItem in _highPriorityItems) { string text = (((int)highPriorityItem.Priority == 3) ? "[!] " : "[*] "); GUILayout.Label(" " + text + highPriorityItem.Title, Array.Empty()); } } public bool DrawBriefingSection() { if (_activeCount == 0) { return false; } int count = _highPriorityItems.Count; string text = ((_activeCount == 1) ? ModLocalization.T("almanac.provider.todo.task") : ModLocalization.T("almanac.provider.todo.tasks")); GUILayout.Label(ModLocalization.T("almanac.provider.todo.briefing.active", _activeCount, text), Array.Empty()); if (count > 0) { GUILayout.Label(ModLocalization.T("almanac.provider.todo.briefing.high", count), Array.Empty()); } return true; } } public class VaultDataProvider : IModDataProvider { private int _currencyCount; private Dictionary _currencies = new Dictionary(); private string _hudSummary = "Loading..."; private bool _isReady; public string ModName => "Vault"; public string ModIcon => "⚿"; public string HudSummary => _hudSummary; public bool IsReady => _isReady; public bool HasBriefingContent { get { if (_isReady) { return _currencyCount > 0; } return false; } } public void Refresh() { try { VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { _isReady = false; return; } _currencies = vaultManager.GetAllNonZeroCurrencies() ?? new Dictionary(); _currencyCount = _currencies.Count; _hudSummary = ((_currencyCount == 0) ? "Vault empty" : string.Format("{0} currenc{1} stored", _currencyCount, (_currencyCount != 1) ? "ies" : "y")); _isReady = true; } catch (Exception ex) { _hudSummary = "Error"; _isReady = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[VaultProvider] Refresh error: " + ex.Message)); } } } public void DrawDashboardSection() { if (_currencyCount == 0) { GUILayout.Label(ModLocalization.T("almanac.provider.vault.empty"), Array.Empty()); return; } GUILayout.Label(ModLocalization.T("almanac.provider.vault.stored", _currencyCount), Array.Empty()); GUILayout.Space(4f); int num = 0; foreach (KeyValuePair currency in _currencies) { if (num >= 10) { GUILayout.Label(ModLocalization.T("almanac.provider.vault.more", _currencyCount - 10), Array.Empty()); break; } GUILayout.Label($" {currency.Key}: {currency.Value}", Array.Empty()); num++; } } public bool DrawBriefingSection() { if (_currencyCount == 0) { return false; } string text = ((_currencyCount == 1) ? ModLocalization.T("almanac.provider.vault.currency") : ModLocalization.T("almanac.provider.vault.currencies")); GUILayout.Label(ModLocalization.T("almanac.provider.vault.briefing", _currencyCount, text), Array.Empty()); return true; } } } namespace HavensAlmanac.Data { public class AlmanacDataAggregator { private readonly List _providers = new List(); private readonly Dictionary _providerErrors = new Dictionary(); public IReadOnlyList Providers => _providers; public int InstalledModCount => _providers.Count; public bool HasAnyData => _providers.Any((IModDataProvider p) => p.IsReady); public int IntegrationModCount => _providers.Count((IModDataProvider p) => !(p is ModHealthBridgeProvider) && !(p is RelationshipDataProvider)); public bool HasDashboardSections { get { if (IntegrationModCount <= 0) { return _providers.OfType().Any((RelationshipDataProvider p) => p.IsReady); } return true; } } public bool HasAnyBriefingContent => _providers.Any((IModDataProvider p) => p.HasBriefingContent); public string GetProviderError(IModDataProvider provider) { if (provider == null) { return null; } _providerErrors.TryGetValue(provider.ModName, out string value); return value; } public void RegisterProvider(IModDataProvider provider) { _providers.Add(provider); } public void RefreshAll() { foreach (IModDataProvider provider in _providers) { try { provider.Refresh(); _providerErrors.Remove(provider.ModName); } catch (Exception ex) { string message = ex.Message; _providerErrors[provider.ModName] = message; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Almanac] Error refreshing " + provider.ModName + ": " + message)); } } } } } public interface IModDataProvider { string ModName { get; } string ModIcon { get; } string HudSummary { get; } bool IsReady { get; } bool HasBriefingContent { get; } void Refresh(); void DrawDashboardSection(); bool DrawBriefingSection(); } public sealed class RelationshipRow { public string Key { get; set; } public string DisplayName { get; set; } public float Points { get; set; } public bool Romanceable { get; set; } public bool IsDating { get; set; } public bool IsMarriedTo { get; set; } public bool IsPrimarySpouse { get; set; } public bool GiftedToday { get; set; } } } namespace HavensAlmanac.Config { public static class AlmanacConfig { internal static KeyCode StaticDashboardToggleKey = (KeyCode)286; internal static bool StaticDashboardRequireCtrl = true; internal static KeyCode StaticHUDToggleKey = (KeyCode)285; internal static bool StaticHUDEnabled = true; internal static float StaticHUDPositionX = -1f; internal static float StaticHUDPositionY = -1f; internal static bool StaticBriefingEnabled = true; internal static float StaticBriefingAutoDismiss = 0f; internal static float StaticUIScale = 1f; public static ConfigEntry DashboardToggleKey { get; private set; } public static ConfigEntry DashboardRequireCtrl { get; private set; } public static ConfigEntry HUDToggleKey { get; private set; } public static ConfigEntry HUDEnabled { get; private set; } public static ConfigEntry HUDPositionX { get; private set; } public static ConfigEntry HUDPositionY { get; private set; } public static ConfigEntry BriefingEnabled { get; private set; } public static ConfigEntry BriefingAutoDismissSeconds { get; private set; } public static ConfigEntry CheckForUpdates { get; private set; } public static ConfigEntry UIScale { get; private set; } public static ConfigEntry RelationshipsEnabled { get; private set; } public static ConfigEntry RelationshipsRomanceOnly { get; private set; } public static ConfigEntry RelationshipsSortMode { get; private set; } public static ConfigEntry RelationshipsShowGiftBadge { get; private set; } public static void Initialize(ConfigFile config) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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_00d3: 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 DashboardToggleKey = config.Bind("Hotkeys", "DashboardToggleKey", (KeyCode)286, "Key to toggle the full dashboard"); StaticDashboardToggleKey = DashboardToggleKey.Value; DashboardToggleKey.SettingChanged += delegate { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) StaticDashboardToggleKey = DashboardToggleKey.Value; }; DashboardRequireCtrl = config.Bind("Hotkeys", "DashboardRequireCtrl", true, "Require Ctrl modifier for dashboard toggle"); StaticDashboardRequireCtrl = DashboardRequireCtrl.Value; DashboardRequireCtrl.SettingChanged += delegate { StaticDashboardRequireCtrl = DashboardRequireCtrl.Value; }; HUDToggleKey = config.Bind("Hotkeys", "HUDToggleKey", (KeyCode)285, "Key to toggle HUD visibility"); StaticHUDToggleKey = HUDToggleKey.Value; HUDToggleKey.SettingChanged += delegate { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) StaticHUDToggleKey = HUDToggleKey.Value; }; HUDEnabled = config.Bind("HUD", "Enabled", true, "Show the compact HUD"); StaticHUDEnabled = HUDEnabled.Value; HUDEnabled.SettingChanged += delegate { StaticHUDEnabled = HUDEnabled.Value; }; HUDPositionX = config.Bind("HUD", "PositionX", -1f, "HUD X position (-1 for default)"); StaticHUDPositionX = HUDPositionX.Value; HUDPositionY = config.Bind("HUD", "PositionY", -1f, "HUD Y position (-1 for default)"); StaticHUDPositionY = HUDPositionY.Value; BriefingEnabled = config.Bind("DailyBriefing", "Enabled", true, "Show daily briefing when you wake up"); StaticBriefingEnabled = BriefingEnabled.Value; BriefingEnabled.SettingChanged += delegate { StaticBriefingEnabled = BriefingEnabled.Value; }; BriefingAutoDismissSeconds = config.Bind("DailyBriefing", "AutoDismissSeconds", 0f, "Auto-dismiss briefing after this many seconds (0 to require manual dismiss)"); StaticBriefingAutoDismiss = BriefingAutoDismissSeconds.Value; BriefingAutoDismissSeconds.SettingChanged += delegate { StaticBriefingAutoDismiss = BriefingAutoDismissSeconds.Value; }; CheckForUpdates = config.Bind("Updates", "CheckForUpdates", true, "Check for mod updates on startup"); UIScale = config.Bind("Display", "UIScale", 1f, new ConfigDescription("Scale factor for Almanac HUD, Dashboard, and Daily Briefing (1.0 = default)", (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); Plugin.Instance?.ApplyUIScaleToAllUI(); }; RelationshipsEnabled = config.Bind("Relationships", "Enabled", true, "Show the built-in Relationships dashboard section (native game heart data)."); RelationshipsRomanceOnly = config.Bind("Relationships", "RomanceOnly", false, "Only list romanceable NPCs in the Relationships section."); RelationshipsSortMode = config.Bind("Relationships", "SortBy", RelationshipSortMode.Name, "Sort order for the Relationships dashboard list."); RelationshipsShowGiftBadge = config.Bind("Relationships", "ShowUngiftedBadge", true, "Show an ungifted-today marker on NPC rows (uses NPCAI.gaveGiftForDay)."); } } public enum RelationshipSortMode { Name, HeartsDesc, HeartsAsc, UngiftedFirst } }