Decompiled source of HavensBirthright v1.3.0

HavensBirthright.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HavensBirthright.Abilities;
using HavensBirthright.Patches;
using Microsoft.CodeAnalysis;
using SunhavenMods.Shared;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using Wish;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("HavensBirthright")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+78f0933428dca634840abb5a205f83634a2fe530")]
[assembly: AssemblyProduct("HavensBirthright")]
[assembly: AssemblyTitle("HavensBirthright")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.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 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; }
		}

		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;
						LogWarning(result.ErrorMessage);
						onComplete?.Invoke(result);
						Object.Destroy((Object)(object)((Component)this).gameObject);
						yield break;
					}
					try
					{
						string json = www.downloadHandler.text;
						string modPattern = "\"" + Regex.Escape(pluginGuid) + "\"\\s*:\\s*\\{([^}]+)\\}";
						Match modMatch = Regex.Match(json, modPattern, RegexOptions.Singleline);
						if (!modMatch.Success)
						{
							result.Success = false;
							result.ErrorMessage = "Mod '" + pluginGuid + "' not found in versions.json";
							LogWarning(result.ErrorMessage);
							onComplete?.Invoke(result);
							Object.Destroy((Object)(object)((Component)this).gameObject);
							yield break;
						}
						string modJson = modMatch.Groups[1].Value;
						result.LatestVersion = ExtractJsonString(modJson, "version");
						result.ModName = ExtractJsonString(modJson, "name");
						result.NexusUrl = ExtractJsonString(modJson, "nexus");
						result.Changelog = ExtractJsonString(modJson, "changelog");
						if (string.IsNullOrEmpty(result.LatestVersion))
						{
							result.Success = false;
							result.ErrorMessage = "Could not parse version from response";
							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;
						LogError(result.ErrorMessage);
					}
				}
				finally
				{
					((IDisposable)www)?.Dispose();
				}
				onComplete?.Invoke(result);
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}

			private string ExtractJsonString(string json, string key)
			{
				string pattern = "\"" + key + "\"\\s*:\\s*(?:\"([^\"]*)\"|null)";
				Match match = Regex.Match(json, pattern);
				return match.Success ? match.Groups[1].Value : null;
			}
		}

		private const string VersionsUrl = "https://azraelgodking.github.io/SunhavenMod/versions.json";

		private static ManualLogSource _logger;

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

		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));
			}
		}
	}
	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)
					{
						Type type3 = type2.MakeGenericType(type);
						object obj = type3.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 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;
			}
			foreach (string text in namespaces)
			{
				type = AccessTools.TypeByName(text + "." + 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" };
			string[] array2 = array;
			foreach (string memberName in array2)
			{
				object staticValue = GetStaticValue(type, memberName);
				if (staticValue != null)
				{
					return staticValue;
				}
			}
			return null;
		}

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

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

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

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

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

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

		public static T TryGetValue<T>(object instance, string memberName, T defaultValue = default(T))
		{
			try
			{
				object instanceValue = GetInstanceValue(instance, memberName);
				if (instanceValue is T result)
				{
					return result;
				}
				if (instanceValue != null && typeof(T).IsAssignableFrom(instanceValue.GetType()))
				{
					return (T)instanceValue;
				}
				return defaultValue;
			}
			catch
			{
				return defaultValue;
			}
		}
	}
	public abstract class PersistentRunnerBase : MonoBehaviour
	{
		private bool _wasInGame = false;

		private float _lastHeartbeat = 0f;

		private string _lastSceneName = "";

		private float _lastSceneCheckTime = 0f;

		private const float SceneCheckInterval = 0.5f;

		protected virtual float HeartbeatInterval => 0f;

		protected virtual string RunnerName => ((object)this).GetType().Name;

		protected virtual void OnUpdate()
		{
		}

		protected virtual void OnMenuTransition()
		{
		}

		protected virtual void OnGameTransition()
		{
		}

		protected virtual void Log(string message)
		{
		}

		protected virtual void LogWarning(string message)
		{
		}

		public static T CreateRunner<T>() where T : PersistentRunnerBase
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			GameObject val = new GameObject("[" + typeof(T).Name + "]");
			((Object)val).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)val);
			return val.AddComponent<T>();
		}

		protected virtual void Awake()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			_lastSceneName = ((Scene)(ref activeScene)).name;
			_wasInGame = !SceneHelpers.IsMenuScene(_lastSceneName);
			Log("[" + RunnerName + "] Initialized in scene: " + _lastSceneName);
		}

		protected virtual void Update()
		{
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			if (HeartbeatInterval > 0f)
			{
				_lastHeartbeat += Time.deltaTime;
				if (_lastHeartbeat >= HeartbeatInterval)
				{
					_lastHeartbeat = 0f;
					Log("[" + RunnerName + "] Heartbeat - still alive");
				}
			}
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime - _lastSceneCheckTime >= 0.5f)
			{
				_lastSceneCheckTime = unscaledTime;
				Scene activeScene = SceneManager.GetActiveScene();
				string name = ((Scene)(ref activeScene)).name;
				if (name != _lastSceneName)
				{
					_lastSceneName = name;
					HandleSceneChange(name);
				}
			}
			try
			{
				OnUpdate();
			}
			catch (Exception ex)
			{
				LogWarning("[" + RunnerName + "] Error in OnUpdate: " + ex.Message);
			}
		}

		private void HandleSceneChange(string sceneName)
		{
			bool flag = SceneHelpers.IsMenuScene(sceneName);
			if (_wasInGame && flag)
			{
				Log("[" + RunnerName + "] Menu transition detected");
				try
				{
					OnMenuTransition();
				}
				catch (Exception ex)
				{
					LogWarning("[" + RunnerName + "] Error in OnMenuTransition: " + ex.Message);
				}
			}
			else if (!_wasInGame && !flag)
			{
				Log("[" + RunnerName + "] Game transition detected");
				try
				{
					OnGameTransition();
				}
				catch (Exception ex2)
				{
					LogWarning("[" + RunnerName + "] Error in OnGameTransition: " + ex2.Message);
				}
			}
			_wasInGame = !flag;
		}

		protected virtual void OnDestroy()
		{
			LogWarning("[" + RunnerName + "] OnDestroy called - this should NOT happen!");
		}
	}
	public static class SceneHelpers
	{
		private static readonly string[] MenuScenePatterns = new string[3] { "menu", "title", "bootstrap" };

		private static readonly string[] ExactMenuScenes = new string[2] { "MainMenu", "Bootstrap" };

		public static bool IsMenuScene(string sceneName)
		{
			if (string.IsNullOrEmpty(sceneName))
			{
				return true;
			}
			string[] exactMenuScenes = ExactMenuScenes;
			foreach (string text in exactMenuScenes)
			{
				if (sceneName == text)
				{
					return true;
				}
			}
			string text2 = sceneName.ToLowerInvariant();
			string[] menuScenePatterns = MenuScenePatterns;
			foreach (string value in menuScenePatterns)
			{
				if (text2.Contains(value))
				{
					return true;
				}
			}
			return false;
		}

		public static bool IsCurrentSceneMenu()
		{
			//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)
			Scene activeScene = SceneManager.GetActiveScene();
			return IsMenuScene(((Scene)(ref activeScene)).name);
		}

		public static bool IsInGame()
		{
			return !IsCurrentSceneMenu();
		}

		public static string GetCurrentSceneName()
		{
			//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)
			Scene activeScene = SceneManager.GetActiveScene();
			return ((Scene)(ref activeScene)).name;
		}

		public static bool IsMainMenu()
		{
			//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)
			Scene activeScene = SceneManager.GetActiveScene();
			return ((Scene)(ref activeScene)).name == "MainMenu";
		}
	}
}
namespace HavensBirthright
{
	public class BirthrightRunner : PersistentRunnerBase
	{
		private float _outdoorTime = 0f;

		private float _lastTidalBlessingCheck = 0f;

		private float _lastInfernalForgeCheck = 0f;

		private float _lastFontOfLightCheck = 0f;

		private bool _apiCacheInitialized = false;

		private Type _cropType;

		private Type _dayCycleType;

		private Type _tileManagerType;

		private object _tileManagerInstance;

		private MethodInfo _isWateredMethod;

		private MethodInfo _waterTileMethod;

		private MethodInfo _isHoedOrWateredMethod;

		private FieldInfo _farmingDataField;

		private FieldInfo _farmingTileMapField;

		private MethodInfo _worldToCellMethod;

		private object _gridInstance;

		private MethodInfo _gridCellToWorldMethod;

		private bool _tidalBlessingDiagLogged = false;

		private MethodInfo _cachedGetAmountMethod;

		private MethodInfo _cachedRemoveItemMethod;

		private bool _inventoryMethodsCached = false;

		private static float _cachedHPRatio = 1f;

		private static string _cachedSeason = null;

		private static bool _cachedIsDaytime = true;

		private static bool _cachedIsInMine = false;

		private static float _cachedQuickLearnerBonus = 0f;

		private static bool _cacheValid = false;

		protected override string RunnerName => "BirthrightRunner";

		public static float CachedHPRatio => _cachedHPRatio;

		public static string CachedSeason => _cachedSeason;

		public static bool CachedIsDaytime => _cachedIsDaytime;

		public static bool CachedIsInMine => _cachedIsInMine;

		public static float CachedQuickLearnerBonus => _cachedQuickLearnerBonus;

		public static bool IsCacheValid => _cacheValid;

		protected override void OnUpdate()
		{
			//IL_0019: 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 (!SceneHelpers.IsInGame())
			{
				return;
			}
			CheckAbilityToggleHotkey();
			if ((int)Plugin.StaticReloadConfigKey != 0 && Input.GetKeyDown(Plugin.StaticReloadConfigKey))
			{
				Plugin.Instance?.ReloadConfig();
			}
			if (!RacialConfig.EnableRacialBonuses.Value)
			{
				return;
			}
			RacialBonusManager racialBonusManager = Plugin.GetRacialBonusManager();
			if (racialBonusManager == null)
			{
				return;
			}
			Race? playerRace = racialBonusManager.GetPlayerRace();
			if (!playerRace.HasValue)
			{
				PlayerPatches.RetryRaceDetection();
				playerRace = racialBonusManager.GetPlayerRace();
				if (!playerRace.HasValue)
				{
					return;
				}
			}
			if (!_apiCacheInitialized)
			{
				InitializeApiCache();
				_apiCacheInitialized = true;
			}
			UpdateStatCache(playerRace.Value);
			if (AbilityConfig.EnableActiveAbilities.Value)
			{
				if (playerRace.Value == Race.AmariBird && AbilityConfig.EnableTailwind.Value)
				{
					UpdateTailwind();
				}
				if (playerRace.Value == Race.WaterElemental && AbilityConfig.EnableTidalBlessing.Value)
				{
					UpdateTidalBlessing();
				}
				if ((playerRace.Value == Race.FireElemental || playerRace.Value == Race.Elemental) && AbilityConfig.EnableInfernalForge.Value)
				{
					UpdateInfernalForge();
				}
				if (playerRace.Value == Race.Angel && AbilityConfig.EnableFontOfLight.Value)
				{
					UpdateFontOfLight();
				}
			}
		}

