Decompiled source of HavensBirthright v1.2.0

HavensBirthright.dll

Decompiled 6 hours 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.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("HavensBirthright")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+c7976283ee1309608b00428a169c130e784344a0")]
[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 text = www.downloadHandler.text;
						string pattern = "\"" + Regex.Escape(pluginGuid) + "\"\\s*:\\s*\\{([^}]+)\\}";
						Match match = Regex.Match(text, pattern, RegexOptions.Singleline);
						if (!match.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 value = match.Groups[1].Value;
						result.LatestVersion = ExtractJsonString(value, "version");
						result.ModName = ExtractJsonString(value, "name");
						result.NexusUrl = ExtractJsonString(value, "nexus");
						result.Changelog = ExtractJsonString(value, "changelog");
						if (string.IsNullOrEmpty(result.LatestVersion))
						{
							result.Success = false;
							result.ErrorMessage = "Could not parse version from response";
							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);
				if (!match.Success)
				{
					return null;
				}
				return match.Groups[1].Value;
			}
		}

		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_000b: 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)
					{
						object obj = type2.MakeGenericType(type).GetProperty("Instance")?.GetValue(null);
						if (obj != null)
						{
							MethodInfo method = type.GetMethod("SendNotification", new Type[5]
							{
								typeof(string),
								typeof(int),
								typeof(int),
								typeof(bool),
								typeof(bool)
							});
							if (method != null)
							{
								method.Invoke(obj, new object[5] { text, 0, 1, false, true });
								return;
							}
						}
					}
				}
			}
			catch (Exception ex)
			{
				if (logger != null)
				{
					logger.LogWarning((object)("Failed to send native notification: " + ex.Message));
				}
			}
			if (logger != null)
			{
				logger.LogWarning((object)("[UPDATE AVAILABLE] " + text));
			}
			if (!string.IsNullOrEmpty(result.NexusUrl) && logger != null)
			{
				logger.LogWarning((object)("Download at: " + result.NexusUrl));
			}
		}
	}
	public static class ReflectionHelper
	{
		public static readonly BindingFlags AllBindingFlags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;

		public static Type FindType(string typeName, params string[] namespaces)
		{
			Type type = AccessTools.TypeByName(typeName);
			if (type != null)
			{
				return type;
			}
			for (int i = 0; i < namespaces.Length; i++)
			{
				type = AccessTools.TypeByName(namespaces[i] + "." + typeName);
				if (type != null)
				{
					return type;
				}
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					type = assembly.GetTypes().FirstOrDefault((Type t) => t.Name == typeName || t.FullName == typeName);
					if (type != null)
					{
						return type;
					}
				}
				catch (ReflectionTypeLoadException)
				{
				}
			}
			return null;
		}

		public static Type FindWishType(string typeName)
		{
			return FindType(typeName, "Wish");
		}

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

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

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

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

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

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

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

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

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

		private float _lastHeartbeat;

		private string _lastSceneName = "";

		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_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			GameObject val = new GameObject("[" + typeof(T).Name + "]")
			{
				hideFlags = (HideFlags)61
			};
			Object.DontDestroyOnLoad((Object)val);
			return val.AddComponent<T>();
		}

		protected virtual void Awake()
		{
			//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();
			_lastSceneName = ((Scene)(ref activeScene)).name;
			_wasInGame = !SceneHelpers.IsMenuScene(_lastSceneName);
			Log("[" + RunnerName + "] Initialized in scene: " + _lastSceneName);
		}

		protected virtual void Update()
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (HeartbeatInterval > 0f)
			{
				_lastHeartbeat += Time.deltaTime;
				if (_lastHeartbeat >= HeartbeatInterval)
				{
					_lastHeartbeat = 0f;
					Log("[" + RunnerName + "] Heartbeat - still alive");
				}
			}
			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();
			exactMenuScenes = MenuScenePatterns;
			foreach (string value in exactMenuScenes)
			{
				if (text2.Contains(value))
				{
					return true;
				}
			}
			return false;
		}

		public static bool IsCurrentSceneMenu()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			return IsMenuScene(((Scene)(ref activeScene)).name);
		}

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

		public static string GetCurrentSceneName()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			return ((Scene)(ref activeScene)).name;
		}

		public static bool IsMainMenu()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			Scene activeScene = SceneManager.GetActiveScene();
			return ((Scene)(ref activeScene)).name == "MainMenu";
		}
	}
}
namespace HavensBirthright
{
	public class BirthrightRunner : PersistentRunnerBase
	{
		private float _outdoorTime;

