Decompiled source of HavensRespec v1.2.0

HavensRespec.dll

Decompiled 15 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HavensRespec.Config;
using HavensRespec.Patches;
using HavensRespec.Services;
using HavensRespec.UI;
using Microsoft.CodeAnalysis;
using SunhavenMods.Shared;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using Wish;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("HavensRespec")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+9c743905a6a1ecc5bdd54cfadfb24c03b53de21b")]
[assembly: AssemblyProduct("HavensRespec")]
[assembly: AssemblyTitle("HavensRespec")]
[assembly: AssemblyVersion("1.0.0.0")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SunhavenMods.Shared
{
	public static class ConfigFileHelper
	{
		public static ConfigFile CreateNamedConfig(string pluginGuid, string configFileName, Action<string> logWarning = null)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			string text = Path.Combine(Paths.ConfigPath, configFileName);
			string text2 = Path.Combine(Paths.ConfigPath, pluginGuid + ".cfg");
			try
			{
				if (!File.Exists(text) && File.Exists(text2))
				{
					File.Copy(text2, text);
				}
			}
			catch (Exception ex)
			{
				logWarning?.Invoke("[Config] Migration to " + configFileName + " failed: " + ex.Message);
			}
			return new ConfigFile(text, true);
		}

		public static bool ReplacePluginConfig(BaseUnityPlugin plugin, ConfigFile newConfig, Action<string> logWarning = null)
		{
			if ((Object)(object)plugin == (Object)null || newConfig == null)
			{
				return false;
			}
			try
			{
				Type typeFromHandle = typeof(BaseUnityPlugin);
				PropertyInfo property = typeFromHandle.GetProperty("Config", BindingFlags.Instance | BindingFlags.Public);
				if (property != null && property.CanWrite)
				{
					property.SetValue(plugin, newConfig, null);
					return true;
				}
				FieldInfo field = typeFromHandle.GetField("<Config>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic);
				if (field != null)
				{
					field.SetValue(plugin, newConfig);
					return true;
				}
				FieldInfo[] fields = typeFromHandle.GetFields(BindingFlags.Instance | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (fieldInfo.FieldType == typeof(ConfigFile))
					{
						fieldInfo.SetValue(plugin, newConfig);
						return true;
					}
				}
			}
			catch (Exception ex)
			{
				logWarning?.Invoke("[Config] ReplacePluginConfig failed: " + ex.Message);
			}
			return false;
		}
	}
	public static class 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
		{
			public void StartCheck(string pluginGuid, string currentVersion, Action<VersionCheckResult> onComplete)
			{
				((MonoBehaviour)this).StartCoroutine(CheckVersionCoroutine(pluginGuid, currentVersion, onComplete));
			}

			private IEnumerator CheckVersionCoroutine(string pluginGuid, string currentVersion, Action<VersionCheckResult> 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);
						LogWarning(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);
							LogWarning(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);
							LogWarning(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)
						{
							Log("Update available for " + result.ModName + ": " + currentVersion + " -> " + result.LatestVersion);
						}
						else
						{
							Log(result.ModName + " is up to date (v" + currentVersion + ")");
						}
					}
					catch (Exception ex)
					{
						result.Success = false;
						result.ErrorMessage = "Parse error: " + ex.Message;
						RecordHealthError(pluginGuid, result.ErrorMessage);
						LogError(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 ManualLogSource _logger;

		private static readonly Dictionary<string, ModHealthSnapshot> HealthByPluginGuid = new Dictionary<string, ModHealthSnapshot>(StringComparer.OrdinalIgnoreCase);

		private static readonly object HealthLock = new object();

		private static readonly Dictionary<string, Regex> ModPatternCache = new Dictionary<string, Regex>(StringComparer.Ordinal);

		private static readonly object ModPatternCacheLock = new object();

		private static readonly Regex ExtractFieldRegex = new Regex("\"(?<key>[^\"]+)\"\\s*:\\s*(?:\"(?<value>[^\"]*)\"|null)", RegexOptions.Compiled);

		public static void CheckForUpdate(string pluginGuid, string currentVersion, ManualLogSource logger = null, Action<VersionCheckResult> onComplete = null)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			_logger = logger;
			TouchHealth(pluginGuid);
			VersionCheckRunner versionCheckRunner = new GameObject("VersionChecker").AddComponent<VersionCheckRunner>();
			Object.DontDestroyOnLoad((Object)(object)((Component)versionCheckRunner).gameObject);
			SceneRootSurvivor.TryRegisterPersistentRunnerGameObject(((Component)versionCheckRunner).gameObject);
			versionCheckRunner.StartCheck(pluginGuid, currentVersion, onComplete);
		}

		public static ModHealthSnapshot GetHealthSnapshot(string pluginGuid)
		{
			if (string.IsNullOrWhiteSpace(pluginGuid))
			{
				return null;
			}
			lock (HealthLock)
			{
				if (!HealthByPluginGuid.TryGetValue(pluginGuid, out var 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');
			string[] array = v1.Split(new char[1] { '.' });
			string[] array2 = v2.Split(new char[1] { '.' });
			int num = Math.Max(array.Length, array2.Length);
			for (int i = 0; i < num; i++)
			{
				int result;
				int num2 = ((i < array.Length && int.TryParse(array[i], out result)) ? result : 0);
				int result2;
				int num3 = ((i < array2.Length && int.TryParse(array2[i], out result2)) ? result2 : 0);
				if (num2 < num3)
				{
					return -1;
				}
				if (num2 > num3)
				{
					return 1;
				}
			}
			return 0;
		}

		internal static void Log(string message)
		{
			ManualLogSource logger = _logger;
			if (logger != null)
			{
				logger.LogInfo((object)("[VersionChecker] " + message));
			}
		}

		internal static void LogWarning(string message)
		{
			ManualLogSource logger = _logger;
			if (logger != null)
			{
				logger.LogWarning((object)("[VersionChecker] " + message));
			}
		}

		internal static void LogError(string message)
		{
			ManualLogSource logger = _logger;
			if (logger != null)
			{
				logger.LogError((object)("[VersionChecker] " + message));
			}
		}

		private static void TouchHealth(string pluginGuid)
		{
			if (string.IsNullOrWhiteSpace(pluginGuid))
			{
				return;
			}
			lock (HealthLock)
			{
				if (!HealthByPluginGuid.TryGetValue(pluginGuid, out var 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 var 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 var 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<string> NoKillSubstrings = new List<string>();

		private static Harmony _harmony;

		public static void TryRegisterPersistentRunnerGameObject(GameObject go)
		{
			if (!((Object)(object)go == (Object)null))
			{
				TryAddNoKillListSubstring(((Object)go).name);
			}
		}

		public static void TryAddNoKillListSubstring(string nameSubstring)
		{
			if (string.IsNullOrEmpty(nameSubstring))
			{
				return;
			}
			lock (Lock)
			{
				bool flag = false;
				for (int i = 0; i < NoKillSubstrings.Count; i++)
				{
					if (string.Equals(NoKillSubstrings[i], nameSubstring, StringComparison.OrdinalIgnoreCase))
					{
						flag = true;
						break;
					}
				}
				if (!flag)
				{
					NoKillSubstrings.Add(nameSubstring);
				}
			}
			EnsurePatched();
		}

		private static void EnsurePatched()
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_00a3: Expected O, but got Unknown
			if (_harmony != null)
			{
				return;
			}
			lock (Lock)
			{
				if (_harmony == null)
				{
					MethodInfo methodInfo = AccessTools.Method(typeof(Scene), "GetRootGameObjects", Type.EmptyTypes, (Type[])null);
					if (!(methodInfo == null))
					{
						string text = typeof(SceneRootSurvivor).Assembly.GetName().Name ?? "Unknown";
						Harmony val = new Harmony("SunhavenMods.SceneRootSurvivor." + text);
						val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(SceneRootSurvivor), "OnGetRootGameObjectsPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						_harmony = val;
					}
				}
			}
		}

		private static void OnGetRootGameObjectsPostfix(ref GameObject[] __result)
		{
			if (__result == null || __result.Length == 0)
			{
				return;
			}
			List<string> list;
			lock (Lock)
			{
				if (NoKillSubstrings.Count == 0)
				{
					return;
				}
				list = new List<string>(NoKillSubstrings);
			}
			List<GameObject> list2 = new List<GameObject>(__result);
			for (int i = 0; i < list.Count; i++)
			{
				string noKill = list[i];
				list2.RemoveAll((GameObject a) => (Object)(object)a != (Object)null && ((Object)a).name.IndexOf(noKill, StringComparison.OrdinalIgnoreCase) >= 0);
			}
			__result = list2.ToArray();
		}
	}
	public static class 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)
		{
			Type type = AccessTools.TypeByName(typeName);
			if (type != null)
			{
				return type;
			}
			for (int i = 0; i < namespaces.Length; i++)
			{
				type = AccessTools.TypeByName(namespaces[i] + "." + typeName);
				if (type != null)
				{
					return type;
				}
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == typeName || t.FullName == typeName);
					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;
			}
			PropertyInfo property = type.GetProperty(memberName, AllBindingFlags);
			if (property != null && property.GetMethod != null)
			{
				return property.GetValue(null);
			}
			FieldInfo field = type.GetField(memberName, AllBindingFlags);
			if (field != null)
			{
				return field.GetValue(null);
			}
			return null;
		}

		public static object GetSingletonInstance(Type type)
		{
			if (type == null)
			{
				return null;
			}
			string[] array = new string[5] { "Instance", "instance", "_instance", "Singleton", "singleton" };
			foreach (string memberName in array)
			{
				object staticValue = GetStaticValue(type, memberName);
				if (staticValue != null)
				{
					return staticValue;
				}
			}
			return null;
		}

		public static object GetInstanceValue(object instance, string memberName)
		{
			if (instance == null)
			{
				return null;
			}
			Type type = instance.GetType();
			while (type != null)
			{
				PropertyInfo property = type.GetProperty(memberName, AllBindingFlags);
				if (property != null && property.GetMethod != null)
				{
					return property.GetValue(instance);
				}
				FieldInfo field = type.GetField(memberName, AllBindingFlags);
				if (field != null)
				{
					return field.GetValue(instance);
				}
				type = type.BaseType;
			}
			return null;
		}

		public static bool SetInstanceValue(object instance, string memberName, object value)
		{
			if (instance == null)
			{
				return false;
			}
			Type type = instance.GetType();
			while (type != null)
			{
				PropertyInfo property = type.GetProperty(memberName, AllBindingFlags);
				if (property != null && property.SetMethod != null)
				{
					property.SetValue(instance, value);
					return true;
				}
				FieldInfo field = type.GetField(memberName, AllBindingFlags);
				if (field != null)
				{
					field.SetValue(instance, value);
					return true;
				}
				type = type.BaseType;
			}
			return false;
		}

		public static object InvokeMethod(object instance, string methodName, params object[] args)
		{
			if (instance == null)
			{
				return null;
			}
			Type type = instance.GetType();
			Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes;
			MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null);
			if (methodInfo == null)
			{
				methodInfo = type.GetMethod(methodName, AllBindingFlags);
			}
			if (methodInfo == null)
			{
				return null;
			}
			return methodInfo.Invoke(instance, args);
		}

		public static object InvokeStaticMethod(Type type, string methodName, params object[] args)
		{
			if (type == null)
			{
				return null;
			}
			Type[] array = args?.Select((object a) => a?.GetType() ?? typeof(object)).ToArray() ?? Type.EmptyTypes;
			MethodInfo methodInfo = AccessTools.Method(type, methodName, array, (Type[])null);
			if (methodInfo == null)
			{
				methodInfo = type.GetMethod(methodName, AllBindingFlags);
			}
			if (methodInfo == null)
			{
				return null;
			}
			return methodInfo.Invoke(null, args);
		}

		public static FieldInfo[] GetAllFields(Type type)
		{
			if (type == null)
			{
				return Array.Empty<FieldInfo>();
			}
			FieldInfo[] fields = type.GetFields(AllBindingFlags);
			IEnumerable<FieldInfo> second;
			if (!(type.BaseType != null) || !(type.BaseType != typeof(object)))
			{
				second = Enumerable.Empty<FieldInfo>();
			}
			else
			{
				IEnumerable<FieldInfo> allFields = GetAllFields(type.BaseType);
				second = allFields;
			}
			return fields.Concat(second).Distinct().ToArray();
		}

		public static PropertyInfo[] GetAllProperties(Type type)
		{
			if (type == null)
			{
				return Array.Empty<PropertyInfo>();
			}
			PropertyInfo[] properties = type.GetProperties(AllBindingFlags);
			IEnumerable<PropertyInfo> second;
			if (!(type.BaseType != null) || !(type.BaseType != typeof(object)))
			{
				second = Enumerable.Empty<PropertyInfo>();
			}
			else
			{
				IEnumerable<PropertyInfo> allProperties = GetAllProperties(type.BaseType);
				second = allProperties;
			}
			return (from p in properties.Concat(second)
				group p by p.Name into g
				select g.First()).ToArray();
		}

		public static T TryGetValue<T>(object instance, string memberName, T defaultValue = default(T))
		{
			try
			{
				object instanceValue = GetInstanceValue(instance, memberName);
				if (instanceValue is T result)
				{
					return result;
				}
				if (instanceValue != null && typeof(T).IsAssignableFrom(instanceValue.GetType()))
				{
					return (T)instanceValue;
				}
				return defaultValue;
			}
			catch
			{
				return defaultValue;
			}
		}
	}
}
namespace HavensRespec
{
	[BepInPlugin("com.azraelgodking.havensrespec", "Haven's Respec", "1.2.0")]
	public class Plugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		private RespecConfig _config;

		private SkillResetService _resetService;

		private CostService _costService;

		private RespecController _controller;

		public static ManualLogSource Log { get; private set; }

		public static Plugin Instance { get; private set; }

		public static bool IsDebugLoggingEnabled
		{
			get
			{
				Plugin instance = Instance;
				if (instance == null)
				{
					return false;
				}
				return (instance._config?.DebugLogging?.Value).GetValueOrDefault();
			}
		}

		private void Awake()
		{
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			try
			{
				ConfigFile val = ConfigFileHelper.CreateNamedConfig("com.azraelgodking.havensrespec", "HavensRespec.cfg", (Action<string>)Log.LogWarning);
				ConfigFileHelper.ReplacePluginConfig((BaseUnityPlugin)(object)this, val, (Action<string>)Log.LogWarning);
				_config = new RespecConfig(val);
				if (!_config.Enabled.Value)
				{
					Log.LogInfo((object)"Haven's Respec disabled in config.");
					return;
				}
				_resetService = new SkillResetService(Log, () => IsDebugLoggingEnabled);
				_costService = new CostService(Log, _config);
				_controller = new RespecController(Log, _config, _resetService, _costService);
				_controller.Install();
				_harmony = new Harmony("com.azraelgodking.havensrespec");
				_harmony.PatchAll(typeof(SkillsSetupProfessionPatch));
				try
				{
					VersionChecker.CheckForUpdate("com.azraelgodking.havensrespec", "1.2.0", Log);
				}
				catch (Exception ex)
				{
					Log.LogDebug((object)("[Respec] VersionChecker swallowed: " + ex.Message));
				}
				Log.LogInfo((object)"Haven's Respec v1.2.0 loaded.");
			}
			catch (Exception arg)
			{
				Log.LogError((object)string.Format("{0} Awake failed: {1}", "Haven's Respec", arg));
			}
		}

		private void OnDestroy()
		{
			try
			{
				_controller?.Uninstall();
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)("Haven's Respec OnDestroy swallowed: " + ex.Message));
				}
			}
		}

		private void Update()
		{
			//IL_001c: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_008e: Unknown result type (might be due to invalid IL or missing references)
			if (_controller == null || _config == null)
			{
				return;
			}
			KeyCode value = _config.ResetCurrentTabHotkey.Value;
			if ((int)value != 0 && Input.GetKeyDown(value))
			{
				ProfessionType? val = _controller.TryGetActiveProfessionTab();
				if (val.HasValue)
				{
					_controller.TryResetCurrentTab(val.Value, bypassConfirm: false);
				}
			}
			KeyCode value2 = _config.UndoHotkey.Value;
			if ((int)value2 != 0 && Input.GetKeyDown(value2))
			{
				ProfessionType? val2 = _controller.TryGetActiveProfessionTab();
				if (val2.HasValue)
				{
					_controller.TryUndoCurrentTab(val2.Value);
				}
			}
		}
	}
	internal static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.azraelgodking.havensrespec";

		public const string PLUGIN_NAME = "Haven's Respec";

		public const string PLUGIN_VERSION = "1.2.0";
	}
}
namespace HavensRespec.UI
{
	internal sealed class ConfirmResetDialog : MonoBehaviour
	{
		private sealed class ButtonHoverTint : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
		{
			public Image Target;