		protected override void OnMenuTransition()
		{
			ActiveAbilityManager.ResetAll();
			_outdoorTime = 0f;
			_lastInfernalForgeCheck = 0f;
			_lastFontOfLightCheck = 0f;
			_apiCacheInitialized = false;
			_inventoryMethodsCached = false;
			_cachedGetAmountMethod = null;
			_cachedRemoveItemMethod = null;
			_tileManagerInstance = null;
			_worldToCellMethod = null;
			_gridInstance = null;
			_gridCellToWorldMethod = null;
			ResetStatCache();
			AbilityPatches.ResetNotificationCache();
			AbilityPatches.ResetReflectionCache();
			Plugin.GetRacialBonusManager()?.ClearPlayerRace();
			PlayerPatches.ResetRaceDetection();
		}

		protected override void OnGameTransition()
		{
			_outdoorTime = 0f;
			_lastInfernalForgeCheck = 0f;
			_apiCacheInitialized = false;
			_inventoryMethodsCached = false;
			_cachedGetAmountMethod = null;
			_cachedRemoveItemMethod = null;
			_tileManagerInstance = null;
			_worldToCellMethod = null;
			_gridInstance = null;
			_gridCellToWorldMethod = null;
			ResetStatCache();
			AbilityPatches.ResetNotificationCache();
			AbilityPatches.ResetReflectionCache();
		}

		private static void ResetStatCache()
		{
			_cacheValid = false;
			_cachedHPRatio = 1f;
			_cachedSeason = null;
			_cachedIsDaytime = true;
			_cachedIsInMine = false;
			_cachedQuickLearnerBonus = 0f;
		}

		protected override void Log(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogInfo((object)message);
			}
		}

		protected override void LogWarning(string message)
		{
			ManualLogSource log = Plugin.Log;
			if (log != null)
			{
				log.LogWarning((object)message);
			}
		}

		private void InitializeApiCache()
		{
			try
			{
				_cropType = ReflectionHelper.FindWishType("Crop");
				_dayCycleType = ReflectionHelper.FindWishType("DayCycle");
				if (_cropType != null)
				{
					Plugin.Log.LogInfo((object)"[BirthrightRunner] Found Crop type for Tidal Blessing");
				}
				if (_dayCycleType != null)
				{
					Plugin.Log.LogInfo((object)"[BirthrightRunner] Found DayCycle type for synergies");
				}
				CacheTileManager();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[BirthrightRunner] API cache init failed: " + ex.Message));
			}
		}

