Decompiled source of HavensRespec v2.1.4
HavensRespec.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using HavensRespec.Config; using HavensRespec.Patches; using HavensRespec.Services; using HavensRespec.UI; using I2.Loc; using Microsoft.CodeAnalysis; using SunhavenMods.Shared; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; using Wish; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("HavensRespec")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+fe4c8b9eaa0b5ceabd85b506ee69e9e122723007")] [assembly: AssemblyProduct("HavensRespec")] [assembly: AssemblyTitle("HavensRespec")] [assembly: AssemblyVersion("1.0.0.0")] [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<string> 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<string> 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("<Config>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.10.10"; } public static class ModDiagnostics { private static readonly Dictionary<string, ModHealthReport> ReportsByPluginGuid = new Dictionary<string, ModHealthReport>(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.10.10"; } lock (Lock) { ReportsByPluginGuid[report.PluginGuid] = report.Clone(); } } public static void ReportRuntime(string pluginGuid, Action<ModHealthReport> 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<ModHealthReport> 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.10.10", IntegrationSummary = integrationSummary, Mode = mode, DataLoaded = dataLoaded, LastPersistenceOutcome = lastPersistenceOutcome }); } } 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 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 const string HavensMirror = "com.azraelgodking.havensmirror"; } public static class ModHealthIntegrationSummary { public static string Build(params (string label, string pluginGuid)[] integrations) { if (integrations == null || integrations.Length == 0) { return "standalone"; } Dictionary<string, PluginInfo> pluginInfos = Chainloader.PluginInfos; if (pluginInfos == null) { return "standalone"; } List<string> list = new List<string>(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 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>(); } FieldInfo[] fields = type.GetFields(AllBindingFlags); IEnumerable<FieldInfo> second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty<FieldInfo>(); } else { IEnumerable<FieldInfo> allFields = GetAllFields(type.BaseType); second = allFields; } return fields.Concat(second).Distinct().ToArray(); } public static PropertyInfo[] GetAllProperties(Type type) { if (type == null) { return Array.Empty<PropertyInfo>(); } PropertyInfo[] properties = type.GetProperties(AllBindingFlags); IEnumerable<PropertyInfo> second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty<PropertyInfo>(); } else { IEnumerable<PropertyInfo> allProperties = GetAllProperties(type.BaseType); second = allProperties; } return (from p in properties.Concat(second) group p by p.Name into g select g.First()).ToArray(); } public static T TryGetValue<T>(object instance, string memberName, T defaultValue = default(T)) { try { object instanceValue = GetInstanceValue(instance, memberName); if (instanceValue is T result) { return result; } if (instanceValue != null && typeof(T).IsAssignableFrom(instanceValue.GetType())) { return (T)instanceValue; } return defaultValue; } catch { return defaultValue; } } } public static class ReflectionProbe { private static readonly HashSet<string> Reported = new HashSet<string>(StringComparer.Ordinal); private static readonly object Lock = new object(); public static bool Try(string site, Action action, ManualLogSource log = null) { if (action == null) { return false; } try { action(); return true; } catch (Exception ex) { bool flag; lock (Lock) { flag = Reported.Add(site ?? "unknown"); } if (flag && log != null) { log.LogWarning((object)("[Probe] " + site + " failed (" + ex.GetType().Name + ": " + ex.Message + ") — game version drift?")); } return false; } } public static bool Try<T>(string site, Func<T> func, out T result, ManualLogSource log = null) { result = default(T); if (func == null) { return false; } try { result = func(); return true; } catch (Exception ex) { bool flag; lock (Lock) { flag = Reported.Add(site ?? "unknown"); } if (flag && log != null) { log.LogWarning((object)("[Probe] " + site + " failed (" + ex.GetType().Name + ": " + ex.Message + ") — game version drift?")); } return false; } } public static void LogOnce(string site, Exception ex, ManualLogSource log = null) { bool flag; lock (Lock) { flag = Reported.Add(site ?? "unknown"); } if (flag) { string text = ((ex != null) ? (ex.GetType().Name + ": " + ex.Message) : "unknown error"); if (log != null) { log.LogWarning((object)("[Probe] " + site + " failed (" + text + ") — game version drift?")); } } } public static void ResetReportedSitesForTests() { lock (Lock) { Reported.Clear(); } } } 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<VersionCheckResult> 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<VersionCheckResult> onComplete) { VersionCheckResult result = new VersionCheckResult { CurrentVersion = currentVersion }; string json = null; string fetchError = null; while (true) { SharedFetchState sharedFetchState = SharedFetchState.Read(); TimeSpan timeSpan = DateTime.UtcNow - sharedFetchState.AtUtc; if (sharedFetchState.Kind == "done" && timeSpan < SharedSuccessTtl && AppDomain.CurrentDomain.GetData("SunhavenMods.VersionChecker.Json") is string text) { json = text; break; } if (sharedFetchState.Kind == "failed" && timeSpan < SharedFailureTtl) { fetchError = sharedFetchState.Message; break; } if (sharedFetchState.Kind == "fetching" && timeSpan < SharedFetchTimeout) { yield return null; continue; } SharedFetchState.Write("fetching", null); UnityWebRequest www = UnityWebRequest.Get("https://raw.githubusercontent.com/AzraelGodKing/SunhavenMods/main/docs/versions.json"); try { www.timeout = 10; yield return www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { fetchError = "Network error: " + www.error; SharedFetchState.Write("failed", fetchError); } else { json = www.downloadHandler.text; AppDomain.CurrentDomain.SetData("SunhavenMods.VersionChecker.Json", json); SharedFetchState.Write("done", null); } } finally { ((IDisposable)www)?.Dispose(); } break; } string text2 = fetchError; if (text2 == null) { try { text2 = EvaluateManifest(json, pluginGuid, currentVersion, result); } catch (Exception ex) { text2 = "Parse error: " + ex.Message; } } if (text2 != null) { result.Success = false; result.UpdateAvailable = false; result.ErrorMessage = text2; RecordHealthError(pluginGuid, text2); LogWarningMsg(text2); } else { result.Success = true; if (result.UpdateAvailable) { LogInfo("Update available for " + result.ModName + ": " + currentVersion + " -> " + result.LatestVersion); } else { LogInfo(result.ModName + " is up to date (v" + currentVersion + ")"); } } onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); } } private struct SharedFetchState { public const string Fetching = "fetching"; public const string Done = "done"; public const string Failed = "failed"; public string Kind; public DateTime AtUtc; public string Message; public static SharedFetchState Read() { SharedFetchState result = new SharedFetchState { AtUtc = DateTime.MinValue }; if (!(AppDomain.CurrentDomain.GetData("SunhavenMods.VersionChecker.State") is string text)) { return result; } string[] array = text.Split(new char[1] { '|' }, 3); if (array.Length < 2 || !long.TryParse(array[1], NumberStyles.None, CultureInfo.InvariantCulture, out var result2)) { return result; } result.Kind = array[0]; result.AtUtc = new DateTime(result2, DateTimeKind.Utc); result.Message = ((array.Length > 2) ? array[2] : null); return result; } public static void Write(string kind, string message) { string text = DateTime.UtcNow.Ticks.ToString(CultureInfo.InvariantCulture); AppDomain.CurrentDomain.SetData("SunhavenMods.VersionChecker.State", kind + "|" + text + "|" + (message ?? string.Empty)); } } private const string VersionsUrl = "https://raw.githubusercontent.com/AzraelGodKing/SunhavenMods/main/docs/versions.json"; private const string SharedStateKey = "SunhavenMods.VersionChecker.State"; private const string SharedJsonKey = "SunhavenMods.VersionChecker.Json"; private static readonly TimeSpan SharedFetchTimeout = TimeSpan.FromSeconds(20.0); private static readonly TimeSpan SharedSuccessTtl = TimeSpan.FromMinutes(30.0); private static readonly TimeSpan SharedFailureTtl = TimeSpan.FromMinutes(1.0); private static readonly Dictionary<string, ModHealthSnapshot> HealthByPluginGuid = new Dictionary<string, ModHealthSnapshot>(StringComparer.OrdinalIgnoreCase); private static readonly object HealthLock = new object(); public static void CheckForUpdate(string pluginGuid, string currentVersion, ManualLogSource logger = null, Action<VersionCheckResult> onComplete = null) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) TouchHealth(pluginGuid); VersionCheckRunner versionCheckRunner = new GameObject("VersionChecker").AddComponent<VersionCheckRunner>(); 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) { return TryCompareVersions(v1, v2).GetValueOrDefault(); } public static int? TryCompareVersions(string v1, string v2) { if (!TryParseVersionCore(v1, out int[] parts) || !TryParseVersionCore(v2, out int[] parts2)) { return null; } int num = Math.Max(parts.Length, parts2.Length); for (int i = 0; i < num; i++) { int num2 = ((i < parts.Length) ? parts[i] : 0); int num3 = ((i < parts2.Length) ? parts2[i] : 0); if (num2 < num3) { return -1; } if (num2 > num3) { return 1; } } return 0; } private static bool TryParseVersionCore(string version, out int[] parts) { parts = null; if (string.IsNullOrWhiteSpace(version)) { return false; } string text = version.Trim().TrimStart('v', 'V'); int num = text.IndexOfAny(new char[2] { '-', '+' }); if (num >= 0) { text = text.Substring(0, num); } string[] array = text.Split(new char[1] { '.' }); int[] array2 = new int[array.Length]; for (int i = 0; i < array.Length; i++) { if (!int.TryParse(array[i], NumberStyles.None, CultureInfo.InvariantCulture, out array2[i])) { return false; } } parts = array2; return true; } internal static string EvaluateManifest(string json, string pluginGuid, string currentVersion, VersionCheckResult result) { Dictionary<string, object> dictionary; try { dictionary = MinimalJsonParser.Parse((json ?? string.Empty).TrimStart(new char[1] { '\ufeff' })) as Dictionary<string, object>; } catch (JsonParseException ex) { return "versions.json is not valid JSON (" + ex.Message + ")"; } if (dictionary == null) { return "versions.json root is not an object"; } if (string.IsNullOrEmpty(pluginGuid) || !dictionary.TryGetValue(pluginGuid, out var value) || !(value is Dictionary<string, object> entry)) { return "Mod '" + pluginGuid + "' not found in versions.json"; } result.LatestVersion = ManifestField(entry, "version"); result.ModName = ManifestField(entry, "name"); result.NexusUrl = ManifestField(entry, "nexus"); result.Changelog = ManifestField(entry, "changelog"); if (string.IsNullOrEmpty(result.LatestVersion)) { return "Could not parse version from response"; } int? num = TryCompareVersions(currentVersion, result.LatestVersion); if (!num.HasValue) { return "Could not compare installed version '" + currentVersion + "' with '" + result.LatestVersion + "'"; } result.UpdateAvailable = num.Value < 0; return null; } private static string ManifestField(Dictionary<string, object> entry, string key) { if (!entry.TryGetValue(key, out object value) || value == null) { return null; } return (value as string) ?? Convert.ToString(value, CultureInfo.InvariantCulture); } 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; } } } 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<string> NoKillSubstrings = new List<string>(); 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<string> list; lock (Lock) { if (NoKillSubstrings.Count == 0) { return; } list = new List<string>(NoKillSubstrings); } List<GameObject> list2 = new List<GameObject>(__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 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<InputField>() != (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<InputField>() != (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; } } public sealed class JsonParseException : Exception { public int Position { get; } public JsonParseException(string message, int position) : base(message) { Position = position; } } 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("\\\""); continue; case '\\': sb.Append("\\\\"); continue; case '\n': sb.Append("\\n"); continue; case '\r': sb.Append("\\r"); continue; case '\t': sb.Append("\\t"); continue; case '\b': sb.Append("\\b"); continue; case '\f': sb.Append("\\f"); continue; } if (c < ' ' || c == '\u007f') { sb.Append("\\u"); int num = c; sb.Append(num.ToString("x4", CultureInfo.InvariantCulture)); } else { sb.Append(c); } } } sb.Append('"'); } internal static object Parse(string json) { if (json == null) { throw new JsonParseException("JSON input is null.", 0); } int pos = 0; object result = ParseValue(json, ref pos); SkipWhitespace(json, ref pos); if (pos < json.Length) { throw new JsonParseException($"Unexpected trailing content at position {pos}.", pos); } return result; } 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) { throw new JsonParseException("Unexpected end of input.", pos); } 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)) { throw new JsonParseException($"Unexpected character '{c}' at position {pos}.", pos); } goto case '-'; case '-': return ParseNumber(json, ref pos); } } internal static Dictionary<string, object> ParseObject(string json, ref int pos) { SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != '{') { throw new JsonParseException("Expected '{' to start object.", pos); } pos++; Dictionary<string, object> dictionary = new Dictionary<string, object>(); SkipWhitespace(json, ref pos); if (pos < json.Length && json[pos] == '}') { pos++; return dictionary; } while (true) { SkipWhitespace(json, ref pos); string key = ParseString(json, ref pos); SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != ':') { throw new JsonParseException("Expected ':' after object key.", pos); } pos++; SkipWhitespace(json, ref pos); dictionary[key] = 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] != '}') { throw new JsonParseException("Expected '}' to close object.", pos); } pos++; return dictionary; } internal static List<object> ParseArray(string json, ref int pos) { SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != '[') { throw new JsonParseException("Expected '[' to start array.", pos); } pos++; List<object> list = new List<object>(); SkipWhitespace(json, ref pos); if (pos < json.Length && json[pos] == ']') { pos++; return list; } while (true) { 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] != ']') { throw new JsonParseException("Expected ']' to close array.", pos); } pos++; return list; } internal static string ParseString(string json, ref int pos) { SkipWhitespace(json, ref pos); if (pos >= json.Length || json[pos] != '"') { throw new JsonParseException("Expected '\"' to start string.", pos); } 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) { throw new JsonParseException("Incomplete Unicode escape in string.", pos); } string text = json.Substring(pos + 1, 4); if (!ushort.TryParse(text, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result)) { throw new JsonParseException("Invalid Unicode escape \\u" + text + ".", pos); } pos += 4; if (result >= 55296 && result <= 56319) { if (pos + 5 >= json.Length || json[pos + 1] != '\\' || json[pos + 2] != 'u') { throw new JsonParseException("Unpaired high surrogate in string.", pos); } if (!ushort.TryParse(json.Substring(pos + 3, 4), NumberStyles.HexNumber, CultureInfo.InvariantCulture, out var result2) || result2 < 56320 || result2 > 57343) { throw new JsonParseException("Invalid low surrogate in string.", pos); } stringBuilder.Append(char.ConvertFromUtf32(char.ConvertToUtf32((char)result, (char)result2))); pos += 6; } else { stringBuilder.Append((char)result); } break; } default: throw new JsonParseException($"Invalid escape '\\{json[pos]}' in string.", pos); } pos++; } else { if (c == '"') { pos++; return stringBuilder.ToString(); } if (c < ' ') { throw new JsonParseException($"Unescaped control character U+{(int)c:X4} in string.", pos); } stringBuilder.Append(c); pos++; } } throw new JsonParseException("Unterminated string.", pos); } internal static object ParseNumber(string json, ref int pos) { int num = pos; bool flag = false; if (pos < json.Length && json[pos] == '-') { pos++; } if (pos >= json.Length || !char.IsDigit(json[pos])) { throw new JsonParseException("Invalid number.", num); } while (pos < json.Length && char.IsDigit(json[pos])) { pos++; } if (pos < json.Length && json[pos] == '.') { flag = true; pos++; if (pos >= json.Length || !char.IsDigit(json[pos])) { throw new JsonParseException("Invalid number fraction.", num); } 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++; } if (pos >= json.Length || !char.IsDigit(json[pos])) { throw new JsonParseException("Invalid number exponent.", num); } while (pos < json.Length && char.IsDigit(json[pos])) { pos++; } } string text = json.Substring(num, pos - num); if (flag) { if (double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } throw new JsonParseException("Invalid floating-point number '" + text + "'.", num); } if (long.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { return result2; } throw new JsonParseException("Invalid integer '" + text + "'.", num); } internal static object ParseLiteral(string json, ref int pos, string literal, object result) { if (pos + literal.Length <= json.Length && string.CompareOrdinal(json, pos, literal, 0, literal.Length) == 0) { pos += literal.Length; return result; } throw new JsonParseException("Expected literal '" + literal + "'.", pos); } 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<string, string> LanguageAlias = new Dictionary<string, string>(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<string, Dictionary<string, string>> _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<string> LanguageChanged { add { LanguageChangeWatcher.LanguageChanged += value; } remove { LanguageChangeWatcher.LanguageChanged -= value; } } public static void Init(string modId, Dictionary<string, Dictionary<string, string>> tables, Harmony harmony, ManualLogSource log) { _modId = modId ?? string.Empty; _tables = tables ?? new Dictionary<string, Dictionary<string, string>>(); _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<string, string> 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<string, string> 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<string, string> 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<string, Dictionary<string, string>> ParseStringsJson(string json) { Dictionary<string, Dictionary<string, string>> dictionary = new Dictionary<string, Dictionary<string, string>>(StringComparer.Ordinal); if (string.IsNullOrWhiteSpace(json)) { return dictionary; } int pos = 0; Dictionary<string, object> dictionary2 = MinimalJsonParser.ParseObject(json, ref pos); if (dictionary2 == null) { return dictionary; } foreach (KeyValuePair<string, object> item in dictionary2) { if (!(item.Value is Dictionary<string, object> dictionary3)) { continue; } Dictionary<string, string> dictionary4 = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair<string, object> 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<string, Dictionary<string, string>> 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<string, Dictionary<string, string>>(); } 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<string, Dictionary<string, string>>(); } } public static void Shutdown() { _log = null; } } public static class LanguageChangeWatcher { private static bool _patched; public static event Action<string> 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<bool> BindForceEnglish(ConfigFile config) { ConfigEntry<bool> entry = config.Bind<bool>("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<string, Dictionary<string, string>> 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 HavensRespec { [BepInPlugin("com.azraelgodking.havensrespec", "Haven's Respec", "2.1.4")] public class Plugin : BaseUnityPlugin { private static Harmony _staticHarmony; private static RespecConfig _staticConfig; private static SkillResetService _staticResetService; private static CostService _staticCostService; private static RespecController _staticController; private static GameObject _persistentRunner; private static RespecPersistentRunner _persistentRunnerComponent; private bool _applicationQuitting; public static ManualLogSource Log { get; private set; } public static Plugin Instance { get; private set; } public static bool IsModEnabled { get { if (_staticConfig != null) { return _staticConfig.Enabled.Value; } return false; } } public static bool IsDebugLoggingEnabled { get { RespecConfig staticConfig = _staticConfig; if (staticConfig == null) { return false; } return staticConfig.DebugLogging?.Value == true; } } internal static RespecController GetController() { return _staticController; } private void Awake() { Instance = this; Log = ((BaseUnityPlugin)this).Logger; try { ConfigFile val = ConfigFileHelper.CreateNamedConfig("com.azraelgodking.havensrespec", "HavensRespec.cfg", (Action<string>)Log.LogWarning); ConfigFileHelper.ReplacePluginConfig((BaseUnityPlugin)(object)this, val, (Action<string>)Log.LogWarning); _staticConfig = new RespecConfig(val); LocalizationBootstrap.BindForceEnglish(val); if (!_staticConfig.Enabled.Value) { Log.LogInfo((object)"Haven's Respec disabled in config."); ModDiagnostics.LogModStartup(Log, "com.azraelgodking.havensrespec", "Haven's Respec", "2.1.4", ModHealthIntegrationSummary.Build(("DevTools", "com.azraelgodking.havendevtools")), "disabled", false); return; } EnsureModServices(); EnsureHarmonyPatched(); CreatePersistentRunner(); SceneManager.sceneLoaded += OnSceneLoaded; try { VersionChecker.CheckForUpdate("com.azraelgodking.havensrespec", "2.1.4", Log); } catch (Exception ex) { Log.LogDebug((object)("[Respec] VersionChecker swallowed: " + ex.Message)); } Log.LogInfo((object)"Haven's Respec v2.1.4 loaded."); ModDiagnostics.LogModStartup(Log, "com.azraelgodking.havensrespec", "Haven's Respec", "2.1.4", ModHealthIntegrationSummary.Build(("DevTools", "com.azraelgodking.havendevtools")), "startup", false); } catch (Exception arg) { Log.LogError((object)string.Format("{0} Awake failed: {1}", "Haven's Respec", arg)); } } private void OnApplicationQuit() { _applicationQuitting = true; TeardownMod(fullShutdown: true); } private void OnDestroy() { //IL_0020: 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) 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] Haven's Respec OnDestroy during expected teardown (scene: " + text + ")")); } } else { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("[Lifecycle] Haven's Respec OnDestroy outside expected teardown (scene: " + text + ")")); } } if (_applicationQuitting) { TeardownMod(fullShutdown: true); } } private static void OnSceneLoaded(Scene scene, LoadSceneMode mode) { ManualLogSource log = Log; if (log != null) { log.LogDebug((object)("[Respec] Scene loaded: " + ((Scene)(ref scene)).name)); } if (!(((Scene)(ref scene)).name == "MainMenu") && !(((Scene)(ref scene)).name == "Bootstrap")) { EnsureModReady(); } } public static void EnsureModReady() { if (_staticConfig == null || !_staticConfig.Enabled.Value) { return; } try { EnsureModServices(); EnsureHarmonyPatched(); CreatePersistentRunner(); if (_staticController != null && SkillsSetupProfessionPatch.OnSetup == null) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[EnsureMod] Re-installing RespecController hooks after plugin recreation."); } _staticController.Install(); } } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)("[EnsureMod] Error: " + ex.Message)); } } } private static void EnsureModServices() { if (_staticResetService == null) { _staticResetService = new SkillResetService(Log, () => IsDebugLoggingEnabled); } if (_staticCostService == null) { _staticCostService = new CostService(Log, _staticConfig); } if (_staticController == null) { _staticController = new RespecController(Log, _staticConfig, _staticResetService, _staticCostService); _staticController.Install(); } } private static void EnsureHarmonyPatched() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (_staticHarmony == null) { _staticHarmony = new Harmony("com.azraelgodking.havensrespec"); LocalizationBootstrap.Init("com.azraelgodking.havensrespec", _staticHarmony, Log, Assembly.GetExecutingAssembly()); ModLocalization.LanguageChanged += OnLanguageChanged; _staticHarmony.PatchAll(typeof(SkillsSetupProfessionPatch)); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[Respec] Harmony patches applied."); } } } private static void CreatePersistentRunner() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown if ((Object)(object)_persistentRunner != (Object)null) { if ((Object)(object)_persistentRunnerComponent == (Object)null || !Object.op_Implicit((Object)(object)_persistentRunnerComponent)) { _persistentRunnerComponent = _persistentRunner.AddComponent<RespecPersistentRunner>(); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[PersistentRunner] Reattached component"); } } return; } _persistentRunner = new GameObject("HavensRespec_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _persistentRunnerComponent = _persistentRunner.AddComponent<RespecPersistentRunner>(); ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"[PersistentRunner] Created"); } } private static void TeardownMod(bool fullShutdown) { ModLocalization.Shutdown(); ModLocalization.LanguageChanged -= OnLanguageChanged; if (!fullShutdown) { return; } try { _staticController?.Uninstall(); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("Haven's Respec teardown: Uninstall failed: " + ex.Message)); } } _staticController = null; _staticResetService = null; _staticCostService = null; try { Harmony staticHarmony = _staticHarmony; if (staticHarmony != null) { staticHarmony.UnpatchSelf(); } } catch (Exception ex2) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogWarning((object)("Haven's Respec teardown: UnpatchSelf failed: " + ex2.Message)); } } _staticHarmony = null; } private static void OnLanguageChanged(string _) { _staticController?.RefreshLocalizedUi(); } internal static void TickHotkeys() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0057: 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) if (_staticController == null || _staticConfig == null || TextInputFocusGuard.ShouldDeferModHotkeys(Log)) { return; } KeyCode value = _staticConfig.ResetCurrentTabHotkey.Value; if ((int)value != 0 && Input.GetKeyDown(value)) { ProfessionType? val = _staticController.TryGetActiveProfessionTab(); if (val.HasValue) { _staticController.TryResetCurrentTab(val.Value, bypassConfirm: false); } } KeyCode value2 = _staticConfig.UndoHotkey.Value; if ((int)value2 != 0 && Input.GetKeyDown(value2)) { ProfessionType? val2 = _staticController.TryGetActiveProfessionTab(); if (val2.HasValue) { _staticController.TryUndoCurrentTab(val2.Value); } } } } public class RespecPersistentRunner : MonoBehaviour { private void Update() { Plugin.TickHotkeys(); } 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)."); } } } } internal static class PluginInfo { public const string PLUGIN_GUID = "com.azraelgodking.havensrespec"; public const string PLUGIN_NAME = "Haven's Respec"; public const string PLUGIN_VERSION = "2.1.4"; } } namespace HavensRespec.UI { internal sealed class ConfirmResetDialog : MonoBehaviour { private TextMeshProUGUI _title; private TextMeshProUGUI _body; private TextMeshProUGUI _cancelLabel; private TextMeshProUGUI _confirmLabel; private RectTransform _cardRt; private RectTransform _cancelRt; private RectTransform _confirmRt; private Image _cancelImage; private Image _confirmImage; private Action _onConfirm; private string _pendingTitle; private string _pendingBody; private bool _closeQueued; private bool _shown; private int _acceptClicksFrame; private const float CardWidth = 420f; private const float DefaultCardHeight = 210f; private const float TitleAreaHeight = 52f; private const float ButtonFooterHeight = 72f; private const float MaxCardHeight = 340f; public event Action Dismissed; public static ConfirmResetDialog BuildUnder(Transform parent) { //IL_0018: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("HavensRespec_ConfirmDialog", new Type[1] { typeof(RectTransform) }); RectTransform component = val.GetComponent<RectTransform>(); ((Transform)component).SetParent(parent, false); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; ((Transform)component).localScale = Vector3.one; CanvasGroup obj = val.AddComponent<CanvasGroup>(); obj.alpha = 1f; obj.interactable = true; obj.blocksRaycasts = true; obj.ignoreParentGroups = true; ConfirmResetDialog confirmResetDialog = val.AddComponent<ConfirmResetDialog>(); confirmResetDialog.BuildHierarchy(); confirmResetDialog.Hide(); return confirmResetDialog; } public void Show(string title, string body, Action onConfirm) { _pendingTitle = title ?? ModLocalization.T("respec.dialog.title"); _pendingBody = body ?? string.Empty; ((TMP_Text)_title).text = _pendingTitle; ((TMP_Text)_body).text = _pendingBody; LayoutCardForBody(_pendingBody); _onConfirm = onConfirm; _shown = true; _closeQueued = false; _acceptClicksFrame = Time.frameCount + 1; ((Component)this).gameObject.SetActive(true); ((Component)this).transform.SetAsLastSibling(); Canvas.ForceUpdateCanvases(); } private void Update() { if (!_shown) { return; } if (CancelPressed()) { Close(runConfirm: false); return; } PaintButtons(); if (Time.frameCount >= _acceptClicksFrame && PrimaryDown()) { if (Contains(_confirmRt)) { Close(runConfirm: true); } else if (Contains(_cancelRt) || !Contains(_cardRt)) { Close(runConfirm: false); } } } private void LateUpdate() { if (_shown) { if ((Object)(object)((Component)this).transform.parent != (Object)null && ((Component)this).transform.GetSiblingIndex() != ((Component)this).transform.parent.childCount - 1) { ((Component)this).transform.SetAsLastSibling(); } SuppressTextRaycasts((Graphic)(object)_title); SuppressTextRaycasts((Graphic)(object)_body); } } public void Hide() { bool shown = _shown; _shown = false; _closeQueued = false; _onConfirm = null; _pendingTitle = null; _pendingBody = null; if (shown && (Object)(object)EventSystem.current != (Object)null) { EventSystem.current.SetSelectedGameObject((GameObject)null); } if (shown) { this.Dismissed?.Invoke(); } ((Component)this).gameObject.SetActive(false); } private void Close(bool runConfirm) { if (!_shown || _closeQueued) { return; } _closeQueued = true; Action action = (runConfirm ? _onConfirm : null); _onConfirm = null; Hide(); try { action?.Invoke(); } catch (Exception arg) { Debug.LogError((object)$"[Haven's Respec] dialog action failed: {arg}"); } } private static bool CancelPressed() { if (Input.GetKeyDown((KeyCode)27)) { return true; } try { if (PlayerInput.GetButtonDown("UICancel")) { return true; } if (PlayerInput.GetButtonDown("Cancel")) { return true; } if (PlayerInput.GetButtonDown("Close")) { return true; } } catch (Exception) { } return false; } private static bool PrimaryDown() { try { if (PlayerInput.GetMouseButtonDown(0)) { return true; } } catch (Exception) { } return Input.GetMouseButtonDown(0); } private void PaintButtons() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) bool flag = false; try { flag = PlayerInput.GetMouseButton(0); } catch (Exception) { flag = Input.GetMouseButton(0); } Paint(_confirmImage, Contains(_confirmRt), flag, RespecStyle.Danger, RespecStyle.DangerHover, RespecStyle.DangerPressed); Paint(_cancelImage, Contains(_cancelRt), flag, RespecStyle.Neutral, RespecStyle.NeutralHover, RespecStyle.NeutralPressed); } private static void Paint(Image image, bool over, bool held, Color normal, Color hover, Color pressed) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)image == (Object)null)) { ((Graphic)image).color = ((!over) ? normal : (held ? pressed : hover)); } } private static bool Contains(RectTransform rt) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)rt == (Object)null || !((Component)rt).gameObject.activeInHierarchy) { return false; } Vector2 val = Vector2.op_Implicit(Input.mousePosition); try { if (MouseVisualManager.UsingController) { val = Vector2.op_Implicit(MouseVisualManager.mousePosition); } } catch (Exception) { val = Vector2.op_Implicit(Input.mousePosition); } Canvas componentInParent = ((Component)rt).GetComponentInParent<Canvas>(); Camera val2 = null; if ((Object)(object)componentInParent != (Object)null && (int)componentInParent.renderMode != 0) { val2 = componentInParent.worldCamera; } if (RectTransformUtility.RectangleContainsScreenPoint(rt, val, val2)) { return true; } if ((Object)(object)val2 == (Object)null && (Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.worldCamera != (Object)null) { return RectTransformUtility.RectangleContainsScreenPoint(rt, val, componentInParent.worldCamera); } if ((Object)(object)val2 != (Object)null) { return RectTransformUtility.RectangleContainsScreenPoint(rt, val, (Camera)null); } return false; } private static void SuppressTextRaycasts(Graphic graphic) { if (!((Object)(object)graphic == (Object)null)) { graphic.raycastTarget = false; Graphic[] componentsInChildren = ((Component)graphic).GetComponentsInChildren<Graphic>(true); for (int i = 0; i < componentsInChildren.Length; i++) { componentsInChildren[i].raycastTarget = false; } } } public void RefreshLocalizedLabels() { if ((Object)(object)_title != (Object)null && ((Component)this).gameObject.activeSelf && !string.IsNullOrEmpty(_pendingTitle)) { ((TMP_Text)_title).text = _pendingTitle; } if ((Object)(object)_body != (Object)null && ((Component)this).gameObject.activeSelf) { ((TMP_Text)_body).text = _pendingBody ?? string.Empty; LayoutCardForBody(_pendingBody ?? string.Empty); } if ((Object)(object)_cancelLabel != (Object)null) { ((TMP_Text)_cancelLabel).text = ModLocalization.T("respec.dialog.cancel"); } if ((Object)(object)_confirmLabel != (Object)null) { ((TMP_Text)_confirmLabel).text = ModLocalization.T("respec.dialog.confirm"); } } private void BuildHierarchy() { //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_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Expected O, but got Unknown //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0264: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Expected O, but got Unknown //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_031d: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_03b3: Unknown result type (might be due to invalid IL or missing references) //IL_03f3: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: 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_0433: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Scrim"); val.transform.SetParent(((Component)this).transform, false); RectTransform obj = val.AddComponent<RectTransform>(); obj.anchorMin = Vector2.zero; obj.anchorMax = Vector2.one; obj.offsetMin = Vector2.zero; obj.offsetMax = Vector2.zero; Image obj2 = val.AddComponent<Image>(); obj2.sprite = RespecStyle.Solid(); ((Graphic)obj2).color = new Color(0f, 0f, 0f, 0.55f); ((Graphic)obj2).raycastTarget = true; GameObject val2 = new GameObject("Card"); val2.transform.SetParent(((Component)this).transform, false); RectTransform val3 = val2.AddComponent<RectTransform>(); val3.anchorMin = new Vector2(0.5f, 0.5f); val3.anchorMax = new Vector2(0.5f, 0.5f); val3.pivot = new Vector2(0.5f, 0.5f); val3.sizeDelta = new Vector2(420f, 210f); _cardRt = val3; Image obj3 = val2.AddComponent<Image>(); obj3.sprite = RespecStyle.SolidRounded(RespecStyle.Wood, RespecStyle.WoodShadow, 28, 3, 8); obj3.type = (Type)1; ((Graphic)obj3).raycastTarget = false; GameObject val4 = new GameObject("Fill"); val4.transform.SetParent(val2.transform, false); RectTransform obj4 = val4.AddComponent<RectTransform>(); obj4.anchorMin = Vector2.zero; obj4.anchorMax = Vector2.one; obj4.offsetMin = new Vector2(4f, 4f); obj4.offsetMax = new Vector2(-4f, -4f); Image obj5 = val4.AddComponent<Image>(); obj5.sprite = RespecStyle.SolidRounded(new Color(0.08f, 0.06f, 0.05f, 0.95f), new Color(0.08f, 0.06f, 0.05f, 0.95f), 22, 0); obj5.type = (Type)1; ((Graphic)obj5).raycastTarget = true; GameObject val5 = new GameObject("Title"); val5.transform.SetParent(val4.transform, false); RectTransform obj6 = val5.AddComponent<RectTransform>(); obj6.anchorMin = new Vector2(0f, 1f); obj6.anchorMax = new Vector2(1f, 1f); obj6.pivot = new Vector2(0.5f, 1f); obj6.sizeDelta = new Vector2(0f, 36f); obj6.anchoredPosition = new Vector2(0f, -12f); _title = val5.AddComponent<TextMeshProUGUI>(); ((TMP_Text)_title).alignment = (TextAlignmentOptions)514; ((TMP_Text)_title).fontSize = 20f; ((TMP_Text)_title).fontStyle = (FontStyles)1; ((Graphic)_title).color = RespecStyle.AccentGold; ((TMP_Text)_title).text = ModLocalization.T("respec.dialog.title"); ((Graphic)_title).raycastTarget = false; GameObject val6 = new GameObject("Body"); val6.transform.SetParent(val4.transform, false); RectTransform obj7 = val6.AddComponent<RectTransform>(); obj7.anchorMin = new Vector2(0f, 0f); obj7.anchorMax = new Vector2(1f, 1f); obj7.offsetMin = new Vector2(18f, 72f); obj7.offsetMax = new Vector2(-18f, -52f); _body = val6.AddComponent<TextMeshProUGUI>(); ((TMP_Text)_body).alignment = (TextAlignmentOptions)257; ((TMP_Text)_body).fontSize = 15f; ((TMP_Text)_body).lineSpacing = 2f; ((TMP_Text)_body).enableWordWrapping = true; ((Graphic)_body).color = new Color(0.95f, 0.92f, 0.83f, 1f); ((TMP_Text)_body).text = string.Empty; ((Graphic)_body).raycastTarget = false; BuildButton(val4.transform, ModLocalization.T("respec.dialog.cancel"), new Vector2(-14f, 14f), RespecStyle.Neutral, out _cancelRt, out _cancelImage, out _cancelLabel); BuildButton(val4.transform, ModLocalization.T("respec.dialog.confirm"), new Vector2(-134f, 14f), RespecStyle.Danger, out _confirmRt, out _confirmImage, out _confirmLabel); } private void LayoutCardForBody(string bodyText) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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) if (!((Object)(object)_cardRt == (Object)null) && !((Object)(object)_body == (Object)null)) { float num = 384f; Vector2 preferredValues = ((TMP_Text)_body).GetPreferredValues(bodyText ?? string.Empty, num, 0f); float num2 = Mathf.Clamp(52f + preferredValues.y + 72f + 12f, 210f, 340f); _cardRt.sizeDelta = new Vector2(420f, num2); } } private static void BuildButton(Transform parent, string label, Vector2 anchoredPos, Color normal, out RectTransform rect, out Image image, out TextMeshProUGUI labelTmp) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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_007a: 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_00a9: 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_00dc: 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_00f3: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_011a: 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) GameObject val = new GameObject("Btn_" + label); val.transform.SetParent(parent, false); RectTransform val2 = val.AddComponent<RectTransform>(); val2.anchorMin = new Vector2(1f, 0f); val2.anchorMax = new Vector2(1f, 0f); val2.pivot = new Vector2(1f, 0f); val2.sizeDelta = new Vector2(110f, 36f); val2.anchoredPosition = anchoredPos; rect = val2; image = val.AddComponent<Image>(); image.sprite = RespecStyle.SolidRounded(Color.white, new Color(0f, 0f, 0f, 0.45f), 20, 1, 5); image.type = (Type)1; ((Graphic)image).color = normal; ((Graphic)image).raycastTarget = true; GameObject val3 = new GameObject("Text"); val3.transform.SetParent(val.transform, false); RectTransform obj = val3.AddComponent<RectTransform>(); obj.anchorMin = Vector2.zero; obj.anchorMax = Vector2.one; obj.offsetMin = Vector2.zero; obj.offsetMax = Vector2.zero; TextMeshProUGUI val4 = val3.AddComponent<TextMeshProUGUI>(); ((TMP_Text)val4).alignment = (TextAlignmentOptions)514; ((TMP_Text)val4).fontSize = 16f; ((TMP_Text)val4).fontStyle = (FontStyles)1; ((Graphic)val4).color = Color.white; ((TMP_Text)val4).text = label; ((Graphic)val4).raycastTarget = false; labelTmp = val4; } } internal sealed class RespecButtonInjector { private sealed class HoverTint : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler { public Image Fill; public RectTransform Root; public Color Normal; public Color Hover; public Color Pressed; private const float HoverScale = 1.04f; private const float PressedScale = 0.97f; private bool _isOver; public void OnPointerEnter(PointerEventData _) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) _isOver = true; Apply(Hover, 1.04f); } public void OnPointerExit(PointerEventData _) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) _isOver = false; Apply(Normal, 1f); } public void OnPointerDown(PointerEventData _) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) Apply(Pressed, 0.97f); } public void OnPointerUp(PointerEventData _) { //IL_0012: 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) Apply(_isOver ? Hover : Normal, _isOver ? 1.04f : 1f); } private void Apply(Color fill, float scale) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Fill != (Object)null) { ((Graphic)Fill).color = fill; } if ((Object)(object)Root != (Object)null) { ((Transform)Root).localScale = new Vector3(scale, scale, 1f); } } } private readonly ManualLogSource _log; private static FieldInfo _skillPointsTmpField; private TextMeshProUGUI _resetLabel; private TextMeshProUGUI _resetAllLabel; private TextMeshProUGUI _undoLabel; private const float ButtonWidth = 96f; private const float ResetAllButtonWidth = 118f; private const float ButtonHeight = 26f; private const float FirstRowCenterOffset = 142f; private const float RowSpacing = 35f; private const float ResetAllExtraYOffset = 6f; private const float HorizontalOffset = -18f; private static Sprite _plaqueFillSprite; private static Sprite _plaqueBorderSprite; private static Sprite _plaqueShadowSprite; public GameObject ResetButton { get; private set; } public GameObject ResetAllButton { get; private set; } public GameObject UndoButton { get; private set; } public event Action OnResetClicked; public event Action OnResetAllClicked; public event Action OnUndoClicked; public RespecButtonInjector(ManualLogSource log) { _log = log; } public bool TryAttach(SkillTree panel, bool enableResetAll) { //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)panel == (Object)null) { return false; } try { if (_skillPointsTmpField == null) { _skillPointsTmpField = typeof(SkillTree).GetField("_skillPointsTMP", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); } object? obj = _skillPointsTmpField?.GetValue(panel); TextMeshProUGUI val = (TextMeshProUGUI)((obj is TextMeshProUGUI) ? obj : null); if ((Object)(object)val == (Object)null) { ManualLogSource log = _log; if (log != null) { log.LogWarning((object)"[Respec] _skillPointsTMP not found on SkillTree; cannot attach button."); } return false; } Transform parent = ((TMP_Text)val).transform.parent; RectTransform component = ((Component)val).GetComponent<RectTransform>(); int rowIndex = 0; ResetButton = BuildButton(parent, component, rowIndex++, ModLocalization.T("respec.button.reset"), 96f, RespecStyle.Danger, RespecStyle.DangerHover, RespecStyle.DangerPressed, delegate { this.OnResetClicked?.Invoke(); }, out _resetLabel); if (enableResetAll) { ResetAllButton = BuildButton(parent, component, rowIndex++, ModLocalization.T("respec.button.reset_all"), 118f, RespecStyle.Danger, RespecStyle.DangerHover, RespecStyle.DangerPressed, delegate { this.OnResetAllClicked?.Invoke(); }, out _resetAllLabel, 6f); } UndoButton = BuildButton(parent, component, rowIndex, ModLocalization.T("respec.button.undo"), 96f, RespecStyle.Neutral, RespecStyle.NeutralHover, RespecStyle.NeutralPressed, delegate { this.OnUndoClicked?.Invoke(); }, out _undoLabel); UndoButton.SetActive(false); return true; } catch (Exception arg) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogError((object)$"[Respec] RespecButtonInjector.TryAttach failed: {arg}"); } return false; } } public void SetUndoVisible(bool visible) { if ((Object)(object)UndoButton != (Object)null) { UndoButton.SetActive(visible); } } public void RefreshLocalizedLabels() { if ((Object)(object)_resetLabel != (Object)null) { ((TMP_Text)_resetLabel).text = ModLocalization.T("respec.button.reset").ToUpperInvariant(); } if ((Object)(object)_resetAllLabel != (Object)null) { ((TMP_Text)_resetAllLabel).text = ModLocalization.T("respec.button.reset_all").ToUpperInvariant(); } if ((Object)(object)_undoLabel != (Object)null) { ((TMP_Text)_undoLabel).text = ModLocalization.T("respec.button.undo").ToUpperInvariant(); } } public void Destroy() { if ((Object)(object)ResetButton != (Object)null) { Object.Destroy((Object)(object)ResetButton); } if ((Object)(object)ResetAllButton != (Object)null) { Object.Destroy((Object)(object)ResetAllButton); } if ((Object)(object)UndoButton != (Object)null) { Object.Destroy((Object)(object)UndoButton); } ResetButton = null; ResetAllButton = null; UndoButton = null; } private static void EnsureSprites() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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) if ((Object)(object)_plaqueFillSprite == (Object)null) { _plaqueFillSprite = RespecStyle.SolidRounded(Color.white, new Color(0f, 0f, 0f, 0f), 32, 0, 8); } if ((Object)(object)_plaqueBorderSprite == (Object)null) { _plaqueBorderSprite = RespecStyle.SolidRounded(new Color(0f, 0f, 0f, 0f), RespecStyle.AccentGold, 32, 2, 8); } if ((Object)(object)_plaqueShadowSprite == (Object)null) { _plaqueShadowSprite = RespecStyle.SolidRounded(Color.white, new Color(0f, 0f, 0f, 0f), 32, 0, 9); } } private static GameObject BuildButton(Transform parent, RectTransform anchorRt, int rowIndex, string label, float buttonWidth, Color normal, Color hover, Color pressed, Action onClick, out TextMeshProUGUI labelTmp, float extraYOffset = 0f) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_0143: 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_016c: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_0260: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Expected O, but got Unknown //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02d0: Unknown result