			public Color Normal;

			public Color Hover;

			public Color Pressed;

			private bool _isOver;

			public void OnPointerEnter(PointerEventData _)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				_isOver = true;
				Apply(Hover);
			}

			public void OnPointerExit(PointerEventData _)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				_isOver = false;
				Apply(Normal);
			}

			public void OnPointerDown(PointerEventData _)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Apply(Pressed);
			}

			public void OnPointerUp(PointerEventData _)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				Apply(_isOver ? Hover : Normal);
			}

			private void Apply(Color fill)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)Target == (Object)null))
				{
					((Graphic)Target).color = fill;
				}
			}
		}

		private TextMeshProUGUI _title;

		private TextMeshProUGUI _body;

		private Action _onConfirm;

		public static ConfirmResetDialog BuildUnder(Transform parent)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("HavensRespec_ConfirmDialog", new Type[1] { typeof(RectTransform) });
			RectTransform component = val.GetComponent<RectTransform>();
			((Transform)component).SetParent(parent, false);
			component.anchorMin = Vector2.zero;
			component.anchorMax = Vector2.one;
			component.offsetMin = Vector2.zero;
			component.offsetMax = Vector2.zero;
			((Transform)component).localScale = Vector3.one;
			Canvas val2 = ((Component)parent).GetComponent<Canvas>() ?? ((Component)parent).GetComponentInParent<Canvas>();
			Canvas val3 = val.AddComponent<Canvas>();
			if ((Object)(object)val2 != (Object)null)
			{
				val3.renderMode = val2.renderMode;
				val3.worldCamera = val2.worldCamera;
				val3.planeDistance = val2.planeDistance;
			}
			val3.overrideSorting = true;
			val3.sortingOrder = (((Object)(object)val2 != (Object)null) ? (val2.sortingOrder + 200) : 32000);
			val.AddComponent<GraphicRaycaster>();
			ConfirmResetDialog confirmResetDialog = val.AddComponent<ConfirmResetDialog>();
			confirmResetDialog.BuildHierarchy();
			confirmResetDialog.Hide();
			return confirmResetDialog;
		}

		public void Show(string title, string body, Action onConfirm)
		{
			((TMP_Text)_title).text = title ?? "Confirm reset?";
			((TMP_Text)_body).text = body ?? string.Empty;
			_onConfirm = onConfirm;
			((Component)this).gameObject.SetActive(true);
			((Component)this).transform.SetAsLastSibling();
		}

		public void Hide()
		{
			((Component)this).gameObject.SetActive(false);
			_onConfirm = null;
		}

		private void BuildHierarchy()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Expected O, but got Unknown
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Expected O, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: 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_0152: Expected O, but got Unknown
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: 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_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Expected O, but got Unknown
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_026a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: Expected O, but got Unknown
			//IL_031a: Unknown result type (might be due to invalid IL or missing references)
			//IL_032f: 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_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: 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_0415: Unknown result type (might be due to invalid IL or missing references)
			//IL_041a: Unknown result type (might be due to invalid IL or missing references)
			//IL_041f: Unknown result type (might be due to invalid IL or missing references)
			//IL_044a: 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: Unknown result type (might be due to invalid IL or missing references)
			//IL_0477: Unknown result type (might be due to invalid IL or missing references)
			//IL_047c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0481: 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)
			GameObject val = new GameObject("Scrim");
			val.transform.SetParent(((Component)this).transform, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			Image obj2 = val.AddComponent<Image>();
			obj2.sprite = RespecStyle.Solid();
			((Graphic)obj2).color = new Color(0f, 0f, 0f, 0.55f);
			((Graphic)obj2).raycastTarget = true;
			Button obj3 = val.AddComponent<Button>();
			((Selectable)obj3).transition = (Transition)0;
			((UnityEvent)obj3.onClick).AddListener(new UnityAction(Hide));
			GameObject val2 = new GameObject("Card");
			val2.transform.SetParent(((Component)this).transform, false);
			RectTransform obj4 = val2.AddComponent<RectTransform>();
			obj4.anchorMin = new Vector2(0.5f, 0.5f);
			obj4.anchorMax = new Vector2(0.5f, 0.5f);
			obj4.pivot = new Vector2(0.5f, 0.5f);
			obj4.sizeDelta = new Vector2(420f, 200f);
			Image obj5 = val2.AddComponent<Image>();
			obj5.sprite = RespecStyle.SolidRounded(RespecStyle.Wood, RespecStyle.WoodShadow, 28, 3, 8);
			obj5.type = (Type)1;
			((Graphic)obj5).raycastTarget = false;
			GameObject val3 = new GameObject("Fill");
			val3.transform.SetParent(val2.transform, false);
			RectTransform obj6 = val3.AddComponent<RectTransform>();
			obj6.anchorMin = Vector2.zero;
			obj6.anchorMax = Vector2.one;
			obj6.offsetMin = new Vector2(4f, 4f);
			obj6.offsetMax = new Vector2(-4f, -4f);
			Image obj7 = val3.AddComponent<Image>();
			obj7.sprite = RespecStyle.SolidRounded(new Color(0.08f, 0.06f, 0.05f, 0.95f), new Color(0.08f, 0.06f, 0.05f, 0.95f), 22, 0);
			obj7.type = (Type)1;
			((Graphic)obj7).raycastTarget = true;
			GameObject val4 = new GameObject("Title");
			val4.transform.SetParent(val3.transform, false);
			RectTransform obj8 = val4.AddComponent<RectTransform>();
			obj8.anchorMin = new Vector2(0f, 1f);
			obj8.anchorMax = new Vector2(1f, 1f);
			obj8.pivot = new Vector2(0.5f, 1f);
			obj8.sizeDelta = new Vector2(0f, 36f);
			obj8.anchoredPosition = new Vector2(0f, -12f);
			_title = val4.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_title).alignment = (TextAlignmentOptions)514;
			((TMP_Text)_title).fontSize = 20f;
			((TMP_Text)_title).fontStyle = (FontStyles)1;
			((Graphic)_title).color = RespecStyle.AccentGold;
			((TMP_Text)_title).text = "Confirm reset?";
			((Graphic)_title).raycastTarget = false;
			GameObject val5 = new GameObject("Body");
			val5.transform.SetParent(val3.transform, false);
			RectTransform obj9 = val5.AddComponent<RectTransform>();
			obj9.anchorMin = new Vector2(0f, 0f);
			obj9.anchorMax = new Vector2(1f, 1f);
			obj9.offsetMin = new Vector2(18f, 60f);
			obj9.offsetMax = new Vector2(-18f, -52f);
			_body = val5.AddComponent<TextMeshProUGUI>();
			((TMP_Text)_body).alignment = (TextAlignmentOptions)257;
			((TMP_Text)_body).fontSize = 15f;
			((Graphic)_body).color = new Color(0.95f, 0.92f, 0.83f, 1f);
			((TMP_Text)_body).text = string.Empty;
			((Graphic)_body).raycastTarget = false;
			BuildButton(val3.transform, "Cancel", new Vector2(-10f, 12f), new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(1f, 0f), RespecStyle.Neutral, RespecStyle.NeutralHover, RespecStyle.NeutralPressed, Hide);
			BuildButton(val3.transform, "Reset", new Vector2(-130f, 12f), new Vector2(1f, 0f), new Vector2(1f, 0f), new Vector2(1f, 0f), RespecStyle.Danger, RespecStyle.DangerHover, RespecStyle.DangerPressed, delegate
			{
				Action onConfirm = _onConfirm;
				Hide();
				onConfirm?.Invoke();
			});
		}

		private static void BuildButton(Transform parent, string label, Vector2 anchoredPos, Vector2 anchorMin, Vector2 anchorMax, Vector2 pivot, Color normal, Color hover, Color pressed, Action onClick)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: 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_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Btn_" + label);
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = anchorMin;
			obj.anchorMax = anchorMax;
			obj.pivot = pivot;
			obj.sizeDelta = new Vector2(110f, 36f);
			obj.anchoredPosition = anchoredPos;
			Image val2 = val.AddComponent<Image>();
			val2.sprite = RespecStyle.SolidRounded(Color.white, new Color(0f, 0f, 0f, 0.45f), 20, 1, 5);
			val2.type = (Type)1;
			((Graphic)val2).color = normal;
			((Graphic)val2).raycastTarget = true;
			Button obj2 = val.AddComponent<Button>();
			ColorBlock colors = ((Selectable)obj2).colors;
			((ColorBlock)(ref colors)).normalColor = Color.white;
			((ColorBlock)(ref colors)).highlightedColor = Color.white;
			((ColorBlock)(ref colors)).pressedColor = Color.white;
			((ColorBlock)(ref colors)).selectedColor = Color.white;
			((Selectable)obj2).colors = colors;
			((Selectable)obj2).transition = (Transition)0;
			((UnityEvent)obj2.onClick).AddListener((UnityAction)delegate
			{
				onClick?.Invoke();
			});
			ButtonHoverTint buttonHoverTint = val.AddComponent<ButtonHoverTint>();
			buttonHoverTint.Target = val2;
			buttonHoverTint.Normal = normal;
			buttonHoverTint.Hover = hover;
			buttonHoverTint.Pressed = pressed;
			GameObject val3 = new GameObject("Text");
			val3.transform.SetParent(val.transform, false);
			RectTransform obj3 = val3.AddComponent<RectTransform>();
			obj3.anchorMin = Vector2.zero;
			obj3.anchorMax = Vector2.one;
			obj3.offsetMin = Vector2.zero;
			obj3.offsetMax = Vector2.zero;
			TextMeshProUGUI obj4 = val3.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj4).alignment = (TextAlignmentOptions)514;
			((TMP_Text)obj4).fontSize = 16f;
			((TMP_Text)obj4).fontStyle = (FontStyles)1;
			((Graphic)obj4).color = Color.white;
			((TMP_Text)obj4).text = label;
			((Graphic)obj4).raycastTarget = false;
		}
	}
	internal sealed class RespecButtonInjector
	{
		private sealed class HoverTint : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
		{
			public Image Fill;

			public RectTransform Root;

			public Color Normal;

			public Color Hover;

			public Color Pressed;

			private const float HoverScale = 1.04f;

			private const float PressedScale = 0.97f;

			private bool _isOver;

			public void OnPointerEnter(PointerEventData _)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				_isOver = true;
				Apply(Hover, 1.04f);
			}

			public void OnPointerExit(PointerEventData _)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				_isOver = false;
				Apply(Normal, 1f);
			}

			public void OnPointerDown(PointerEventData _)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Apply(Pressed, 0.97f);
			}

			public void OnPointerUp(PointerEventData _)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				Apply(_isOver ? Hover : Normal, _isOver ? 1.04f : 1f);
			}

			private void Apply(Color fill, float scale)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0035: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)Fill != (Object)null)
				{
					((Graphic)Fill).color = fill;
				}
				if ((Object)(object)Root != (Object)null)
				{
					((Transform)Root).localScale = new Vector3(scale, scale, 1f);
				}
			}
		}

		private readonly ManualLogSource _log;

		private static FieldInfo _skillPointsTmpField;

		private const float ButtonWidth = 96f;

		private const float ButtonHeight = 26f;

		private const float FirstRowCenterOffset = 135f;

		private const float RowSpacing = 30f;

		private const float HorizontalOffset = -14f;

		private static Sprite _plaqueFillSprite;

		private static Sprite _plaqueBorderSprite;

		private static Sprite _plaqueShadowSprite;

		public GameObject ResetButton { get; private set; }

		public GameObject UndoButton { get; private set; }

		public event Action OnResetClicked;

		public event Action OnUndoClicked;

		public RespecButtonInjector(ManualLogSource log)
		{
			_log = log;
		}

		public bool TryAttach(SkillTree panel)
		{
			//IL_008e: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)panel == (Object)null)
			{
				return false;
			}
			try
			{
				if (_skillPointsTmpField == null)
				{
					_skillPointsTmpField = typeof(SkillTree).GetField("_skillPointsTMP", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
				object? obj = _skillPointsTmpField?.GetValue(panel);
				TextMeshProUGUI val = (TextMeshProUGUI)((obj is TextMeshProUGUI) ? obj : null);
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)"[Respec] _skillPointsTMP not found on SkillTree; cannot attach button.");
					}
					return false;
				}
				Transform parent = ((TMP_Text)val).transform.parent;
				RectTransform component = ((Component)val).GetComponent<RectTransform>();
				ResetButton = BuildButton(parent, component, 0, "Reset", RespecStyle.Danger, RespecStyle.DangerHover, RespecStyle.DangerPressed, delegate
				{
					this.OnResetClicked?.Invoke();
				});
				UndoButton = BuildButton(parent, component, 1, "Undo", RespecStyle.Neutral, RespecStyle.NeutralHover, RespecStyle.NeutralPressed, delegate
				{
					this.OnUndoClicked?.Invoke();
				});
				UndoButton.SetActive(false);
				return true;
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogError((object)$"[Respec] RespecButtonInjector.TryAttach failed: {arg}");
				}
				return false;
			}
		}

		public void SetUndoVisible(bool visible)
		{
			if ((Object)(object)UndoButton != (Object)null)
			{
				UndoButton.SetActive(visible);
			}
		}

		public void Destroy()
		{
			if ((Object)(object)ResetButton != (Object)null)
			{
				Object.Destroy((Object)(object)ResetButton);
			}
			if ((Object)(object)UndoButton != (Object)null)
			{
				Object.Destroy((Object)(object)UndoButton);
			}
			ResetButton = null;
			UndoButton = null;
		}

		private static void EnsureSprites()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_plaqueFillSprite == (Object)null)
			{
				_plaqueFillSprite = RespecStyle.SolidRounded(Color.white, new Color(0f, 0f, 0f, 0f), 32, 0, 8);
			}
			if ((Object)(object)_plaqueBorderSprite == (Object)null)
			{
				_plaqueBorderSprite = RespecStyle.SolidRounded(new Color(0f, 0f, 0f, 0f), RespecStyle.AccentGold, 32, 2, 8);
			}
			if ((Object)(object)_plaqueShadowSprite == (Object)null)
			{
				_plaqueShadowSprite = RespecStyle.SolidRounded(Color.white, new Color(0f, 0f, 0f, 0f), 32, 0, 9);
			}
		}

		private static GameObject BuildButton(Transform parent, RectTransform anchorRt, int rowIndex, string label, Color normal, Color hover, Color pressed, Action onClick)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Expected O, but got Unknown
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0250: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Expected O, but got Unknown
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Unknown result type (might be due to invalid IL or missing references)
			//IL_038c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0392: Unknown result type (might be due to invalid IL or missing references)
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_0399: Unknown result type (might be due to invalid IL or missing references)
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			EnsureSprites();
			GameObject val = new GameObject("HavensRespec_" + label + "Button");
			val.transform.SetParent(parent, false);
			RectTransform val2 = val.AddComponent<RectTransform>();
			Vector2 val3 = default(Vector2);
			((Vector2)(ref val3))..ctor(0.5f, 0.5f);
			val2.anchorMax = val3;
			val2.anchorMin = val3;
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.sizeDelta = new Vector2(96f, 26f);
			((Transform)val2).localScale = Vector3.one;
			float num = 0f - (135f + (float)rowIndex * 30f);
			((Transform)val2).localPosition = ((Transform)anchorRt).localPosition + new Vector3(-14f, num, 0f);
			Image val4 = val.AddComponent<Image>();
			((Graphic)val4).color = new Color(0f, 0f, 0f, 0f);
			((Graphic)val4).raycastTarget = true;
			Button obj = val.AddComponent<Button>();
			((Selectable)obj).transition = (Transition)0;
			((Selectable)obj).targetGraphic = (Graphic)(object)val4;
			((UnityEvent)obj.onClick).AddListener((UnityAction)delegate
			{
				onClick?.Invoke();
			});
			Image obj2 = AddChildImage(val.transform, "Shadow", _plaqueShadowSprite, new Color(0f, 0f, 0f, 0.45f));
			StretchFill(((Graphic)obj2).rectTransform, new Vector2(-1f, -3f), new Vector2(2f, 0f));
			((Graphic)obj2).raycastTarget = false;
			Image val5 = AddChildImage(val.transform, "Fill", _plaqueFillSprite, normal);
			StretchFill(((Graphic)val5).rectTransform, Vector2.zero, Vector2.zero);
			((Graphic)val5).raycastTarget = false;
			Image obj3 = AddChildImage(val.transform, "Gloss", _plaqueFillSprite, new Color(1f, 1f, 1f, 0.18f));
			RectTransform rectTransform = ((Graphic)obj3).rectTransform;
			rectTransform.anchorMin = new Vector2(0f, 0.5f);
			rectTransform.anchorMax = new Vector2(1f, 1f);
			rectTransform.offsetMin = new Vector2(3f, 0f);
			rectTransform.offsetMax = new Vector2(-3f, -2f);
			((Graphic)obj3).raycastTarget = false;
			Image obj4 = AddChildImage(val.transform, "Border", _plaqueBorderSprite, RespecStyle.AccentGold);
			StretchFill(((Graphic)obj4).rectTransform, Vector2.zero, Vector2.zero);
			((Graphic)obj4).raycastTarget = false;
			GameObject val6 = new GameObject("Label");
			val6.transform.SetParent(val.transform, false);
			RectTransform obj5 = val6.AddComponent<RectTransform>();
			obj5.anchorMin = Vector2.zero;
			obj5.anchorMax = Vector2.one;
			obj5.offsetMin = new Vector2(0f, 0f);
			obj5.offsetMax = new Vector2(0f, -1f);
			TextMeshProUGUI obj6 = val6.AddComponent<TextMeshProUGUI>();
			((TMP_Text)obj6).alignment = (TextAlignmentOptions)514;
			((TMP_Text)obj6).fontSize = 12f;
			((TMP_Text)obj6).fontStyle = (FontStyles)1;
			((TMP_Text)obj6).enableWordWrapping = false;
			((TMP_Text)obj6).overflowMode = (TextOverflowModes)0;
			((TMP_Text)obj6).characterSpacing = 8f;
			((Graphic)obj6).color = RespecStyle.Parchment;
			((TMP_Text)obj6).text = label.ToUpperInvariant();
			((Graphic)obj6).raycastTarget = false;
			Type type = Type.GetType("I2.Loc.Localize, I2Localization") ?? Type.GetType("I2.Loc.Localize, Assembly-CSharp");
			if (type != null)
			{
				Component component = val6.GetComponent(type);
				if ((Object)(object)component != (Object)null)
				{
					Object.Destroy((Object)(object)component);
				}
			}
			HoverTint hoverTint = val.AddComponent<HoverTint>();
			hoverTint.Fill = val5;
			hoverTint.Root = val2;
			hoverTint.Normal = normal;
			hoverTint.Hover = hover;
			hoverTint.Pressed = pressed;
			return val;
		}

		private static Image AddChildImage(Transform parent, string name, Sprite sprite, Color color)
		{
			//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_0013: 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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(parent, false);
			RectTransform obj = val.AddComponent<RectTransform>();
			obj.anchorMin = Vector2.zero;
			obj.anchorMax = Vector2.one;
			obj.offsetMin = Vector2.zero;
			obj.offsetMax = Vector2.zero;
			Image obj2 = val.AddComponent<Image>();
			obj2.sprite = sprite;
			obj2.type = (Type)1;
			((Graphic)obj2).color = color;
			return obj2;
		}

		private static void StretchFill(RectTransform rt, Vector2 offsetMin, Vector2 offsetMax)
		{
			//IL_0001: 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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			rt.anchorMin = Vector2.zero;
			rt.anchorMax = Vector2.one;
			rt.offsetMin = offsetMin;
			rt.offsetMax = offsetMax;
		}
	}
	internal sealed class RespecController
	{
		private sealed class HoverTint : MonoBehaviour, IPointerEnterHandler, IEventSystemHandler, IPointerExitHandler, IPointerDownHandler, IPointerUpHandler
		{
			public Image Target;

			public Color Normal;

			public Color Hover;

			public Color Pressed;

			private bool _isOver;

			public void OnPointerEnter(PointerEventData _)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				_isOver = true;
				Apply(Hover);
			}

			public void OnPointerExit(PointerEventData _)
			{
				//IL_0009: Unknown result type (might be due to invalid IL or missing references)
				_isOver = false;
				Apply(Normal);
			}

			public void OnPointerDown(PointerEventData _)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Apply(Pressed);
			}

			public void OnPointerUp(PointerEventData _)
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_000a: Unknown result type (might be due to invalid IL or missing references)
				Apply(_isOver ? Hover : Normal);
			}

			private void Apply(Color fill)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)Target == (Object)null))
				{
					((Graphic)Target).color = fill;
				}
			}
		}

		private readonly ManualLogSource _log;

		private readonly RespecConfig _config;

		private readonly SkillResetService _resetService;

		private readonly CostService _costService;

		private readonly Dictionary<ProfessionType, RespecButtonInjector> _injectors = new Dictionary<ProfessionType, RespecButtonInjector>();

		private Skills _activeSkills;

		private ConfirmResetDialog _dialog;

		private GameObject _resetAllButton;

		public RespecController(ManualLogSource log, RespecConfig config, SkillResetService resetService, CostService costService)
		{
			_log = log;
			_config = config;
			_resetService = resetService;
			_costService = costService;
		}

		public void Install()
		{
			SkillsSetupProfessionPatch.OnSetup = HandleSetupProfession;
		}

		public void Uninstall()
		{
			SkillsSetupProfessionPatch.OnSetup = null;
			foreach (RespecButtonInjector value in _injectors.Values)
			{
				value.Destroy();
			}
			_injectors.Clear();
			if ((Object)(object)_dialog != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)_dialog).gameObject);
				_dialog = null;
			}
			if ((Object)(object)_resetAllButton != (Object)null)
			{
				Object.Destroy((Object)(object)_resetAllButton);
				_resetAllButton = null;
			}
		}

		public void TryResetCurrentTab(ProfessionType profession, bool bypassConfirm)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_activeSkills == (Object)null))
			{
				BeginResetFlow(_activeSkills, profession, bypassConfirm);
			}
		}

		public void TryUndoCurrentTab(ProfessionType profession)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_activeSkills == (Object)null))
			{
				PerformUndo(_activeSkills, profession);
			}
		}

		public bool HasUndo(ProfessionType profession)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			return _resetService.HasUndo(profession);
		}

		public ProfessionType? TryGetActiveProfessionTab()
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_activeSkills == (Object)null)
			{
				return null;
			}
			try
			{
				ProfessionType[] orderedProfessions = ProfessionUiMap.OrderedProfessions;
				foreach (ProfessionType val in orderedProfessions)
				{
					string text = ProfessionUiMap.ResolvePanelFieldName(val);
					Component val2 = (Component)((text == null) ? null : /*isinst with value type is only supported in some contexts*/);
					if ((Object)(object)val2 != (Object)null && val2.gameObject.activeInHierarchy)
					{
						return val;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)("[Respec] TryGetActiveProfessionTab failed: " + ex.Message));
				}
			}
			return null;
		}

		private void HandleSetupProfession(Skills skills, ProfessionType profession, SkillTree panel)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			_activeSkills = skills;
			if (_injectors.TryGetValue(profession, out var value))
			{
				value.Destroy();
				_injectors.Remove(profession);
			}
			if (!_config.InjectButtons.Value)
			{
				return;
			}
			RespecButtonInjector respecButtonInjector = new RespecButtonInjector(_log);
			if (respecButtonInjector.TryAttach(panel))
			{
				respecButtonInjector.OnResetClicked += delegate
				{
					//IL_000d: Unknown result type (might be due to invalid IL or missing references)
					BeginResetFlow(skills, profession, _config.ShiftSkipsConfirmation.Value && IsShiftHeld());
				};
				respecButtonInjector.OnUndoClicked += delegate
				{
					//IL_000d: Unknown result type (might be due to invalid IL or missing references)
					PerformUndo(skills, profession);
				};
				respecButtonInjector.SetUndoVisible(_config.EnableUndo.Value && _resetService.HasUndo(profession));
				_injectors[profession] = respecButtonInjector;
				EnsureDialog((Component)(object)panel);
				EnsureResetAllButton(skills, panel);
			}
		}

		private void BeginResetFlow(Skills skills, ProfessionType profession, bool bypassConfirm)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			int estimatedPoints = ResolveEstimatedRefund(skills, profession);
			if (!_costService.CanAfford(estimatedPoints, out var balance, out var cost))
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)$"[Respec] Cannot afford reset of {profession}: cost={cost}, balance={balance}.");
				}
				return;
			}
			if (!_config.RequireConfirmation.Value || bypassConfirm)
			{
				PerformReset(skills, profession, estimatedPoints);
				return;
			}
			EnsureDialog((Component)(object)skills);
			if ((Object)(object)_dialog == (Object)null)
			{
				PerformReset(skills, profession, estimatedPoints);
				return;
			}
			string body = BuildConfirmBody(profession, estimatedPoints, cost);
			_dialog.Show($"Reset {profession}?", body, delegate
			{
				//IL_000d: Unknown result type (might be due to invalid IL or missing references)
				PerformReset(skills, profession, estimatedPoints);
			});
		}

		private bool PerformReset(Skills skills, ProfessionType profession, int estimatedPoints)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: 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_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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)
			if (!_resetService.ResetProfession(skills, profession, out var pointsRefunded))
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)$"[Respec] Reset of {profession} failed.");
				}
				return false;
			}
			int num = _costService.CalculateCost(pointsRefunded);
			if (num > 0)
			{
				ResetSnapshot restoredSnapshot;
				if (!_costService.CanAfford(pointsRefunded, out var balance, out var _))
				{
					ManualLogSource log2 = _log;
					if (log2 != null)
					{
						log2.LogWarning((object)$"[Respec] Actual reset cost check failed for {profession}: cost={num}, balance={balance}. Rolling back reset.");
					}
					_resetService.UndoLastReset(skills, profession, out restoredSnapshot);
					return false;
				}
				if (!_costService.TryDeduct(pointsRefunded))
				{
					ManualLogSource log3 = _log;
					if (log3 != null)
					{
						log3.LogWarning((object)$"[Respec] Cost deduction failed for {profession} (actual cost {num}). Rolling back reset.");
					}
					_resetService.UndoLastReset(skills, profession, out restoredSnapshot);
					return false;
				}
				_resetService.AttachUndoCharge(profession, num, _config.CostMode.Value);
			}
			if (_injectors.TryGetValue(profession, out var value))
			{
				value.SetUndoVisible(_config.EnableUndo.Value && _resetService.HasUndo(profession));
			}
			if (pointsRefunded == 0)
			{
				ManualLogSource log4 = _log;
				if (log4 != null)
				{
					log4.LogInfo((object)$"[Respec] {profession} reset complete; nothing was allocated (0 active node(s) — skill-points counter unchanged).");
				}
			}
			else
			{
				ManualLogSource log5 = _log;
				if (log5 != null)
				{
					log5.LogInfo((object)$"[Respec] {profession} reset complete; refunded {pointsRefunded} point(s){((estimatedPoints != pointsRefunded) ? $" (pre-flight estimate was {estimatedPoints})" : string.Empty)}.");
				}
			}
			return true;
		}

		private void PerformUndo(Skills skills, ProfessionType profession)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			if (!_resetService.UndoLastReset(skills, profession, out var restoredSnapshot))
			{
				return;
			}
			if (restoredSnapshot != null && restoredSnapshot.ChargedCost > 0 && restoredSnapshot.ChargedCostMode != 0)
			{
				if (!_costService.TryRefund(restoredSnapshot.ChargedCost, restoredSnapshot.ChargedCostMode))
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)$"[Respec] Undo restored {profession} nodes but failed to refund {restoredSnapshot.ChargedCost} ({restoredSnapshot.ChargedCostMode}).");
					}
				}
				else
				{
					ManualLogSource log2 = _log;
					if (log2 != null)
					{
						log2.LogInfo((object)$"[Respec] Undo refunded {restoredSnapshot.ChargedCost} ({restoredSnapshot.ChargedCostMode}) for {profession}.");
					}
				}
			}
			if (_injectors.TryGetValue(profession, out var value))
			{
				value.SetUndoVisible(_config.EnableUndo.Value && _resetService.HasUndo(profession));
			}
		}

		private void BeginResetAllFlow(Skills skills, bool bypassConfirm)
		{
			//IL_0029: 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)
			int num = 0;
			int num2 = 0;
			ProfessionType[] orderedProfessions = ProfessionUiMap.OrderedProfessions;
			int i;
			for (i = 0; i < orderedProfessions.Length; i++)
			{
				ProfessionType profession = orderedProfessions[i];
				int num3 = ResolveEstimatedRefund(skills, profession);
				num += num3;
				num2 += _costService.CalculateCost(num3);
			}
			if (num2 > 0 && !_costService.CanAfford(num, out var balance, out i))
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)$"[Respec] Cannot afford Reset All preflight: total cost={num2}, balance={balance}.");
				}
				return;
			}
			if (!_config.RequireConfirmation.Value || bypassConfirm)
			{
				PerformResetAll(skills);
				return;
			}
			EnsureDialog((Component)(object)skills);
			if ((Object)(object)_dialog == (Object)null)
			{
				PerformResetAll(skills);
				return;
			}
			string body = $"This will reset all profession trees.\n\nEstimated total refund: {num} point(s)." + ((num2 > 0) ? ("\nEstimated total cost: " + _costService.CostLabel(num) + " (final charge uses actual refunded points).") : "\nCost: Free") + (_config.EnableUndo.Value ? "\n\nUndo remains per-profession and will refund each profession's charged cost." : "\n\nUndo is disabled in config — this cannot be reversed.");
			_dialog.Show("Reset all professions?", body, delegate
			{
				PerformResetAll(skills);
			});
		}

		private void PerformResetAll(Skills skills)
		{
			//IL_0015: 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_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			List<ProfessionType> list = new List<ProfessionType>();
			ProfessionType[] orderedProfessions = ProfessionUiMap.OrderedProfessions;
			foreach (ProfessionType val in orderedProfessions)
			{
				if (!PerformReset(skills, val, ResolveEstimatedRefund(skills, val)))
				{
					for (int num2 = list.Count - 1; num2 >= 0; num2--)
					{
						PerformUndo(skills, list[num2]);
					}
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)$"[Respec] Reset All aborted on {val}; rolled back {list.Count} profession(s).");
					}
					return;
				}
				list.Add(val);
				num++;
			}
			ManualLogSource log2 = _log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"[Respec] Reset All complete: processed {num} profession(s).");
			}
		}

		private void EnsureDialog(Component sceneAnchor)
		{
			if (!((Object)(object)_dialog != (Object)null) && !((Object)(object)sceneAnchor == (Object)null))
			{
				Canvas val = sceneAnchor.GetComponentInParent<Canvas>();
				if ((Object)(object)val == (Object)null)
				{
					val = Object.FindObjectOfType<Canvas>();
				}
				if (!((Object)(object)val == (Object)null))
				{
					_dialog = ConfirmResetDialog.BuildUnder(((Component)val).transform);
				}
			}
		}

		private string BuildConfirmBody(ProfessionType profession, int pointsRefunded, int cost)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			string obj = ((pointsRefunded > 0) ? string.Format("This will clear every allocated node in {0}. Estimated refund: {1} skill point{2}.", profession, pointsRefunded, (pointsRefunded == 1) ? string.Empty : "s") : $"This will clear every allocated node in {profession}.");
			string text = ((cost > 0) ? ("\n\nEstimated cost: " + _costService.CostLabel(pointsRefunded) + " (final charge uses actual refunded points).") : string.Empty);
			string text2 = (_config.EnableUndo.Value ? "\n\nYou can press \"Undo\" to restore your previous allocation (and any charged cost) until the game closes." : "\n\nUndo is disabled in config — this cannot be reversed.");
			return obj + text + text2;
		}

		private static bool IsShiftHeld()
		{
			if (!Input.GetKey((KeyCode)304))
			{
				return Input.GetKey((KeyCode)303);
			}
			return true;
		}

		private int ResolveEstimatedRefund(Skills skills, ProfessionType profession)
		{
			//IL_0007: 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)
			if (_resetService.TryEstimateExactRefund(skills, profession, out var refundedPoints))
			{
				return Mathf.Max(0, refundedPoints);
			}
			return Mathf.Max(0, _resetService.GetAllocatedPoints(profession));
		}

		private void EnsureResetAllButton(Skills skills, SkillTree panel)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Expected O, but got Unknown
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: 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_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			if (!_config.EnableResetAll.Value || !_config.InjectButtons.Value || (Object)(object)panel == (Object)null)
			{
				if ((Object)(object)_resetAllButton != (Object)null)
				{
					Object.Destroy((Object)(object)_resetAllButton);
					_resetAllButton = null;
				}
			}
			else
			{
				if ((Object)(object)_resetAllButton != (Object)null)
				{
					return;
				}
				Transform parent = ((Component)panel).transform.parent;
				if (!((Object)(object)parent == (Object)null))
				{
					GameObject val = new GameObject("HavensRespec_ResetAllButton");
					val.transform.SetParent(parent, false);
					RectTransform obj = val.AddComponent<RectTransform>();
					Vector2 val2 = default(Vector2);
					((Vector2)(ref val2))..ctor(0.5f, 1f);
					obj.anchorMax = val2;
					obj.anchorMin = val2;
					obj.pivot = new Vector2(0.5f, 1f);
					obj.sizeDelta = new Vector2(130f, 28f);
					obj.anchoredPosition = new Vector2(0f, -8f);
					((Transform)obj).localScale = Vector3.one;
					Image val3 = val.AddComponent<Image>();
					val3.sprite = RespecStyle.SolidRounded(Color.white, new Color(0f, 0f, 0f, 0f), 32, 0, 8);
					val3.type = (Type)1;
					((Graphic)val3).color = RespecStyle.Danger;
					Button obj2 = val.AddComponent<Button>();
					((Selectable)obj2).transition = (Transition)0;
					((UnityEvent)obj2.onClick).AddListener((UnityAction)delegate
					{
						BeginResetAllFlow(skills, _config.ShiftSkipsConfirmation.Value && IsShiftHeld());
					});
					HoverTint hoverTint = val.AddComponent<HoverTint>();
					hoverTint.Target = val3;
					hoverTint.Normal = RespecStyle.Danger;
					hoverTint.Hover = RespecStyle.DangerHover;
					hoverTint.Pressed = RespecStyle.DangerPressed;
					GameObject val4 = new GameObject("Label");
					val4.transform.SetParent(val.transform, false);
					RectTransform obj3 = val4.AddComponent<RectTransform>();
					obj3.anchorMin = Vector2.zero;
					obj3.anchorMax = Vector2.one;
					obj3.offsetMin = Vector2.zero;
					obj3.offsetMax = Vector2.zero;
					TextMeshProUGUI obj4 = val4.AddComponent<TextMeshProUGUI>();
					((TMP_Text)obj4).alignment = (TextAlignmentOptions)514;
					((TMP_Text)obj4).fontSize = 12f;
					((TMP_Text)obj4).fontStyle = (FontStyles)1;
					((TMP_Text)obj4).enableWordWrapping = false;
					((Graphic)obj4).color = RespecStyle.Parchment;
					((TMP_Text)obj4).text = "RESET ALL";
					((Graphic)obj4).raycastTarget = false;
					_resetAllButton = val;
				}
			}
		}
	}
	internal static class RespecStyle
	{
		public static readonly Color Ink = new Color(0.09f, 0.07f, 0.05f, 1f);

		public static readonly Color Parchment = new Color(0.96f, 0.9f, 0.75f, 1f);

		public static readonly Color Wood = new Color(0.38f, 0.25f, 0.16f, 1f);

		public static readonly Color WoodShadow = new Color(0.22f, 0.14f, 0.09f, 1f);

		public static readonly Color AccentGold = new Color(0.95f, 0.76f, 0.32f, 1f);

		public static readonly Color Danger = new Color(0.78f, 0.28f, 0.22f, 1f);

		public static readonly Color DangerHover = new Color(0.92f, 0.36f, 0.28f, 1f);

		public static readonly Color DangerPressed = new Color(0.62f, 0.2f, 0.16f, 1f);

		public static readonly Color Neutral = new Color(0.5f, 0.38f, 0.22f, 1f);

		public static readonly Color NeutralHover = new Color(0.64f, 0.49f, 0.28f, 1f);

		public static readonly Color NeutralPressed = new Color(0.36f, 0.27f, 0.16f, 1f);

		private static readonly Dictionary<string, Sprite> _cache = new Dictionary<string, Sprite>();

		public static Sprite SolidRounded(Color fill, Color border, int size = 24, int borderThickness = 2, int cornerRadius = 6)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			string key = $"rounded|{fill}|{border}|{size}|{borderThickness}|{cornerRadius}";
			if (_cache.TryGetValue(key, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Texture2D val = new Texture2D(size, size, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1
			};
			Color val2 = default(Color);
			((Color)(ref val2))..ctor(0f, 0f, 0f, 0f);
			for (int i = 0; i < size; i++)
			{
				for (int j = 0; j < size; j++)
				{
					bool num = IsInsideRoundedRect(j, i, size, size, cornerRadius);
					bool flag = num && !IsInsideRoundedRect(j, i, size, size, cornerRadius, borderThickness);
					if (!num)
					{
						val.SetPixel(j, i, val2);
					}
					else if (flag)
					{
						val.SetPixel(j, i, border);
					}
					else
					{
						val.SetPixel(j, i, fill);
					}
				}
			}
			val.Apply();
			Sprite val3 = Sprite.Create(val, new Rect(0f, 0f, (float)size, (float)size), new Vector2(0.5f, 0.5f), 100f, 0u, (SpriteMeshType)0, new Vector4((float)cornerRadius, (float)cornerRadius, (float)cornerRadius, (float)cornerRadius));
			_cache[key] = val3;
			return val3;
		}

		public static Sprite Solid()
		{
			//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_002f: Expected O, but got Unknown
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if (_cache.TryGetValue("solid", out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Texture2D val = new Texture2D(4, 4, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)1
			};
			Color[] array = (Color[])(object)new Color[16];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = Color.white;
			}
			val.SetPixels(array);
			val.Apply();
			Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, 4f, 4f), new Vector2(0.5f, 0.5f));
			_cache["solid"] = val2;
			return val2;
		}

		private static bool IsInsideRoundedRect(int x, int y, int w, int h, int r, int inset = 0)
		{
			r = Mathf.Max(0, r - inset);
			int num = w - 1 - inset;
			int num2 = h - 1 - inset;
			if (x < inset || x > num || y < inset || y > num2)
			{
				return false;
			}
			int num3 = Mathf.Clamp(x, inset + r, num - r);
			int num4 = Mathf.Clamp(y, inset + r, num2 - r);
			int num5 = x - num3;
			int num6 = y - num4;
			return num5 * num5 + num6 * num6 <= r * r;
		}
	}
}
namespace HavensRespec.Services
{
	internal sealed class CostService
	{
		private readonly ManualLogSource _log;