		private void CacheTileManager()
		{
			try
			{
				_tileManagerType = ReflectionHelper.FindWishType("TileManager");
				if (_tileManagerType == null)
				{
					Plugin.Log.LogWarning((object)"[BirthrightRunner] TileManager type not found — Tidal Blessing will use fallback");
					return;
				}
				_isWateredMethod = _tileManagerType.GetMethod("IsWatered", new Type[1] { typeof(Vector2Int) });
				_waterTileMethod = _tileManagerType.GetMethod("Water", new Type[2]
				{
					typeof(Vector2Int),
					typeof(short)
				});
				_isHoedOrWateredMethod = _tileManagerType.GetMethod("IsHoedOrWatered", new Type[1] { typeof(Vector2Int) });
				_farmingDataField = _tileManagerType.GetField("farmingData", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				_farmingTileMapField = _tileManagerType.GetField("farmingTileMap", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Plugin.Log.LogInfo((object)("[BirthrightRunner] TileManager cached — " + $"IsWatered:{_isWateredMethod != null}, Water:{_waterTileMethod != null}, " + $"IsHoedOrWatered:{_isHoedOrWateredMethod != null}, " + $"farmingData:{_farmingDataField != null}, farmingTileMap:{_farmingTileMapField != null}"));
				TryGetTileManagerInstance();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[BirthrightRunner] TileManager cache failed: " + ex.Message));
			}
		}

		private void TryGetTileManagerInstance()
		{
			if (_tileManagerInstance != null)
			{
				object tileManagerInstance = _tileManagerInstance;
				Object val = (Object)((tileManagerInstance is Object) ? tileManagerInstance : null);
				if (val == null || !(val == (Object)null))
				{
					return;
				}
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[BirthrightRunner] TileManager instance was stale (destroyed Unity object) — clearing");
				}
				_tileManagerInstance = null;
			}
			if (_tileManagerType == null)
			{
				return;
			}
			try
			{
				_tileManagerInstance = ReflectionHelper.GetSingletonInstance(_tileManagerType);
				if (_tileManagerInstance != null)
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogInfo((object)"[BirthrightRunner] TileManager instance acquired");
					}
					if (_worldToCellMethod == null && _farmingTileMapField != null)
					{
						try
						{
							object value = _farmingTileMapField.GetValue(_tileManagerInstance);
							if (value != null)
							{
								_worldToCellMethod = value.GetType().GetMethod("WorldToCell", new Type[1] { typeof(Vector3) });
								ManualLogSource log3 = Plugin.Log;
								if (log3 != null)
								{
									log3.LogInfo((object)("[BirthrightRunner] WorldToCell method: " + ((_worldToCellMethod != null) ? "found" : "NOT found")));
								}
							}
							else
							{
								ManualLogSource log4 = Plugin.Log;
								if (log4 != null)
								{
									log4.LogInfo((object)"[BirthrightRunner] farmingTileMap is null on TileManager instance");
								}
							}
						}
						catch (Exception ex)
						{
							ManualLogSource log5 = Plugin.Log;
							if (log5 != null)
							{
								log5.LogWarning((object)("[BirthrightRunner] Failed to cache WorldToCell: " + ex.Message));
							}
						}
					}
					if (_gridInstance != null || !(_farmingTileMapField != null))
					{
						return;
					}
					try
					{
						object value2 = _farmingTileMapField.GetValue(_tileManagerInstance);
						if (value2 == null)
						{
							return;
						}
						_gridInstance = value2.GetType().GetProperty("layoutGrid")?.GetValue(value2);
						if (_gridInstance != null)
						{
							_gridCellToWorldMethod = _gridInstance.GetType().GetMethod("CellToWorld", new Type[1] { typeof(Vector3Int) });
							ManualLogSource log6 = Plugin.Log;
							if (log6 != null)
							{
								log6.LogInfo((object)("[BirthrightRunner] Grid cached — CellToWorld: " + ((_gridCellToWorldMethod != null) ? "found" : "NOT found")));
							}
						}
						else
						{
							ManualLogSource log7 = Plugin.Log;
							if (log7 != null)
							{
								log7.LogInfo((object)"[BirthrightRunner] layoutGrid is null — distance filtering will be disabled");
							}
						}
						return;
					}
					catch (Exception ex2)
					{
						ManualLogSource log8 = Plugin.Log;
						if (log8 != null)
						{
							log8.LogWarning((object)("[BirthrightRunner] Failed to cache Grid: " + ex2.Message));
						}
						return;
					}
				}
				ManualLogSource log9 = Plugin.Log;
				if (log9 != null)
				{
					log9.LogInfo((object)"[BirthrightRunner] TileManager instance NOT available (singleton returned null)");
				}
			}
			catch (Exception ex3)
			{
				ManualLogSource log10 = Plugin.Log;
				if (log10 != null)
				{
					log10.LogWarning((object)("[BirthrightRunner] TileManager instance lookup failed: " + ex3.Message));
				}
			}
		}

		private void UpdateStatCache(Race race)
		{
			try
			{
				_cachedIsInMine = IsInMine();
				if (_dayCycleType != null)
				{
					object singletonInstance = ReflectionHelper.GetSingletonInstance(_dayCycleType);
					if (singletonInstance != null)
					{
						float num = ReflectionHelper.TryGetValue(singletonInstance, "Hour", -1f);
						if (num < 0f)
						{
							num = ReflectionHelper.TryGetValue(singletonInstance, "CurrentHour", -1f);
						}
						if (num < 0f)
						{
							num = ReflectionHelper.TryGetValue(singletonInstance, "currentHour", -1f);
						}
						if (num < 0f)
						{
							int num2 = ReflectionHelper.TryGetValue(singletonInstance, "Hour", -1);
							if (num2 >= 0)
							{
								num = num2;
							}
						}
						_cachedIsDaytime = num < 0f || (num >= 6f && num < 18f);
						object instanceValue = ReflectionHelper.GetInstanceValue(singletonInstance, "Season");
						if (instanceValue == null)
						{
							instanceValue = ReflectionHelper.GetInstanceValue(singletonInstance, "season");
						}
						if (instanceValue == null)
						{
							instanceValue = ReflectionHelper.GetInstanceValue(singletonInstance, "CurrentSeason");
						}
						_cachedSeason = instanceValue?.ToString();
					}
				}
				Player instance = Player.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					float maxHealth = instance.MaxHealth;
					if (maxHealth > 0f)
					{
						float num3 = ReflectionHelper.TryGetValue(instance, "health", maxHealth);
						_cachedHPRatio = num3 / maxHealth;
					}
					else
					{
						_cachedHPRatio = 1f;
					}
					if (race == Race.Human && AbilityConfig.EnableQuickLearner != null && AbilityConfig.EnableQuickLearner.Value)
					{
						_cachedQuickLearnerBonus = CalculateQuickLearnerBonus(instance);
					}
					else
					{
						_cachedQuickLearnerBonus = 0f;
					}
				}
				_cacheValid = true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("[BirthrightRunner] Cache update error: " + ex.Message));
				_cacheValid = true;
			}
		}

		private static float CalculateQuickLearnerBonus(Player player)
		{
			try
			{
				int value = AbilityConfig.QuickLearnerSkillThreshold.Value;
				int num = 0;
				float num2 = ReflectionHelper.TryGetValue(player, "FarmingSkillLevel", 0f);
				float num3 = ReflectionHelper.TryGetValue(player, "MiningSkillLevel", 0f);
				float num4 = ReflectionHelper.TryGetValue(player, "FishingSkillLevel", 0f);
				float num5 = ReflectionHelper.TryGetValue(player, "ExplorationSkillLevel", 0f);
				if (num2 >= (float)value)
				{
					num++;
				}
				if (num3 >= (float)value)
				{
					num++;
				}
				if (num4 >= (float)value)
				{
					num++;
				}
				if (num5 >= (float)value)
				{
					num++;
				}
				try
				{
					object instanceValue = ReflectionHelper.GetInstanceValue(player, "Professions");
					if (instanceValue != null)
					{
						Type type = ReflectionHelper.FindWishType("ProfessionType");
						if (type != null)
						{
							object key = Enum.Parse(type, "Crafting");
							if (instanceValue is IDictionary dictionary && dictionary.Contains(key))
							{
								object instance = dictionary[key];
								int num6 = ReflectionHelper.TryGetValue(instance, "level", 0);
								if (num6 >= value)
								{
									num++;
								}
							}
						}
					}
				}
				catch
				{
				}
				float num7 = (float)num * AbilityConfig.QuickLearnerBonusPerSkill.Value;
				return Mathf.Min(num7, AbilityConfig.QuickLearnerMaxBonus.Value);
			}
			catch
			{
				return 0f;
			}
		}

		private void UpdateTailwind()
		{
			string text = SceneHelpers.GetCurrentSceneName().ToLowerInvariant();
			if (text.Contains("house") || text.Contains("inn") || text.Contains("shop") || text.Contains("cave") || text.Contains("mine") || text.Contains("dungeon") || text.Contains("interior"))
			{
				_outdoorTime = 0f;
				ActiveAbilityManager.SetBonusValue("TailwindOutdoor", 0f);
				return;
			}
			_outdoorTime += Time.deltaTime;
			float num = _outdoorTime / 60f;
			float value = Mathf.Min(num * AbilityConfig.TailwindBonusPerMinute.Value, AbilityConfig.TailwindMaxBonus.Value);
			ActiveAbilityManager.SetBonusValue("TailwindOutdoor", value);
		}

		private void UpdateTidalBlessing()
		{
			//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b7: Unknown result type (might be due to invalid IL or missing references)
			if (!ActiveAbilityManager.IsRuntimeEnabled("TidalBlessing"))
			{
				_tidalBlessingDiagLogged = false;
			}
			else
			{
				if (Time.time - _lastTidalBlessingCheck < AbilityConfig.TidalBlessingCooldown.Value)
				{
					return;
				}
				_lastTidalBlessingCheck = Time.time;
				if (ActiveAbilityManager.IsOnCooldown("TidalBlessing"))
				{
					return;
				}
				try
				{
					Player instance = Player.Instance;
					if ((Object)(object)instance == (Object)null)
					{
						return;
					}
					float maxHealth = instance.MaxHealth;
					float num = ReflectionHelper.TryGetValue(instance, "health", -1f);
					if (num < 0f)
					{
						num = ReflectionHelper.TryGetValue(instance, "Health", maxHealth);
					}
					float num2 = num / maxHealth * 100f;
					if (!_tidalBlessingDiagLogged)
					{
						_tidalBlessingDiagLogged = true;
						ManualLogSource log = Plugin.Log;
						if (log != null)
						{
							log.LogInfo((object)"[TidalBlessing] === DIAGNOSTIC ===");
						}
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogInfo((object)$"[TidalBlessing]   HP: {num:F0}/{maxHealth:F0} ({num2:F0}%), threshold: {AbilityConfig.TidalBlessingHPThreshold.Value}%");
						}
						ManualLogSource log3 = Plugin.Log;
						if (log3 != null)
						{
							log3.LogInfo((object)("[TidalBlessing]   TileManager: type=" + ((_tileManagerType != null) ? "ok" : "NULL") + ", instance=" + ((_tileManagerInstance != null) ? "ok" : "NULL")));
						}
						ManualLogSource log4 = Plugin.Log;
						if (log4 != null)
						{
							log4.LogInfo((object)("[TidalBlessing]   Methods: Water=" + ((_waterTileMethod != null) ? "ok" : "NULL") + ", farmingData field=" + ((_farmingDataField != null) ? "ok" : "NULL")));
						}
						ManualLogSource log5 = Plugin.Log;
						if (log5 != null)
						{
							log5.LogInfo((object)$"[TidalBlessing]   Cooldown: {AbilityConfig.TidalBlessingCooldown.Value}s, HP cost: {AbilityConfig.TidalBlessingHPCostPercent.Value}%");
						}
						if (_farmingDataField != null && _tileManagerInstance != null)
						{
							try
							{
								if (_farmingDataField.GetValue(_tileManagerInstance) is IDictionary dictionary)
								{
									int num3 = 0;
									int num4 = 0;
									foreach (DictionaryEntry item in dictionary)
									{
										string text = item.Value.ToString();
										if (text == "Hoed" || text == "2")
										{
											num3++;
										}
										else if (text == "Watered" || text == "3")
										{
											num4++;
										}
									}
									ManualLogSource log6 = Plugin.Log;
									if (log6 != null)
									{
										log6.LogInfo((object)$"[TidalBlessing]   farmingData: {dictionary.Count} total, {num3} hoed, {num4} watered");
									}
								}
							}
							catch (Exception ex)
							{
								ManualLogSource log7 = Plugin.Log;
								if (log7 != null)
								{
									log7.LogDebug((object)("[BirthrightRunner] TidalBlessing diagnostic: " + ex.Message));
								}
							}
						}
						ManualLogSource log8 = Plugin.Log;
						if (log8 != null)
						{
							log8.LogInfo((object)("[TidalBlessing]   Scene: " + SceneHelpers.GetCurrentSceneName()));
						}
						ManualLogSource log9 = Plugin.Log;
						if (log9 != null)
						{
							log9.LogInfo((object)$"[TidalBlessing]   Player world pos: ({((Component)instance).transform.position.x:F1}, {((Component)instance).transform.position.y:F1})");
						}
						ManualLogSource log10 = Plugin.Log;
						if (log10 != null)
						{
							log10.LogInfo((object)("[TidalBlessing]   Grid distance filter: " + ((_gridCellToWorldMethod != null && _gridInstance != null) ? "enabled (radius=1)" : "disabled (no Grid)")));
						}
						ManualLogSource log11 = Plugin.Log;
						if (log11 != null)
						{
							log11.LogInfo((object)"[TidalBlessing] === END DIAGNOSTIC ===");
						}
					}
					if (num2 <= AbilityConfig.TidalBlessingHPThreshold.Value)
					{
						return;
					}
					if (_tileManagerType != null && _isWateredMethod != null && _waterTileMethod != null)
					{
						UpdateTidalBlessingViaTileManager(instance, maxHealth, num);
						return;
					}
					ManualLogSource log12 = Plugin.Log;
					if (log12 != null)
					{
						log12.LogInfo((object)"[TidalBlessing] Using fallback Crop approach (TileManager methods not available)");
					}
					UpdateTidalBlessingViaCropObjects(instance, maxHealth, num);
				}
				catch (Exception ex2)
				{
					Plugin.Log.LogWarning((object)("[TidalBlessing] Error: " + ex2.Message));
					ManualLogSource log13 = Plugin.Log;
					if (log13 != null)
					{
						log13.LogWarning((object)("[TidalBlessing] Stack: " + ex2.StackTrace));
					}
				}
			}
		}

		private void UpdateTidalBlessingViaTileManager(Player player, float maxHP, float currentHP)
		{
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_031c: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0233: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: 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_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			object tileManagerInstance = _tileManagerInstance;
			Object val = (Object)((tileManagerInstance is Object) ? tileManagerInstance : null);
			if (val != null && val == (Object)null)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"[TidalBlessing] TileManager instance was stale — re-acquiring");
				}
				_tileManagerInstance = null;
			}
			if (_tileManagerInstance == null)
			{
				TryGetTileManagerInstance();
			}
			if (_tileManagerInstance == null)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)"[TidalBlessing] TileManager instance not available — skipping this cycle");
				}
			}
			else
			{
				if (_farmingDataField == null || !(_farmingDataField.GetValue(_tileManagerInstance) is IDictionary dictionary) || dictionary.Count == 0)
				{
					return;
				}
				float num = maxHP * (AbilityConfig.TidalBlessingHPCostPercent.Value / 100f);
				Scene activeScene = SceneManager.GetActiveScene();
				short num2 = (short)((Scene)(ref activeScene)).buildIndex;
				int num3 = 0;
				int num4 = 0;
				List<Vector2Int> list = new List<Vector2Int>();
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(((Component)player).transform.position.x, ((Component)player).transform.position.y);
				int num5 = 1;
				bool flag = _gridCellToWorldMethod != null && _gridInstance != null;
				foreach (DictionaryEntry item2 in dictionary)
				{
					string text = item2.Value.ToString();
					if (!(text == "Hoed") && !(text == "2"))
					{
						continue;
					}
					Vector2Int item = (Vector2Int)item2.Key;
					if (flag)
					{
						try
						{
							Vector3 val3 = (Vector3)_gridCellToWorldMethod.Invoke(_gridInstance, new object[1] { (object)new Vector3Int(((Vector2Int)(ref item)).x, ((Vector2Int)(ref item)).y, 0) });
							float num6 = Vector2.Distance(val2, new Vector2(val3.x, val3.y));
							if (num6 <= (float)num5)
							{
								list.Add(item);
							}
						}
						catch
						{
							list.Add(item);
						}
					}
					else
					{
						list.Add(item);
					}
				}
				num4 = list.Count;
				bool flag2 = default(bool);
				foreach (Vector2Int item3 in list)
				{
					float num7 = (currentHP - num * (float)(num3 + 1)) / maxHP * 100f;
					if (num7 <= AbilityConfig.TidalBlessingHPThreshold.Value)
					{
						break;
					}
					try
					{
						object obj2 = _waterTileMethod.Invoke(_tileManagerInstance, new object[2] { item3, num2 });
						int num8;
						if (obj2 is bool)
						{
							flag2 = (bool)obj2;
							num8 = 1;
						}
						else
						{
							num8 = 0;
						}
						if (((uint)num8 & (flag2 ? 1u : 0u)) != 0)
						{
							num3++;
						}
					}
					catch (Exception ex)
					{
						ManualLogSource log3 = Plugin.Log;
						if (log3 != null)
						{
							log3.LogWarning((object)$"[TidalBlessing] Water({item3}) error: {ex.Message}");
						}
					}
				}
				if (num3 > 0)
				{
					float num9 = num * (float)num3;
					float num10 = currentHP - num9;
					if (num10 < 1f)
					{
						num10 = 1f;
					}
					ReflectionHelper.SetInstanceValue(player, "Health", num10);
					ManualLogSource log4 = Plugin.Log;
					if (log4 != null)
					{
						log4.LogInfo((object)$"[TidalBlessing] Watered {num3}/{num4} hoed tiles via farmingData scan");
					}
				}
				else if (num4 > 0)
				{
					ManualLogSource log5 = Plugin.Log;
					if (log5 != null)
					{
						log5.LogInfo((object)$"[TidalBlessing] Found {num4} hoed tiles but Water() returned false for all");
					}
				}
				else
				{
					ManualLogSource log6 = Plugin.Log;
					if (log6 != null)
					{
						log6.LogInfo((object)"[TidalBlessing] No hoed tiles in farmingData");
					}
				}
			}
		}

		private void UpdateTidalBlessingViaCropObjects(Player player, float maxHP, float currentHP)
		{
			//IL_0035: 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_00ce: 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)
			//IL_00da: 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_00f7: 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)
			if (_cropType == null)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)"[TidalBlessing] No Crop type and no TileManager — cannot water");
				}
				return;
			}
			Vector3 position = ((Component)player).transform.position;
			int num = 1;
			int num2 = 0;
			Object[] array = Object.FindObjectsOfType(_cropType);
			ManualLogSource log2 = Plugin.Log;
			if (log2 != null)
			{
				log2.LogInfo((object)$"[TidalBlessing] Fallback mode: FindObjectsOfType found {((array != null) ? array.Length : 0)} Crop objects");
			}
			if (array == null || array.Length == 0)
			{
				return;
			}
			float num3 = maxHP * (AbilityConfig.TidalBlessingHPCostPercent.Value / 100f);
			Object[] array2 = array;
			foreach (Object val in array2)
			{
				Component val2 = (Component)(object)((val is Component) ? val : null);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				float num4 = Vector2.Distance(new Vector2(position.x, position.y), new Vector2(val2.transform.position.x, val2.transform.position.y));
				if (num4 > (float)num)
				{
					continue;
				}
				object instanceValue = ReflectionHelper.GetInstanceValue(val, "data");
				if (instanceValue == null || !ReflectionHelper.TryGetValue(instanceValue, "watered", defaultValue: false))
				{
					float num5 = (currentHP - num3 * (float)(num2 + 1)) / maxHP * 100f;
					if (num5 <= AbilityConfig.TidalBlessingHPThreshold.Value)
					{
						break;
					}
					Type type = ((object)val).GetType();
					MethodInfo method = type.GetMethod("Water", BindingFlags.Instance | BindingFlags.Public);
					if (method != null)
					{
						method.Invoke(val, null);
						num2++;
					}
					else if (instanceValue != null)
					{
						ReflectionHelper.SetInstanceValue(instanceValue, "watered", true);
						num2++;
					}
				}
			}
			if (num2 > 0)
			{
				float num6 = num3 * (float)num2;
				float num7 = currentHP - num6;
				if (num7 < 1f)
				{
					num7 = 1f;
				}
				ReflectionHelper.SetInstanceValue(player, "Health", num7);
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)$"[TidalBlessing] Watered {num2} crops via fallback Crop.Water()");
				}
			}
		}

		private void UpdateInfernalForge()
		{
			if (!ActiveAbilityManager.IsRuntimeEnabled("InfernalForge"))
			{
				return;
			}
			float num = AbilityConfig.InfernalForgeScanInterval?.Value ?? 2f;
			if (Time.time - _lastInfernalForgeCheck < num)
			{
				return;
			}
			_lastInfernalForgeCheck = Time.time;
			try
			{
				Player instance = Player.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				object instanceValue = ReflectionHelper.GetInstanceValue(instance, "Inventory");
				if (instanceValue == null)
				{
					instanceValue = ReflectionHelper.GetInstanceValue(instance, "inventory");
				}
				if (instanceValue == null)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogInfo((object)"[InfernalForge] Could not find player inventory");
					}
					return;
				}
				if (!_inventoryMethodsCached)
				{
					CacheInventoryMethods(instanceValue);
					_inventoryMethodsCached = true;
				}
				float maxMana = instance.MaxMana;
				float num2 = ReflectionHelper.TryGetValue(instance, "Mana", 0f);
				float num3 = num2 / maxMana * 100f;
				if (num3 <= AbilityConfig.InfernalForgeManaThreshold.Value)
				{
					return;
				}
				int num4 = AbilityConfig.InfernalForgeOrePerBar.Value;
				if (num4 < 1)
				{
					num4 = 3;
				}
				MethodInfo addItemIntMethod = AbilityPatches.GetAddItemIntMethod(instanceValue);
				int num5 = 0;
				float num6 = 0f;
				foreach (KeyValuePair<int, int> item in AbilityPatches.OreToBarMap)
				{
					int key = item.Key;
					int value = item.Value;
					int inventoryAmount = GetInventoryAmount(instanceValue, key);
					if (inventoryAmount < num4)
					{
						continue;
					}
					float value2;
					float num7 = (AbilityPatches.OreManaCostMap.TryGetValue(key, out value2) ? value2 : 3f);
					float num8 = maxMana * (num7 / 100f);
					int num9 = inventoryAmount / num4;
					int num10 = num9 * num4;
					float num11 = num8 * (float)num9;
					float num12 = (num2 - num6 - num11) / maxMana * 100f;
					if (num12 < AbilityConfig.InfernalForgeManaThreshold.Value)
					{
						num9 = 0;
						for (int num13 = inventoryAmount / num4; num13 >= 1; num13--)
						{
							float num14 = num8 * (float)num13;
							float num15 = (num2 - num6 - num14) / maxMana * 100f;
							if (num15 >= AbilityConfig.InfernalForgeManaThreshold.Value)
							{
								num9 = num13;
								break;
							}
						}
						if (num9 <= 0)
						{
							continue;
						}
						num10 = num9 * num4;
						num11 = num8 * (float)num9;
					}
					if (!RemoveInventoryItem(instanceValue, key, num10))
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogWarning((object)$"[InfernalForge] Failed to remove {num10}x ore {key}");
						}
						continue;
					}
					bool flag = false;
					if (addItemIntMethod != null)
					{
						flag = AbilityPatches.InvokeAddItem(addItemIntMethod, instanceValue, value, num9, notify: true);
					}
					if (!flag)
					{
						ManualLogSource log3 = Plugin.Log;
						if (log3 != null)
						{
							log3.LogError((object)$"[InfernalForge] Bar addition failed for {num9}x bar {value} — returning ore");
						}
						AddInventoryItem(instanceValue, key, num10);
						continue;
					}
					num5 += num9;
					num6 += num11;
					ManualLogSource log4 = Plugin.Log;
					if (log4 != null)
					{
						log4.LogInfo((object)$"[InfernalForge] Smelted {num10}x ore {key} → {num9}x bar {value} (cost: {num7}% per bar)");
					}
				}
				if (num5 > 0)
				{
					float num16 = num2 - num6;
					if (num16 < 0f)
					{
						num16 = 0f;
					}
					ReflectionHelper.SetInstanceValue(instance, "Mana", num16);
					AbilityPatches.SendGameNotification($"Infernal Forge: Smelted {num5} bar(s) (-{num6:F1} Mana)");
					ManualLogSource log5 = Plugin.Log;
					if (log5 != null)
					{
						log5.LogInfo((object)$"[InfernalForge] Total: {num5} bars, {num6:F1} mana used");
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log6 = Plugin.Log;
				if (log6 != null)
				{
					log6.LogWarning((object)("[InfernalForge] Error: " + ex.Message));
				}
				ManualLogSource log7 = Plugin.Log;
				if (log7 != null)
				{
					log7.LogWarning((object)("[InfernalForge] Stack: " + ex.StackTrace));
				}
			}
		}

		private void UpdateFontOfLight()
		{
			if (!ActiveAbilityManager.IsRuntimeEnabled("FontOfLight"))
			{
				return;
			}
			float num = AbilityConfig.FontOfLightInterval?.Value ?? 45f;
			if (Time.time - _lastFontOfLightCheck < num)
			{
				return;
			}
			_lastFontOfLightCheck = Time.time;
			try
			{
				Player instance = Player.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return;
				}
				float maxMana = instance.MaxMana;
				float num2 = ReflectionHelper.TryGetValue(instance, "Mana", 0f);
				float num3 = ((maxMana > 0f) ? (num2 / maxMana * 100f) : 0f);
				if (num3 >= (AbilityConfig.FontOfLightManaThreshold?.Value ?? 80f))
				{
					return;
				}
				int num4 = AbilityConfig.FontOfLightGoldCost?.Value ?? 10;
				if (num4 > 0)
				{
					Type type = AccessTools.TypeByName("Wish.GameSave");
					if (type != null)
					{
						int num6 = ((ReflectionHelper.GetStaticValue(type, "Coins") is int num5) ? num5 : 0);
						if (num6 < num4)
						{
							return;
						}
					}
				}
				float num7 = AbilityConfig.FontOfLightManaPercent?.Value ?? 5f;
				float num8 = maxMana * (num7 / 100f);
				if (num8 <= 0f)
				{
					return;
				}
				ReflectionHelper.InvokeMethod(instance, "AddMana", num8, 1f);
				if (num4 > 0)
				{
					MethodInfo methodInfo = AccessTools.Method(((object)instance).GetType(), "AddMoney", new Type[4]
					{
						typeof(int),
						typeof(bool),
						typeof(bool),
						typeof(bool)
					}, (Type[])null);
					if (methodInfo != null)
					{
						methodInfo.Invoke(instance, new object[4]
						{
							-num4,
							false,
							false,
							false
						});
					}
				}
				AbilityPatches.SendGameNotification($"Font of Light: +{num7:F0}% mana" + ((num4 > 0) ? $" (-{num4} gold)" : ""));
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[FontOfLight] Error: " + ex.Message));
				}
			}
		}

		private void CacheInventoryMethods(object inventory)
		{
			try
			{
				Type type = inventory.GetType();
				_cachedGetAmountMethod = type.GetMethod("GetAmount", new Type[1] { typeof(int) });
				if (_cachedGetAmountMethod == null)
				{
					_cachedGetAmountMethod = type.GetMethod("GetItemAmount", new Type[1] { typeof(int) });
				}
				_cachedRemoveItemMethod = type.GetMethod("RemoveAll", new Type[1] { typeof(int) });
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogInfo((object)("[InfernalForge] Inventory methods cached - GetAmount:" + (_cachedGetAmountMethod?.Name ?? "null") + ", RemoveItem:" + (_cachedRemoveItemMethod?.Name ?? "null")));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("[InfernalForge] Failed to cache inventory methods: " + ex.Message));
				}
			}
		}

		private int GetInventoryAmount(object inventory, int itemId)
		{
			try
			{
				if (_cachedGetAmountMethod != null && _cachedGetAmountMethod.Invoke(inventory, new object[1] { itemId }) is int result)
				{
					return result;
				}
				object obj = ReflectionHelper.InvokeMethod(inventory, "GetAmount", itemId);
				if (obj is int)
				{
					int result2 = (int)obj;
					if (true)
					{
						return result2;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)$"[InfernalForge] GetAmount failed for item {itemId}: {ex.Message}");
				}
			}
			return 0;
		}

		private bool RemoveInventoryItem(object inventory, int itemId, int amount)
		{
			try
			{
				if (_cachedRemoveItemMethod == null)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)$"[InfernalForge] No cached RemoveAll method — cannot remove {amount}x item {itemId}");
					}
					return false;
				}
				int inventoryAmount = GetInventoryAmount(inventory, itemId);
				if (inventoryAmount < amount)
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)$"[InfernalForge] Not enough items: have {inventoryAmount}, need {amount} of item {itemId}");
					}
					return false;
				}
				int num = inventoryAmount - amount;
				_cachedRemoveItemMethod.Invoke(inventory, new object[1] { itemId });
				if (num > 0)
				{
					AddInventoryItem(inventory, itemId, num);
				}
				return true;
			}
			catch (Exception ex)
			{
				Exception ex2 = ex.InnerException ?? ex;
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogWarning((object)$"[InfernalForge] RemoveItem failed for {amount}x item {itemId}: {ex2.GetType().Name}: {ex2.Message}");
				}
				return false;
			}
		}

		private void AddInventoryItem(object inventory, int itemId, int amount)
		{
			try
			{
				MethodInfo addItemIntMethod = AbilityPatches.GetAddItemIntMethod(inventory);
				if (addItemIntMethod != null)
				{
					AbilityPatches.InvokeAddItem(addItemIntMethod, inventory, itemId, amount, notify: false);
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)$"[InfernalForge] Failed to return {amount}x item {itemId}: {ex.Message}");
				}
			}
		}

		public static float GetCurrentHour()
		{
			try
			{
				Type type = ReflectionHelper.FindWishType("DayCycle");
				if (type == null)
				{
					return -1f;
				}
				object singletonInstance = ReflectionHelper.GetSingletonInstance(type);
				if (singletonInstance != null)
				{
					float num = ReflectionHelper.TryGetValue(singletonInstance, "Hour", -1f);
					if (num >= 0f)
					{
						return num;
					}
					num = ReflectionHelper.TryGetValue(singletonInstance, "CurrentHour", -1f);
					if (num >= 0f)
					{
						return num;
					}
					num = ReflectionHelper.TryGetValue(singletonInstance, "currentHour", -1f);
					if (num >= 0f)
					{
						return num;
					}
					int num2 = ReflectionHelper.TryGetValue(singletonInstance, "Hour", -1);
					if (num2 >= 0)
					{
						return num2;
					}
				}
				return ReflectionHelper.TryGetValue(null, "Hour", -1f);
			}
			catch
			{
				return -1f;
			}
		}

		public static string GetCurrentSeason()
		{
			try
			{
				Type type = ReflectionHelper.FindWishType("DayCycle");
				if (type == null)
				{
					return null;
				}
				object singletonInstance = ReflectionHelper.GetSingletonInstance(type);
				if (singletonInstance != null)
				{
					object instanceValue = ReflectionHelper.GetInstanceValue(singletonInstance, "Season");
					if (instanceValue != null)
					{
						return instanceValue.ToString();
					}
					instanceValue = ReflectionHelper.GetInstanceValue(singletonInstance, "season");
					if (instanceValue != null)
					{
						return instanceValue.ToString();
					}
					instanceValue = ReflectionHelper.GetInstanceValue(singletonInstance, "CurrentSeason");
					if (instanceValue != null)
					{
						return instanceValue.ToString();
					}
				}
				return null;
			}
			catch
			{
				return null;
			}
		}

		public static bool IsInMine()
		{
			string text = SceneHelpers.GetCurrentSceneName().ToLowerInvariant();
			return text.Contains("mine") || text.Contains("dungeon") || text.Contains("cave") || text.Contains("underground") || text.Contains("nelvari") || text.Contains("withergate");
		}

		public static bool IsDaytime()
		{
			float currentHour = GetCurrentHour();
			if (currentHour < 0f)
			{
				return true;
			}
			return currentHour >= 6f && currentHour < 18f;
		}

		public static float GetPlayerHPRatio()
		{
			try
			{
				Player instance = Player.Instance;
				if ((Object)(object)instance == (Object)null)
				{
					return 1f;
				}
				float maxHealth = instance.MaxHealth;
				if (maxHealth <= 0f)
				{
					return 1f;
				}
				float num = ReflectionHelper.TryGetValue(instance, "health", maxHealth);
				return num / maxHealth;
			}
			catch
			{
				return 1f;
			}
		}

		private void CheckAbilityToggleHotkey()
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!Input.GetKeyDown(Plugin.StaticAbilityToggleKey))
				{
					return;
				}
				RacialBonusManager racialBonusManager = Plugin.GetRacialBonusManager();
				if (racialBonusManager == null)
				{
					return;
				}
				Race? playerRace = racialBonusManager.GetPlayerRace();
				if (!playerRace.HasValue)
				{
					return;
				}
				string activeAbilityForRace = GetActiveAbilityForRace(playerRace.Value);
				if (activeAbilityForRace == null)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogDebug((object)$"[BirthrightRunner] No toggleable active ability for race {playerRace.Value}");
					}
					return;
				}
				bool flag = ActiveAbilityManager.ToggleRuntime(activeAbilityForRace);
				string abilityDisplayName = GetAbilityDisplayName(activeAbilityForRace);
				AbilityPatches.SendGameNotification(abilityDisplayName + ": " + (flag ? "ON" : "OFF"));
				ManualLogSource log2 = Plugin.Log;
				if (log2 != null)
				{
					log2.LogInfo((object)("[BirthrightRunner] Toggled " + abilityDisplayName + ": " + (flag ? "ON" : "OFF")));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogWarning((object)("[BirthrightRunner] Toggle hotkey error: " + ex.Message));
				}
			}
		}

		private static string GetActiveAbilityForRace(Race race)
		{
			return race switch
			{
				Race.FireElemental => "InfernalForge", 
				Race.WaterElemental => "TidalBlessing", 
				Race.Angel => "FontOfLight", 
				Race.Demon => "SoulHarvest", 
				_ => null, 
			};
		}

		private static string GetAbilityDisplayName(string abilityKey)
		{
			return abilityKey switch
			{
				"InfernalForge" => "Infernal Forge", 
				"TidalBlessing" => "Tidal Blessing", 
				"FontOfLight" => "Font of Light", 
				"SoulHarvest" => "Soul Harvest", 
				_ => abilityKey, 
			};
		}
	}
	[BepInPlugin("com.azraelgodking.havensbirthright", "Haven's Birthright", "1.3.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static KeyCode StaticAbilityToggleKey = (KeyCode)290;

		internal static KeyCode StaticReloadConfigKey = (KeyCode)293;

		private Harmony _harmony;

		private RacialBonusManager _racialBonusManager;

		private ConfigEntry<bool> _checkForUpdates;

		private ConfigEntry<KeyCode> _reloadConfigKey;

		private BirthrightRunner _runner;

		public static Plugin Instance { get; private set; }

		public static ManualLogSource Log { get; private set; }

		public static ConfigFile ConfigFile { get; private set; }

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Expected O, but got Unknown
			//IL_0350: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Expected O, but got Unknown
			//IL_050c: Unknown result type (might be due to invalid IL or missing references)
			//IL_052b: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			ConfigFile = ((BaseUnityPlugin)this).Config;
			Log.LogInfo((object)"Loading Haven's Birthright v1.3.0");
			try
			{
				RacialConfig.Initialize(((BaseUnityPlugin)this).Config);
				AbilityConfig.Initialize(((BaseUnityPlugin)this).Config);
				StaticAbilityToggleKey = AbilityConfig.ActiveAbilityToggleKey.Value;
				_checkForUpdates = ((BaseUnityPlugin)this).Config.Bind<bool>("Updates", "CheckForUpdates", true, "Check for mod updates on startup");
				_reloadConfigKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "ReloadConfigKey", (KeyCode)293, "Key to reload config from file (edit the .cfg file, then press this key in-game to apply)");
				StaticAbilityToggleKey = AbilityConfig.ActiveAbilityToggleKey.Value;
				StaticReloadConfigKey = _reloadConfigKey.Value;
				SubscribeConfigChanged();
				_racialBonusManager = new RacialBonusManager();
				_runner = PersistentRunnerBase.CreateRunner<BirthrightRunner>();
				Log.LogInfo((object)"BirthrightRunner created for active abilities");
				_harmony = new Harmony("com.azraelgodking.havensbirthright");
				try
				{
					Type typeFromHandle = typeof(Player);
					Log.LogInfo((object)("Player type: " + typeFromHandle.FullName + " from " + typeFromHandle.Assembly.GetName().Name));
					PatchMethod(typeFromHandle, "InitializeAsOwner", typeof(PlayerPatches), "OnPlayerInitialized");
					PatchMethod(typeFromHandle, "Initialize", typeof(PlayerPatches), "OnPlayerInitialize", Type.EmptyTypes);
					PatchMethod(typeFromHandle, "GetStat", typeof(StatPatches), "ModifyGetStat", new Type[1] { typeof(StatType) });
					PatchMethodPrefix(typeFromHandle, "ReceiveDamage", typeof(CombatPatches), "ModifyDamageReceived");
					PatchMethod(typeFromHandle, "ReceiveDamage", typeof(CombatPatches), "OnDamageReceivedPostfix");
					Type type = AccessTools.TypeByName("Wish.NPCAI");
					if (type != null)
					{
						MethodInfo methodInfo = AccessTools.Method(type, "AddRelationship", new Type[3]
						{
							typeof(float),
							typeof(float),
							typeof(bool)
						}, (Type[])null);
						if (methodInfo != null)
						{
							MethodInfo methodInfo2 = AccessTools.Method(typeof(EconomyPatches), "ModifyRelationshipGain", (Type[])null, (Type[])null);
							_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
							Log.LogInfo((object)"Successfully patched NPCAI.AddRelationship");
						}
						else
						{
							Log.LogWarning((object)"Could not find NPCAI.AddRelationship - relationship bonuses will not work");
						}
					}
					else
					{
						Log.LogWarning((object)"Could not find NPCAI type - relationship bonuses will not work");
					}
					PatchShopBuyItemForDiscount();
					PatchMethodPrefix(typeFromHandle, "AddMana", typeof(AbilityPatches), "OnPlayerAddManaPrefix");
					Type type2 = AccessTools.TypeByName("Wish.EnemyAI");
					if (type2 != null)
					{
						MethodInfo methodInfo3 = AccessTools.Method(type2, "Die", new Type[1] { typeof(bool) }, (Type[])null);
						if (methodInfo3 != null)
						{
							MethodInfo methodInfo4 = AccessTools.Method(typeof(SoulHarvestPatches), "OnEnemyDiePostfix", (Type[])null, (Type[])null);
							if (methodInfo4 != null)
							{
								_harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(methodInfo4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
								Log.LogInfo((object)"Successfully patched EnemyAI.Die (Soul Harvest)");
							}
						}
					}
					IEnumerable<MethodBase> patchedMethods = _harmony.GetPatchedMethods();
					int num = 0;
					foreach (MethodBase item in patchedMethods)
					{
						Log.LogInfo((object)("Patched: " + item.DeclaringType?.Name + "." + item.Name));
						num++;
					}
					Log.LogInfo((object)$"Total methods patched: {num}");
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"Harmony patching failed: {arg}");
				}
				if (_checkForUpdates.Value)
				{
					VersionChecker.CheckForUpdate("com.azraelgodking.havensbirthright", "1.3.0", Log, delegate(VersionChecker.VersionCheckResult result)
					{
						result.NotifyUpdateAvailable(Log);
					});
				}
				Log.LogInfo((object)"Haven's Birthright loaded successfully!");
				Log.LogInfo((object)("Active abilities: " + (AbilityConfig.EnableActiveAbilities.Value ? "ENABLED" : "DISABLED")));
				Log.LogInfo((object)("Racial drawbacks: " + (AbilityConfig.EnableRacialDrawbacks.Value ? "ENABLED" : "DISABLED")));
				Log.LogInfo((object)("Conditional synergies: " + (AbilityConfig.EnableConditionalSynergies.Value ? "ENABLED" : "DISABLED")));
				Log.LogInfo((object)$"Ability toggle key: {StaticAbilityToggleKey}");
				Log.LogInfo((object)$"Config reload key: {StaticReloadConfigKey} (edit .cfg then press in-game to apply)");
			}
			catch (Exception arg2)
			{
				Log.LogError((object)string.Format("Failed to load {0}: {1}", "Haven's Birthright", arg2));
			}
		}

		private void SubscribeConfigChanged()
		{
			AbilityConfig.ActiveAbilityToggleKey.SettingChanged += UpdateKeybinds;
			_reloadConfigKey.SettingChanged += UpdateKeybinds;
			void UpdateKeybinds(object s, EventArgs e)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				StaticAbilityToggleKey = AbilityConfig.ActiveAbilityToggleKey.Value;
				StaticReloadConfigKey = _reloadConfigKey.Value;
			}
		}

		public void ReloadConfig()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				RacialConfig.Initialize(((BaseUnityPlugin)this).Config);
				AbilityConfig.Initialize(((BaseUnityPlugin)this).Config);
				StaticAbilityToggleKey = AbilityConfig.ActiveAbilityToggleKey.Value;
				StaticReloadConfigKey = _reloadConfigKey.Value;
				_racialBonusManager = new RacialBonusManager();
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)"[Haven's Birthright] Config reloaded from file");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log2 = Log;
				if (log2 != null)
				{
					log2.LogError((object)("[Haven's Birthright] Config reload failed: " + ex.Message));
				}
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void PatchMethod(Type targetType, string methodName, Type patchType, string patchMethodName, Type[] parameters = null)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = ((parameters == null) ? AccessTools.Method(targetType, methodName, (Type[])null, (Type[])null) : AccessTools.Method(targetType, methodName, parameters, (Type[])null));
				if (methodInfo == null)
				{
					Log.LogWarning((object)("Could not find method " + targetType.Name + "." + methodName));
					return;
				}
				MethodInfo methodInfo2 = AccessTools.Method(patchType, patchMethodName, (Type[])null, (Type[])null);
				if (methodInfo2 == null)
				{
					Log.LogWarning((object)("Could not find patch method " + patchType.Name + "." + patchMethodName));
					return;
				}
				_harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)("Successfully patched " + targetType.Name + "." + methodName));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to patch " + targetType.Name + "." + methodName + ": " + ex.Message));
			}
		}

		private void PatchMethodPrefix(Type targetType, string methodName, Type patchType, string patchMethodName, Type[] parameters = null)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = ((parameters == null) ? AccessTools.Method(targetType, methodName, (Type[])null, (Type[])null) : AccessTools.Method(targetType, methodName, parameters, (Type[])null));
				if (methodInfo == null)
				{
					Log.LogWarning((object)("Could not find method " + targetType.Name + "." + methodName));
					return;
				}
				MethodInfo methodInfo2 = AccessTools.Method(patchType, patchMethodName, (Type[])null, (Type[])null);
				if (methodInfo2 == null)
				{
					Log.LogWarning((object)("Could not find patch method " + patchType.Name + "." + patchMethodName));
					return;
				}
				_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Log.LogInfo((object)("Successfully patched " + targetType.Name + "." + methodName + " (prefix)"));
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to patch " + targetType.Name + "." + methodName + ": " + ex.Message));
			}
		}

		private void PatchShopBuyItemForDiscount()
		{
			try
			{
				Type type = AccessTools.TypeByName("Wish.Shop");
				if (type == null)
				{
					Log.LogWarning((object)"Could not find Wish.Shop type - shop discounts will not work");
					return;
				}
				Type type2 = AccessTools.TypeByName("Wish.ShopItemInfo2");
				Type type3 = AccessTools.TypeByName("Wish.ShopLoot2");
				if (type2 != null)
				{
					MethodInfo methodInfo = AccessTools.Method(type, "BuyItem", new Type[2]
					{
						type2,
						typeof(int)
					}, (Type[])null);
					if (methodInfo != null)
					{
						PatchMethodPrefix(type, "BuyItem", typeof(EconomyPatches), "OnBeforeShopBuyItem", new Type[2]
						{
							type2,
							typeof(int)
						});
					}
				}
				if (type3 != null)
				{
					MethodInfo methodInfo2 = AccessTools.Method(type, "BuyItem", new Type[2]
					{
						type3,
						typeof(int)
					}, (Type[])null);
					if (methodInfo2 != null)
					{
						PatchMethodPrefix(type, "BuyItem", typeof(EconomyPatches), "OnBeforeShopBuyItem", new Type[2]
						{
							type3,
							typeof(int)
						});
					}
					MethodInfo methodInfo3 = AccessTools.Method(type, "BuyItem", new Type[1] { type3 }, (Type[])null);
					if (methodInfo3 != null)
					{
						PatchMethodPrefix(type, "BuyItem", typeof(EconomyPatches), "OnBeforeShopBuyItemSingle", new Type[1] { type3 });
					}
				}
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Failed to patch Shop for discount: " + ex.Message));
			}
		}

		public static RacialBonusManager GetRacialBonusManager()
		{
			return Instance?._racialBonusManager;
		}

		public static void EnsureRunner()
		{
			if ((Object)(object)Instance == (Object)null)
			{
				return;
			}
			if (!((Object)(object)Instance._runner == (Object)null))
			{
				Object runner = (Object)(object)Instance._runner;
				if (runner == null || !(runner == (Object)null))
				{
					return;
				}
			}
			Instance._runner = PersistentRunnerBase.CreateRunner<BirthrightRunner>();
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogInfo((object)"[Plugin] BirthrightRunner recreated after destruction");
			}
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "com.azraelgodking.havensbirthright";

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

		public const string PLUGIN_VERSION = "1.3.0";
	}
	public enum Race
	{
		Human,
		Elf,
		Angel,
		Demon,
		Elemental,
		FireElemental,
		WaterElemental,
		Amari,
		AmariCat,
		AmariDog,
		AmariBird,
		AmariAquatic,
		AmariReptile,
		Naga
	}
	public enum ElementalVariant
	{
		None,
		Fire,
		Water
	}
	public enum BonusType
	{
		MeleeStrength,
		MagicPower,
		Defense,
		CriticalChance,
		AttackSpeed,
		FarmingSpeed,
		CropQuality,
		CropYield,
		WateringEfficiency,
		MiningSpeed,
		MiningYield,
		WoodcuttingSpeed,
		WoodcuttingYield,
		FishingSpeed,
		FishingLuck,
		ForagingChance,
		CraftingSpeed,
		CraftingQuality,
		RelationshipGain,
		ShopDiscount,
		MovementSpeed,
		MaxHealth,
		MaxMana,
		ManaRegen,
		HealthRegen,
		ExperienceGain,
		GoldFind,
		LuckBonus,
		DodgeChance,
		FishingMinigameSpeed,
		WoodcuttingDamage,
		MiningDamage,
		SpellAttackSpeed,
		AirSkipChance,
		CommunityTokenGain,
		TripleGoldChance
	}
	public class RacialBonus
	{
		public BonusType Type { get; set; }

		public float Value { get; set; }

		public bool IsPercentage { get; set; }

		public string Description { get; set; }

		public RacialBonus(BonusType type, float value, bool isPercentage, string description)
		{
			Type = type;
			Value = value;
			IsPercentage = isPercentage;
			Description = description;
		}

		public string GetFormattedValue()
		{
			if (IsPercentage)
			{
				return (Value >= 0f) ? $"+{Value}%" : $"{Value}%";
			}
			return (Value >= 0f) ? $"+{Value}" : $"{Value}";
		}
	}
	public class RacialBonusManager
	{
		private Dictionary<Race, List<RacialBonus>> _racialBonuses;

		private Race? _currentPlayerRace;

		private int _cachedGameSubRace = -1;

		public RacialBonusManager()
		{
			_racialBonuses = new Dictionary<Race, List<RacialBonus>>();
			InitializeDefaultBonuses();
		}

		private void InitializeDefaultBonuses()
		{
			_racialBonuses[Race.Human] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.ExperienceGain, RacialConfig.HumanExpBonus.Value, isPercentage: true, "Adaptable: Gain experience faster"),
				new RacialBonus(BonusType.RelationshipGain, RacialConfig.HumanRelationshipBonus.Value, isPercentage: true, "Charismatic: Build relationships faster"),
				new RacialBonus(BonusType.ShopDiscount, RacialConfig.HumanShopDiscount.Value, isPercentage: true, "Silver Tongue: Small discount at shops"),
				new RacialBonus(BonusType.CommunityTokenGain, RacialConfig.HumanCommunityTokenBonus.Value, isPercentage: true, "Community Leader: Earn more community tokens")
			};
			_racialBonuses[Race.Elf] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.FarmingSpeed, RacialConfig.ElfFarmingBonus.Value, isPercentage: true, "Nature's Touch: Farm crops faster"),
				new RacialBonus(BonusType.CropQuality, RacialConfig.ElfCropQualityBonus.Value, isPercentage: true, "Green Thumb: Higher chance for quality crops"),
				new RacialBonus(BonusType.ForagingChance, RacialConfig.ElfForagingBonus.Value, isPercentage: true, "Forest Walker: Find more foragables"),
				new RacialBonus(BonusType.ManaRegen, RacialConfig.ElfManaRegenBonus.Value, isPercentage: true, "Arcane Heritage: Faster mana regeneration"),
				new RacialBonus(BonusType.WoodcuttingDamage, RacialConfig.ElfWoodcuttingDamageBonus.Value, isPercentage: true, "Timber Master: Deal more damage to trees")
			};
			_racialBonuses[Race.Angel] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.MaxMana, RacialConfig.AngelMaxManaBonus.Value, isPercentage: true, "Divine Reservoir: Increased maximum mana"),
				new RacialBonus(BonusType.MagicPower, RacialConfig.AngelMagicBonus.Value, isPercentage: true, "Holy Light: Enhanced magic damage"),
				new RacialBonus(BonusType.HealthRegen, RacialConfig.AngelHealthRegenBonus.Value, isPercentage: true, "Blessed Recovery: Faster health regeneration"),
				new RacialBonus(BonusType.LuckBonus, RacialConfig.AngelLuckBonus.Value, isPercentage: true, "Fortune's Favor: Blessed with good luck"),
				new RacialBonus(BonusType.SpellAttackSpeed, RacialConfig.AngelSpellAttackSpeedBonus.Value, isPercentage: true, "Divine Haste: Cast spells faster")
			};
			_racialBonuses[Race.Demon] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.MeleeStrength, RacialConfig.DemonMeleeBonus.Value, isPercentage: true, "Infernal Might: Increased melee damage"),
				new RacialBonus(BonusType.CriticalChance, RacialConfig.DemonCritBonus.Value, isPercentage: true, "Ruthless: Higher critical hit chance"),
				new RacialBonus(BonusType.MaxHealth, RacialConfig.DemonHealthBonus.Value, isPercentage: true, "Hellforged Vitality: Increased maximum health"),
				new RacialBonus(BonusType.GoldFind, RacialConfig.DemonGoldBonus.Value, isPercentage: true, "Greed: Find more gold"),
				new RacialBonus(BonusType.TripleGoldChance, RacialConfig.DemonTripleGoldBonus.Value, isPercentage: true, "Avarice: Chance for triple gold drops"),
				new RacialBonus(BonusType.MiningDamage, RacialConfig.DemonMiningDamageBonus.Value, isPercentage: true, "Hellfire Pick: Deal more damage to rocks")
			};
			_racialBonuses[Race.FireElemental] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.MeleeStrength, RacialConfig.FireElementalMeleeBonus.Value, isPercentage: true, "Burning Fury: Increased melee damage"),
				new RacialBonus(BonusType.MagicPower, RacialConfig.FireElementalMagicBonus.Value, isPercentage: true, "Inferno: Enhanced magic damage"),
				new RacialBonus(BonusType.AttackSpeed, RacialConfig.FireElementalAttackSpeedBonus.Value, isPercentage: true, "Wildfire: Faster attack speed"),
				new RacialBonus(BonusType.CriticalChance, RacialConfig.FireElementalCritBonus.Value, isPercentage: true, "Scorching Strike: Higher critical hit chance")
			};
			_racialBonuses[Race.WaterElemental] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.Defense, RacialConfig.WaterElementalDefenseBonus.Value, isPercentage: true, "Tidal Shield: Increased defense"),
				new RacialBonus(BonusType.HealthRegen, RacialConfig.WaterElementalHealthRegenBonus.Value, isPercentage: true, "Healing Waters: Faster health regeneration"),
				new RacialBonus(BonusType.ManaRegen, RacialConfig.WaterElementalManaRegenBonus.Value, isPercentage: true, "Flowing Spirit: Faster mana regeneration"),
				new RacialBonus(BonusType.FishingLuck, RacialConfig.WaterElementalFishingBonus.Value, isPercentage: true, "Aquatic Kinship: Better fishing luck"),
				new RacialBonus(BonusType.FishingMinigameSpeed, RacialConfig.WaterElementalFishingMinigameBonus.Value, isPercentage: true, "Water Mastery: Faster fishing minigame"),
				new RacialBonus(BonusType.AirSkipChance, RacialConfig.WaterElementalAirSkipBonus.Value, isPercentage: true, "Breathless: No need for air underwater")
			};
			_racialBonuses[Race.Elemental] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.MiningSpeed, RacialConfig.ElementalMiningSpeedBonus.Value, isPercentage: true, "Stone Affinity: Mine faster"),
				new RacialBonus(BonusType.MiningYield, RacialConfig.ElementalMiningYieldBonus.Value, isPercentage: true, "Earth's Bounty: Chance for extra ore"),
				new RacialBonus(BonusType.MagicPower, RacialConfig.ElementalMagicBonus.Value, isPercentage: true, "Elemental Mastery: Enhanced magic damage"),
				new RacialBonus(BonusType.Defense, RacialConfig.ElementalDefenseBonus.Value, isPercentage: true, "Hardened Form: Increased defense")
			};
			_racialBonuses[Race.Amari] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.MovementSpeed, RacialConfig.AmariSpeedBonus.Value, isPercentage: true, "Swift Paws: Move faster"),
				new RacialBonus(BonusType.AttackSpeed, RacialConfig.AmariAttackSpeedBonus.Value, isPercentage: true, "Predator's Reflexes: Attack faster"),
				new RacialBonus(BonusType.CraftingSpeed, RacialConfig.AmariCraftingBonus.Value, isPercentage: true, "Skilled Artisan: Craft items faster"),
				new RacialBonus(BonusType.WoodcuttingSpeed, RacialConfig.AmariWoodcuttingBonus.Value, isPercentage: true, "Forest Hunter: Chop trees faster")
			};
			_racialBonuses[Race.AmariCat] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.MovementSpeed, RacialConfig.AmariCatSpeedBonus.Value, isPercentage: true, "Feline Grace: Move faster"),
				new RacialBonus(BonusType.AttackSpeed, RacialConfig.AmariCatAttackSpeedBonus.Value, isPercentage: true, "Quick Pounce: Attack faster"),
				new RacialBonus(BonusType.CriticalChance, RacialConfig.AmariCatCritBonus.Value, isPercentage: true, "Predator's Strike: Higher critical hit chance"),
				new RacialBonus(BonusType.DodgeChance, RacialConfig.AmariCatDodgeBonus.Value, isPercentage: true, "Nine Lives: Higher chance to dodge attacks")
			};
			_racialBonuses[Race.AmariDog] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.MaxHealth, RacialConfig.AmariDogHealthBonus.Value, isPercentage: true, "Loyal Heart: Increased maximum health"),
				new RacialBonus(BonusType.Defense, RacialConfig.AmariDogDefenseBonus.Value, isPercentage: true, "Guardian's Resolve: Increased defense"),
				new RacialBonus(BonusType.RelationshipGain, RacialConfig.AmariDogRelationshipBonus.Value, isPercentage: true, "Best Friend: Build relationships faster"),
				new RacialBonus(BonusType.ExperienceGain, RacialConfig.AmariDogExpBonus.Value, isPercentage: true, "Eager Learner: Gain experience faster")
			};
			_racialBonuses[Race.AmariBird] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.MovementSpeed, RacialConfig.AmariBirdSpeedBonus.Value, isPercentage: true, "Wind Rider: Move faster"),
				new RacialBonus(BonusType.ForagingChance, RacialConfig.AmariBirdForagingBonus.Value, isPercentage: true, "Keen Eye: Find more foragables"),
				new RacialBonus(BonusType.ManaRegen, RacialConfig.AmariBirdManaRegenBonus.Value, isPercentage: true, "Sky Spirit: Faster mana regeneration"),
				new RacialBonus(BonusType.DodgeChance, RacialConfig.AmariBirdDodgeBonus.Value, isPercentage: true, "Evasive Flight: Higher chance to dodge attacks")
			};
			_racialBonuses[Race.AmariAquatic] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.FishingSpeed, RacialConfig.AmariAquaticFishingSpeedBonus.Value, isPercentage: true, "Water Born: Fish faster"),
				new RacialBonus(BonusType.FishingLuck, RacialConfig.AmariAquaticFishingLuckBonus.Value, isPercentage: true, "Tidal Blessing: Better fishing luck"),
				new RacialBonus(BonusType.ManaRegen, RacialConfig.AmariAquaticManaRegenBonus.Value, isPercentage: true, "Flowing Spirit: Faster mana regeneration"),
				new RacialBonus(BonusType.HealthRegen, RacialConfig.AmariAquaticHealthRegenBonus.Value, isPercentage: true, "Healing Waters: Faster health regeneration"),
				new RacialBonus(BonusType.FishingMinigameSpeed, RacialConfig.AmariAquaticFishingMinigameBonus.Value, isPercentage: true, "Aquatic Instinct: Faster fishing minigame"),
				new RacialBonus(BonusType.AirSkipChance, RacialConfig.AmariAquaticAirSkipBonus.Value, isPercentage: true, "Amphibious: Chance to skip air requirement")
			};
			_racialBonuses[Race.AmariReptile] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.Defense, RacialConfig.AmariReptileDefenseBonus.Value, isPercentage: true, "Scaled Hide: Increased defense"),
				new RacialBonus(BonusType.MeleeStrength, RacialConfig.AmariReptileMeleeBonus.Value, isPercentage: true, "Primal Strength: Increased melee damage"),
				new RacialBonus(BonusType.MaxHealth, RacialConfig.AmariReptileHealthBonus.Value, isPercentage: true, "Cold Blood: Increased maximum health"),
				new RacialBonus(BonusType.MiningSpeed, RacialConfig.AmariReptileMiningBonus.Value, isPercentage: true, "Burrow Instinct: Mine faster")
			};
			_racialBonuses[Race.Naga] = new List<RacialBonus>
			{
				new RacialBonus(BonusType.FishingSpeed, RacialConfig.NagaFishingSpeedBonus.Value, isPercentage: true, "Aquatic Nature: Fish faster"),
				new RacialBonus(BonusType.FishingLuck, RacialConfig.NagaFishingLuckBonus.Value, isPercentage: true, "Sea's Blessing: Better fishing luck"),
				new RacialBonus(BonusType.Defense, RacialConfig.NagaDefenseBonus.Value, isPercentage: true, "Scaled Hide: Increased defense"),
				new RacialBonus(BonusType.ManaRegen, RacialConfig.NagaManaRegenBonus.Value, isPercentage: true, "Tidal Magic: Faster mana regeneration"),
				new RacialBonus(BonusType.AirSkipChance, RacialConfig.NagaAirSkipBonus.Value, isPercentage: true, "Serpent's Breath: Chance to skip air requirement")
			};
			Plugin.Log.LogInfo((object)$"Initialized racial bonuses for {_racialBonuses.Count} races");
		}

		public void SetPlayerRace(Race race)
		{
			_currentPlayerRace = race;
			Plugin.Log.LogInfo((object)$"Player race set to: {race}");
		}

		public void SetPlayerRace(Race race, ElementalVariant variant)
		{
			if (race == Race.Elemental)
			{
				if (1 == 0)
				{
				}
				Race value = variant switch
				{
					ElementalVariant.Fire => Race.FireElemental, 
					ElementalVariant.Water => Race.WaterElemental, 
					_ => Race.Elemental, 
				};
				if (1 == 0)
				{
				}
				_currentPlayerRace = value;
			}
			else
			{
				_currentPlayerRace = race;
			}
			Plugin.Log.LogInfo((object)$"Player race set to: {_currentPlayerRace} (variant: {variant})");
		}

		public void SetPlayerRace(Race race, int gameSubRace)
		{
			_currentPlayerRace = race;
			_cachedGameSubRace = gameSubRace;
			Plugin.Log.LogInfo((object)$"Player race set to: {race} (cached SubRace: {gameSubRace})");
		}

		public bool IsElemental()
		{
			return _currentPlayerRace.GetValueOrDefault() == Race.Elemental || _currentPlayerRace.GetValueOrDefault() == Race.FireElemental || _currentPlayerRace.GetValueOrDefault() == Race.WaterElemental;
		}

		public bool IsAmari()
		{
			return _currentPlayerRace.GetValueOrDefault() == Race.Amari || _currentPlayerRace.GetValueOrDefault() == Race.AmariCat || _currentPlayerRace.GetValueOrDefault() == Race.AmariDog || _currentPlayerRace.GetValueOrDefault() == Race.AmariBird || _currentPlayerRace.GetValueOrDefault() == Race.AmariAquatic || _currentPlayerRace.GetValueOrDefault() == Race.AmariReptile;
		}

		public bool IsNaga()
		{
			return _currentPlayerRace.GetValueOrDefault() == Race.Naga;
		}

		public int GetCachedSubRace()
		{
			return _cachedGameSubRace;
		}

		public void ClearPlayerRace()
		{
			_currentPlayerRace = null;
			_cachedGameSubRace = -1;
			Plugin.Log.LogDebug((object)"[RacialBonusManager] Player race cleared");
		}

		public Race? GetPlayerRace()
		{
			return _currentPlayerRace;
		}

		public List<RacialBonus> GetBonusesForRace(Race race)
		{
			if (_racialBonuses.TryGetValue(race, out var value))
			{
				return value;
			}
			return new List<RacialBonus>();
		}

		public List<RacialBonus> GetCurrentPlayerBonuses()
		{
			if (_currentPlayerRace.HasValue)
			{
				return GetBonusesForRace(_currentPlayerRace.Value);
			}
			return new List<RacialBonus>();
		}

		public float GetBonusValue(BonusType type)
		{
			if (!_currentPlayerRace.HasValue)
			{
				return 0f;
			}
			List<RacialBonus> bonusesForRace = GetBonusesForRace(_currentPlayerRace.Value);
			foreach (RacialBonus item in bonusesForRace)
			{
				if (item.Type == type)
				{
					return item.Value;
				}
			}
			return 0f;
		}

		public float ApplyBonus(float baseValue, BonusType type)
		{
			float bonusValue = GetBonusValue(type);
			if (bonusValue == 0f)
			{
				return baseValue;
			}
			return baseValue * (1f + bonusValue / 100f);
		}

		public bool HasBonus(BonusType type)
		{
			return GetBonusValue(type) != 0f;
		}

		public void RefreshBonuses()
		{
			InitializeDefaultBonuses();
			Plugin.Log.LogInfo((object)"Racial bonuses refreshed from config");
		}
	}
	public static class RacialConfig
	{
		public static ConfigEntry<float> HumanExpBonus;

		public static ConfigEntry<float> HumanRelationshipBonus;

		public static ConfigEntry<float> HumanShopDiscount;

		public static ConfigEntry<float> ElfFarmingBonus;

		public static ConfigEntry<float> ElfCropQualityBonus;

		public static ConfigEntry<float> ElfForagingBonus;

		public static ConfigEntry<float> ElfManaRegenBonus;

		public static ConfigEntry<float> AngelMaxManaBonus;

		public static ConfigEntry<float> AngelMagicBonus;

		public static ConfigEntry<float> AngelHealthRegenBonus;

		public static ConfigEntry<float> AngelLuckBonus;

		public static ConfigEntry<float> DemonMeleeBonus;

		public static ConfigEntry<float> DemonCritBonus;

		public static ConfigEntry<float> DemonHealthBonus;

		public static ConfigEntry<float> DemonGoldBonus;

		public static ConfigEntry<float> FireElementalMeleeBonus;

		public static ConfigEntry<float> FireElementalMagicBonus;

		public static ConfigEntry<float> FireElementalAttackSpeedBonus;

		public static ConfigEntry<float> FireElementalCritBonus;

		public static ConfigEntry<float> WaterElementalDefenseBonus;

		public static ConfigEntry<float> WaterElementalHealthRegenBonus;

		public static ConfigEntry<float> WaterElementalManaRegenBonus;

		public static ConfigEntry<float> WaterElementalFishingBonus;

		public static ConfigEntry<float> ElementalMiningSpeedBonus;

		public static ConfigEntry<float> ElementalMiningYieldBonus;

		public static ConfigEntry<float> ElementalMagicBonus;

		public static ConfigEntry<float> ElementalDefenseBonus;

		public static ConfigEntry<float> AmariSpeedBonus;

		public static ConfigEntry<float> AmariAttackSpeedBonus;

		public static ConfigEntry<float> AmariCraftingBonus;

		public static ConfigEntry<float> AmariWoodcuttingBonus;

		public static ConfigEntry<float> AmariCatSpeedBonus;

		public static ConfigEntry<float> AmariCatAttackSpeedBonus;

		public static ConfigEntry<float> AmariCatCritBonus;

		public static ConfigEntry<float> AmariCatDodgeBonus;

		public static ConfigEntry<float> AmariDogHealthBonus;

		public static ConfigEntry<float> AmariDogDefenseBonus;

		public static ConfigEntry<float> AmariDogRelationshipBonus;

		public static ConfigEntry<float> AmariDogExpBonus;

		public static ConfigEntry<float> AmariBirdSpeedBonus;

		public static ConfigEntry<float> AmariBirdForagingBonus;

		public static ConfigEntry<float> AmariBirdManaRegenBonus;

		public static ConfigEntry<float> AmariBirdDodgeBonus;

		public static ConfigEntry<float> AmariAquaticFishingSpeedBonus;

		public static ConfigEntry<float> AmariAquaticFishingLuckBonus;

		public static ConfigEntry<float> AmariAquaticManaRegenBonus;

		public static ConfigEntry<float> AmariAquaticHealthRegenBonus;

		public static ConfigEntry<float> AmariReptileDefenseBonus;

		public static ConfigEntry<float> AmariReptileMeleeBonus;

		public static ConfigEntry<float> AmariReptileHealthBonus;

		public static ConfigEntry<float> AmariReptileMiningBonus;

		public static ConfigEntry<float> NagaFishingSpeedBonus;

		public static ConfigEntry<float> NagaFishingLuckBonus;

		public static ConfigEntry<float> NagaDefenseBonus;

		public static ConfigEntry<float> NagaManaRegenBonus;

		public static ConfigEntry<float> NagaAirSkipBonus;

		public static ConfigEntry<float> ElfWoodcuttingDamageBonus;

		public static ConfigEntry<float> HumanCommunityTokenBonus;

		public static ConfigEntry<float> DemonTripleGoldBonus;

		public static ConfigEntry<float> DemonMiningDamageBonus;

		public static ConfigEntry<float> AngelSpellAttackSpeedBonus;

		public static ConfigEntry<float> WaterElementalFishingMinigameBonus;

		public static ConfigEntry<float> WaterElementalAirSkipBonus;

		public static ConfigEntry<float> AmariAquaticFishingMinigameBonus;

		public static ConfigEntry<float> AmariAquaticAirSkipBonus;

		public static ConfigEntry<bool> EnableRacialBonuses;

		public static ConfigEntry<bool> ShowBonusNotifications;

		public static void Initialize(ConfigFile config)
		{
			EnableRacialBonuses = config.Bind<bool>("General", "EnableRacialBonuses", true, "Enable or disable all racial bonuses");
			ShowBonusNotifications = config.Bind<bool>("General", "ShowBonusNotifications", true, "Show notifications when racial bonuses are applied");
			HumanExpBonus = config.Bind<float>("Human", "ExperienceBonus", 10f, "Percentage bonus to experience gain");
			HumanRelationshipBonus = config.Bind<float>("Human", "RelationshipBonus", 15f, "Percentage bonus to relationship point gain");
			HumanShopDiscount = config.Bind<float>("Human", "ShopDiscount", 5f, "Percentage discount at shops");
			ElfFarmingBonus = config.Bind<float>("Elf", "FarmingSpeedBonus", 15f, "Percentage bonus to farming speed");
			ElfCropQualityBonus = config.Bind<float>("Elf", "CropQualityBonus", 20f, "Percentage bonus to crop quality chance");
			ElfForagingBonus = config.Bind<float>("Elf", "ForagingBonus", 25f, "Percentage bonus to foraging find chance");
			ElfManaRegenBonus = config.Bind<float>("Elf", "ManaRegenBonus", 15f, "Percentage bonus to mana regeneration");
			AngelMaxManaBonus = config.Bind<float>("Angel", "MaxManaBonus", 20f, "Percentage bonus to maximum mana");
			AngelMagicBonus = config.Bind<float>("Angel", "MagicPowerBonus", 15f, "Percentage bonus to magic damage");
			AngelHealthRegenBonus = config.Bind<float>("Angel", "HealthRegenBonus", 25f, "Percentage bonus to health regeneration");
			AngelLuckBonus = config.Bind<float>("Angel", "LuckBonus", 10f, "Percentage bonus to luck");
			DemonMeleeBonus = config.Bind<float>("Demon", "MeleeDamageBonus", 20f, "Percentage bonus to melee damage");
			DemonCritBonus = config.Bind<float>("Demon", "CriticalChanceBonus", 15f, "Percentage bonus to critical hit chance");
			DemonHealthBonus = config.Bind<float>("Demon", "MaxHealthBonus", 15f, "Percentage bonus to maximum health");
			DemonGoldBonus = config.Bind<float>("Demon", "GoldFindBonus", 20f, "Percentage bonus to gold drops");
			FireElementalMeleeBonus = config.Bind<float>("Fire Elemental", "MeleeDamageBonus", 15f, "Percentage bonus to melee damage");
			FireElementalMagicBonus = config.Bind<float>("Fire Elemental", "MagicPowerBonus", 20f, "Percentage bonus to magic damage");
			FireElementalAttackSpeedBonus = config.Bind<float>("Fire Elemental", "AttackSpeedBonus", 10f, "Percentage bonus to attack speed");
			FireElementalCritBonus = config.Bind<float>("Fire Elemental", "CriticalChanceBonus", 15f, "Percentage bonus to critical hit chance");
			WaterElementalDefenseBonus = config.Bind<float>("Water Elemental", "DefenseBonus", 20f, "Percentage bonus to defense");
			WaterElementalHealthRegenBonus = config.Bind<float>("Water Elemental", "HealthRegenBonus", 20f, "Percentage bonus to health regeneration");
			WaterElementalManaRegenBonus = config.Bind<float>("Water Elemental", "ManaRegenBonus", 25f, "Percentage bonus to mana regeneration");
			WaterElementalFishingBonus = config.Bind<float>("Water Elemental", "FishingLuckBonus", 20f, "Percentage bonus to fishing luck");
			ElementalMiningSpeedBonus = config.Bind<float>("Elemental (Generic)", "MiningSpeedBonus", 20f, "Percentage bonus to mining speed (used if Fire/Water variant cannot be detected)");
			ElementalMiningYieldBonus = config.Bind<float>("Elemental (Generic)", "MiningYieldBonus", 15f, "Percentage bonus to mining yield");
			ElementalMagicBonus = config.Bind<float>("Elemental (Generic)", "MagicPowerBonus", 10f, "Percentage bonus to magic damage");
			ElementalDefenseBonus = config.Bind<float>("Elemental (Generic)", "DefenseBonus", 15f, "Percentage bonus to defense");
			AmariSpeedBonus = config.Bind<float>("Amari (Generic)", "MovementSpeedBonus", 15f, "Percentage bonus to movement speed (used if variant cannot be detected)");
			AmariAttackSpeedBonus = config.Bind<float>("Amari (Generic)", "AttackSpeedBonus", 15f, "Percentage bonus to attack speed");
			AmariCraftingBonus = config.Bind<float>("Amari (Generic)", "CraftingSpeedBonus", 20f, "Percentage bonus to crafting speed");
			AmariWoodcuttingBonus = config.Bind<float>("Amari (Generic)", "WoodcuttingSpeedBonus", 15f, "Percentage bonus to woodcutting speed");
			AmariCatSpeedBonus = config.Bind<float>("Amari Cat", "MovementSpeedBonus", 20f, "Percentage bonus to movement speed");
			AmariCatAttackSpeedBonus = config.Bind<float>("Amari Cat", "AttackSpeedBonus", 20f, "Percentage bonus to attack speed");
			AmariCatCritBonus = config.Bind<float>("Amari Cat", "CriticalChanceBonus", 15f, "Percentage bonus to critical hit chance");
			AmariCatDodgeBonus = config.Bind<float>("Amari Cat", "DodgeChanceBonus", 10f, "Percentage bonus to dodge chance");
			AmariDogHealthBonus = config.Bind<float>("Amari Dog", "MaxHealthBonus", 20f, "Percentage bonus to maximum health");
			AmariDogDefenseBonus = config.Bind<float>("Amari Dog", "DefenseBonus", 15f, "Percentage bonus to defense");
			AmariDogRelationshipBonus = config.Bind<float>("Amari Dog", "RelationshipBonus", 25f, "Percentage bonus to relationship point gain");
			AmariDogExpBonus = config.Bind<float>("Amari Dog", "ExperienceBonus", 10f, "Percentage bonus to experience gain");
			AmariBirdSpeedBonus = config.Bind<float>("Amari Bird", "MovementSpeedBonus", 25f, "Percentage bonus to movement speed");
			AmariBirdForagingBonus = config.Bind<float>("Amari Bird", "ForagingBonus", 25f, "Percentage bonus to foraging find chance");
			AmariBirdManaRegenBonus = config.Bind<float>("Amari Bird", "ManaRegenBonus", 15f, "Percentage bonus to mana regeneration");
			AmariBirdDodgeBonus = config.Bind<float>("Amari Bird", "DodgeChanceBonus", 15f, "Percentage bonus to dodge chance");
			AmariAquaticFishingSpeedBonus = config.Bind<float>("Amari Aquatic", "FishingSpeedBonus", 25f, "Percentage bonus to fishing speed");
			AmariAquaticFishingLuckBonus = config.Bind<float>("Amari Aquatic", "FishingLuckBonus", 25f, "Percentage bonus to fishing luck");
			AmariAquaticManaRegenBonus = config.Bind<float>("Amari Aquatic", "ManaRegenBonus", 15f, "Percentage bonus to mana regeneration");
			AmariAquaticHealthRegenBonus = config.Bind<float>("Amari Aquatic", "HealthRegenBonus", 15f, "Percentage bonus to health regeneration");
			AmariReptileDefenseBonus = config.Bind<float>("Amari Reptile", "DefenseBonus", 25f, "Percentage bonus to defense");
			AmariReptileMeleeBonus = config.Bind<float>("Amari Reptile", "MeleeDamageBonus", 15f, "Percentage bonus to melee damage");
			AmariReptileHealthBonus = config.Bind<float>("Amari Reptile", "MaxHealthBonus", 15f, "Percentage bonus to maximum health");
			AmariReptileMiningBonus = config.Bind<float>("Amari Reptile", "MiningSpeedBonus", 20f, "Percentage bonus to mining speed");
			NagaFishingSpeedBonus = config.Bind<float>("Naga", "FishingSpeedBonus", 25f, "Percentage bonus to fishing speed");
			NagaFishingLuckBonus = config.Bind<float>("Naga", "FishingLuckBonus", 20f, "Percentage bonus to fishing luck");
			NagaDefenseBonus = config.Bind<float>("Naga", "DefenseBonus