using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using SunhavenMods.Shared; using TheVault.Api; using TheVault.Integration; using TheVault.Modding; using TheVault.Patches; using TheVault.UI; using TheVault.Vault; 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("TheVault")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("3.3.2.0")] [assembly: AssemblyInformationalVersion("3.3.2+5c08b5aa5d0be9c4b93df77f697dc55d5ac97088")] [assembly: AssemblyProduct("TheVault")] [assembly: AssemblyTitle("TheVault")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("3.3.2.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SunhavenMods.Shared { public static class ConfigFileHelper { public static ConfigFile CreateNamedConfig(string pluginGuid, string configFileName, Action logWarning = null) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown string text = Path.Combine(Paths.ConfigPath, configFileName); string text2 = Path.Combine(Paths.ConfigPath, pluginGuid + ".cfg"); try { if (!File.Exists(text) && File.Exists(text2)) { File.Copy(text2, text); } } catch (Exception ex) { logWarning?.Invoke("[Config] Migration to " + configFileName + " failed: " + ex.Message); } return new ConfigFile(text, true); } public static bool ReplacePluginConfig(BaseUnityPlugin plugin, ConfigFile newConfig, Action logWarning = null) { if ((Object)(object)plugin == (Object)null || newConfig == null) { return false; } try { Type typeFromHandle = typeof(BaseUnityPlugin); PropertyInfo property = typeFromHandle.GetProperty("Config", BindingFlags.Instance | BindingFlags.Public); if (property != null && property.CanWrite) { property.SetValue(plugin, newConfig, null); return true; } FieldInfo field = typeFromHandle.GetField("k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic); if (field != null) { field.SetValue(plugin, newConfig); return true; } FieldInfo[] fields = typeFromHandle.GetFields(BindingFlags.Instance | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(ConfigFile)) { fieldInfo.SetValue(plugin, newConfig); return true; } } } catch (Exception ex) { logWarning?.Invoke("[Config] ReplacePluginConfig failed: " + ex.Message); } return false; } } public static class VersionChecker { public class VersionCheckResult { public bool Success { get; set; } public bool UpdateAvailable { get; set; } public string CurrentVersion { get; set; } public string LatestVersion { get; set; } public string ModName { get; set; } public string NexusUrl { get; set; } public string Changelog { get; set; } public string ErrorMessage { get; set; } } public class ModHealthSnapshot { public string PluginGuid { get; set; } public DateTime LastCheckUtc { get; set; } public int ExceptionCount { get; set; } public string LastError { get; set; } } private class VersionCheckRunner : MonoBehaviour { private ManualLogSource _pluginLog; public void StartCheck(string pluginGuid, string currentVersion, ManualLogSource pluginLog, Action onComplete) { _pluginLog = pluginLog; ((MonoBehaviour)this).StartCoroutine(CheckVersionCoroutine(pluginGuid, currentVersion, onComplete)); } private void LogInfo(string message) { ManualLogSource pluginLog = _pluginLog; if (pluginLog != null) { pluginLog.LogInfo((object)("[VersionChecker] " + message)); } } private void LogWarningMsg(string message) { ManualLogSource pluginLog = _pluginLog; if (pluginLog != null) { pluginLog.LogWarning((object)("[VersionChecker] " + message)); } } private void LogErrorMsg(string message) { ManualLogSource pluginLog = _pluginLog; if (pluginLog != null) { pluginLog.LogError((object)("[VersionChecker] " + message)); } } private IEnumerator CheckVersionCoroutine(string pluginGuid, string currentVersion, Action onComplete) { VersionCheckResult result = new VersionCheckResult { CurrentVersion = currentVersion }; UnityWebRequest www = UnityWebRequest.Get("https://azraelgodking.github.io/SunhavenMod/versions.json"); try { www.timeout = 10; yield return www.SendWebRequest(); if ((int)www.result == 2 || (int)www.result == 3) { result.Success = false; result.ErrorMessage = "Network error: " + www.error; RecordHealthError(pluginGuid, result.ErrorMessage); LogWarningMsg(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } try { string text = www.downloadHandler.text; Match match = GetModPattern(pluginGuid).Match(text); if (!match.Success) { result.Success = false; result.ErrorMessage = "Mod '" + pluginGuid + "' not found in versions.json"; RecordHealthError(pluginGuid, result.ErrorMessage); LogWarningMsg(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } string value = match.Groups[1].Value; result.LatestVersion = ExtractJsonString(value, "version"); result.ModName = ExtractJsonString(value, "name"); result.NexusUrl = ExtractJsonString(value, "nexus"); result.Changelog = ExtractJsonString(value, "changelog"); if (string.IsNullOrEmpty(result.LatestVersion)) { result.Success = false; result.ErrorMessage = "Could not parse version from response"; RecordHealthError(pluginGuid, result.ErrorMessage); LogWarningMsg(result.ErrorMessage); onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); yield break; } result.Success = true; result.UpdateAvailable = CompareVersions(currentVersion, result.LatestVersion) < 0; if (result.UpdateAvailable) { LogInfo("Update available for " + result.ModName + ": " + currentVersion + " -> " + result.LatestVersion); } else { LogInfo(result.ModName + " is up to date (v" + currentVersion + ")"); } } catch (Exception ex) { result.Success = false; result.ErrorMessage = "Parse error: " + ex.Message; RecordHealthError(pluginGuid, result.ErrorMessage); LogErrorMsg(result.ErrorMessage); } } finally { ((IDisposable)www)?.Dispose(); } onComplete?.Invoke(result); Object.Destroy((Object)(object)((Component)this).gameObject); } private string ExtractJsonString(string json, string key) { Match match = ExtractFieldRegex.Match(json); while (match.Success) { if (string.Equals(match.Groups["key"].Value, key, StringComparison.Ordinal)) { return match.Groups["value"].Value; } match = match.NextMatch(); } return null; } } private const string VersionsUrl = "https://azraelgodking.github.io/SunhavenMod/versions.json"; private static readonly Dictionary HealthByPluginGuid = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly object HealthLock = new object(); private static readonly Dictionary ModPatternCache = new Dictionary(StringComparer.Ordinal); private static readonly object ModPatternCacheLock = new object(); private static readonly Regex ExtractFieldRegex = new Regex("\"(?[^\"]+)\"\\s*:\\s*(?:\"(?[^\"]*)\"|null)", RegexOptions.Compiled); public static void CheckForUpdate(string pluginGuid, string currentVersion, ManualLogSource logger = null, Action onComplete = null) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) TouchHealth(pluginGuid); VersionCheckRunner versionCheckRunner = new GameObject("VersionChecker").AddComponent(); Object.DontDestroyOnLoad((Object)(object)((Component)versionCheckRunner).gameObject); SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(((Component)versionCheckRunner).gameObject); versionCheckRunner.StartCheck(pluginGuid, currentVersion, logger, onComplete); } public static ModHealthSnapshot GetHealthSnapshot(string pluginGuid) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return null; } lock (HealthLock) { if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value)) { return null; } return new ModHealthSnapshot { PluginGuid = value.PluginGuid, LastCheckUtc = value.LastCheckUtc, ExceptionCount = value.ExceptionCount, LastError = value.LastError }; } } public static int CompareVersions(string v1, string v2) { if (string.IsNullOrEmpty(v1) || string.IsNullOrEmpty(v2)) { return 0; } v1 = v1.TrimStart('v', 'V'); v2 = v2.TrimStart('v', 'V'); int num = v1.IndexOfAny(new char[2] { '-', '+' }); if (num >= 0) { v1 = v1.Substring(0, num); } int num2 = v2.IndexOfAny(new char[2] { '-', '+' }); if (num2 >= 0) { v2 = v2.Substring(0, num2); } string[] array = v1.Split(new char[1] { '.' }); string[] array2 = v2.Split(new char[1] { '.' }); int num3 = Math.Max(array.Length, array2.Length); for (int i = 0; i < num3; i++) { int result; int num4 = ((i < array.Length && int.TryParse(array[i], out result)) ? result : 0); int result2; int num5 = ((i < array2.Length && int.TryParse(array2[i], out result2)) ? result2 : 0); if (num4 < num5) { return -1; } if (num4 > num5) { return 1; } } return 0; } private static void TouchHealth(string pluginGuid) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return; } lock (HealthLock) { if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value)) { value = new ModHealthSnapshot { PluginGuid = pluginGuid }; HealthByPluginGuid[pluginGuid] = value; } value.LastCheckUtc = DateTime.UtcNow; } } private static void RecordHealthError(string pluginGuid, string errorMessage) { if (string.IsNullOrWhiteSpace(pluginGuid)) { return; } lock (HealthLock) { if (!HealthByPluginGuid.TryGetValue(pluginGuid, out ModHealthSnapshot value)) { value = new ModHealthSnapshot { PluginGuid = pluginGuid }; HealthByPluginGuid[pluginGuid] = value; } value.LastCheckUtc = DateTime.UtcNow; value.ExceptionCount++; value.LastError = errorMessage; } } private static Regex GetModPattern(string pluginGuid) { lock (ModPatternCacheLock) { if (!ModPatternCache.TryGetValue(pluginGuid, out Regex value)) { value = new Regex("\"" + Regex.Escape(pluginGuid) + "\"\\s*:\\s*\\{([^}]+)\\}", RegexOptions.Compiled | RegexOptions.Singleline); ModPatternCache[pluginGuid] = value; } return value; } } } public static class VersionCheckerExtensions { public static void NotifyUpdateAvailable(this VersionChecker.VersionCheckResult result, ManualLogSource logger = null) { if (!result.UpdateAvailable) { return; } string text = result.ModName + " update available: v" + result.LatestVersion; try { Type type = ReflectionHelper.FindWishType("NotificationStack"); if (type != null) { Type type2 = ReflectionHelper.FindType("SingletonBehaviour`1", "Wish"); if (type2 != null) { object obj = type2.MakeGenericType(type).GetProperty("Instance")?.GetValue(null); if (obj != null) { MethodInfo method = type.GetMethod("SendNotification", new Type[5] { typeof(string), typeof(int), typeof(int), typeof(bool), typeof(bool) }); if (method != null) { method.Invoke(obj, new object[5] { text, 0, 1, false, true }); return; } } } } } catch (Exception ex) { if (logger != null) { logger.LogWarning((object)("Failed to send native notification: " + ex.Message)); } } if (logger != null) { logger.LogWarning((object)("[UPDATE AVAILABLE] " + text)); } if (!string.IsNullOrEmpty(result.NexusUrl) && logger != null) { logger.LogWarning((object)("Download at: " + result.NexusUrl)); } } } public static class SceneRootSurvivor { private static readonly object Lock = new object(); private static readonly List NoKillSubstrings = new List(); private static Harmony _harmony; public static void TryRegisterPersistentRunnerGameObject(GameObject go) { if (!((Object)(object)go == (Object)null)) { TryAddNoKillListSubstring(((Object)go).name); } } public static void TryAddNoKillListSubstring(string nameSubstring) { if (string.IsNullOrEmpty(nameSubstring)) { return; } lock (Lock) { bool flag = false; for (int i = 0; i < NoKillSubstrings.Count; i++) { if (string.Equals(NoKillSubstrings[i], nameSubstring, StringComparison.OrdinalIgnoreCase)) { flag = true; break; } } if (!flag) { NoKillSubstrings.Add(nameSubstring); } } EnsurePatched(); } private static void EnsurePatched() { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00a3: Expected O, but got Unknown if (_harmony != null) { return; } lock (Lock) { if (_harmony == null) { MethodInfo methodInfo = AccessTools.Method(typeof(Scene), "GetRootGameObjects", Type.EmptyTypes, (Type[])null); if (!(methodInfo == null)) { string text = typeof(SceneRootSurvivor).Assembly.GetName().Name ?? "Unknown"; Harmony val = new Harmony("SunhavenMods.SceneRootSurvivor." + text); val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SceneRootSurvivor), "OnGetRootGameObjectsPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony = val; } } } } private static void OnGetRootGameObjectsPostfix(ref GameObject[] __result) { if (__result == null || __result.Length == 0) { return; } List list; lock (Lock) { if (NoKillSubstrings.Count == 0) { return; } list = new List(NoKillSubstrings); } List list2 = new List(__result); for (int i = 0; i < list.Count; i++) { string noKill = list[i]; list2.RemoveAll((GameObject a) => (Object)(object)a != (Object)null && ((Object)a).name.IndexOf(noKill, StringComparison.OrdinalIgnoreCase) >= 0); } __result = list2.ToArray(); } } public static class ReflectionHelper { public static readonly BindingFlags AllBindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; public static Type FindType(string typeName, params string[] namespaces) { string typeName2 = typeName; Type type = AccessTools.TypeByName(typeName2); if (type != null) { return type; } for (int i = 0; i < namespaces.Length; i++) { type = AccessTools.TypeByName(namespaces[i] + "." + typeName2); if (type != null) { return type; } } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == typeName2 || t.FullName == typeName2); if (type != null) { return type; } } catch (ReflectionTypeLoadException) { } } return null; } public static Type FindWishType(string typeName) { return FindType(typeName, "Wish"); } public static object GetStaticValue(Type type, string memberName) { if (type == null) { return null; } try { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.GetMethod != null && property.GetIndexParameters().Length == 0) { return property.GetValue(null); } } catch (AmbiguousMatchException) { return null; } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { return field.GetValue(null); } return null; } public static object GetSingletonInstance(Type type) { if (type == null) { return null; } string[] array = new string[5] { "Instance", "instance", "_instance", "Singleton", "singleton" }; foreach (string memberName in array) { object staticValue = GetStaticValue(type, memberName); if (staticValue != null) { return staticValue; } } return null; } public static object GetInstanceValue(object instance, string memberName) { if (instance == null) { return null; } Type type = instance.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.GetMethod != null) { return property.GetValue(instance); } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { return field.GetValue(instance); } type = type.BaseType; } return null; } public static bool SetInstanceValue(object instance, string memberName, object value) { if (instance == null) { return false; } Type type = instance.GetType(); while (type != null) { PropertyInfo property = type.GetProperty(memberName, AllBindingFlags); if (property != null && property.SetMethod != null) { property.SetValue(instance, value); return true; } FieldInfo field = type.GetField(memberName, AllBindingFlags); if (field != null) { field.SetValue(instance, value); return true; } type = type.BaseType; } return false; } public static object InvokeMethod(object instance, string methodName, params object[] args) { if (instance == null) { return null; } Type type = instance.GetType(); Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes; MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null); if (methodInfo == null) { methodInfo = type.GetMethod(methodName, AllBindingFlags); } if (methodInfo == null) { return null; } return methodInfo.Invoke(instance, args); } public static object InvokeStaticMethod(Type type, string methodName, params object[] args) { if (type == null) { return null; } Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes; MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null); if (methodInfo == null) { methodInfo = type.GetMethod(methodName, AllBindingFlags); } if (methodInfo == null) { return null; } return methodInfo.Invoke(null, args); } public static FieldInfo[] GetAllFields(Type type) { if (type == null) { return Array.Empty(); } FieldInfo[] fields = type.GetFields(AllBindingFlags); IEnumerable second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty(); } else { IEnumerable allFields = GetAllFields(type.BaseType); second = allFields; } return fields.Concat(second).Distinct().ToArray(); } public static PropertyInfo[] GetAllProperties(Type type) { if (type == null) { return Array.Empty(); } PropertyInfo[] properties = type.GetProperties(AllBindingFlags); IEnumerable second; if (!(type.BaseType != null) || !(type.BaseType != typeof(object))) { second = Enumerable.Empty(); } else { IEnumerable allProperties = GetAllProperties(type.BaseType); second = allProperties; } return (from p in properties.Concat(second) group p by p.Name into g select g.First()).ToArray(); } public static T TryGetValue(object instance, string memberName, T defaultValue = default(T)) { try { object instanceValue = GetInstanceValue(instance, memberName); if (instanceValue is T result) { return result; } if (instanceValue != null && typeof(T).IsAssignableFrom(instanceValue.GetType())) { return (T)instanceValue; } return defaultValue; } catch { return defaultValue; } } } public static class IconCache { private struct CachedIcon { public Texture2D Texture; public bool OwnsTexture; } private static readonly Dictionary _iconCache = new Dictionary(); private const int MaxCacheSize = 200; private static readonly HashSet _loadingItems = new HashSet(); private static readonly HashSet _failedItems = new HashSet(); private static readonly Dictionary _currencyToItemId = new Dictionary(); private static Texture2D _fallbackTexture; private static ManualLogSource _log; private static Type _databaseType; private static Type _itemDataType; private static MethodInfo _getDataMethod; private static bool _reflectionInitialized; private static bool _initialized; private static bool _iconsLoaded; private static int[] _pendingPreloadItemIds; public static void Initialize(ManualLogSource log, int[] preloadItemIds = null) { _log = log; if (_initialized) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogDebug((object)"[IconCache] Already initialized"); } return; } _initialized = true; ManualLogSource log3 = _log; if (log3 != null) { log3.LogInfo((object)"[IconCache] Initializing icon cache..."); } _fallbackTexture = CreateFallbackTexture(); ManualLogSource log4 = _log; if (log4 != null) { log4.LogInfo((object)"[IconCache] Created fallback texture"); } if (preloadItemIds != null && preloadItemIds.Length != 0) { _pendingPreloadItemIds = (int[])preloadItemIds.Clone(); ManualLogSource log5 = _log; if (log5 != null) { log5.LogInfo((object)$"[IconCache] Preload: {_pendingPreloadItemIds.Length} item ID(s) will queue with LoadAllIcons"); } } } public static void RegisterCurrency(string currencyId, int itemId) { _currencyToItemId[currencyId] = itemId; } public static void LoadAllIcons() { if (_iconsLoaded) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)"[IconCache] Icons already loaded, skipping"); } return; } if (!InitializeReflection()) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogError((object)"[IconCache] Failed to initialize reflection, cannot load icons"); } _iconsLoaded = true; return; } foreach (KeyValuePair item in _currencyToItemId) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogDebug((object)$"[IconCache] Queuing load for: {item.Key} (ItemID: {item.Value})"); } LoadIcon(item.Value); } if (_pendingPreloadItemIds != null) { int[] pendingPreloadItemIds = _pendingPreloadItemIds; foreach (int num in pendingPreloadItemIds) { if (num > 0) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogDebug((object)$"[IconCache] Queuing preload item ID: {num}"); } LoadIcon(num); } } } _iconsLoaded = true; ManualLogSource log5 = _log; if (log5 != null) { log5.LogInfo((object)$"[IconCache] Queued {_currencyToItemId.Count} currency icon(s); preload queue processed."); } } public static Texture2D GetIconForCurrency(string currencyId) { if (_currencyToItemId.TryGetValue(currencyId, out var value)) { return GetIcon(value); } return GetFallbackTexture(); } public static Texture2D GetIcon(int itemId) { if (itemId <= 0) { return GetFallbackTexture(); } if (_iconCache.TryGetValue(itemId, out var value)) { return value.Texture; } if (!_loadingItems.Contains(itemId) && !_failedItems.Contains(itemId)) { LoadIcon(itemId); } return GetFallbackTexture(); } private static Texture2D GetFallbackTexture() { if ((Object)(object)_fallbackTexture == (Object)null) { _fallbackTexture = CreateFallbackTexture(); } return _fallbackTexture; } public static bool IsIconLoaded(int itemId) { return _iconCache.ContainsKey(itemId); } public static bool IsIconLoaded(string currencyId) { if (_currencyToItemId.TryGetValue(currencyId, out var value)) { return IsIconLoaded(value); } return false; } public static int GetItemIdForCurrency(string currencyId) { if (!_currencyToItemId.TryGetValue(currencyId, out var value)) { return -1; } return value; } private static bool InitializeReflection() { if (_reflectionInitialized) { if (_databaseType != null && _itemDataType != null) { return _getDataMethod != null; } return false; } _reflectionInitialized = true; try { string[] array = new string[4] { "Database", "Wish.Database", "PSS.Database", "SunHaven.Database" }; for (int i = 0; i < array.Length; i++) { _databaseType = AccessTools.TypeByName(array[i]); if (_databaseType != null) { ManualLogSource log = _log; if (log != null) { log.LogInfo((object)("[IconCache] Found Database type: " + _databaseType.FullName)); } break; } } MethodInfo[] methods; if (_databaseType == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type[] types = assembly.GetTypes(); foreach (Type type in types) { if (!(type.Name == "Database") || type.IsNested) { continue; } methods = type.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo in methods) { if (methodInfo.Name == "GetData" && methodInfo.IsGenericMethod) { _databaseType = type; ManualLogSource log2 = _log; if (log2 != null) { log2.LogInfo((object)("[IconCache] Found Database type: " + type.FullName)); } break; } } if (_databaseType != null) { break; } } if (_databaseType != null) { break; } } catch (Exception ex) { ManualLogSource log3 = _log; if (log3 != null) { log3.LogDebug((object)("[IconCache] Skipping assembly " + assembly.GetName().Name + ": " + ex.Message)); } } } } if (_databaseType == null) { ManualLogSource log4 = _log; if (log4 != null) { log4.LogError((object)"[IconCache] Could not find Database type"); } return false; } _itemDataType = AccessTools.TypeByName("Wish.ItemData"); if (_itemDataType == null) { ManualLogSource log5 = _log; if (log5 != null) { log5.LogError((object)"[IconCache] Could not find Wish.ItemData type"); } return false; } methods = _databaseType.GetMethods(BindingFlags.Static | BindingFlags.Public); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name == "GetData") || !methodInfo2.IsGenericMethod) { continue; } ParameterInfo[] parameters = methodInfo2.GetParameters(); if (methodInfo2.GetGenericArguments().Length == 1 && parameters.Length == 3 && parameters[0].ParameterType == typeof(int)) { _getDataMethod = methodInfo2.MakeGenericMethod(_itemDataType); ManualLogSource log6 = _log; if (log6 != null) { log6.LogInfo((object)"[IconCache] Found Database.GetData method"); } break; } } if (_getDataMethod == null) { ManualLogSource log7 = _log; if (log7 != null) { log7.LogError((object)"[IconCache] Could not find Database.GetData method"); } return false; } return true; } catch (Exception ex2) { ManualLogSource log8 = _log; if (log8 != null) { log8.LogError((object)("[IconCache] Error initializing reflection: " + ex2.Message)); } return false; } } private static void LoadIcon(int itemId) { if (itemId <= 0 || _loadingItems.Contains(itemId) || _iconCache.ContainsKey(itemId)) { return; } _loadingItems.Add(itemId); try { if (!InitializeReflection() || _getDataMethod == null) { _failedItems.Add(itemId); _loadingItems.Remove(itemId); return; } Type delegateType = typeof(Action<>).MakeGenericType(_itemDataType); ParameterExpression parameterExpression = Expression.Parameter(_itemDataType, "itemData"); ConstantExpression arg = Expression.Constant(itemId); MethodCallExpression body = Expression.Call(typeof(IconCache).GetMethod("OnIconLoadedInternal", BindingFlags.Static | BindingFlags.NonPublic), arg, Expression.Convert(parameterExpression, typeof(object))); Delegate @delegate = Expression.Lambda(delegateType, body, parameterExpression).Compile(); Action action = Expression.Lambda(Expression.Call(typeof(IconCache).GetMethod("OnIconLoadFailed", BindingFlags.Static | BindingFlags.NonPublic), arg), Array.Empty()).Compile(); _getDataMethod.Invoke(null, new object[3] { itemId, @delegate, action }); } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)$"[IconCache] Error loading icon {itemId}: {ex.Message}"); } _failedItems.Add(itemId); _loadingItems.Remove(itemId); } } private static void OnIconLoadedInternal(int itemId, object itemData) { _loadingItems.Remove(itemId); if (itemData == null) { _failedItems.Add(itemId); return; } try { Type type = itemData.GetType(); object obj = null; BindingFlags[] array = new BindingFlags[4] { BindingFlags.Instance | BindingFlags.Public, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy, BindingFlags.Instance | BindingFlags.NonPublic, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy }; BindingFlags[] array2 = array; foreach (BindingFlags bindingAttr in array2) { PropertyInfo property = type.GetProperty("icon", bindingAttr); if (property != null) { obj = property.GetValue(itemData); break; } } if (obj == null) { array2 = array; foreach (BindingFlags bindingAttr2 in array2) { FieldInfo field = type.GetField("icon", bindingAttr2); if (field != null) { obj = field.GetValue(itemData); break; } } } if (obj == null) { Type type2 = type; while (type2 != null && obj == null) { PropertyInfo[] properties = type2.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.Name.Equals("icon", StringComparison.OrdinalIgnoreCase)) { obj = propertyInfo.GetValue(itemData); break; } } if (obj == null) { FieldInfo[] fields = type2.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.Name.Equals("icon", StringComparison.OrdinalIgnoreCase)) { obj = fieldInfo.GetValue(itemData); break; } } } type2 = type2.BaseType; } } Sprite val = (Sprite)((obj is Sprite) ? obj : null); if (val != null) { CacheSprite(itemId, val); } else { _failedItems.Add(itemId); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)$"[IconCache] Error processing icon {itemId}: {ex.Message}"); } _failedItems.Add(itemId); } } private static void OnIconLoadFailed(int itemId) { _loadingItems.Remove(itemId); _failedItems.Add(itemId); } private static void CacheSprite(int itemId, Sprite sprite) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sprite == (Object)null || (Object)(object)sprite.texture == (Object)null) { _failedItems.Add(itemId); return; } try { Rect rect = sprite.rect; Texture2D val; bool ownsTexture; if (((Rect)(ref rect)).width == (float)((Texture)sprite.texture).width) { rect = sprite.rect; if (((Rect)(ref rect)).height == (float)((Texture)sprite.texture).height) { val = sprite.texture; ownsTexture = false; goto IL_0077; } } val = ExtractSpriteTexture(sprite); ownsTexture = (Object)(object)val != (Object)null; goto IL_0077; IL_0077: if ((Object)(object)val != (Object)null) { _iconCache[itemId] = new CachedIcon { Texture = val, OwnsTexture = ownsTexture }; if (_iconCache.Count <= 200) { return; } int num = -1; int num2 = -1; foreach (int key in _iconCache.Keys) { if (num2 < 0) { num2 = key; } if (!_loadingItems.Contains(key) && !_failedItems.Contains(key)) { num = key; break; } } if (num < 0) { num = num2; } if (num >= 0 && _iconCache.TryGetValue(num, out var value)) { if (value.OwnsTexture && (Object)(object)value.Texture != (Object)null) { Object.Destroy((Object)(object)value.Texture); } _iconCache.Remove(num); } } else { _failedItems.Add(itemId); } } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)$"[IconCache] Error caching sprite {itemId}: {ex.Message}"); } _failedItems.Add(itemId); } } private static Texture2D ExtractSpriteTexture(Sprite sprite) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown try { Rect rect = sprite.rect; int num = (int)((Rect)(ref rect)).width; int num2 = (int)((Rect)(ref rect)).height; if (!((Texture)sprite.texture).isReadable) { return CopyTextureViaRenderTexture(sprite); } Texture2D val = new Texture2D(num, num2, (TextureFormat)4, false); Color[] pixels = sprite.texture.GetPixels((int)((Rect)(ref rect)).x, (int)((Rect)(ref rect)).y, num, num2); val.SetPixels(pixels); val.Apply(); return val; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("[IconCache] Error extracting sprite texture: " + ex.Message)); } return null; } } private static Texture2D CopyTextureViaRenderTexture(Sprite sprite) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown RenderTexture val = null; RenderTexture active = RenderTexture.active; try { Rect rect = sprite.rect; int num = (int)((Rect)(ref rect)).width; int num2 = (int)((Rect)(ref rect)).height; val = RenderTexture.GetTemporary(((Texture)sprite.texture).width, ((Texture)sprite.texture).height, 0, (RenderTextureFormat)0); Graphics.Blit((Texture)(object)sprite.texture, val); RenderTexture.active = val; Texture2D val2 = new Texture2D(num, num2, (TextureFormat)4, false); val2.ReadPixels(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, (float)num, (float)num2), 0, 0); val2.Apply(); return val2; } catch (Exception ex) { ManualLogSource log = _log; if (log != null) { log.LogDebug((object)("[IconCache] Error copying texture via RenderTexture: " + ex.Message)); } return null; } finally { RenderTexture.active = active; if ((Object)(object)val != (Object)null) { RenderTexture.ReleaseTemporary(val); } } } private static Texture2D CreateFallbackTexture() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) int num = 32; Texture2D val = new Texture2D(num, num); Color val2 = default(Color); ((Color)(ref val2))..ctor(0.3f, 0.3f, 0.4f, 0.8f); Color val3 = default(Color); ((Color)(ref val3))..ctor(0.5f, 0.5f, 0.6f, 1f); for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { if (j == 0 || j == num - 1 || i == 0 || i == num - 1) { val.SetPixel(j, i, val3); } else { val.SetPixel(j, i, val2); } } } val.Apply(); return val; } public static void Clear() { foreach (KeyValuePair item in _iconCache) { if (item.Value.OwnsTexture && (Object)(object)item.Value.Texture != (Object)null) { Object.Destroy((Object)(object)item.Value.Texture); } } _iconCache.Clear(); _loadingItems.Clear(); _failedItems.Clear(); _initialized = false; _iconsLoaded = false; _pendingPreloadItemIds = null; } public static (int loaded, int loading, int failed) GetStats() { return (_iconCache.Count, _loadingItems.Count, _failedItems.Count); } public static void LogStatus() { (int, int, int) stats = GetStats(); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)$"[IconCache] Loaded: {stats.Item1}, Loading: {stats.Item2}, Failed: {stats.Item3}"); } } } public static class TextInputFocusGuard { private const float DefaultPollIntervalSeconds = 0.25f; private static float _nextPollTime = -1f; private static bool _cachedDefer; private static bool _tmpTypeLookupDone; private static Type _tmpInputFieldType; private static bool _qcLookupDone; private static Type _qcType; private static PropertyInfo _qcInstanceProp; private static PropertyInfo _qcIsActiveProp; private static FieldInfo _qcIsActiveField; public static bool ShouldDeferModHotkeys(ManualLogSource debugLog = null, float pollIntervalSeconds = 0.25f) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextPollTime) { return _cachedDefer; } _nextPollTime = realtimeSinceStartup + Mathf.Max(0.05f, pollIntervalSeconds); bool flag = false; try { if (GUIUtility.keyboardControl != 0) { flag = true; } if (!flag) { EventSystem current = EventSystem.current; GameObject val = ((current != null) ? current.currentSelectedGameObject : null); if ((Object)(object)val != (Object)null) { if ((Object)(object)val.GetComponent() != (Object)null) { flag = true; } else if (TryGetTmpInputField(val)) { flag = true; } } } if (!flag && IsQuantumConsoleActive(debugLog)) { flag = true; } } catch (Exception ex) { if (debugLog != null) { debugLog.LogDebug((object)("[TextInputFocusGuard] " + ex.Message)); } } _cachedDefer = flag; return flag; } private static bool TryGetTmpInputField(GameObject go) { if (!_tmpTypeLookupDone) { _tmpTypeLookupDone = true; _tmpInputFieldType = AccessTools.TypeByName("TMPro.TMP_InputField"); } if (_tmpInputFieldType == null) { return false; } return (Object)(object)go.GetComponent(_tmpInputFieldType) != (Object)null; } private static bool IsQuantumConsoleActive(ManualLogSource debugLog) { try { if (!_qcLookupDone) { _qcLookupDone = true; _qcType = AccessTools.TypeByName("QFSW.QC.QuantumConsole"); if (_qcType != null) { _qcInstanceProp = AccessTools.Property(_qcType, "Instance"); _qcIsActiveProp = AccessTools.Property(_qcType, "IsActive"); _qcIsActiveField = AccessTools.Field(_qcType, "isActive") ?? AccessTools.Field(_qcType, "_isActive"); } } if (_qcType == null) { return false; } object obj = _qcInstanceProp?.GetValue(null); if (obj == null) { return false; } if (_qcIsActiveProp != null && _qcIsActiveProp.PropertyType == typeof(bool)) { return (bool)_qcIsActiveProp.GetValue(obj); } if (_qcIsActiveField != null && _qcIsActiveField.FieldType == typeof(bool)) { return (bool)_qcIsActiveField.GetValue(obj); } } catch (Exception ex) { if (debugLog != null) { debugLog.LogDebug((object)("[TextInputFocusGuard] Quantum Console focus check failed: " + ex.Message)); } } return false; } } } namespace TheVault { public static class ItemIds { public const int SpringToken = 18020; public const int SummerToken = 18021; public const int WinterToken = 18022; public const int FallToken = 18023; public const int CopperKey = 1251; public const int IronKey = 1252; public const int AdamantKey = 1253; public const int MithrilKey = 1254; public const int SuniteKey = 1255; public const int GloriteKey = 1256; public const int KingsLostMineKey = 1257; public const int CommunityToken = 18013; public const int Doubloon = 60014; public const int BlackBottleCap = 60013; public const int RedCarnivalTicket = 18012; public const int CandyCornPieces = 18016; public const int ManaShard = 18015; } public class PersistentUpdateRunner : MonoBehaviour { private string _lastKnownScene = ""; private bool _wasInMenuScene = true; private float _sceneCheckTimer; private float _heartbeatTimer; private int _heartbeatCount; private const float SCENE_CHECK_INTERVAL = 0.5f; private const float HEARTBEAT_INTERVAL = 30f; private void Awake() { ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[PersistentRunner] Created hidden persistent runner"); } } private void Update() { Plugin.TickAutoSave(); _sceneCheckTimer += Time.deltaTime; if (_sceneCheckTimer >= 0.5f) { _sceneCheckTimer = 0f; CheckForMenuSceneChange(); } _heartbeatTimer += Time.deltaTime; if (_heartbeatTimer >= 30f) { _heartbeatTimer = 0f; _heartbeatCount++; ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)string.Format("[PersistentRunner Heartbeat #{0}] Scene: {1}, VaultLoaded: {2}, Character: {3}", _heartbeatCount, _lastKnownScene, PlayerPatches.IsVaultLoaded, PlayerPatches.LoadedCharacterName ?? "none")); } } CheckHotkeys(); if (PlayerPatches.IsVaultLoaded) { ItemPatches.DrainAutoDepositNotifications(); } PlayerPatches.TrySynchronizeCharacterContext(); } private void CheckHotkeys() { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_008f: 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) try { if (!TextInputFocusGuard.ShouldDeferModHotkeys(Plugin.Log) && !((Object)(object)Plugin.GetVaultUI() == (Object)null)) { if ((!Plugin.StaticRequireCtrl || Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) && Input.GetKeyDown(Plugin.StaticToggleKey)) { Plugin.ToggleMainVaultWindow(); } if ((int)Plugin.StaticAltToggleKey != 0 && Input.GetKeyDown(Plugin.StaticAltToggleKey)) { Plugin.ToggleMainVaultWindow(); } if (Input.GetKeyDown(Plugin.StaticHUDToggleKey)) { Plugin.GetVaultHUD()?.Toggle(); } if ((int)Plugin.StaticQuickConvertKey != 0 && Input.GetKeyDown(Plugin.StaticQuickConvertKey)) { Plugin.TryQuickConvertHotkey(); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("[PersistentRunner] Hotkey error: " + ex.Message)); } } } private void CheckForMenuSceneChange() { //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) try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (!(name != _lastKnownScene)) { return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[PersistentRunner] Scene changed: '" + _lastKnownScene + "' -> '" + name + "'")); } _lastKnownScene = name; string text = name.ToLowerInvariant(); bool flag = text.Contains("menu") || text.Contains("title"); if (flag && !_wasInMenuScene) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[PersistentRunner] Menu scene detected: " + name)); } PlayerPatches.SaveAndReset(); } _wasInMenuScene = flag; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("[PersistentRunner] 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)."); } } } } [BepInPlugin("com.azraelgodking.thevault", "The Vault", "3.3.2")] public class Plugin : BaseUnityPlugin { private readonly struct QuickConvertRule { public string FromCurrencyId { get; } public string ToCurrencyId { get; } public int FromAmount { get; } public int ToAmount { get; } public QuickConvertRule(string fromCurrencyId, string toCurrencyId, int fromAmount, int toAmount) { FromCurrencyId = fromCurrencyId; ToCurrencyId = toCurrencyId; FromAmount = fromAmount; ToAmount = toAmount; } } private static VaultManager _staticVaultManager; private static VaultSaveSystem _staticSaveSystem; private static VaultUI _staticVaultUI; private static VaultHUD _staticVaultHUD; internal static KeyCode StaticToggleKey = (KeyCode)118; internal static bool StaticRequireCtrl = true; internal static KeyCode StaticAltToggleKey = (KeyCode)289; internal static KeyCode StaticHUDToggleKey = (KeyCode)288; internal static KeyCode StaticQuickConvertKey = (KeyCode)287; private const float MinWindowScale = 0.5f; private const float MaxWindowScale = 3f; private Harmony _harmony; private VaultManager _vaultManager; private VaultSaveSystem _saveSystem; private VaultUI _vaultUI; private VaultHUD _vaultHUD; private ConfigEntry _toggleKey; private ConfigEntry _requireCtrlModifier; private ConfigEntry _altToggleKey; private ConfigEntry _enableHUD; private ConfigEntry _hudPosition; private ConfigEntry _hudPositionX; private ConfigEntry _hudPositionY; private ConfigEntry _hudScale; private ConfigEntry _hudCompactMode; private ConfigEntry _hudDensity; private ConfigEntry _hudToggleKey; private ConfigEntry _quickConvertKey; private ConfigEntry _quickConvertTable; private ConfigEntry _windowScale; private ConfigEntry _enableAutoSave; private ConfigEntry _autoSaveInterval; private ConfigEntry _checkForUpdates; private string _hudPositionConfigBaseline; private static bool _debugFullVaultInspector; private bool _wasInMenuScene = true; private bool _applicationQuitting; private static GameObject _persistentRunner; private static PersistentUpdateRunner _updateRunner; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static ConfigFile ConfigFile { get; private set; } public static bool LastVaultLoadQuarantinedCorruptFile { get { if (_staticSaveSystem != null) { return _staticSaveSystem.LastLoadQuarantinedCorruptFile; } return false; } } private void Awake() { //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0181: 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_0191: 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_0258: Unknown result type (might be due to invalid IL or missing references) //IL_0262: Expected O, but got Unknown //IL_0306: Unknown result type (might be due to invalid IL or missing references) //IL_0316: 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 The Vault v3.3.2"); VaultSaveCompatibility.WarnIfBreakingRelease(Log); CreatePersistentRunner(); try { InitializeConfig(); SubscribeConfigChanged(); _vaultManager = new VaultManager(); _saveSystem = new VaultSaveSystem(_vaultManager); _staticVaultManager = _vaultManager; _staticSaveSystem = _saveSystem; _saveSystem.SetAutoSaveIntervalSeconds(Mathf.Max(10f, _autoSaveInterval.Value)); _vaultManager.OnVaultLoaded += OnVaultDataLoaded; GameObject val = CreateTheVaultUiHost(); Object.DontDestroyOnLoad((Object)(object)val); _vaultUI = val.AddComponent(); _vaultUI.Initialize(_vaultManager); _vaultUI.SetScale(GetValidatedWindowScale()); _vaultUI.SetToggleKey(_toggleKey.Value, _requireCtrlModifier.Value); _vaultUI.SetAltToggleKey(_altToggleKey.Value); _staticVaultUI = _vaultUI; StaticToggleKey = _toggleKey.Value; StaticRequireCtrl = _requireCtrlModifier.Value; StaticAltToggleKey = _altToggleKey.Value; StaticHUDToggleKey = _hudToggleKey.Value; StaticQuickConvertKey = _quickConvertKey.Value; _vaultHUD = val.AddComponent(); _vaultHUD.Initialize(_vaultManager); _vaultHUD.SetEnabled(_enableHUD.Value); ApplyVaultHudPlacementFromConfig(_vaultHUD); WireVaultHudPositionPersistence(_vaultHUD); _vaultHUD.SetScale(Mathf.Clamp(_hudScale.Value, 0.5f, 3f)); _vaultHUD.SetHudDensity(GetResolvedHudDensity()); _staticVaultHUD = _vaultHUD; VaultModApiBridge.Instance = (IVaultModApi)(object)new VaultModApiAdapter(); IconCache.Initialize(Log); RegisterIconCacheCurrencies(); RegisterItemMappings(); _harmony = new Harmony("com.azraelgodking.thevault"); ApplyPatches(); PatchGameSave(); SceneManager.sceneLoaded += OnSceneLoaded; Log.LogInfo((object)"Subscribed to SceneManager.sceneLoaded for vault loading"); if (_checkForUpdates.Value) { VersionChecker.CheckForUpdate("com.azraelgodking.thevault", "3.3.2", Log, delegate(VersionChecker.VersionCheckResult result) { result.NotifyUpdateAvailable(Log); }); } Log.LogInfo((object)"The Vault loaded successfully!"); Log.LogInfo((object)string.Format("Press {0}{1} or {2} to open the vault", _requireCtrlModifier.Value ? "Ctrl+" : "", _toggleKey.Value, _altToggleKey.Value)); } catch (Exception arg) { Log.LogError((object)string.Format("Failed to load {0}: {1}", "The Vault", arg)); } } private void CreatePersistentRunner() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown if ((Object)(object)_persistentRunner != (Object)null) { Log.LogInfo((object)"PersistentRunner already exists"); return; } _persistentRunner = new GameObject("TheVault_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _updateRunner = _persistentRunner.AddComponent(); Log.LogInfo((object)"Created hidden PersistentRunner that survives game cleanup"); } 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, "TheVault.cfg"); string text2 = Path.Combine(Paths.ConfigPath, "com.azraelgodking.thevault.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 TheVault.cfg failed: " + ex.Message)); } } return new ConfigFile(text, true); } private static GameObject CreateTheVaultUiHost() { //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_0024: 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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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: Expected O, but got Unknown GameObject val = new GameObject("TheVault_UI", new Type[1] { typeof(RectTransform) }); RectTransform component = val.GetComponent(); component.anchorMin = Vector2.zero; component.anchorMax = Vector2.one; component.pivot = new Vector2(0.5f, 0.5f); component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; component.anchoredPosition = Vector2.zero; ((Transform)component).localScale = Vector3.one; return val; } 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_persistentRunner == (Object)null || (Object)(object)_updateRunner == (Object)null) { ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[EnsureUI] Recreating PersistentRunner..."); } _persistentRunner = new GameObject("TheVault_PersistentRunner"); Object.DontDestroyOnLoad((Object)(object)_persistentRunner); ((Object)_persistentRunner).hideFlags = (HideFlags)61; SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(_persistentRunner); _updateRunner = _persistentRunner.AddComponent(); ManualLogSource log2 = Log; if (log2 != null) { log2.LogInfo((object)"[EnsureUI] PersistentRunner recreated"); } } if ((Object)(object)_staticVaultUI == (Object)null) { ManualLogSource log3 = Log; if (log3 != null) { log3.LogInfo((object)"[EnsureUI] Recreating VaultUI..."); } GameObject obj = CreateTheVaultUiHost(); Object.DontDestroyOnLoad((Object)(object)obj); _staticVaultUI = obj.AddComponent(); _staticVaultUI.Initialize(_staticVaultManager); float scale = (((Object)(object)Instance != (Object)null) ? Instance.GetValidatedWindowScale() : 1f); _staticVaultUI.SetScale(scale); _staticVaultUI.SetToggleKey(StaticToggleKey, StaticRequireCtrl); _staticVaultUI.SetAltToggleKey(StaticAltToggleKey); _staticVaultHUD = obj.AddComponent(); _staticVaultHUD.Initialize(_staticVaultManager); if ((Object)(object)Instance != (Object)null) { Instance._vaultUI = _staticVaultUI; Instance._vaultHUD = _staticVaultHUD; _staticVaultHUD.SetEnabled(Instance._enableHUD.Value); Instance.ApplyVaultHudPlacementFromConfig(_staticVaultHUD); Instance.WireVaultHudPositionPersistence(_staticVaultHUD); _staticVaultHUD.SetScale(Mathf.Clamp(Instance._hudScale.Value, 0.5f, 3f)); _staticVaultHUD.SetHudDensity(GetResolvedHudDensity()); } ManualLogSource log4 = Log; if (log4 != null) { log4.LogInfo((object)"[EnsureUI] VaultUI and VaultHUD recreated"); } } } catch (Exception ex) { ManualLogSource log5 = Log; if (log5 != null) { log5.LogError((object)("[EnsureUI] Error recreating UI: " + ex.Message)); } } } private void InitializeConfig() { //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0129: Expected O, but got Unknown //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Expected O, but got Unknown //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Expected O, but got Unknown _toggleKey = ConfigFile.Bind("UI", "ToggleKey", (KeyCode)118, "Key to toggle the vault UI"); _requireCtrlModifier = ConfigFile.Bind("UI", "RequireCtrlModifier", true, "Require Ctrl key to be held when pressing toggle key"); _altToggleKey = ConfigFile.Bind("UI", "AltToggleKey", (KeyCode)289, "Alternative key to toggle vault UI (no modifier required). Useful for Steam Deck."); _enableHUD = ConfigFile.Bind("HUD", "EnableHUD", true, "Show a persistent HUD bar displaying vault currency totals"); _hudPosition = ConfigFile.Bind("HUD", "Position", "TopLeft", "Anchor when not using a custom drag position. Changing this clears PositionX/PositionY. Drag the HUD top strip to save a custom position (like Sun Haven Todo)."); _hudPositionX = ConfigFile.Bind("HUD", "PositionX", -1f, "HUD left edge in pixels after dragging (-1 = use Position anchor only)"); _hudPositionY = ConfigFile.Bind("HUD", "PositionY", -1f, "HUD top edge in pixels after dragging (-1 = use Position anchor only)"); _hudScale = ConfigFile.Bind("HUD", "Scale", 1.25f, new ConfigDescription("Scale factor for the HUD bar (1.0 = smaller, 1.25 = new default, 2.0 = very large). Use a dot as decimal separator in the config file (e.g. 1.25).", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 3f), Array.Empty())); _hudCompactMode = ConfigFile.Bind("HUD", "CompactMode", false, "Legacy: when Density is Normal and this is true, HUD uses Compact spacing. Prefer [HUD] Density."); _hudDensity = ConfigFile.Bind("HUD", "Density", "Normal", new ConfigDescription("HUD bar density: Normal, Compact, or Minimal.", (AcceptableValueBase)(object)new AcceptableValueList(new string[3] { "Normal", "Compact", "Minimal" }), Array.Empty())); _windowScale = ConfigFile.Bind("Display", "WindowScale", 1f, new ConfigDescription("Scale factor for the main Vault window (1.0 = default, 1.5 = 50% larger)", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 3f), Array.Empty())); _hudToggleKey = ConfigFile.Bind("HUD", "ToggleKey", (KeyCode)288, "Key to toggle the HUD display on/off"); _quickConvertKey = ConfigFile.Bind("Hotkeys", "QuickConvertKey", (KeyCode)287, "Key to trigger quick token/key conversion using [QuickConvert] ExchangeTable"); _quickConvertTable = ConfigFile.Bind("QuickConvert", "ExchangeTable", "seasonal_Winter->key_copper:10:1;key_copper->seasonal_Winter:1:10", "Conversion rules: from->to:fromAmount:toAmount;from->to:... (example seasonal_Winter->key_copper:10:1)"); _enableAutoSave = ConfigFile.Bind("Saving", "EnableAutoSave", true, "Automatically save vault data periodically"); _autoSaveInterval = ConfigFile.Bind("Saving", "AutoSaveInterval", 300f, "Auto-save interval in seconds (default: 5 minutes)"); _checkForUpdates = ConfigFile.Bind("Updates", "CheckForUpdates", true, "Check for mod updates on startup"); _hudPositionConfigBaseline = _hudPosition.Value; } private void SubscribeConfigChanged() { if (ConfigFile != null) { ConfigFile.SettingChanged += OnAnyConfigSettingChanged; } } private void OnAnyConfigSettingChanged(object sender, SettingChangedEventArgs args) { try { if (((args != null) ? args.ChangedSetting : null) != null && args.ChangedSetting.ConfigFile == ConfigFile) { ApplyConfigToState(); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("[The Vault] Config change handler failed: " + ex.Message)); } } } private void ApplyConfigToState() { //IL_000d: 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_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_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_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) try { float validatedWindowScale = GetValidatedWindowScale(); StaticToggleKey = _toggleKey.Value; StaticRequireCtrl = _requireCtrlModifier.Value; StaticAltToggleKey = _altToggleKey.Value; StaticHUDToggleKey = _hudToggleKey.Value; StaticQuickConvertKey = _quickConvertKey.Value; _staticSaveSystem?.SetAutoSaveIntervalSeconds(Mathf.Max(10f, _autoSaveInterval.Value)); VaultUI vaultUI = GetVaultUI(); if ((Object)(object)vaultUI != (Object)null) { vaultUI.SetScale(validatedWindowScale); vaultUI.SetToggleKey(StaticToggleKey, StaticRequireCtrl); vaultUI.SetAltToggleKey(StaticAltToggleKey); } VaultHUD vaultHUD = GetVaultHUD(); if ((Object)(object)vaultHUD != (Object)null) { vaultHUD.SetEnabled(_enableHUD.Value); string value = _hudPosition.Value; if (!string.Equals(value, _hudPositionConfigBaseline, StringComparison.OrdinalIgnoreCase)) { ((ConfigEntryBase)_hudPositionX).SetSerializedValue("-1"); ((ConfigEntryBase)_hudPositionY).SetSerializedValue("-1"); vaultHUD.ClearCustomHudPlacement(); } _hudPositionConfigBaseline = value; vaultHUD.SetPosition(ParseHUDPosition(value)); if (_hudPositionX.Value >= 0f && _hudPositionY.Value >= 0f) { vaultHUD.RestoreHudPixelPosition(_hudPositionX.Value, _hudPositionY.Value); } vaultHUD.SetScale(Mathf.Clamp(_hudScale.Value, 0.5f, 3f)); vaultHUD.SetHudDensity(GetResolvedHudDensity()); } } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogError((object)("[The Vault] ApplyConfigToState failed: " + ex.Message)); } } } public void ApplyVaultWindowScaleToUi() { float validatedWindowScale = GetValidatedWindowScale(); _staticVaultUI?.SetScale(validatedWindowScale); } public void ReloadConfig() { try { ConfigFile.Reload(); ApplyConfigToState(); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)"[The Vault] Config reloaded from file"); } } catch (Exception ex) { ManualLogSource log2 = Log; if (log2 != null) { log2.LogError((object)("[The Vault] Config reload failed: " + ex.Message)); } } } public static KeyCode GetConfigToggleKey() { //IL_0028: 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) return (Instance?._toggleKey?.Value).GetValueOrDefault((KeyCode)118); } public static void SetConfigToggleKey(KeyCode k) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (Instance?._toggleKey != null) { Instance._toggleKey.Value = k; } } public static bool GetConfigRequireCtrl() { return (Instance?._requireCtrlModifier?.Value).GetValueOrDefault(true); } public static void SetConfigRequireCtrl(bool v) { if (Instance?._requireCtrlModifier != null) { Instance._requireCtrlModifier.Value = v; } } public static KeyCode GetConfigAltToggleKey() { //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) return (Instance?._altToggleKey?.Value).GetValueOrDefault((KeyCode)289); } public static void SetConfigAltToggleKey(KeyCode k) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (Instance?._altToggleKey != null) { Instance._altToggleKey.Value = k; } } public static bool GetConfigHUDEnabled() { return (Instance?._enableHUD?.Value).GetValueOrDefault(true); } public static void SetConfigHUDEnabled(bool v) { if (Instance?._enableHUD != null) { Instance._enableHUD.Value = v; } } public static float GetConfigHUDScale() { return (Instance?._hudScale?.Value).GetValueOrDefault(1f); } public static void SetConfigHUDScale(float v) { if (Instance?._hudScale != null) { Instance._hudScale.Value = Mathf.Clamp(v, 0.5f, 3f); } } public static bool GetConfigHUDCompactMode() { return (Instance?._hudCompactMode?.Value).GetValueOrDefault(); } public static void SetConfigHUDCompactMode(bool v) { if (Instance?._hudCompactMode != null) { Instance._hudCompactMode.Value = v; } } public static string GetConfigHudDensity() { return Instance?._hudDensity?.Value ?? "Normal"; } public static void SetConfigHudDensity(string v) { if (Instance?._hudDensity != null) { if (string.IsNullOrWhiteSpace(v)) { v = "Normal"; } Instance._hudDensity.Value = v; } } public static VaultHudDensity GetResolvedHudDensity() { if ((Object)(object)Instance == (Object)null) { return VaultHudDensity.Normal; } string a = Instance._hudDensity?.Value?.Trim() ?? "Normal"; if (string.Equals(a, "Minimal", StringComparison.OrdinalIgnoreCase)) { return VaultHudDensity.Minimal; } if (string.Equals(a, "Compact", StringComparison.OrdinalIgnoreCase)) { return VaultHudDensity.Compact; } if (Instance._hudCompactMode != null && Instance._hudCompactMode.Value) { return VaultHudDensity.Compact; } return VaultHudDensity.Normal; } public static float GetConfigWindowScale() { return Instance?.GetValidatedWindowScale() ?? 1f; } public static void SetConfigWindowScale(float v) { if (Instance?._windowScale != null) { Instance._windowScale.Value = Instance.ClampWindowScaleValue(v); } } private float GetValidatedWindowScale() { if (_windowScale == null) { return 1f; } float num = ClampWindowScaleValue(_windowScale.Value); if (!Mathf.Approximately(_windowScale.Value, num)) { _windowScale.Value = num; } return num; } private float ClampWindowScaleValue(float value) { return Mathf.Clamp(value, 0.5f, 3f); } public static bool GetConfigDebugFullVaultInspector() { return _debugFullVaultInspector; } public static void SetConfigDebugFullVaultInspector(bool v) { _debugFullVaultInspector = v; } private void RegisterIconCacheCurrencies() { IconCache.RegisterCurrency("seasonal_Spring", 18020); IconCache.RegisterCurrency("seasonal_Summer", 18021); IconCache.RegisterCurrency("seasonal_Fall", 18023); IconCache.RegisterCurrency("seasonal_Winter", 18022); IconCache.RegisterCurrency("key_copper", 1251); IconCache.RegisterCurrency("key_iron", 1252); IconCache.RegisterCurrency("key_adamant", 1253); IconCache.RegisterCurrency("key_mithril", 1254); IconCache.RegisterCurrency("key_sunite", 1255); IconCache.RegisterCurrency("key_glorite", 1256); IconCache.RegisterCurrency("key_kingslostmine", 1257); IconCache.RegisterCurrency("special_communitytoken", 18013); IconCache.RegisterCurrency("special_doubloon", 60014); IconCache.RegisterCurrency("special_blackbottlecap", 60013); IconCache.RegisterCurrency("special_redcarnivalticket", 18012); IconCache.RegisterCurrency("special_candycornpieces", 18016); IconCache.RegisterCurrency("special_manashard", 18015); } private void RegisterItemMappings() { ItemPatches.AutoDepositEnabled = true; ItemPatches.RegisterItemCurrencyMapping(18020, "seasonal_Spring", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18021, "seasonal_Summer", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18022, "seasonal_Winter", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18023, "seasonal_Fall", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1251, "key_copper", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1252, "key_iron", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1253, "key_adamant", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1254, "key_mithril", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1255, "key_sunite", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1256, "key_glorite", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(1257, "key_kingslostmine", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18013, "special_communitytoken", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(60014, "special_doubloon", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(60013, "special_blackbottlecap", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18012, "special_redcarnivalticket", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18016, "special_candycornpieces", autoDeposit: true); ItemPatches.RegisterItemCurrencyMapping(18015, "special_manashard", autoDeposit: true); ItemPatches.InitializePickupCache(); Log.LogInfo((object)"Registered item-to-currency mappings with auto-deposit enabled"); } private void ApplyPatches() { try { Type typeFromHandle = typeof(Player); PatchMethod(typeFromHandle, "InitializeAsOwner", typeof(PlayerPatches), "OnPlayerInitialized"); PatchShopBuyItem(); PatchGameSaveSaveLoad(); PatchMethod(typeof(MainMenuController), "HomeMenu", typeof(SaveLoadPatches), "OnReturnToMenu"); Type type = AccessTools.TypeByName("Wish.TitleScreen"); if (type != null) { PatchMethod(type, "Start", typeof(SaveLoadPatches), "OnReturnToMenu"); } PatchItemPickup(typeFromHandle); IEnumerable patchedMethods = _harmony.GetPatchedMethods(); int num = 0; foreach (MethodBase item in patchedMethods) { Log.LogInfo((object)("Patched: " + item.DeclaringType?.Name + "." + item.Name)); num++; } Log.LogInfo((object)$"Total methods patched: {num}"); } catch (Exception arg) { Log.LogError((object)$"Harmony patching failed: {arg}"); } } private void PatchMethod(Type targetType, string methodName, Type patchType, string patchMethodName, Type[] parameters = null) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown try { MethodInfo methodInfo = ((parameters == null) ? AccessTools.Method(targetType, methodName, (Type[])null, (Type[])null) : AccessTools.Method(targetType, methodName, parameters, (Type[])null)); if (methodInfo == null) { Log.LogWarning((object)("Could not find method " + targetType.Name + "." + methodName + ". Available methods: " + DescribeAvailableMethods(targetType))); } else { MethodInfo methodInfo2 = AccessTools.Method(patchType, patchMethodName, (Type[])null, (Type[])null); if (methodInfo2 == null) { Log.LogWarning((object)("Could not find patch method " + patchType.Name + "." + patchMethodName)); return; } _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + targetType.Name + "." + methodName)); } } catch (Exception ex) { Log.LogError((object)("Failed to patch " + targetType.Name + "." + methodName + ": " + ex.Message)); } } private void PatchMethodPrefix(Type targetType, string methodName, Type patchType, string patchMethodName, Type[] parameters = null) { //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown try { MethodInfo methodInfo = ((parameters == null) ? AccessTools.Method(targetType, methodName, (Type[])null, (Type[])null) : AccessTools.Method(targetType, methodName, parameters, (Type[])null)); if (methodInfo == null) { Log.LogWarning((object)("Could not find method " + targetType.Name + "." + methodName + ". Available methods: " + DescribeAvailableMethods(targetType))); return; } MethodInfo methodInfo2 = AccessTools.Method(patchType, patchMethodName, (Type[])null, (Type[])null); if (methodInfo2 == null) { Log.LogWarning((object)("Could not find patch method " + patchType.Name + "." + patchMethodName)); return; } _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + targetType.Name + "." + methodName + " (prefix)")); } catch (Exception ex) { Log.LogError((object)("Failed to patch " + targetType.Name + "." + methodName + ": " + ex.Message)); } } private void PatchGameSave() { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown try { Type typeFromHandle = typeof(GameSave); MethodInfo methodInfo = AccessTools.Method(typeof(GameSavePatches), "OnLoadCharacterAny", (Type[])null, (Type[])null); if (methodInfo == null) { Log.LogWarning((object)"Could not find GameSavePatches.OnLoadCharacterAny"); return; } List list = (from m in AccessTools.GetDeclaredMethods(typeFromHandle) where string.Equals(m.Name, "LoadCharacter", StringComparison.Ordinal) select m).ToList(); if (list.Count == 0) { Log.LogWarning((object)"No GameSave.LoadCharacter overloads found to patch"); return; } foreach (MethodInfo item in list) { _harmony.Patch((MethodBase)item, (HarmonyMethod)null, new HarmonyMethod(methodInfo), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); string text = string.Join(", ", from p in item.GetParameters() select p.ParameterType.Name); Log.LogInfo((object)("Patched GameSave.LoadCharacter(" + text + ")")); } } catch (Exception ex) { Log.LogError((object)("Error patching GameSave: " + ex.Message)); } } private void PatchGameSaveSaveLoad() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown try { Type typeFromHandle = typeof(GameSave); MethodInfo methodInfo = AccessTools.Method(typeFromHandle, "SaveGame", (Type[])null, (Type[])null); if (methodInfo != null) { MethodInfo methodInfo2 = AccessTools.Method(typeof(SaveLoadPatches), "OnGameSaved", (Type[])null, (Type[])null); if (methodInfo2 != null) { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched GameSave.SaveGame"); } } MethodInfo methodInfo3 = AccessTools.Method(typeFromHandle, "LoadGame", (Type[])null, (Type[])null); if (methodInfo3 != null) { MethodInfo methodInfo4 = AccessTools.Method(typeof(SaveLoadPatches), "OnGameLoaded", (Type[])null, (Type[])null); if (methodInfo4 != null) { _harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched GameSave.LoadGame"); } } } catch (Exception ex) { Log.LogError((object)("Error patching GameSave save/load: " + ex.Message)); } } private void PatchShopBuyItem() { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown //IL_022a: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Expected O, but got Unknown //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Expected O, but got Unknown //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Expected O, but got Unknown try { Type typeFromHandle = typeof(Shop); Type typeFromHandle2 = typeof(ShopItemInfo2); Type typeFromHandle3 = typeof(ShopLoot2); if (typeFromHandle2 != null) { MethodInfo methodInfo = AccessTools.Method(typeFromHandle, "BuyItem", new Type[2] { typeFromHandle2, typeof(int) }, (Type[])null); if (methodInfo != null) { MethodInfo methodInfo2 = AccessTools.Method(typeof(ShopPatches), "OnBeforeBuyItem", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(ShopPatches), "OnAfterBuyItem", (Type[])null, (Type[])null); if (methodInfo2 != null) { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched Shop.BuyItem(ShopItemInfo2,int) for vault (prefix)"); } if (methodInfo3 != null) { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched Shop.BuyItem(ShopItemInfo2,int) for vault (postfix)"); } } } if (!(typeFromHandle3 != null)) { return; } MethodInfo methodInfo4 = AccessTools.Method(typeFromHandle, "BuyItem", new Type[2] { typeFromHandle3, typeof(int) }, (Type[])null); if (methodInfo4 != null) { MethodInfo methodInfo5 = AccessTools.Method(typeof(ShopPatches), "OnBeforeBuyItem", (Type[])null, (Type[])null); MethodInfo methodInfo6 = AccessTools.Method(typeof(ShopPatches), "OnAfterBuyItem", (Type[])null, (Type[])null); if (methodInfo5 != null) { _harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(methodInfo5), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched Shop.BuyItem(ShopLoot2,int) for vault (prefix)"); } if (methodInfo6 != null) { _harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(methodInfo6), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched Shop.BuyItem(ShopLoot2,int) for vault (postfix)"); } } MethodInfo methodInfo7 = AccessTools.Method(typeFromHandle, "BuyItem", new Type[1] { typeFromHandle3 }, (Type[])null); if (methodInfo7 != null) { MethodInfo methodInfo8 = AccessTools.Method(typeof(ShopPatches), "OnBeforeBuyItemSingle", (Type[])null, (Type[])null); MethodInfo methodInfo9 = AccessTools.Method(typeof(ShopPatches), "OnAfterBuyItemSingle", (Type[])null, (Type[])null); if (methodInfo8 != null) { _harmony.Patch((MethodBase)methodInfo7, new HarmonyMethod(methodInfo8), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched Shop.BuyItem(ShopLoot2) for vault (prefix)"); } if (methodInfo9 != null) { _harmony.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, new HarmonyMethod(methodInfo9), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)"Patched Shop.BuyItem(ShopLoot2) for vault (postfix)"); } } } catch (Exception ex) { Log.LogError((object)("Error patching Shop: " + ex.Message)); } } private void PatchItemPickup(Type playerType) { //IL_005d: 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_0178: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Expected O, but got Unknown //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Expected O, but got Unknown //IL_042c: Unknown result type (might be due to invalid IL or missing references) //IL_0439: Expected O, but got Unknown //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_04b1: Unknown result type (might be due to invalid IL or missing references) //IL_04be: Expected O, but got Unknown //IL_0321: Unknown result type (might be due to invalid IL or missing references) //IL_032e: Expected O, but got Unknown //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Expected O, but got Unknown //IL_0554: Unknown result type (might be due to invalid IL or missing references) //IL_0561: Expected O, but got Unknown //IL_0628: Unknown result type (might be due to invalid IL or missing references) //IL_062f: Unknown result type (might be due to invalid IL or missing references) //IL_063c: Expected O, but got Unknown //IL_063c: Expected O, but got Unknown MethodInfo methodInfo = AccessTools.Method(playerType, "Pickup", (Type[])null, (Type[])null); if (methodInfo != null) { MethodInfo methodInfo2 = AccessTools.Method(typeof(ItemPatches), "OnPlayerPickupPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo3 = AccessTools.Method(typeof(ItemPatches), "OnPlayerPickup", (Type[])null, (Type[])null); if (methodInfo2 != null) { object obj = _harmony; object obj2 = methodInfo; ? val = new HarmonyMethod(methodInfo2); if (methodInfo3 != null) { obj = (object)new HarmonyMethod(methodInfo3); obj2 = (object)val; val = obj2; } else { obj = null; obj2 = (object)val; val = obj2; } ((Harmony)obj).Patch((MethodBase)val, (HarmonyMethod)obj2, (HarmonyMethod)obj, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Patched " + playerType.Name + ".Pickup for auto-deposit")); } } else { Log.LogWarning((object)"Could not find Pickup method on Player"); } Type typeFromHandle = typeof(Inventory); MethodInfo[] methods = typeFromHandle.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); Type typeFromHandle2 = typeof(Item); MethodInfo methodInfo4 = AccessTools.Method(typeFromHandle, "AddItem", new Type[6] { typeFromHandle2, typeof(int), typeof(int), typeof(bool), typeof(bool), typeof(bool) }, (Type[])null); if (methodInfo4 != null) { MethodInfo methodInfo5 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItemObjectPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo6 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItemObjectPostfix", (Type[])null, (Type[])null); if (methodInfo5 != null) { object obj3 = _harmony; object obj4 = methodInfo4; ? val2 = new HarmonyMethod(methodInfo5); if (methodInfo6 != null) { obj3 = (object)new HarmonyMethod(methodInfo6); obj4 = (object)val2; val2 = obj4; } else { obj3 = null; obj4 = (object)val2; val2 = obj4; } ((Harmony)obj3).Patch((MethodBase)val2, (HarmonyMethod)obj4, (HarmonyMethod)obj3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Patched " + typeFromHandle.Name + ".AddItem(Item,...) for auto-deposit")); } else if (methodInfo6 != null) { _harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(methodInfo6), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Patched " + typeFromHandle.Name + ".AddItem(Item,...) postfix only")); } } else { Log.LogWarning((object)"Could not find AddItem(Item,int,int,bool,bool,bool)"); MethodInfo[] array = methods; foreach (MethodInfo methodInfo7 in array) { if (methodInfo7.Name != "AddItem") { continue; } ParameterInfo[] parameters = methodInfo7.GetParameters(); if (parameters.Length == 0 || !(parameters[0].ParameterType == typeFromHandle2)) { continue; } MethodInfo methodInfo8 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItemObjectPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo9 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItemObjectPostfix", (Type[])null, (Type[])null); if (methodInfo8 != null) { object obj5 = _harmony; object obj6 = methodInfo7; ? val3 = new HarmonyMethod(methodInfo8); if (methodInfo9 != null) { obj5 = (object)new HarmonyMethod(methodInfo9); obj6 = (object)val3; val3 = obj6; } else { obj5 = null; obj6 = (object)val3; val3 = obj6; } ((Harmony)obj5).Patch((MethodBase)val3, (HarmonyMethod)obj6, (HarmonyMethod)obj5, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Patched " + typeFromHandle.Name + "." + methodInfo7.Name + " for auto-deposit")); break; } if (methodInfo9 != null) { _harmony.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, new HarmonyMethod(methodInfo9), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Patched " + typeFromHandle.Name + "." + methodInfo7.Name + " postfix only")); break; } } } MethodInfo methodInfo10 = AccessTools.Method(typeFromHandle, "AddItem", new Type[2] { typeof(int), typeof(int) }, (Type[])null); if (methodInfo10 == null) { methodInfo10 = AccessTools.Method(typeFromHandle, "AddItem", new Type[3] { typeof(int), typeof(int), typeof(bool) }, (Type[])null); } if (methodInfo10 != null) { MethodInfo methodInfo11 = AccessTools.Method(typeof(ItemPatches), "OnInventoryAddItem", (Type[])null, (Type[])null); if (methodInfo11 != null) { _harmony.Patch((MethodBase)methodInfo10, (HarmonyMethod)null, new HarmonyMethod(methodInfo11), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + typeFromHandle.Name + ".AddItem for auto-deposit")); } } MethodInfo methodInfo12 = AccessTools.Method(typeFromHandle, "GetAmount", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo12 != null) { MethodInfo methodInfo13 = AccessTools.Method(typeof(ItemPatches), "OnInventoryGetAmount", (Type[])null, (Type[])null); if (methodInfo13 != null) { _harmony.Patch((MethodBase)methodInfo12, (HarmonyMethod)null, new HarmonyMethod(methodInfo13), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + typeFromHandle.Name + ".GetAmount for vault integration")); } } else { Log.LogWarning((object)"Could not find Inventory.GetAmount method"); } MethodInfo methodInfo14 = AccessTools.Method(typeFromHandle, "HasEnough", new Type[2] { typeof(int), typeof(int) }, (Type[])null); if (methodInfo14 != null) { MethodInfo methodInfo15 = AccessTools.Method(typeof(ItemPatches), "OnInventoryHasEnough", (Type[])null, (Type[])null); if (methodInfo15 != null) { _harmony.Patch((MethodBase)methodInfo14, (HarmonyMethod)null, new HarmonyMethod(methodInfo15), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + typeFromHandle.Name + ".HasEnough for vault integration")); } } else { Log.LogWarning((object)"Could not find Inventory.HasEnough method"); } MethodInfo methodInfo16 = AccessTools.Method(typeFromHandle, "RemoveItem", new Type[3] { typeof(int), typeof(int), typeof(int) }, (Type[])null); if (methodInfo16 != null) { MethodInfo methodInfo17 = AccessTools.Method(typeof(ItemPatches), "OnInventoryRemoveItemPrefix", (Type[])null, (Type[])null); MethodInfo methodInfo18 = AccessTools.Method(typeof(ItemPatches), "OnInventoryRemoveItemPostfix", (Type[])null, (Type[])null); if (methodInfo17 != null && methodInfo18 != null) { _harmony.Patch((MethodBase)methodInfo16, new HarmonyMethod(methodInfo17), new HarmonyMethod(methodInfo18), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log.LogInfo((object)("Successfully patched " + typeFromHandle.Name + ".RemoveItem for vault integration")); } } else { Log.LogWarning((object)"Could not find Inventory.RemoveItem method"); } } private static string DescribeAvailableMethods(Type targetType) { if (targetType == null) { return ""; } try { IOrderedEnumerable values = from s in targetType.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Select(delegate(MethodInfo m) { string text = string.Join(", ", from p in m.GetParameters() select p.ParameterType.Name); return m.Name + "(" + text + ")"; }).Distinct() orderby s select s; return string.Join("; ", values); } catch (Exception ex) { return ""; } } private void ApplyVaultHudPlacementFromConfig(VaultHUD hud) { hud.SetPosition(ParseHUDPosition(_hudPosition.Value)); if (_hudPositionX.Value >= 0f && _hudPositionY.Value >= 0f) { hud.RestoreHudPixelPosition(_hudPositionX.Value, _hudPositionY.Value); } } private void WireVaultHudPositionPersistence(VaultHUD hud) { hud.OnPositionChanged = delegate(float x, float y) { ((ConfigEntryBase)_hudPositionX).SetSerializedValue(x.ToString(CultureInfo.InvariantCulture)); ((ConfigEntryBase)_hudPositionY).SetSerializedValue(y.ToString(CultureInfo.InvariantCulture)); }; } private static VaultHUD.HUDPosition ParseHUDPosition(string position) { return position?.ToLower() switch { "topleft" => VaultHUD.HUDPosition.TopLeft, "topcenter" => VaultHUD.HUDPosition.TopCenter, "topright" => VaultHUD.HUDPosition.TopRight, "bottomleft" => VaultHUD.HUDPosition.BottomLeft, "bottomcenter" => VaultHUD.HUDPosition.BottomCenter, "bottomright" => VaultHUD.HUDPosition.BottomRight, _ => VaultHUD.HUDPosition.TopLeft, }; } private void OnApplicationQuit() { _applicationQuitting = true; Log.LogInfo((object)"Application quitting - saving vault data"); _saveSystem?.ForceSave(); } private void OnDisable() { //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; string text2 = text.ToLowerInvariant(); if (!Application.isPlaying || text2.Contains("menu") || text2.Contains("title")) { Log.LogInfo((object)("[Lifecycle] Plugin OnDisable during expected teardown (scene: " + text + ")")); return; } Log.LogWarning((object)"[CRITICAL] Plugin OnDisable called outside expected teardown."); Log.LogWarning((object)("[CRITICAL] Current scene: " + text)); Log.LogWarning((object)("[CRITICAL] Stack trace: " + Environment.StackTrace)); } private void OnDestroy() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) VaultModApiBridge.Instance = null; if (_vaultManager != null) { _vaultManager.OnVaultLoaded -= OnVaultDataLoaded; } if (ConfigFile != null) { ConfigFile.SettingChanged -= OnAnyConfigSettingChanged; } Scene activeScene = SceneManager.GetActiveScene(); string text = ((Scene)(ref activeScene)).name ?? string.Empty; string text2 = text.ToLowerInvariant(); if (_applicationQuitting || !Application.isPlaying || text2.Contains("menu") || text2.Contains("title")) { Log.LogInfo((object)("[Lifecycle] Plugin OnDestroy during expected teardown (scene: " + text + ")")); } else { Log.LogWarning((object)"[CRITICAL] Plugin OnDestroy called outside expected teardown."); Log.LogWarning((object)("[CRITICAL] Current scene: " + text)); Log.LogWarning((object)("[CRITICAL] Stack trace: " + Environment.StackTrace)); } SceneManager.sceneLoaded -= OnSceneLoaded; _saveSystem?.ForceSave(); } public static VaultManager GetVaultManager() { return _staticVaultManager; } public static VaultSaveSystem GetSaveSystem() { return _staticSaveSystem; } public static VaultUI GetVaultUI() { return _staticVaultUI; } public static bool IsMainVaultPanelVisible() { if ((Object)(object)_staticVaultUI != (Object)null) { return _staticVaultUI.IsVisible; } return false; } public static void ToggleMainVaultWindow() { _staticVaultUI?.Toggle(); } public static void OpenVault() { _staticVaultUI?.Show(); } public static void CloseVault() { _staticVaultUI?.Hide(); } public static bool LoadVaultForPlayer(string playerName) { if (_staticSaveSystem == null) { return false; } return _staticSaveSystem.Load(playerName); } public static void SaveVault() { _staticSaveSystem?.ForceSave(); } public static VaultHUD GetVaultHUD() { return _staticVaultHUD; } public static void ToggleHUD() { _staticVaultHUD?.Toggle(); } internal static void TickAutoSave() { if (Instance?._enableAutoSave != null && Instance._enableAutoSave.Value) { _staticSaveSystem?.CheckAutoSave(); } } internal static void TryQuickConvertHotkey() { Instance?.TryQuickConvert(); } private void TryQuickConvert() { VaultManager vaultManager = GetVaultManager(); if (vaultManager == null) { return; } foreach (QuickConvertRule item in ParseQuickConvertRules(_quickConvertTable?.Value)) { if (vaultManager.GetCurrency(item.FromCurrencyId) < item.FromAmount || !TryMutateCurrency(vaultManager, item.FromCurrencyId, item.FromAmount, add: false)) { continue; } if (!TryMutateCurrency(vaultManager, item.ToCurrencyId, item.ToAmount, add: true)) { TryMutateCurrency(vaultManager, item.FromCurrencyId, item.FromAmount, add: true); continue; } ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"[QuickConvert] {item.FromAmount} {item.FromCurrencyId} -> {item.ToAmount} {item.ToCurrencyId}"); } break; } } private static List ParseQuickConvertRules(string table) { List list = new List(); if (string.IsNullOrWhiteSpace(table)) { return list; } string[] array = table.Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); int num = text.IndexOf("->", StringComparison.Ordinal); int num2 = text.IndexOf(':'); int num3 = ((num2 >= 0) ? text.IndexOf(':', num2 + 1) : (-1)); if (num > 0 && num2 > num && num3 > num2) { string text2 = text.Substring(0, num).Trim(); string text3 = text.Substring(num + 2, num2 - (num + 2)).Trim(); if (int.TryParse(text.Substring(num2 + 1, num3 - num2 - 1), out var result) && result > 0 && int.TryParse(text.Substring(num3 + 1), out var result2) && result2 > 0 && !string.IsNullOrWhiteSpace(text2) && !string.IsNullOrWhiteSpace(text3)) { list.Add(new QuickConvertRule(text2, text3, result, result2)); } } } return list; } private static bool TryMutateCurrency(VaultManager manager, string currencyId, int amount, bool add) { if (manager == null || string.IsNullOrWhiteSpace(currencyId) || amount <= 0) { return false; } if (currencyId.StartsWith("seasonal_", StringComparison.Ordinal)) { if (!Enum.TryParse(currencyId.Substring("seasonal_".Length), out var result)) { return false; } if (!add) { return manager.RemoveSeasonalTokens(result, amount); } return manager.AddSeasonalTokens(result, amount); } if (currencyId.StartsWith("community_", StringComparison.Ordinal)) { string tokenId = currencyId.Substring("community_".Length); if (!add) { return manager.RemoveCommunityTokens(tokenId, amount); } return manager.AddCommunityTokens(tokenId, amount); } if (currencyId.StartsWith("key_", StringComparison.Ordinal)) { string keyId = currencyId.Substring("key_".Length); if (!add) { return manager.RemoveKeys(keyId, amount); } return manager.AddKeys(keyId, amount); } if (currencyId.StartsWith("special_", StringComparison.Ordinal)) { string specialId = currencyId.Substring("special_".Length); if (!add) { return manager.RemoveSpecial(specialId, amount); } return manager.AddSpecial(specialId, amount); } if (currencyId.StartsWith("orb_", StringComparison.Ordinal)) { string orbId = currencyId.Substring("orb_".Length); if (!add) { return manager.RemoveOrbs(orbId, amount); } return manager.AddOrbs(orbId, amount); } if (currencyId.StartsWith("custom_", StringComparison.Ordinal)) { string currencyId2 = currencyId.Substring("custom_".Length); if (!add) { return manager.RemoveCustomCurrency(currencyId2, amount); } return manager.AddCustomCurrency(currencyId2, amount); } return false; } private static void OnVaultDataLoaded() { try { VaultModApiBridge.NotifyVaultLoaded(); } catch (Exception ex) { ManualLogSource log = Log; if (log != null) { log.LogWarning((object)("[The Vault] Failed to raise OnVaultLoaded bridge event: " + ex.Message)); } } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) try { Log.LogDebug((object)$"[SceneChange] Scene loaded: '{((Scene)(ref scene)).name}' (mode: {mode})"); string text = ((Scene)(ref scene)).name.ToLowerInvariant(); bool flag = text.Contains("menu") || text.Contains("title"); if (flag && !_wasInMenuScene) { Log.LogInfo((object)("Menu scene detected: " + ((Scene)(ref scene)).name)); PlayerPatches.SaveAndReset(); } _wasInMenuScene = flag; } catch (Exception ex) { Log.LogError((object)("Error in OnSceneLoaded: " + ex.Message)); } } } public static class PluginInfo { public const string PLUGIN_GUID = "com.azraelgodking.thevault"; public const string PLUGIN_NAME = "The Vault"; public const string PLUGIN_VERSION = "3.3.2"; } public static class SecretGifts { private static readonly HashSet _eligibleSteamIdHashes = new HashSet { "Tr4eyf86yKRu6fhNQjQOrO/ZnwasQaY/fgKV0PcnoGA=", "zAKJKUjJVGiQRm+4gCGcMZqq1sF8vQZT7bhd+r0Q0kw=" }; private const int TREASURE_CHEST_ITEM_ID = 3789; private const int GIFT_QUANTITY = 10; private const string ENCRYPTION_SALT = "SecretG1fts_S@lt2026_Secure"; private const int KEY_SIZE = 256; private const int ITERATIONS = 10000; private static readonly byte[] _iv = new byte[16] { 83, 101, 99, 114, 101, 116, 71, 105, 102, 116, 115, 73, 86, 48, 48, 49 }; private const string FILE_HEADER = "SCRTGIFT"; private static string _currentSteamId; private static bool _giftCheckPerformed; private static bool _dataLoaded; private static HashSet _giftRecipients = new HashSet(); public static void CheckAndGiveFirstOpenGift(string characterName) { if (_giftCheckPerformed) { return; } _giftCheckPerformed = true; try { _currentSteamId = TryGetSteamId(); if (string.IsNullOrEmpty(_currentSteamId)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[SecretGifts] Could not retrieve Steam ID"); } return; } if (!_dataLoaded) { LoadGiftRecipients(); _dataLoaded = true; } string text = ComputeHash(_currentSteamId); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[SecretGifts] Steam hash: " + text)); } if (!_eligibleSteamIdHashes.Contains(text)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"[SecretGifts] User not eligible for secret gift"); } return; } if (HasReceivedGift(characterName)) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("[SecretGifts] " + characterName + " has already received the secret gift")); } return; } ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)("[SecretGifts] Giving secret gift to " + characterName + "!")); } if (SpawnItems(3789, 10)) { MarkGiftReceived(characterName); ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[SecretGifts] Successfully gave {10} treasure chests to {characterName}!"); } } } catch (Exception ex) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogError((object)("[SecretGifts] Error: " + ex.Message)); } } } public static void ResetGiftCheck() { _giftCheckPerformed = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[SecretGifts] Reset for character switch. {_giftRecipients.Count} gift records in memory."); } } private static bool SpawnItems(int itemId, int amount) { try { Player instance = Player.Instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.Inventory == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[SecretGifts] Player or inventory not available"); } return false; } Inventory inventory = instance.Inventory; MethodInfo methodInfo = AccessTools.Method(((object)inventory).GetType(), "AddItem", new Type[3] { typeof(int), typeof(int), typeof(bool) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(inventory, new object[3] { itemId, amount, true }); return true; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[SecretGifts] Could not find AddItem method"); } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("[SecretGifts] Spawn error: " + ex.Message)); } } return false; } private static string GetSaveDirectory() { return Path.Combine(Paths.ConfigPath, "TheVault", "Saves"); } private static string GetGiftTrackingFilePath() { return Path.Combine(GetSaveDirectory(), "SecretGifts.dat"); } private static string GetBackupFilePath() { return Path.Combine(GetSaveDirectory(), "SecretGifts.dat.backup"); } private static void LoadGiftRecipients() { try { string giftTrackingFilePath = GetGiftTrackingFilePath(); string backupFilePath = GetBackupFilePath(); if (File.Exists(giftTrackingFilePath) && TryLoadFromFile(giftTrackingFilePath)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[SecretGifts] Loaded {_giftRecipients.Count} gift records"); } return; } if (File.Exists(backupFilePath)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[SecretGifts] Main file failed, trying backup..."); } if (TryLoadFromFile(backupFilePath)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"[SecretGifts] Restored {_giftRecipients.Count} gift records from backup"); } SaveGiftRecipients(); return; } } _giftRecipients = new HashSet(); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"[SecretGifts] No existing gift records found, starting fresh"); } } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogWarning((object)("[SecretGifts] Error loading gift records: " + ex.Message)); } _giftRecipients = new HashSet(); } } private static bool TryLoadFromFile(string filePath) { try { string text = Decrypt(File.ReadAllBytes(filePath)); if (string.IsNullOrEmpty(text)) { return false; } _giftRecipients = new HashSet(); string[] array = text.Split(new char[2] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text2 in array) { if (!string.IsNullOrWhiteSpace(text2)) { _giftRecipients.Add(text2.Trim()); } } return true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[SecretGifts] TryLoadFromFile: " + ex.Message)); } return false; } } private static void SaveGiftRecipients() { try { string giftTrackingFilePath = GetGiftTrackingFilePath(); string backupFilePath = GetBackupFilePath(); string saveDirectory = GetSaveDirectory(); if (!Directory.Exists(saveDirectory)) { Directory.CreateDirectory(saveDirectory); } byte[] bytes = Encrypt(string.Join("\n", _giftRecipients)); string text = giftTrackingFilePath + ".tmp"; File.WriteAllBytes(text, bytes); if (File.Exists(giftTrackingFilePath)) { if (File.Exists(backupFilePath)) { File.Delete(backupFilePath); } File.Move(giftTrackingFilePath, backupFilePath); } File.Move(text, giftTrackingFilePath); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[SecretGifts] Saved {_giftRecipients.Count} gift records (encrypted)"); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("[SecretGifts] Error saving gift records: " + ex.Message)); } } } private static bool HasReceivedGift(string characterName) { string item = _currentSteamId + ":" + characterName; return _giftRecipients.Contains(item); } private static void MarkGiftReceived(string characterName) { string item = _currentSteamId + ":" + characterName; _giftRecipients.Add(item); SaveGiftRecipients(); } private static byte[] GenerateKey() { string text = ((!string.IsNullOrEmpty(_currentSteamId)) ? ("Steam_" + _currentSteamId) : "DefaultKey"); using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes("SecretG1fts_S@lt2026_Secure_" + text + "_SecretGiftsKey", Encoding.UTF8.GetBytes("SecretG1fts_S@lt2026_Secure"), 10000); return rfc2898DeriveBytes.GetBytes(32); } private static byte[] Encrypt(string plainText) { byte[] key = GenerateKey(); using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform transform = aes.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); byte[] bytes = Encoding.UTF8.GetBytes("SCRTGIFT"); memoryStream.Write(bytes, 0, bytes.Length); using (CryptoStream stream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { using StreamWriter streamWriter = new StreamWriter(stream, Encoding.UTF8); streamWriter.Write(plainText); } return memoryStream.ToArray(); } private static string Decrypt(byte[] cipherData) { try { if (cipherData.Length < "SCRTGIFT".Length) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[SecretGifts] File too small, may be corrupted"); } return null; } if (Encoding.UTF8.GetString(cipherData, 0, "SCRTGIFT".Length) != "SCRTGIFT") { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[SecretGifts] Detected legacy unencrypted file, will re-encrypt on save"); } return Encoding.UTF8.GetString(cipherData); } byte[] key = GenerateKey(); using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform transform = aes.CreateDecryptor(); using MemoryStream stream = new MemoryStream(cipherData, "SCRTGIFT".Length, cipherData.Length - "SCRTGIFT".Length); using CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Read); using StreamReader streamReader = new StreamReader(stream2, Encoding.UTF8); return streamReader.ReadToEnd(); } catch (CryptographicException ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("[SecretGifts] Decryption failed: " + ex.Message)); } return null; } catch (Exception ex2) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("[SecretGifts] Error decrypting: " + ex2.Message)); } return null; } } private static string TryGetSteamId() { try { Assembly assembly = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { string name = assembly2.GetName().Name; if (name.Contains("rlabrecque") || name == "Steamworks.NET") { assembly = assembly2; break; } } if (assembly == null) { assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly3 in assemblies) { try { if (assembly3.GetType("Steamworks.SteamUser") != null) { assembly = assembly3; break; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[SecretGifts] Skipping assembly for Steam search: " + ex.Message)); } } } } if (assembly == null) { return null; } Type type = assembly.GetType("Steamworks.SteamUser"); if (type == null) { return null; } MethodInfo method = type.GetMethod("GetSteamID", BindingFlags.Static | BindingFlags.Public); if (method == null) { return null; } object obj = method.Invoke(null, null); if (obj == null) { return null; } string text = obj.ToString(); if (string.IsNullOrEmpty(text) || text == "0" || text.Length < 10) { return null; } return text; } catch (Exception ex2) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("[SecretGifts] TryGetSteamId: " + ex2.Message)); } return null; } } private static string ComputeHash(string input) { using SHA256 sHA = SHA256.Create(); return Convert.ToBase64String(sHA.ComputeHash(Encoding.UTF8.GetBytes(input))); } } public static class VaultSaveCompatibility { public const bool ThisReleaseMayBreakVaultSaves = false; public static void WarnIfBreakingRelease(ManualLogSource log) { } } } namespace TheVault.Vault { public static class VaultCurrencyIds { public const string PrefixSeasonal = "seasonal_"; public const string PrefixCommunity = "community_"; public const string PrefixKey = "key_"; public const string PrefixSpecial = "special_"; public const string PrefixOrb = "orb_"; public const string PrefixTicket = "ticket_"; public const string PrefixCustom = "custom_"; public const string SeasonalSpring = "seasonal_Spring"; public const string SeasonalSummer = "seasonal_Summer"; public const string SeasonalFall = "seasonal_Fall"; public const string SeasonalWinter = "seasonal_Winter"; public const string KeyCopper = "key_copper"; public const string KeyIron = "key_iron"; public const string KeyAdamant = "key_adamant"; public const string KeyMithril = "key_mithril"; public const string KeySunite = "key_sunite"; public const string KeyGlorite = "key_glorite"; public const string KeyKingsLostMine = "key_kingslostmine"; public const string SpecialCommunityToken = "special_communitytoken"; public const string SpecialDoubloon = "special_doubloon"; public const string SpecialBlackBottleCap = "special_blackbottlecap"; public const string SpecialRedCarnivalTicket = "special_redcarnivalticket"; public const string SpecialCandyCornPieces = "special_candycornpieces"; public const string SpecialManaShard = "special_manashard"; public static readonly string[] AllSeasonalFullIds = new string[4] { "seasonal_Spring", "seasonal_Summer", "seasonal_Fall", "seasonal_Winter" }; public static readonly string[] AllKeyFullIds = new string[7] { "key_copper", "key_iron", "key_adamant", "key_mithril", "key_sunite", "key_glorite", "key_kingslostmine" }; public static readonly string[] AllSpecialFullIds = new string[6] { "special_communitytoken", "special_doubloon", "special_blackbottlecap", "special_redcarnivalticket", "special_candycornpieces", "special_manashard" }; public static string FullCustom(string shortId) { return "custom_" + shortId; } public static string FullSeasonalFromTokenName(string tokenName) { return "seasonal_" + tokenName; } public static bool TryStripPrefix(string fullId, string prefix, out string remainder) { remainder = null; if (string.IsNullOrEmpty(fullId) || string.IsNullOrEmpty(prefix)) { return false; } if (!fullId.StartsWith(prefix, StringComparison.Ordinal)) { return false; } remainder = fullId.Substring(prefix.Length); return true; } } [Serializable] public class VaultData { public int Version { get; set; } = 2; public Dictionary SeasonalTokens { get; set; } public Dictionary CommunityTokens { get; set; } public Dictionary Keys { get; set; } public Dictionary CustomCurrencies { get; set; } public Dictionary Tickets { get; set; } public Dictionary Orbs { get; set; } public DateTime LastSaved { get; set; } public string PlayerName { get; set; } public VaultData() { SeasonalTokens = new Dictionary(); CommunityTokens = new Dictionary(); Keys = new Dictionary(); CustomCurrencies = new Dictionary(); Tickets = new Dictionary(); Orbs = new Dictionary(); LastSaved = DateTime.Now; PlayerName = ""; foreach (SeasonalTokenType value in Enum.GetValues(typeof(SeasonalTokenType))) { SeasonalTokens[value] = 0; } } public VaultData Clone() { return new VaultData { Version = Version, LastSaved = LastSaved, PlayerName = PlayerName, SeasonalTokens = new Dictionary(SeasonalTokens), CommunityTokens = new Dictionary(CommunityTokens), Keys = new Dictionary(Keys), CustomCurrencies = new Dictionary(CustomCurrencies), Tickets = new Dictionary(Tickets), Orbs = new Dictionary(Orbs) }; } } public enum SeasonalTokenType { Spring, Summer, Fall, Winter, Anniversary, Special } public class CurrencyDefinition { public string Id { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public CurrencyCategory Category { get; set; } public string IconPath { get; set; } public int GameItemId { get; set; } public CurrencyDefinition(string id, string displayName, CurrencyCategory category, int gameItemId = -1) { Id = id; DisplayName = displayName; Category = category; GameItemId = gameItemId; Description = ""; IconPath = ""; } } public enum CurrencyCategory { SeasonalToken, CommunityToken, Key, Special, Orb, Custom } public static class VaultDataMigration { public static VaultData Migrate(VaultData data, Action logInfo = null) { data = data ?? new VaultData(); if (data.Version < 1) { data.Version = 1; logInfo?.Invoke("Migrated vault data to version 1"); } if (data.Version < 2) { data.Version = 2; logInfo?.Invoke("Migrated vault data to version 2 (no transform; reserves the version for future field changes)"); } return data; } } public static class VaultDataSanitizer { public static int Sanitize(VaultData data) { if (data == null) { return 0; } int num = 0; VaultData vaultData = data; if (vaultData.SeasonalTokens == null) { Dictionary dictionary2 = (vaultData.SeasonalTokens = new Dictionary()); } vaultData = data; if (vaultData.CommunityTokens == null) { Dictionary dictionary4 = (vaultData.CommunityTokens = new Dictionary()); } vaultData = data; if (vaultData.Keys == null) { Dictionary dictionary4 = (vaultData.Keys = new Dictionary()); } vaultData = data; if (vaultData.CustomCurrencies == null) { Dictionary dictionary4 = (vaultData.CustomCurrencies = new Dictionary()); } vaultData = data; if (vaultData.Tickets == null) { Dictionary dictionary4 = (vaultData.Tickets = new Dictionary()); } vaultData = data; if (vaultData.Orbs == null) { Dictionary dictionary4 = (vaultData.Orbs = new Dictionary()); } foreach (SeasonalTokenType value in Enum.GetValues(typeof(SeasonalTokenType))) { if (!data.SeasonalTokens.ContainsKey(value)) { data.SeasonalTokens[value] = 0; } } foreach (SeasonalTokenType item in new List(data.SeasonalTokens.Keys)) { if (data.SeasonalTokens[item] < 0) { data.SeasonalTokens[item] = 0; num++; } } num += ClampStringKeyedDict(data.CommunityTokens); num += NormalizeDictKeysLower(data.Keys); num += NormalizeDictKeysLower(data.Tickets); num += ClampStringKeyedDict(data.Keys); num += ClampStringKeyedDict(data.CustomCurrencies); num += ClampStringKeyedDict(data.Tickets); return num + ClampStringKeyedDict(data.Orbs); } private static int NormalizeDictKeysLower(Dictionary dict) { if (dict == null || dict.Count == 0) { return 0; } Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (KeyValuePair item in dict) { string key = (string.IsNullOrEmpty(item.Key) ? "" : item.Key.ToLowerInvariant()); if (dictionary.ContainsKey(key)) { dictionary[key] += item.Value; } else { dictionary[key] = item.Value; } } bool flag = dictionary.Count == dict.Count; if (flag) { foreach (KeyValuePair item2 in dict) { string text = (string.IsNullOrEmpty(item2.Key) ? "" : item2.Key.ToLowerInvariant()); if (item2.Key != text || dictionary[text] != item2.Value) { flag = false; break; } } } if (flag) { return 0; } dict.Clear(); foreach (KeyValuePair item3 in dictionary) { dict[item3.Key] = item3.Value; } return 1; } private static int ClampStringKeyedDict(Dictionary dict) { int num = 0; foreach (string item in new List(dict.Keys)) { if (dict[item] < 0) { dict[item] = 0; num++; } } return num; } } public class VaultManager { private VaultData _vaultData; private Dictionary _currencyDefinitions; private bool _isDirty; public bool IsDirty => _isDirty; public event Action OnCurrencyChanged; public event Action OnVaultLoaded; public VaultManager() { _vaultData = new VaultData(); _currencyDefinitions = new Dictionary(); _isDirty = false; InitializeCurrencyDefinitions(); } private void InitializeCurrencyDefinitions() { RegisterCurrency(new CurrencyDefinition("token_spring", "Spring Token", CurrencyCategory.SeasonalToken, 18020)); RegisterCurrency(new CurrencyDefinition("token_summer", "Summer Token", CurrencyCategory.SeasonalToken, 18021)); RegisterCurrency(new CurrencyDefinition("token_fall", "Fall Token", CurrencyCategory.SeasonalToken, 18023)); RegisterCurrency(new CurrencyDefinition("token_winter", "Winter Token", CurrencyCategory.SeasonalToken, 18022)); RegisterCurrency(new CurrencyDefinition("key_copper", "Copper Key", CurrencyCategory.Key, 1251)); RegisterCurrency(new CurrencyDefinition("key_iron", "Iron Key", CurrencyCategory.Key, 1252)); RegisterCurrency(new CurrencyDefinition("key_adamant", "Adamant Key", CurrencyCategory.Key, 1253)); RegisterCurrency(new CurrencyDefinition("key_mithril", "Mithril Key", CurrencyCategory.Key, 1254)); RegisterCurrency(new CurrencyDefinition("key_sunite", "Sunite Key", CurrencyCategory.Key, 1255)); RegisterCurrency(new CurrencyDefinition("key_glorite", "Glorite Key", CurrencyCategory.Key, 1256)); RegisterCurrency(new CurrencyDefinition("key_kingslostmine", "King's Lost Mine Key", CurrencyCategory.Key, 1257)); RegisterCurrency(new CurrencyDefinition("special_communitytoken", "Community Token", CurrencyCategory.Special, 18013)); RegisterCurrency(new CurrencyDefinition("special_doubloon", "Doubloon", CurrencyCategory.Special, 60014)); RegisterCurrency(new CurrencyDefinition("special_blackbottlecap", "Black Bottle Cap", CurrencyCategory.Special, 60013)); RegisterCurrency(new CurrencyDefinition("special_redcarnivalticket", "Red Carnival Ticket", CurrencyCategory.Special, 18012)); RegisterCurrency(new CurrencyDefinition("special_candycornpieces", "Candy Corn Pieces", CurrencyCategory.Special, 18016)); RegisterCurrency(new CurrencyDefinition("special_manashard", "Mana Shard", CurrencyCategory.Special, 18015)); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Initialized {_currencyDefinitions.Count} currency definitions"); } } public void RegisterCurrency(CurrencyDefinition definition) { _currencyDefinitions[definition.Id] = definition; } public CurrencyDefinition GetCurrencyDefinition(string currencyId) { if (!_currencyDefinitions.TryGetValue(currencyId, out CurrencyDefinition value)) { return null; } return value; } public CurrencyDefinition GetCurrencyByGameItemId(int gameItemId) { foreach (CurrencyDefinition value in _currencyDefinitions.Values) { if (value.GameItemId == gameItemId) { return value; } } return null; } public IEnumerable GetAllCurrencies() { return _currencyDefinitions.Values; } public IEnumerable GetCurrenciesByCategory(CurrencyCategory category) { foreach (KeyValuePair currencyDefinition in _currencyDefinitions) { if (currencyDefinition.Value.Category == category) { yield return currencyDefinition.Value; } } } public void LoadVaultData(VaultData data) { _vaultData = data ?? new VaultData(); int num = VaultDataSanitizer.Sanitize(_vaultData); if (num > 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"[Vault] Sanitized vault data ({num} correction(s)); negative or missing fields were normalized."); } } _isDirty = false; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Vault data loaded for player: " + _vaultData.PlayerName)); } this.OnVaultLoaded?.Invoke(); } public VaultData GetVaultData() { _vaultData.LastSaved = DateTime.Now; return _vaultData; } public void MarkClean() { _isDirty = false; } public void SetPlayerName(string playerName) { _vaultData.PlayerName = playerName; _isDirty = true; } public int GetSeasonalTokens(SeasonalTokenType type) { if (!_vaultData.SeasonalTokens.TryGetValue(type, out var value)) { return 0; } return value; } public bool AddSeasonalTokens(SeasonalTokenType type, int amount) { if (amount < 0) { return false; } int seasonalTokens = GetSeasonalTokens(type); _vaultData.SeasonalTokens[type] = seasonalTokens + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke(VaultCurrencyIds.FullSeasonalFromTokenName(type.ToString()), seasonalTokens, seasonalTokens + amount); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Added {amount} {type} tokens. New total: {_vaultData.SeasonalTokens[type]}"); } return true; } public bool RemoveSeasonalTokens(SeasonalTokenType type, int amount) { if (amount < 0) { return false; } int seasonalTokens = GetSeasonalTokens(type); if (seasonalTokens < amount) { return false; } int arg = seasonalTokens; _vaultData.SeasonalTokens[type] = seasonalTokens - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke(VaultCurrencyIds.FullSeasonalFromTokenName(type.ToString()), arg, seasonalTokens - amount); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Removed {amount} {type} tokens. New total: {_vaultData.SeasonalTokens[type]}"); } return true; } public bool HasSeasonalTokens(SeasonalTokenType type, int amount) { return GetSeasonalTokens(type) >= amount; } public int GetCommunityTokens(string tokenId) { if (!_vaultData.CommunityTokens.TryGetValue(tokenId, out var value)) { return 0; } return value; } public bool AddCommunityTokens(string tokenId, int amount) { if (amount < 0 || string.IsNullOrEmpty(tokenId)) { return false; } int communityTokens = GetCommunityTokens(tokenId); _vaultData.CommunityTokens[tokenId] = communityTokens + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("community_" + tokenId, communityTokens, communityTokens + amount); return true; } public bool RemoveCommunityTokens(string tokenId, int amount) { if (amount < 0 || string.IsNullOrEmpty(tokenId)) { return false; } int communityTokens = GetCommunityTokens(tokenId); if (communityTokens < amount) { return false; } int arg = communityTokens; _vaultData.CommunityTokens[tokenId] = communityTokens - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("community_" + tokenId, arg, communityTokens - amount); return true; } public bool HasCommunityTokens(string tokenId, int amount) { return GetCommunityTokens(tokenId) >= amount; } public int GetKeys(string keyId) { if (!_vaultData.Keys.TryGetValue(keyId, out var value)) { return 0; } return value; } public bool AddKeys(string keyId, int amount) { if (amount < 0 || string.IsNullOrEmpty(keyId)) { return false; } int keys = GetKeys(keyId); _vaultData.Keys[keyId] = keys + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("key_" + keyId, keys, keys + amount); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Added {amount} {keyId} keys. New total: {_vaultData.Keys[keyId]}"); } return true; } public bool RemoveKeys(string keyId, int amount) { if (amount < 0 || string.IsNullOrEmpty(keyId)) { return false; } int keys = GetKeys(keyId); if (keys < amount) { return false; } int arg = keys; _vaultData.Keys[keyId] = keys - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("key_" + keyId, arg, keys - amount); return true; } public bool HasKeys(string keyId, int amount) { return GetKeys(keyId) >= amount; } public int GetSpecial(string specialId) { if (!_vaultData.Tickets.TryGetValue(specialId, out var value)) { return 0; } return value; } public bool AddSpecial(string specialId, int amount) { if (amount < 0 || string.IsNullOrEmpty(specialId)) { return false; } int special = GetSpecial(specialId); _vaultData.Tickets[specialId] = special + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("special_" + specialId, special, special + amount); return true; } public bool RemoveSpecial(string specialId, int amount) { if (amount < 0 || string.IsNullOrEmpty(specialId)) { return false; } int special = GetSpecial(specialId); if (special < amount) { return false; } int arg = special; _vaultData.Tickets[specialId] = special - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("special_" + specialId, arg, special - amount); return true; } public bool HasSpecial(string specialId, int amount) { return GetSpecial(specialId) >= amount; } public int GetTickets(string ticketId) { return GetSpecial(ticketId); } public bool AddTickets(string ticketId, int amount) { return AddSpecial(ticketId, amount); } public bool RemoveTickets(string ticketId, int amount) { return RemoveSpecial(ticketId, amount); } public bool HasTickets(string ticketId, int amount) { return HasSpecial(ticketId, amount); } public int GetOrbs(string orbId) { if (!_vaultData.Orbs.TryGetValue(orbId, out var value)) { return 0; } return value; } public bool AddOrbs(string orbId, int amount) { if (amount < 0 || string.IsNullOrEmpty(orbId)) { return false; } int orbs = GetOrbs(orbId); _vaultData.Orbs[orbId] = orbs + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("orb_" + orbId, orbs, orbs + amount); return true; } public bool RemoveOrbs(string orbId, int amount) { if (amount < 0 || string.IsNullOrEmpty(orbId)) { return false; } int orbs = GetOrbs(orbId); if (orbs < amount) { return false; } int arg = orbs; _vaultData.Orbs[orbId] = orbs - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke("orb_" + orbId, arg, orbs - amount); return true; } public bool HasOrbs(string orbId, int amount) { return GetOrbs(orbId) >= amount; } public int GetCustomCurrency(string currencyId) { if (!_vaultData.CustomCurrencies.TryGetValue(currencyId, out var value)) { return 0; } return value; } public bool AddCustomCurrency(string currencyId, int amount) { if (amount < 0 || string.IsNullOrEmpty(currencyId)) { return false; } int customCurrency = GetCustomCurrency(currencyId); _vaultData.CustomCurrencies[currencyId] = customCurrency + amount; _isDirty = true; this.OnCurrencyChanged?.Invoke(VaultCurrencyIds.FullCustom(currencyId), customCurrency, customCurrency + amount); return true; } public bool RemoveCustomCurrency(string currencyId, int amount) { if (amount < 0 || string.IsNullOrEmpty(currencyId)) { return false; } int customCurrency = GetCustomCurrency(currencyId); if (customCurrency < amount) { return false; } int arg = customCurrency; _vaultData.CustomCurrencies[currencyId] = customCurrency - amount; _isDirty = true; this.OnCurrencyChanged?.Invoke(VaultCurrencyIds.FullCustom(currencyId), arg, customCurrency - amount); return true; } public bool HasCustomCurrency(string currencyId, int amount) { return GetCustomCurrency(currencyId) >= amount; } public int GetCurrency(string fullCurrencyId) { if (string.IsNullOrEmpty(fullCurrencyId)) { return 0; } if (fullCurrencyId.StartsWith("seasonal_")) { if (Enum.TryParse(fullCurrencyId.Substring("seasonal_".Length), out var result)) { return GetSeasonalTokens(result); } } else { if (fullCurrencyId.StartsWith("community_")) { return GetCommunityTokens(fullCurrencyId.Substring("community_".Length)); } if (fullCurrencyId.StartsWith("key_")) { return GetKeys(fullCurrencyId.Substring("key_".Length)); } if (fullCurrencyId.StartsWith("special_")) { return GetSpecial(fullCurrencyId.Substring("special_".Length)); } if (fullCurrencyId.StartsWith("orb_")) { return GetOrbs(fullCurrencyId.Substring("orb_".Length)); } if (fullCurrencyId.StartsWith("custom_")) { return GetCustomCurrency(fullCurrencyId.Substring("custom_".Length)); } } return 0; } public bool HasCurrency(string fullCurrencyId, int amount) { return GetCurrency(fullCurrencyId) >= amount; } public Dictionary GetAllNonZeroCurrencies() { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair seasonalToken in _vaultData.SeasonalTokens) { if (seasonalToken.Value > 0) { dictionary[VaultCurrencyIds.FullSeasonalFromTokenName(seasonalToken.Key.ToString())] = seasonalToken.Value; } } foreach (KeyValuePair communityToken in _vaultData.CommunityTokens) { if (communityToken.Value > 0) { dictionary["community_" + communityToken.Key] = communityToken.Value; } } foreach (KeyValuePair key in _vaultData.Keys) { if (key.Value > 0) { dictionary["key_" + key.Key] = key.Value; } } foreach (KeyValuePair ticket in _vaultData.Tickets) { if (ticket.Value > 0) { dictionary["special_" + ticket.Key] = ticket.Value; } } foreach (KeyValuePair orb in _vaultData.Orbs) { if (orb.Value > 0) { dictionary["orb_" + orb.Key] = orb.Value; } } foreach (KeyValuePair customCurrency in _vaultData.CustomCurrencies) { if (customCurrency.Value > 0) { dictionary[VaultCurrencyIds.FullCustom(customCurrency.Key)] = customCurrency.Value; } } return dictionary; } public void MergeOrphanCustomBalances(Dictionary keyedByFullCurrencyId) { if (keyedByFullCurrencyId == null || _vaultData?.CustomCurrencies == null) { return; } foreach (KeyValuePair customCurrency in _vaultData.CustomCurrencies) { string key = VaultCurrencyIds.FullCustom(customCurrency.Key); if (!keyedByFullCurrencyId.ContainsKey(key)) { keyedByFullCurrencyId[key] = customCurrency.Value; } } } public void FillDebugCustomHud(Dictionary dest) { if (dest == null) { return; } dest.Clear(); if (_vaultData == null) { return; } Dictionary dictionary = new Dictionary(); foreach (CurrencyDefinition item in GetCurrenciesByCategory(CurrencyCategory.Custom)) { string key = VaultCurrencyIds.FullCustom(item.Id); dictionary[key] = GetCustomCurrency(item.Id); } if (_vaultData.CustomCurrencies != null) { foreach (KeyValuePair customCurrency in _vaultData.CustomCurrencies) { string key2 = VaultCurrencyIds.FullCustom(customCurrency.Key); if (!dictionary.ContainsKey(key2)) { dictionary[key2] = customCurrency.Value; } } } List list = new List(dictionary.Keys); list.Sort(StringComparer.Ordinal); foreach (string item2 in list) { dest[item2] = dictionary[item2]; } } public List BuildDebugVaultReport() { List list = new List(); if (_vaultData == null) { list.Add("(no vault data)"); return list; } list.Add("PlayerName=" + _vaultData.PlayerName); list.Add($"LastSaved={_vaultData.LastSaved:yyyy-MM-dd HH:mm:ss}"); list.Add($"Version={_vaultData.Version}"); list.Add(""); list.Add("[SeasonalTokens]"); foreach (SeasonalTokenType value2 in Enum.GetValues(typeof(SeasonalTokenType))) { int value; int num = ((_vaultData.SeasonalTokens != null && _vaultData.SeasonalTokens.TryGetValue(value2, out value)) ? value : 0); list.Add($" {value2} = {num}"); } list.Add(""); list.Add("[CommunityTokens]"); AppendSortedStringDict(list, _vaultData.CommunityTokens); list.Add(""); list.Add("[Keys]"); AppendSortedStringDict(list, _vaultData.Keys); list.Add(""); list.Add("[Tickets / special_* backing store]"); AppendSortedStringDict(list, _vaultData.Tickets); list.Add(""); list.Add("[Orbs]"); AppendSortedStringDict(list, _vaultData.Orbs); list.Add(""); list.Add("[CustomCurrencies]"); AppendSortedStringDict(list, _vaultData.CustomCurrencies); return list; } private static void AppendSortedStringDict(List lines, Dictionary dict) { if (dict == null || dict.Count == 0) { lines.Add(" (empty)"); return; } List list = new List(dict.Keys); list.Sort(StringComparer.Ordinal); foreach (string item in list) { lines.Add($" {item} = {dict[item]}"); } } } public class VaultSaveSystem { private readonly string _saveDirectory; private readonly VaultManager _vaultManager; private string _currentSaveFile; private static string _cachedSteamId = null; private static bool _steamIdChecked = false; private const string ENCRYPTION_SALT = "TheV4ultS@lt2026Secure"; private const int KEY_SIZE = 256; private const int ITERATIONS = 10000; private static readonly byte[] _iv = new byte[16] { 67, 117, 114, 114, 101, 110, 99, 121, 83, 112, 101, 108, 108, 73, 86, 49 }; private float _autoSaveIntervalSeconds = 300f; private float _lastAutoSave; private bool _needsReEncryption; public bool LastLoadQuarantinedCorruptFile { get; private set; } public VaultSaveSystem(VaultManager vaultManager) { _vaultManager = vaultManager; _saveDirectory = Path.Combine(Paths.ConfigPath, "TheVault", "Saves"); _lastAutoSave = Time.time; if (!Directory.Exists(_saveDirectory)) { Directory.CreateDirectory(_saveDirectory); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Created save directory: " + _saveDirectory)); } } MigrateOldSaves(); } private void MigrateOldSaves() { try { string path = Path.Combine(Paths.ConfigPath, "CurrencySpell", "Saves"); if (!Directory.Exists(path)) { return; } string[] files = Directory.GetFiles(path, "*.vault"); if (files.Length == 0) { return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Found {files.Length} save files in old CurrencySpell folder, migrating..."); } string[] array = files; foreach (string text in array) { string fileName = Path.GetFileName(text); string text2 = Path.Combine(_saveDirectory, fileName); if (!File.Exists(text2)) { File.Copy(text, text2); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Migrated save: " + fileName)); } } } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)"Save migration complete. You can delete the old CurrencySpell folder if desired."); } } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)("Failed to migrate old saves: " + ex.Message)); } } } private string GetSaveFilePath(string playerName) { string text = SanitizeFileName(playerName); if (string.IsNullOrEmpty(text)) { text = "default"; } string perPlayerFileSuffix = GetPerPlayerFileSuffix(playerName); return Path.Combine(_saveDirectory, text + "_" + perPlayerFileSuffix + ".vault"); } private string GetLegacySaveFilePath(string playerName) { string text = SanitizeFileName(playerName); if (string.IsNullOrEmpty(text)) { text = "default"; } return Path.Combine(_saveDirectory, text + ".vault"); } private string GetPerPlayerFileSuffix(string playerName) { string text = TryGetSteamId(); if (!string.IsNullOrEmpty(text)) { return "steam_" + SanitizeFileName(text); } try { using SHA256 sHA = SHA256.Create(); string s = SystemInfo.deviceUniqueIdentifier + "\0" + playerName; byte[] array = sHA.ComputeHash(Encoding.UTF8.GetBytes(s)); StringBuilder stringBuilder = new StringBuilder(16); for (int i = 0; i < 8; i++) { stringBuilder.Append(array[i].ToString("x2")); } return "local_" + stringBuilder.ToString(); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[VaultSave] local suffix fallback: " + ex.Message)); } return "local_unknown"; } } private static bool TryQuarantineUnreadableFile(string filePath, string reason) { if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath)) { return false; } try { string path = Path.GetDirectoryName(filePath) ?? ""; string text = DateTime.UtcNow.ToString("yyyyMMdd-HHmmss"); string text2 = Path.Combine(path, Path.GetFileNameWithoutExtension(filePath) + ".corrupt-" + text + ".bak"); int num = 0; while (File.Exists(text2)) { num++; text2 = Path.Combine(path, Path.GetFileNameWithoutExtension(filePath) + ".corrupt-" + text + "-" + num + ".bak"); } File.Move(filePath, text2); ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("[VaultSave] Quarantined unreadable vault to: " + text2 + " (" + reason + ")")); } return true; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("[VaultSave] Failed to quarantine corrupt vault file: " + ex.Message)); } return false; } } private string SanitizeFileName(string name) { if (string.IsNullOrEmpty(name)) { return ""; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } return name.Trim(); } public bool Load(string playerName) { LastLoadQuarantinedCorruptFile = false; if (!IsValidPlayerName(playerName)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[VaultSave] Load skipped: player name was null/empty/invalid."); } return false; } try { string saveFilePath = GetSaveFilePath(playerName); string legacySaveFilePath = GetLegacySaveFilePath(playerName); _currentSaveFile = saveFilePath; string text = null; bool flag = false; if (File.Exists(saveFilePath)) { text = saveFilePath; } else if (File.Exists(legacySaveFilePath)) { text = legacySaveFilePath; flag = true; } if (text == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("No existing save file for player '" + playerName + "', creating new vault")); } VaultData data = new VaultData { PlayerName = playerName }; _vaultManager.LoadVaultData(data); return true; } byte[] array = File.ReadAllBytes(text); string text2 = Decrypt(array, playerName); if (string.IsNullOrEmpty(text2)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Current decryption failed, attempting legacy migration for '" + playerName + "'...")); } text2 = TryLegacyDecryption(array, playerName); if (!string.IsNullOrEmpty(text2)) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"Legacy decryption successful - will re-encrypt with new method on save"); } _needsReEncryption = true; } } if (string.IsNullOrEmpty(text2)) { TryQuarantineUnreadableFile(text, "all decryption paths failed"); LastLoadQuarantinedCorruptFile = true; ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)("[VaultSave] Vault file for '" + playerName + "' could not be decrypted. It was moved to a .corrupt-*.bak file in your Saves folder. Starting an empty vault in memory — restore the backup manually if you need the old data.")); } _vaultManager.LoadVaultData(new VaultData { PlayerName = playerName }); return true; } VaultDataWrapper vaultDataWrapper = JsonUtility.FromJson(text2); VaultData data2; if (vaultDataWrapper != null) { data2 = vaultDataWrapper.ToVaultData(); } else { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogWarning((object)"[VaultSave] Failed to deserialize vault JSON after decrypt; quarantining file."); } TryQuarantineUnreadableFile(text, "JSON deserialize failed"); LastLoadQuarantinedCorruptFile = true; data2 = new VaultData { PlayerName = playerName }; } data2 = MigrateData(data2); _vaultManager.LoadVaultData(data2); ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)("Loaded vault data for player '" + playerName + "'")); } if (_needsReEncryption) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)"Re-encrypting vault with new method..."); } Save(); _needsReEncryption = false; ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)"Vault successfully migrated to new encryption!"); } } if (flag && File.Exists(legacySaveFilePath)) { try { ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogInfo((object)"[VaultSave] Migrating legacy save filename (Steam/local suffix)..."); } Save(); if (File.Exists(saveFilePath)) { File.Delete(legacySaveFilePath); ManualLogSource log11 = Plugin.Log; if (log11 != null) { log11.LogInfo((object)("[VaultSave] Migrated to new path and removed legacy file: " + legacySaveFilePath)); } } } catch (Exception ex) { ManualLogSource log12 = Plugin.Log; if (log12 != null) { log12.LogWarning((object)("[VaultSave] Legacy filename migration: " + ex.Message)); } } } return true; } catch (Exception ex2) { ManualLogSource log13 = Plugin.Log; if (log13 != null) { log13.LogError((object)("Failed to load vault data: " + ex2.Message)); } string saveFilePath2 = GetSaveFilePath(playerName); string legacySaveFilePath2 = GetLegacySaveFilePath(playerName); _currentSaveFile = saveFilePath2; string text3 = (File.Exists(saveFilePath2) ? saveFilePath2 : legacySaveFilePath2); if (File.Exists(text3)) { TryQuarantineUnreadableFile(text3, "exception during load: " + ex2.Message); LastLoadQuarantinedCorruptFile = true; } _vaultManager.LoadVaultData(new VaultData { PlayerName = playerName }); return true; } } private string TryLegacyDecryption(byte[] encryptedData, string playerName) { string[] array = new string[3] { "TheV4ultS@lt2026Secure_" + playerName + "_TheVaultPortable", "TheV4ultS@lt2026Secure_Player_" + playerName + "_TheVaultPortable", "TheV4ultS@lt2026Secure_" + playerName + "_" + GetMachineId() }; foreach (string combined in array) { try { byte[] key = GenerateLegacyKey(combined); string text = DecryptWithKey(encryptedData, key); if (!string.IsNullOrEmpty(text) && text.Contains("PlayerName")) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Successfully decrypted with legacy method"); } return text; } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("[VaultSave] Legacy decryption attempt failed: " + ex.Message)); } } } return null; } private string GetMachineId() { try { return SystemInfo.deviceUniqueIdentifier; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[VaultSave] Could not resolve machine id for legacy decryption fallback: " + ex.Message)); } return "unknown"; } } private byte[] GenerateLegacyKey(string combined) { using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes(combined, Encoding.UTF8.GetBytes("TheV4ultS@lt2026Secure"), 10000); return rfc2898DeriveBytes.GetBytes(32); } private string DecryptWithKey(byte[] encryptedData, byte[] key) { try { using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform cryptoTransform = aes.CreateDecryptor(); byte[] bytes = cryptoTransform.TransformFinalBlock(encryptedData, 0, encryptedData.Length); return Encoding.UTF8.GetString(bytes); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[VaultSave] DecryptWithKey failed: " + ex.Message)); } return null; } } public bool Save() { if (string.IsNullOrEmpty(_currentSaveFile)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"No save file set, cannot save"); } return false; } try { VaultData vaultData = _vaultManager.GetVaultData(); if (!IsValidPlayerName(vaultData?.PlayerName)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[VaultSave] Save skipped: current vault player name was null/empty/invalid."); } return false; } string plainText = JsonUtility.ToJson((object)VaultDataWrapper.FromVaultData(vaultData), true); byte[] bytes = Encrypt(plainText, vaultData.PlayerName); string text = _currentSaveFile + ".tmp"; File.WriteAllBytes(text, bytes); if (File.Exists(_currentSaveFile)) { string text2 = _currentSaveFile + ".backup"; if (File.Exists(text2)) { File.Delete(text2); } File.Move(_currentSaveFile, text2); } File.Move(text, _currentSaveFile); _vaultManager.MarkClean(); _lastAutoSave = Time.time; ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Saved vault data to " + _currentSaveFile)); } return true; } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("Failed to save vault data: " + ex.Message)); } return false; } } public void ForceSave() { if (_vaultManager.IsDirty) { Save(); } } public void CheckAutoSave() { if (_vaultManager.IsDirty && Time.time - _lastAutoSave >= _autoSaveIntervalSeconds) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Auto-saving vault data..."); } Save(); } } public void SetAutoSaveIntervalSeconds(float seconds) { _autoSaveIntervalSeconds = Mathf.Max(10f, seconds); } private VaultData MigrateData(VaultData data) { return VaultDataMigration.Migrate(data, delegate(string message) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)message); } }); } public bool DeleteSave(string playerName) { if (!IsValidPlayerName(playerName)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[VaultSave] DeleteSave skipped: player name was null/empty/invalid."); } return false; } try { string[] array = new string[2] { GetSaveFilePath(playerName), GetLegacySaveFilePath(playerName) }; foreach (string text in array) { if (File.Exists(text)) { File.Delete(text); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Deleted vault save file: " + text)); } } string path = text + ".backup"; if (File.Exists(path)) { File.Delete(path); } } return true; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Failed to delete vault save: " + ex.Message)); } return false; } } public string[] GetAllSavedPlayers() { try { string[] files = Directory.GetFiles(_saveDirectory, "*.vault"); string[] array = new string[files.Length]; for (int i = 0; i < files.Length; i++) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(files[i]); array[i] = fileNameWithoutExtension; } return array; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Failed to get saved players: " + ex.Message)); } return Array.Empty(); } } public bool ExportVault(string exportPath) { try { string contents = JsonUtility.ToJson((object)VaultDataWrapper.FromVaultData(_vaultManager.GetVaultData()), true); File.WriteAllText(exportPath, contents); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Exported vault to " + exportPath)); } return true; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Failed to export vault: " + ex.Message)); } return false; } } public bool ImportVault(string importPath) { try { if (!File.Exists(importPath)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Import file not found: " + importPath)); } return false; } VaultDataWrapper vaultDataWrapper = JsonUtility.FromJson(File.ReadAllText(importPath)); if (vaultDataWrapper == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)"Failed to parse import file"); } return false; } VaultData data = vaultDataWrapper.ToVaultData(); data = MigrateData(data); _vaultManager.LoadVaultData(data); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Imported vault from " + importPath)); } return true; } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("Failed to import vault: " + ex.Message)); } return false; } } private static bool IsValidPlayerName(string playerName) { if (string.IsNullOrWhiteSpace(playerName)) { return false; } return !string.Equals(playerName.Trim(), "null", StringComparison.OrdinalIgnoreCase); } private static string TryGetSteamId() { if (_steamIdChecked) { return _cachedSteamId; } _steamIdChecked = true; _cachedSteamId = null; try { Assembly assembly = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { string name = assembly2.GetName().Name; if (name.Contains("rlabrecque") || name == "Steamworks.NET") { assembly = assembly2; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[VaultSave] Found Steamworks assembly: " + name)); } break; } } if (assembly == null) { assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly3 in assemblies) { try { if (assembly3.GetType("Steamworks.SteamUser") != null) { assembly = assembly3; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("[VaultSave] Found SteamUser in assembly: " + assembly3.GetName().Name)); } break; } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogDebug((object)("[VaultSave] Skipping assembly for Steam search: " + ex.Message)); } } } } if (assembly == null) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)"[VaultSave] Steam assembly not found - using player name for encryption"); } return null; } Type type = assembly.GetType("Steamworks.SteamUser"); if (type == null) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)"[VaultSave] SteamUser type not found in assembly"); } return null; } MethodInfo method = type.GetMethod("GetSteamID", BindingFlags.Static | BindingFlags.Public); if (method == null) { ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)"[VaultSave] GetSteamID method not found"); } return null; } object obj = method.Invoke(null, null); if (obj == null) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogInfo((object)"[VaultSave] GetSteamID returned null"); } return null; } string text = obj.ToString(); if (string.IsNullOrEmpty(text) || text == "0" || text.Length < 10) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogInfo((object)("[VaultSave] Invalid Steam ID: " + text)); } return null; } _cachedSteamId = text; ManualLogSource log9 = Plugin.Log; if (log9 != null) { log9.LogInfo((object)$"[VaultSave] Successfully retrieved Steam ID for encryption (length: {text.Length})"); } return _cachedSteamId; } catch (Exception ex2) { ManualLogSource log10 = Plugin.Log; if (log10 != null) { log10.LogWarning((object)("[VaultSave] Failed to get Steam ID: " + ex2.Message)); } return null; } } private byte[] GenerateKey(string playerName) { string text = TryGetSteamId(); string text2; if (!string.IsNullOrEmpty(text)) { text2 = "Steam_" + text; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Using Steam ID for vault encryption (cross-device compatible)"); } } else { text2 = "Player_" + playerName; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Using player name for vault encryption (non-Steam mode)"); } } using Rfc2898DeriveBytes rfc2898DeriveBytes = new Rfc2898DeriveBytes("TheV4ultS@lt2026Secure_" + text2 + "_TheVaultPortable", Encoding.UTF8.GetBytes("TheV4ultS@lt2026Secure"), 10000); return rfc2898DeriveBytes.GetBytes(32); } private byte[] Encrypt(string plainText, string playerName) { byte[] key = GenerateKey(playerName); using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform transform = aes.CreateEncryptor(); using MemoryStream memoryStream = new MemoryStream(); byte[] bytes = Encoding.UTF8.GetBytes("CSVAULT2"); memoryStream.Write(bytes, 0, bytes.Length); using (CryptoStream stream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) { using StreamWriter streamWriter = new StreamWriter(stream, Encoding.UTF8); streamWriter.Write(plainText); } return memoryStream.ToArray(); } private string Decrypt(byte[] cipherData, string playerName) { try { if (cipherData.Length < 8) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Vault file too small, may be corrupted"); } return null; } if (Encoding.UTF8.GetString(cipherData, 0, 8) != "CSVAULT2") { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Detected legacy unencrypted vault file, will re-encrypt on save"); } return Encoding.UTF8.GetString(cipherData); } byte[] key = GenerateKey(playerName); using Aes aes = Aes.Create(); aes.Key = key; aes.IV = _iv; aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using ICryptoTransform transform = aes.CreateDecryptor(); using MemoryStream stream = new MemoryStream(cipherData, 8, cipherData.Length - 8); using CryptoStream stream2 = new CryptoStream(stream, transform, CryptoStreamMode.Read); using StreamReader streamReader = new StreamReader(stream2, Encoding.UTF8); return streamReader.ReadToEnd(); } catch (CryptographicException ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Decryption failed (file may have been tampered with): " + ex.Message)); } return null; } catch (Exception ex2) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("Error decrypting vault: " + ex2.Message)); } return null; } } } [Serializable] public class VaultDataWrapper { public int Version; public string PlayerName; public string LastSaved; public int[] SeasonalTokenTypes; public int[] SeasonalTokenValues; public string[] CommunityTokenKeys; public int[] CommunityTokenValues; public string[] KeyIds; public int[] KeyValues; public string[] TicketIds; public int[] TicketValues; public string[] OrbIds; public int[] OrbValues; public string[] CustomCurrencyIds; public int[] CustomCurrencyValues; public static VaultDataWrapper FromVaultData(VaultData data) { VaultDataWrapper vaultDataWrapper = new VaultDataWrapper { Version = data.Version, PlayerName = (data.PlayerName ?? ""), LastSaved = data.LastSaved.ToString("o") }; List list = new List(); List list2 = new List(); foreach (KeyValuePair seasonalToken in data.SeasonalTokens) { list.Add((int)seasonalToken.Key); list2.Add(seasonalToken.Value); } vaultDataWrapper.SeasonalTokenTypes = list.ToArray(); vaultDataWrapper.SeasonalTokenValues = list2.ToArray(); List list3 = new List(); List list4 = new List(); foreach (KeyValuePair communityToken in data.CommunityTokens) { list3.Add(communityToken.Key); list4.Add(communityToken.Value); } vaultDataWrapper.CommunityTokenKeys = list3.ToArray(); vaultDataWrapper.CommunityTokenValues = list4.ToArray(); List list5 = new List(); List list6 = new List(); foreach (KeyValuePair key in data.Keys) { list5.Add(key.Key); list6.Add(key.Value); } vaultDataWrapper.KeyIds = list5.ToArray(); vaultDataWrapper.KeyValues = list6.ToArray(); List list7 = new List(); List list8 = new List(); foreach (KeyValuePair ticket in data.Tickets) { list7.Add(ticket.Key); list8.Add(ticket.Value); } vaultDataWrapper.TicketIds = list7.ToArray(); vaultDataWrapper.TicketValues = list8.ToArray(); List list9 = new List(); List list10 = new List(); foreach (KeyValuePair orb in data.Orbs) { list9.Add(orb.Key); list10.Add(orb.Value); } vaultDataWrapper.OrbIds = list9.ToArray(); vaultDataWrapper.OrbValues = list10.ToArray(); List list11 = new List(); List list12 = new List(); foreach (KeyValuePair customCurrency in data.CustomCurrencies) { list11.Add(customCurrency.Key); list12.Add(customCurrency.Value); } vaultDataWrapper.CustomCurrencyIds = list11.ToArray(); vaultDataWrapper.CustomCurrencyValues = list12.ToArray(); return vaultDataWrapper; } public VaultData ToVaultData() { VaultData vaultData = new VaultData { Version = Version, PlayerName = (PlayerName ?? "") }; if (DateTime.TryParse(LastSaved, out var result)) { vaultData.LastSaved = result; } vaultData.SeasonalTokens.Clear(); if (SeasonalTokenTypes != null && SeasonalTokenValues != null) { int num = Math.Min(SeasonalTokenTypes.Length, SeasonalTokenValues.Length); for (int i = 0; i < num; i++) { if (Enum.IsDefined(typeof(SeasonalTokenType), SeasonalTokenTypes[i])) { vaultData.SeasonalTokens[(SeasonalTokenType)SeasonalTokenTypes[i]] = SeasonalTokenValues[i]; } } } vaultData.CommunityTokens.Clear(); if (CommunityTokenKeys != null && CommunityTokenValues != null) { int num2 = Math.Min(CommunityTokenKeys.Length, CommunityTokenValues.Length); for (int j = 0; j < num2; j++) { vaultData.CommunityTokens[CommunityTokenKeys[j]] = CommunityTokenValues[j]; } } vaultData.Keys.Clear(); if (KeyIds != null && KeyValues != null) { int num3 = Math.Min(KeyIds.Length, KeyValues.Length); for (int k = 0; k < num3; k++) { vaultData.Keys[KeyIds[k]] = KeyValues[k]; } } vaultData.Tickets.Clear(); if (TicketIds != null && TicketValues != null) { int num4 = Math.Min(TicketIds.Length, TicketValues.Length); for (int l = 0; l < num4; l++) { vaultData.Tickets[TicketIds[l]] = TicketValues[l]; } } vaultData.Orbs.Clear(); if (OrbIds != null && OrbValues != null) { int num5 = Math.Min(OrbIds.Length, OrbValues.Length); for (int m = 0; m < num5; m++) { vaultData.Orbs[OrbIds[m]] = OrbValues[m]; } } vaultData.CustomCurrencies.Clear(); if (CustomCurrencyIds != null && CustomCurrencyValues != null) { int num6 = Math.Min(CustomCurrencyIds.Length, CustomCurrencyValues.Length); for (int n = 0; n < num6; n++) { vaultData.CustomCurrencies[CustomCurrencyIds[n]] = CustomCurrencyValues[n]; } } return vaultData; } } } namespace TheVault.UI { public enum VaultHudDensity { Normal, Compact, Minimal } public class VaultHUD : MonoBehaviour { private struct Cell { public bool Sep; public string Id; public int Amount; } public enum HUDPosition { TopLeft, TopCenter, TopRight, BottomLeft, BottomCenter, BottomRight } private const int HudWindowId = 88420; private VaultManager _vaultManager; private bool _isEnabled = true; private HUDPosition _position; private bool _useCustomHudPlacement; private Rect _windowRect; private float _layoutOuterW; private float _layoutOuterH; private float _hudDragBandHeight; private GUIStyle _hudWindowStyle; private Texture2D _clearWindowTex; public Action OnPositionChanged; private float _opacity = 0.95f; private float _scale = 1f; private VaultHudDensity _hudDensity; private float _stylesForMul = -1f; private float _texturesForOpacity = -1f; private GUIStyle _bgStyle; private GUIStyle _borderStyle; private GUIStyle _valueStyle; private GUIStyle _shortStyle; private Texture2D _bgTex; private Texture2D _borderTex; private Texture2D _shadowTex; private Texture2D _sepTex; private readonly Color _bgTop = new Color(0.11f, 0.13f, 0.18f, 1f); private readonly Color _bgBot = new Color(0.06f, 0.07f, 0.1f, 1f); private readonly Color _border = new Color(0.3f, 0.36f, 0.46f, 0.62f); private readonly Color _shadow = new Color(0f, 0f, 0f, 0.3f); private readonly Color _accent = new Color(0.45f, 0.72f, 0.98f, 1f); private readonly Color _value = new Color(0.96f, 0.86f, 0.48f, 1f); private readonly Color _short = new Color(0.88f, 0.9f, 0.96f, 1f); private const float ScreenInset = 12f; private const float MaxHeightFrac = 0.58f; private const float MaxWidthFrac = 0.9f; private const float MaxWidthPx = 1200f; private const float BasePad = 10f; private const float BaseGap = 12f; private const float BaseRowGap = 5f; private const float BaseIcon = 22f; private const float BaseGroupGap = 16f; private float _lastUpdateTime; private const float UpdateInterval = 0.5f; private static bool _loggedQuarantineHudOnce; private readonly Dictionary _seasonal = new Dictionary(); private readonly Dictionary _keys = new Dictionary(); private readonly Dictionary _special = new Dictionary(); private readonly Dictionary _custom = new Dictionary(); private readonly List _cells = new List(64); private readonly List> _rows = new List>(10); private readonly Stack> _rowListPool = new Stack>(); public bool IsEnabled => _isEnabled; public void Initialize(VaultManager vaultManager) { _vaultManager = vaultManager; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"VaultHUD initialized"); } } public void SetEnabled(bool enabled) { _isEnabled = enabled; } public void SetPosition(HUDPosition position) { _position = position; } public void ClearCustomHudPlacement() { _useCustomHudPlacement = false; } public void RestoreHudPixelPosition(float x, float y) { ((Rect)(ref _windowRect)).x = x; ((Rect)(ref _windowRect)).y = y; _useCustomHudPlacement = true; } public void SetScale(float scale) { _scale = Mathf.Clamp(scale, 0.5f, 3f); _stylesForMul = -1f; } public void SetHudDensity(VaultHudDensity density) { _hudDensity = density; _stylesForMul = -1f; } public void SetOpacity(float opacity) { _opacity = Mathf.Clamp01(opacity); _stylesForMul = -1f; _texturesForOpacity = -1f; } public void Toggle() { _isEnabled = !_isEnabled; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("VaultHUD toggled: " + (_isEnabled ? "ON" : "OFF"))); } } private void Update() { if (!_loggedQuarantineHudOnce && Plugin.LastVaultLoadQuarantinedCorruptFile) { _loggedQuarantineHudOnce = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[The Vault] A vault file was unreadable and was quarantined (see LogOutput / BepInEx). An empty vault is in use — check TheVault/Saves for .corrupt-*.bak"); } } if (_isEnabled && _vaultManager != null && !(Time.time - _lastUpdateTime < 0.5f)) { RefreshCaches(); _lastUpdateTime = Time.time; } } private void RefreshCaches() { bool configDebugFullVaultInspector = Plugin.GetConfigDebugFullVaultInspector(); _seasonal.Clear(); string[] allSeasonalFullIds = VaultCurrencyIds.AllSeasonalFullIds; foreach (string text in allSeasonalFullIds) { int currency = _vaultManager.GetCurrency(text); if (configDebugFullVaultInspector || currency != 0) { _seasonal[text] = currency; } } _keys.Clear(); allSeasonalFullIds = VaultCurrencyIds.AllKeyFullIds; foreach (string text2 in allSeasonalFullIds) { int currency2 = _vaultManager.GetCurrency(text2); if (configDebugFullVaultInspector || currency2 != 0) { _keys[text2] = currency2; } } _special.Clear(); allSeasonalFullIds = VaultCurrencyIds.AllSpecialFullIds; foreach (string text3 in allSeasonalFullIds) { int currency3 = _vaultManager.GetCurrency(text3); if (configDebugFullVaultInspector || currency3 != 0) { _special[text3] = currency3; } } _custom.Clear(); if (configDebugFullVaultInspector) { _vaultManager.FillDebugCustomHud(_custom); return; } foreach (CurrencyDefinition item in _vaultManager.GetCurrenciesByCategory(CurrencyCategory.Custom)) { string text4 = VaultCurrencyIds.FullCustom(item.Id); int currency4 = _vaultManager.GetCurrency(text4); if (currency4 != 0) { _custom[text4] = currency4; } } } private float DensityMul() { return _hudDensity switch { VaultHudDensity.Minimal => 0.68f, VaultHudDensity.Compact => 0.82f, _ => 1f, }; } private void EnsureStyles(float mul) { //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Expected O, but got Unknown //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Expected O, but got Unknown //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Expected O, but got Unknown //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Expected O, but got Unknown //IL_00a6: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: 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_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_02b4: 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_0284: Expected O, but got Unknown mul = Mathf.Max(0.01f, mul); int num; if (!((Object)(object)_bgTex == (Object)null)) { num = ((Mathf.Abs(_opacity - _texturesForOpacity) > 0.0001f) ? 1 : 0); if (num == 0) { goto IL_0146; } } else { num = 1; } if ((Object)(object)_bgTex != (Object)null) { Object.Destroy((Object)(object)_bgTex); } if ((Object)(object)_borderTex != (Object)null) { Object.Destroy((Object)(object)_borderTex); } if ((Object)(object)_shadowTex != (Object)null) { Object.Destroy((Object)(object)_shadowTex); } if ((Object)(object)_sepTex != (Object)null) { Object.Destroy((Object)(object)_sepTex); } _bgTex = MakeVerticalGradientTex(8, 48, WithA(_bgTop, _opacity), WithA(_bgBot, _opacity)); _borderTex = MakeTex(2, 2, WithA(_border, _opacity)); _shadowTex = MakeTex(2, 2, WithA(_shadow, _opacity)); _sepTex = MakeTex(1, 1, new Color(1f, 1f, 1f, 0.35f * _opacity)); _texturesForOpacity = _opacity; goto IL_0146; IL_0146: if (num != 0 || !(Mathf.Abs(mul - _stylesForMul) < 0.001f) || _bgStyle == null) { _stylesForMul = mul; int num2 = Mathf.Max(2, Mathf.RoundToInt(10f * mul)); int fontSize = Mathf.Max(9, Mathf.RoundToInt(14f * mul)); int fontSize2 = Mathf.Max(8, Mathf.RoundToInt(13f * mul)); if (_bgStyle == null) { _bgStyle = new GUIStyle(); } _bgStyle.normal.background = _bgTex; _bgStyle.padding = new RectOffset(num2, num2, num2 / 2, num2 / 2); if (_borderStyle == null) { _borderStyle = new GUIStyle(); } _borderStyle.normal.background = _borderTex; if (_valueStyle == null) { _valueStyle = new GUIStyle(GUI.skin.label); } _valueStyle.fontSize = fontSize; _valueStyle.fontStyle = (FontStyle)1; _valueStyle.alignment = (TextAnchor)3; _valueStyle.normal.textColor = _value; if (_shortStyle == null) { _shortStyle = new GUIStyle(GUI.skin.label); } _shortStyle.fontSize = fontSize2; _shortStyle.fontStyle = (FontStyle)0; _shortStyle.alignment = (TextAnchor)3; _shortStyle.normal.textColor = _short; } } private static Color WithA(Color c, float a) { //IL_0000: 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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) return new Color(c.r, c.g, c.b, a); } private static Texture2D MakeTex(int w, int h, Color c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_0017: 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) Texture2D val = new Texture2D(w, h); Color[] array = (Color[])(object)new Color[w * h]; for (int i = 0; i < array.Length; i++) { array[i] = c; } val.SetPixels(array); val.Apply(); return val; } private static Texture2D MakeVerticalGradientTex(int w, int h, Color top, Color bot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_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) Texture2D val = new Texture2D(w, h); for (int i = 0; i < h; i++) { float num = ((h <= 1) ? 0f : ((float)i / (float)(h - 1))); Color val2 = Color.Lerp(top, bot, num); for (int j = 0; j < w; j++) { val.SetPixel(j, i, val2); } } val.Apply(); return val; } private void BuildCells() { _cells.Clear(); bool needSep = false; AppendOrdered(VaultCurrencyIds.AllSeasonalFullIds, _seasonal); AppendOrdered(VaultCurrencyIds.AllKeyFullIds, _keys); AppendOrdered(VaultCurrencyIds.AllSpecialFullIds, _special); if (_custom.Count > 0) { List list = new List(_custom.Keys); list.Sort(StringComparer.Ordinal); foreach (string item in list) { if (needSep) { _cells.Add(new Cell { Sep = true }); needSep = false; } _cells.Add(new Cell { Id = item, Amount = _custom[item] }); } } if (_cells.Count > 0 && _cells[_cells.Count - 1].Sep) { _cells.RemoveAt(_cells.Count - 1); } void AppendOrdered(string[] ordered, Dictionary values) { bool flag = false; foreach (string text in ordered) { if (values.TryGetValue(text, out var value)) { if (needSep) { _cells.Add(new Cell { Sep = true }); needSep = false; } _cells.Add(new Cell { Id = text, Amount = value }); flag = true; } } if (flag) { needSep = true; } } } private float CellWidth(Cell c, float mul) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (c.Sep) { return Mathf.Max(6f, 16f * mul); } float num = 22f * mul; float x = _valueStyle.CalcSize(new GUIContent(FormatNumber(c.Amount))).x; return (IconCache.IsIconLoaded(c.Id) ? num : Mathf.Max(num, _shortStyle.CalcSize(new GUIContent(GetShortName(c.Id))).x)) + 3f * mul + x + 2f; } private List RentRowList() { if (_rowListPool.Count <= 0) { return new List(16); } return _rowListPool.Pop(); } private void ReleaseAllRowsToPool() { for (int i = 0; i < _rows.Count; i++) { _rows[i].Clear(); _rowListPool.Push(_rows[i]); } _rows.Clear(); } private void WrapRows(float innerW, float mul) { ReleaseAllRowsToPool(); if (_cells.Count == 0) { return; } float num = 12f * mul; List row = RentRowList(); float w = 0f; for (int i = 0; i < _cells.Count; i++) { Cell cell = _cells[i]; float num2 = CellWidth(cell, mul); float num3 = ((row.Count > 0) ? num : 0f) + num2; if (row.Count > 0 && w + num3 > innerW + 0.5f) { Flush(); } if (row.Count > 0) { w += num; } row.Add(cell); w += num2; } Flush(); if (row.Count == 0) { _rowListPool.Push(row); } void Flush() { if (row.Count != 0) { _rows.Add(row); row = RentRowList(); w = 0f; } } } private float RowHeight(float mul) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) float num = 22f * mul; float y = _valueStyle.CalcSize(new GUIContent("0")).y; return Mathf.Max(num + 2f, y + 4f); } private Rect PlaceRect(float w, float h) { //IL_00e2: Unknown result type (might be due to invalid IL or missing references) w = Mathf.Min(w, (float)Screen.width - 24f); float num = 12f; float num2 = 12f; switch (_position) { case HUDPosition.TopLeft: num = 12f; num2 = 12f; break; case HUDPosition.TopCenter: num = ((float)Screen.width - w) / 2f; num2 = 12f; break; case HUDPosition.TopRight: num = (float)Screen.width - w - 12f; num2 = 12f; break; case HUDPosition.BottomLeft: num = 12f; num2 = (float)Screen.height - h - 12f; break; case HUDPosition.BottomCenter: num = ((float)Screen.width - w) / 2f; num2 = (float)Screen.height - h - 12f; break; case HUDPosition.BottomRight: num = (float)Screen.width - w - 12f; num2 = (float)Screen.height - h - 12f; break; } return new Rect(num, num2, w, h); } private void EnsureHudWindowStyle() { //IL_0048: 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_005e: 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_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_00a1: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (_hudWindowStyle == null) { if ((Object)(object)_clearWindowTex == (Object)null) { _clearWindowTex = MakeTex(2, 2, new Color(0f, 0f, 0f, 0f)); } GUIStyle val = new GUIStyle(GUI.skin.window); val.normal.background = _clearWindowTex; val.onNormal.background = _clearWindowTex; val.border = new RectOffset(0, 0, 0, 0); val.padding = new RectOffset(0, 0, 0, 0); val.margin = new RectOffset(0, 0, 0, 0); _hudWindowStyle = val; } } private void OnGUI() { //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_03be: Unknown result type (might be due to invalid IL or missing references) //IL_03c3: 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_03d8: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Expected O, but got Unknown //IL_03e8: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Unknown result type (might be due to invalid IL or missing references) if (!_isEnabled || _vaultManager == null || !PlayerPatches.IsVaultLoaded || Plugin.IsMainVaultPanelVisible()) { return; } if (Time.time - _lastUpdateTime > 0.5f) { RefreshCaches(); _lastUpdateTime = Time.time; } BuildCells(); if (_cells.Count == 0) { return; } float num = DensityMul(); float num2 = _scale * num; float num3 = Mathf.Min(new float[3] { (float)Screen.width - 24f, (float)Screen.width * 0.9f, 1200f }); float num4 = Mathf.Max(80f, (float)Screen.height * 0.58f - 24f); float num5 = 1f; float num6 = 10f * num2; float num7 = 5f * num2; float num8 = 220f; float num9 = 40f; float num10 = 200f; float num11 = 18f; for (int i = 0; i < 6; i++) { float num12 = num2 * num5; EnsureStyles(num12); num6 = 10f * num12; num10 = Mathf.Max(80f, num3 - 2f * num6); WrapRows(num10, num12); num11 = RowHeight(num12); float num13 = (float)_rows.Count * num11 + (float)Mathf.Max(0, _rows.Count - 1) * num7; num9 = Mathf.Max(28f * num12, num13 + 2f * num6 + 4f); float num14 = 0f; float num15 = 12f * num12; foreach (List row in _rows) { float num16 = 0f; for (int j = 0; j < row.Count; j++) { if (j > 0) { num16 += num15; } num16 += CellWidth(row[j], num12); } num14 = Mathf.Max(num14, num16); } num8 = Mathf.Clamp(num14 + 2f * num6, 100f, num3); float num17 = Mathf.Min(new float[3] { 1f, num4 / Mathf.Max(1f, num9) * 0.99f, num3 / Mathf.Max(1f, num8) * 0.99f }); if (Mathf.Abs(num17 - num5) < 0.01f) { break; } num5 = num17; } num8 = (_layoutOuterW = Mathf.Min(num8, (float)Screen.width - 24f)); _layoutOuterH = num9; float num18 = Mathf.Max(2f, 2.5f * _stylesForMul); _hudDragBandHeight = Mathf.Max(22f, num6 + num18 + 8f); ((Rect)(ref _windowRect)).width = num8; ((Rect)(ref _windowRect)).height = num9; if (!_useCustomHudPlacement) { Rect val = PlaceRect(num8, num9); ((Rect)(ref _windowRect)).x = ((Rect)(ref val)).x; ((Rect)(ref _windowRect)).y = ((Rect)(ref val)).y; } else { ((Rect)(ref _windowRect)).x = Mathf.Clamp(((Rect)(ref _windowRect)).x, 0f, Mathf.Max(0f, (float)Screen.width - ((Rect)(ref _windowRect)).width)); ((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, 0f, Mathf.Max(0f, (float)Screen.height - ((Rect)(ref _windowRect)).height)); } EnsureHudWindowStyle(); GUI.depth = -400; Rect windowRect = _windowRect; _windowRect = GUI.Window(88420, _windowRect, new WindowFunction(DrawHudWindow), "", _hudWindowStyle); ((Rect)(ref _windowRect)).x = Mathf.Clamp(((Rect)(ref _windowRect)).x, 0f, Mathf.Max(0f, (float)Screen.width - ((Rect)(ref _windowRect)).width)); ((Rect)(ref _windowRect)).y = Mathf.Clamp(((Rect)(ref _windowRect)).y, 0f, Mathf.Max(0f, (float)Screen.height - ((Rect)(ref _windowRect)).height)); if (Math.Abs(((Rect)(ref _windowRect)).x - ((Rect)(ref windowRect)).x) > 0.1f || Math.Abs(((Rect)(ref _windowRect)).y - ((Rect)(ref windowRect)).y) > 0.1f) { _useCustomHudPlacement = true; OnPositionChanged?.Invoke(((Rect)(ref _windowRect)).x, ((Rect)(ref _windowRect)).y); } } private void DrawHudWindow(int windowId) { //IL_004a: 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_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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_031f: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Expected O, but got Unknown //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Expected O, but got Unknown //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) float layoutOuterW = _layoutOuterW; float layoutOuterH = _layoutOuterH; float num = 10f * _stylesForMul; float num2 = Mathf.Max(2f, 2.5f * _stylesForMul); float num3 = RowHeight(_stylesForMul); GUI.DrawTexture(new Rect(3f, 3f, layoutOuterW, layoutOuterH), (Texture)(object)_shadowTex, (ScaleMode)0); GUI.DrawTexture(new Rect(-1f, -1f, layoutOuterW + 2f, layoutOuterH + 2f), (Texture)(object)_borderTex, (ScaleMode)0); GUI.Box(new Rect(0f, 0f, layoutOuterW, layoutOuterH), GUIContent.none, _bgStyle); GUI.DrawTexture(new Rect(1f, 1f, layoutOuterW - 2f, num2), (Texture)(object)Texture2D.whiteTexture, (ScaleMode)0, false, 0f, WithA(_accent, _opacity * 0.85f), 0f, 0f); GUI.BeginGroup(new Rect(0f, 0f, layoutOuterW, layoutOuterH)); float num4 = num; float num5 = num + num2 + 2f; float num6 = 12f * _stylesForMul; float num7 = 22f * _stylesForMul; for (int i = 0; i < _rows.Count; i++) { if (i > 0) { num5 += 5f * _stylesForMul; } float num8 = num4; List list = _rows[i]; for (int j = 0; j < list.Count; j++) { if (j > 0) { num8 += num6; } Cell cell = list[j]; if (cell.Sep) { float num9 = Mathf.Max(6f, 16f * _stylesForMul); GUI.DrawTexture(new Rect(num8 + num9 * 0.5f, num5 + 2f, 1f, num7 - 4f), (Texture)(object)_sepTex); num8 += num9; continue; } Texture2D iconForCurrency = IconCache.GetIconForCurrency(cell.Id); if ((Object)(object)iconForCurrency != (Object)null && IconCache.IsIconLoaded(cell.Id)) { GUI.DrawTexture(new Rect(num8, num5 + 1f, num7, num7), (Texture)(object)iconForCurrency, (ScaleMode)2); num8 += num7; } else { string shortName = GetShortName(cell.Id); float x = _shortStyle.CalcSize(new GUIContent(shortName)).x; GUI.Label(new Rect(num8, num5, Mathf.Max(num7, x), num3), shortName, _shortStyle); num8 += Mathf.Max(num7, x); } num8 += 3f * _stylesForMul; string text = FormatNumber(cell.Amount); float num10 = _valueStyle.CalcSize(new GUIContent(text)).x + 2f; GUI.Label(new Rect(num8, num5, num10, num3), text, _valueStyle); num8 += num10; } num5 += num3; } GUI.EndGroup(); GUI.DragWindow(new Rect(0f, 0f, layoutOuterW, _hudDragBandHeight)); } private void OnDestroy() { if ((Object)(object)_clearWindowTex != (Object)null) { Object.Destroy((Object)(object)_clearWindowTex); _clearWindowTex = null; } } private static string FormatNumber(int value) { if (value >= 1000000) { float num = (float)value / 1000000f; if (num >= 10f || num == (float)(int)num) { return $"{(int)num}M"; } return $"{num:0.#}M"; } if (value >= 1000) { float num2 = (float)value / 1000f; if (num2 >= 10f || num2 == (float)(int)num2) { return $"{(int)num2}K"; } return $"{num2:0.#}K"; } return value.ToString(); } private static string GetShortName(string currencyId) { if (currencyId.StartsWith("seasonal_")) { string text = currencyId.Substring("seasonal_".Length); return text switch { "Spring" => "Sp", "Summer" => "Su", "Fall" => "Fa", "Winter" => "Wi", _ => text.Substring(0, 2), }; } if (currencyId.StartsWith("key_")) { string text2 = currencyId.Substring("key_".Length); return text2 switch { "copper" => "Cu", "iron" => "Fe", "adamant" => "Ad", "mithril" => "Mi", "sunite" => "Su", "glorite" => "Gl", "kingslostmine" => "KL", _ => text2.Substring(0, 2), }; } if (currencyId.StartsWith("special_")) { string text3 = currencyId.Substring("special_".Length); return text3 switch { "communitytoken" => "CT", "doubloon" => "Db", "blackbottlecap" => "BB", "redcarnivalticket" => "RC", "candycornpieces" => "CC", "manashard" => "MS", _ => text3.Substring(0, 2).ToUpperInvariant(), }; } if (currencyId.StartsWith("custom_")) { string text4 = currencyId.Substring("custom_".Length); if (text4.Length <= 2) { return text4.ToUpperInvariant(); } return char.ToUpperInvariant(text4[0]).ToString() + char.ToUpperInvariant(text4[text4.Length - 1]); } return "??"; } } public static class VaultInventoryOperations { public static void WithdrawToInventory(VaultManager vaultManager, string currencyId, int amount, Action setStatus) { int currency = vaultManager.GetCurrency(currencyId); if (currency < amount) { setStatus?.Invoke($"Not enough in vault (have {currency}, need {amount}).", arg2: true); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"Not enough {currencyId} in vault (have {currency}, need {amount})"); } return; } int itemForCurrency = ItemPatches.GetItemForCurrency(currencyId); if (itemForCurrency < 0) { setStatus?.Invoke("This currency cannot be turned into an inventory item.", arg2: true); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("No item mapping found for currency " + currencyId)); } return; } if ((Object)(object)Player.Instance == (Object)null) { setStatus?.Invoke("Player not ready — try again in-game.", arg2: true); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"Player instance not found"); } return; } Inventory val = (Inventory)(((object)Player.Instance.PlayerInventory) ?? ((object)Player.Instance.Inventory)); if ((Object)(object)val == (Object)null) { setStatus?.Invoke("Inventory not available.", arg2: true); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)"Player inventory not found"); } return; } try { ItemPatches.IsWithdrawing = true; ItemPatches.StartWithdrawing(itemForCurrency); try { RemoveCurrency(vaultManager, currencyId, amount); MethodInfo methodInfo = AccessTools.Method(((object)val).GetType(), "AddItem", new Type[3] { typeof(int), typeof(int), typeof(bool) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(val, new object[3] { itemForCurrency, amount, true }); ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogInfo((object)$"Withdrew {amount} of {currencyId} (itemId={itemForCurrency}) to inventory"); } setStatus?.Invoke($"Withdrew {amount} to inventory.", arg2: false); return; } MethodInfo methodInfo2 = AccessTools.Method(((object)val).GetType(), "AddItem", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo2 != null) { for (int i = 0; i < amount; i++) { methodInfo2.Invoke(val, new object[1] { itemForCurrency }); } ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"Withdrew {amount} of {currencyId} (itemId={itemForCurrency}) to inventory (simple method)"); } setStatus?.Invoke($"Withdrew {amount} to inventory.", arg2: false); } else { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogError((object)"Could not find AddItem method on inventory"); } AddCurrencyBack(vaultManager, currencyId, amount); setStatus?.Invoke("Could not add to inventory — vault balance unchanged.", arg2: true); } } finally { ItemPatches.IsWithdrawing = false; ItemPatches.StopWithdrawing(itemForCurrency); } } catch (Exception ex) { ManualLogSource log8 = Plugin.Log; if (log8 != null) { log8.LogError((object)("Error withdrawing to inventory: " + ex.Message)); } ItemPatches.StopWithdrawing(itemForCurrency); AddCurrencyBack(vaultManager, currencyId, amount); setStatus?.Invoke("Withdraw failed — see BepInEx log. Vault balance restored.", arg2: true); } } public static void DepositFromInventory(VaultManager vaultManager, string currencyId, int amount, Action setStatus) { int itemForCurrency = ItemPatches.GetItemForCurrency(currencyId); if (itemForCurrency < 0) { setStatus?.Invoke("Deposit needs an inventory item mapping for this currency.", arg2: true); return; } int rawInventoryCount = ItemPatches.GetRawInventoryCount(itemForCurrency); if (rawInventoryCount < amount) { setStatus?.Invoke($"Only {rawInventoryCount} in your inventory (bag).", arg2: true); } else if (ItemPatches.DepositItemToVault(itemForCurrency, amount)) { setStatus?.Invoke($"Deposited {amount} into the vault.", arg2: false); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"[Vault UI] Deposited {amount} of item {itemForCurrency} as {currencyId}"); } } else { setStatus?.Invoke("Deposit failed — see BepInEx log.", arg2: true); } } public static void RemoveCurrency(VaultManager vaultManager, string currencyId, int amount) { if (currencyId.StartsWith("seasonal_")) { if (Enum.TryParse(currencyId.Substring("seasonal_".Length), out var result)) { vaultManager.RemoveSeasonalTokens(result, amount); } } else if (currencyId.StartsWith("community_")) { vaultManager.RemoveCommunityTokens(currencyId.Substring("community_".Length), amount); } else if (currencyId.StartsWith("key_")) { vaultManager.RemoveKeys(currencyId.Substring("key_".Length), amount); } else if (currencyId.StartsWith("special_")) { vaultManager.RemoveSpecial(currencyId.Substring("special_".Length), amount); } else if (currencyId.StartsWith("orb_")) { vaultManager.RemoveOrbs(currencyId.Substring("orb_".Length), amount); } else if (currencyId.StartsWith("custom_")) { vaultManager.RemoveCustomCurrency(currencyId.Substring("custom_".Length), amount); } } private static void AddCurrencyBack(VaultManager vaultManager, string currencyId, int amount) { if (currencyId.StartsWith("seasonal_")) { if (Enum.TryParse(currencyId.Substring("seasonal_".Length), out var result)) { vaultManager.AddSeasonalTokens(result, amount); } } else if (currencyId.StartsWith("community_")) { vaultManager.AddCommunityTokens(currencyId.Substring("community_".Length), amount); } else if (currencyId.StartsWith("key_")) { vaultManager.AddKeys(currencyId.Substring("key_".Length), amount); } else if (currencyId.StartsWith("special_")) { vaultManager.AddSpecial(currencyId.Substring("special_".Length), amount); } else if (currencyId.StartsWith("orb_")) { vaultManager.AddOrbs(currencyId.Substring("orb_".Length), amount); } else if (currencyId.StartsWith("custom_")) { vaultManager.AddCustomCurrency(currencyId.Substring("custom_".Length), amount); } } } public class VaultUI : MonoBehaviour { private VaultManager _vaultManager; private bool _isVisible; private Rect _windowRect; private Vector2 _scrollPosition; private Vector2 _debugVaultScroll; private CurrencyCategory _selectedCategory; private string _depositAmount = "1"; private string _selectedCurrencyId = ""; private bool _showDebugVaultDump; private GUIStyle _windowStyle; private GUIStyle _headerStyle; private GUIStyle _titleStyle; private GUIStyle _buttonStyle; private GUIStyle _withdrawButtonStyle; private GUIStyle _labelStyle; private GUIStyle _valueStyle; private GUIStyle _categoryButtonStyle; private GUIStyle _selectedCategoryStyle; private GUIStyle _rowStyle; private GUIStyle _closeButtonStyle; private GUIStyle _textFieldStyle; private Texture2D _windowBackground; private Texture2D _rowBackground; private Texture2D _rowAltBackground; private Texture2D _buttonNormal; private Texture2D _buttonHover; private Texture2D _withdrawNormal; private Texture2D _withdrawHover; private Texture2D _tabNormal; private Texture2D _tabSelected; private Texture2D _headerBarBg; private GUIStyle _headerBarStyle; private readonly Color _windowBgColor = new Color(0.12f, 0.12f, 0.18f, 0.98f); private readonly Color _headerGradientStart = new Color(0.25f, 0.45f, 0.65f); private readonly Color _headerGradientEnd = new Color(0.15f, 0.3f, 0.5f); private readonly Color _accentColor = new Color(0.4f, 0.7f, 0.95f); private readonly Color _goldColor = new Color(0.95f, 0.8f, 0.3f); private readonly Color _withdrawColor = new Color(0.7f, 0.35f, 0.35f); private readonly Color _withdrawHoverColor = new Color(0.85f, 0.4f, 0.4f); private readonly Color _rowEvenColor = new Color(0.15f, 0.15f, 0.22f, 0.9f); private readonly Color _rowOddColor = new Color(0.18f, 0.18f, 0.26f, 0.9f); private readonly Color _textColor = new Color(0.9f, 0.9f, 0.95f); private readonly Color _textDimColor = new Color(0.6f, 0.6f, 0.7f); private GUIStyle _subtitleStyle; private GUIStyle _emptyStyle; private GUIStyle _toggleOnStyle; private GUIStyle _toggleOffStyle; private GUIStyle _iconFallbackStyle; private GUIStyle _hintStyle; private GUIStyle _selectedNameStyle; private GUIStyle _amountLabelStyle; private GUIStyle _statusOkStyle; private GUIStyle _statusErrorStyle; private string _vaultStatusMessage; private bool _vaultStatusIsError; private float _vaultStatusUntil; private static bool _shownCorruptVaultSessionNotice; private Texture2D _toggleOnBg; private Texture2D _toggleOnHover; private Texture2D _toggleOffBg; private Texture2D _toggleOffHover; private const float BASE_WINDOW_WIDTH = 520f; private const float BASE_ROW_HEIGHT = 40f; private const float BASE_HEADER_HEIGHT = 118f; private const float BASE_FOOTER_HEIGHT = 200f; private const float BASE_MIN_CONTENT_HEIGHT = 120f; private const float BASE_MAX_CONTENT_HEIGHT = 420f; private const float BASE_SETTINGS_CONTENT_HEIGHT = 340f; private float _scale = 1f; private bool _stylesDirty = true; private KeyCode _toggleKey = (KeyCode)118; private bool _requiresModifier = true; private KeyCode _altToggleKey = (KeyCode)289; private bool _showSettings; private Vector2 _settingsScroll; private static readonly KeyCode[] SettingsKeyOptions; private static readonly string[] SettingsKeyNames; private const string PAUSE_ID = "TheVault_UI"; private static readonly CurrencyCategory[] _enabledCategories; private int _rowIndex; private float WindowWidth => 520f * _scale; private float RowHeight => 40f * _scale; private float HeaderHeight => 118f * _scale; private float FooterHeight => 200f * _scale; private float MinContentHeight => 120f * _scale; private float MaxContentHeight => 420f * _scale; private float SettingsContentHeight => 340f * _scale; public bool IsVisible => _isVisible; private int ScaledFont(int baseSize) { return Mathf.Max(8, Mathf.RoundToInt((float)baseSize * _scale)); } private float Scaled(float value) { return value * _scale; } private int ScaledInt(float value) { return Mathf.RoundToInt(value * _scale); } public void Initialize(VaultManager vaultManager) { //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) _vaultManager = vaultManager; _isVisible = false; float windowWidth = WindowWidth; float num = HeaderHeight + Scaled(260f) + FooterHeight; float num2 = ((float)Screen.width - windowWidth) / 2f; float num3 = ((float)Screen.height - num) / 2f; _windowRect = new Rect(num2, num3, windowWidth, num); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"VaultUI initialized"); } } public void SetToggleKey(KeyCode key, bool requireModifier = true) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _toggleKey = key; _requiresModifier = requireModifier; } public void SetAltToggleKey(KeyCode key) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _altToggleKey = key; } public void SetScale(float scale) { //IL_0065: 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) _scale = Mathf.Clamp(scale, 0.5f, 3f); _stylesDirty = true; _windowStyle = null; float windowWidth = WindowWidth; float num = HeaderHeight + 260f * _scale + FooterHeight; _windowRect = new Rect(((float)Screen.width - windowWidth) / 2f, ((float)Screen.height - num) / 2f, windowWidth, num); } public void Show() { _isVisible = true; try { if ((Object)(object)Player.Instance != (Object)null) { Player.Instance.AddPauseObject("TheVault_UI"); } PlayerInput.DisableInput("TheVault_UI"); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not block game input: " + ex.Message)); } } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Vault UI opened"); } IconCache.LogStatus(); SecretGifts.CheckAndGiveFirstOpenGift(PlayerPatches.LoadedCharacterName); if (!_shownCorruptVaultSessionNotice && Plugin.LastVaultLoadQuarantinedCorruptFile) { _shownCorruptVaultSessionNotice = true; SetVaultStatus("Your vault file could not be read and was moved to a .corrupt-*.bak file in Saves. An empty vault is in memory — restore the backup from your config folder if needed.", isError: true); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[VaultUI] Your vault file could not be read and was moved to a .corrupt-*.bak file in Saves. An empty vault is in memory — restore the backup from your config folder if needed."); } } } public void Hide() { if (!_isVisible) { return; } _isVisible = false; try { if ((Object)(object)Player.Instance != (Object)null) { Player.Instance.RemovePauseObject("TheVault_UI"); } PlayerInput.EnableInput("TheVault_UI"); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Could not re-enable game input: " + ex.Message)); } } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Vault UI closed"); } } public void Toggle() { if (!PlayerPatches.IsVaultLoaded) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Cannot toggle vault UI - save not yet loaded"); } } else if (_isVisible) { Hide(); } else { Show(); } } private void OnDestroy() { if (_isVisible) { Hide(); } DisposeGeneratedTextures(); } private void Awake() { _stylesDirty = true; _windowStyle = null; } private void OnEnable() { _stylesDirty = true; _windowStyle = null; } private void Update() { if (!string.IsNullOrEmpty(_vaultStatusMessage) && Time.unscaledTime > _vaultStatusUntil) { _vaultStatusMessage = null; } if (_isVisible && Input.GetKeyDown((KeyCode)27)) { Hide(); } } private void SetVaultStatus(string message, bool isError) { _vaultStatusMessage = message; _vaultStatusIsError = isError; _vaultStatusUntil = Time.unscaledTime + 8f; } private Texture2D MakeTex(int width, int height, Color color) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < array.Length; i++) { array[i] = color; } Texture2D val = new Texture2D(width, height); val.SetPixels(array); val.Apply(); return val; } private Texture2D MakeGradientTex(int width, int height, Color top, Color bottom) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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) Texture2D val = new Texture2D(width, height); for (int i = 0; i < height; i++) { Color val2 = Color.Lerp(bottom, top, (float)i / (float)height); for (int j = 0; j < width; j++) { val.SetPixel(j, i, val2); } } val.Apply(); return val; } private Texture2D MakeRoundedTex(int width, int height, Color color, int radius) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Expected O, but got Unknown //IL_00e0: 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) Texture2D val = new Texture2D(width, height); Color val2 = default(Color); ((Color)(ref val2))..ctor(0f, 0f, 0f, 0f); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { bool flag = false; int num = 0; int num2 = 0; if (j < radius && i < radius) { num = radius - j; num2 = radius - i; flag = true; } else if (j >= width - radius && i < radius) { num = j - (width - radius - 1); num2 = radius - i; flag = true; } else if (j < radius && i >= height - radius) { num = radius - j; num2 = i - (height - radius - 1); flag = true; } else if (j >= width - radius && i >= height - radius) { num = j - (width - radius - 1); num2 = i - (height - radius - 1); flag = true; } if (flag && num * num + num2 * num2 > radius * radius) { val.SetPixel(j, i, val2); } else { val.SetPixel(j, i, color); } } } val.Apply(); return val; } private static void DestroyTex(ref Texture2D tex) { if ((Object)(object)tex != (Object)null) { Object.Destroy((Object)(object)tex); tex = null; } } private void DisposeGeneratedTextures() { DestroyTex(ref _windowBackground); DestroyTex(ref _rowBackground); DestroyTex(ref _rowAltBackground); DestroyTex(ref _buttonNormal); DestroyTex(ref _buttonHover); DestroyTex(ref _withdrawNormal); DestroyTex(ref _withdrawHover); DestroyTex(ref _tabNormal); DestroyTex(ref _tabSelected); DestroyTex(ref _headerBarBg); DestroyTex(ref _toggleOnBg); DestroyTex(ref _toggleOnHover); DestroyTex(ref _toggleOffBg); DestroyTex(ref _toggleOffHover); _windowStyle = null; _headerBarStyle = null; _headerStyle = null; _titleStyle = null; _buttonStyle = null; _withdrawButtonStyle = null; _labelStyle = null; _valueStyle = null; _categoryButtonStyle = null; _selectedCategoryStyle = null; _rowStyle = null; _closeButtonStyle = null; _textFieldStyle = null; _subtitleStyle = null; _emptyStyle = null; _toggleOnStyle = null; _toggleOffStyle = null; _iconFallbackStyle = null; _hintStyle = null; _selectedNameStyle = null; _amountLabelStyle = null; _statusOkStyle = null; _statusErrorStyle = null; } private void InitializeStyles() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_00a2: 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) //IL_00d0: 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_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_014c: 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_0161: 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_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Expected O, but got Unknown //IL_01ae: Expected O, but got Unknown //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Expected O, but got Unknown //IL_01f5: 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_022c: Expected O, but got Unknown //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_023d: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Expected O, but got Unknown //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0271: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_0295: Expected O, but got Unknown //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Expected O, but got Unknown //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02fc: Unknown result type (might be due to invalid IL or missing references) //IL_0329: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Expected O, but got Unknown //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_034b: Unknown result type (might be due to invalid IL or missing references) //IL_0355: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Unknown result type (might be due to invalid IL or missing references) //IL_036c: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_039c: Expected O, but got Unknown //IL_03a7: Unknown result type (might be due to invalid IL or missing references) //IL_03ac: Unknown result type (might be due to invalid IL or missing references) //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Expected O, but got Unknown //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_03ff: Unknown result type (might be due to invalid IL or missing references) //IL_0410: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Unknown result type (might be due to invalid IL or missing references) //IL_0421: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Unknown result type (might be due to invalid IL or missing references) //IL_0438: Unknown result type (might be due to invalid IL or missing references) //IL_0442: Unknown result type (might be due to invalid IL or missing references) //IL_0453: Unknown result type (might be due to invalid IL or missing references) //IL_0459: Unknown result type (might be due to invalid IL or missing references) //IL_0468: Expected O, but got Unknown //IL_0473: Unknown result type (might be due to invalid IL or missing references) //IL_0478: Unknown result type (might be due to invalid IL or missing references) //IL_0486: Unknown result type (might be due to invalid IL or missing references) //IL_048d: Unknown result type (might be due to invalid IL or missing references) //IL_049c: Expected O, but got Unknown //IL_04a7: Unknown result type (might be due to invalid IL or missing references) //IL_04ac: Unknown result type (might be due to invalid IL or missing references) //IL_04ba: Unknown result type (might be due to invalid IL or missing references) //IL_04c1: Unknown result type (might be due to invalid IL or missing references) //IL_04c8: Unknown result type (might be due to invalid IL or missing references) //IL_04cf: Unknown result type (might be due to invalid IL or missing references) //IL_04de: Expected O, but got Unknown //IL_04e9: Unknown result type (might be due to invalid IL or missing references) //IL_04ee: Unknown result type (might be due to invalid IL or missing references) //IL_04fc: Unknown result type (might be due to invalid IL or missing references) //IL_0503: Unknown result type (might be due to invalid IL or missing references) //IL_0530: Unknown result type (might be due to invalid IL or missing references) //IL_053a: Expected O, but got Unknown //IL_053a: Unknown result type (might be due to invalid IL or missing references) //IL_0553: Unknown result type (might be due to invalid IL or missing references) //IL_055d: Expected O, but got Unknown //IL_055d: Unknown result type (might be due to invalid IL or missing references) //IL_056e: Unknown result type (might be due to invalid IL or missing references) //IL_0575: Unknown result type (might be due to invalid IL or missing references) //IL_057f: Unknown result type (might be due to invalid IL or missing references) //IL_0590: Unknown result type (might be due to invalid IL or missing references) //IL_0597: Unknown result type (might be due to invalid IL or missing references) //IL_05a6: Expected O, but got Unknown //IL_05ad: Unknown result type (might be due to invalid IL or missing references) //IL_05b2: Unknown result type (might be due to invalid IL or missing references) //IL_05b9: Unknown result type (might be due to invalid IL or missing references) //IL_05ca: Unknown result type (might be due to invalid IL or missing references) //IL_05d0: Unknown result type (might be due to invalid IL or missing references) //IL_05da: Unknown result type (might be due to invalid IL or missing references) //IL_05eb: Unknown result type (might be due to invalid IL or missing references) //IL_05f1: Unknown result type (might be due to invalid IL or missing references) //IL_0600: Expected O, but got Unknown //IL_0601: Unknown result type (might be due to invalid IL or missing references) //IL_0606: Unknown result type (might be due to invalid IL or missing references) //IL_0633: Unknown result type (might be due to invalid IL or missing references) //IL_063d: Expected O, but got Unknown //IL_063d: Unknown result type (might be due to invalid IL or missing references) //IL_0656: Unknown result type (might be due to invalid IL or missing references) //IL_0660: Expected O, but got Unknown //IL_0665: Expected O, but got Unknown //IL_0670: Unknown result type (might be due to invalid IL or missing references) //IL_0675: Unknown result type (might be due to invalid IL or missing references) //IL_0683: Unknown result type (might be due to invalid IL or missing references) //IL_068a: Unknown result type (might be due to invalid IL or missing references) //IL_06b7: Unknown result type (might be due to invalid IL or missing references) //IL_06c1: Expected O, but got Unknown //IL_06c1: Unknown result type (might be due to invalid IL or missing references) //IL_06d2: Unknown result type (might be due to invalid IL or missing references) //IL_06d9: Unknown result type (might be due to invalid IL or missing references) //IL_06e3: Unknown result type (might be due to invalid IL or missing references) //IL_06f4: Unknown result type (might be due to invalid IL or missing references) //IL_06fa: Unknown result type (might be due to invalid IL or missing references) //IL_0709: Expected O, but got Unknown //IL_0714: Unknown result type (might be due to invalid IL or missing references) //IL_0719: Unknown result type (might be due to invalid IL or missing references) //IL_0727: Unknown result type (might be due to invalid IL or missing references) //IL_072e: Unknown result type (might be due to invalid IL or missing references) //IL_075b: Unknown result type (might be due to invalid IL or missing references) //IL_0765: Expected O, but got Unknown //IL_0765: Unknown result type (might be due to invalid IL or missing references) //IL_076c: Unknown result type (might be due to invalid IL or missing references) //IL_077b: Expected O, but got Unknown //IL_0782: Unknown result type (might be due to invalid IL or missing references) //IL_0787: Unknown result type (might be due to invalid IL or missing references) //IL_078e: Unknown result type (might be due to invalid IL or missing references) //IL_07a1: Expected O, but got Unknown //IL_07ad: Unknown result type (might be due to invalid IL or missing references) //IL_07be: Unknown result type (might be due to invalid IL or missing references) //IL_07c3: Unknown result type (might be due to invalid IL or missing references) //IL_07cf: Expected O, but got Unknown //IL_07db: Unknown result type (might be due to invalid IL or missing references) //IL_07fd: Unknown result type (might be due to invalid IL or missing references) //IL_0824: Unknown result type (might be due to invalid IL or missing references) //IL_084b: Unknown result type (might be due to invalid IL or missing references) //IL_0872: Unknown result type (might be due to invalid IL or missing references) //IL_088c: Unknown result type (might be due to invalid IL or missing references) //IL_0891: Unknown result type (might be due to invalid IL or missing references) //IL_089f: Unknown result type (might be due to invalid IL or missing references) //IL_08a6: Unknown result type (might be due to invalid IL or missing references) //IL_08ab: Unknown result type (might be due to invalid IL or missing references) //IL_08b5: Expected O, but got Unknown //IL_08b5: Unknown result type (might be due to invalid IL or missing references) //IL_08c6: Unknown result type (might be due to invalid IL or missing references) //IL_08db: Unknown result type (might be due to invalid IL or missing references) //IL_08e5: Unknown result type (might be due to invalid IL or missing references) //IL_08f6: Unknown result type (might be due to invalid IL or missing references) //IL_08fc: Unknown result type (might be due to invalid IL or missing references) //IL_0906: Unknown result type (might be due to invalid IL or missing references) //IL_0917: Unknown result type (might be due to invalid IL or missing references) //IL_091d: Unknown result type (might be due to invalid IL or missing references) //IL_092c: Expected O, but got Unknown //IL_0937: Unknown result type (might be due to invalid IL or missing references) //IL_093c: Unknown result type (might be due to invalid IL or missing references) //IL_094a: Unknown result type (might be due to invalid IL or missing references) //IL_0951: Unknown result type (might be due to invalid IL or missing references) //IL_0956: Unknown result type (might be due to invalid IL or missing references) //IL_0960: Expected O, but got Unknown //IL_0960: Unknown result type (might be due to invalid IL or missing references) //IL_0971: Unknown result type (might be due to invalid IL or missing references) //IL_0986: Unknown result type (might be due to invalid IL or missing references) //IL_0990: Unknown result type (might be due to invalid IL or missing references) //IL_09a1: Unknown result type (might be due to invalid IL or missing references) //IL_09a7: Unknown result type (might be due to invalid IL or missing references) //IL_09b1: Unknown result type (might be due to invalid IL or missing references) //IL_09c2: Unknown result type (might be due to invalid IL or missing references) //IL_09c8: Unknown result type (might be due to invalid IL or missing references) //IL_09d7: Expected O, but got Unknown //IL_09de: Unknown result type (might be due to invalid IL or missing references) //IL_09e3: Unknown result type (might be due to invalid IL or missing references) //IL_09f1: Unknown result type (might be due to invalid IL or missing references) //IL_09fd: Expected O, but got Unknown //IL_0a09: Unknown result type (might be due to invalid IL or missing references) //IL_0a1a: Unknown result type (might be due to invalid IL or missing references) //IL_0a1f: Unknown result type (might be due to invalid IL or missing references) //IL_0a26: Unknown result type (might be due to invalid IL or missing references) //IL_0a32: Expected O, but got Unknown //IL_0a3e: Unknown result type (might be due to invalid IL or missing references) //IL_0a4f: Unknown result type (might be due to invalid IL or missing references) //IL_0a54: Unknown result type (might be due to invalid IL or missing references) //IL_0a60: Expected O, but got Unknown //IL_0a6c: Unknown result type (might be due to invalid IL or missing references) //IL_0a7d: Unknown result type (might be due to invalid IL or missing references) //IL_0a87: Expected O, but got Unknown //IL_0a93: Unknown result type (might be due to invalid IL or missing references) //IL_0aa4: Unknown result type (might be due to invalid IL or missing references) //IL_0aa9: Unknown result type (might be due to invalid IL or missing references) //IL_0ab0: Unknown result type (might be due to invalid IL or missing references) //IL_0abc: Expected O, but got Unknown //IL_0ad6: Unknown result type (might be due to invalid IL or missing references) //IL_0ae7: Unknown result type (might be due to invalid IL or missing references) //IL_0aec: Unknown result type (might be due to invalid IL or missing references) //IL_0af3: Unknown result type (might be due to invalid IL or missing references) //IL_0aff: Expected O, but got Unknown //IL_0b19: Unknown result type (might be due to invalid IL or missing references) if (_stylesDirty || _windowStyle == null || _headerBarStyle == null) { DisposeGeneratedTextures(); _windowBackground = MakeRoundedTex(64, 64, _windowBgColor, 8); _rowBackground = MakeTex(2, 2, _rowEvenColor); _rowAltBackground = MakeTex(2, 2, _rowOddColor); _buttonNormal = MakeRoundedTex(32, 32, new Color(0.25f, 0.45f, 0.65f, 0.9f), 4); _buttonHover = MakeRoundedTex(32, 32, new Color(0.35f, 0.55f, 0.75f, 0.95f), 4); _withdrawNormal = MakeRoundedTex(32, 32, _withdrawColor, 4); _withdrawHover = MakeRoundedTex(32, 32, _withdrawHoverColor, 4); _tabNormal = MakeRoundedTex(32, 32, new Color(0.2f, 0.2f, 0.28f, 0.9f), 6); _tabSelected = MakeGradientTex(32, 32, _accentColor, new Color(0.3f, 0.5f, 0.7f)); _headerBarBg = MakeTex(2, 2, new Color(0.14f, 0.18f, 0.26f, 0.98f)); GUIStyle val = new GUIStyle(); val.normal.background = _headerBarBg; val.padding = new RectOffset(ScaledInt(12f), ScaledInt(12f), ScaledInt(10f), ScaledInt(10f)); _headerBarStyle = val; GUIStyle val2 = new GUIStyle(GUI.skin.window) { padding = new RectOffset(ScaledInt(15f), ScaledInt(15f), ScaledInt(10f), ScaledInt(15f)), border = new RectOffset(ScaledInt(12f), ScaledInt(12f), ScaledInt(12f), ScaledInt(12f)) }; val2.normal.background = _windowBackground; val2.normal.textColor = _textColor; _windowStyle = val2; GUIStyle val3 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(24), fontStyle = (FontStyle)1, alignment = (TextAnchor)4 }; val3.normal.textColor = _goldColor; _titleStyle = val3; GUIStyle val4 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(14), fontStyle = (FontStyle)1, alignment = (TextAnchor)3 }; val4.normal.textColor = _accentColor; _headerStyle = val4; GUIStyle val5 = new GUIStyle(GUI.skin.button) { fontSize = ScaledFont(12), fontStyle = (FontStyle)1, padding = new RectOffset(ScaledInt(12f), ScaledInt(12f), ScaledInt(6f), ScaledInt(6f)) }; val5.normal.background = _buttonNormal; val5.normal.textColor = _textColor; val5.hover.background = _buttonHover; val5.hover.textColor = Color.white; val5.active.background = _buttonHover; val5.active.textColor = Color.white; _buttonStyle = val5; GUIStyle val6 = new GUIStyle(GUI.skin.button) { fontSize = ScaledFont(11), fontStyle = (FontStyle)1, padding = new RectOffset(ScaledInt(4f), ScaledInt(4f), ScaledInt(4f), ScaledInt(4f)), alignment = (TextAnchor)4 }; val6.normal.background = _withdrawNormal; val6.normal.textColor = _textColor; val6.hover.background = _withdrawHover; val6.hover.textColor = Color.white; val6.active.background = _withdrawHover; val6.active.textColor = Color.white; _withdrawButtonStyle = val6; GUIStyle val7 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(13) }; val7.normal.textColor = _textColor; _labelStyle = val7; GUIStyle val8 = new GUIStyle(GUI.skin.label) { fontSize = ScaledFont(16), fontStyle = (FontStyle)1, alignment = (TextAnchor)3 }; val8.normal.textColor = _goldColor; _valueStyle = val8; GUIStyle val9 = new GUIStyle(GUI.skin.button) { fontSize = ScaledFont(12), fontStyle = (FontStyle)0, padding = new RectOffset(ScaledInt(14f), ScaledInt(14f), ScaledInt(8f), ScaledInt(8f)), margin = new RectOffset(ScaledInt(3f), ScaledInt(3f), 0, 0) }; val9.normal.background = _tabNormal; val9.normal.textColor = _textDimColor; val9.hover.background = _buttonHover; val9.hover.textColor = _textColor; _categoryButtonStyle = val9; GUIStyle val10 = new GUIStyle(_categoryButtonStyle) { fontStyle = (FontStyle)1 }; val10.normal.background = _tabSelected; val10.normal.textColor = Color.white; val10.hover.background = _tabSelected; val10.hover.textColor = Color.white; _selectedCategoryStyle = val10; _rowStyle = new GUIStyle { padding = new RectOffset(ScaledInt(8f), ScaledInt(8f), ScaledInt(6f), ScaledInt(6f)), margin = new RectOffset(0, 0, ScaledInt(2f), ScaledInt(2f)) }; GUIStyle val11 = new GUIStyle(GUI.skin.button) { fontSize = ScaledFont(13), fontStyle = (FontStyle)1, padding = new RectOffset(ScaledInt(20f), ScaledInt(20f), ScaledInt(10f), ScaledInt(10f)) }; val11.normal.background = _buttonNormal; val11.normal.textColor = _textColor; val11.hover.background = _buttonHover; val11.hover.textColor = Color.white; _closeButtonStyle = val11; GUIStyle val12 = new GUIStyle(GUI.skin.textField) { fontSize = ScaledFont(13), alignment = (TextAnchor)4, padding = new RectOffset(ScaledInt(6f), ScaledInt(6f), ScaledInt(4f), ScaledInt(4f)) }; val12.normal.textColor = _textColor; _textFieldStyle = val12; _subtitleStyle = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4, fontSize = ScaledFont(11) }; _subtitleStyle.normal.textColor = _textDimColor; _emptyStyle = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4 }; _emptyStyle.normal.textColor = _textDimColor; _toggleOnBg = MakeTex(1, 1, new Color(0.2f, 0.5f, 0.2f, 0.9f)); _toggleOnHover = MakeTex(1, 1, new Color(0.25f, 0.6f, 0.25f, 1f)); _toggleOffBg = MakeTex(1, 1, new Color(0.3f, 0.3f, 0.35f, 0.9f)); _toggleOffHover = MakeTex(1, 1, new Color(0.4f, 0.4f, 0.45f, 1f)); GUIStyle val13 = new GUIStyle(GUI.skin.button) { fontSize = ScaledFont(9), fontStyle = (FontStyle)1, padding = new RectOffset(1, 1, 1, 1) }; val13.normal.background = _toggleOnBg; val13.normal.textColor = new Color(0.5f, 1f, 0.5f); val13.hover.background = _toggleOnHover; val13.hover.textColor = Color.white; val13.active.background = _toggleOnHover; val13.active.textColor = Color.white; _toggleOnStyle = val13; GUIStyle val14 = new GUIStyle(GUI.skin.button) { fontSize = ScaledFont(9), fontStyle = (FontStyle)1, padding = new RectOffset(1, 1, 1, 1) }; val14.normal.background = _toggleOffBg; val14.normal.textColor = new Color(0.6f, 0.6f, 0.6f); val14.hover.background = _toggleOffHover; val14.hover.textColor = Color.white; val14.active.background = _toggleOffHover; val14.active.textColor = Color.white; _toggleOffStyle = val14; _iconFallbackStyle = new GUIStyle(_labelStyle) { fontSize = ScaledFont(10), alignment = (TextAnchor)4 }; _iconFallbackStyle.normal.textColor = _accentColor; _hintStyle = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4, fontStyle = (FontStyle)2 }; _hintStyle.normal.textColor = _textDimColor; _selectedNameStyle = new GUIStyle(_labelStyle) { fontStyle = (FontStyle)1 }; _selectedNameStyle.normal.textColor = _accentColor; _amountLabelStyle = new GUIStyle(_labelStyle); _amountLabelStyle.normal.textColor = _textDimColor; _statusErrorStyle = new GUIStyle(_hintStyle) { fontStyle = (FontStyle)0, alignment = (TextAnchor)4 }; _statusErrorStyle.normal.textColor = new Color(1f, 0.42f, 0.42f); _statusOkStyle = new GUIStyle(_hintStyle) { fontStyle = (FontStyle)0, alignment = (TextAnchor)4 }; _statusOkStyle.normal.textColor = new Color(0.5f, 0.95f, 0.65f); _stylesDirty = false; } } private void OnGUI() { //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Expected O, but got Unknown //IL_021b: Unknown result type (might be due to invalid IL or missing references) //IL_0220: Unknown result type (might be due to invalid IL or missing references) if (!_isVisible || _vaultManager == null || !PlayerPatches.IsVaultLoaded) { return; } if (_stylesDirty || _windowStyle == null || _headerBarStyle == null) { InitializeStyles(); if (_windowStyle == null || _headerBarStyle == null) { return; } } float contentHeight = GetContentHeight(); float num = Scaled(320f); float num2 = Mathf.Clamp(HeaderHeight + contentHeight + FooterHeight, num, (float)Screen.height - Scaled(60f)); ((Rect)(ref _windowRect)).width = WindowWidth; ((Rect)(ref _windowRect)).height = num2; if (((Rect)(ref _windowRect)).yMax > (float)Screen.height - Scaled(20f)) { ((Rect)(ref _windowRect)).y = (float)Screen.height - num2 - Scaled(20f); } if (((Rect)(ref _windowRect)).y < Scaled(10f)) { ((Rect)(ref _windowRect)).y = Scaled(10f); } if (((Rect)(ref _windowRect)).xMax > (float)Screen.width - Scaled(10f)) { ((Rect)(ref _windowRect)).x = (float)Screen.width - WindowWidth - Scaled(10f); } if (((Rect)(ref _windowRect)).x < Scaled(10f)) { ((Rect)(ref _windowRect)).x = Scaled(10f); } GUI.color = new Color(0f, 0f, 0f, 0.3f); GUI.DrawTexture(new Rect(((Rect)(ref _windowRect)).x + Scaled(4f), ((Rect)(ref _windowRect)).y + Scaled(4f), ((Rect)(ref _windowRect)).width, ((Rect)(ref _windowRect)).height), (Texture)(object)Texture2D.whiteTexture); GUI.color = Color.white; _windowRect = GUI.Window(((object)this).GetHashCode(), _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); } private void DrawWindow(int windowId) { //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Unknown result type (might be due to invalid IL or missing references) //IL_01df: 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_012d: 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_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) if (!Plugin.GetConfigDebugFullVaultInspector()) { _showDebugVaultDump = false; } GUILayout.BeginVertical(_headerBarStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(Scaled(100f)) }); GUILayout.Space(Scaled(4f)); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("✧ The Vault ✧", _titleStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(Scaled(2f)); GUILayout.Label("Currency Storage System", _subtitleStyle, Array.Empty()); GUILayout.Space(Scaled(10f)); DrawCategoryTabs(); GUILayout.Space(Scaled(4f)); GUILayout.EndVertical(); DrawHorizontalLine(_accentColor, 1f); GUILayout.Space(Scaled(6f)); if (_showSettings) { DrawSettingsPanel(); } else if (_showDebugVaultDump) { float contentHeight = GetContentHeight(); _debugVaultScroll = GUILayout.BeginScrollView(_debugVaultScroll, false, false, GUIStyle.none, GUI.skin.verticalScrollbar, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(contentHeight) }); DrawDebugVaultDumpContent(); GUILayout.EndScrollView(); GUILayout.Space(Scaled(8f)); DrawHorizontalLine(_accentColor, 1f); GUILayout.Space(Scaled(8f)); GUILayout.Label("Withdraw / sweep are disabled in Debug view. Turn off Debug in Settings to use them.", _hintStyle, Array.Empty()); } else { float contentHeight2 = GetContentHeight(); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, false, false, GUIStyle.none, GUI.skin.verticalScrollbar, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(contentHeight2) }); DrawCurrencyList(); GUILayout.EndScrollView(); GUILayout.Space(Scaled(8f)); DrawHorizontalLine(_accentColor, 1f); GUILayout.Space(Scaled(8f)); DrawControls(); } GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Close", _closeButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(120f)), GUILayout.Height(Scaled(35f)) })) { Hide(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUI.DragWindow(new Rect(0f, 0f, WindowWidth, Scaled(108f))); } private float GetContentHeight() { if (_showSettings) { return SettingsContentHeight; } if (_showDebugVaultDump) { return MaxContentHeight; } int count = GetCurrenciesForCategory(_selectedCategory).Count; if (count == 0) { return MinContentHeight; } return Mathf.Clamp((float)count * RowHeight + Scaled(8f), MinContentHeight, MaxContentHeight); } private void DrawHorizontalLine(Color color, float height) { //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_0020: 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) Rect rect = GUILayoutUtility.GetRect(1f, height, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.color = color * 0.6f; GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture); GUI.color = Color.white; } private void DrawCategoryTabs() { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); CurrencyCategory[] enabledCategories = _enabledCategories; foreach (CurrencyCategory currencyCategory in enabledCategories) { GUIStyle val = ((!_showSettings && !_showDebugVaultDump && _selectedCategory == currencyCategory) ? _selectedCategoryStyle : _categoryButtonStyle); if (GUILayout.Button(GetCategoryIcon(currencyCategory) + " " + GetCategoryDisplayName(currencyCategory), val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(88f) })) { _showSettings = false; _showDebugVaultDump = false; _selectedCategory = currencyCategory; _selectedCurrencyId = ""; } } if (Plugin.GetConfigDebugFullVaultInspector()) { GUIStyle val2 = (_showDebugVaultDump ? _selectedCategoryStyle : _categoryButtonStyle); if (GUILayout.Button("⁜ Debug", val2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(72f) })) { _showSettings = false; _showDebugVaultDump = true; _selectedCurrencyId = ""; } } GUIStyle val3 = ((_showSettings && !_showDebugVaultDump) ? _selectedCategoryStyle : _categoryButtonStyle); if (GUILayout.Button("⚙ Settings", val3, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinWidth(88f) })) { _showSettings = true; _showDebugVaultDump = false; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private string GetCategoryIcon(CurrencyCategory category) { return category switch { CurrencyCategory.SeasonalToken => "☀", CurrencyCategory.CommunityToken => "♥", CurrencyCategory.Key => "⚿", CurrencyCategory.Special => "★", CurrencyCategory.Custom => "☆", _ => "•", }; } private string GetCategoryDisplayName(CurrencyCategory category) { return category switch { CurrencyCategory.SeasonalToken => "Seasonal", CurrencyCategory.CommunityToken => "Community", CurrencyCategory.Key => "Keys", CurrencyCategory.Special => "Special", CurrencyCategory.Custom => "Custom", _ => category.ToString(), }; } private void DrawDebugVaultDumpContent() { if (_vaultManager == null) { GUILayout.Label("(Vault not initialized)", _emptyStyle, Array.Empty()); return; } GUILayout.Label("Raw snapshot of all vault dictionaries (Seasonal, Community, Keys, Tickets, Orbs, Custom).", _hintStyle, Array.Empty()); GUILayout.Space(Scaled(6f)); foreach (string item in _vaultManager.BuildDebugVaultReport()) { GUILayout.Label(item, _labelStyle, Array.Empty()); } } private void DrawCurrencyList() { Dictionary currenciesForCategory = GetCurrenciesForCategory(_selectedCategory); if (currenciesForCategory.Count == 0) { GUILayout.Space(20f); if (_selectedCategory == CurrencyCategory.Custom) { GUILayout.Label("No custom currencies yet.", _emptyStyle, Array.Empty()); GUILayout.Space(6f); GUILayout.Label("Other mods register entries with TheVault.Api.VaultIntegration.RegisterCustomCurrency (after The Vault loads).", _hintStyle, Array.Empty()); GUILayout.Space(4f); GUILayout.Label("Use a short id (a-z, 0-9, underscore) and optional Sun Haven item id for withdraw / auto-deposit.", _hintStyle, Array.Empty()); } else if (_selectedCategory == CurrencyCategory.SeasonalToken) { GUILayout.Label("No seasonal tokens in the vault.", _emptyStyle, Array.Empty()); GUILayout.Space(6f); GUILayout.Label("Deposit festival tokens from your inventory, or enable auto-deposit for those items.", _hintStyle, Array.Empty()); } else if (_selectedCategory == CurrencyCategory.Key) { GUILayout.Label("No keys stored in the vault.", _emptyStyle, Array.Empty()); GUILayout.Space(6f); GUILayout.Label("Keys you move into the vault appear here. Use Sweep or per-item auto-deposit to add them.", _hintStyle, Array.Empty()); } else if (_selectedCategory == CurrencyCategory.Special) { GUILayout.Label("No special currencies in the vault.", _emptyStyle, Array.Empty()); GUILayout.Space(6f); GUILayout.Label("Doubloons, tickets, shards, and similar currencies show here once deposited.", _hintStyle, Array.Empty()); } else { GUILayout.Label("No items in this category", _emptyStyle, Array.Empty()); } return; } _rowIndex = 0; foreach (KeyValuePair item in currenciesForCategory) { DrawCurrencyRow(item.Key, item.Value, _rowIndex % 2 == 0); _rowIndex++; } } private Dictionary GetCurrenciesForCategory(CurrencyCategory category) { return VaultUiShared.GetCurrenciesForCategory(_vaultManager, category); } private void DrawCurrencyRow(string currencyId, int amount, bool isEvenRow) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: 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_0068: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Expected O, but got Unknown //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0305: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_0395: 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_03b8: Unknown result type (might be due to invalid IL or missing references) //IL_03c1: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Unknown result type (might be due to invalid IL or missing references) //IL_03e6: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_0414: Unknown result type (might be due to invalid IL or missing references) //IL_041d: Unknown result type (might be due to invalid IL or missing references) bool flag = _selectedCurrencyId == currencyId; bool flag2 = ItemPatches.IsAutoDepositEnabled(currencyId); Rect rect = GUILayoutUtility.GetRect(0f, RowHeight, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUI.color = (Color)(flag ? new Color(0.3f, 0.5f, 0.7f, 0.5f) : (isEvenRow ? _rowEvenColor : _rowOddColor)); GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture); GUI.color = Color.white; float num = ((Rect)(ref rect)).y + (((Rect)(ref rect)).height - Scaled(26f)) / 2f; float num2 = ((Rect)(ref rect)).x + Scaled(8f); float num3 = ((Rect)(ref rect)).y + (((Rect)(ref rect)).height - Scaled(20f)) / 2f; GUIStyle val = (flag2 ? _toggleOnStyle : _toggleOffStyle); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(num2, num3, 20f, 20f); string text = (flag2 ? "ON" : "--"); if (GUI.Button(val2, text, val)) { ItemPatches.ToggleAutoDeposit(currencyId); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[UI] Toggle clicked for " + currencyId)); } } num2 += 24f; float num4 = Scaled(28f); float num5 = ((Rect)(ref rect)).y + (((Rect)(ref rect)).height - num4) / 2f; Texture2D iconForCurrency = IconCache.GetIconForCurrency(currencyId); if ((Object)(object)iconForCurrency != (Object)null && IconCache.IsIconLoaded(currencyId)) { GUI.DrawTexture(new Rect(num2, num5, num4, num4), (Texture)(object)iconForCurrency, (ScaleMode)2); } else { string currencyIcon = VaultUiShared.GetCurrencyIcon(currencyId); GUI.Label(new Rect(num2, num, num4, Scaled(26f)), currencyIcon, _iconFallbackStyle); } num2 += 32f; string displayName = VaultUiShared.GetDisplayName(_vaultManager, currencyId); GUI.Label(new Rect(num2, num, 140f, 26f), displayName, _labelStyle); num2 += 144f; string text2 = "x" + VaultUiShared.FormatNumber(amount); GUIStyle val3 = new GUIStyle(_valueStyle) { fontSize = ScaledFont(16), alignment = (TextAnchor)3 }; GUI.Label(new Rect(num2, num, 70f, 26f), text2, val3); num2 += 74f; float num6 = ((Rect)(ref rect)).y + (((Rect)(ref rect)).height - Scaled(28f)) / 2f; float num7 = ((Rect)(ref rect)).x + ((Rect)(ref rect)).width - Scaled(8f); GUI.enabled = amount >= 10; if (GUI.Button(new Rect(num7 - 40f, num6, 40f, 28f), "-10", _withdrawButtonStyle)) { WithdrawToInventory(currencyId, 10); } GUI.enabled = amount >= 5; if (GUI.Button(new Rect(num7 - 78f, num6, 34f, 28f), "-5", _withdrawButtonStyle)) { WithdrawToInventory(currencyId, 5); } GUI.enabled = amount >= 1; if (GUI.Button(new Rect(num7 - 116f, num6, 34f, 28f), "-1", _withdrawButtonStyle)) { WithdrawToInventory(currencyId, 1); } GUI.enabled = true; if ((int)Event.current.type == 0 && ((Rect)(ref rect)).Contains(Event.current.mousePosition)) { bool num8 = ((Rect)(ref val2)).Contains(Event.current.mousePosition); Rect val4 = new Rect(num7 - 116f, num6, 34f, 28f); bool flag3 = ((Rect)(ref val4)).Contains(Event.current.mousePosition); val4 = new Rect(num7 - 78f, num6, 34f, 28f); bool flag4 = ((Rect)(ref val4)).Contains(Event.current.mousePosition); val4 = new Rect(num7 - 40f, num6, 40f, 28f); bool flag5 = ((Rect)(ref val4)).Contains(Event.current.mousePosition); if (!num8 && !flag3 && !flag4 && !flag5) { _selectedCurrencyId = currencyId; } } } private void DrawSettingsPanel() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_0440: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(Scaled(4f)); GUILayout.Label("⚙ Settings (saved to config file)", _subtitleStyle, Array.Empty()); GUILayout.Space(Scaled(6f)); float num = Mathf.Min(SettingsContentHeight, MaxContentHeight); _settingsScroll = GUILayout.BeginScrollView(_settingsScroll, false, false, GUIStyle.none, GUI.skin.verticalScrollbar, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num) }); GUILayout.Label("Display", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Window scale:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(90f)) }); float configWindowScale = Plugin.GetConfigWindowScale(); float num2 = GUILayout.HorizontalSlider(configWindowScale, 0.5f, 3f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(120f)) }); if (Math.Abs(num2 - configWindowScale) > 0.01f) { Plugin.SetConfigWindowScale(num2); } GUILayout.Label($"{num2:F1}", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(28f)) }); GUILayout.EndHorizontal(); GUILayout.Space(Scaled(6f)); GUILayout.Label("HUD", _labelStyle, Array.Empty()); GUILayout.Label("Drag the currency bar by its top strip to move it; position saves to config (like Sun Haven Todo). Changing [HUD] Position resets a custom placement.", _hintStyle, Array.Empty()); bool configHUDEnabled = Plugin.GetConfigHUDEnabled(); bool flag = GUILayout.Toggle(configHUDEnabled, " Show vault currency bar", _labelStyle, Array.Empty()); if (flag != configHUDEnabled) { Plugin.SetConfigHUDEnabled(flag); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("HUD scale:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(80f)) }); float configHUDScale = Plugin.GetConfigHUDScale(); float num3 = GUILayout.HorizontalSlider(configHUDScale, 0.5f, 3f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(120f)) }); if (Math.Abs(num3 - configHUDScale) > 0.01f) { Plugin.SetConfigHUDScale(num3); } GUILayout.Label($"{num3:F1}", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(28f)) }); GUILayout.EndHorizontal(); GUILayout.Label("HUD density:", _labelStyle, Array.Empty()); string configHudDensity = Plugin.GetConfigHudDensity(); string[] array = new string[3] { "Normal", "Compact", "Minimal" }; int num4 = Array.IndexOf(array, configHudDensity); if (num4 < 0) { num4 = 0; } int num5 = GUILayout.SelectionGrid(num4, array, 3, _buttonStyle, Array.Empty()); if (num5 != num4) { Plugin.SetConfigHudDensity(array[num5]); } bool configHUDCompactMode = Plugin.GetConfigHUDCompactMode(); bool flag2 = GUILayout.Toggle(configHUDCompactMode, " Legacy: Compact when density is Normal ([HUD] CompactMode)", _labelStyle, Array.Empty()); if (flag2 != configHUDCompactMode) { Plugin.SetConfigHUDCompactMode(flag2); } GUILayout.Space(Scaled(6f)); GUILayout.Label("Debug (full vault inspector)", _labelStyle, Array.Empty()); GUILayout.Label(Plugin.GetConfigDebugFullVaultInspector() ? "ON — zeros visible in tabs, Debug tab available, HUD shows all slots." : "OFF — enable in Haven Dev Tools (F11) → Azrael's Mods → The Vault, or set [The Vault] FullVaultInspector in HavenDevTools config.", _hintStyle, Array.Empty()); GUILayout.Space(Scaled(6f)); GUILayout.Label("Open Vault", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Key:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(50f)) }); int num6 = IndexOfKey(Plugin.GetConfigToggleKey()); int num7 = GUILayout.SelectionGrid(num6, SettingsKeyNames, 6, _buttonStyle, Array.Empty()); if (num7 != num6 && num7 >= 0) { Plugin.SetConfigToggleKey(SettingsKeyOptions[num7]); } GUILayout.EndHorizontal(); bool configRequireCtrl = Plugin.GetConfigRequireCtrl(); bool flag3 = GUILayout.Toggle(configRequireCtrl, " Require Ctrl (e.g. Ctrl+V)", _labelStyle, Array.Empty()); if (flag3 != configRequireCtrl) { Plugin.SetConfigRequireCtrl(flag3); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Alt key:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(50f)) }); int num8 = IndexOfKey(Plugin.GetConfigAltToggleKey()); int num9 = GUILayout.SelectionGrid(num8, SettingsKeyNames, 6, _buttonStyle, Array.Empty()); if (num9 != num8 && num9 >= 0) { Plugin.SetConfigAltToggleKey(SettingsKeyOptions[num9]); } GUILayout.EndHorizontal(); GUILayout.Space(Scaled(8f)); GUILayout.Label("Changes save automatically.", _hintStyle, Array.Empty()); GUILayout.EndScrollView(); } private static int IndexOfKey(KeyCode k) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Invalid comparison between I4 and Unknown for (int i = 0; i < SettingsKeyOptions.Length; i++) { if ((int)SettingsKeyOptions[i] == (int)k) { return i; } } return 0; } private void DrawControls() { if (!string.IsNullOrEmpty(_vaultStatusMessage)) { GUIStyle val = (_vaultStatusIsError ? _statusErrorStyle : _statusOkStyle); GUILayout.Label(_vaultStatusMessage, val, Array.Empty()); GUILayout.Space(Scaled(4f)); } if (string.IsNullOrEmpty(_selectedCurrencyId)) { GUILayout.Label("Click a row to select, or use quick withdraw / deposit buttons", _hintStyle, Array.Empty()); } else { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("▶ " + VaultUiShared.GetDisplayName(_vaultManager, _selectedCurrencyId), _selectedNameStyle, Array.Empty()); GUILayout.Space(Scaled(15f)); GUILayout.Label("Qty:", _amountLabelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(30f)) }); _depositAmount = GUILayout.TextField(_depositAmount, 6, _textFieldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(55f)) }); GUILayout.Space(Scaled(8f)); if (GUILayout.Button("Withdraw", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(86f)) })) { if (!int.TryParse(_depositAmount, out var result) || result <= 0) { SetVaultStatus("Enter a positive number for quantity.", isError: true); } else { WithdrawToInventory(_selectedCurrencyId, result); } } if (GUILayout.Button("Deposit", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Scaled(86f)) })) { if (!int.TryParse(_depositAmount, out var result2) || result2 <= 0) { SetVaultStatus("Enter a positive number for quantity.", isError: true); } else { DepositFromInventory(_selectedCurrencyId, result2); } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); int itemForCurrency = ItemPatches.GetItemForCurrency(_selectedCurrencyId); if (itemForCurrency > 0) { int rawInventoryCount = ItemPatches.GetRawInventoryCount(itemForCurrency); GUILayout.Label($"In inventory (bag): {rawInventoryCount}", _hintStyle, Array.Empty()); } else { GUILayout.Label("No matching inventory item for this row — deposit is unavailable.", _hintStyle, Array.Empty()); } } GUILayout.Space(Scaled(6f)); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Sweep Inventory (Auto-Deposit Now)", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Scaled(260f)), GUILayout.Height(Scaled(32f)) })) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[Vault UI] Sweep button clicked"); } ItemPatches.ForceAutoDepositAll(); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private void WithdrawToInventory(string currencyId, int amount) { VaultInventoryOperations.WithdrawToInventory(_vaultManager, currencyId, amount, SetVaultStatus); } private void DepositFromInventory(string currencyId, int amount) { VaultInventoryOperations.DepositFromInventory(_vaultManager, currencyId, amount, SetVaultStatus); } static VaultUI() { KeyCode[] array = new KeyCode[19]; RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/); SettingsKeyOptions = (KeyCode[])(object)array; SettingsKeyNames = new string[19] { "None", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12", "V", "B", "G", "K", "U", "H" }; _enabledCategories = new CurrencyCategory[4] { CurrencyCategory.SeasonalToken, CurrencyCategory.Key, CurrencyCategory.Special, CurrencyCategory.Custom }; } } public static class VaultUiShared { public static Dictionary GetCurrenciesForCategory(VaultManager vaultManager, CurrencyCategory category) { Dictionary dictionary = new Dictionary(); if (vaultManager == null) { return dictionary; } bool configDebugFullVaultInspector = Plugin.GetConfigDebugFullVaultInspector(); foreach (CurrencyDefinition item in vaultManager.GetCurrenciesByCategory(category)) { string id = item.Id; int num = 0; switch (category) { case CurrencyCategory.SeasonalToken: { string text5 = id.Replace("token_", ""); text5 = char.ToUpper(text5[0]) + text5.Substring(1); if (Enum.TryParse(text5, out var result)) { num = vaultManager.GetSeasonalTokens(result); } if (configDebugFullVaultInspector || num != 0) { dictionary["seasonal_" + text5] = num; } break; } case CurrencyCategory.CommunityToken: { string text = id.Replace("token_", ""); num = vaultManager.GetCommunityTokens(text); if (configDebugFullVaultInspector || num != 0) { dictionary["community_" + text] = num; } break; } case CurrencyCategory.Key: { string text4 = id.Replace("key_", ""); num = vaultManager.GetKeys(text4); if (configDebugFullVaultInspector || num != 0) { dictionary["key_" + text4] = num; } break; } case CurrencyCategory.Special: { string text2 = id.Replace("special_", ""); num = vaultManager.GetSpecial(text2); if (configDebugFullVaultInspector || num != 0) { dictionary["special_" + text2] = num; } break; } case CurrencyCategory.Orb: { string text3 = id.Replace("orb_", ""); num = vaultManager.GetOrbs(text3); if (configDebugFullVaultInspector || num != 0) { dictionary["orb_" + text3] = num; } break; } case CurrencyCategory.Custom: num = vaultManager.GetCustomCurrency(id); if (configDebugFullVaultInspector || num != 0) { dictionary["custom_" + id] = num; } break; } } if (category == CurrencyCategory.Custom && configDebugFullVaultInspector) { vaultManager.MergeOrphanCustomBalances(dictionary); } if (category == CurrencyCategory.Custom && dictionary.Count > 1) { List list = new List(dictionary.Keys); list.Sort(StringComparer.Ordinal); Dictionary dictionary2 = new Dictionary(list.Count); { foreach (string item2 in list) { dictionary2[item2] = dictionary[item2]; } return dictionary2; } } return dictionary; } public static string GetDisplayName(VaultManager vaultManager, string currencyId) { if (currencyId.StartsWith("seasonal_")) { return currencyId.Substring("seasonal_".Length) + " Token"; } if (currencyId.StartsWith("community_")) { string text = currencyId.Substring("community_".Length); if (!(text == "community")) { return "Community " + CapitalizeFirst(text); } return "Community Token"; } if (currencyId.StartsWith("key_")) { return FormatKeyName(currencyId.Substring("key_".Length)); } if (currencyId.StartsWith("special_")) { return FormatSpecialName(currencyId.Substring("special_".Length)); } if (currencyId.StartsWith("orb_")) { return CapitalizeFirst(currencyId.Substring("orb_".Length)) + " Orb"; } if (currencyId.StartsWith("custom_") && vaultManager != null) { string text2 = currencyId.Substring("custom_".Length); CurrencyDefinition currencyDefinition = vaultManager.GetCurrencyDefinition(text2); if (currencyDefinition != null && !string.IsNullOrEmpty(currencyDefinition.DisplayName)) { return currencyDefinition.DisplayName; } return CapitalizeFirst(text2.Replace("_", " ")); } return currencyId; } public static string GetCurrencyIcon(string currencyId) { if (currencyId.StartsWith("seasonal_")) { return currencyId.Substring("seasonal_".Length).ToLower() switch { "spring" => "[Sp]", "summer" => "[Su]", "fall" => "[Fa]", "winter" => "[Wi]", _ => "[T]", }; } if (currencyId.StartsWith("community_")) { return "[C]"; } if (currencyId.StartsWith("key_")) { return "[K]"; } if (currencyId.StartsWith("special_")) { return currencyId.Substring("special_".Length).ToLower() switch { "communitytoken" => "[C]", "doubloon" => "[D]", "blackbottlecap" => "[B]", "redcarnivalticket" => "[R]", "candycornpieces" => "[CC]", "manashard" => "[M]", _ => "[S]", }; } if (currencyId.StartsWith("orb_")) { return "[O]"; } if (currencyId.StartsWith("custom_")) { return "[+]"; } return "[?]"; } public static string FormatNumber(int value) { if (value >= 1000000) { float num = (float)value / 1000000f; if (num >= 10f || num == (float)(int)num) { return $"{(int)num}M"; } return $"{num:0.#}M"; } if (value >= 1000) { float num2 = (float)value / 1000f; if (num2 >= 10f || num2 == (float)(int)num2) { return $"{(int)num2}K"; } return $"{num2:0.#}K"; } return value.ToString(); } private static string CapitalizeFirst(string s) { if (string.IsNullOrEmpty(s)) { return s; } return char.ToUpper(s[0]) + s.Substring(1); } private static string FormatKeyName(string keyName) { return keyName switch { "copper" => "Copper Key", "iron" => "Iron Key", "adamant" => "Adamant Key", "mithril" => "Mithril Key", "sunite" => "Sunite Key", "glorite" => "Glorite Key", "kingslostmine" => "King's Lost Mine Key", _ => CapitalizeFirst(keyName) + " Key", }; } private static string FormatSpecialName(string specialName) { return specialName switch { "communitytoken" => "Community Token", "doubloon" => "Doubloon", "blackbottlecap" => "Black Bottle Cap", "redcarnivalticket" => "Red Carnival Ticket", "candycornpieces" => "Candy Corn Pieces", "manashard" => "Mana Shard", _ => CapitalizeFirst(specialName), }; } } } namespace TheVault.Patches { public static class GameSavePatches { public static int LastLoadedSlot { get; private set; } = -1; public static string LastLoadedCharacterName { get; private set; } = null; public static void ResetLastLoadedSlot() { LastLoadedSlot = -1; LastLoadedCharacterName = null; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"GameSavePatches: Reset LastLoadedSlot and LastLoadedCharacterName"); } } public static void OnGameSaveLoad() { try { CharacterData currentCharacter = GameSave.CurrentCharacter; if (currentCharacter != null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("GameSave.Load: " + currentCharacter.characterName)); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnGameSaveLoad: " + ex.Message)); } } } public static void OnLoadCharacterAny(object[] __args, MethodBase __originalMethod) { try { int num = -1; if (__args != null && __args.Length != 0 && __args[0] != null) { try { num = Convert.ToInt32(__args[0]); } catch { num = -1; } } string arg = ((__originalMethod != null) ? __originalMethod.Name : "LoadCharacter"); ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)$"GameSave.{arg} invoked (slot={num})"); } if (num >= 0) { LastLoadedSlot = num; } GameSave instance = SingletonBehaviour.Instance; if (((instance != null) ? instance.Saves : null) == null || num < 0 || num >= SingletonBehaviour.Instance.Saves.Count) { TrySetCharacterFromCurrentCharacter("LoadCharacter fallback"); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)$"GameSavePatches: invalid or missing slot {num}; used fallback"); } return; } string characterNameFromSaveData = GetCharacterNameFromSaveData(SingletonBehaviour.Instance.Saves[num]); if (!string.IsNullOrEmpty(characterNameFromSaveData)) { LastLoadedCharacterName = characterNameFromSaveData; PlayerPatches.SetPendingCharacterName(characterNameFromSaveData); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogDebug((object)$"GameSavePatches: character name '{characterNameFromSaveData}' from slot {num}"); } } else { TrySetCharacterFromCurrentCharacter("LoadCharacter slot resolve fallback"); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)$"GameSavePatches: could not resolve name for slot {num}"); } } } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)("Error in OnLoadCharacterAny: " + ex.Message)); } } } private static string GetCharacterNameFromSaveData(GameSaveData saveData) { if (saveData == null) { return null; } CharacterData characterData = saveData.characterData; string text = ((characterData != null) ? characterData.characterName : null); if (!string.IsNullOrEmpty(text)) { return text; } string characterNameFromUnknownSaveData = GetCharacterNameFromUnknownSaveData(saveData); if (!string.IsNullOrEmpty(characterNameFromUnknownSaveData)) { return characterNameFromUnknownSaveData; } if (!string.IsNullOrEmpty(saveData.fileName)) { string text2 = saveData.fileName; if (text2.EndsWith(".save", StringComparison.OrdinalIgnoreCase)) { text2 = text2.Substring(0, text2.Length - 5); } if (!string.IsNullOrEmpty(text2)) { return text2; } } return null; } private static string GetCharacterNameFromUnknownSaveData(object saveData) { if (saveData == null) { return null; } Type type = saveData.GetType(); string[] array = new string[6] { "characterName", "CharacterName", "name", "Name", "playerName", "PlayerName" }; string[] array2 = array; foreach (string name in array2) { PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.PropertyType == typeof(string)) { string text = property.GetValue(saveData) as string; if (!string.IsNullOrEmpty(text)) { return text; } } } PropertyInfo property2 = type.GetProperty("characterData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property2 != null) { object value = property2.GetValue(saveData); if (value != null) { Type type2 = value.GetType(); array2 = array; foreach (string name2 in array2) { PropertyInfo property3 = type2.GetProperty(name2, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property3 != null && property3.PropertyType == typeof(string)) { string text2 = property3.GetValue(value) as string; if (!string.IsNullOrEmpty(text2)) { return text2; } } } } } return null; } public static void OnSetCurrentCharacter() { try { CharacterData currentCharacter = GameSave.CurrentCharacter; if (currentCharacter != null) { string characterName = currentCharacter.characterName; if (!string.IsNullOrEmpty(characterName)) { LastLoadedCharacterName = characterName; PlayerPatches.SetPendingCharacterName(characterName); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("GameSave.SetCurrentCharacter: " + characterName)); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnSetCurrentCharacter: " + ex.Message)); } } } private static void TrySetCharacterFromCurrentCharacter(string reason) { CharacterData currentCharacter = GameSave.CurrentCharacter; string text = ((currentCharacter != null) ? currentCharacter.characterName : null); if (!string.IsNullOrEmpty(text)) { LastLoadedCharacterName = text; PlayerPatches.SetPendingCharacterName(text); ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("GameSavePatches: using CurrentCharacter '" + text + "' (" + reason + ")")); } } } } public static class ItemPatches { private enum InventoryRemoveKind { None, IntIntInt, IntInt, IntIntBool, RemoveAmount } private struct InventoryRemovePlan { public InventoryRemoveKind Kind; public MethodInfo Method; } private static Dictionary _itemToCurrency = new Dictionary(); private static Dictionary _currencyToItem = new Dictionary(); private static readonly HashSet _withdrawingItemIds = new HashSet(); private static bool _itemIdReflectionCached = false; private static FieldInfo _cachedItemIdField; private static PropertyInfo _cachedItemIdProperty; private static MethodInfo _cachedItemIdMethod; private static Func _cachedGetItemIdDelegate; private static bool _canResolveItemId; private static bool _loggedMissingItemIdResolver; private static readonly Dictionary _inventoryRemoveByType = new Dictionary(); private const BindingFlags InstanceMemberFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; private static object _itemInfoDatabaseInstance; private static FieldInfo _allItemSellInfosField; private static int _lastCurrencyLookupItemId = -1; private static string _lastCurrencyLookupCurrencyId; private static bool _lastCurrencyLookupValid; private static bool _isProcessingAutoDeposit = false; private static bool _skipObjectAddItemPostfixOnce = false; private static int _suppressVaultRemoveHookDepth = 0; private static bool _skipVaultInGetAmount = false; private static readonly List<(int itemId, int amount)> _autoDepositNotifyQueue = new List<(int, int)>(); private static readonly object _notifyQueueLock = new object(); private static readonly Dictionary _mergeBuffer = new Dictionary(); public static bool AutoDepositEnabled { get; set; } = false; public static bool IsWithdrawing { get; set; } = false; public static bool IsAutoDepositEnabled(string currencyId) { int itemForCurrency = GetItemForCurrency(currencyId); if (itemForCurrency < 0) { return false; } if (AutoDepositEnabled && _itemToCurrency.TryGetValue(itemForCurrency, out (string, bool) value)) { return value.Item2; } return false; } public static void ToggleAutoDeposit(string currencyId) { int itemForCurrency = GetItemForCurrency(currencyId); if (itemForCurrency >= 0 && _itemToCurrency.TryGetValue(itemForCurrency, out (string, bool) value)) { _itemToCurrency[itemForCurrency] = (value.Item1, !value.Item2); } } public static void StartWithdrawing(int itemId) { _withdrawingItemIds.Add(itemId); } public static void StopWithdrawing(int itemId) { _withdrawingItemIds.Remove(itemId); } public static void ResetState() { IsWithdrawing = false; _withdrawingItemIds.Clear(); } public static bool IsPlayerMainInventory(object inv) { if (inv == null || (Object)(object)Player.Instance == (Object)null) { return false; } Player instance = Player.Instance; if (inv == instance.PlayerInventory) { return true; } if ((Object)(object)instance.Inventory != (Object)null) { return inv == instance.Inventory; } return false; } private static object GetPlayerBagForMutation() { Player instance = Player.Instance; if ((Object)(object)instance == (Object)null) { return null; } return ((object)instance.PlayerInventory) ?? ((object)instance.Inventory); } public static void RegisterItemCurrencyMapping(int gameItemId, string currencyId, bool autoDeposit = false) { _itemToCurrency[gameItemId] = (currencyId, autoDeposit); _currencyToItem[currencyId] = gameItemId; if (_lastCurrencyLookupValid && _lastCurrencyLookupItemId == gameItemId) { _lastCurrencyLookupCurrencyId = currencyId; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Registered item-currency mapping: Item {gameItemId} <-> {currencyId}"); } } public static void ForceAutoDepositAll() { try { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"[Sweep] Sweep Inventory button pressed"); } if (!AutoDepositEnabled) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"[Sweep] Auto-deposit is disabled; sweep skipped"); } return; } if ((Object)(object)Player.Instance == (Object)null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"[Sweep] Player.Instance is null"); } return; } PlayerInventory playerInventory = Player.Instance.PlayerInventory; if ((Object)(object)playerInventory == (Object)null) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogWarning((object)"[Sweep] PlayerInventory is null"); } return; } int num = 0; foreach (KeyValuePair item in _itemToCurrency) { int key = item.Key; if (!item.Value.Item2) { continue; } int num2; try { _skipVaultInGetAmount = true; try { Inventory val = (Inventory)(object)playerInventory; if (val != null) { num2 = val.GetAmount(key); } else { MethodInfo methodInfo = AccessTools.Method(((object)playerInventory).GetType(), "GetAmount", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo == null) { continue; } num2 = (int)(methodInfo.Invoke(playerInventory, new object[1] { key }) ?? ((object)0)); } } finally { _skipVaultInGetAmount = false; } } catch (Exception ex) { _skipVaultInGetAmount = false; ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogDebug((object)$"[Sweep] GetAmount({key}) failed: {ex.Message}"); } continue; } if (num2 > 0 && DepositItemToVault(key, num2)) { num += num2; } } ManualLogSource log6 = Plugin.Log; if (log6 != null) { log6.LogInfo((object)$"[Sweep] Auto-deposited a total of {num} items from inventory into the vault"); } ShowSweepNotification(num); } catch (Exception ex2) { ManualLogSource log7 = Plugin.Log; if (log7 != null) { log7.LogError((object)("[Sweep] Error during ForceAutoDepositAll: " + ex2.Message)); } } } private static void ShowSweepNotification(int totalDeposited) { try { SendTextNotification((totalDeposited > 0) ? string.Format("Swept {0} item{1} to the vault", totalDeposited, (totalDeposited == 1) ? "" : "s") : "No vault currencies in inventory"); } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[Sweep] ShowSweepNotification: " + ex.Message)); } } } private static void SendTextNotification(string text) { try { Type type = ReflectionHelper.FindWishType("NotificationStack"); if (!(type == null)) { object singletonInstance = ReflectionHelper.GetSingletonInstance(type); MethodInfo methodInfo = AccessTools.Method(type, "SendNotification", new Type[5] { typeof(string), typeof(int), typeof(int), typeof(bool), typeof(bool) }, (Type[])null); if (singletonInstance != null && methodInfo != null) { methodInfo.Invoke(singletonInstance, new object[5] { text, 0, 0, false, false }); } } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[ItemPatches] SendTextNotification: " + ex.Message)); } } } public static string GetCurrencyForItem(int gameItemId) { if (_lastCurrencyLookupValid && gameItemId == _lastCurrencyLookupItemId) { return _lastCurrencyLookupCurrencyId; } object obj; if (_itemToCurrency.TryGetValue(gameItemId, out (string, bool) value)) { (obj, _) = value; } else { obj = null; } _lastCurrencyLookupItemId = gameItemId; _lastCurrencyLookupCurrencyId = (string)obj; _lastCurrencyLookupValid = true; return (string)obj; } public static int GetItemForCurrency(string currencyId) { if (!_currencyToItem.TryGetValue(currencyId, out var value)) { return -1; } return value; } public static int GetRawInventoryCount(int itemId) { Player instance = Player.Instance; if ((Object)(object)((instance != null) ? instance.PlayerInventory : null) == (Object)null) { return 0; } object playerInventory = Player.Instance.PlayerInventory; try { _skipVaultInGetAmount = true; try { Inventory val = (Inventory)((playerInventory is Inventory) ? playerInventory : null); if (val != null) { return val.GetAmount(itemId); } MethodInfo methodInfo = AccessTools.Method(playerInventory.GetType(), "GetAmount", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo == null) { return 0; } object obj = methodInfo.Invoke(playerInventory, new object[1] { itemId }); return (obj != null) ? ((int)obj) : 0; } finally { _skipVaultInGetAmount = false; } } catch (Exception ex) { _skipVaultInGetAmount = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[ItemPatches] GetRawInventoryCount: " + ex.Message)); } return 0; } } public static bool ShouldAutoDeposit(int gameItemId) { if (!AutoDepositEnabled) { return false; } if (_itemToCurrency.TryGetValue(gameItemId, out (string, bool) value)) { return value.Item2; } return false; } public static bool DepositItemToVault(int gameItemId, int amount) { if (amount <= 0) { return false; } string currencyForItem = GetCurrencyForItem(gameItemId); if (string.IsNullOrEmpty(currencyForItem)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"No currency mapping for item {gameItemId}"); } return false; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { return false; } BeginSuppressVaultRemoveHook(); try { if (!RemoveItemFromInventory(gameItemId, amount)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)$"Failed to remove {amount} of item {gameItemId} from inventory"); } return false; } } finally { EndSuppressVaultRemoveHook(); } AddCurrencyToVault(vaultManager, currencyForItem, amount); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"Deposited {amount} of item {gameItemId} as {currencyForItem}"); } return true; } public static bool WithdrawCurrencyToInventory(string currencyId, int amount) { if (amount <= 0) { return false; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { return false; } if (!vaultManager.HasCurrency(currencyId, amount)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Not enough " + currencyId + " in vault")); } return false; } int itemForCurrency = GetItemForCurrency(currencyId); if (itemForCurrency < 0) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("No item mapping for currency " + currencyId)); } return false; } if (!RemoveCurrencyFromVault(vaultManager, currencyId, amount)) { return false; } if (!AddItemToInventory(itemForCurrency, amount)) { AddCurrencyToVault(vaultManager, currencyId, amount); ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)$"Failed to add item {itemForCurrency} to inventory, rolled back vault"); } return false; } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"Withdrew {amount} {currencyId} as item {itemForCurrency}"); } return true; } public static void OnItemPickedUp(object __instance, int itemId, int amount) { try { if (!ShouldAutoDeposit(itemId)) { return; } string currencyForItem = GetCurrencyForItem(itemId); if (string.IsNullOrEmpty(currencyForItem)) { return; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager != null && RemoveItemFromInventory(itemId, amount)) { AddCurrencyToVault(vaultManager, currencyForItem, amount); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Auto-deposited {amount} of item {itemId} as {currencyForItem}"); } EnqueueAutoDepositNotification(currencyForItem, amount); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnItemPickedUp: " + ex.Message)); } } } public static void OnItemPickedUpSingle(object __instance, int itemId) { OnItemPickedUp(__instance, itemId, 1); } public static bool OnPlayerPickupPrefix(int item, int amount, bool rollForExtra) { return true; } public static void OnInventoryAddItem(object __instance, int item, int amount, bool sendNotification) { try { if (!IsPlayerMainInventory(__instance) || _isProcessingAutoDeposit || IsWithdrawing || _withdrawingItemIds.Contains(item) || !ShouldAutoDeposit(item)) { return; } string currencyForItem = GetCurrencyForItem(item); if (string.IsNullOrEmpty(currencyForItem)) { return; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { return; } _isProcessingAutoDeposit = true; try { BeginSuppressVaultRemoveHook(); bool flag; try { flag = TryRemoveFromInventory(__instance, item, amount) || RemoveItemFromInventory(item, amount); } finally { EndSuppressVaultRemoveHook(); } if (!flag) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"Failed to remove {amount} of item {item} from inventory for auto-deposit"); } } else { AddCurrencyToVault(vaultManager, currencyForItem, amount); } } finally { _isProcessingAutoDeposit = false; } } catch (Exception ex) { _isProcessingAutoDeposit = false; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnInventoryAddItem: " + ex.Message)); } } } public static void OnInventoryAddItemWithNotify(object __instance, int item, int amount, bool sendNotification) { try { if (!IsPlayerMainInventory(__instance) || _isProcessingAutoDeposit || IsWithdrawing || _withdrawingItemIds.Contains(item) || !ShouldAutoDeposit(item)) { return; } string currencyForItem = GetCurrencyForItem(item); if (string.IsNullOrEmpty(currencyForItem)) { return; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { return; } _isProcessingAutoDeposit = true; try { BeginSuppressVaultRemoveHook(); bool flag; try { flag = TryRemoveFromInventory(__instance, item, amount) || RemoveItemFromInventory(item, amount); } finally { EndSuppressVaultRemoveHook(); } if (!flag) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)$"Failed to remove {amount} of item {item} from inventory for auto-deposit"); } } else { AddCurrencyToVault(vaultManager, currencyForItem, amount); } } finally { _isProcessingAutoDeposit = false; } } catch (Exception ex) { _isProcessingAutoDeposit = false; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnInventoryAddItemWithNotify: " + ex.Message)); } } } private static void BeginSuppressVaultRemoveHook() { _suppressVaultRemoveHookDepth++; } private static void EndSuppressVaultRemoveHook() { _suppressVaultRemoveHookDepth = Math.Max(0, _suppressVaultRemoveHookDepth - 1); } private static bool IsVaultRemoveHookSuppressed() { return _suppressVaultRemoveHookDepth > 0; } public static bool OnInventoryAddItemObjectPrefix(object __instance, object item, int amount, int slot, bool sendNotification, bool specialItem, bool superSecretCheck) { try { if (_isProcessingAutoDeposit) { return true; } if (!IsPlayerMainInventory(__instance)) { return true; } int itemId = GetItemId(item); if (itemId < 0) { return true; } if (IsWithdrawing || _withdrawingItemIds.Contains(itemId)) { return true; } if (!ShouldAutoDeposit(itemId)) { return true; } string currencyForItem = GetCurrencyForItem(itemId); if (string.IsNullOrEmpty(currencyForItem)) { return true; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { return true; } _isProcessingAutoDeposit = true; try { AddCurrencyToVault(vaultManager, currencyForItem, amount); if (sendNotification) { EnqueueAutoDepositNotification(currencyForItem, amount); } _skipObjectAddItemPostfixOnce = true; return false; } finally { _isProcessingAutoDeposit = false; } } catch (Exception ex) { _isProcessingAutoDeposit = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Error in OnInventoryAddItemObjectPrefix: " + ex.Message + "\n" + ex.StackTrace)); } return true; } } public static void OnInventoryAddItemObjectPostfix(object __instance, object item, int amount, int slot, bool sendNotification, bool specialItem, bool superSecretCheck) { try { if (_skipObjectAddItemPostfixOnce) { _skipObjectAddItemPostfixOnce = false; } else { if (_isProcessingAutoDeposit || !IsPlayerMainInventory(__instance)) { return; } int itemId = GetItemId(item); if (IsWithdrawing || _withdrawingItemIds.Contains(itemId) || !ShouldAutoDeposit(itemId)) { return; } string currencyForItem = GetCurrencyForItem(itemId); if (string.IsNullOrEmpty(currencyForItem)) { return; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { return; } _isProcessingAutoDeposit = true; try { BeginSuppressVaultRemoveHook(); bool flag; try { flag = TryRemoveFromInventory(__instance, itemId, amount) || RemoveItemFromInventory(itemId, amount); } finally { EndSuppressVaultRemoveHook(); } if (flag) { AddCurrencyToVault(vaultManager, currencyForItem, amount); } return; } finally { _isProcessingAutoDeposit = false; } } } catch (Exception ex) { _isProcessingAutoDeposit = false; ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)("Error in OnInventoryAddItemObjectPostfix: " + ex.Message + "\n" + ex.StackTrace)); } } } public static void InitializePickupCache() { EnsureItemIdCache(); if (!_canResolveItemId) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[ItemPatches] Item ID resolver is unavailable; pickup object patch paths will be skipped."); } } } private static void EnsureItemIdCache() { if (_itemIdReflectionCached) { return; } lock (typeof(ItemPatches)) { if (_itemIdReflectionCached) { return; } try { Type typeFromHandle = typeof(Item); _cachedItemIdField = AccessTools.Field(typeFromHandle, "id") ?? AccessTools.Field(typeFromHandle, "_id"); if (_cachedItemIdField == null) { _cachedItemIdProperty = AccessTools.Property(typeFromHandle, "id") ?? AccessTools.Property(typeFromHandle, "Id") ?? AccessTools.Property(typeFromHandle, "ItemID"); } if (_cachedItemIdField == null && _cachedItemIdProperty == null) { _cachedItemIdMethod = AccessTools.Method(typeFromHandle, "ID", (Type[])null, (Type[])null); if (_cachedItemIdMethod != null) { try { Func typedDelegate = (Func)Delegate.CreateDelegate(typeof(Func), _cachedItemIdMethod); _cachedGetItemIdDelegate = delegate(object obj) { Item val = (Item)((obj is Item) ? obj : null); return (val != null) ? typedDelegate(val) : (-1); }; } catch { _cachedGetItemIdDelegate = null; } } } _canResolveItemId = _cachedGetItemIdDelegate != null || _cachedItemIdField != null || _cachedItemIdProperty != null; _itemIdReflectionCached = true; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[ItemPatches] EnsureItemIdCache: " + ex.Message)); } _canResolveItemId = false; _itemIdReflectionCached = true; } } } private static int GetItemId(object item) { if (item == null) { return -1; } Item val = (Item)((item is Item) ? item : null); if (val != null) { return val.ID(); } EnsureItemIdCache(); if (!_canResolveItemId) { if (!_loggedMissingItemIdResolver) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"[ItemPatches] Missing item ID resolver; skipping object-based pickup handling."); } _loggedMissingItemIdResolver = true; } return -1; } try { if (_cachedGetItemIdDelegate != null) { return _cachedGetItemIdDelegate(item); } if (_cachedItemIdField != null && _cachedItemIdField.GetValue(item) is int result) { return result; } if (_cachedItemIdProperty != null) { object value = _cachedItemIdProperty.GetValue(item); if (value is int) { return (int)value; } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogDebug((object)("[ItemPatches] GetItemId: " + ex.Message)); } } return -1; } private static InventoryRemovePlan ResolveInventoryRemovePlan(Type invType) { if (_inventoryRemoveByType.TryGetValue(invType, out var value)) { return value; } InventoryRemovePlan inventoryRemovePlan = default(InventoryRemovePlan); inventoryRemovePlan.Kind = InventoryRemoveKind.None; inventoryRemovePlan.Method = null; InventoryRemovePlan inventoryRemovePlan2 = inventoryRemovePlan; MethodInfo method = invType.GetMethod("RemoveItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(int), typeof(int), typeof(int) }, null); if (method != null) { inventoryRemovePlan2.Kind = InventoryRemoveKind.IntIntInt; inventoryRemovePlan2.Method = method; } else { method = invType.GetMethod("RemoveItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(int), typeof(int) }, null); if (method != null) { inventoryRemovePlan2.Kind = InventoryRemoveKind.IntInt; inventoryRemovePlan2.Method = method; } else { method = invType.GetMethod("RemoveItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[3] { typeof(int), typeof(int), typeof(bool) }, null); if (method != null) { inventoryRemovePlan2.Kind = InventoryRemoveKind.IntIntBool; inventoryRemovePlan2.Method = method; } else { method = invType.GetMethod("RemoveItemAmount", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2] { typeof(int), typeof(int) }, null); if (method != null) { inventoryRemovePlan2.Kind = InventoryRemoveKind.RemoveAmount; inventoryRemovePlan2.Method = method; } } } } _inventoryRemoveByType[invType] = inventoryRemovePlan2; return inventoryRemovePlan2; } private static bool InvokeInventoryRemove(in InventoryRemovePlan plan, object inventory, int itemId, int amount) { if (plan.Method == null) { return false; } try { object obj; switch (plan.Kind) { case InventoryRemoveKind.IntIntInt: obj = plan.Method.Invoke(inventory, new object[3] { itemId, amount, 0 }); break; case InventoryRemoveKind.IntInt: obj = plan.Method.Invoke(inventory, new object[2] { itemId, amount }); break; case InventoryRemoveKind.IntIntBool: obj = plan.Method.Invoke(inventory, new object[3] { itemId, amount, false }); break; case InventoryRemoveKind.RemoveAmount: obj = plan.Method.Invoke(inventory, new object[2] { itemId, amount }); break; default: return false; } if (obj is bool result) { return result; } if (obj is ICollection collection) { return collection.Count > 0; } return plan.Kind == InventoryRemoveKind.IntIntInt && obj != null; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[ItemPatches] Inventory remove invoke failed: " + ex.Message)); } return false; } } private static bool TryRemoveFromInventory(object inventory, int itemId, int amount) { if (inventory == null) { return false; } InventoryRemovePlan plan = ResolveInventoryRemovePlan(inventory.GetType()); return InvokeInventoryRemove(in plan, inventory, itemId, amount); } private static bool RemoveItemFromInventory(int itemId, int amount) { try { object obj = Player.Instance; if (obj == null) { PropertyInfo property = typeof(Player).GetProperty("LocalPlayer", BindingFlags.Static | BindingFlags.Public); if (property != null) { obj = property.GetValue(null); } } if (obj == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Player (Instance and LocalPlayer) is null"); } return false; } object playerBagForMutation = GetPlayerBagForMutation(); if (playerBagForMutation != null) { return TryRemoveFromInventory(playerBagForMutation, itemId, amount); } Type type = obj.GetType(); FieldInfo fieldInfo = type.GetField("playerInventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("_playerInventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("inventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetField("_inventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); PropertyInfo propertyInfo = type.GetProperty("PlayerInventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? type.GetProperty("Inventory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); object obj2 = ((fieldInfo != null) ? fieldInfo.GetValue(obj) : null); if (obj2 == null && propertyInfo != null) { obj2 = propertyInfo.GetValue(obj); } if (obj2 != null) { return TryRemoveFromInventory(obj2, itemId, amount); } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"Could not find player inventory"); } return false; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Error removing item from inventory: " + ex.Message)); } return false; } } private static bool AddItemToInventory(int itemId, int amount) { try { Player instance = Player.Instance; if ((Object)(object)instance == (Object)null) { return false; } object playerBagForMutation = GetPlayerBagForMutation(); if (playerBagForMutation != null) { Inventory val = (Inventory)((playerBagForMutation is Inventory) ? playerBagForMutation : null); if (val != null) { val.AddItem(itemId, amount, true); return true; } MethodInfo methodInfo = AccessTools.Method(playerBagForMutation.GetType(), "AddItem", new Type[3] { typeof(int), typeof(int), typeof(bool) }, (Type[])null); if (methodInfo != null) { methodInfo.Invoke(playerBagForMutation, new object[3] { itemId, amount, true }); return true; } MethodInfo methodInfo2 = AccessTools.Method(playerBagForMutation.GetType(), "AddItem", new Type[5] { typeof(int), typeof(int), typeof(int), typeof(bool), typeof(bool) }, (Type[])null); if (methodInfo2 != null) { methodInfo2.Invoke(playerBagForMutation, new object[5] { itemId, amount, 0, true, true }); return true; } } MethodInfo methodInfo3 = AccessTools.Method(typeof(Player), "AddItemToInventory", new Type[2] { typeof(int), typeof(int) }, (Type[])null); if (methodInfo3 != null) { object obj = methodInfo3.Invoke(instance, new object[2] { itemId, amount }); 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; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Could not find a method to add items to inventory"); } return false; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error adding item to inventory: " + ex.Message)); } return false; } } private static void AddCurrencyToVault(VaultManager vaultManager, string currencyId, int amount) { if (TryStripPrefix(currencyId, "seasonal_", out string suffix)) { if (Enum.TryParse(suffix, out var result)) { vaultManager.AddSeasonalTokens(result, amount); } } else if (TryStripPrefix(currencyId, "community_", out suffix)) { vaultManager.AddCommunityTokens(suffix, amount); } else if (TryStripPrefix(currencyId, "special_", out suffix)) { vaultManager.AddSpecial(suffix, amount); } else if (TryStripPrefix(currencyId, "key_", out suffix)) { vaultManager.AddKeys(suffix, amount); } else if (TryStripPrefix(currencyId, "ticket_", out suffix)) { vaultManager.AddTickets(suffix, amount); } else if (TryStripPrefix(currencyId, "orb_", out suffix)) { vaultManager.AddOrbs(suffix, amount); } else if (TryStripPrefix(currencyId, "custom_", out suffix)) { vaultManager.AddCustomCurrency(suffix, amount); } } private static bool RemoveCurrencyFromVault(VaultManager vaultManager, string currencyId, int amount) { if (TryStripPrefix(currencyId, "seasonal_", out string suffix)) { if (Enum.TryParse(suffix, out var result)) { return vaultManager.RemoveSeasonalTokens(result, amount); } } else { if (TryStripPrefix(currencyId, "community_", out suffix)) { return vaultManager.RemoveCommunityTokens(suffix, amount); } if (TryStripPrefix(currencyId, "special_", out suffix)) { return vaultManager.RemoveSpecial(suffix, amount); } if (TryStripPrefix(currencyId, "key_", out suffix)) { return vaultManager.RemoveKeys(suffix, amount); } if (TryStripPrefix(currencyId, "ticket_", out suffix)) { return vaultManager.RemoveTickets(suffix, amount); } if (TryStripPrefix(currencyId, "orb_", out suffix)) { return vaultManager.RemoveOrbs(suffix, amount); } if (TryStripPrefix(currencyId, "custom_", out suffix)) { return vaultManager.RemoveCustomCurrency(suffix, amount); } } return false; } private static bool TryStripPrefix(string id, string prefix, out string suffix) { if (id.StartsWith(prefix, StringComparison.Ordinal)) { suffix = id.Substring(prefix.Length); return true; } suffix = null; return false; } public static int GetVaultAmount(int itemId) { string currencyForItem = GetCurrencyForItem(itemId); if (string.IsNullOrEmpty(currencyForItem)) { return 0; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { return 0; } if (currencyForItem.StartsWith("seasonal_")) { if (Enum.TryParse(currencyForItem.Substring("seasonal_".Length), out var result)) { return vaultManager.GetSeasonalTokens(result); } } else { if (currencyForItem.StartsWith("community_")) { return vaultManager.GetCommunityTokens(currencyForItem.Substring("community_".Length)); } if (currencyForItem.StartsWith("special_")) { return vaultManager.GetSpecial(currencyForItem.Substring("special_".Length)); } if (currencyForItem.StartsWith("key_")) { return vaultManager.GetKeys(currencyForItem.Substring("key_".Length)); } if (currencyForItem.StartsWith("ticket_")) { return vaultManager.GetTickets(currencyForItem.Substring("ticket_".Length)); } if (currencyForItem.StartsWith("orb_")) { return vaultManager.GetOrbs(currencyForItem.Substring("orb_".Length)); } } return 0; } public static bool IsVaultCurrency(int itemId) { return _itemToCurrency.ContainsKey(itemId); } public static void OnInventoryGetAmount(object __instance, int id, ref int __result) { try { if (_skipVaultInGetAmount || !IsVaultCurrency(id) || !IsPlayerMainInventory(__instance)) { return; } int vaultAmount = GetVaultAmount(id); if (vaultAmount > 0) { int num = __result; __result += vaultAmount; ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)$"GetAmount for item {id}: inventory={num}, vault={vaultAmount}, total={__result}"); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnInventoryGetAmount: " + ex.Message)); } } } public static void OnInventoryHasEnough(object __instance, int id, int amount, ref bool __result) { try { if (!IsPlayerMainInventory(__instance) || __result || !IsVaultCurrency(id)) { return; } int vaultAmount = GetVaultAmount(id); if (vaultAmount >= amount) { __result = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"HasEnough for item {id}: vault has {vaultAmount}, need {amount} - returning TRUE"); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnInventoryHasEnough: " + ex.Message)); } } } public static void OnInventoryRemoveItemPrefix(object __instance, int id, int amount, int slot, ref int __state) { __state = -1; try { if (IsVaultRemoveHookSuppressed() || _isProcessingAutoDeposit || IsWithdrawing || !IsVaultCurrency(id) || !IsPlayerMainInventory(__instance)) { return; } _skipVaultInGetAmount = true; try { Inventory val = (Inventory)((__instance is Inventory) ? __instance : null); if (val != null) { __state = val.GetAmount(id); } else { MethodInfo methodInfo = AccessTools.Method(__instance.GetType(), "GetAmount", new Type[1] { typeof(int) }, (Type[])null); if (methodInfo != null) { __state = (int)(methodInfo.Invoke(__instance, new object[1] { id }) ?? ((object)0)); } } } finally { _skipVaultInGetAmount = false; } if (__state >= 0) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"RemoveItem prefix: item {id}, rawInventory={__state}, requestedAmount={amount}"); } } } catch (Exception ex) { _skipVaultInGetAmount = false; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnInventoryRemoveItemPrefix: " + ex.Message)); } } } public static void OnInventoryRemoveItemPostfix(object __instance, int id, int amount, int slot, int __state) { try { if (IsVaultRemoveHookSuppressed() || _isProcessingAutoDeposit || IsWithdrawing || !IsVaultCurrency(id) || !IsPlayerMainInventory(__instance) || __state < 0) { return; } if (__state >= amount) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"RemoveItem postfix: item {id}, inventory had enough ({__state} >= {amount})"); } return; } int num = amount - __state; string currencyForItem = GetCurrencyForItem(id); if (string.IsNullOrEmpty(currencyForItem)) { return; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { return; } int vaultAmount = GetVaultAmount(id); if (num > 0 && vaultAmount >= num) { bool flag = RemoveCurrencyFromVault(vaultManager, currencyForItem, num); ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"RemoveItem postfix: deducted {num} of item {id} from vault (success={flag})"); } } else if (num > 0) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)$"RemoveItem postfix: vault has {vaultAmount} but need {num} more"); } } } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("Error in OnInventoryRemoveItemPostfix: " + ex.Message)); } } } public static void EnqueueAutoDepositNotification(string currencyId, int amount) { int itemForCurrency = GetItemForCurrency(currencyId); if (itemForCurrency < 0) { return; } lock (_notifyQueueLock) { _autoDepositNotifyQueue.Add((itemForCurrency, amount)); } } public static void DrainAutoDepositNotifications() { lock (_notifyQueueLock) { if (_autoDepositNotifyQueue.Count == 0) { return; } _mergeBuffer.Clear(); foreach (var item in _autoDepositNotifyQueue) { if (!_mergeBuffer.TryGetValue(item.itemId, out var value)) { value = 0; } _mergeBuffer[item.itemId] = value + item.amount; } _autoDepositNotifyQueue.Clear(); bool flag = true; foreach (KeyValuePair item2 in _mergeBuffer) { if (flag) { ShowAutoDepositNotificationImmediate(item2.Key, item2.Value); flag = false; } else { _autoDepositNotifyQueue.Add((item2.Key, item2.Value)); } } } } private static void ShowAutoDepositNotificationImmediate(int itemId, int amount) { try { string text = TryGetItemName(itemId); if (string.IsNullOrWhiteSpace(text)) { text = "Vault"; } NotificationStack instance = SingletonBehaviour.Instance; if (instance != null) { instance.SendNotification(text, itemId, amount, false, false); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[ItemPatches] ShowAutoDepositNotificationImmediate: " + ex.Message)); } } } private static string TryGetItemName(int itemId) { try { if (_itemInfoDatabaseInstance != null) { object itemInfoDatabaseInstance = _itemInfoDatabaseInstance; Object val = (Object)((itemInfoDatabaseInstance is Object) ? itemInfoDatabaseInstance : null); if (val == null || !(val == (Object)null)) { goto IL_0054; } } _itemInfoDatabaseInstance = ReflectionHelper.GetSingletonInstance(ReflectionHelper.FindWishType("ItemInfoDatabase")); _allItemSellInfosField = _itemInfoDatabaseInstance?.GetType().GetField("allItemSellInfos", BindingFlags.Instance | BindingFlags.Public); goto IL_0054; IL_0054: if (_itemInfoDatabaseInstance == null || _allItemSellInfosField == null) { return null; } if (!(_allItemSellInfosField.GetValue(_itemInfoDatabaseInstance) is IDictionary dictionary) || !dictionary.Contains(itemId)) { return null; } object obj = dictionary[itemId]; if (obj == null) { return null; } PropertyInfo property = obj.GetType().GetProperty("name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { return property.GetValue(obj) as string; } FieldInfo field = obj.GetType().GetField("name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(obj) as string; } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)$"[ItemPatches] TryGetItemName({itemId}): {ex.Message}"); } } return null; } } public static class PlayerPatches { private static bool _isVaultLoaded = false; private static string _loadedCharacterName = null; private static string _pendingCharacterName = null; private static string _lastCharacterSourceLog = null; private static string _lastCharacterNameLog = null; private static readonly object _contextLoadLock = new object(); private static float _lastSaveAndResetRealtime = -100f; private const float SaveAndResetDedupSeconds = 1.5f; public static bool IsVaultLoaded => _isVaultLoaded; public static string LoadedCharacterName => _loadedCharacterName; public static void OnPlayerInitialized(Player __instance) { try { Plugin.EnsureUIComponentsExist(); string currentCharacterName = GetCurrentCharacterName(); if (string.IsNullOrEmpty(currentCharacterName) || currentCharacterName == "default") { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Could not determine character name on player init"); } return; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Player initialized: " + currentCharacterName)); } lock (_contextLoadLock) { if (_isVaultLoaded && _loadedCharacterName == currentCharacterName) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("Vault already loaded for " + currentCharacterName)); } return; } if (_isVaultLoaded && _loadedCharacterName != currentCharacterName) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("Switching from " + _loadedCharacterName + " to " + currentCharacterName)); } Plugin.SaveVault(); ResetState(); } LoadVaultForCharacter(currentCharacterName); } } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)("Error in OnPlayerInitialized: " + ex.Message)); } } } private static void LoadVaultForCharacter(string characterName) { try { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Loading vault for: " + characterName)); } if (!Plugin.LoadVaultForPlayer(characterName)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Vault load did not run for '" + characterName + "' (invalid name or save system missing). Previous vault state is unchanged — not marking as loaded.")); } return; } _isVaultLoaded = true; _loadedCharacterName = characterName; ClearPendingCharacterName(); VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)"VaultManager not available during character load"); } } else { vaultManager.SetPlayerName(characterName); } IconCache.LoadAllIcons(); ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)("Vault loaded successfully for " + characterName)); } } catch (Exception ex) { ManualLogSource log5 = Plugin.Log; if (log5 != null) { log5.LogError((object)("Error loading vault: " + ex.Message)); } } } private static string GetCurrentCharacterName() { try { if (!string.IsNullOrEmpty(_pendingCharacterName)) { string text = NormalizeCharacterNameForVault(_pendingCharacterName); LogCharacterSourceOnce("pending", text); return text; } string lastLoadedCharacterName = GameSavePatches.LastLoadedCharacterName; if (!string.IsNullOrEmpty(lastLoadedCharacterName)) { string text2 = NormalizeCharacterNameForVault(lastLoadedCharacterName); LogCharacterSourceOnce("lastLoaded", text2); return text2; } CharacterData currentCharacter = GameSave.CurrentCharacter; if (currentCharacter != null && !string.IsNullOrEmpty(currentCharacter.characterName)) { string text3 = NormalizeCharacterNameForVault(currentCharacter.characterName); ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("GetCurrentCharacterName: FALLBACK to CurrentCharacter = '" + text3 + "' (LastLoadedCharacterName was null)")); } return text3; } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"GetCurrentCharacterName: Could not determine character name from any source"); } return "default"; } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Error getting character name: " + ex.Message)); } return "default"; } } internal static string NormalizeCharacterNameForVault(string name) { if (string.IsNullOrEmpty(name)) { return "default"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); foreach (char oldChar in invalidFileNameChars) { name = name.Replace(oldChar, '_'); } name = name.Trim(); if (!string.IsNullOrEmpty(name)) { return name; } return "default"; } internal static void SetPendingCharacterName(string characterName) { _pendingCharacterName = NormalizeCharacterNameForVault(characterName); ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("Set pending character name: '" + _pendingCharacterName + "'")); } } private static void ClearPendingCharacterName() { _pendingCharacterName = null; } private static void LogCharacterSourceOnce(string source, string characterName) { if (!string.Equals(_lastCharacterSourceLog, source, StringComparison.Ordinal) || !string.Equals(_lastCharacterNameLog, characterName, StringComparison.Ordinal)) { _lastCharacterSourceLog = source; _lastCharacterNameLog = characterName; ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("GetCurrentCharacterName: Using " + source + " character name = '" + characterName + "'")); } } } public static void ResetState() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Resetting vault state"); } _isVaultLoaded = false; _loadedCharacterName = null; _lastCharacterSourceLog = null; _lastCharacterNameLog = null; ClearPendingCharacterName(); GameSavePatches.ResetLastLoadedSlot(); ItemPatches.ResetState(); IconCache.Clear(); } public static void SaveAndReset() { try { float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup - _lastSaveAndResetRealtime < 1.5f) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)$"SaveAndReset deduplicated (last run {realtimeSinceStartup - _lastSaveAndResetRealtime:0.00}s ago)"); } return; } _lastSaveAndResetRealtime = realtimeSinceStartup; if (_isVaultLoaded) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("Saving vault for " + _loadedCharacterName + " before menu")); } Plugin.SaveVault(); } ResetState(); SecretGifts.ResetGiftCheck(); } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Error in SaveAndReset: " + ex.Message)); } } } public static void ResetVaultLoaded() { ResetState(); } public static void ForceVaultReload() { ResetState(); } public static void TriggerVaultLoad(string characterName) { try { string text = NormalizeCharacterNameForVault(characterName); if (string.IsNullOrEmpty(text) || string.Equals(text, "default", StringComparison.OrdinalIgnoreCase)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"TriggerVaultLoad: invalid character name"); } return; } lock (_contextLoadLock) { if (_isVaultLoaded && string.Equals(_loadedCharacterName, text, StringComparison.Ordinal)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)("TriggerVaultLoad: vault already loaded for " + text)); } return; } if (_isVaultLoaded && !string.Equals(_loadedCharacterName, text, StringComparison.Ordinal)) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)("TriggerVaultLoad: switching from " + _loadedCharacterName + " to " + text)); } Plugin.SaveVault(); ResetState(); } LoadVaultForCharacter(text); } } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("TriggerVaultLoad: " + ex.Message)); } } } internal static void TrySynchronizeCharacterContext() { try { if ((Object)(object)Player.Instance == (Object)null) { return; } string currentCharacterName = GetCurrentCharacterName(); if (string.IsNullOrEmpty(currentCharacterName) || string.Equals(currentCharacterName, "default", StringComparison.OrdinalIgnoreCase)) { return; } lock (_contextLoadLock) { if (!_isVaultLoaded || !string.Equals(_loadedCharacterName, currentCharacterName, StringComparison.Ordinal)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("[CharacterSync] Aligning vault context to '" + currentCharacterName + "' (currently '" + (_loadedCharacterName ?? "none") + "')")); } Plugin.EnsureUIComponentsExist(); if (_isVaultLoaded && !string.Equals(_loadedCharacterName, currentCharacterName, StringComparison.Ordinal)) { Plugin.SaveVault(); ResetState(); } LoadVaultForCharacter(currentCharacterName); } } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("[CharacterSync] Error: " + ex.Message)); } } } } public static class SaveLoadPatches { public static void OnGameSaved() { try { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Game saved - saving vault"); } Plugin.SaveVault(); } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error saving vault: " + ex.Message)); } } } public static void OnGameLoaded() { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Game loaded"); } } public static void OnReturnToMenu() { try { ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)"Returning to menu"); } PlayerPatches.SaveAndReset(); } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error on return to menu: " + ex.Message)); } } } } public static class ShopPatches { private static Dictionary _vaultPurchaseRequirements = new Dictionary(); private static readonly Dictionary _currencyDisplayNameCache = new Dictionary(StringComparer.Ordinal); public static void RegisterVaultPurchase(int itemId, string currencyId, int amount) { _vaultPurchaseRequirements[itemId] = (currencyId, amount); ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)$"Registered vault purchase: Item {itemId} requires {amount} {currencyId}"); } } public static void ClearVaultPurchases() { _vaultPurchaseRequirements.Clear(); } public static bool OnBeforeBuyItem(object __instance, object __0, int __1) { try { int itemIdFromItemInfo = GetItemIdFromItemInfo(__0); if (itemIdFromItemInfo < 0) { return true; } if (!_vaultPurchaseRequirements.TryGetValue(itemIdFromItemInfo, out (string, int) value)) { return true; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"VaultManager not available for purchase check"); } return true; } if (!vaultManager.HasCurrency(value.Item1, value.Item2)) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"Purchase blocked: insufficient {value.Item1} (need {value.Item2})"); } ShowInsufficientFundsMessage(value.Item1, value.Item2); return false; } ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogInfo((object)$"Vault purchase approved: {value.Item2} {value.Item1}"); } return true; } catch (Exception ex) { ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogError((object)("Error in OnBeforeBuyItem: " + ex.Message)); } return true; } } public static void OnAfterBuyItem(object __instance, object __0, int __1) { try { int itemIdFromItemInfo = GetItemIdFromItemInfo(__0); if (itemIdFromItemInfo < 0 || !_vaultPurchaseRequirements.TryGetValue(itemIdFromItemInfo, out (string, int) value)) { return; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { return; } if (!DeductCurrency(vaultManager, value.Item1, value.Item2)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogError((object)$"Failed to deduct {value.Item2} {value.Item1} after purchase; potential desync."); } } else { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"Deducted {value.Item2} {value.Item1} for purchase"); } } } catch (Exception ex) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogError((object)("Error in OnAfterBuyItem: " + ex.Message)); } } } public static void OnAfterBuyItemSingle(object __instance, object __0) { OnAfterBuyItem(__instance, __0, 1); } public static bool OnBeforeBuyItemSingle(object __instance, object __0) { try { int itemIdFromItemInfo = GetItemIdFromItemInfo(__0); if (itemIdFromItemInfo < 0) { return true; } if (!_vaultPurchaseRequirements.TryGetValue(itemIdFromItemInfo, out (string, int) value)) { return true; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"VaultManager not available"); } return true; } if (!vaultManager.HasCurrency(value.Item1, value.Item2)) { ShowInsufficientFundsMessage(value.Item1, value.Item2); return false; } return true; } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error in OnBeforeBuyItemSingle: " + ex.Message)); } return true; } } private static int GetItemIdFromItemInfo(object itemInfo) { if (itemInfo == null) { return -1; } try { ShopItemInfo2 val = (ShopItemInfo2)((itemInfo is ShopItemInfo2) ? itemInfo : null); if (val != null) { return val.id; } ShopLoot2 val2 = (ShopLoot2)((itemInfo is ShopLoot2) ? itemInfo : null); if (val2 != null) { return val2.id; } FieldInfo fieldInfo = AccessTools.Field(itemInfo.GetType(), "id"); if (fieldInfo != null) { return (int)fieldInfo.GetValue(itemInfo); } PropertyInfo propertyInfo = AccessTools.Property(itemInfo.GetType(), "id"); if (propertyInfo != null) { return (int)propertyInfo.GetValue(itemInfo); } return -1; } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogDebug((object)("[ShopPatches] GetItemIdFromItemInfo: " + ex.Message)); } return -1; } } private static bool DeductCurrency(VaultManager vaultManager, string currencyId, int amount) { if (TryExtractSuffix(currencyId, "seasonal_", out string suffix)) { if (Enum.TryParse(suffix, out var result)) { return vaultManager.RemoveSeasonalTokens(result, amount); } ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Invalid seasonal token type '" + suffix + "' in currency id '" + currencyId + "'")); } return false; } if (TryExtractSuffix(currencyId, "community_", out string suffix2)) { return vaultManager.RemoveCommunityTokens(suffix2, amount); } if (TryExtractSuffix(currencyId, "key_", out string suffix3)) { return vaultManager.RemoveKeys(suffix3, amount); } if (TryExtractSuffix(currencyId, "special_", out string suffix4)) { return vaultManager.RemoveSpecial(suffix4, amount); } if (TryExtractSuffix(currencyId, "orb_", out string suffix5)) { return vaultManager.RemoveOrbs(suffix5, amount); } if (TryExtractSuffix(currencyId, "custom_", out string suffix6)) { return vaultManager.RemoveCustomCurrency(suffix6, amount); } ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)("Unknown vault currency id format '" + currencyId + "', skipping deduction")); } return false; } private static void ShowInsufficientFundsMessage(string currencyId, int required) { try { string currencyDisplayName = GetCurrencyDisplayName(currencyId); string text = $"Need {required} {currencyDisplayName}"; if ((Object)(object)SingletonBehaviour.Instance != (Object)null) { SingletonBehaviour.Instance.SendNotification(text, 0, 0, false, false); return; } ManualLogSource log = Plugin.Log; if (log != null) { log.LogInfo((object)("Insufficient funds: " + text + " (" + currencyId + ")")); } } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogError((object)("Error showing notification: " + ex.Message)); } } } private static string GetCurrencyDisplayName(string currencyId) { if (string.IsNullOrEmpty(currencyId)) { return string.Empty; } if (_currencyDisplayNameCache.TryGetValue(currencyId, out string value)) { return value; } string text = currencyId; string suffix2; string suffix3; string suffix4; string suffix5; string suffix6; if (TryExtractSuffix(currencyId, "seasonal_", out string suffix)) { text = suffix + " Tokens"; } else if (TryExtractSuffix(currencyId, "community_", out suffix2)) { text = "Community Tokens"; } else if (TryExtractSuffix(currencyId, "key_", out suffix3)) { text = suffix3 + " Keys"; } else if (TryExtractSuffix(currencyId, "special_", out suffix4)) { text = FormatSpecialName(suffix4); } else if (TryExtractSuffix(currencyId, "orb_", out suffix5)) { text = suffix5 + " Orbs"; } else if (TryExtractSuffix(currencyId, "custom_", out suffix6)) { text = suffix6; } _currencyDisplayNameCache[currencyId] = text; return text; } private static bool TryExtractSuffix(string currencyId, string prefix, out string suffix) { suffix = string.Empty; if (string.IsNullOrEmpty(currencyId)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Encountered empty currency id"); } return false; } if (!currencyId.StartsWith(prefix, StringComparison.Ordinal)) { return false; } suffix = currencyId.Remove(0, prefix.Length); return true; } private static string FormatSpecialName(string specialName) { return specialName switch { "doubloon" => "Doubloons", "blackbottlecap" => "Black Bottle Caps", "redcarnivalticket" => "Red Carnival Tickets", "candycornpieces" => "Candy Corn Pieces", "manashard" => "Mana Shards", _ => specialName, }; } } } namespace TheVault.Integration { internal sealed class VaultModApiAdapter : IVaultModApi { public bool IsVaultReady => VaultIntegration.IsVaultReady; public bool RegisterCustomCurrency(string id, string displayName, int gameItemId = -1, bool enableAutoDeposit = false) { return VaultIntegration.RegisterCustomCurrency(id, displayName, gameItemId, enableAutoDeposit); } } } namespace TheVault.Api { public static class VaultIntegration { private static readonly Regex ValidId = new Regex("^[a-z0-9_]{1,64}$", RegexOptions.Compiled | RegexOptions.CultureInvariant); public static bool IsVaultManagerAvailable => Plugin.GetVaultManager() != null; public static bool IsVaultReady => IsVaultManagerAvailable; public static bool IsVaultDataLoadedForCurrentSession => PlayerPatches.IsVaultLoaded; public static bool RegisterCustomCurrency(string id, string displayName, int gameItemId = -1, bool enableAutoDeposit = false) { if (string.IsNullOrWhiteSpace(displayName)) { displayName = id; } string text = NormalizeId(id); if (text == null || !ValidId.IsMatch(text)) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("[VaultIntegration] Invalid custom currency id '" + id + "'. Use lowercase letters, digits, underscore only (max 64).")); } return false; } VaultManager vaultManager = Plugin.GetVaultManager(); if (vaultManager == null) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogWarning((object)"[VaultIntegration] The Vault is not loaded yet. Use [BepInDependency(\"com.azraelgodking.thevault\")] and register from Awake, or Chainloader.OnPluginActivated."); } return false; } if (vaultManager.GetCurrencyDefinition(text) != null) { ManualLogSource log3 = Plugin.Log; if (log3 != null) { log3.LogWarning((object)("[VaultIntegration] Currency id '" + text + "' is already registered.")); } return false; } CurrencyDefinition definition = new CurrencyDefinition(text, displayName.Trim(), CurrencyCategory.Custom, gameItemId); vaultManager.RegisterCurrency(definition); string text2 = VaultCurrencyIds.FullCustom(text); if (gameItemId > 0) { IconCache.RegisterCurrency(text2, gameItemId); ItemPatches.RegisterItemCurrencyMapping(gameItemId, text2, enableAutoDeposit); } ManualLogSource log4 = Plugin.Log; if (log4 != null) { log4.LogInfo((object)$"[VaultIntegration] Registered custom currency '{text2}' ({displayName}), itemId={gameItemId}, autoDeposit={enableAutoDeposit}"); } return true; } private static string NormalizeId(string id) { if (string.IsNullOrWhiteSpace(id)) { return null; } id = id.Trim().ToLowerInvariant(); if (id.StartsWith("custom_", StringComparison.Ordinal)) { id = id.Substring("custom_".Length); } if (!string.IsNullOrEmpty(id)) { return id; } return null; } } }