		private readonly RespecConfig _config;

		private static MethodInfo _addMoneyMethod;

		private static MethodInfo _addTicketsMethod;

		private static PropertyInfo _gameSaveCoinsProp;

		private static PropertyInfo _gameSaveTicketsProp;

		private static bool _reflectionInitialized;

		public CostService(ManualLogSource log, RespecConfig config)
		{
			_log = log;
			_config = config;
		}

		public int CalculateCost(int pointsRefunded)
		{
			if (pointsRefunded <= 0)
			{
				return 0;
			}
			return _config.CostMode.Value switch
			{
				RespecCostMode.Gold => pointsRefunded * Math.Max(0, _config.GoldPerPoint.Value), 
				RespecCostMode.Gems => pointsRefunded * Math.Max(0, _config.GemsPerPoint.Value), 
				_ => 0, 
			};
		}

		public string CostLabel(int pointsRefunded)
		{
			int num = CalculateCost(pointsRefunded);
			if (num <= 0)
			{
				return "Free";
			}
			return _config.CostMode.Value switch
			{
				RespecCostMode.Gold => $"{num:N0} gold", 
				RespecCostMode.Gems => $"{num:N0} tickets", 
				_ => "Free", 
			};
		}

		public bool CanAfford(int pointsRefunded, out int balance, out int cost)
		{
			cost = CalculateCost(pointsRefunded);
			balance = ReadBalance();
			if (cost <= 0)
			{
				return true;
			}
			if (balance < 0)
			{
				return false;
			}
			return balance >= cost;
		}

