using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HavenDevTools.API; using HavenDevTools.Config; using HavenDevTools.Integrations; using HavenDevTools.Services; using HavenDevTools.UI; using I2.Loc; using Microsoft.CodeAnalysis; using SunhavenMods.Shared; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; using Wish; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("HavenDevTools")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+79057c8571b97b27a9323b455b8f88a51829b688")] [assembly: AssemblyProduct("HavenDevTools")] [assembly: AssemblyTitle("HavenDevTools")] [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 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 ItemSearch { private static readonly ManualLogSource _log = Logger.CreateLogSource("ItemSearch"); private static object _dbInstance; private static FieldInfo _dictField; public static string FormatDisplay(string name, int itemId) { if (string.IsNullOrEmpty(name)) { return $"#{itemId}"; } return $"{name} (#{itemId})"; } public static List> SearchItems(string query, int maxResults = 50) { List> list = new List>(); if (string.IsNullOrEmpty(query) || query.Trim().Length < 2) { return list; } try { Dictionary itemDictionary = GetItemDictionary(); if (itemDictionary == null) { return list; } string text = query.Trim().ToLowerInvariant(); int result; bool flag = int.TryParse(query.Trim(), out result); List> list2 = new List>(); List> list3 = new List>(); List> list4 = new List>(); foreach (KeyValuePair item2 in itemDictionary) { int key = item2.Key; string text2 = item2.Value?.name; if (!string.IsNullOrEmpty(text2)) { KeyValuePair item = new KeyValuePair(key, text2); string text3 = text2.ToLowerInvariant(); if (flag && key == result) { list2.Add(item); } else if (text3 == text) { list2.Add(item); } else if (text3.StartsWith(text)) { list3.Add(item); } else if (text3.Contains(text)) { list4.Add(item); } else if (flag && key.ToString().Contains(query.Trim())) { list4.Add(item); } } } list3.Sort((KeyValuePair a, KeyValuePair b) => string.Compare(a.Value, b.Value, StringComparison.OrdinalIgnoreCase)); list4.Sort((KeyValuePair a, KeyValuePair b) => string.Compare(a.Value, b.Value, StringComparison.OrdinalIgnoreCase)); list.AddRange(list2); list.AddRange(list3); list.AddRange(list4); if (list.Count > maxResults) { list.RemoveRange(maxResults, list.Count - maxResults); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("[ItemSearch] SearchItems error: " + ex.Message)); } } return list; } public static string GetItemName(int itemId) { try { Dictionary itemDictionary = GetItemDictionary(); if (itemDictionary == null || !itemDictionary.ContainsKey(itemId)) { return null; } return itemDictionary[itemId]?.name; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)$"[ItemSearch] GetItemName({itemId}): {ex.Message}"); } return null; } } public static ItemSellInfo GetItemSellInfo(int itemId) { try { Dictionary itemDictionary = GetItemDictionary(); if (itemDictionary != null && itemDictionary.TryGetValue(itemId, out var value)) { return value; } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)$"[ItemSearch] GetItemSellInfo({itemId}): {ex.Message}"); } } return null; } public static List> GetAllItems() { List> list = new List>(); try { Dictionary itemDictionary = GetItemDictionary(); if (itemDictionary == null) { return list; } foreach (KeyValuePair item in itemDictionary) { string value = item.Value?.name; if (!string.IsNullOrEmpty(value)) { list.Add(new KeyValuePair(item.Key, value)); } } list.Sort((KeyValuePair a, KeyValuePair b) => string.Compare(a.Value, b.Value, StringComparison.OrdinalIgnoreCase)); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("[ItemSearch] GetAllItems: " + ex.Message)); } } return list; } private static Dictionary GetItemDictionary() { try { if (_dbInstance != null) { object dbInstance = _dbInstance; Object val = (Object)((dbInstance is Object) ? dbInstance : null); if (val == null || !(val == (Object)null)) { goto IL_005b; } } _dbInstance = null; _dictField = null; _dbInstance = GetSingletonInstance("Wish.ItemInfoDatabase"); if (_dbInstance != null) { _dictField = _dbInstance.GetType().GetField("allItemSellInfos", BindingFlags.Instance | BindingFlags.Public); } goto IL_005b; IL_005b: if (_dbInstance == null || _dictField == null) { return null; } return _dictField.GetValue(_dbInstance) as Dictionary; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("[ItemSearch] GetItemDictionary: " + ex.Message)); } return null; } } private static object GetSingletonInstance(string typeName) { try { Type type = AccessTools.TypeByName("Wish.SingletonBehaviour`1"); if (type == null) { return null; } Type type2 = AccessTools.TypeByName(typeName); if (type2 == null) { return null; } return type.MakeGenericType(type2).GetProperty("Instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy)?.GetValue(null); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("[ItemSearch] GetSingletonInstance(" + typeName + "): " + ex.Message)); } return null; } } } public static class TextInputFocusGuard { private const float DefaultPollIntervalSeconds = 0.25f; private static float _nextPollTime = -1f; private static bool _cachedDefer; private static bool _tmpTypeLookupDone; private static Type _tmpInputFieldType; private static bool _qcLookupDone; private static Type _qcType; private static PropertyInfo _qcInstanceProp; private static PropertyInfo _qcIsActiveProp; private static FieldInfo _qcIsActiveField; public static bool ShouldDeferModHotkeys(ManualLogSource debugLog = null, float pollIntervalSeconds = 0.25f) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextPollTime) { return _cachedDefer; } _nextPollTime = realtimeSinceStartup + Mathf.Max(0.05f, pollIntervalSeconds); bool flag = false; try { if (GUIUtility.keyboardControl != 0) { flag = true; } if (!flag) { EventSystem current = EventSystem.current; GameObject val = ((current != null) ? current.currentSelectedGameObject : null); if ((Object)(object)val != (Object)null) { if ((Object)(object)val.GetComponent() != (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); } } } } namespace HavenDevTools { [BepInPlugin("com.azraelgodking.havendevtools", "Haven Dev Tools", "2.1.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.*/)] public class Plugin : BaseUnityPlugin { private static ItemInspector _staticItemInspector; private static CurrencyTracker _staticCurrencyTracker; private static BundleInspector _staticBundleInspector; private static RaceModifierTracker _staticRaceModifierTracker; private static DebugWindow _staticDebugWindow; private static DebugOverlay _staticDebugOverlay; private static FpsCounterOverlay _staticFpsCounterOverlay; private static CommandConsole _staticCommandConsole; private static LogViewerPanel _staticLogViewer; private static GameObject _persistentRunner; private static PersistentRunner _persistentRunnerComponent; internal static KeyCode StaticToggleKey = (KeyCode)292; internal static KeyCode StaticOverlayToggleKey = (KeyCode)287; private static string _currentPlayerName; 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; } public static bool HasTheVault { get; private set; } public static bool HasSMUT { get; private set; } public static bool HasHavensBirthright { get; private set; } public static bool HasSenpaisChest { get; private set; } public static bool HasBirthdayReminder { get; private set; } public static bool HasSunhavenTodo { get; private set; } public static bool HasHavensAlmanac { get; private set; } public static bool HasTrinketFortune { get; private set; } public static bool HasCropOptimizer { get; private set; } public static bool HasFasterRaces { get; private set; } public static bool HasHavensRespec { get; private set; } public static bool HasGiftingAssistant { get; private set; } public static bool HasUltraPolygamy => UltraPolygamyHelper.IsAvailable; public static bool IsAuthorized => true; public static string CurrentPlayerName => _currentPlayerName; private void Awake() { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) Instance = this; Log = ((BaseUnityPlugin)this).Logger; ConfigFile = CreateNamedConfig(); ConfigFileHelper.ReplacePluginConfig((BaseUnityPlugin)(object)this, ConfigFile, (Action)Log.LogWarning); Log.LogInfo((object)"Loading Haven Dev Tools v2.1.1"); try { ModConfig.Initialize(ConfigFile); StaticToggleKey = ModConfig.ToggleKey.Value; StaticOverlayToggleKey = ModConfig.OverlayToggleKey.Value; CreatePersistentRunner(); DetectInstalledMods(); ModConfig.SyncTheVaultFullVaultInspectorToPlugin(); _staticItemInspector = new ItemInspector(); _staticCurrencyTracker = new CurrencyTracker(); _staticBundleInspector = new BundleInspector(); _staticRaceModifierTracker = new RaceModifierTracker(); _staticCommandConsole = new CommandConsole(); _staticLogViewer = new LogViewerPanel(); CreateUIComponents(); _harmony = new Harmony("com.azraelgodking.havendevtools"); LocalizationBootstrap.BindForceEnglish(ConfigFile); LocalizationBootstrap.Init("com.azraelgodking.havendevtools", _harmony, Log); PatchPlayerInit(); SceneManager.sceneLoaded += OnSceneLoaded; if (ModConfig.CheckForUpdates.Value) { VersionChecker.CheckForUpdate("com.azraelgodking.havendevtools", "2.1.1", Log, delegate(VersionChecker.VersionCheckResult result) { result.NotifyUpdateAvailable(Log); }); } Log.LogInfo((object)"Haven Dev Tools loaded successfully!"); ReportStartupHealth(); Log.LogInfo((object)$"Press {ModConfig.ToggleKey.Value} to open the debug window (requires authorization)"); Log.LogInfo((object)$"Press {ModConfig.OverlayToggleKey.Value} to toggle the overlay"); Log.LogInfo((object)$"Detected mods - TheVault: {HasTheVault}, SMUT: {HasSMUT}, Birthright: {HasHavensBirthright}, SenpaisChest: {HasSenpaisChest}, Birthday: {HasBirthdayReminder}, Todo: {HasSunhavenTodo}, Almanac: {HasHavensAlmanac}, TrinketFortune: {HasTrinketFortune}"); } catch (Exception arg) { Log.LogError((object)string.Format("Failed to load {0}: {1}", "Haven Dev Tools", arg)); } } private static ConfigFile CreateNamedConfig() { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, "HavenDevTools.cfg"); string text2 = Path.Combine(Paths.ConfigPath, "com.azraelgodking.havendevtools.cfg"); try { if (!File.Exists(text) && File.Exists(text2)) { File.Copy(text2, text); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[Config] Migration to HavenDevTools.cfg failed: " + ex.Message)); } } return new ConfigFile(text, true); } private void CreatePersistentRunner() { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown if (!((Object)(object)_persistentRunner != (Object)null) || !((Object)(object)_persistentRunnerComponent != (Object)null)) { _persistentRunner = new GameObject("HavenDevTools_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _persistentRunnerComponent = _persistentRunner.AddComponent(); Log.LogInfo((object)"[PersistentRunner] Created hidden persistent runner"); } } 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_0010: 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) GameObject val = new GameObject("HavenDevTools_UI"); Object.DontDestroyOnLoad((Object)val); _staticDebugWindow = val.AddComponent(); _staticDebugOverlay = val.AddComponent(); _staticFpsCounterOverlay = val.AddComponent(); Log.LogInfo((object)"UI components created"); } public static void EnsureUIComponentsExist() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) 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("HavenDevTools_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _persistentRunnerComponent = _persistentRunner.AddComponent(); ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"[EnsureUI] PersistentRunner recreated"); } } if ((Object)(object)_staticDebugWindow == (Object)null || (Object)(object)_staticDebugOverlay == (Object)null || (Object)(object)_staticFpsCounterOverlay == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)"[EnsureUI] Recreating UI components..."); } GameObject val = new GameObject("HavenDevTools_UI"); Object.DontDestroyOnLoad((Object)val); _staticDebugWindow = val.AddComponent(); _staticDebugOverlay = val.AddComponent(); _staticFpsCounterOverlay = val.AddComponent(); ManualLogSource log4 = Log; if (log4 != null) { log4.LogInfo((object)"[EnsureUI] UI components recreated"); } } if (_staticItemInspector == null) { _staticItemInspector = new ItemInspector(); } if (_staticCurrencyTracker == null) { _staticCurrencyTracker = new CurrencyTracker(); } if (_staticBundleInspector == null) { _staticBundleInspector = new BundleInspector(); } if (_staticRaceModifierTracker == null) { _staticRaceModifierTracker = new RaceModifierTracker(); } if (_staticCommandConsole == null) { _staticCommandConsole = new CommandConsole(); } if (_staticLogViewer == null) { _staticLogViewer = new LogViewerPanel(); } } catch (Exception ex) { ManualLogSource log5 = Log; if (log5 != null) { log5.LogError((object)("[EnsureUI] Error recreating components: " + ex.Message)); } } } public static void RefreshInstalledMods() { HasTheVault = IsSuitePluginLoaded("com.azraelgodking.thevault", "TheVault"); HasSMUT = IsSuitePluginLoaded("com.azraelgodking.sunhavenmuseumutilitytracker", "SunHavenMuseumUtilityTracker"); HasHavensBirthright = IsSuitePluginLoaded("com.azraelgodking.havensbirthright", "HavensBirthright"); HasSenpaisChest = IsSuitePluginLoaded("com.azraelgodking.senpaischest", "SenpaisChest"); HasBirthdayReminder = IsSuitePluginLoaded("com.azraelgodking.squirrelsbirthdayreminder", "BirthdayReminder"); HasSunhavenTodo = IsSuitePluginLoaded("com.azraelgodking.sunhaventodo", "SunhavenTodo"); HasHavensAlmanac = IsSuitePluginLoaded("com.azraelgodking.havensalmanac", "HavensAlmanac"); HasTrinketFortune = IsSuitePluginLoaded("com.azraelgodking.trinketfortune", "TrinketFortune"); HasCropOptimizer = IsSuitePluginLoaded("com.azraelgodking.cropoptimizer", "CropOptimizer"); HasFasterRaces = IsSuitePluginLoaded("com.azraelgodking.fasterraces", "FasterRaces"); HasHavensRespec = IsSuitePluginLoaded("com.azraelgodking.havensrespec", "HavensRespec"); HasGiftingAssistant = IsSuitePluginLoaded("com.azraelgodking.giftingassistant", "GiftingAssistant"); } private static bool IsSuitePluginLoaded(string pluginGuid, string assemblyName) { if (Chainloader.PluginInfos != null && !string.IsNullOrEmpty(pluginGuid) && Chainloader.PluginInfos.ContainsKey(pluginGuid)) { return true; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { if (string.Equals(assemblies[i].GetName().Name, assemblyName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private void DetectInstalledMods() { RefreshInstalledMods(); Log.LogInfo((object)$"Mod detection complete - TheVault: {HasTheVault}, SMUT: {HasSMUT}, Birthright: {HasHavensBirthright}, SenpaisChest: {HasSenpaisChest}, Birthday: {HasBirthdayReminder}, Todo: {HasSunhavenTodo}, Almanac: {HasHavensAlmanac}, TrinketFortune: {HasTrinketFortune}, CropOptimizer: {HasCropOptimizer}, FasterRaces: {HasFasterRaces}, Respec: {HasHavensRespec}, GiftingAssistant: {HasGiftingAssistant}, UltraPolygamy: {HasUltraPolygamy}"); } private void ReportStartupHealth() { ModDiagnostics.LogModStartup(Log, "com.azraelgodking.havendevtools", "Haven Dev Tools", "2.1.1", "Suite tabs: " + ModHealthIntegrationSummary.Build(("Vault", "com.azraelgodking.thevault"), ("SMUT", "com.azraelgodking.sunhavenmuseumutilitytracker"), ("Birthright", "com.azraelgodking.havensbirthright"), ("SenpaisChest", "com.azraelgodking.senpaischest"), ("Birthday", "com.azraelgodking.squirrelsbirthdayreminder"), ("Todo", "com.azraelgodking.sunhaventodo"), ("Almanac", "com.azraelgodking.havensalmanac"), ("TrinketFortune", "com.azraelgodking.trinketfortune"), ("CropOptimizer", "com.azraelgodking.cropoptimizer"), ("FasterRaces", "com.azraelgodking.fasterraces"), ("Respec", "com.azraelgodking.havensrespec"), ("GiftingAssistant", "com.azraelgodking.giftingassistant")), "startup", false); } private void PatchPlayerInit() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown try { MethodInfo methodInfo = AccessTools.Method(typeof(Player), "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)"Patched Player.InitializeAsOwner"); } } catch (Exception ex) { Log.LogError((object)("Failed to patch player init: " + ex.Message)); } } private static void OnPlayerInitialized() { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[HavenDevTools] Player initialized, ensuring UI exists..."); } EnsureUIComponentsExist(); UpdatePlayerContext(); } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { Log.LogDebug((object)("[SceneChange] Scene loaded: '" + ((Scene)(ref scene)).name + "'")); string text = ((Scene)(ref scene)).name.ToLowerInvariant(); if (text.Contains("menu") || text.Contains("title")) { Log.LogDebug((object)("Menu scene detected: " + ((Scene)(ref scene)).name)); _currentPlayerName = null; } } private void OnApplicationQuit() { _applicationQuitting = true; } 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 + ")")); } } } private static void UpdatePlayerContext() { try { if ((Object)(object)Player.Instance != (Object)null) { PropertyInfo property = ((object)Player.Instance).GetType().GetProperty("PlayerName", BindingFlags.Instance | BindingFlags.Public); if (property != null) { _currentPlayerName = property.GetValue(Player.Instance) as string; } if (string.IsNullOrEmpty(_currentPlayerName)) { _currentPlayerName = ((Object)Player.Instance).name; } } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("[HavenDevTools] Player name fallback: " + ex.Message)); } _currentPlayerName = "Unknown"; } } public static ItemInspector GetItemInspector() { return _staticItemInspector; } public static CurrencyTracker GetCurrencyTracker() { return _staticCurrencyTracker; } public static BundleInspector GetBundleInspector() { return _staticBundleInspector; } public static RaceModifierTracker GetRaceModifierTracker() { return _staticRaceModifierTracker; } public static DebugWindow GetDebugWindow() { return _staticDebugWindow; } public static DebugOverlay GetDebugOverlay() { return _staticDebugOverlay; } public static CommandConsole GetCommandConsole() { return _staticCommandConsole; } public static LogViewerPanel GetLogViewer() { return _staticLogViewer; } public static void ToggleDebugWindow() { _staticDebugWindow?.Toggle(); } public static void ToggleDebugOverlay() { _staticDebugOverlay?.Toggle(); } } public class PersistentRunner : MonoBehaviour { private float _heartbeatTimer; private int _heartbeatCount; private const float HEARTBEAT_INTERVAL = 30f; private bool _syncedTheVaultInspectorOnce; private void Awake() { ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PersistentRunner] Awake - hidden persistent runner active"); } } private void Update() { if (!_syncedTheVaultInspectorOnce) { _syncedTheVaultInspectorOnce = true; Plugin.RefreshInstalledMods(); ModConfig.SyncTheVaultFullVaultInspectorToPlugin(); } CheckHotkeys(); _heartbeatTimer += Time.deltaTime; if (_heartbeatTimer >= 30f) { _heartbeatTimer = 0f; _heartbeatCount++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)string.Format("[PersistentRunner Heartbeat #{0}] Player: {1}", _heartbeatCount, Plugin.CurrentPlayerName ?? "none")); } } } private void CheckHotkeys() { //IL_0013: 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) try { if (!TextInputFocusGuard.ShouldDeferModHotkeys(Plugin.Log)) { if (Input.GetKeyDown(Plugin.StaticToggleKey)) { Plugin.ToggleDebugWindow(); } if (Input.GetKeyDown(Plugin.StaticOverlayToggleKey)) { Plugin.ToggleDebugOverlay(); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("[PersistentRunner] Hotkey error: " + ex.Message)); } } } 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.havendevtools"; public const string PLUGIN_NAME = "Haven Dev Tools"; public const string PLUGIN_VERSION = "2.1.1"; } } namespace HavenDevTools.UI { public class DebugOverlay : MonoBehaviour { private bool _isVisible; private GUIStyle _overlayStyle; private GUIStyle _labelStyle; private GUIStyle _valueStyle; private bool _stylesInitialized; private float _updateTimer; private const float UPDATE_INTERVAL = 0.5f; private string _playerName = ""; private string _currentRace = ""; private int _gold; private string _heldItemInfo = ""; private string _position = ""; private void Awake() { _isVisible = ModConfig.ShowOverlayOnStart?.Value ?? false; } public void Toggle() { _isVisible = !_isVisible; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[DebugOverlay] " + (_isVisible ? "Shown" : "Hidden"))); } } public void Show() { _isVisible = true; } public void Hide() { _isVisible = false; } private void Update() { if (_isVisible) { _updateTimer += Time.deltaTime; if (_updateTimer >= 0.5f) { _updateTimer = 0f; UpdateCachedData(); } } } private void UpdateCachedData() { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) try { _playerName = Plugin.CurrentPlayerName ?? "Unknown"; _currentRace = Plugin.GetRaceModifierTracker()?.GetCurrentRace() ?? "Unknown"; _gold = Plugin.GetCurrencyTracker()?.GetGold() ?? 0; (int, string)? tuple = Plugin.GetItemInspector()?.GetHeldItem(); _heldItemInfo = (tuple.HasValue ? $"{tuple.Value.Item2} ({tuple.Value.Item1})" : "None"); if ((Object)(object)Player.Instance != (Object)null) { Vector3 position = ((Component)Player.Instance).transform.position; _position = $"({position.x:F1}, {position.y:F1})"; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[DebugOverlay] Update error: " + ex.Message)); } } } private void OnGUI() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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) if (_isVisible) { InitializeStyles(); OverlayPositionType overlayPosition = ModConfig.GetOverlayPosition(); Rect overlayRect = GetOverlayRect(overlayPosition); GUI.Box(overlayRect, "", _overlayStyle); GUILayout.BeginArea(new Rect(((Rect)(ref overlayRect)).x + 8f, ((Rect)(ref overlayRect)).y + 8f, ((Rect)(ref overlayRect)).width - 16f, ((Rect)(ref overlayRect)).height - 16f)); DrawOverlayContent(); GUILayout.EndArea(); } } private void InitializeStyles() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: 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_00d8: Expected O, but got Unknown if (!_stylesInitialized) { Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, new Color(0.05f, 0.05f, 0.1f, 0.85f)); val.Apply(); GUIStyle val2 = new GUIStyle(GUI.skin.box); val2.normal.background = val; _overlayStyle = val2; GUIStyle val3 = new GUIStyle(GUI.skin.label) { fontSize = 11 }; val3.normal.textColor = new Color(0.7f, 0.7f, 0.8f); _labelStyle = val3; GUIStyle val4 = new GUIStyle(GUI.skin.label) { fontSize = 11, fontStyle = (FontStyle)1 }; val4.normal.textColor = new Color(0.9f, 0.95f, 1f); _valueStyle = val4; _stylesInitialized = true; } } private Rect GetOverlayRect(OverlayPositionType position) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_00a6: 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) float num = 220f; ConfigEntry showPerformance = ModConfig.ShowPerformance; float num2 = ((showPerformance == null || showPerformance.Value) ? 155 : 130); float num3 = 10f; return (Rect)(position switch { OverlayPositionType.TopLeft => new Rect(num3, num3, num, num2), OverlayPositionType.TopRight => new Rect((float)Screen.width - num - num3, num3, num, num2), OverlayPositionType.BottomLeft => new Rect(num3, (float)Screen.height - num2 - num3, num, num2), OverlayPositionType.BottomRight => new Rect((float)Screen.width - num - num3, (float)Screen.height - num2 - num3, num, num2), _ => new Rect((float)Screen.width - num - num3, num3, num, num2), }); } private void DrawOverlayContent() { GUILayout.Label(ModLocalization.T("devtools.title"), _valueStyle, Array.Empty()); GUILayout.Space(3f); DrawRow(ModLocalization.T("devtools.overlay.player"), _playerName); DrawRow(ModLocalization.T("devtools.overlay.race"), _currentRace); DrawRow(ModLocalization.T("devtools.overlay.gold"), _gold.ToString("N0")); DrawRow(ModLocalization.T("devtools.overlay.position"), _position); DrawRow(ModLocalization.T("devtools.overlay.held"), _heldItemInfo); ConfigEntry showPerformance = ModConfig.ShowPerformance; if (showPerformance == null || showPerformance.Value) { GUILayout.Label(ModLocalization.T("devtools.perf.fps", PerformanceSampler.SmoothedFps), _valueStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.perf.memory", PerformanceSampler.MemoryMb), _valueStyle, Array.Empty()); } GUILayout.FlexibleSpace(); GUILayout.Label(ModLocalization.T("devtools.overlay.keys"), _labelStyle, Array.Empty()); } private void DrawRow(string label, string value) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(label, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); GUILayout.Label(value, _valueStyle, Array.Empty()); GUILayout.EndHorizontal(); } } public class DebugWindow : MonoBehaviour { private bool _isVisible; private Rect _windowRect; private Vector2 _scrollPosition; private GUIStyle _windowStyle; private GUIStyle _headerStyle; private GUIStyle _sectionHeaderStyle; private GUIStyle _buttonStyle; private GUIStyle _labelStyle; private GUIStyle _textFieldStyle; private GUIStyle _boxStyle; private bool _stylesInitialized; private bool _isResizing; private Vector2 _resizeStartMouse; private Vector2 _resizeStartSize; private float _contentAreaHeight = 280f; private GUIStyle _headerBarStyle; private GUIStyle _subtitleStyle; private GUIStyle _fpsHeaderStyle; private GUIStyle _closeButtonStyle; private GUIStyle _resizeGripStyle; private Texture2D _headerBarTexture; private Texture2D _resizeGripTexture; private int _selectedTab; private int _toolsSubTab; private string _cachedTabLabelsLanguage; private string[] _mainTabLabels; private string[] _toolsSubTabLabels; private List _raceNamesSource; private string[] _raceNamesForGrid; private static readonly HashSet AzraelsExtensionGuids = new HashSet { "com.azraelgodking.trinketfortune" }; private string _itemSearchText = ""; private string _lastItemSearchQuery; private string _itemIdInput = ""; private string _spawnAmount = "1"; private int _selectedItemId; private string _selectedItemName = ""; private List> _searchResults = new List>(); private Vector2 _itemScrollPosition; private Vector2 _currencyScrollPosition; private int _selectedSectionIndex; private int _selectedBundleIndex; private Vector2 _bundleScrollPosition; private int _selectedRaceIndex; private Vector2 _raceScrollPosition; private bool _relationshipRomanceOnly = true; private string _relationshipFilter = ""; private int _selectedRelationshipIndex = -1; private string _relationshipHeartsInput = "40"; private string _relationshipCycleInput = "8"; private Vector2 _relationshipScrollPosition; private string _relationshipStatusMessage = ""; private float _relationshipStatusUntil; private List _relationshipRows = new List(); private bool _requestConsoleFocus; private string _marriableFilter = ""; private Vector2 _marriableScrollPosition; private readonly HashSet _marriableSelectedKeys = new HashSet(StringComparer.Ordinal); private List _marriableRows = new List(); private string _marriableStatusMessage = ""; private float _marriableStatusUntil; private const string PAUSE_ID = "HavenDevTools_Debug"; private int _lastDrawnToolsSubTab = -1; private void Awake() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) float num = ModConfig.DebugWindowWidth?.Value ?? 560f; float num2 = ModConfig.DebugWindowHeight?.Value ?? 640f; _windowRect = new Rect(50f, 50f, num, num2); DebugWindowLayout.ClampToScreen(ref _windowRect); } public void Toggle() { if (_isVisible) { Hide(); } else { Show(); } } public void Show() { _isVisible = true; BlockInput(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DebugWindow] Shown"); } } public void Hide() { _isVisible = false; UnblockInput(); GUIUtility.keyboardControl = 0; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[DebugWindow] Hidden"); } } private void BlockInput() { ConfigEntry pauseGameWhenDebugOpen = ModConfig.PauseGameWhenDebugOpen; if (pauseGameWhenDebugOpen == null || !pauseGameWhenDebugOpen.Value) { return; } try { if ((Object)(object)Player.Instance != (Object)null) { Player.Instance.AddPauseObject("HavenDevTools_Debug"); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[DebugWindow] Could not block input: " + ex.Message)); } } } private void UnblockInput() { try { if ((Object)(object)Player.Instance != (Object)null) { Player.Instance.RemovePauseObject("HavenDevTools_Debug"); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[DebugWindow] Could not unblock input: " + ex.Message)); } } } private void Update() { if (_isVisible && Input.GetKeyDown((KeyCode)27)) { Hide(); } } private void OnGUI() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) if (_isVisible) { InitializeStyles(); DebugWindowLayout.ClampToScreen(ref _windowRect); _windowRect = GUI.Window(((object)this).GetHashCode(), _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); DebugWindowLayout.ClampToScreen(ref _windowRect); } } private float ListHeight(float minHeight, float heightRatio = 0.42f) { return Mathf.Max(minHeight, _contentAreaHeight * heightRatio); } private void EnsureToolbarLabels() { string currentLanguage = ModLocalization.CurrentLanguage; if (_mainTabLabels == null || !string.Equals(_cachedTabLabelsLanguage, currentLanguage, StringComparison.OrdinalIgnoreCase)) { _cachedTabLabelsLanguage = currentLanguage; _mainTabLabels = new string[4] { ModLocalization.T("devtools.tab.health"), ModLocalization.T("devtools.tab.tools"), ModLocalization.T("devtools.tab.suite"), ModLocalization.T("devtools.tab.extensions") }; _toolsSubTabLabels = new string[8] { ModLocalization.T("devtools.tab.relationships"), ModLocalization.T("devtools.tab.marriable"), ModLocalization.T("devtools.tab.items"), ModLocalization.T("devtools.tab.currencies"), ModLocalization.T("devtools.tab.console"), ModLocalization.T("devtools.tab.log"), ModLocalization.T("devtools.tab.perf"), ModLocalization.T("devtools.tab.utility") }; } } private string[] GetRaceNamesForGrid(IReadOnlyList races) { if (_raceNamesSource != null && _raceNamesForGrid != null && _raceNamesSource.Count == races.Count) { bool flag = true; for (int i = 0; i < races.Count; i++) { if (!string.Equals(_raceNamesSource[i], races[i], StringComparison.Ordinal)) { flag = false; break; } } if (flag) { return _raceNamesForGrid; } } _raceNamesSource = new List(races); _raceNamesForGrid = _raceNamesSource.ToArray(); return _raceNamesForGrid; } private void InitializeStyles() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown //IL_0109: Expected O, but got Unknown //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Expected O, but got Unknown //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Expected O, but got Unknown //IL_01a1: 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_01ae: 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_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Expected O, but got Unknown //IL_01fa: Expected O, but got Unknown //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Expected O, but got Unknown //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Expected O, but got Unknown //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Expected O, but got Unknown //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Expected O, but got Unknown //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Expected O, but got Unknown //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_0307: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Expected O, but got Unknown //IL_0318: Unknown result type (might be due to invalid IL or missing references) //IL_031f: Expected O, but got Unknown //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_034a: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Expected O, but got Unknown //IL_0369: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_0392: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_03a0: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03af: Expected O, but got Unknown //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_03bc: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03cc: Unknown result type (might be due to invalid IL or missing references) //IL_03d9: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Expected O, but got Unknown //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_041f: Unknown result type (might be due to invalid IL or missing references) //IL_0429: Expected O, but got Unknown //IL_0429: Unknown result type (might be due to invalid IL or missing references) //IL_042e: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Expected O, but got Unknown //IL_043d: Expected O, but got Unknown //IL_0447: Unknown result type (might be due to invalid IL or missing references) //IL_0451: Expected O, but got Unknown if (!_stylesInitialized) { Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, new Color(0.08f, 0.08f, 0.12f, 0.98f)); val.Apply(); Texture2D val2 = new Texture2D(1, 1); val2.SetPixel(0, 0, new Color(0.2f, 0.4f, 0.6f, 0.9f)); val2.Apply(); Texture2D val3 = new Texture2D(1, 1); val3.SetPixel(0, 0, new Color(0.3f, 0.5f, 0.7f, 0.95f)); val3.Apply(); Texture2D val4 = new Texture2D(1, 1); val4.SetPixel(0, 0, new Color(0.12f, 0.12f, 0.18f, 0.9f)); val4.Apply(); GUIStyle val5 = new GUIStyle(GUI.skin.window); val5.normal.background = val; val5.normal.textColor = Color.white; val5.padding = new RectOffset(10, 10, 25, 10); _windowStyle = val5; GUIStyle val6 = new GUIStyle(GUI.skin.label) { fontSize = 18, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; val6.normal.textColor = new Color(0.4f, 0.8f, 1f); _headerStyle = val6; GUIStyle val7 = new GUIStyle(GUI.skin.label) { fontSize = 14, fontStyle = (FontStyle)1 }; val7.normal.textColor = new Color(0.8f, 0.9f, 1f); _sectionHeaderStyle = val7; GUIStyle val8 = new GUIStyle(GUI.skin.button) { fontSize = 12 }; val8.normal.background = val2; val8.normal.textColor = Color.white; val8.hover.background = val3; val8.hover.textColor = Color.white; val8.padding = new RectOffset(8, 8, 4, 4); _buttonStyle = val8; GUIStyle val9 = new GUIStyle(GUI.skin.label) { fontSize = 12 }; val9.normal.textColor = new Color(0.9f, 0.9f, 0.95f); _labelStyle = val9; GUIStyle val10 = new GUIStyle(GUI.skin.textField) { fontSize = 12 }; val10.normal.textColor = Color.white; _textFieldStyle = val10; GUIStyle val11 = new GUIStyle(GUI.skin.box); val11.normal.background = val4; _boxStyle = val11; _headerBarTexture = new Texture2D(1, 1); _headerBarTexture.SetPixel(0, 0, new Color(0.14f, 0.18f, 0.28f, 0.98f)); _headerBarTexture.Apply(); GUIStyle val12 = new GUIStyle(); val12.normal.background = _headerBarTexture; _headerBarStyle = val12; GUIStyle val13 = new GUIStyle(_labelStyle) { fontSize = 11 }; val13.normal.textColor = new Color(0.7f, 0.78f, 0.88f); _subtitleStyle = val13; Texture2D val14 = new Texture2D(1, 1); val14.SetPixel(0, 0, new Color(0.45f, 0.18f, 0.18f, 0.9f)); val14.Apply(); Texture2D val15 = new Texture2D(1, 1); val15.SetPixel(0, 0, new Color(0.65f, 0.22f, 0.22f, 0.95f)); val15.Apply(); GUIStyle val16 = new GUIStyle(GUI.skin.button) { fontSize = 14, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, padding = new RectOffset(0, 0, 0, 0) }; val16.normal.background = val14; val16.normal.textColor = Color.white; val16.hover.background = val15; val16.hover.textColor = Color.white; _closeButtonStyle = val16; _resizeGripTexture = DebugWindowLayout.CreateResizeGripTexture(); GUIStyle val17 = new GUIStyle(GUI.skin.box); val17.normal.background = _resizeGripTexture; val17.border = new RectOffset(0, 0, 0, 0); val17.padding = new RectOffset(0, 0, 0, 0); _resizeGripStyle = val17; _windowStyle.padding = new RectOffset(8, 8, 8, 8); _stylesInitialized = true; } } private void DrawHeaderBar(int windowId) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Expected O, but got Unknown Rect rect = GUILayoutUtility.GetRect(0f, 44f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.DrawTexture(rect, (Texture)(object)_headerBarTexture, (ScaleMode)0); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + 10f, ((Rect)(ref rect)).y + 4f, ((Rect)(ref rect)).width - 30f - 16f, ((Rect)(ref rect)).height - 6f); GUI.Label(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y, ((Rect)(ref val)).width, 20f), ModLocalization.T("devtools.title"), _headerStyle); GUI.Label(new Rect(((Rect)(ref val)).x, ((Rect)(ref val)).y + 20f, ((Rect)(ref val)).width, 18f), ModLocalization.T("devtools.player", Plugin.CurrentPlayerName ?? string.Empty), _subtitleStyle); if (GUI.Button(new Rect(((Rect)(ref rect)).xMax - 30f - 6f, ((Rect)(ref rect)).y + 8f, 30f, 24f), "×", _closeButtonStyle)) { Hide(); } if (_fpsHeaderStyle == null) { _fpsHeaderStyle = new GUIStyle(_subtitleStyle) { alignment = (TextAnchor)5 }; } _fpsHeaderStyle.normal.textColor = PerformanceSampler.FpsColor(PerformanceSampler.SmoothedFps); GUI.Label(new Rect(((Rect)(ref rect)).xMax - 30f - 96f, ((Rect)(ref rect)).y + 10f, 84f, 20f), ModLocalization.T("devtools.perf.fps", PerformanceSampler.SmoothedFps), _fpsHeaderStyle); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width - 30f - 8f, 44f)); } private void DrawModStatusRow() { string text = ModLocalization.T("devtools.yes"); string text2 = ModLocalization.T("devtools.no"); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.mod.thevault", Plugin.HasTheVault ? text : text2), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.mod.smut", Plugin.HasSMUT ? text : text2), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.mod.birthright", Plugin.HasHavensBirthright ? text : text2), _labelStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.mod.senpai", Plugin.HasSenpaisChest ? text : text2), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.mod.todo", Plugin.HasSunhavenTodo ? text : text2), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.mod.ultrapolygamy", Plugin.HasUltraPolygamy ? text : text2), _labelStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private void DrawWindowFooter() { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.window.hint"), _subtitleStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); if (DebugWindowLayout.DrawResizeGrip(((Rect)(ref _windowRect)).width, ((Rect)(ref _windowRect)).height, _resizeGripStyle, ref _isResizing, ref _resizeStartMouse, ref _resizeStartSize, out var newWidth, out var newHeight)) { ((Rect)(ref _windowRect)).width = newWidth; ((Rect)(ref _windowRect)).height = newHeight; ModConfig.SaveDebugWindowSize(newWidth, newHeight); } } private void DrawWindow(int windowId) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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) GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); DrawHeaderBar(windowId); GUILayout.Space(6f); DrawModStatusRow(); GUILayout.Space(8f); EnsureToolbarLabels(); _selectedTab = GUILayout.Toolbar(_selectedTab, _mainTabLabels, _buttonStyle, Array.Empty()); GUILayout.Space(8f); _contentAreaHeight = Mathf.Max(120f, ((Rect)(ref _windowRect)).height - 210f); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandHeight(true), GUILayout.ExpandWidth(true) }); switch (_selectedTab) { case 0: ModHealthDashboardPanel.RefreshRows(); ModHealthDashboardPanel.Draw(_boxStyle, _sectionHeaderStyle, _buttonStyle, _labelStyle); break; case 1: DrawToolsTab(); break; case 2: AzraelsModsPanel.Draw(_boxStyle, _buttonStyle, _labelStyle, _sectionHeaderStyle); break; case 3: DrawExtensionsTab(); break; } GUILayout.EndScrollView(); DrawWindowFooter(); GUILayout.EndVertical(); } private void DrawExtensionsTab() { IReadOnlyList panels = DevToolsRegistry.Panels; if (panels.Count == 0) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.extensions"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(5f); GUILayout.Label(ModLocalization.T("devtools.extensions.none"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } foreach (IDevToolsPanel item in panels) { if (!AzraelsExtensionGuids.Contains(item.ModGuid)) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(item.DisplayName, _sectionHeaderStyle, Array.Empty()); GUILayout.Space(5f); try { item.Draw(_boxStyle, _buttonStyle, _labelStyle); } catch (Exception ex) { GUILayout.Label("Error: " + ex.Message, _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.Space(8f); } } } private void DrawToolsTab() { EnsureToolbarLabels(); _toolsSubTab = GUILayout.Toolbar(_toolsSubTab, _toolsSubTabLabels, _buttonStyle, Array.Empty()); GUILayout.Space(8f); if (_toolsSubTab == 4 && _lastDrawnToolsSubTab != 4) { _requestConsoleFocus = true; } switch (_toolsSubTab) { case 0: DrawRelationshipsTab(); break; case 1: DrawMarriableTab(); break; case 2: DrawItemsTab(); break; case 3: DrawCurrenciesTab(); break; case 4: DrawConsoleTab(); break; case 5: DrawLogViewerTab(); break; case 6: DrawPerformanceTab(); break; case 7: DrawUtilityTab(); break; } _lastDrawnToolsSubTab = _toolsSubTab; } private void DrawRelationshipsTab() { //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.relationships.title"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(4f); if (!NpcRelationshipEditor.IsAvailable) { GUILayout.Label(ModLocalization.T("devtools.relationships.unavailable"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } GUILayout.BeginHorizontal(Array.Empty()); _relationshipRomanceOnly = GUILayout.Toggle(_relationshipRomanceOnly, ModLocalization.T("devtools.relationships.romanceOnly"), _labelStyle, Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.relationships.refresh"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { RefreshRelationshipRows(); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.relationships.filter"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); _relationshipFilter = GUILayout.TextField(_relationshipFilter ?? string.Empty, _textFieldStyle, Array.Empty()); if (GUILayout.Button("X", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) })) { _relationshipFilter = string.Empty; RefreshRelationshipRows(); } GUILayout.EndHorizontal(); if (_relationshipRows.Count == 0) { RefreshRelationshipRows(); } _relationshipScrollPosition = GUILayout.BeginScrollView(_relationshipScrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(ListHeight(100f, 0.45f)) }); for (int i = 0; i < _relationshipRows.Count; i++) { NpcRelationshipEditor.NpcRelationshipRow npcRelationshipRow = _relationshipRows[i]; string text = BuildRelationshipStatusLabel(npcRelationshipRow); if (GUILayout.Toggle(i == _selectedRelationshipIndex, $"{npcRelationshipRow.DisplayName} ({npcRelationshipRow.Key}) — {npcRelationshipRow.Hearts:0.#} {text}", _buttonStyle, Array.Empty())) { _selectedRelationshipIndex = i; _relationshipHeartsInput = ((int)Math.Round(npcRelationshipRow.Hearts)).ToString(); } } GUILayout.EndScrollView(); if (_selectedRelationshipIndex >= 0 && _selectedRelationshipIndex < _relationshipRows.Count) { NpcRelationshipEditor.NpcRelationshipRow selected = _relationshipRows[_selectedRelationshipIndex]; GUILayout.Label(ModLocalization.T("devtools.relationships.selected", selected.DisplayName, selected.Key), _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.relationships.hearts"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); _relationshipHeartsInput = GUILayout.TextField(_relationshipHeartsInput ?? "0", _textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); if (GUILayout.Button(ModLocalization.T("devtools.relationships.setHearts"), _buttonStyle, Array.Empty())) { RunRelationshipAction(() => NpcRelationshipEditor.SetHearts(selected.Key, ParseInt(_relationshipHeartsInput, 0))); } int[] array = new int[4] { 25, 40, 75, 100 }; for (int num = 0; num < array.Length; num++) { int preset = array[num]; if (GUILayout.Button(preset.ToString(), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) })) { RunRelationshipAction(() => NpcRelationshipEditor.SetHearts(selected.Key, preset)); } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("-5", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) })) { RunRelationshipAction(() => NpcRelationshipEditor.AdjustHearts(selected.Key, -5)); } if (GUILayout.Button("+5", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) })) { RunRelationshipAction(() => NpcRelationshipEditor.AdjustHearts(selected.Key, 5)); } if (selected.Romanceable && GUILayout.Button(ModLocalization.T("devtools.relationships.date"), _buttonStyle, Array.Empty())) { RunRelationshipAction(() => NpcRelationshipEditor.DateNpc(selected.Key)); } if (selected.Romanceable && GUILayout.Button(ModLocalization.T("devtools.relationships.platonic"), _buttonStyle, Array.Empty())) { RunRelationshipAction(() => NpcRelationshipEditor.SetPlatonic(selected.Key)); } GUILayout.EndHorizontal(); if (selected.Romanceable) { GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.relationships.marry"), _buttonStyle, Array.Empty())) { RunRelationshipAction(() => NpcRelationshipEditor.MarryNpc(selected.Key)); } if (GUILayout.Button(ModLocalization.T("devtools.relationships.divorce"), _buttonStyle, Array.Empty())) { RunRelationshipAction(() => NpcRelationshipEditor.DivorceNpc(selected.Key)); } if (GUILayout.Button(ModLocalization.T("devtools.relationships.setPrimary"), _buttonStyle, Array.Empty())) { RunRelationshipAction(() => NpcRelationshipEditor.SetPrimarySpouse(selected.Key)); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.relationships.cycle"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); _relationshipCycleInput = GUILayout.TextField(_relationshipCycleInput ?? "0", _textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); if (GUILayout.Button(ModLocalization.T("devtools.relationships.skipCycle"), _buttonStyle, Array.Empty())) { RunRelationshipAction(() => NpcRelationshipEditor.SkipToCycle(selected.Key, ParseInt(_relationshipCycleInput, 0))); } GUILayout.EndHorizontal(); } } GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.relationships.divorcePrimary"), _buttonStyle, Array.Empty())) { RunRelationshipAction(() => NpcRelationshipEditor.DivorcePrimarySpouse()); } if (GUILayout.Button(ModLocalization.T("devtools.relationships.resetAll"), _buttonStyle, Array.Empty())) { NpcRelationshipEditor.ResetAllHearts(); SetRelationshipStatus(ModLocalization.T("devtools.relationships.actionOk")); RefreshRelationshipRows(); } GUILayout.EndHorizontal(); if (!string.IsNullOrEmpty(_relationshipStatusMessage) && Time.realtimeSinceStartup < _relationshipStatusUntil) { GUILayout.Label(_relationshipStatusMessage, _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawMarriableTab() { //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.marriable.title"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(4f); bool isAvailable = UltraPolygamyHelper.IsAvailable; GUILayout.Label(ModLocalization.T(UltraPolygamyHelper.StatusLocalizationKey), _labelStyle, Array.Empty()); if (!NpcRelationshipEditor.IsAvailable) { GUILayout.Label(ModLocalization.T("devtools.marriable.unavailable"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } if (!isAvailable) { GUILayout.Label(ModLocalization.T("devtools.marriable.polyRequired"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.marriable.refresh"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { RefreshMarriableRows(); } if (GUILayout.Button(ModLocalization.T("devtools.marriable.selectAll"), _buttonStyle, Array.Empty())) { SelectAllMarriable(unmarriedOnly: true); } if (GUILayout.Button(ModLocalization.T("devtools.marriable.clearSelection"), _buttonStyle, Array.Empty())) { _marriableSelectedKeys.Clear(); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.marriable.filter"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); _marriableFilter = GUILayout.TextField(_marriableFilter ?? string.Empty, _textFieldStyle, Array.Empty()); if (GUILayout.Button("X", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) })) { _marriableFilter = string.Empty; RefreshMarriableRows(); } GUILayout.EndHorizontal(); if (_marriableRows.Count == 0) { RefreshMarriableRows(); } _marriableScrollPosition = GUILayout.BeginScrollView(_marriableScrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(ListHeight(120f, 0.5f)) }); foreach (NpcRelationshipEditor.NpcRelationshipRow marriableRow in _marriableRows) { bool flag = _marriableSelectedKeys.Contains(marriableRow.Key); string text = (marriableRow.IsMarriedTo ? ModLocalization.T("devtools.marriable.alreadyMarried") : string.Empty); string text2 = (string.IsNullOrEmpty(text) ? $"{marriableRow.DisplayName} ({marriableRow.Key}) — {marriableRow.Hearts:0.#}" : $"{marriableRow.DisplayName} ({marriableRow.Key}) — {marriableRow.Hearts:0.#} [{text}]"); bool flag2 = GUILayout.Toggle(flag, text2, _buttonStyle, Array.Empty()); if (flag2 != flag) { if (flag2) { _marriableSelectedKeys.Add(marriableRow.Key); } else { _marriableSelectedKeys.Remove(marriableRow.Key); } } } GUILayout.EndScrollView(); GUILayout.Label(ModLocalization.T("devtools.marriable.selectedCount", _marriableSelectedKeys.Count), _labelStyle, Array.Empty()); GUI.enabled = _marriableSelectedKeys.Count > 0; if (GUILayout.Button(ModLocalization.T("devtools.marriable.marrySelected"), _buttonStyle, Array.Empty())) { RunMarriableAction(); } GUI.enabled = true; if (!string.IsNullOrEmpty(_marriableStatusMessage) && Time.realtimeSinceStartup < _marriableStatusUntil) { GUILayout.Label(_marriableStatusMessage, _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void RefreshMarriableRows() { _marriableRows = new List(NpcRelationshipEditor.GetRows(romanceOnly: true, _marriableFilter)); _marriableSelectedKeys.RemoveWhere((string key) => _marriableRows.All((NpcRelationshipEditor.NpcRelationshipRow r) => !string.Equals(r.Key, key, StringComparison.Ordinal))); } private void SelectAllMarriable(bool unmarriedOnly) { RefreshMarriableRows(); foreach (NpcRelationshipEditor.NpcRelationshipRow marriableRow in _marriableRows) { if (!unmarriedOnly || !marriableRow.IsMarriedTo) { _marriableSelectedKeys.Add(marriableRow.Key); } } } private void RunMarriableAction() { if (!UltraPolygamyHelper.IsAvailable) { SetMarriableStatus(ModLocalization.T("devtools.marriable.polyRequired")); return; } if (_marriableSelectedKeys.Count == 0) { SetMarriableStatus(ModLocalization.T("devtools.marriable.noneSelected")); return; } List npcKeys = _marriableSelectedKeys.ToList(); int num = 0; try { num = NpcRelationshipEditor.MarryMultiple(npcKeys); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Marriable] " + ex.Message)); } } if (num > 0) { SetMarriableStatus(ModLocalization.T("devtools.marriable.actionOk", num)); _marriableSelectedKeys.Clear(); } else { SetMarriableStatus(ModLocalization.T("devtools.marriable.actionFail")); } RefreshMarriableRows(); RefreshRelationshipRows(); } private void SetMarriableStatus(string message) { _marriableStatusMessage = message; _marriableStatusUntil = Time.realtimeSinceStartup + 3f; } private string BuildRelationshipStatusLabel(NpcRelationshipEditor.NpcRelationshipRow row) { List list = new List(); if (row.IsDating) { list.Add(ModLocalization.T("devtools.relationships.status.dating")); } if (row.IsMarriedTo) { list.Add(ModLocalization.T("devtools.relationships.status.married")); } if (row.IsPrimarySpouse) { list.Add(ModLocalization.T("devtools.relationships.status.primary")); } if (list.Count <= 0) { return string.Empty; } return "[" + string.Join(", ", list) + "]"; } private void RefreshRelationshipRows() { _relationshipRows = new List(NpcRelationshipEditor.GetRows(_relationshipRomanceOnly, _relationshipFilter)); if (_selectedRelationshipIndex >= _relationshipRows.Count) { _selectedRelationshipIndex = _relationshipRows.Count - 1; } } private void RunRelationshipAction(Func action) { bool flag = false; try { flag = action(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Relationships] " + ex.Message)); } } SetRelationshipStatus(flag ? ModLocalization.T("devtools.relationships.actionOk") : ModLocalization.T("devtools.relationships.actionFail")); RefreshRelationshipRows(); } private void SetRelationshipStatus(string message) { _relationshipStatusMessage = message; _relationshipStatusUntil = Time.realtimeSinceStartup + 3f; } private static int ParseInt(string text, int fallback) { if (!int.TryParse(text, out var result)) { return fallback; } return result; } private void DrawConsoleTab() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.console.title"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(5f); GUILayout.Label(ModLocalization.T("devtools.console.hint"), _labelStyle, Array.Empty()); CommandConsole commandConsole = Plugin.GetCommandConsole(); if (commandConsole != null) { float outputScrollHeight = ListHeight(140f, 0.55f); if (_requestConsoleFocus) { commandConsole.Draw(_boxStyle, _buttonStyle, _labelStyle, _textFieldStyle, requestFocus: true, outputScrollHeight); _requestConsoleFocus = false; } else { commandConsole.Draw(_boxStyle, _buttonStyle, _labelStyle, _textFieldStyle, requestFocus: false, outputScrollHeight); } } else { GUILayout.Label(ModLocalization.T("devtools.console.init"), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawLogViewerTab() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.log.title"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(5f); LogViewerPanel logViewer = Plugin.GetLogViewer(); if (logViewer != null) { logViewer.Draw(_boxStyle, _buttonStyle, _labelStyle, ListHeight(140f, 0.55f)); } else { GUILayout.Label(ModLocalization.T("devtools.log.init"), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawPerformanceTab() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.perf.title"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(5f); GUILayout.Label(ModLocalization.T("devtools.perf.fps", PerformanceSampler.SmoothedFps), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.perf.fpsRange", PerformanceSampler.MinFps, PerformanceSampler.MaxFps), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.perf.memory", PerformanceSampler.MemoryMb), _labelStyle, Array.Empty()); GUILayout.Space(6f); if (ModConfig.ShowFpsCounter != null) { bool value = ModConfig.ShowFpsCounter.Value; bool flag = GUILayout.Toggle(value, ModLocalization.T("devtools.perf.showFpsCounter"), _labelStyle, Array.Empty()); if (flag != value) { ModConfig.ShowFpsCounter.Value = flag; } } if (ModConfig.ShowPerformance != null) { bool value2 = ModConfig.ShowPerformance.Value; bool flag2 = GUILayout.Toggle(value2, ModLocalization.T("devtools.perf.showOverlayPerf"), _labelStyle, Array.Empty()); if (flag2 != value2) { ModConfig.ShowPerformance.Value = flag2; } } GUILayout.Label(ModLocalization.T("devtools.perf.fpsCounterHint"), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.perf.overlayHint"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawItemsTab() { //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.items.title"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(5f); (int, string)? tuple = Plugin.GetItemInspector()?.GetHeldItem(); if (tuple.HasValue) { GUILayout.Label(ModLocalization.T("devtools.items.held", tuple.Value.Item2, tuple.Value.Item1), _labelStyle, Array.Empty()); } else { GUILayout.Label(ModLocalization.T("devtools.items.heldNone"), _labelStyle, Array.Empty()); } GUILayout.Space(10f); GUILayout.Label(ModLocalization.T("devtools.search"), _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); _itemSearchText = GUILayout.TextField(_itemSearchText, _textFieldStyle, Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.clear"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) })) { _itemSearchText = ""; _lastItemSearchQuery = ""; _searchResults.Clear(); } GUILayout.EndHorizontal(); if (_itemSearchText != _lastItemSearchQuery) { _lastItemSearchQuery = _itemSearchText; _searchResults = ItemSearch.SearchItems(_itemSearchText); } if (_selectedItemId > 0) { GUILayout.Space(2f); GUILayout.Label(ModLocalization.T("devtools.selected", ItemSearch.FormatDisplay(_selectedItemName, _selectedItemId)), _labelStyle, Array.Empty()); } if (_searchResults.Count > 0) { GUILayout.Space(4f); float num = Mathf.Min(ListHeight(80f, 0.35f), (float)(24 + _searchResults.Count * 22)); _itemScrollPosition = GUILayout.BeginScrollView(_itemScrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num) }); foreach (KeyValuePair searchResult in _searchResults) { if (GUILayout.Button(ItemSearch.FormatDisplay(searchResult.Value, searchResult.Key), (searchResult.Key == _selectedItemId) ? _buttonStyle : _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) })) { _selectedItemId = searchResult.Key; _selectedItemName = searchResult.Value; _itemIdInput = searchResult.Key.ToString(); } } GUILayout.EndScrollView(); } else if (_itemSearchText.Length >= 2) { GUILayout.Space(2f); GUILayout.Label(ModLocalization.T("devtools.items.notFound"), _labelStyle, Array.Empty()); } GUILayout.Space(10f); GUILayout.Label(ModLocalization.T("devtools.spawn"), _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.spawn.id"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(22f) }); _itemIdInput = GUILayout.TextField(_itemIdInput, _textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.Label(ModLocalization.T("devtools.spawn.qty"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) }); _spawnAmount = GUILayout.TextField(_spawnAmount, _textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); if (GUILayout.Button("1", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(22f) })) { _spawnAmount = "1"; } if (GUILayout.Button("10", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { _spawnAmount = "10"; } if (GUILayout.Button("99", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { _spawnAmount = "99"; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.spawn.toInventory"), _buttonStyle, Array.Empty()) && int.TryParse(_itemIdInput, out var result) && int.TryParse(_spawnAmount, out var result2) && result2 > 0) { Plugin.GetItemInspector()?.SpawnItem(result, result2); } if (_selectedItemId > 0 && GUILayout.Button(ModLocalization.T("devtools.spawn.one"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { Plugin.GetItemInspector()?.SpawnItem(_selectedItemId); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawCurrenciesTab() { //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.currency.title"), _sectionHeaderStyle, Array.Empty()); GUILayout.FlexibleSpace(); CurrencyTracker currencyTracker = Plugin.GetCurrencyTracker(); if (currencyTracker != null && GUILayout.Button(ModLocalization.T("devtools.relationships.refresh"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { currencyTracker.GetSummary(forceRefresh: true); } GUILayout.EndHorizontal(); GUILayout.Space(5f); if (currencyTracker == null) { GUILayout.Label(ModLocalization.T("devtools.currency.unavailable"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } CurrencySummary summary = currencyTracker.GetSummary(); GUILayout.Label(ModLocalization.T("devtools.currency.gold", summary.Gold), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.currency.orbs", summary.Orbs), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.currency.tickets", summary.Tickets), _labelStyle, Array.Empty()); GUILayout.Space(10f); GUILayout.Label(ModLocalization.T("devtools.currency.inventory"), _sectionHeaderStyle, Array.Empty()); _currencyScrollPosition = GUILayout.BeginScrollView(_currencyScrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(ListHeight(90f, 0.3f)) }); if (summary.InventoryCurrencies.Count == 0) { GUILayout.Label(ModLocalization.T("devtools.currency.none"), _labelStyle, Array.Empty()); } else { foreach (KeyValuePair inventoryCurrency in summary.InventoryCurrencies) { GUILayout.Label($" {inventoryCurrency.Key}: {inventoryCurrency.Value}", _labelStyle, Array.Empty()); } } GUILayout.EndScrollView(); if (Plugin.HasTheVault) { GUILayout.Space(10f); GUILayout.Label(ModLocalization.T("devtools.currency.vault"), _sectionHeaderStyle, Array.Empty()); if (summary.VaultCurrencies.Count == 0) { GUILayout.Label(ModLocalization.T("devtools.currency.vaultEmpty"), _labelStyle, Array.Empty()); } else { foreach (KeyValuePair vaultCurrency in summary.VaultCurrencies) { GUILayout.Label($" {vaultCurrency.Key}: {vaultCurrency.Value}", _labelStyle, Array.Empty()); } } } else { GUILayout.Space(5f); GUILayout.Label(ModLocalization.T("devtools.currency.vaultMissing"), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawBundlesTab() { //IL_02e8: 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_0310: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.museum.title"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(5f); if (!Plugin.HasSMUT) { GUILayout.Label(ModLocalization.T("devtools.museum.notInstalled"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } BundleInspector bundleInspector = Plugin.GetBundleInspector(); if (bundleInspector == null) { GUILayout.Label(ModLocalization.T("devtools.museum.unavailable"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } DonationStats donationStats = bundleInspector.GetDonationStats(); if (donationStats.IsLoaded) { GUILayout.Label(ModLocalization.T("devtools.museum.character", donationStats.CharacterName), _labelStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.museum.progress", donationStats.TotalDonated, donationStats.TotalItems, donationStats.CompletionPercent), _labelStyle, Array.Empty()); } else { GUILayout.Label(ModLocalization.T("devtools.museum.notLoaded"), _labelStyle, Array.Empty()); } GUILayout.Space(10f); List allSections = bundleInspector.GetAllSections(); if (allSections.Count == 0) { GUILayout.Label(ModLocalization.T("devtools.museum.noData"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } string[] array = new string[allSections.Count]; for (int i = 0; i < allSections.Count; i++) { array[i] = allSections[i].Name; } GUILayout.Label(ModLocalization.T("devtools.museum.section"), _labelStyle, Array.Empty()); _selectedSectionIndex = GUILayout.SelectionGrid(_selectedSectionIndex, array, 3, _buttonStyle, Array.Empty()); if (_selectedSectionIndex >= allSections.Count) { _selectedSectionIndex = 0; } MuseumSectionInfo museumSectionInfo = allSections[_selectedSectionIndex]; GUILayout.Space(5f); if (museumSectionInfo.Bundles.Count > 0) { string[] array2 = new string[museumSectionInfo.Bundles.Count]; for (int j = 0; j < museumSectionInfo.Bundles.Count; j++) { array2[j] = museumSectionInfo.Bundles[j].Name; } if (_selectedBundleIndex >= museumSectionInfo.Bundles.Count) { _selectedBundleIndex = 0; } GUILayout.Label(ModLocalization.T("devtools.museum.bundle"), _labelStyle, Array.Empty()); _selectedBundleIndex = GUILayout.SelectionGrid(_selectedBundleIndex, array2, 2, _buttonStyle, Array.Empty()); MuseumBundleInfo museumBundleInfo = museumSectionInfo.Bundles[_selectedBundleIndex]; GUILayout.Space(5f); GUILayout.Label(ModLocalization.T("devtools.museum.itemsIn", museumBundleInfo.Name), _sectionHeaderStyle, Array.Empty()); _bundleScrollPosition = GUILayout.BeginScrollView(_bundleScrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(ListHeight(100f, 0.35f)) }); foreach (MuseumItemInfo item in museumBundleInfo.Items) { string arg = (bundleInspector.HasDonated(item.Id) ? "[X]" : "[ ]"); string text = ((item.Quantity > 1) ? $" x{item.Quantity}" : ""); bool flag = item.GameItemId > 0; GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label($"{arg} {item.Name} (ID: {item.GameItemId})", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) }); bool enabled = GUI.enabled; GUI.enabled = flag; if (GUILayout.Button(flag ? ModLocalization.T("devtools.museum.spawn", text) : "—", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }) && flag) { Plugin.GetItemInspector()?.SpawnItem(item.GameItemId, item.Quantity); } GUI.enabled = enabled; if (!flag) { GUILayout.Label(ModLocalization.T("devtools.museum.idInAssets"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } GUILayout.EndVertical(); } private void DrawRaceBonusesTab() { //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.race.title"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(5f); RaceModifierTracker raceModifierTracker = Plugin.GetRaceModifierTracker(); if (raceModifierTracker == null) { GUILayout.Label(ModLocalization.T("devtools.race.unavailable"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } string currentRace = raceModifierTracker.GetCurrentRace(); GUILayout.Label(ModLocalization.T("devtools.race.current", currentRace), _labelStyle, Array.Empty()); GUILayout.Space(10f); if (Plugin.HasHavensBirthright) { List activeRaceBonuses = raceModifierTracker.GetActiveRaceBonuses(); GUILayout.Label(ModLocalization.T("devtools.race.bonuses"), _sectionHeaderStyle, Array.Empty()); if (activeRaceBonuses.Count == 0) { GUILayout.Label(ModLocalization.T("devtools.race.noBonuses"), _labelStyle, Array.Empty()); } else { foreach (RaceBonusInfo item in activeRaceBonuses) { GUILayout.Label(" " + item.Type + ": " + item.GetFormattedValue(), _labelStyle, Array.Empty()); GUILayout.Label(" " + item.Description, _labelStyle, Array.Empty()); } } } else { GUILayout.Label(ModLocalization.T("devtools.race.birthrightMissing"), _labelStyle, Array.Empty()); } GUILayout.Space(10f); GUILayout.Label(ModLocalization.T("devtools.race.browse"), _sectionHeaderStyle, Array.Empty()); List allRaces = raceModifierTracker.GetAllRaces(); if (allRaces.Count > 0) { string[] raceNamesForGrid = GetRaceNamesForGrid(allRaces); if (_selectedRaceIndex >= allRaces.Count) { _selectedRaceIndex = 0; } _selectedRaceIndex = GUILayout.SelectionGrid(_selectedRaceIndex, raceNamesForGrid, 4, _buttonStyle, Array.Empty()); GUILayout.Space(5f); List bonusesForRace = raceModifierTracker.GetBonusesForRace(allRaces[_selectedRaceIndex]); _raceScrollPosition = GUILayout.BeginScrollView(_raceScrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(ListHeight(100f, 0.35f)) }); if (bonusesForRace.Count == 0) { GUILayout.Label(ModLocalization.T("devtools.race.noDefined"), _labelStyle, Array.Empty()); } else { foreach (RaceBonusInfo item2 in bonusesForRace) { GUILayout.Label(" " + item2.Type + ": " + item2.GetFormattedValue(), _labelStyle, Array.Empty()); GUILayout.Label(" " + item2.Description, _labelStyle, Array.Empty()); } } GUILayout.EndScrollView(); } GUILayout.EndVertical(); } private void DrawUtilityTab() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.utility"), _sectionHeaderStyle, Array.Empty()); GUILayout.Space(5f); GUILayout.Label(ModLocalization.T("devtools.config"), _sectionHeaderStyle, Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.config.reload"), _buttonStyle, Array.Empty())) { ConfigReloader.ReloadHavenDevToolsConfig(); } GUILayout.Space(10f); GUILayout.Label(ModLocalization.T("devtools.logging"), _sectionHeaderStyle, Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.logCurrencyIds"), _buttonStyle, Array.Empty())) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"=== Currency Item IDs ==="); } foreach (KeyValuePair currencyItemId in CurrencyTracker.CurrencyItemIds) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$" {currencyItemId.Key}: {currencyItemId.Value}"); } } } if (GUILayout.Button(ModLocalization.T("devtools.logPlayerStats"), _buttonStyle, Array.Empty())) { LogPlayerStats(); } GUILayout.EndVertical(); } private void LogPlayerStats() { try { if ((Object)(object)Player.Instance == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Player not available"); } return; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"=== Player Stats ==="); } PropertyInfo[] properties = ((object)Player.Instance).GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public); foreach (PropertyInfo propertyInfo in properties) { try { if (!propertyInfo.CanRead || propertyInfo.GetIndexParameters().Length != 0) { continue; } object value = propertyInfo.GetValue(Player.Instance); if (value != null && (value is int || value is float || value is string || value is bool)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$" {propertyInfo.Name}: {value}"); } } } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogDebug((object)("[DebugWindow] Player stats property iteration: " + ex.Message)); } } } } catch (Exception ex2) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)("Error logging player stats: " + ex2.Message)); } } } } internal static class DebugWindowLayout { public const float MinWidth = 420f; public const float MinHeight = 340f; public const float HeaderHeight = 44f; public const float GripSize = 14f; public const float DefaultWidth = 560f; public const float DefaultHeight = 640f; public static void ClampToScreen(ref Rect windowRect) { float num = Mathf.Max(420f, (float)Screen.width - 16f); float num2 = Mathf.Max(340f, (float)Screen.height - 16f); ((Rect)(ref windowRect)).width = Mathf.Clamp(((Rect)(ref windowRect)).width, 420f, num); ((Rect)(ref windowRect)).height = Mathf.Clamp(((Rect)(ref windowRect)).height, 340f, num2); ((Rect)(ref windowRect)).x = Mathf.Clamp(((Rect)(ref windowRect)).x, 8f, (float)Screen.width - ((Rect)(ref windowRect)).width - 8f); ((Rect)(ref windowRect)).y = Mathf.Clamp(((Rect)(ref windowRect)).y, 8f, (float)Screen.height - ((Rect)(ref windowRect)).height - 8f); } public static bool DrawResizeGrip(float windowWidth, float windowHeight, GUIStyle gripStyle, ref bool isResizing, ref Vector2 resizeStartMouse, ref Vector2 resizeStartSize, out float newWidth, out float newHeight) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Invalid comparison between Unknown and I4 //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Invalid comparison between Unknown and I4 //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Invalid comparison between Unknown and I4 //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) newWidth = windowWidth; newHeight = windowHeight; Rect val = default(Rect); ((Rect)(ref val))..ctor(windowWidth - 14f - 4f, windowHeight - 14f - 4f, 14f, 14f); GUI.Box(val, GUIContent.none, gripStyle); Event current = Event.current; if ((int)current.type == 0 && current.button == 0 && ((Rect)(ref val)).Contains(current.mousePosition)) { isResizing = true; resizeStartMouse = current.mousePosition; resizeStartSize = new Vector2(windowWidth, windowHeight); current.Use(); } if (isResizing && (int)current.type == 3) { Vector2 val2 = current.mousePosition - resizeStartMouse; newWidth = Mathf.Clamp(resizeStartSize.x + val2.x, 420f, (float)Screen.width - 16f); newHeight = Mathf.Clamp(resizeStartSize.y + val2.y, 340f, (float)Screen.height - 16f); current.Use(); return true; } if (((int)current.type == 1) & isResizing) { isResizing = false; return true; } if ((int)current.type == 1) { isResizing = false; } return false; } public static Texture2D CreateResizeGripTexture() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0063: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(16, 16, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; Color val2 = default(Color); ((Color)(ref val2))..ctor(0f, 0f, 0f, 0f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.55f, 0.65f, 0.75f, 0.85f); Color[] array = (Color[])(object)new Color[256]; for (int i = 0; i < array.Length; i++) { array[i] = val2; } for (int j = 0; j < 4; j++) { int num = 3 + j * 3; for (int k = 0; k < 4 - j; k++) { int num2 = num + k; int num3 = 12 - k - j; if (num2 >= 0 && num2 < 16 && num3 >= 0 && num3 < 16) { array[num3 * 16 + num2] = val3; } } } val.SetPixels(array); val.Apply(); return val; } } public class FpsCounterOverlay : MonoBehaviour { private GUIStyle _labelStyle; private GUIStyle _boxStyle; private bool _stylesInitialized; private void Update() { PerformanceSampler.Tick(); } private void OnGUI() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) ConfigEntry showFpsCounter = ModConfig.ShowFpsCounter; if (showFpsCounter != null && showFpsCounter.Value) { InitializeStyles(); Rect counterRect = GetCounterRect(ModConfig.GetFpsCounterPosition()); GUI.Box(counterRect, GUIContent.none, _boxStyle); _labelStyle.normal.textColor = PerformanceSampler.FpsColor(PerformanceSampler.SmoothedFps); GUI.Label(new Rect(((Rect)(ref counterRect)).x + 6f, ((Rect)(ref counterRect)).y + 3f, ((Rect)(ref counterRect)).width - 12f, ((Rect)(ref counterRect)).height - 6f), ModLocalization.T("devtools.perf.fps", PerformanceSampler.SmoothedFps), _labelStyle); } } private void InitializeStyles() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown if (!_stylesInitialized) { Texture2D val = new Texture2D(1, 1); val.SetPixel(0, 0, new Color(0.05f, 0.05f, 0.1f, 0.72f)); val.Apply(); GUIStyle val2 = new GUIStyle(GUI.skin.box); val2.normal.background = val; _boxStyle = val2; _labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 13, fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; _stylesInitialized = true; } } private static Rect GetCounterRect(OverlayPositionType position) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) return (Rect)(position switch { OverlayPositionType.TopLeft => new Rect(8f, 8f, 92f, 24f), OverlayPositionType.TopRight => new Rect((float)Screen.width - 92f - 8f, 8f, 92f, 24f), OverlayPositionType.BottomLeft => new Rect(8f, (float)Screen.height - 24f - 8f, 92f, 24f), OverlayPositionType.BottomRight => new Rect((float)Screen.width - 92f - 8f, (float)Screen.height - 24f - 8f, 92f, 24f), _ => new Rect(8f, 8f, 92f, 24f), }); } } public class LogViewerPanel { private class LogCaptureListener : ILogListener, IDisposable { public void LogEvent(object sender, LogEventArgs eventArgs) { OnLog(eventArgs); } public void Dispose() { } } private class LogEntry { public int Level; public string Message; public string Source; public DateTime Timestamp; } private static readonly List _entries = new List(); private static readonly object _lock = new object(); private Vector2 _scrollPosition; private int _levelFilterIndex = 1; private static readonly string[] _levelNames = new string[4] { "Debug", "Info", "Warning", "Error" }; private static bool _listenerAttached; private GUIStyle _logLineStyle; private bool _logLineStyleInitialized; private static readonly LogCaptureListener _captureListener = new LogCaptureListener(); public LogViewerPanel() { if (!_listenerAttached) { Logger.Listeners.Add((ILogListener)(object)_captureListener); _listenerAttached = true; } } private static void OnLog(LogEventArgs e) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 LogLevel level = e.Level; int num; if ((int)level <= 4) { if ((int)level != 2) { if ((int)level != 4) { goto IL_002f; } num = 2; } else { num = 3; } } else if ((int)level != 16) { if ((int)level != 32) { goto IL_002f; } num = 0; } else { num = 1; } goto IL_0031; IL_0031: int level2 = num; lock (_lock) { _entries.Add(new LogEntry { Level = level2, Message = (e.Data?.ToString() ?? ""), Source = e.Source.SourceName, Timestamp = DateTime.Now }); int num2 = ModConfig.MaxLogEntries?.Value ?? 500; while (_entries.Count > num2) { _entries.RemoveAt(0); } return; } IL_002f: num = 1; goto IL_0031; } public void Draw(GUIStyle boxStyle, GUIStyle buttonStyle, GUIStyle labelStyle, float listHeight = 200f) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: 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_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_015d: 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_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) if (!_logLineStyleInitialized) { _logLineStyle = new GUIStyle(labelStyle); _logLineStyleInitialized = true; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.log.level"), labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); _levelFilterIndex = GUILayout.Toolbar(_levelFilterIndex, _levelNames, buttonStyle, Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.log.export"), buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { ExportToFile(); } GUILayout.EndHorizontal(); GUILayout.Space(5f); List list; lock (_lock) { list = new List(_entries); } _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(listHeight) }); int levelFilterIndex = _levelFilterIndex; foreach (LogEntry item in list) { if (item.Level >= levelFilterIndex) { Color textColor = (Color)(item.Level switch { 0 => new Color(0.6f, 0.6f, 0.6f), 1 => new Color(0.9f, 0.9f, 0.9f), 2 => new Color(1f, 0.85f, 0.3f), 3 => new Color(1f, 0.4f, 0.4f), _ => Color.white, }); _logLineStyle.normal.textColor = textColor; GUILayout.Label($"[{item.Timestamp:HH:mm:ss}] [{_levelNames[item.Level]}] [{item.Source}] {item.Message}", _logLineStyle, Array.Empty()); } } GUILayout.EndScrollView(); } private static void ExportToFile() { try { string text = Path.Combine(Paths.BepInExRootPath, "LogExport.txt"); List list; lock (_lock) { list = new List(_entries); } List list2 = new List(); foreach (LogEntry item in list) { list2.Add($"[{item.Timestamp:yyyy-MM-dd HH:mm:ss}] [{_levelNames[item.Level]}] [{item.Source}] {item.Message}"); } File.WriteAllLines(text, list2); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[LogViewer] Exported {list2.Count} lines to {text}"); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("[LogViewer] Export failed: " + ex.Message)); } } } } public static class ModHealthDashboardPanel { public static readonly (string guid, string name)[] KnownSuiteMods = new(string, string)[13] { ("com.azraelgodking.havendevtools", "Haven Dev Tools"), ("com.azraelgodking.senpaischest", "Senpai's Chest"), ("com.azraelgodking.squirrelsbirthdayreminder", "Birthday Reminder"), ("com.azraelgodking.havensbirthright", "Haven's Birthright"), ("com.azraelgodking.sunhavenmuseumutilitytracker", "S.M.U.T."), ("com.azraelgodking.sunhaventodo", "Sun Haven Todo"), ("com.azraelgodking.thevault", "The Vault"), ("com.azraelgodking.havensalmanac", "Haven's Almanac"), ("com.azraelgodking.fasterraces", "Faster Races"), ("com.azraelgodking.trinketfortune", "Trinket Fortune"), ("com.azraelgodking.cropoptimizer", "Crop Optimizer"), ("com.azraelgodking.havensrespec", "Haven's Respec"), ("com.azraelgodking.giftingassistant", "Gifting Assistant") }; private static IReadOnlyList _cachedRows = Array.Empty(); private static ModHealthAggregator.SharedCodeSkewAnalysis _cachedSkew; private static DateTime _lastRefreshUtc = DateTime.MinValue; private static readonly HashSet ExpandedGuids = new HashSet(StringComparer.OrdinalIgnoreCase); private static Vector2 _scrollPosition; private static Dictionary _versionResults = new Dictionary(StringComparer.OrdinalIgnoreCase); private static bool _isCheckingVersions; public static void Draw(GUIStyle boxStyle, GUIStyle sectionHeaderStyle, GUIStyle buttonStyle, GUIStyle labelStyle) { //IL_004b: 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_005a: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(boxStyle, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.health.title"), sectionHeaderStyle, Array.Empty()); GUILayout.Space(4f); DrawToolbar(buttonStyle, labelStyle); GUILayout.Space(6f); DrawSkewBanner(labelStyle); GUILayout.Space(4f); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, Array.Empty()); DrawModRows(buttonStyle, labelStyle); GUILayout.Space(8f); DrawVersionCheckerSection(sectionHeaderStyle, buttonStyle, labelStyle); GUILayout.EndScrollView(); GUILayout.EndVertical(); } private static void DrawToolbar(GUIStyle buttonStyle, GUIStyle labelStyle) { GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(ModLocalization.T("devtools.health.refresh"), buttonStyle, Array.Empty())) { RefreshRows(force: true); } if (GUILayout.Button(ModLocalization.T("devtools.health.copyAll"), buttonStyle, Array.Empty())) { CopyToClipboard(ModHealthAggregator.BuildCombinedDiagnosticDump(_cachedRows)); } int num = 0; for (int i = 0; i < _cachedRows.Count; i++) { if (_cachedRows[i].HasIssue) { num++; } } object[] obj = new object[3] { _cachedRows.Count.ToString(), num.ToString(), null }; ModHealthAggregator.SharedCodeSkewAnalysis cachedSkew = _cachedSkew; obj[2] = ((cachedSkew != null && cachedSkew.HasSkew) ? ModLocalization.T("devtools.health.skewShort") : ModLocalization.T("devtools.health.skewNone")); GUILayout.Label(ModLocalization.T("devtools.health.summary", obj), labelStyle, Array.Empty()); GUILayout.EndHorizontal(); } private static void DrawSkewBanner(GUIStyle labelStyle) { //IL_0015: 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) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown if (_cachedSkew == null || !_cachedSkew.HasSkew) { return; } GUIStyle val = new GUIStyle(labelStyle) { wordWrap = true }; val.normal.textColor = new Color(1f, 0.85f, 0.35f); GUIStyle val2 = val; GUILayout.Label(ModLocalization.T("devtools.health.skewTitle"), val2, Array.Empty()); foreach (KeyValuePair> item in _cachedSkew.ModsByRevision) { string text = string.Join(", ", item.Value); GUILayout.Label(ModLocalization.T("devtools.health.skewRevision", item.Key, text), val2, Array.Empty()); } } private static void DrawModRows(GUIStyle buttonStyle, GUIStyle labelStyle) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (_cachedRows.Count == 0) { GUILayout.Label(ModLocalization.T("devtools.health.noMods"), labelStyle, Array.Empty()); return; } foreach (ModHealthAggregator.MergedModHealthRow cachedRow in _cachedRows) { GUILayout.BeginVertical(GUI.skin.box, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); string key = (cachedRow.HasIssue ? "devtools.health.statusIssue" : (cachedRow.HasDiagnosticsReport ? "devtools.health.statusOk" : "devtools.health.statusNoReport")); GUIStyle val = new GUIStyle(labelStyle); val.normal.textColor = (cachedRow.HasIssue ? new Color(1f, 0.55f, 0.55f) : (cachedRow.HasDiagnosticsReport ? new Color(0.55f, 1f, 0.55f) : new Color(0.75f, 0.75f, 0.75f))); GUIStyle val2 = val; GUILayout.Label(ModLocalization.T(key), val2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(72f) }); GUILayout.Label(string.IsNullOrEmpty(cachedRow.InstalledVersion) ? cachedRow.DisplayName : (cachedRow.DisplayName + " v" + cachedRow.InstalledVersion), labelStyle, Array.Empty()); if (GUILayout.Button(ExpandedGuids.Contains(cachedRow.PluginGuid) ? "▼" : ModLocalization.T("devtools.health.expand"), buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }) && !ExpandedGuids.Add(cachedRow.PluginGuid)) { ExpandedGuids.Remove(cachedRow.PluginGuid); } if (GUILayout.Button(ModLocalization.T("devtools.health.copyRow"), buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(56f) })) { StringBuilder stringBuilder = new StringBuilder(); cachedRow.AppendDiagnosticDump(stringBuilder); CopyToClipboard(stringBuilder.ToString()); } GUILayout.EndHorizontal(); if (!string.IsNullOrEmpty(cachedRow.IntegrationSummary)) { GUILayout.Label(ModLocalization.T("devtools.health.integrations", cachedRow.IntegrationSummary), labelStyle, Array.Empty()); } if (!string.IsNullOrEmpty(cachedRow.SharedCodeRevision)) { GUILayout.Label(ModLocalization.T("devtools.health.sharedCode", cachedRow.SharedCodeRevision), labelStyle, Array.Empty()); } if (ExpandedGuids.Contains(cachedRow.PluginGuid)) { StringBuilder stringBuilder2 = new StringBuilder(); cachedRow.AppendDiagnosticDump(stringBuilder2); GUILayout.Label(stringBuilder2.ToString(), labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.Space(4f); } } private static void DrawVersionCheckerSection(GUIStyle sectionHeaderStyle, GUIStyle buttonStyle, GUIStyle labelStyle) { //IL_013e: 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_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Expected O, but got Unknown //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Expected O, but got Unknown //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Expected O, but got Unknown GUILayout.Label(ModLocalization.T("devtools.versions.title"), sectionHeaderStyle, Array.Empty()); GUILayout.Space(3f); GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = !_isCheckingVersions; if (GUILayout.Button(_isCheckingVersions ? ModLocalization.T("devtools.versions.checking") : ModLocalization.T("devtools.versions.checkAll"), buttonStyle, Array.Empty())) { CheckAllModVersions(); } GUI.enabled = true; if (GUILayout.Button(ModLocalization.T("devtools.versions.testNotify"), buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) })) { TestUpdateNotification(); } GUILayout.EndHorizontal(); GUILayout.Space(5f); (string, string)[] knownSuiteMods = KnownSuiteMods; for (int i = 0; i < knownSuiteMods.Length; i++) { (string, string) tuple = knownSuiteMods[i]; GUILayout.BeginHorizontal(Array.Empty()); string text = ModHealthResolver.ResolveInstalledVersion(tuple.Item1); GUILayout.Label((text != null) ? (tuple.Item2 + " v" + text) : (tuple.Item2 + " (" + ModLocalization.T("devtools.versions.notInstalled") + ")"), labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); if (_versionResults.TryGetValue(tuple.Item1, out VersionChecker.VersionCheckResult value)) { if (!value.Success) { GUIStyle val = new GUIStyle(labelStyle); val.normal.textColor = new Color(1f, 0.5f, 0.5f); GUIStyle val2 = val; GUILayout.Label("Error: " + value.ErrorMessage, val2, Array.Empty()); } else if (value.UpdateAvailable) { GUIStyle val3 = new GUIStyle(labelStyle); val3.normal.textColor = new Color(1f, 0.9f, 0.3f); GUIStyle val4 = val3; GUILayout.Label(ModLocalization.T("devtools.versions.update", value.LatestVersion), val4, Array.Empty()); } else { GUIStyle val5 = new GUIStyle(labelStyle); val5.normal.textColor = new Color(0.5f, 1f, 0.5f); GUIStyle val6 = val5; GUILayout.Label(ModLocalization.T("devtools.versions.upToDate"), val6, Array.Empty()); } } else { GUILayout.Label(ModLocalization.T("devtools.versions.notChecked"), labelStyle, Array.Empty()); } GUILayout.EndHorizontal(); } } public static void RefreshRows(bool force = false) { if (force || !(DateTime.UtcNow - _lastRefreshUtc < TimeSpan.FromSeconds(2.0))) { _cachedRows = ModHealthAggregator.CollectMergedRows(ModHealthResolver.ResolveDisplayName, ModHealthResolver.ResolveInstalledVersion); _cachedSkew = ModHealthAggregator.AnalyzeSharedCodeSkew(_cachedRows); _lastRefreshUtc = DateTime.UtcNow; } } public static int GetIssueCount() { RefreshRows(); int num = 0; for (int i = 0; i < _cachedRows.Count; i++) { if (_cachedRows[i].HasIssue) { num++; } } return num; } private static void CheckAllModVersions() { _isCheckingVersions = true; _versionResults.Clear(); int pendingChecks = KnownSuiteMods.Length; (string, string)[] knownSuiteMods = KnownSuiteMods; for (int i = 0; i < knownSuiteMods.Length; i++) { (string guid, string name) mod = knownSuiteMods[i]; string text = ModHealthResolver.ResolveInstalledVersion(mod.guid); if (text == null) { int num = pendingChecks; pendingChecks = num - 1; if (pendingChecks <= 0) { _isCheckingVersions = false; } continue; } VersionChecker.CheckForUpdate(mod.guid, text, Plugin.Log, delegate(VersionChecker.VersionCheckResult result) { _versionResults[mod.guid] = result; int num2 = pendingChecks; pendingChecks = num2 - 1; if (pendingChecks <= 0) { _isCheckingVersions = false; } RefreshRows(force: true); }); } } private static void TestUpdateNotification() { new VersionChecker.VersionCheckResult { Success = true, UpdateAvailable = true, ModName = "Haven Dev Tools", CurrentVersion = "1.0.0", LatestVersion = "9.9.9", NexusUrl = "https://example.com" }.NotifyUpdateAvailable(Plugin.Log); } private static void CopyToClipboard(string text) { try { GUIUtility.systemCopyBuffer = text ?? string.Empty; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[Health] Clipboard copy failed: " + ex.Message)); } } } } internal static class PerformanceSampler { private const float Smoothing = 0.12f; private static float _smoothedFps; private static float _minFps = float.MaxValue; private static float _maxFps; private static float _resetTimer; private const float MinMaxResetSeconds = 3f; public static float SmoothedFps => _smoothedFps; public static float MinFps { get { if (_minFps != float.MaxValue) { return _minFps; } return _smoothedFps; } } public static float MaxFps => _maxFps; public static float MemoryMb => (float)GC.GetTotalMemory(forceFullCollection: false) / 1048576f; public static void Tick() { float num = 1f / Mathf.Max(0.0001f, Time.unscaledDeltaTime); if (_smoothedFps <= 0f) { _smoothedFps = num; } else { _smoothedFps = Mathf.Lerp(_smoothedFps, num, 0.12f); } _minFps = Mathf.Min(_minFps, num); _maxFps = Mathf.Max(_maxFps, num); _resetTimer += Time.unscaledDeltaTime; if (_resetTimer >= 3f) { _resetTimer = 0f; _minFps = num; _maxFps = num; } } public static Color FpsColor(float fps) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (fps >= 55f) { return new Color(0.45f, 0.95f, 0.55f); } if (fps >= 40f) { return new Color(0.95f, 0.85f, 0.35f); } return new Color(0.95f, 0.45f, 0.45f); } } } namespace HavenDevTools.Services { public class BundleInspector { public BundleInspector() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[BundleInspector] Initialized"); } } public List GetAllSections() { List list = new List(); if (!Plugin.HasSMUT) { return list; } try { Assembly sMUTAssembly = GetSMUTAssembly(); if (sMUTAssembly == null) { return list; } Type type = sMUTAssembly.GetType("SunHavenMuseumUtilityTracker.Data.MuseumContent"); if (type == null) { return list; } MethodInfo method = type.GetMethod("GetAllSections", BindingFlags.Static | BindingFlags.Public); if (method == null) { return list; } if (!(method.Invoke(null, null) is IList list2)) { return list; } foreach (object item2 in list2) { MuseumSectionInfo museumSectionInfo = new MuseumSectionInfo { Name = (item2.GetType().GetProperty("Name")?.GetValue(item2)?.ToString() ?? "Unknown"), Bundles = new List() }; PropertyInfo property = item2.GetType().GetProperty("Bundles"); if (property != null && property.GetValue(item2) is IList list3) { foreach (object item3 in list3) { MuseumBundleInfo museumBundleInfo = new MuseumBundleInfo { Name = (item3.GetType().GetProperty("Name")?.GetValue(item3)?.ToString() ?? "Unknown"), Items = new List() }; string text = item3.GetType().GetProperty("Id")?.GetValue(item3)?.ToString() ?? ""; item2.GetType().GetProperty("Id")?.GetValue(item2)?.ToString(); List<(int, string)> list4 = TryGetResolvedAquariumItems(sMUTAssembly, text); if (list4 != null && list4.Count > 0) { foreach (var (num, text2) in list4) { museumBundleInfo.Items.Add(new MuseumItemInfo { Id = $"{text}_fish_{num}", Name = (text2 ?? $"Item {num}"), GameItemId = num, Quantity = 1 }); } } else { PropertyInfo property2 = item3.GetType().GetProperty("Items"); if (property2 != null && property2.GetValue(item3) is IList list5) { foreach (object item4 in list5) { string name = item4.GetType().GetProperty("Name")?.GetValue(item4)?.ToString() ?? "Unknown"; MuseumItemInfo item = new MuseumItemInfo { Id = (item4.GetType().GetProperty("Id")?.GetValue(item4)?.ToString() ?? ""), Name = name, GameItemId = Convert.ToInt32(item4.GetType().GetProperty("GameItemId")?.GetValue(item4) ?? ((object)0)), Quantity = MuseumItemInfo.ParseQuantityFromName(name) }; museumBundleInfo.Items.Add(item); } } } museumSectionInfo.Bundles.Add(museumBundleInfo); } } list.Add(museumSectionInfo); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[BundleInspector] Error getting sections: " + ex.Message)); } } return list; } public DonationStats GetDonationStats() { DonationStats donationStats = new DonationStats(); if (!Plugin.HasSMUT) { return donationStats; } try { Assembly sMUTAssembly = GetSMUTAssembly(); if (sMUTAssembly == null) { return donationStats; } Type type = sMUTAssembly.GetType("SunHavenMuseumUtilityTracker.Plugin"); if (type == null) { return donationStats; } MethodInfo method = type.GetMethod("GetDonationManager", BindingFlags.Static | BindingFlags.Public); if (method == null) { return donationStats; } object obj = method.Invoke(null, null); if (obj == null) { return donationStats; } PropertyInfo property = obj.GetType().GetProperty("IsLoaded"); if (property != null && !(bool)property.GetValue(obj)) { return donationStats; } MethodInfo method2 = obj.GetType().GetMethod("GetOverallStats"); if (method2 != null) { object obj2 = method2.Invoke(obj, null); if (obj2 != null) { FieldInfo fieldInfo = obj2.GetType().GetField("donated") ?? obj2.GetType().GetField("Item1"); FieldInfo fieldInfo2 = obj2.GetType().GetField("total") ?? obj2.GetType().GetField("Item2"); if (fieldInfo != null) { donationStats.TotalDonated = Convert.ToInt32(fieldInfo.GetValue(obj2)); } if (fieldInfo2 != null) { donationStats.TotalItems = Convert.ToInt32(fieldInfo2.GetValue(obj2)); } } } MethodInfo method3 = obj.GetType().GetMethod("GetOverallCompletionPercent"); if (method3 != null) { donationStats.CompletionPercent = (float)method3.Invoke(obj, null); } PropertyInfo property2 = obj.GetType().GetProperty("CurrentCharacter"); if (property2 != null) { donationStats.CharacterName = property2.GetValue(obj)?.ToString() ?? "Unknown"; } donationStats.IsLoaded = true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[BundleInspector] Error getting donation stats: " + ex.Message)); } } return donationStats; } public bool HasDonated(string itemId) { if (!Plugin.HasSMUT) { return false; } try { Assembly sMUTAssembly = GetSMUTAssembly(); if (sMUTAssembly == null) { return false; } object obj = (sMUTAssembly.GetType("SunHavenMuseumUtilityTracker.Plugin")?.GetMethod("GetDonationManager", BindingFlags.Static | BindingFlags.Public))?.Invoke(null, null); if (obj == null) { return false; } MethodInfo method = obj.GetType().GetMethod("HasDonated", new Type[1] { typeof(string) }); if (method != null) { return (bool)method.Invoke(obj, new object[1] { itemId }); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[BundleInspector] Error checking donation: " + ex.Message)); } } return false; } private Assembly GetSMUTAssembly() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name == "SunHavenMuseumUtilityTracker") { return assembly; } } return null; } private static List<(int GameItemId, string Name)> TryGetResolvedAquariumItems(Assembly smutAssembly, string bundleId) { if (smutAssembly == null || string.IsNullOrEmpty(bundleId)) { return null; } try { if ((smutAssembly.GetType("SunHavenMuseumUtilityTracker.Data.MuseumContent")?.GetMethod("GetResolvedAquariumItems", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null))?.Invoke(null, new object[1] { bundleId }) is IList { Count: >0 } list) { List<(int, string)> list2 = new List<(int, string)>(); foreach (object item in list) { Type type = item.GetType(); FieldInfo field = type.GetField("Item1", BindingFlags.Instance | BindingFlags.Public); FieldInfo field2 = type.GetField("Item2", BindingFlags.Instance | BindingFlags.Public); int num = ((field != null) ? Convert.ToInt32(field.GetValue(item)) : 0); string text = ((field2 != null) ? field2.GetValue(item)?.ToString() : null); list2.Add((num, text ?? $"Item {num}")); } return list2; } } catch { } return null; } } public class MuseumSectionInfo { public string Name { get; set; } public List Bundles { get; set; } } public class MuseumBundleInfo { public string Name { get; set; } public List Items { get; set; } } public class MuseumItemInfo { public string Id { get; set; } public string Name { get; set; } public int GameItemId { get; set; } public int Quantity { get; set; } = 1; public static int ParseQuantityFromName(string name) { if (string.IsNullOrEmpty(name)) { return 1; } Match match = Regex.Match(name, "\\(x([\\d,]+)\\)"); if (match.Success && int.TryParse(match.Groups[1].Value.Replace(",", ""), out var result)) { return result; } return 1; } } public class DonationStats { public bool IsLoaded { get; set; } public string CharacterName { get; set; } public int TotalDonated { get; set; } public int TotalItems { get; set; } public float CompletionPercent { get; set; } } public class CommandConsole { private string _input = ""; private readonly List _output = new List(); private readonly List _history = new List(); private int _historyIndex = -1; private Vector2 _outputScroll; private const int MaxOutputLines = 100; private const string ConsoleInputControl = "ConsoleInput"; public void Draw(GUIStyle boxStyle, GUIStyle buttonStyle, GUIStyle labelStyle, GUIStyle textFieldStyle, bool requestFocus = false, float outputScrollHeight = -1f) { //IL_003e: 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_0057: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label(ModLocalization.T("devtools.console.output"), labelStyle, Array.Empty()); float num = ((outputScrollHeight > 0f) ? outputScrollHeight : ((float)Mathf.Min(120, 20 + _output.Count * 18))); _outputScroll = GUILayout.BeginScrollView(_outputScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num) }); foreach (string item in _output) { GUILayout.Label(item, labelStyle, Array.Empty()); } GUILayout.EndScrollView(); GUILayout.Space(5f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(">", labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(12f) }); GUI.SetNextControlName("ConsoleInput"); _input = GUILayout.TextField(_input, textFieldStyle, Array.Empty()); bool executeClicked = GUILayout.Button(ModLocalization.T("devtools.console.execute"), buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.EndHorizontal(); if (requestFocus) { GUI.FocusControl("ConsoleInput"); } HandleInputKeyboard(executeClicked); GUILayout.Label(ModLocalization.T("devtools.console.commands"), labelStyle, Array.Empty()); } private void HandleInputKeyboard(bool executeClicked) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Invalid comparison between Unknown and I4 //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Invalid comparison between Unknown and I4 //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Invalid comparison between Unknown and I4 Event current = Event.current; if (current == null || GUI.GetNameOfFocusedControl() != "ConsoleInput") { return; } if ((int)current.type == 4) { if ((int)current.keyCode == 13 || (int)current.keyCode == 271) { SubmitInput(); current.Use(); return; } if ((int)current.keyCode == 273 && _history.Count > 0) { _historyIndex = Mathf.Clamp(_historyIndex - 1, 0, _history.Count - 1); _input = _history[_historyIndex]; current.Use(); return; } if ((int)current.keyCode == 274 && _history.Count > 0) { _historyIndex = Mathf.Clamp(_historyIndex + 1, 0, _history.Count); _input = ((_historyIndex >= _history.Count) ? string.Empty : _history[_historyIndex]); current.Use(); } } if (executeClicked) { SubmitInput(); } } private void SubmitInput() { Execute(_input); _input = string.Empty; GUI.FocusControl("ConsoleInput"); } public void Execute(string command) { if (string.IsNullOrWhiteSpace(command)) { return; } command = command.Trim(); _history.Add(command); if (_history.Count > 50) { _history.RemoveAt(0); } _historyIndex = _history.Count; string[] array = command.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return; } string text = array[0].ToLowerInvariant(); try { switch (text) { case "spawn": { if (array.Length >= 2 && int.TryParse(array[1], out var result4)) { int result5; int num = ((array.Length < 3 || !int.TryParse(array[2], out result5)) ? 1 : result5); ItemInspector itemInspector = Plugin.GetItemInspector(); if (itemInspector != null && itemInspector.SpawnItem(result4, num)) { AddOutput($"Spawned item {result4} x{num}"); } else { AddOutput($"Failed to spawn {result4}"); } } else { AddOutput("Usage: spawn [qty]"); } break; } case "teleport": case "tp": if (array.Length >= 2) { string text2 = array[1]; SceneManager.LoadScene(text2); AddOutput("Loading scene: " + text2); } else { AddOutput("Usage: tp "); } break; case "time.set": { if (array.Length >= 2 && float.TryParse(array[1], out var result3)) { if (TrySetTime(result3)) { AddOutput($"Set time to {result3}"); } else { AddOutput("Could not set time"); } } else { AddOutput("Usage: time.set "); } break; } case "reload.config": ConfigReloader.ReloadHavenDevToolsConfig(); AddOutput("Config reloaded"); break; case "player.stat": if (array.Length >= 3) { if (TrySetPlayerStat(array[1], array[2])) { AddOutput("Set " + array[1] + " = " + array[2]); } else { AddOutput("Failed to set " + array[1]); } } else { AddOutput("Usage: player.stat "); } break; case "relationship.set": case "rel.set": { if (array.Length >= 3 && int.TryParse(array[2], out var result2)) { if (NpcRelationshipEditor.SetHearts(array[1], result2)) { AddOutput($"Set {array[1]} hearts to {result2}"); } else { AddOutput("NPC not found: " + array[1]); } } else { AddOutput("Usage: relationship.set <0-100>"); } break; } case "relationship.marry": case "rel.marry": if (array.Length >= 2) { if (NpcRelationshipEditor.MarryNpc(array[1])) { AddOutput("Married " + array[1]); } else { AddOutput("Could not marry " + array[1]); } } else { AddOutput("Usage: relationship.marry "); } break; case "rel.divorce": case "relationship.divorce": if (array.Length >= 2) { if (NpcRelationshipEditor.DivorceNpc(array[1])) { AddOutput("Divorced " + array[1]); } else if (NpcRelationshipEditor.DivorcePrimarySpouse()) { AddOutput("Divorced primary spouse"); } else { AddOutput("No spouse to divorce"); } } else if (NpcRelationshipEditor.DivorcePrimarySpouse()) { AddOutput("Divorced primary spouse"); } else { AddOutput("Usage: relationship.divorce [npc]"); } break; case "relationship.cycle": case "rel.cycle": { if (array.Length >= 3 && int.TryParse(array[2], out var result)) { if (NpcRelationshipEditor.SkipToCycle(array[1], result)) { AddOutput($"Set {array[1]} to cycle {result}"); } else { AddOutput("NPC not found: " + array[1]); } } else { AddOutput("Usage: relationship.cycle <0-15>"); } break; } default: AddOutput("Unknown command: " + text + ". Try: spawn, tp, time.set, relationship.set, relationship.marry"); break; } } catch (Exception ex) { AddOutput("Error: " + ex.Message); } } private void AddOutput(string line) { _output.Add(line); if (_output.Count > 100) { _output.RemoveAt(0); } } private static bool TrySetTime(float hour) { try { Type type = ReflectionHelper.FindType("DayCycle", "Wish"); if (type == null) { return false; } Object val = Object.FindObjectOfType(type); if (val == (Object)null) { return false; } Type type2 = ((object)val).GetType(); PropertyInfo propertyInfo = type2.GetProperty("CurrentTime") ?? type2.GetProperty("currentTime"); if (propertyInfo != null && propertyInfo.CanWrite) { propertyInfo.SetValue(val, hour); return true; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[CommandConsole] TrySetTime: " + ex.Message)); } } return false; } private static bool TrySetPlayerStat(string statName, string valueStr) { try { if ((Object)(object)Player.Instance == (Object)null) { return false; } PropertyInfo property = ((object)Player.Instance).GetType().GetProperty(statName, BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public); if (property == null || !property.CanWrite) { return false; } object value = valueStr; float result2; if (property.PropertyType == typeof(int) && int.TryParse(valueStr, out var result)) { value = result; } else if (property.PropertyType == typeof(float) && float.TryParse(valueStr, out result2)) { value = result2; } else if (property.PropertyType == typeof(bool)) { value = valueStr.Equals("true", StringComparison.OrdinalIgnoreCase) || valueStr == "1"; } property.SetValue(Player.Instance, Convert.ChangeType(value, property.PropertyType)); return true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[CommandConsole] TrySetPlayerStat: " + ex.Message)); } } return false; } } public static class ConfigReloader { public static void ReloadHavenDevToolsConfig() { //IL_0022: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) try { if (Plugin.ConfigFile != null) { Plugin.ConfigFile.Reload(); ModConfig.Initialize(Plugin.ConfigFile); Plugin.StaticToggleKey = ModConfig.ToggleKey.Value; Plugin.StaticOverlayToggleKey = ModConfig.OverlayToggleKey.Value; ModConfig.SyncTheVaultFullVaultInspectorToPlugin(); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[ConfigReloader] HavenDevTools config reloaded"); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("[ConfigReloader] Error: " + ex.Message)); } } } } public class CurrencyTracker { private const float SummaryRefreshIntervalSeconds = 0.5f; public static readonly Dictionary CurrencyItemIds = new Dictionary { { "Spring Token", 18020 }, { "Summer Token", 18021 }, { "Fall Token", 18023 }, { "Winter Token", 18022 }, { "Copper Key", 1251 }, { "Iron Key", 1252 }, { "Adamant Key", 1253 }, { "Mithril Key", 1254 }, { "Sunite Key", 1255 }, { "Glorite Key", 1256 }, { "King's Lost Mine Key", 1257 }, { "Community Token", 18013 }, { "Doubloon", 60014 }, { "Black Bottle Cap", 60013 }, { "Red Carnival Ticket", 18012 }, { "Candy Corn Pieces", 18016 }, { "Mana Shard", 18015 } }; private PropertyInfo _gameSaveCoinsProperty; private PropertyInfo _singletonInstanceProperty; private PropertyInfo _currentSaveProperty; private FieldInfo _currentSaveCoinsField; private PropertyInfo _playerOrbsProperty; private PropertyInfo _playerTicketsProperty; private MethodInfo _inventoryGetAmountMethod; private Type _vaultPluginType; private MethodInfo _vaultGetVaultManagerMethod; private MethodInfo _vaultGetAllNonZeroCurrenciesMethod; private bool _goldFallbackResolved; private bool _playerPropertiesResolved; private bool _vaultReflectionResolved; private CurrencySummary _cachedSummary; private float _lastSummaryRefreshTime = float.NegativeInfinity; public CurrencyTracker() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[CurrencyTracker] Initialized"); } } public int GetInventoryAmount(int itemId) { try { Player instance = Player.Instance; if ((Object)(object)((instance != null) ? instance.Inventory : null) == (Object)null) { return 0; } Inventory inventory = Player.Instance.Inventory; if (_inventoryGetAmountMethod == null) { _inventoryGetAmountMethod = AccessTools.Method(((object)inventory).GetType(), "GetAmount", new Type[1] { typeof(int) }, (Type[])null); } if (_inventoryGetAmountMethod != null) { return (int)_inventoryGetAmountMethod.Invoke(inventory, new object[1] { itemId }); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[CurrencyTracker] Error getting inventory amount: " + ex.Message)); } } return 0; } public int GetVaultAmount(string currencyId) { if (!Plugin.HasTheVault) { return 0; } try { if (!EnsureVaultReflection()) { return 0; } object obj = _vaultGetVaultManagerMethod.Invoke(null, null); if (obj == null) { return 0; } MethodInfo method = obj.GetType().GetMethod("GetCurrency", new Type[1] { typeof(string) }); if (method != null) { return (int)method.Invoke(obj, new object[1] { currencyId }); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[CurrencyTracker] Error getting vault amount: " + ex.Message)); } } return 0; } public Dictionary GetAllVaultCurrencies() { Dictionary result = new Dictionary(); if (!Plugin.HasTheVault) { return result; } try { if (!EnsureVaultReflection()) { return result; } object obj = _vaultGetVaultManagerMethod.Invoke(null, null); if (obj == null) { return result; } if (_vaultGetAllNonZeroCurrenciesMethod != null && _vaultGetAllNonZeroCurrenciesMethod.Invoke(obj, null) is Dictionary result2) { return result2; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[CurrencyTracker] Error getting all vault currencies: " + ex.Message)); } } return result; } public int GetGold() { try { EnsureGoldReflection(); if (_gameSaveCoinsProperty != null) { return (int)_gameSaveCoinsProperty.GetValue(null); } if (_singletonInstanceProperty != null && _currentSaveProperty != null && _currentSaveCoinsField != null) { object value = _singletonInstanceProperty.GetValue(null); if (value != null) { object value2 = _currentSaveProperty.GetValue(value); if (value2 != null) { return Convert.ToInt32(_currentSaveCoinsField.GetValue(value2)); } } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[CurrencyTracker] Error getting gold: " + ex.Message)); } } return 0; } public int GetOrbs() { try { if ((Object)(object)Player.Instance == (Object)null) { return 0; } EnsurePlayerProperties(); if (_playerOrbsProperty != null) { return (int)_playerOrbsProperty.GetValue(Player.Instance); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[CurrencyTracker] Error getting orbs: " + ex.Message)); } } return 0; } public int GetTickets() { try { if ((Object)(object)Player.Instance == (Object)null) { return 0; } EnsurePlayerProperties(); if (_playerTicketsProperty != null) { return (int)_playerTicketsProperty.GetValue(Player.Instance); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[CurrencyTracker] Error getting tickets: " + ex.Message)); } } return 0; } public CurrencySummary GetSummary(bool forceRefresh = false) { if (!forceRefresh && _cachedSummary != null && Time.unscaledTime - _lastSummaryRefreshTime < 0.5f) { return _cachedSummary; } _cachedSummary = BuildSummary(); _lastSummaryRefreshTime = Time.unscaledTime; return _cachedSummary; } private CurrencySummary BuildSummary() { CurrencySummary currencySummary = new CurrencySummary { Gold = GetGold(), Orbs = GetOrbs(), Tickets = GetTickets(), InventoryCurrencies = new Dictionary(), VaultCurrencies = new Dictionary() }; foreach (KeyValuePair currencyItemId in CurrencyItemIds) { int inventoryAmount = GetInventoryAmount(currencyItemId.Value); if (inventoryAmount > 0) { currencySummary.InventoryCurrencies[currencyItemId.Key] = inventoryAmount; } } if (Plugin.HasTheVault) { currencySummary.VaultCurrencies = GetAllVaultCurrencies(); } return currencySummary; } private void EnsureGoldReflection() { if (_gameSaveCoinsProperty != null || _goldFallbackResolved) { return; } Type type = AccessTools.TypeByName("Wish.GameSave"); if (type != null) { _gameSaveCoinsProperty = type.GetProperty("Coins", BindingFlags.Static | BindingFlags.Public); } if (_gameSaveCoinsProperty != null) { return; } Type type2 = AccessTools.TypeByName("SingletonBehaviour`1"); if (type2 != null && type != null) { Type type3 = type2.MakeGenericType(type); _singletonInstanceProperty = type3.GetProperty("Instance", BindingFlags.Static | BindingFlags.Public); if (_singletonInstanceProperty != null) { object value = _singletonInstanceProperty.GetValue(null); if (value != null) { _currentSaveProperty = value.GetType().GetProperty("CurrentSave"); if (_currentSaveProperty != null) { object value2 = _currentSaveProperty.GetValue(value); if (value2 != null) { _currentSaveCoinsField = value2.GetType().GetField("coins", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } } } } } _goldFallbackResolved = true; } private void EnsurePlayerProperties() { if (!_playerPropertiesResolved && !((Object)(object)Player.Instance == (Object)null)) { Type type = ((object)Player.Instance).GetType(); _playerOrbsProperty = type.GetProperty("Orbs", BindingFlags.Instance | BindingFlags.Public); _playerTicketsProperty = type.GetProperty("Tickets", BindingFlags.Instance | BindingFlags.Public); _playerPropertiesResolved = true; } } private bool EnsureVaultReflection() { if (_vaultReflectionResolved) { return _vaultGetVaultManagerMethod != null; } _vaultReflectionResolved = true; _vaultPluginType = ReflectionHelper.FindModPlugin("TheVault"); if (_vaultPluginType == null) { return false; } _vaultGetVaultManagerMethod = _vaultPluginType.GetMethod("GetVaultManager", BindingFlags.Static | BindingFlags.Public); if (_vaultGetVaultManagerMethod == null) { return false; } object obj = _vaultGetVaultManagerMethod.Invoke(null, null); if (obj == null) { return false; } _vaultGetAllNonZeroCurrenciesMethod = obj.GetType().GetMethod("GetAllNonZeroCurrencies"); return _vaultGetAllNonZeroCurrenciesMethod != null; } } public class CurrencySummary { public int Gold { get; set; } public int Orbs { get; set; } public int Tickets { get; set; } public Dictionary InventoryCurrencies { get; set; } public Dictionary VaultCurrencies { get; set; } } public class ItemInspector { private static MethodInfo _cachedAddItemMethod; private static int _addItemArgCount; public ItemInspector() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[ItemInspector] Initialized"); } } public bool SpawnItem(int itemId, int amount = 1) { try { if ((Object)(object)Player.Instance == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[ItemInspector] Player.Instance is null"); } return false; } Inventory inventory = Player.Instance.Inventory; if ((Object)(object)inventory == (Object)null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[ItemInspector] Player.Inventory is null"); } return false; } MethodInfo addItemMethod = GetAddItemMethod(inventory); if (addItemMethod == null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[ItemInspector] AddItem method not found"); } return false; } object obj = ((_addItemArgCount != 3) ? addItemMethod.Invoke(inventory, new object[2] { itemId, amount }) : addItemMethod.Invoke(inventory, new object[3] { itemId, amount, true })); Type returnType = addItemMethod.ReturnType; if (returnType == typeof(bool)) { if (obj is bool && !(bool)obj) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)$"[ItemInspector] AddItem rejected item {itemId} x{amount}"); } return false; } } else if (returnType != typeof(void) && obj == null) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)"[ItemInspector] AddItem returned null"); } return false; } return true; } catch (Exception ex) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogError((object)("[ItemInspector] Spawn error: " + ex.Message)); } return false; } } private static MethodInfo GetAddItemMethod(object inventory) { if (_cachedAddItemMethod != null) { return _cachedAddItemMethod; } try { Type type = inventory.GetType(); _cachedAddItemMethod = AccessTools.Method(type, "AddItem", new Type[3] { typeof(int), typeof(int), typeof(bool) }, (Type[])null); if (_cachedAddItemMethod != null) { _addItemArgCount = 3; return _cachedAddItemMethod; } _cachedAddItemMethod = AccessTools.Method(type, "AddItem", new Type[2] { typeof(int), typeof(int) }, (Type[])null); if (_cachedAddItemMethod != null) { _addItemArgCount = 2; return _cachedAddItemMethod; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[ItemInspector] GetAddItemMethod: " + ex.Message)); } } return null; } public (int id, string name)? GetHeldItem() { try { Player instance = Player.Instance; if ((Object)(object)((instance != null) ? instance.Inventory : null) == (Object)null) { return null; } Inventory inventory = Player.Instance.Inventory; Type type = ((object)inventory).GetType(); int num = 0; PropertyInfo propertyInfo = type.GetProperty("SelectedSlot") ?? type.GetProperty("selectedSlot"); if (propertyInfo != null) { num = (int)propertyInfo.GetValue(inventory); } else { FieldInfo field = type.GetField("selectedSlot", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (!(field != null)) { return null; } num = (int)field.GetValue(inventory); } MethodInfo methodInfo = AccessTools.Method(type, "GetItem", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo == null) { return null; } object obj = methodInfo.Invoke(inventory, new object[1] { num }); if (obj == null) { return null; } PropertyInfo propertyInfo2 = obj.GetType().GetProperty("id") ?? obj.GetType().GetProperty("ID"); if (propertyInfo2 == null) { return null; } int num2 = (int)propertyInfo2.GetValue(obj); string item = ItemSearch.GetItemName(num2) ?? $"Item {num2}"; return (num2, item); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[ItemInspector] GetHeldItem: " + ex.Message)); } return null; } } } internal static class ModHealthResolver { public static string ResolveDisplayName(string pluginGuid) { if (string.IsNullOrEmpty(pluginGuid)) { return null; } 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; } public static string ResolveInstalledVersion(string pluginGuid) { if (string.IsNullOrEmpty(pluginGuid)) { return null; } 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 static bool IsPluginLoaded(string pluginGuid) { try { return !string.IsNullOrEmpty(pluginGuid) && Chainloader.PluginInfos != null && Chainloader.PluginInfos.ContainsKey(pluginGuid); } catch { return false; } } } internal static class NpcDictionaryHelper { internal static string ResolveNpcKey(IReadOnlyDictionary npcs, string input) { if (npcs == null || string.IsNullOrWhiteSpace(input)) { return null; } foreach (KeyValuePair npc in npcs) { if (npc.Key.Equals(input, StringComparison.OrdinalIgnoreCase)) { return npc.Key; } } return null; } } public static class NpcRelationshipEditor { public sealed class NpcRelationshipRow { public string Key; public string DisplayName; public float Hearts; public bool Romanceable; public bool IsDating; public bool IsMarriedTo; public bool IsPrimarySpouse; } public static bool IsAvailable { get { if (SingletonBehaviour.Instance?._npcs != null) { GameSave instance = SingletonBehaviour.Instance; object obj; if (instance == null) { obj = null; } else { GameSaveData currentSave = instance.CurrentSave; obj = ((currentSave != null) ? currentSave.characterData : null); } return obj != null; } return false; } } public static IReadOnlyList GetRows(bool romanceOnly, string nameFilter) { List list = new List(); if (!IsAvailable) { return list; } GameSave instance = SingletonBehaviour.Instance; Dictionary relationships = instance.CurrentSave.characterData.Relationships; string text = instance.GetProgressStringCharacter("MarriedWith") ?? string.Empty; string value = nameFilter?.Trim() ?? string.Empty; foreach (NPCAI item in SingletonBehaviour.Instance._npcs.Values.Where((NPCAI n) => (Object)(object)n != (Object)null).OrderBy((NPCAI n) => n.OriginalName, StringComparer.OrdinalIgnoreCase)) { if (!romanceOnly || item.Romanceable) { string originalName = item.OriginalName; if (string.IsNullOrEmpty(value) || originalName.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0 || (item.LocalizedActualNPCName ?? string.Empty).IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { relationships.TryGetValue(originalName, out var value2); list.Add(new NpcRelationshipRow { Key = originalName, DisplayName = (item.LocalizedActualNPCName ?? originalName), Hearts = value2, Romanceable = item.Romanceable, IsDating = instance.GetProgressBoolCharacter("Dating" + originalName), IsMarriedTo = instance.GetProgressBoolCharacter("MarriedTo" + originalName), IsPrimarySpouse = (!string.IsNullOrEmpty(text) && text.Equals(originalName, StringComparison.Ordinal)) }); } } } return list; } public static string ResolveNpcKey(string input) { return NpcDictionaryHelper.ResolveNpcKey(SingletonBehaviour.Instance?._npcs, input); } public static bool TryGetNpc(string key, out NPCAI npc) { npc = null; if (string.IsNullOrWhiteSpace(key)) { return false; } string text = ResolveNpcKey(key); if (text == null) { return false; } return SingletonBehaviour.Instance._npcs.TryGetValue(text, out npc); } public static bool SetHearts(string npcKey, int amount) { if (!TryGetNpc(npcKey, out NPCAI _)) { return false; } string key = ResolveNpcKey(npcKey); amount = Math.Max(0, Math.Min(100, amount)); SingletonBehaviour.Instance.CurrentSave.characterData.Relationships[key] = amount; return true; } public static bool AdjustHearts(string npcKey, int delta) { if (!TryGetNpc(npcKey, out NPCAI _)) { return false; } string key = ResolveNpcKey(npcKey); Dictionary relationships = SingletonBehaviour.Instance.CurrentSave.characterData.Relationships; relationships.TryGetValue(key, out var value); relationships[key] = Math.Max(0f, Math.Min(100f, value + (float)delta)); return true; } public static bool DateNpc(string npcKey) { if (!TryGetNpc(npcKey, out NPCAI npc)) { return false; } npc.DatePlayer(); return true; } public static bool SetPlatonic(string npcKey) { if (!TryGetNpc(npcKey, out NPCAI npc)) { return false; } npc.PlatonicPlayer(); return true; } public static bool MarryNpcPolygamy(string npcKey) { if (!UltraPolygamyHelper.IsAvailable) { return false; } if (!TryGetNpc(npcKey, out NPCAI npc) || !npc.Romanceable) { return false; } SetHearts(npc.OriginalName, 100); npc.MarryPlayer(); return true; } public static int MarryMultiple(IReadOnlyList npcKeys) { if (!UltraPolygamyHelper.IsAvailable || npcKeys == null || npcKeys.Count == 0) { return 0; } int num = 0; foreach (string npcKey in npcKeys) { if (TryGetNpc(npcKey, out NPCAI npc) && npc.Romanceable && !SingletonBehaviour.Instance.GetProgressBoolCharacter("MarriedTo" + npc.OriginalName) && MarryNpcPolygamy(npc.OriginalName)) { num++; } } return num; } public static bool MarryNpc(string npcKey) { if (!TryGetNpc(npcKey, out NPCAI npc)) { return false; } GameSave instance = SingletonBehaviour.Instance; string progressStringCharacter = instance.GetProgressStringCharacter("MarriedWith"); if (!string.IsNullOrWhiteSpace(progressStringCharacter)) { instance.SetProgressStringCharacter("MarriedWith", string.Empty); instance.SetProgressBoolCharacter("Married", false); instance.SetProgressBoolCharacter("MarriedTo" + progressStringCharacter, false); GameSave.CurrentCharacter.Relationships[progressStringCharacter] = 40f; NPCAI realNPC = SingletonBehaviour.Instance.GetRealNPC(progressStringCharacter); if (realNPC != null) { realNPC.GenerateCycle(false); } } SetHearts(npc.OriginalName, 100); npc.MarryPlayer(); return true; } public static bool DivorcePrimarySpouse() { GameSave instance = SingletonBehaviour.Instance; string progressStringCharacter = instance.GetProgressStringCharacter("MarriedWith"); if (string.IsNullOrWhiteSpace(progressStringCharacter)) { return false; } instance.SetProgressStringCharacter("MarriedWith", string.Empty); instance.SetProgressBoolCharacter("Married", false); instance.SetProgressBoolCharacter("MarriedTo" + progressStringCharacter, false); instance.SetProgressBoolWorld(progressStringCharacter + "MarriedWalkPath", false, true); NPCAI realNPC = SingletonBehaviour.Instance.GetRealNPC(progressStringCharacter); if (realNPC != null) { realNPC.GeneratePath(); } if ((Object)(object)realNPC != (Object)null) { SingletonBehaviour.Instance.StartNPCPath(realNPC); } GameSave.CurrentCharacter.Relationships[progressStringCharacter] = 40f; if (realNPC != null) { realNPC.GenerateCycle(false); } return true; } public static bool DivorceNpc(string npcKey) { if (!TryGetNpc(npcKey, out NPCAI npc)) { return false; } string originalName = npc.OriginalName; GameSave instance = SingletonBehaviour.Instance; bool flag = string.Equals(instance.GetProgressStringCharacter("MarriedWith"), originalName, StringComparison.Ordinal); instance.SetProgressBoolCharacter("MarriedTo" + originalName, false); instance.SetProgressBoolCharacter("Dating" + originalName, false); instance.SetProgressBoolWorld(originalName + "MarriedWalkPath", false, true); int count; if (flag) { instance.SetProgressStringCharacter("MarriedWith", string.Empty); instance.SetProgressBoolCharacter("Married", false); } else if (!AnyMarriedRomanceables(out count)) { instance.SetProgressBoolCharacter("Married", false); instance.SetProgressStringCharacter("MarriedWith", string.Empty); } GameSave.CurrentCharacter.Relationships[originalName] = 40f; npc.GenerateCycle(false); npc.GeneratePath(); SingletonBehaviour.Instance.StartNPCPath(npc); return true; } public static bool SkipToCycle(string npcKey, int cycle) { if (!TryGetNpc(npcKey, out NPCAI npc)) { return false; } string text = ResolveNpcKey(npcKey); cycle = Math.Max(0, Math.Min(15, cycle)); GameSave instance = SingletonBehaviour.Instance; for (int i = 0; i < 16; i++) { instance.SetProgressBoolCharacter(text + " Cycle " + i, i < cycle); } npc.GenerateCycle(true); return true; } public static void ResetAllHearts() { if (IsAvailable) { SingletonBehaviour.Instance.CurrentSave.characterData.relationships = new Dictionary(); } } public static bool SetPrimarySpouse(string npcKey) { if (!TryGetNpc(npcKey, out NPCAI _)) { return false; } string text = ResolveNpcKey(npcKey); GameSave instance = SingletonBehaviour.Instance; if (!instance.GetProgressBoolCharacter("MarriedTo" + text)) { return false; } string progressStringCharacter = instance.GetProgressStringCharacter("MarriedWith"); if (!string.IsNullOrWhiteSpace(progressStringCharacter) && !progressStringCharacter.Equals(text, StringComparison.Ordinal)) { instance.SetProgressBoolWorld(progressStringCharacter + "MarriedWalkPath", false, true); } instance.SetProgressStringCharacter("MarriedWith", text); instance.SetProgressBoolCharacter("Married", true); instance.SetProgressBoolWorld(text + "MarriedWalkPath", true, true); NPCAI realNPC = SingletonBehaviour.Instance.GetRealNPC(text); if (realNPC != null) { realNPC.GeneratePath(); } if ((Object)(object)realNPC != (Object)null) { SingletonBehaviour.Instance.StartNPCPath(realNPC); } return true; } private static bool AnyMarriedRomanceables(out int count) { count = 0; if (!IsAvailable) { return false; } GameSave instance = SingletonBehaviour.Instance; foreach (NPCAI value in SingletonBehaviour.Instance._npcs.Values) { if (!((Object)(object)value == (Object)null) && value.Romanceable && instance.GetProgressBoolCharacter("MarriedTo" + value.OriginalName)) { count++; } } return count > 0; } } public class RaceModifierTracker { public RaceModifierTracker() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[RaceModifierTracker] Initialized"); } } public string GetCurrentRace() { if (!Plugin.HasHavensBirthright) { return "Unknown (Haven's Birthright not loaded)"; } try { Assembly birthrightAssembly = GetBirthrightAssembly(); if (birthrightAssembly == null) { return "Unknown"; } Type type = birthrightAssembly.GetType("HavensBirthright.Plugin"); if (type == null) { return "Unknown"; } MethodInfo method = type.GetMethod("GetRacialBonusManager", BindingFlags.Static | BindingFlags.Public); if (method == null) { return "Unknown"; } object obj = method.Invoke(null, null); if (obj == null) { return "Unknown"; } MethodInfo method2 = obj.GetType().GetMethod("GetPlayerRace"); if (method2 != null) { object obj2 = method2.Invoke(obj, null); if (obj2 != null) { return obj2.ToString(); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[RaceModifierTracker] Error getting race: " + ex.Message)); } } return "Unknown"; } public List GetActiveRaceBonuses() { List list = new List(); if (!Plugin.HasHavensBirthright) { return list; } try { Assembly birthrightAssembly = GetBirthrightAssembly(); if (birthrightAssembly == null) { return list; } Type type = birthrightAssembly.GetType("HavensBirthright.Plugin"); if (type == null) { return list; } MethodInfo method = type.GetMethod("GetRacialBonusManager", BindingFlags.Static | BindingFlags.Public); if (method == null) { return list; } object obj = method.Invoke(null, null); if (obj == null) { return list; } MethodInfo method2 = obj.GetType().GetMethod("GetCurrentPlayerBonuses"); if (method2 == null) { return list; } if (!(method2.Invoke(obj, null) is IList list2)) { return list; } foreach (object item2 in list2) { Type type2 = item2.GetType(); PropertyInfo property = type2.GetProperty("Type"); PropertyInfo property2 = type2.GetProperty("Value"); PropertyInfo property3 = type2.GetProperty("IsPercentage"); PropertyInfo property4 = type2.GetProperty("Description"); RaceBonusInfo item = new RaceBonusInfo { Type = (property?.GetValue(item2)?.ToString() ?? "Unknown"), Value = ((property2 != null) ? Convert.ToSingle(property2.GetValue(item2)) : 0f), IsPercentage = (property3 != null && (bool)property3.GetValue(item2)), Description = (property4?.GetValue(item2)?.ToString() ?? "") }; list.Add(item); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[RaceModifierTracker] Error getting bonuses: " + ex.Message)); } } return list; } public List GetBonusesForRace(string raceName) { List list = new List(); if (!Plugin.HasHavensBirthright) { return list; } try { Assembly birthrightAssembly = GetBirthrightAssembly(); if (birthrightAssembly == null) { return list; } Type type = birthrightAssembly.GetType("HavensBirthright.Plugin"); if (type == null) { return list; } MethodInfo method = type.GetMethod("GetRacialBonusManager", BindingFlags.Static | BindingFlags.Public); if (method == null) { return list; } object obj = method.Invoke(null, null); if (obj == null) { return list; } Type type2 = birthrightAssembly.GetType("HavensBirthright.Race"); if (type2 == null) { return list; } object obj2; try { obj2 = Enum.Parse(type2, raceName, ignoreCase: true); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[RaceModifierTracker] Enum.Parse for race '" + raceName + "': " + ex.Message)); } return list; } MethodInfo method2 = obj.GetType().GetMethod("GetBonusesForRace"); if (method2 == null) { return list; } if (!(method2.Invoke(obj, new object[1] { obj2 }) is IList list2)) { return list; } foreach (object item2 in list2) { Type type3 = item2.GetType(); PropertyInfo property = type3.GetProperty("Type"); PropertyInfo property2 = type3.GetProperty("Value"); PropertyInfo property3 = type3.GetProperty("IsPercentage"); PropertyInfo property4 = type3.GetProperty("Description"); RaceBonusInfo item = new RaceBonusInfo { Type = (property?.GetValue(item2)?.ToString() ?? "Unknown"), Value = ((property2 != null) ? Convert.ToSingle(property2.GetValue(item2)) : 0f), IsPercentage = (property3 != null && (bool)property3.GetValue(item2)), Description = (property4?.GetValue(item2)?.ToString() ?? "") }; list.Add(item); } } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[RaceModifierTracker] Error getting bonuses for race: " + ex2.Message)); } } return list; } public List GetAllRaces() { List list = new List(); if (!Plugin.HasHavensBirthright) { return new List { "Human", "Elf", "Angel", "Demon", "Elemental", "FireElemental", "WaterElemental", "Amari", "AmariCat", "AmariDog", "AmariBird", "AmariAquatic", "AmariReptile", "Naga" }; } try { Assembly birthrightAssembly = GetBirthrightAssembly(); if (birthrightAssembly == null) { return list; } Type type = birthrightAssembly.GetType("HavensBirthright.Race"); if (type != null) { foreach (object value in Enum.GetValues(type)) { list.Add(value.ToString()); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[RaceModifierTracker] Error getting races: " + ex.Message)); } } return list; } private Assembly GetBirthrightAssembly() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (assembly.GetName().Name == "HavensBirthright") { return assembly; } } return null; } } public class RaceBonusInfo { public string Type { get; set; } public float Value { get; set; } public bool IsPercentage { get; set; } public string Description { get; set; } public string GetFormattedValue() { if (IsPercentage) { if (!(Value >= 0f)) { return $"{Value}%"; } return $"+{Value}%"; } if (!(Value >= 0f)) { return $"{Value}"; } return $"+{Value}"; } } public static class UltraPolygamyHelper { public const string PluginGuid = "vurawnica.sunhaven.polygamy"; public static bool IsPluginLoaded => Chainloader.PluginInfos.ContainsKey("vurawnica.sunhaven.polygamy"); public static bool IsAvailable { get { if (IsPluginLoaded) { return IsMarryPatchActive(); } return false; } } public static string StatusLocalizationKey { get { if (!IsPluginLoaded) { return "devtools.marriable.polyStatus.notLoaded"; } if (!IsMarryPatchActive()) { return "devtools.marriable.polyStatus.patchMissing"; } return "devtools.marriable.polyStatus.ready"; } } public static bool IsMarryPatchActive() { try { MethodInfo methodInfo = AccessTools.Method(typeof(NPCAI), "MarryPlayer", (Type[])null, (Type[])null); if (methodInfo == null) { return false; } Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo); if (patchInfo?.Prefixes == null || patchInfo.Prefixes.Count == 0) { return false; } return patchInfo.Prefixes.Any((Patch p) => p.owner.IndexOf("Polygamy", StringComparison.OrdinalIgnoreCase) >= 0 || p.owner.IndexOf("UltraPolygamy", StringComparison.OrdinalIgnoreCase) >= 0); } catch { return false; } } } } namespace HavenDevTools.Integrations { public static class AzraelsModsPanel { private static int _selectedSubTab; private static Vector2 _scrollPosition; private static Vector2 _bundleScrollPosition; private static Vector2 _raceScrollPosition; private static int _selectedSectionIndex; private static int _selectedBundleIndex; private static int _selectedRaceIndex; private static Type _cachedSenpaisChestPlugin; private static Type _cachedBirthdayReminderPlugin; private static Type _cachedSunhavenTodoPlugin; private static Type _cachedHavensAlmanacPlugin; private static Type _cachedCropOptimizerPlugin; private static MethodInfo _cachedCropGetHudSummary; private static Type _cachedFasterRacesPlugin; private static Type _cachedTrinketFortunePlugin; private static MethodInfo _cachedTrinketGetDevToolsSummary; private static Type _cachedGiftingAssistantPlugin; private static Type ResolveModPlugin(string assemblyName, ref Type cache, params string[] alternateAssemblyNames) { if (cache != null && string.Equals(cache.Assembly.GetName().Name, assemblyName, StringComparison.OrdinalIgnoreCase)) { return cache; } cache = ReflectionHelper.FindModPlugin(assemblyName); if (cache == null && alternateAssemblyNames != null) { foreach (string assemblyName2 in alternateAssemblyNames) { cache = ReflectionHelper.FindModPlugin(assemblyName2); if (cache != null) { break; } } } return cache; } public static void Draw(GUIStyle boxStyle, GUIStyle buttonStyle, GUIStyle labelStyle, GUIStyle sectionHeaderStyle) { //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) Plugin.RefreshInstalledMods(); List list = new List(); List list2 = new List(); int num = 0; if (Plugin.HasSenpaisChest) { list.Add(ModLocalization.T("azrael.tab.senpais_chest")); list2.Add(num); } num++; if (Plugin.HasTheVault) { list.Add(ModLocalization.T("azrael.tab.the_vault")); list2.Add(num); } num++; if (Plugin.HasSMUT) { list.Add(ModLocalization.T("azrael.tab.smut")); list2.Add(num); } num++; if (Plugin.HasHavensBirthright) { list.Add(ModLocalization.T("azrael.tab.birthright")); list2.Add(num); } num++; if (Plugin.HasBirthdayReminder) { list.Add(ModLocalization.T("azrael.tab.birthday")); list2.Add(num); } num++; if (Plugin.HasSunhavenTodo) { list.Add(ModLocalization.T("azrael.tab.todo")); list2.Add(num); } num++; if (Plugin.HasHavensAlmanac) { list.Add(ModLocalization.T("azrael.tab.almanac")); list2.Add(num); } num++; if (Plugin.HasTrinketFortune) { list.Add(ModLocalization.T("azrael.tab.trinket_fortune")); list2.Add(num); } num++; if (Plugin.HasCropOptimizer) { list.Add(ModLocalization.T("azrael.tab.crop_optimizer")); list2.Add(num); } num++; if (Plugin.HasFasterRaces) { list.Add(ModLocalization.T("azrael.tab.faster_races")); list2.Add(num); } num++; if (Plugin.HasHavensRespec) { list.Add(ModLocalization.T("azrael.tab.havens_respec")); list2.Add(num); } num++; if (Plugin.HasGiftingAssistant) { list.Add(ModLocalization.T("azrael.tab.gifting_assistant")); list2.Add(num); } if (list.Count == 0) { GUILayout.Label(ModLocalization.T("azrael.none_detected"), labelStyle, Array.Empty()); return; } int num2 = ((_selectedSubTab < list2.Count) ? list2[_selectedSubTab] : 0); _selectedSubTab = Mathf.Clamp(_selectedSubTab, 0, list.Count - 1); num2 = list2[_selectedSubTab]; _selectedSubTab = GUILayout.Toolbar(_selectedSubTab, list.ToArray(), buttonStyle, Array.Empty()); GUILayout.Space(8f); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, Array.Empty()); switch (num2) { case 0: DrawSenpaisChest(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 1: DrawTheVault(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 2: DrawSMUT(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 3: DrawBirthright(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 4: DrawBirthdayReminder(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 5: DrawTodo(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 6: DrawAlmanac(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 7: DrawTrinketFortune(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 8: DrawCropOptimizer(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 9: DrawFasterRaces(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 10: DrawHavensRespec(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; case 11: DrawGiftingAssistant(boxStyle, buttonStyle, labelStyle, sectionHeaderStyle); break; } GUILayout.EndScrollView(); } private static void DrawSenpaisChest(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.senpai.title"), sectionHeader, Array.Empty()); try { Type type = ResolveModPlugin("SenpaisChest", ref _cachedSenpaisChestPlugin, "SenpaiChest"); if (type == null) { GUILayout.Label(ModLocalization.T("azrael.suite.plugin_not_found", "Senpai's Chest"), label, Array.Empty()); GUILayout.EndVertical(); return; } object obj = ReflectionHelper.InvokeStaticMethod(type, "GetManager"); if (obj == null) { GUILayout.Label(ModLocalization.T("azrael.suite.awaiting_character"), label, Array.Empty()); GUILayout.EndVertical(); return; } MethodInfo method = obj.GetType().GetMethod("GetSaveData"); if (method != null) { object obj2 = method.Invoke(obj, null); if (obj2 != null) { PropertyInfo property = obj2.GetType().GetProperty("Chests"); if (property != null && property.GetValue(obj2) is IList list) { GUILayout.Label($"Smart Chests: {list.Count}", label, Array.Empty()); foreach (object item in list) { PropertyInfo? obj3 = item?.GetType().GetProperty("ChestName"); PropertyInfo propertyInfo = item?.GetType().GetProperty("ChestId"); PropertyInfo propertyInfo2 = item?.GetType().GetProperty("IsEnabled"); string text = obj3?.GetValue(item)?.ToString() ?? "?"; string text2 = propertyInfo?.GetValue(item)?.ToString() ?? "?"; bool flag = (bool)(propertyInfo2?.GetValue(item) ?? ((object)false)); GUILayout.Label(" " + (flag ? "[ON]" : "[OFF]") + " " + text + " (" + text2 + ")", label, Array.Empty()); } } } } MethodInfo method2 = obj.GetType().GetMethod("GetGroups"); if (method2 != null && method2.Invoke(obj, null) is IList { Count: >0 } list2) { GUILayout.Space(5f); GUILayout.Label($"Groups: {list2.Count}", label, Array.Empty()); foreach (object item2 in list2) { string text3 = (item2?.GetType().GetProperty("Name"))?.GetValue(item2)?.ToString() ?? "?"; GUILayout.Label(" - " + text3, label, Array.Empty()); } } GUILayout.Space(8f); if (GUILayout.Button(ModLocalization.T("azrael.senpai.trigger_scan"), button, Array.Empty())) { obj.GetType().GetMethod("ExecuteScan", new Type[2] { typeof(int), typeof(bool) })?.Invoke(obj, new object[2] { 999, false }); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[AzraelsMods] Triggered SenpaisChest manual scan"); } } } catch (Exception ex) { GUILayout.Label("Error: " + ex.Message, label, Array.Empty()); } GUILayout.EndVertical(); } private static void DrawTheVault(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.vault.title"), sectionHeader, Array.Empty()); CurrencyTracker currencyTracker = Plugin.GetCurrencyTracker(); if (currencyTracker == null) { GUILayout.Label(ModLocalization.T("devtools.currency.unavailable"), label, Array.Empty()); GUILayout.EndVertical(); return; } CurrencySummary summary = currencyTracker.GetSummary(); GUILayout.Label(ModLocalization.T("azrael.vault.currencies"), label, Array.Empty()); if (summary.VaultCurrencies.Count == 0) { GUILayout.Label(ModLocalization.T("azrael.vault.empty"), label, Array.Empty()); } else { foreach (KeyValuePair vaultCurrency in summary.VaultCurrencies) { GUILayout.Label($" {vaultCurrency.Key}: {vaultCurrency.Value}", label, Array.Empty()); } } GUILayout.Space(8f); if (ModConfig.TheVaultFullVaultInspector != null) { bool value = ModConfig.TheVaultFullVaultInspector.Value; bool flag = GUILayout.Toggle(value, ModLocalization.T("azrael.vault.inspector_toggle"), label, Array.Empty()); if (flag != value) { ModConfig.TheVaultFullVaultInspector.Value = flag; } GUILayout.Label(ModLocalization.T("azrael.vault.inspector_hint"), label, Array.Empty()); } GUILayout.EndVertical(); } private static void DrawSMUT(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0227: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.smut.title"), sectionHeader, Array.Empty()); BundleInspector bundleInspector = Plugin.GetBundleInspector(); if (bundleInspector == null) { GUILayout.Label(ModLocalization.T("devtools.museum.unavailable"), label, Array.Empty()); GUILayout.EndVertical(); return; } DonationStats donationStats = bundleInspector.GetDonationStats(); if (donationStats.IsLoaded) { GUILayout.Label(ModLocalization.T("devtools.museum.character", donationStats.CharacterName), label, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.museum.progress", donationStats.TotalDonated, donationStats.TotalItems, donationStats.CompletionPercent), label, Array.Empty()); } List allSections = bundleInspector.GetAllSections(); if (allSections.Count == 0) { GUILayout.EndVertical(); return; } string[] array = allSections.Select((MuseumSectionInfo s) => s.Name).ToArray(); if (_selectedSectionIndex >= allSections.Count) { _selectedSectionIndex = 0; } GUILayout.Label(ModLocalization.T("devtools.museum.section"), label, Array.Empty()); _selectedSectionIndex = GUILayout.SelectionGrid(_selectedSectionIndex, array, 3, button, Array.Empty()); MuseumSectionInfo museumSectionInfo = allSections[_selectedSectionIndex]; if (museumSectionInfo.Bundles.Count > 0) { string[] array2 = museumSectionInfo.Bundles.Select((MuseumBundleInfo b) => b.Name).ToArray(); if (_selectedBundleIndex >= museumSectionInfo.Bundles.Count) { _selectedBundleIndex = 0; } GUILayout.Label(ModLocalization.T("devtools.museum.bundle"), label, Array.Empty()); _selectedBundleIndex = GUILayout.SelectionGrid(_selectedBundleIndex, array2, 2, button, Array.Empty()); MuseumBundleInfo museumBundleInfo = museumSectionInfo.Bundles[_selectedBundleIndex]; GUILayout.Label(ModLocalization.T("devtools.museum.itemsIn", museumBundleInfo.Name), sectionHeader, Array.Empty()); _bundleScrollPosition = GUILayout.BeginScrollView(_bundleScrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(120f) }); foreach (MuseumItemInfo item in museumBundleInfo.Items) { string arg = (bundleInspector.HasDonated(item.Id) ? "[X]" : "[ ]"); string text = ((item.Quantity > 1) ? $" x{item.Quantity}" : ""); bool flag = item.GameItemId > 0; GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label($"{arg} {item.Name} (ID: {item.GameItemId})", label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(280f) }); GUI.enabled = flag; if (GUILayout.Button(flag ? ModLocalization.T("devtools.museum.spawn", text) : "—", button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }) && flag) { Plugin.GetItemInspector()?.SpawnItem(item.GameItemId, item.Quantity); } GUI.enabled = true; if (!flag) { GUILayout.Label("(Unity)", label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } GUILayout.EndVertical(); } private static void DrawBirthright(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.birthright.title"), sectionHeader, Array.Empty()); RaceModifierTracker raceModifierTracker = Plugin.GetRaceModifierTracker(); if (raceModifierTracker == null) { GUILayout.Label(ModLocalization.T("devtools.race.unavailable"), label, Array.Empty()); GUILayout.EndVertical(); return; } GUILayout.Label(ModLocalization.T("devtools.race.current", raceModifierTracker.GetCurrentRace()), label, Array.Empty()); List activeRaceBonuses = raceModifierTracker.GetActiveRaceBonuses(); if (activeRaceBonuses.Count > 0) { GUILayout.Label(ModLocalization.T("azrael.birthright.active_bonuses"), label, Array.Empty()); foreach (RaceBonusInfo item in activeRaceBonuses) { GUILayout.Label(" " + item.Type + ": " + item.GetFormattedValue(), label, Array.Empty()); } } List allRaces = raceModifierTracker.GetAllRaces(); if (allRaces.Count > 0) { GUILayout.Space(5f); string[] array = allRaces.ToArray(); if (_selectedRaceIndex >= allRaces.Count) { _selectedRaceIndex = 0; } _selectedRaceIndex = GUILayout.SelectionGrid(_selectedRaceIndex, array, 4, button, Array.Empty()); List bonusesForRace = raceModifierTracker.GetBonusesForRace(allRaces[_selectedRaceIndex]); _raceScrollPosition = GUILayout.BeginScrollView(_raceScrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(100f) }); foreach (RaceBonusInfo item2 in bonusesForRace) { GUILayout.Label(" " + item2.Type + ": " + item2.GetFormattedValue(), label, Array.Empty()); } GUILayout.EndScrollView(); } GUILayout.EndVertical(); } private static void DrawBirthdayReminder(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.birthday.title"), sectionHeader, Array.Empty()); try { Type type = ResolveModPlugin("BirthdayReminder", ref _cachedBirthdayReminderPlugin); if (type == null) { GUILayout.Label(ModLocalization.T("azrael.suite.plugin_not_found", "Birthday Reminder"), label, Array.Empty()); GUILayout.EndVertical(); return; } object obj = ReflectionHelper.InvokeStaticMethod(type, "GetManager"); if (obj == null) { GUILayout.Label(ModLocalization.T("azrael.suite.awaiting_character"), label, Array.Empty()); GUILayout.EndVertical(); return; } object arg = obj.GetType().GetProperty("HasBirthdays")?.GetValue(obj); object arg2 = obj.GetType().GetProperty("HasUngiftedBirthdays")?.GetValue(obj); IList list = obj.GetType().GetProperty("TodaysBirthdays")?.GetValue(obj) as IList; GUILayout.Label($"Has birthdays today: {arg}", label, Array.Empty()); GUILayout.Label($"Has ungifted: {arg2}", label, Array.Empty()); if (list != null) { GUILayout.Label($"Today's birthdays: {list.Count}", label, Array.Empty()); foreach (object item in list) { PropertyInfo? obj2 = item?.GetType().GetProperty("NpcName") ?? item?.GetType().GetProperty("Name"); PropertyInfo propertyInfo = item?.GetType().GetProperty("HasBeenGifted"); string text = obj2?.GetValue(item)?.ToString() ?? "?"; bool flag = (bool)(propertyInfo?.GetValue(item) ?? ((object)false)); GUILayout.Label(" - " + text + " " + (flag ? "[Gifted]" : ""), label, Array.Empty()); } } GUILayout.Space(8f); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(ModLocalization.T("azrael.birthday.check"), button, Array.Empty())) { ReflectionHelper.InvokeStaticMethod(type, "CheckBirthdays"); } if (GUILayout.Button(ModLocalization.T("azrael.birthday.refresh"), button, Array.Empty())) { obj.GetType().GetMethod("ManualRefresh")?.Invoke(obj, null); } if (GUILayout.Button(ModLocalization.T("azrael.birthday.test_notify"), button, Array.Empty())) { ReflectionHelper.InvokeStaticMethod(type, "SendAllBirthdayNotifications"); } GUILayout.EndHorizontal(); } catch (Exception ex) { GUILayout.Label("Error: " + ex.Message, label, Array.Empty()); } GUILayout.EndVertical(); } private static void DrawTodo(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.todo.title"), sectionHeader, Array.Empty()); try { Type type = ResolveModPlugin("SunhavenTodo", ref _cachedSunhavenTodoPlugin); if (type == null) { GUILayout.Label(ModLocalization.T("azrael.suite.plugin_not_found", "Sun Haven Todo"), label, Array.Empty()); GUILayout.EndVertical(); return; } object obj = ReflectionHelper.InvokeStaticMethod(type, "GetTodoManager"); if (obj == null) { GUILayout.Label(ModLocalization.T("azrael.suite.awaiting_character"), label, Array.Empty()); string text = ReflectionHelper.InvokeStaticMethod(type, "GetOpenListShortcutDisplay") as string; if (!string.IsNullOrEmpty(text)) { GUILayout.Label("Shortcut: " + text, label, Array.Empty()); } GUILayout.EndVertical(); return; } object obj2 = obj.GetType().GetMethod("GetData")?.Invoke(obj, null); int num = 0; string text2 = ""; if (obj2 != null) { num = (obj2.GetType().GetProperty("Items")?.GetValue(obj2) as IList)?.Count ?? 0; } text2 = (obj.GetType().GetProperty("CurrentCharacter")?.GetValue(obj))?.ToString() ?? "?"; string text3 = ReflectionHelper.GetStaticMethod(type, "GetOpenListShortcutDisplay")?.Invoke(null, null)?.ToString() ?? "Ctrl+T"; GUILayout.Label($"Tasks: {num}", label, Array.Empty()); GUILayout.Label("Character: " + text2, label, Array.Empty()); GUILayout.Label("Shortcut: " + text3, label, Array.Empty()); GUILayout.Space(8f); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(ModLocalization.T("azrael.todo.toggle_ui"), button, Array.Empty())) { ReflectionHelper.InvokeStaticMethod(type, "ToggleUI"); } if (GUILayout.Button(ModLocalization.T("azrael.todo.toggle_hud"), button, Array.Empty())) { ReflectionHelper.InvokeStaticMethod(type, "ToggleHUD"); } if (GUILayout.Button(ModLocalization.T("azrael.todo.save"), button, Array.Empty())) { ReflectionHelper.InvokeStaticMethod(type, "SaveData"); } GUILayout.EndHorizontal(); } catch (Exception ex) { GUILayout.Label("Error: " + ex.Message, label, Array.Empty()); } GUILayout.EndVertical(); } private static void DrawAlmanac(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.almanac.title"), sectionHeader, Array.Empty()); try { Type type = ResolveModPlugin("HavensAlmanac", ref _cachedHavensAlmanacPlugin); if (type == null) { GUILayout.Label(ModLocalization.T("azrael.suite.plugin_not_found", "Haven's Almanac"), label, Array.Empty()); GUILayout.EndVertical(); return; } object obj = ReflectionHelper.InvokeStaticMethod(type, "GetDataAggregator"); if (obj == null) { GUILayout.Label(ModLocalization.T("azrael.suite.awaiting_character"), label, Array.Empty()); GUILayout.EndVertical(); return; } PropertyInfo? property = obj.GetType().GetProperty("InstalledModCount"); PropertyInfo property2 = obj.GetType().GetProperty("HasAnyData"); int num = (int)(property?.GetValue(obj) ?? ((object)0)); bool flag = (bool)(property2?.GetValue(obj) ?? ((object)false)); GUILayout.Label($"Installed mods: {num}", label, Array.Empty()); GUILayout.Label($"Has data: {flag}", label, Array.Empty()); if (obj.GetType().GetProperty("Providers")?.GetValue(obj) is IList { Count: >0 } list) { GUILayout.Label("Providers:", label, Array.Empty()); foreach (object item in list) { PropertyInfo? obj2 = item?.GetType().GetProperty("ModName"); PropertyInfo propertyInfo = item?.GetType().GetProperty("IsReady"); string text = obj2?.GetValue(item)?.ToString() ?? "?"; bool flag2 = (bool)(propertyInfo?.GetValue(item) ?? ((object)false)); GUILayout.Label(" - " + text + " " + (flag2 ? "[Ready]" : ""), label, Array.Empty()); } } GUILayout.Space(8f); if (GUILayout.Button(ModLocalization.T("azrael.almanac.refresh"), button, Array.Empty())) { obj.GetType().GetMethod("RefreshAll")?.Invoke(obj, null); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[AzraelsMods] Refreshed Almanac data"); } } } catch (Exception ex) { GUILayout.Label("Error: " + ex.Message, label, Array.Empty()); } GUILayout.EndVertical(); } private static void DrawTrinketFortune(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { IDevToolsPanel devToolsPanel = DevToolsRegistry.Panels.FirstOrDefault((IDevToolsPanel p) => p.ModGuid == "com.azraelgodking.trinketfortune"); if (devToolsPanel != null) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(devToolsPanel.DisplayName, sectionHeader, Array.Empty()); GUILayout.Space(5f); try { devToolsPanel.Draw(box, button, label); } catch (Exception ex) { GUILayout.Label("Error: " + ex.Message, label, Array.Empty()); } GUILayout.EndVertical(); return; } GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.trinket.title"), sectionHeader, Array.Empty()); try { Type type = ResolveModPlugin("TrinketFortune", ref _cachedTrinketFortunePlugin); if (type == null) { GUILayout.Label(ModLocalization.T("azrael.suite.plugin_not_found", "Trinket Fortune"), label, Array.Empty()); GUILayout.EndVertical(); return; } if (_cachedTrinketGetDevToolsSummary == null) { _cachedTrinketGetDevToolsSummary = ReflectionHelper.GetStaticMethod(type, "GetDevToolsSummary"); } string text = _cachedTrinketGetDevToolsSummary?.Invoke(null, null) as string; GUILayout.Label(string.IsNullOrEmpty(text) ? ModLocalization.T("azrael.trinket.unavailable") : text, label, Array.Empty()); } catch (Exception ex2) { GUILayout.Label("Error: " + ex2.Message, label, Array.Empty()); } GUILayout.EndVertical(); } private static void DrawCropOptimizer(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.crop.title"), sectionHeader, Array.Empty()); try { Type type = ResolveModPlugin("CropOptimizer", ref _cachedCropOptimizerPlugin); if (type == null) { GUILayout.Label(ModLocalization.T("azrael.suite.plugin_not_found", "Crop Optimizer"), label, Array.Empty()); GUILayout.EndVertical(); return; } if (_cachedCropGetHudSummary == null) { _cachedCropGetHudSummary = ReflectionHelper.GetStaticMethod(type, "GetHudSummary"); } string text = _cachedCropGetHudSummary?.Invoke(null, null) as string; GUILayout.Label(string.IsNullOrEmpty(text) ? ModLocalization.T("azrael.crop.unavailable") : text, label, Array.Empty()); } catch (Exception ex) { GUILayout.Label("Error: " + ex.Message, label, Array.Empty()); } GUILayout.EndVertical(); } private static void DrawFasterRaces(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.races.title"), sectionHeader, Array.Empty()); try { Type type = ResolveModPlugin("FasterRaces", ref _cachedFasterRacesPlugin); if (type == null) { GUILayout.Label(ModLocalization.T("azrael.suite.plugin_not_found", "Faster Races"), label, Array.Empty()); GUILayout.EndVertical(); return; } FieldInfo field = type.GetField("EnableMod", ReflectionHelper.AllBindingFlags); FieldInfo field2 = type.GetField("SpeedBonusPercent", ReflectionHelper.AllBindingFlags); bool num = ReadConfigEntryBool(field?.GetValue(null)); float num2 = ReadConfigEntryFloat(field2?.GetValue(null)); if (!num) { GUILayout.Label(ModLocalization.T("azrael.races.disabled"), label, Array.Empty()); } else { GUILayout.Label(ModLocalization.T("azrael.races.speed", num2), label, Array.Empty()); } } catch (Exception ex) { GUILayout.Label("Error: " + ex.Message, label, Array.Empty()); } GUILayout.EndVertical(); } private static void DrawHavensRespec(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { RespecSimulatorPanel.Draw(box, button, label, sectionHeader); } private static void DrawGiftingAssistant(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("azrael.gifting.title"), sectionHeader, Array.Empty()); try { Type type = ResolveModPlugin("GiftingAssistant", ref _cachedGiftingAssistantPlugin); if (type == null) { GUILayout.Label(ModLocalization.T("azrael.suite.plugin_not_found", "Gifting Assistant"), label, Array.Empty()); GUILayout.EndVertical(); return; } object obj = type.GetProperty("StaticEnabled", ReflectionHelper.AllBindingFlags)?.GetValue(null); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) == 0) { GUILayout.Label(ModLocalization.T("azrael.gifting.disabled"), label, Array.Empty()); GUILayout.EndVertical(); return; } string text = (ReflectionHelper.InvokeStaticMethod(type, "GetOpenShortcutDisplay") as string) ?? "Ctrl+G"; GUILayout.Label("Shortcut: " + text, label, Array.Empty()); object obj2 = ReflectionHelper.InvokeStaticMethod(type, "GetManager"); if (obj2 == null) { GUILayout.Label(ModLocalization.T("azrael.suite.awaiting_character"), label, Array.Empty()); } else { int num2 = (obj2.GetType().GetMethod("GetEntries")?.Invoke(obj2, null) as ICollection)?.Count ?? 0; string text2 = obj2.GetType().GetProperty("CurrentCharacter")?.GetValue(obj2)?.ToString(); if (string.IsNullOrEmpty(text2)) { text2 = "?"; } GUILayout.Label(ModLocalization.T("azrael.gifting.roster", num2), label, Array.Empty()); GUILayout.Label("Character: " + text2, label, Array.Empty()); } GUILayout.Space(8f); if (GUILayout.Button(ModLocalization.T("azrael.gifting.toggle_ui"), button, Array.Empty())) { ReflectionHelper.InvokeStaticMethod(type, "ToggleUI"); } } catch (Exception ex) { GUILayout.Label("Error: " + ex.Message, label, Array.Empty()); } GUILayout.EndVertical(); } private static bool ReadConfigEntryBool(object configEntry) { if (configEntry == null) { return false; } object obj = configEntry.GetType().GetProperty("Value")?.GetValue(configEntry); 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; } private static float ReadConfigEntryFloat(object configEntry) { if (configEntry == null) { return 0f; } object obj = configEntry.GetType().GetProperty("Value")?.GetValue(configEntry); if (obj is float) { return (float)obj; } return 0f; } } internal static class RespecSimulatorPanel { private static Type _apiType; private static int _selectedProfessionIndex; private static string _statusMessage = string.Empty; public static void Draw(GUIStyle box, GUIStyle button, GUIStyle label, GUIStyle sectionHeader) { GUILayout.BeginVertical(box, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.respec.simulator.title"), sectionHeader, Array.Empty()); if (!Plugin.HasHavensRespec) { GUILayout.Label(ModLocalization.T("azrael.respec.unavailable"), label, Array.Empty()); GUILayout.EndVertical(); return; } if (!EnsureApi(out string error)) { GUILayout.Label(error, label, Array.Empty()); GUILayout.EndVertical(); return; } if (!InvokeStaticBool("IsReady")) { GUILayout.Label(ModLocalization.T("devtools.respec.simulator.not_ready"), label, Array.Empty()); GUILayout.EndVertical(); return; } int num = InvokeStaticInt("ProfessionCount"); if (num <= 0) { GUILayout.Label(ModLocalization.T("devtools.respec.simulator.not_ready"), label, Array.Empty()); GUILayout.EndVertical(); return; } string[] array = new string[num]; for (int i = 0; i < num; i++) { array[i] = InvokeStaticStringMethod("GetProfessionName", i); } int num2 = InvokeStaticIntMethod("GetActiveProfessionIndex"); if (num2 >= 0 && num2 < num) { _selectedProfessionIndex = num2; } _selectedProfessionIndex = Mathf.Clamp(_selectedProfessionIndex, 0, num - 1); _selectedProfessionIndex = GUILayout.SelectionGrid(_selectedProfessionIndex, array, 2, button, Array.Empty()); GUILayout.Space(6f); if (InvokeHasPending(out int professionIndex, out int refunded, out int cost, out bool canAfford, out string costLabel)) { DrawPendingState(button, label, array, professionIndex, refunded, cost, canAfford, costLabel); } else { DrawEstimateState(button, label); } if (!string.IsNullOrEmpty(_statusMessage)) { GUILayout.Label(_statusMessage, label, Array.Empty()); } GUILayout.EndVertical(); } private static void DrawEstimateState(GUIStyle button, GUIStyle label) { if (TryGetEstimate(_selectedProfessionIndex, out int refund, out int cost, out bool canAfford, out string costLabel)) { GUILayout.Label(ModLocalization.T("devtools.respec.simulator.estimate_refund", refund), label, Array.Empty()); GUILayout.Label((cost > 0) ? ModLocalization.T("devtools.respec.simulator.estimate_cost", costLabel) : ModLocalization.T("devtools.respec.simulator.estimate_free"), label, Array.Empty()); if (cost > 0 && !canAfford) { GUILayout.Label(ModLocalization.T("devtools.respec.simulator.cannot_afford"), label, Array.Empty()); } } GUILayout.Space(4f); if (GUILayout.Button(ModLocalization.T("devtools.respec.simulator.simulate"), button, Array.Empty())) { if (InvokeTrySimulate(_selectedProfessionIndex, out string errorMessage)) { _statusMessage = ModLocalization.T("devtools.respec.simulator.started"); } else { _statusMessage = (string.IsNullOrEmpty(errorMessage) ? ModLocalization.T("devtools.respec.simulator.failed") : errorMessage); } } } private static void DrawPendingState(GUIStyle button, GUIStyle label, string[] professionNames, int pendingIndex, int refunded, int cost, bool canAfford, string costLabel) { string text = ((pendingIndex >= 0 && pendingIndex < professionNames.Length) ? professionNames[pendingIndex] : "?"); GUILayout.Label(ModLocalization.T("devtools.respec.simulator.pending_intro", text), label, Array.Empty()); GUILayout.Label(ModLocalization.T("devtools.respec.simulator.pending_refund", refunded), label, Array.Empty()); GUILayout.Label((cost > 0) ? ModLocalization.T("devtools.respec.simulator.pending_cost", costLabel) : ModLocalization.T("devtools.respec.simulator.estimate_free"), label, Array.Empty()); if (cost > 0 && !canAfford) { GUILayout.Label(ModLocalization.T("devtools.respec.simulator.cannot_afford"), label, Array.Empty()); } GUILayout.Space(4f); GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = canAfford || cost <= 0; if (GUILayout.Button(ModLocalization.T("devtools.respec.simulator.apply"), button, Array.Empty())) { if (InvokeTryCommit(out string errorMessage)) { _statusMessage = ModLocalization.T("devtools.respec.simulator.applied"); } else { _statusMessage = (string.IsNullOrEmpty(errorMessage) ? ModLocalization.T("devtools.respec.simulator.failed") : errorMessage); } } GUI.enabled = true; if (GUILayout.Button(ModLocalization.T("devtools.respec.simulator.revert"), button, Array.Empty())) { if (InvokeTryCancel(out string errorMessage2)) { _statusMessage = ModLocalization.T("devtools.respec.simulator.reverted"); } else { _statusMessage = (string.IsNullOrEmpty(errorMessage2) ? ModLocalization.T("devtools.respec.simulator.failed") : errorMessage2); } } GUILayout.EndHorizontal(); } private static bool EnsureApi(out string error) { error = null; if (_apiType != null) { return true; } _apiType = ReflectionHelper.FindType("RespecDevToolsApi", "HavensRespec.DevTools"); if (_apiType == null) { error = ModLocalization.T("devtools.respec.simulator.api_missing"); return false; } return true; } private static bool InvokeStaticBool(string memberName) { try { object staticValue = ReflectionHelper.GetStaticValue(_apiType, memberName); bool flag = default(bool); int num; if (staticValue is bool) { flag = (bool)staticValue; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static int InvokeStaticInt(string memberName) { try { return (ReflectionHelper.GetStaticValue(_apiType, memberName) is int num) ? num : (-1); } catch { return -1; } } private static int InvokeStaticIntMethod(string methodName, params object[] args) { try { return (ReflectionHelper.InvokeStaticMethod(_apiType, methodName, args) is int num) ? num : (-1); } catch { return -1; } } private static string InvokeStaticStringMethod(string methodName, params object[] args) { try { return (ReflectionHelper.InvokeStaticMethod(_apiType, methodName, args) as string) ?? "?"; } catch { return "?"; } } private static bool TryGetEstimate(int professionIndex, out int refund, out int cost, out bool canAfford, out string costLabel) { refund = 0; cost = 0; canAfford = true; costLabel = string.Empty; try { MethodInfo method = _apiType.GetMethod("TryGetEstimate", ReflectionHelper.AllBindingFlags); if (method == null) { return false; } object[] array = new object[5] { professionIndex, 0, 0, true, string.Empty }; object obj = method.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) == 0) { return false; } refund = (int)array[1]; cost = (int)array[2]; canAfford = (bool)array[3]; costLabel = (array[4] as string) ?? string.Empty; return true; } catch { return false; } } private static bool InvokeTrySimulate(int professionIndex, out string errorMessage) { errorMessage = null; try { MethodInfo method = _apiType.GetMethod("TrySimulate", ReflectionHelper.AllBindingFlags); if (method == null) { return false; } object[] array = new object[2] { professionIndex, null }; object obj = method.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } int result = num & (flag ? 1 : 0); errorMessage = array[1] as string; return (byte)result != 0; } catch (Exception ex) { errorMessage = ex.Message; return false; } } private static bool InvokeHasPending(out int professionIndex, out int refunded, out int cost, out bool canAfford, out string costLabel) { professionIndex = -1; refunded = 0; cost = 0; canAfford = true; costLabel = string.Empty; try { MethodInfo method = _apiType.GetMethod("HasPendingSimulation", ReflectionHelper.AllBindingFlags); if (method == null) { return false; } object[] array = new object[5] { -1, 0, 0, true, string.Empty }; object obj = method.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) == 0) { return false; } professionIndex = (int)array[0]; refunded = (int)array[1]; cost = (int)array[2]; canAfford = (bool)array[3]; costLabel = (array[4] as string) ?? string.Empty; return true; } catch { return false; } } private static bool InvokeTryCommit(out string errorMessage) { errorMessage = null; try { MethodInfo method = _apiType.GetMethod("TryCommitSimulation", ReflectionHelper.AllBindingFlags); if (method == null) { return false; } object[] array = new object[1]; object obj = method.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } int result = num & (flag ? 1 : 0); errorMessage = array[0] as string; return (byte)result != 0; } catch (Exception ex) { errorMessage = ex.Message; return false; } } private static bool InvokeTryCancel(out string errorMessage) { errorMessage = null; try { MethodInfo method = _apiType.GetMethod("TryCancelSimulation", ReflectionHelper.AllBindingFlags); if (method == null) { return false; } object[] array = new object[1]; object obj = method.Invoke(null, array); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } int result = num & (flag ? 1 : 0); errorMessage = array[0] as string; return (byte)result != 0; } catch (Exception ex) { errorMessage = ex.Message; return false; } } } } namespace HavenDevTools.Config { public static class ModConfig { private static bool _theVaultInspectorHooked; private static Type _cachedTheVaultPluginType; private static MethodInfo _cachedSetTheVaultFullInspector; public static ConfigEntry ToggleKey { get; private set; } public static ConfigEntry OverlayToggleKey { get; private set; } public static ConfigEntry ShowOverlayOnStart { get; private set; } public static ConfigEntry OverlayPosition { get; private set; } public static ConfigEntry ShowPerformance { get; private set; } public static ConfigEntry ShowFpsCounter { get; private set; } public static ConfigEntry FpsCounterPosition { get; private set; } public static ConfigEntry MaxLogEntries { get; private set; } public static ConfigEntry LogLevelFilter { get; private set; } public static ConfigEntry CheckForUpdates { get; private set; } public static ConfigEntry TheVaultFullVaultInspector { get; private set; } public static ConfigEntry PauseGameWhenDebugOpen { get; private set; } public static ConfigEntry DebugWindowWidth { get; private set; } public static ConfigEntry DebugWindowHeight { get; private set; } public static void SaveDebugWindowSize(float width, float height) { if (DebugWindowWidth != null) { DebugWindowWidth.Value = width; } if (DebugWindowHeight != null) { DebugWindowHeight.Value = height; } } public static void Initialize(ConfigFile config) { ToggleKey = config.Bind("Hotkeys", "ToggleKey", (KeyCode)292, "Key to toggle the debug window (requires authorization)"); OverlayToggleKey = config.Bind("Hotkeys", "OverlayToggleKey", (KeyCode)287, "Key to toggle the debug overlay"); ShowOverlayOnStart = config.Bind("Overlay", "ShowOnStart", false, "Show the debug overlay when the game starts"); OverlayPosition = config.Bind("Overlay", "Position", "TopRight", "Overlay position: TopLeft, TopRight, BottomLeft, BottomRight"); ShowPerformance = config.Bind("Overlay", "ShowPerformance", true, "Show FPS and memory usage in the debug overlay"); ShowFpsCounter = config.Bind("Overlay", "ShowFpsCounter", true, "Show a compact FPS counter in a screen corner (independent of the F6 overlay)"); FpsCounterPosition = config.Bind("Overlay", "FpsCounterPosition", "TopLeft", "FPS counter position: TopLeft, TopRight, BottomLeft, BottomRight"); MaxLogEntries = config.Bind("LogViewer", "MaxLogEntries", 500, "Maximum number of log entries to keep in the in-game log viewer"); LogLevelFilter = config.Bind("LogViewer", "LogLevelFilter", "Info", "Minimum log level to display: Debug, Info, Warning, Error"); CheckForUpdates = config.Bind("Updates", "CheckForUpdates", true, "Check for mod updates on startup"); TheVaultFullVaultInspector = config.Bind("The Vault", "FullVaultInspector", false, "When true: The Vault lists every defined currency (including 0), adds the Debug tab with a raw vault dump, and the HUD shows all slots. Same as the former [Debug] FullVaultInspector in TheVault.cfg (moved here)."); PauseGameWhenDebugOpen = config.Bind("DebugWindow", "PauseGameWhenDebugOpen", false, "Pause the player while the F11 debug window is open. Leave false so in-game chat and other UI stay usable with the window open."); DebugWindowWidth = config.Bind("DebugWindow", "Width", 560f, "Debug window width in pixels (drag bottom-right corner to resize in-game)."); DebugWindowHeight = config.Bind("DebugWindow", "Height", 640f, "Debug window height in pixels (drag bottom-right corner to resize in-game)."); if (!_theVaultInspectorHooked) { TheVaultFullVaultInspector.SettingChanged += delegate { SyncTheVaultFullVaultInspectorToPlugin(); }; _theVaultInspectorHooked = true; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Configuration initialized"); } } public static void SyncTheVaultFullVaultInspectorToPlugin() { try { if (!Plugin.HasTheVault || TheVaultFullVaultInspector == null) { return; } if ((object)_cachedTheVaultPluginType == null) { _cachedTheVaultPluginType = ReflectionHelper.FindModPlugin("TheVault"); } if (!(_cachedTheVaultPluginType == null)) { if ((object)_cachedSetTheVaultFullInspector == null) { _cachedSetTheVaultFullInspector = _cachedTheVaultPluginType.GetMethod("SetConfigDebugFullVaultInspector", BindingFlags.Static | BindingFlags.Public); } if (!(_cachedSetTheVaultFullInspector == null)) { _cachedSetTheVaultFullInspector.Invoke(null, new object[1] { TheVaultFullVaultInspector.Value }); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[ModConfig] SyncTheVaultFullVaultInspectorToPlugin: " + ex.Message)); } } } public static OverlayPositionType GetOverlayPosition() { return ParseOverlayPosition(OverlayPosition.Value); } public static OverlayPositionType GetFpsCounterPosition() { return ParseOverlayPosition(FpsCounterPosition.Value); } private static OverlayPositionType ParseOverlayPosition(string value) { return value?.ToLower() switch { "topleft" => OverlayPositionType.TopLeft, "topright" => OverlayPositionType.TopRight, "bottomleft" => OverlayPositionType.BottomLeft, "bottomright" => OverlayPositionType.BottomRight, _ => OverlayPositionType.TopRight, }; } } public enum OverlayPositionType { TopLeft, TopRight, BottomLeft, BottomRight } } namespace HavenDevTools.API { public static class DevToolsRegistry { private static readonly List _panels = new List(); private static readonly object _lock = new object(); public static IReadOnlyList Panels { get { lock (_lock) { return _panels.ToArray(); } } } public static void Register(IDevToolsPanel panel) { if (panel == null) { return; } lock (_lock) { if (_panels.Exists((IDevToolsPanel p) => p.ModGuid == panel.ModGuid)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[DevToolsRegistry] Panel for " + panel.ModGuid + " already registered")); } return; } _panels.Add(panel); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[DevToolsRegistry] Registered panel: " + panel.DisplayName + " (" + panel.ModGuid + ")")); } } } public static void Unregister(string modGuid) { lock (_lock) { _panels.RemoveAll((IDevToolsPanel p) => p.ModGuid == modGuid); } } } public interface IDevToolsPanel { string ModGuid { get; } string DisplayName { get; } void Draw(GUIStyle boxStyle, GUIStyle buttonStyle, GUIStyle labelStyle); } }