		private float _lastTidalBlessingCheck;

		private float _lastInfernalForgeCheck;

		private bool _apiCacheInitialized;

		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;

		private MethodInfo _cachedGetAmountMethod;

		private MethodInfo _cachedRemoveItemMethod;

		private bool _inventoryMethodsCached;

		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()
		{
			if (!SceneHelpers.IsInGame())
			{
				return;
			}
			CheckAbilityToggleHotkey();
			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();
				}
			}
		}

		protected override void OnMenuTransition()
		{
			ActiveAbilityManager.ResetAll();
			_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();
			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)
					{
						_cachedHPRatio = ReflectionHelper.TryGetValue(instance, "health", maxHealth) / 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) && ReflectionHelper.TryGetValue(dictionary[key], "level", 0) >= value)
							{
								num++;
							}
						}
					}
				}
				catch
				{
				}
				return Mathf.Min((float)num * AbilityConfig.QuickLearnerBonusPerSkill.Value, 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);
			}
			else
			{
				_outdoorTime += Time.deltaTime;
				float value = Mathf.Min(_outdoorTime / 60f * AbilityConfig.TailwindBonusPerMinute.Value, AbilityConfig.TailwindMaxBonus.Value);
				ActiveAbilityManager.SetBonusValue("TailwindOutdoor", value);
			}
		}

		private void UpdateTidalBlessing()
		{
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: 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;
									{
										IDictionaryEnumerator dictionaryEnumerator = dictionary.GetEnumerator();
										try
										{
											while (dictionaryEnumerator.MoveNext())
											{
												switch (((DictionaryEntry)dictionaryEnumerator.Current).Value.ToString())
												{
												case "Hoed":
												case "2":
													num3++;
													break;
												case "Watered":
												case "3":
													num4++;
													break;
												}
											}
										}
										finally
										{
											IDisposable disposable = dictionaryEnumerator as IDisposable;
											if (disposable != null)
											{
												disposable.Dispose();
											}
										}
									}
									ManualLogSource log6 = Plugin.Log;
									if (log6 != null)
									{
										log6.LogInfo((object)$"[TidalBlessing]   farmingData: {dictionary.Count} total, {num3} hoed, {num4} watered");
									}
								}
							}
							catch
							{
							}
						}
						ManualLogSource log7 = Plugin.Log;
						if (log7 != null)
						{
							log7.LogInfo((object)("[TidalBlessing]   Scene: " + SceneHelpers.GetCurrentSceneName()));
						}
						ManualLogSource log8 = Plugin.Log;
						if (log8 != null)
						{
							log8.LogInfo((object)$"[TidalBlessing]   Player world pos: ({((Component)instance).transform.position.x:F1}, {((Component)instance).transform.position.y:F1})");
						}
						ManualLogSource log9 = Plugin.Log;
						if (log9 != null)
						{
							log9.LogInfo((object)("[TidalBlessing]   Grid distance filter: " + ((_gridCellToWorldMethod != null && _gridInstance != null) ? "enabled (radius=1)" : "disabled (no Grid)")));
						}
						ManualLogSource log10 = Plugin.Log;
						if (log10 != null)
						{
							log10.LogInfo((object)"[TidalBlessing] === END DIAGNOSTIC ===");
						}
					}
					if (num2 <= AbilityConfig.TidalBlessingHPThreshold.Value)
					{
						return;
					}
					if (_tileManagerType != null && _isWateredMethod != null && _waterTileMethod != null)
					{
						UpdateTidalBlessingViaTileManager(instance, maxHealth, num);
						return;
					}
					ManualLogSource log11 = Plugin.Log;
					if (log11 != null)
					{
						log11.LogInfo((object)"[TidalBlessing] Using fallback Crop approach (TileManager methods not available)");
					}
					UpdateTidalBlessingViaCropObjects(instance, maxHealth, num);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("[TidalBlessing] Error: " + ex.Message));
					ManualLogSource log12 = Plugin.Log;
					if (log12 != null)
					{
						log12.LogWarning((object)("[TidalBlessing] Stack: " + ex.StackTrace));
					}
				}
			}
		}

		private void UpdateTidalBlessingViaTileManager(Player player, float maxHP, float currentHP)
		{
			//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_0199: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: 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) });
							if (Vector2.Distance(val2, new Vector2(val3.x, val3.y)) <= (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)
				{
					if ((currentHP - num * (float)(num3 + 1)) / maxHP * 100f <= AbilityConfig.TidalBlessingHPThreshold.Value)
					{
						break;
					}
					try
					{
						object obj2 = _waterTileMethod.Invoke(_tileManagerInstance, new object[2] { item3, num2 });
						int num6;
						if (obj2 is bool)
						{
							flag2 = (bool)obj2;
							num6 = 1;
						}
						else
						{
							num6 = 0;
						}
						if (((uint)num6 & (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 num7 = num * (float)num3;
					float num8 = currentHP - num7;
					if (num8 < 1f)
					{
						num8 = 1f;
					}
					ReflectionHelper.SetInstanceValue(player, "Health", num8);
					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_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: 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_00de: 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 || Vector2.Distance(new Vector2(position.x, position.y), new Vector2(val2.transform.position.x, val2.transform.position.y)) > (float)num)
				{
					continue;
				}
				object instanceValue = ReflectionHelper.GetInstanceValue(val, "data");
				if (instanceValue == null || !ReflectionHelper.TryGetValue(instanceValue, "watered", defaultValue: false))
				{
					if ((currentHP - num3 * (float)(num2 + 1)) / maxHP * 100f <= AbilityConfig.TidalBlessingHPThreshold.Value)
					{
						break;
					}
					MethodInfo method = ((object)val).GetType().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 num4 = num3 * (float)num2;
				float num5 = currentHP - num4;
				if (num5 < 1f)
				{
					num5 = 1f;
				}
				ReflectionHelper.SetInstanceValue(player, "Health", num5);
				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);
				if (num2 / maxMana * 100f <= AbilityConfig.InfernalForgeManaThreshold.Value)
				{
					return;
				}
				int num3 = AbilityConfig.InfernalForgeOrePerBar.Value;
				if (num3 < 1)
				{
					num3 = 3;
				}
				MethodInfo addItemIntMethod = AbilityPatches.GetAddItemIntMethod(instanceValue);
				int num4 = 0;
				float num5 = 0f;
				foreach (KeyValuePair<int, int> item in AbilityPatches.OreToBarMap)
				{
					int key = item.Key;
					int value = item.Value;
					int inventoryAmount = GetInventoryAmount(instanceValue, key);
					if (inventoryAmount < num3)
					{
						continue;
					}
					float value2;
					float num6 = (AbilityPatches.OreManaCostMap.TryGetValue(key, out value2) ? value2 : 3f);
					float num7 = maxMana * (num6 / 100f);
					int num8 = inventoryAmount / num3;
					int num9 = num8 * num3;
					float num10 = num7 * (float)num8;
					if ((num2 - num5 - num10) / maxMana * 100f < AbilityConfig.InfernalForgeManaThreshold.Value)
					{
						num8 = 0;
						for (int num11 = inventoryAmount / num3; num11 >= 1; num11--)
						{
							float num12 = num7 * (float)num11;
							if ((num2 - num5 - num12) / maxMana * 100f >= AbilityConfig.InfernalForgeManaThreshold.Value)
							{
								num8 = num11;
								break;
							}
						}
						if (num8 <= 0)
						{
							continue;
						}
						num9 = num8 * num3;
						num10 = num7 * (float)num8;
					}
					if (!RemoveInventoryItem(instanceValue, key, num9))
					{
						ManualLogSource log2 = Plugin.Log;
						if (log2 != null)
						{
							log2.LogWarning((object)$"[InfernalForge] Failed to remove {num9}x ore {key}");
						}
						continue;
					}
					bool flag = false;
					if (addItemIntMethod != null)
					{
						flag = AbilityPatches.InvokeAddItem(addItemIntMethod, instanceValue, value, num8, notify: true);
					}
					if (!flag)
					{
						ManualLogSource log3 = Plugin.Log;
						if (log3 != null)
						{
							log3.LogError((object)$"[InfernalForge] Bar addition failed for {num8}x bar {value} — returning ore");
						}
						AddInventoryItem(instanceValue, key, num9);
						continue;
					}
					num4 += num8;
					num5 += num10;
					ManualLogSource log4 = Plugin.Log;
					if (log4 != null)
					{
						log4.LogInfo((object)$"[InfernalForge] Smelted {num9}x ore {key} → {num8}x bar {value} (cost: {num6}% per bar)");
					}
				}
				if (num4 > 0)
				{
					float num13 = num2 - num5;
					if (num13 < 0f)
					{
						num13 = 0f;
					}
					ReflectionHelper.SetInstanceValue(instance, "Mana", num13);
					AbilityPatches.SendGameNotification($"Infernal Forge: Smelted {num4} bar(s) (-{num5:F1} Mana)");
					ManualLogSource log5 = Plugin.Log;
					if (log5 != null)
					{
						log5.LogInfo((object)$"[InfernalForge] Total: {num4} bars, {num5: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 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)
				{
					return (int)obj;
				}
			}
			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();
			if (!text.Contains("mine") && !text.Contains("dungeon") && !text.Contains("cave") && !text.Contains("underground") && !text.Contains("nelvari"))
			{
				return text.Contains("withergate");
			}
			return true;
		}

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

		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;
				}
				return ReflectionHelper.TryGetValue(instance, "health", maxHealth) / maxHealth;
			}
			catch
			{
				return 1f;
			}
		}

		private void CheckAbilityToggleHotkey()
		{
			//IL_0000: 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", 
				_ => null, 
			};
		}

		private static string GetAbilityDisplayName(string abilityKey)
		{
			if (!(abilityKey == "InfernalForge"))
			{
				if (abilityKey == "TidalBlessing")
				{
					return "Tidal Blessing";
				}
				return abilityKey;
			}
			return "Infernal Forge";
		}
	}
	[BepInPlugin("com.azraelgodking.havensbirthright", "Haven's Birthright", "1.2.0")]
	public class Plugin : BaseUnityPlugin
	{
		internal static KeyCode StaticAbilityToggleKey = (KeyCode)290;

		private Harmony _harmony;

		private RacialBonusManager _racialBonusManager;

		private ConfigEntry<bool> _checkForUpdates;

		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_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_03b2: 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.2.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");
				_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)
					{
						PatchMethodPrefix(type, "AddFriendship", typeof(EconomyPatches), "ModifyRelationshipGain", new Type[1] { typeof(int) });
					}
					else
					{
						Log.LogWarning((object)"Could not find NPCAI type - relationship bonuses will not work");
					}
					Type type2 = AccessTools.TypeByName("Wish.ShopMenu");
					if (type2 != null)
					{
						PatchMethodPrefix(type2, "BuyItem", typeof(EconomyPatches), "ModifyBuyPrice");
					}
					else
					{
						Log.LogWarning((object)"Could not find ShopMenu type - shop discounts will not work");
					}
					PatchMethodPrefix(typeFromHandle, "AddMana", typeof(AbilityPatches), "OnPlayerAddManaPrefix");
					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.2.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}");
			}
			catch (Exception arg2)
			{
				Log.LogError((object)string.Format("Failed to load {0}: {1}", "Haven's Birthright", arg2));
			}
		}

		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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: 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_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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));
			}
		}

		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.2.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)
			{
				if (!(Value >= 0f))
				{
					return $"{Value}%";
				}
				return $"+{Value}%";
			}
			if (!(Value >= 0f))
			{
				return $"{Value}";
			}
			return $"+{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)
			{
				_currentPlayerRace = variant switch
				{
					ElementalVariant.Fire => Race.FireElemental, 
					ElementalVariant.Water => Race.WaterElemental, 
					_ => Race.Elemental, 
				};
			}
			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()
		{
			if (_currentPlayerRace.GetValueOrDefault() != Race.Elemental && _currentPlayerRace.GetValueOrDefault() != Race.FireElemental)
			{
				return _currentPlayerRace.GetValueOrDefault() == Race.WaterElemental;
			}
			return true;
		}

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

		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;
			}
			foreach (RacialBonus item in GetBonusesForRace(_currentPlayerRace.Value))
			{
				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", 10f, "Percentage bonus to defense");
			NagaManaRegenBonus = config.Bind<float>("Naga", "ManaRegenBonus", 15f, "Percentage bonus to mana regeneration");
			NagaAirSkipBonus = config.Bind<float>("Naga", "AirSkipChanceBonus", 15f, "Percentage chance to skip air requirement while swimming/fishing");
			ElfWoodcuttingDamageBonus = config.Bind<float>("Elf", "WoodcuttingDamageBonus", 15f, "Percentage bonus to tree damage");
			HumanCommunityTokenBonus = config.Bind<float>("Human", "CommunityTokenBonus", 10f, "Percentage bonus to daily community token gain");
			DemonTripleGoldBonus = config.Bind<float>("Demon", "TripleGoldChanceBonus", 10f, "Percentage chance for triple gold drops");
			DemonMiningDamageBonus = config.Bind<float>("Demon", "MiningDamageBonus", 15f, "Percentage bonus to mining damage");
			AngelSpellAttackSpeedBonus = config.Bind<float>("Angel", "SpellAttackSpeedBonus", 15f, "Percentage bonus to spell casting speed");
			WaterElementalFishingMinigameBonus = config.Bind<float>("Water Elemental", "FishingMinigameSpeedBonus", 20f, "Percentage bonus to fishing minigame speed");
			WaterElementalAirSkipBonus = config.Bind<float>("Water Elemental", "AirSkipChanceBonus", 20f, "Percentage chance to skip air requirement while swimming/fishing");
			AmariAquaticFishingMinigameBonus = config.Bind<float>("Amari Aquatic", "FishingMinigameSpeedBonus", 15f, "Percentage bonus to fishing minigame speed");
			AmariAquaticAirSkipBonus = config.Bind<float>("Amari Aquatic", "AirSkipChanceBonus", 15f, "Percentage chance to skip air requirement while swimming/fishing");
			Plugin.Log.LogInfo((object)"Configuration initialized");
		}
	}
}
namespace HavensBirthright.Patches
{
	public static class AbilityPatches
	{
		internal static readonly Dictionary<int, int> OreToBarMap = new Dictionary<int, int>
		{
			{ 1100, 1200 },
			{ 1101, 1201 },
			{ 1102, 1202 },
			{ 1103, 1203 },
			{ 1104, 1204 },
			{ 1105, 1205 },
			{ 1107, 1206 },
			{ 1108, 1207 }
		};