		public bool TryDeduct(int pointsRefunded)
		{
			int num = CalculateCost(pointsRefunded);
			if (num <= 0)
			{
				return true;
			}
			EnsureReflection();
			try
			{
				Player instance = Player.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)"[Respec] TryDeduct: Player.Instance was null.");
					}
					return false;
				}
				switch (_config.CostMode.Value)
				{
				case RespecCostMode.Gold:
					if (_addMoneyMethod == null)
					{
						return false;
					}
					_addMoneyMethod.Invoke(instance, new object[4]
					{
						-num,
						true,
						false,
						true
					});
					return true;
				case RespecCostMode.Gems:
					if (_addTicketsMethod == null)
					{
						return false;
					}
					_addTicketsMethod.Invoke(instance, new object[1] { -num });
					return true;
				default:
					return true;
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogError((object)$"[Respec] TryDeduct failed: {arg}");
				}
				return false;
			}
		}

		public bool TryRefund(int amount, RespecCostMode mode)
		{
			if (amount <= 0 || mode == RespecCostMode.None)
			{
				return true;
			}
			EnsureReflection();
			try
			{
				Player instance = Player.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					ManualLogSource log = _log;
					if (log != null)
					{
						log.LogWarning((object)"[Respec] TryRefund: Player.Instance was null.");
					}
					return false;
				}
				switch (mode)
				{
				case RespecCostMode.Gold:
					if (_addMoneyMethod == null)
					{
						return false;
					}
					_addMoneyMethod.Invoke(instance, new object[4] { amount, true, false, true });
					return true;
				case RespecCostMode.Gems:
					if (_addTicketsMethod == null)
					{
						return false;
					}
					_addTicketsMethod.Invoke(instance, new object[1] { amount });
					return true;
				default:
					return true;
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogError((object)$"[Respec] TryRefund failed: {arg}");
				}
				return false;
			}
		}

		private int ReadBalance()
		{
			EnsureReflection();
			try
			{
				switch (_config.CostMode.Value)
				{
				case RespecCostMode.Gold:
					if (_gameSaveCoinsProp != null)
					{
						return Convert.ToInt32(_gameSaveCoinsProp.GetValue(null));
					}
					break;
				case RespecCostMode.Gems:
					if (_gameSaveTicketsProp != null)
					{
						return Convert.ToInt32(_gameSaveTicketsProp.GetValue(null));
					}
					break;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)("[Respec] ReadBalance swallowed: " + ex.Message));
				}
			}
			return -1;
		}

		private static void EnsureReflection()
		{
			if (!_reflectionInitialized)
			{
				_addMoneyMethod = AccessTools.Method(typeof(Player), "AddMoney", new Type[4]
				{
					typeof(int),
					typeof(bool),
					typeof(bool),
					typeof(bool)
				}, (Type[])null);
				_addTicketsMethod = AccessTools.Method(typeof(Player), "AddTickets", new Type[1] { typeof(int) }, (Type[])null);
				_gameSaveCoinsProp = typeof(GameSave).GetProperty("Coins", BindingFlags.Static | BindingFlags.Public);
				_gameSaveTicketsProp = typeof(GameSave).GetProperty("Tickets", BindingFlags.Static | BindingFlags.Public);
				_reflectionInitialized = true;
			}
		}
	}
	internal static class ProfessionUiMap
	{
		public static readonly ProfessionType[] OrderedProfessions;

		public static string ResolvePanelFieldName(ProfessionType profession)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Expected I4, but got Unknown
			return (int)profession switch
			{
				0 => "_combatPanel", 
				1 => "_farmingPanel", 
				3 => "_miningPanel", 
				4 => "_artisanryPanel", 
				2 => "_fishingPanel", 
				_ => null, 
			};
		}

		static ProfessionUiMap()
		{
			ProfessionType[] array = new ProfessionType[5];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			OrderedProfessions = (ProfessionType[])(object)array;
		}
	}
	internal sealed class ResetSnapshot
	{
		public ProfessionType Profession { get; }

		public IReadOnlyDictionary<int, int> Nodes { get; }

		public int SkillPointsUsed { get; }

		public int NumActiveNodes { get; }

		public int ChargedCost { get; }

		public RespecCostMode ChargedCostMode { get; }

		public ResetSnapshot(ProfessionType profession, IReadOnlyDictionary<int, int> nodes, int skillPointsUsed, int numActiveNodes, int chargedCost = 0, RespecCostMode chargedCostMode = RespecCostMode.None)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			Profession = profession;
			Nodes = nodes;
			SkillPointsUsed = skillPointsUsed;
			NumActiveNodes = numActiveNodes;
			ChargedCost = chargedCost;
			ChargedCostMode = chargedCostMode;
		}
	}
	internal sealed class SkillResetService
	{
		private readonly ManualLogSource _log;

		private readonly Func<bool> _isDebug;

		private readonly Dictionary<ProfessionType, Stack<ResetSnapshot>> _undoByProfession = new Dictionary<ProfessionType, Stack<ResetSnapshot>>();

		private static MethodInfo _updateProfessionMethod;

		private static FieldInfo _professionNodeDictionaryField;

		private static FieldInfo _skillPointsUsedField;

		private static FieldInfo _numActiveNodesField;

		private static bool _reflectionInitialized;

		public SkillResetService(ManualLogSource log, Func<bool> isDebug)
		{
			_log = log;
			_isDebug = isDebug;
		}

		public bool HasUndo(ProfessionType profession)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if (_undoByProfession.TryGetValue(profession, out var value))
			{
				return value.Count > 0;
			}
			return false;
		}

		public void AttachUndoCharge(ProfessionType profession, int chargedCost, RespecCostMode chargedCostMode)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (_undoByProfession.TryGetValue(profession, out var value) && value.Count != 0 && chargedCost > 0 && chargedCostMode != 0)
			{
				ResetSnapshot resetSnapshot = value.Pop();
				value.Push(new ResetSnapshot(resetSnapshot.Profession, resetSnapshot.Nodes, resetSnapshot.SkillPointsUsed, resetSnapshot.NumActiveNodes, chargedCost, chargedCostMode));
			}
		}

		public bool ResetProfession(Skills skills, ProfessionType profession, out int pointsRefunded)
		{
			//IL_0292: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0257: Unknown result type (might be due to invalid IL or missing references)
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			pointsRefunded = 0;
			if ((Object)(object)skills == (Object)null)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogWarning((object)"[Respec] ResetProfession: Skills instance was null.");
				}
				return false;
			}
			EnsureReflection();
			try
			{
				Profession val = ResolveProfessionData(profession);
				if (val == null)
				{
					ManualLogSource log2 = _log;
					if (log2 != null)
					{
						log2.LogWarning((object)$"[Respec] ResetProfession({profession}): no Profession data on the current save.");
					}
					return false;
				}
				ResetSnapshot resetSnapshot = CaptureSnapshot(profession, val);
				int num = 0;
				int num2 = 0;
				int num3 = 0;
				try
				{
					if (_professionNodeDictionaryField?.GetValue(skills) is IDictionary dictionary && dictionary.Contains(profession) && dictionary[profession] is SkillNode[] array)
					{
						SkillNode[] array2 = array;
						foreach (SkillNode val2 in array2)
						{
							if (!((Object)(object)val2 == (Object)null))
							{
								num++;
								if (val2.active)
								{
									num3 += Mathf.Max(1, val2.NodeAmount);
									num2++;
									val2.SetActive(false, false, 0);
								}
							}
						}
					}
				}
				catch (Exception ex)
				{
					ManualLogSource log3 = _log;
					if (log3 != null)
					{
						log3.LogDebug((object)("[Respec] Node-walk deactivation swallowed: " + ex.Message));
					}
				}
				List<int> list = new List<int>(val.Nodes.Keys);
				foreach (int item in list)
				{
					val.Nodes[item] = 0;
				}
				foreach (int item2 in CollectNodeHashes(skills, profession, val))
				{
					val.Nodes[item2] = 0;
				}
				SetSkillPointsUsed(profession, 0);
				SetNumActiveNodes(profession, 0);
				pointsRefunded = ((num > 0) ? num3 : resetSnapshot.SkillPointsUsed);
				TryRefreshProfessionPanel(skills, profession);
				PushUndoSnapshot(profession, resetSnapshot);
				if (num > 0)
				{
					ManualLogSource log4 = _log;
					if (log4 != null)
					{
						log4.LogInfo((object)$"[Respec] Reset {profession}: refunded {pointsRefunded} point(s) from {num2}/{num} active node(s); cleared {list.Count} save key(s).");
					}
				}
				else
				{
					ManualLogSource log5 = _log;
					if (log5 != null)
					{
						log5.LogInfo((object)$"[Respec] Reset {profession}: refunded {pointsRefunded} point(s) (fallback from skillPointsUsed snapshot); cleared {list.Count} save key(s). _professionNodeDictionary was unreadable.");
					}
				}
				return true;
			}
			catch (Exception arg)
			{
				ManualLogSource log6 = _log;
				if (log6 != null)
				{
					log6.LogError((object)$"[Respec] ResetProfession({profession}) failed: {arg}");
				}
				return false;
			}
		}

		public bool UndoLastReset(Skills skills, ProfessionType profession, out ResetSnapshot restoredSnapshot)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			restoredSnapshot = null;
			if (!_undoByProfession.TryGetValue(profession, out var value) || value.Count == 0)
			{
				return false;
			}
			ResetSnapshot resetSnapshot = value.Pop();
			EnsureReflection();
			try
			{
				Profession val = ResolveProfessionData(profession);
				if (val == null)
				{
					return false;
				}
				val.Nodes.Clear();
				foreach (KeyValuePair<int, int> node in resetSnapshot.Nodes)
				{
					val.Nodes[node.Key] = node.Value;
				}
				SetSkillPointsUsed(profession, resetSnapshot.SkillPointsUsed);
				SetNumActiveNodes(profession, resetSnapshot.NumActiveNodes);
				TryRefreshProfessionPanel(skills, profession);
				if (value.Count == 0)
				{
					_undoByProfession.Remove(profession);
				}
				restoredSnapshot = resetSnapshot;
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogInfo((object)$"[Respec] Undo {profession}: restored {resetSnapshot.Nodes.Count} node(s), {resetSnapshot.SkillPointsUsed} point(s) used.");
				}
				return true;
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = _log;
				if (log2 != null)
				{
					log2.LogError((object)$"[Respec] UndoLastReset({profession}) failed: {arg}");
				}
				return false;
			}
		}

		public int GetAllocatedPoints(ProfessionType profession)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (_skillPointsUsedField?.GetValue(null) is Dictionary<ProfessionType, int> dictionary && dictionary.TryGetValue(profession, out var value))
				{
					return value;
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)$"[Respec] GetAllocatedPoints({profession}) failed: {ex.Message}");
				}
			}
			return 0;
		}

		public bool TryEstimateExactRefund(Skills skills, ProfessionType profession, out int refundedPoints)
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			refundedPoints = 0;
			if ((Object)(object)skills == (Object)null)
			{
				return false;
			}
			EnsureReflection();
			try
			{
				int num = 0;
				if (_professionNodeDictionaryField?.GetValue(skills) is IDictionary dictionary && dictionary.Contains(profession) && dictionary[profession] is SkillNode[] array)
				{
					SkillNode[] array2 = array;
					foreach (SkillNode val in array2)
					{
						if (!((Object)(object)val == (Object)null))
						{
							num++;
							if (val.active)
							{
								refundedPoints += Mathf.Max(1, val.NodeAmount);
							}
						}
					}
				}
				if (num > 0)
				{
					return true;
				}
				refundedPoints = GetAllocatedPoints(profession);
				return refundedPoints > 0;
			}
			catch (Exception ex)
			{
				ManualLogSource log = _log;
				if (log != null)
				{
					log.LogDebug((object)$"[Respec] TryEstimateExactRefund({profession}) fallback: {ex.Message}");
				}
				refundedPoints = GetAllocatedPoints(profession);
				return refundedPoints >= 0;
			}
		}

		private Profession ResolveProfessionData(ProfessionType profession)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			GameSave instance = SingletonBehaviour<GameSave>.Instance;
			if ((Object)(object)instance == (Object)null || instance.CurrentSave == null || instance.CurrentSave.characterData == null)
			{
				return null;
			}
			if (!instance.CurrentSave.characterData.Professions.TryGetValue(profession, out var value))
			{
				return null;
			}
			return value;
		}

		private ResetSnapshot CaptureSnapshot(ProfessionType profession, Profession profData)
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<int, int> dictionary = new Dictionary<int, int>(profData.Nodes.Count);
			foreach (KeyValuePair<int, int> node in profData.Nodes)
			{
				dictionary[node.Key] = node.Value;
			}
			int skillPointsUsed = 0;
			int numActiveNodes = 0;
			try
			{
				if (_skillPointsUsedField?.GetValue(null) is Dictionary<ProfessionType, int> dictionary2 && dictionary2.TryGetValue(profession, out var value))
				{
					skillPointsUsed = value;
				}
				if (_numActiveNodesField?.GetValue(null) is Dictionary<ProfessionType, int> dictionary3 && dictionary3.TryGetValue(profession, out var value2))
				{
					n