		internal static readonly Dictionary<int, float> OreManaCostMap = new Dictionary<int, float>
		{
			{ 1100, 1f },
			{ 1101, 2f },
			{ 1105, 3f },
			{ 1102, 4f },
			{ 1103, 5f },
			{ 1104, 6f },
			{ 1107, 7f },
			{ 1108, 8f }
		};

		private static bool _reflectionInitialized;

		private static bool _reflectionInitFailed;

		private static int _reflectionInitAttempts;

		private const int MAX_REFLECTION_INIT_ATTEMPTS = 3;

		private static FieldInfo _cachedItemIdField;

		private static PropertyInfo _cachedItemIdProperty;

		private static MethodInfo _cachedItemIdMethod;

		private static MethodInfo _cachedAddItemIntMethod;

		private static int _cachedAddItemArgCount;

		private static bool _isSmeltingItem;

		private static bool _genericElementalWarningLogged;

		private static object _notificationStackInstance;

		private static MethodInfo _sendNotificationMethod;

		private static bool _notificationInitialized;

		private static bool IsVerbose
		{
			get
			{
				if (AbilityConfig.InfernalForgeVerboseLogging != null)
				{
					return AbilityConfig.InfernalForgeVerboseLogging.Value;
				}
				return false;
			}
		}

		private static void InitializeNotificationSystem()
		{
			if (_notificationInitialized)
			{
				return;
			}
			_notificationInitialized = true;
			try
			{
				Type type = AccessTools.TypeByName("Wish.NotificationStack");
				if (type == null)
				{
					ManualLogSource log = Plugin.Log;
					if (log != null)
					{
						log.LogWarning((object)"[AbilityPatches] NotificationStack type not found");
					}
					return;
				}
				_sendNotificationMethod = AccessTools.Method(type, "SendNotification", new Type[5]
				{
					typeof(string),
					typeof(int),
					typeof(int),
					typeof(bool),
					typeof(bool)
				}, (Type[])null);
				if (_sendNotificationMethod == null)
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)"[AbilityPatches] SendNotification(string,int,int,bool,bool) not found");
					}
					return;
				}
				TryGetNotificationInstance(type);
				ManualLogSource log3 = Plugin.Log;
				if (log3 != null)
				{
					log3.LogInfo((object)("[AbilityPatches] Notification system initialized" + ((_notificationStackInstance != null) ? " (instance ready)" : " (instance deferred)")));
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log4 = Plugin.Log;
				if (log4 != null)
				{
					log4.LogWarning((object)("[AbilityPatches] Notification init failed: " + ex.Message));
				}
			}
		}

		private static void TryGetNotificationInstance(Type notifStackType = null)
		{
			if (_notificationStackInstance != null)
			{
				return;
			}
			try
			{
				if (notifStackType == null)
				{
					notifStackType = AccessTools.TypeByName("Wish.NotificationStack");
				}
				if (notifStackType == null)
				{
					return;
				}
				Type type = AccessTools.TypeByName("Wish.SingletonBehaviour`1");
				if (type != null)
				{
					PropertyInfo propertyInfo = AccessTools.Property(type.MakeGenericType(notifStackType), "Instance");
					if (propertyInfo != null)
					{
						_notificationStackInstance = propertyInfo.GetValue(null);
						if (_notificationStackInstance != null)
						{
							return;
						}
					}
				}
				_notificationStackInstance = ReflectionHelper.GetSingletonInstance(notifStackType);
				if (_notificationStackInstance == null)
				{
					MethodInfo method = typeof(Object).GetMethod("FindObjectOfType", Type.EmptyTypes);
					if (method != null)
					{
						_notificationStackInstance = method.MakeGenericMethod(notifStackType).Invoke(null, null);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[AbilityPatches] Notification instance lookup failed: " + ex.Message));
				}
			}
		}

		internal static void SendGameNotification(string text, int id = 0, int amount = 0, bool unique = false, bool error = false)
		{
			try
			{
				if (!_notificationInitialized)
				{
					InitializeNotificationSystem();
				}
				if (!(_sendNotificationMethod == null))
				{
					if (_notificationStackInstance == null)
					{
						TryGetNotificationInstance();
					}
					if (_notificationStackInstance != null)
					{
						_sendNotificationMethod.Invoke(_notificationStackInstance, new object[5] { text, id, amount, unique, error });
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("[AbilityPatches] Notification send failed: " + ex.Message));
				}
			}
		}

		private static void InitializeReflectionCache()
		{
			if (_reflectionInitialized || _reflectionInitFailed)
			{
				return;
			}
			_reflectionInitAttempts++;
			if (_reflectionInitAttempts > 3)
			{
				_reflectionInitFailed = true;
				ManualLogSource log = Plugin.Log;
				if (log != null)
				{
					log.LogError((object)$"[AbilityPatches] Reflection init failed after {3} attempts - giving up");
				}
				return;
			}
			try
			{
				Type type = AccessTools.TypeByName("Wish.Item");
				if (type == null)
				{
					ManualLogSource log2 = Plugin.Log;
					if (log2 != null)
					{
						log2.LogWarning((object)$"[AbilityPatches] Could not find Wish.Item type (attempt {_reflectionInitAttempts}/{3})");
					}
					return;
				}
				string[] array = new string[6] { "id", "_id", "itemId", "_itemId", "ItemId", "m_id" };
				foreach (string text in array)
				{
					_cachedItemIdField = AccessTools.Field(type, text);
					if (_cachedItemIdField != null)
					{
						break;
					}
				}
				array = new string[6] { "id", "Id", "ID", "ItemID", "itemId", "ItemId" };
				foreach (string text2 in array)
				{
					_cachedItemIdProperty = AccessTools.Property(type, text2);
					if (_cachedItemIdProperty != null)
					{
						break;
					}
				}
				array = new string[5] { "ID", "GetID", "GetId", "GetItemID", "GetItemId" };
				foreach (string text3 in array)
				{
					_cachedItemIdMethod = AccessTools.Method(type, text3, (Type[])null, (Type[])null);
					if (_cachedItemIdMethod != null)
					{
						break;
					}
				}
				if (_cachedItemIdField == null && _cachedItemIdProperty == null && _cachedItemIdMethod == null)
				{
					ManualLogSource log3 = Plugin.Log;
					if (log3 != null)
					{
						log3.LogWarning((object)$"[AbilityPatches] ALL Item ID lookups failed (attempt {_reflectionInitAttempts}/{3})! Enumerating Wish.Item members containing 'id':");
					}
					MemberInfo[] members = type.GetMembers(ReflectionHelper.AllBindingFlags);
					foreach (MemberInfo memberInfo in members)
					{
						if (memberInfo.Name.IndexOf("id", StringComparison.OrdinalI