Decompiled source of ToolOfTheTrade v1.0.0

ToolOfTheTrade.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BeastRunes.Registration;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using ItemManager;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ToolOfTheTrade")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Yggdrah")]
[assembly: AssemblyProduct("ToolOfTheTrade")]
[assembly: AssemblyCopyright("Copyright \ufffd  2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("E0E2F92E-557C-4A05-9D89-AA92A0BD75C4")]
[assembly: AssemblyFileVersion("1.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BeastRunes
{
	[BepInPlugin("Yggdrah.ToolOfTheTrade", "ToolOfTheTrade", "1.0.0")]
	public class BeastRunesPlugin : BaseUnityPlugin
	{
		private class ConfigurationManagerAttributes
		{
			public int? Order;

			public bool? Browsable;

			public string? Category;

			public Action<ConfigEntryBase>? CustomDrawer;
		}

		private class AcceptableShortcuts : AcceptableValueBase
		{
			public AcceptableShortcuts()
				: base(typeof(KeyboardShortcut))
			{
			}

			public override object Clamp(object value)
			{
				return value;
			}

			public override bool IsValid(object value)
			{
				return true;
			}

			public override string ToDescriptionString()
			{
				return "# Acceptable values: " + string.Join(", ", UnityInput.Current.SupportedKeyCodes);
			}
		}

		internal const string ModName = "ToolOfTheTrade";

		internal const string ModVersion = "1.0.0";

		internal const string Author = "Yggdrah";

		private const string ModGUID = "Yggdrah.ToolOfTheTrade";

		private readonly Harmony _harmony = new Harmony("Yggdrah.ToolOfTheTrade");

		internal static BeastRunesPlugin? context;

		public static readonly ManualLogSource BeastRunesLogger = Logger.CreateLogSource("ToolOfTheTrade");

		public const string bundleName = "beastrunes";

		private static readonly Dictionary<string, AssetBundle> _assetBundleCache = new Dictionary<string, AssetBundle>();

		private static readonly string ConfigFileName = "Yggdrah.ToolOfTheTrade.cfg";

		private static readonly string ConfigFileFullPath;

		private static readonly ConfigSync ConfigSync;

		internal static string ConnectionError;

		private static ConfigEntry<bool>? _serverConfigLocked;

		private const long RELOAD_DELAY = 10000000L;

		private FileSystemWatcher? _watcher;

		private DateTime _lastConfigReloadTime;

		private readonly object _reloadLock = new object();

		public void Awake()
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			context = this;
			_serverConfigLocked = config("01 - ServerSync", "01 - Lock Configuration", value: true, "Whether to enable or disable if the configuration is locked in which only server admins can change");
			ConfigSync.AddLockingConfigEntry<bool>(_serverConfigLocked);
			Config.RegisterAllConfigs(((BaseUnityPlugin)this).Config, "Yggdrah.ToolOfTheTrade");
			Assembly executingAssembly = Assembly.GetExecutingAssembly();
			_harmony.PatchAll(executingAssembly);
			SetupWatcher();
			BeastRunes.Registration.Item.RegisterAll("beastrunes");
			((BaseUnityPlugin)this).Config.Save();
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
			BeastRunesLogger.LogInfo((object)"ToolOfTheTrade 1.0.0 loaded successfully.");
			BeastRunesLogger.LogInfo((object)"Using AssetBundle: beastrunes");
		}

		private void OnDestroy()
		{
			SaveWithRespectToConfigSet();
			_watcher?.Dispose();
			_harmony.UnpatchSelf();
		}

		public static AssetBundle? GetAssetBundle(string filename)
		{
			if (string.IsNullOrEmpty(filename))
			{
				BeastRunesLogger.LogWarning((object)"Attempted to load an AssetBundle with an empty name.");
				return null;
			}
			if (_assetBundleCache.TryGetValue(filename, out AssetBundle value))
			{
				return value;
			}
			AssetBundle val = ((IEnumerable<AssetBundle>)Resources.FindObjectsOfTypeAll<AssetBundle>()).FirstOrDefault((Func<AssetBundle, bool>)((AssetBundle a) => ((Object)a).name == filename));
			if ((Object)(object)val != (Object)null)
			{
				_assetBundleCache[filename] = val;
				return val;
			}
			try
			{
				Assembly executingAssembly = Assembly.GetExecutingAssembly();
				string text = executingAssembly.GetManifestResourceNames().FirstOrDefault((string str) => str.EndsWith(filename, StringComparison.OrdinalIgnoreCase));
				if (text == null)
				{
					BeastRunesLogger.LogError((object)("Could not find embedded AssetBundle resource ending with '" + filename + "'."));
					return null;
				}
				using Stream stream = executingAssembly.GetManifestResourceStream(text);
				if (stream == null)
				{
					BeastRunesLogger.LogError((object)("Could not open AssetBundle resource '" + text + "'."));
					return null;
				}
				AssetBundle val2 = AssetBundle.LoadFromStream(stream);
				if ((Object)(object)val2 == (Object)null)
				{
					BeastRunesLogger.LogError((object)("AssetBundle.LoadFromStream failed for '" + filename + "'."));
					return null;
				}
				_assetBundleCache[filename] = val2;
				BeastRunesLogger.LogInfo((object)("Loaded AssetBundle '" + filename + "' from '" + text + "'."));
				return val2;
			}
			catch (Exception arg)
			{
				BeastRunesLogger.LogError((object)$"Failed loading AssetBundle '{filename}': {arg}");
				return null;
			}
		}

		private void SetupWatcher()
		{
			_watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName);
			_watcher.Changed += ReadConfigValues;
			_watcher.Created += ReadConfigValues;
			_watcher.Renamed += ReadConfigValues;
			_watcher.IncludeSubdirectories = true;
			_watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject;
			_watcher.EnableRaisingEvents = true;
		}

		private void ReadConfigValues(object sender, FileSystemEventArgs e)
		{
			DateTime now = DateTime.Now;
			long num = now.Ticks - _lastConfigReloadTime.Ticks;
			if (num < 10000000)
			{
				return;
			}
			lock (_reloadLock)
			{
				if (!File.Exists(ConfigFileFullPath))
				{
					BeastRunesLogger.LogWarning((object)"Config file does not exist. Skipping reload.");
					return;
				}
				try
				{
					BeastRunesLogger.LogDebug((object)"Reloading configuration...");
					SaveWithRespectToConfigSet(reload: true);
					BeastRunesLogger.LogInfo((object)"Configuration reload complete.");
				}
				catch (Exception ex)
				{
					BeastRunesLogger.LogError((object)("Error reloading configuration: " + ex.Message));
				}
			}
			_lastConfigReloadTime = now;
		}

		private void SaveWithRespectToConfigSet(bool reload = false)
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			if (reload)
			{
				((BaseUnityPlugin)this).Config.Reload();
			}
			((BaseUnityPlugin)this).Config.Save();
			if (saveOnConfigSet)
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		internal ConfigEntry<T> config<T>(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, description.Tags);
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			SyncedConfigEntry<T> syncedConfigEntry = ConfigSync.AddConfigEntry<T>(val2);
			syncedConfigEntry.SynchronizedConfig = synchronizedSetting;
			return val2;
		}

		internal ConfigEntry<T> config<T>(string group, string name, T value, string description, bool synchronizedSetting = true)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			return config(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting);
		}

		static BeastRunesPlugin()
		{
			string configPath = Paths.ConfigPath;
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName;
			ConfigSync = new ConfigSync("Yggdrah.ToolOfTheTrade")
			{
				DisplayName = "ToolOfTheTrade",
				CurrentVersion = "1.0.0",
				MinimumRequiredVersion = "1.0.0",
				ModRequired = false
			};
			ConnectionError = "";
		}
	}
	public static class KeyboardExtensions
	{
		public static bool IsKeyDown(this KeyboardShortcut shortcut)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return (int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey) && ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey);
		}

		public static bool IsKeyHeld(this KeyboardShortcut shortcut)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return (int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey) && ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func<KeyCode, bool>)Input.GetKey);
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	public static class RegisterAndCheckVersion
	{
		private static void Prefix(ZNetPeer peer, ref ZNet __instance)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			peer.m_rpc.Register<ZPackage>("ToolOfTheTrade_VersionCheck", (Action<ZRpc, ZPackage>)RpcHandlers.RPC_WaitForMe_Version);
			ZPackage val = new ZPackage();
			val.Write("1.0.0");
			peer.m_rpc.Invoke("ToolOfTheTrade_VersionCheck", new object[1] { val });
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
	public static class VerifyClient
	{
		private static bool Prefix(ZRpc rpc, ZPackage pkg, ref ZNet __instance)
		{
			if (!__instance.IsServer() || RpcHandlers.ValidatedPeers.Contains(rpc))
			{
				return true;
			}
			rpc.Invoke("Error", new object[1] { 3 });
			return false;
		}

		private static void Postfix(ZNet __instance)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "RequestAdminSync", new object[1] { (object)new ZPackage() });
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
	public class ShowConnectionError
	{
		private static void Postfix(FejdStartup __instance)
		{
			if (__instance.m_connectionFailedPanel.activeSelf)
			{
				__instance.m_connectionFailedError.fontSizeMax = 25f;
				__instance.m_connectionFailedError.fontSizeMin = 15f;
				TMP_Text connectionFailedError = __instance.m_connectionFailedError;
				connectionFailedError.text = connectionFailedError.text + "\n" + BeastRunesPlugin.ConnectionError;
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "Disconnect")]
	public static class RemoveDisconnectedPeerFromVerified
	{
		private static void Prefix(ZNetPeer peer, ref ZNet __instance)
		{
			if (__instance.IsServer())
			{
				RpcHandlers.ValidatedPeers.Remove(peer.m_rpc);
			}
		}
	}
	public static class RpcHandlers
	{
		public static readonly List<ZRpc> ValidatedPeers = new List<ZRpc>();

		public static void RPC_WaitForMe_Version(ZRpc rpc, ZPackage pkg)
		{
			string text = pkg.ReadString();
			if (text != "1.0.0")
			{
				BeastRunesPlugin.ConnectionError = "ToolOfTheTrade Installed: 1.0.0\n Needed: " + text;
				if (ZNet.instance.IsServer())
				{
					rpc.Invoke("Error", new object[1] { 3 });
				}
			}
			else if (ZNet.instance.IsServer())
			{
				ValidatedPeers.Add(rpc);
			}
		}
	}
}
namespace BeastRunes.Registration
{
	public static class Config
	{
		public static ConfigEntry<T> LocalConfig<T>(string section, string key, T defaultValue, string description)
		{
			if ((Object)(object)BeastRunesPlugin.context == (Object)null)
			{
				return null;
			}
			return ((BaseUnityPlugin)BeastRunesPlugin.context).Config.Bind<T>(section, key, defaultValue, description);
		}

		public static ConfigEntry<T> LocalConfig<T>(string section, string key, T defaultValue, ConfigDescription description)
		{
			if ((Object)(object)BeastRunesPlugin.context == (Object)null || ((BaseUnityPlugin)BeastRunesPlugin.context).Config == null)
			{
				return null;
			}
			return ((BaseUnityPlugin)BeastRunesPlugin.context).Config.Bind<T>(section, key, defaultValue, description);
		}

		public static ConfigEntry<T> SyncConfig<T>(string section, string key, T defaultValue, string description)
		{
			if ((Object)(object)BeastRunesPlugin.context == (Object)null)
			{
				return null;
			}
			return BeastRunesPlugin.context.config(section, key, defaultValue, description);
		}

		public static void RegisterAllConfigs(ConfigFile config, string modGuid)
		{
		}
	}
	public static class Item
	{
		public static ItemManager.Item? GoldBar_Yggg { get; private set; }

		public static ItemManager.Item? KnifeChitinGold { get; private set; }

		public static ItemManager.Item? KnifeSkollAndHatiGold { get; private set; }

		public static ItemManager.Item? Axe_Eikthyr_Ygg { get; private set; }

		public static ItemManager.Item? Golden_Hammer_Ygg { get; private set; }

		public static ItemManager.Item? Axe_Yggdrah_Ygg { get; private set; }

		public static ItemManager.Item? Belt_Gold_Ygg { get; private set; }

		public static ItemManager.Item? Belt_Silver_Ygg { get; private set; }

		public static ItemManager.Item? BeltOdin_Ygg { get; private set; }

		public static ItemManager.Item? CapeOdin_Ygg { get; private set; }

		public static ItemManager.Item? HelmetOdin_Ygg { get; private set; }

		public static ItemManager.Item? Ygg_pants { get; private set; }

		public static ItemManager.Item? DragonSlayer_Helm_Ygg { get; private set; }

		public static ItemManager.Item? DragonSlayer_Chest_Ygg { get; private set; }

		public static ItemManager.Item? DragonSlayer_Legs_Ygg { get; private set; }

		public static ItemManager.Item? KnifeStone_Ygg { get; private set; }

		public static void RegisterAll(string bundleName)
		{
			RegisterItems(bundleName);
		}

		private static void RegisterItems(string bundleName)
		{
			GoldBar_Yggg = new ItemManager.Item(bundleName, "GoldBar_Yggg");
			GoldBar_Yggg.Name.English("Gold Bar");
			GoldBar_Yggg.Crafting.Add(CraftingTable.Forge, 1);
			GoldBar_Yggg.RequiredItems.Add("Coins", 999);
			GoldBar_Yggg.CraftAmount = 1;
			KnifeChitinGold = new ItemManager.Item(bundleName, "KnifeChitinGold");
			KnifeChitinGold.Name.English("Golden Abyssal Razor. (Indestructable)");
			KnifeChitinGold.Crafting.Add(CraftingTable.Forge, 1);
			KnifeChitinGold.RequiredItems.Add("KnifeChitin", 1);
			KnifeChitinGold.RequiredItems.Add("GoldBar_Yggg", 1);
			KnifeChitinGold.CraftAmount = 1;
			KnifeSkollAndHatiGold = new ItemManager.Item(bundleName, "KnifeSkollAndHatiGold");
			KnifeSkollAndHatiGold.Name.English("Golden Skoll and Hati. (Indestructable)");
			KnifeSkollAndHatiGold.Crafting.Add(CraftingTable.BlackForge, 1);
			KnifeSkollAndHatiGold.RequiredItems.Add("KnifeChitinGold", 1);
			KnifeSkollAndHatiGold.RequiredItems.Add("KnifeSkollAndHati", 1);
			KnifeSkollAndHatiGold.RequiredItems.Add("GoldBar_Yggg", 2);
			KnifeSkollAndHatiGold.CraftAmount = 1;
			Axe_Eikthyr_Ygg = new ItemManager.Item(bundleName, "Axe_Eikthyr_Ygg");
			Axe_Eikthyr_Ygg.Name.English("Forsaken Golden Artifact Tool. (Indestructable)");
			Golden_Hammer_Ygg = new ItemManager.Item(bundleName, "Golden_Hammer_Ygg");
			Golden_Hammer_Ygg.Name.English("Golden Indestructable Hammer");
			KnifeStone_Ygg = new ItemManager.Item(bundleName, "KnifeStone_Ygg");
			KnifeStone_Ygg.Name.English("Stone Knife");
			KnifeStone_Ygg.Crafting.Add(CraftingTable.Inventory, 0);
			KnifeStone_Ygg.RequiredItems.Add("Stone", 3);
			KnifeStone_Ygg.CraftAmount = 1;
			Axe_Yggdrah_Ygg = new ItemManager.Item(bundleName, "Axe_Yggdrah_Ygg");
			Axe_Yggdrah_Ygg.Name.English("Yggdrah's Golden Dual Axe");
			Belt_Gold_Ygg = new ItemManager.Item(bundleName, "Belt_Gold_Ygg");
			Belt_Gold_Ygg.Name.English("Golden Artifact Belt. (Indestructable)");
			Belt_Silver_Ygg = new ItemManager.Item(bundleName, "Belt_Silver_Ygg");
			Belt_Silver_Ygg.Name.English("Silver Megingjord");
			Belt_Silver_Ygg.Crafting.Add(CraftingTable.Forge, 1);
			Belt_Silver_Ygg.RequiredItems.Add("BeltStrength", 1);
			Belt_Silver_Ygg.RequiredItems.Add("TrophyGoblinKing", 1);
			Belt_Silver_Ygg.RequiredItems.Add("Silver", 5);
			Belt_Silver_Ygg.CraftAmount = 1;
			BeltOdin_Ygg = new ItemManager.Item(bundleName, "BeltOdin_Ygg");
			BeltOdin_Ygg.Name.English("Belt of Odin");
			CapeOdin_Ygg = new ItemManager.Item(bundleName, "CapeOdin_Ygg");
			CapeOdin_Ygg.Name.English("Cape of Odin");
			HelmetOdin_Ygg = new ItemManager.Item(bundleName, "HelmetOdin_Ygg");
			HelmetOdin_Ygg.Name.English("Hood of Odin");
			Ygg_pants = new ItemManager.Item(bundleName, "Ygg_pants");
			Ygg_pants.Name.English("Golden Indestructable Armor");
			DragonSlayer_Helm_Ygg = new ItemManager.Item(bundleName, "DragonSlayer_Helm_Ygg");
			DragonSlayer_Helm_Ygg.Name.English("Golden Indestructable Armor");
			DragonSlayer_Chest_Ygg = new ItemManager.Item(bundleName, "DragonSlayer_Chest_Ygg");
			DragonSlayer_Chest_Ygg.Name.English("Golden Indestructable Armor");
			DragonSlayer_Legs_Ygg = new ItemManager.Item(bundleName, "DragonSlayer_Legs_Ygg");
			DragonSlayer_Legs_Ygg.Name.English("Golden Indestructable Armor");
		}
	}
}
namespace BeastRunes.Patches
{
	internal static class GatherForagingSystem
	{
		private const float DefaultGatherDuration = 1.5f;

		private const float CopperKnifeDuration = 1.2f;

		private const float AbyssalRazorDuration = 1f;

		private const float GoldenAbyssalRazorDuration = 0.75f;

		private const float SilverKnifeDuration = 0.65f;

		private const float BlackMetalKnifeDuration = 0.5f;

		private const float SkollAndHatiDuration = 0.01f;

		private const float GoldenSkollAndHatiDuration = 0.01f;

		private const float MaxGatherDistance = 2.4f;

		private const float MoveCancelDistance = 0.3f;

		private const float MoveCancelThresholdSqr = 0.01f;

		private static ForageWorkSession _session;

		private static ItemData _gatherKnife;

		private static ItemData _originalWeapon;

		private static bool _knifeWasAutoEquipped;

		private static bool _equipmentCaptured;

		private static bool _knifeCheckedOutFromToolSlot;

		internal static bool AllowOriginalInteract { get; private set; }

		public static bool Active => _session != null && _session.Active;

		public static bool IsTarget(Pickable pickable)
		{
			return Active && _session != null && (Object)(object)_session.Target == (Object)(object)pickable;
		}

		public static bool IsSupportedForage(Pickable pickable)
		{
			if ((Object)(object)pickable == (Object)null || (Object)(object)((Component)pickable).gameObject == (Object)null)
			{
				return false;
			}
			string cleanName = GetCleanName(((Object)((Component)pickable).gameObject).name);
			if (string.IsNullOrEmpty(cleanName))
			{
				return false;
			}
			return Matches(cleanName, "RaspberryBush") || Matches(cleanName, "BlueberryBush") || Matches(cleanName, "CloudberryBush") || Matches(cleanName, "Pickable_Raspberry") || Matches(cleanName, "Pickable_Blueberry") || Matches(cleanName, "Pickable_Cloudberry") || Matches(cleanName, "Mushroom") || Matches(cleanName, "MushroomYellow") || Matches(cleanName, "MushroomBlue") || Matches(cleanName, "Pickable_Mushroom") || Matches(cleanName, "Pickable_MushroomYellow") || Matches(cleanName, "Pickable_MushroomBlue") || Matches(cleanName, "Dandelion") || Matches(cleanName, "Pickable_Dandelion") || Matches(cleanName, "Thistle") || Matches(cleanName, "Pickable_Thistle");
		}

		private static bool Matches(string actual, string expected)
		{
			return string.Equals(actual, expected, StringComparison.OrdinalIgnoreCase);
		}

		private static string GetCleanName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return string.Empty;
			}
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			if (num >= 0)
			{
				name = name.Substring(0, num);
			}
			return name.Trim();
		}

		private static string GetKnifePrefabName(ItemData knife)
		{
			if (knife == null)
			{
				return string.Empty;
			}
			try
			{
				if ((Object)(object)knife.m_dropPrefab != (Object)null)
				{
					return GetCleanName(((Object)knife.m_dropPrefab).name);
				}
			}
			catch
			{
			}
			return string.Empty;
		}

		private static float GetGatherDurationForKnife(ItemData knife)
		{
			if (IsSkollAndHatiKnife(knife))
			{
				string knifePrefabName = GetKnifePrefabName(knife);
				return Matches(knifePrefabName, "KnifeSkollAndHatiGold") ? 0.01f : 0.01f;
			}
			string knifePrefabName2 = GetKnifePrefabName(knife);
			if (Matches(knifePrefabName2, "KnifeCopper"))
			{
				return 1.2f;
			}
			if (Matches(knifePrefabName2, "KnifeChitin"))
			{
				return 1f;
			}
			if (Matches(knifePrefabName2, "KnifeChitinGold"))
			{
				return 0.75f;
			}
			if (Matches(knifePrefabName2, "KnifeSilver"))
			{
				return 0.65f;
			}
			if (Matches(knifePrefabName2, "KnifeBlackMetal"))
			{
				return 0.5f;
			}
			if (Matches(knifePrefabName2, "KnifeSkollaAndHati"))
			{
				return 0.01f;
			}
			if (Matches(knifePrefabName2, "KnifeSkollAndHatiGold"))
			{
				return 0.01f;
			}
			return 1.5f;
		}

		private static bool IsSkollAndHatiKnife(ItemData knife)
		{
			if (knife == null)
			{
				return false;
			}
			string knifePrefabName = GetKnifePrefabName(knife);
			if (Matches(knifePrefabName, "KnifeSkollaAndHati") || Matches(knifePrefabName, "KnifeSkollAndHatiGold"))
			{
				return true;
			}
			try
			{
				string text = ((knife.m_shared != null) ? knife.m_shared.m_name : string.Empty);
				if (!string.IsNullOrEmpty(text))
				{
					string text2 = text.ToLowerInvariant();
					return text2.Contains("skoll") && text2.Contains("hati");
				}
			}
			catch
			{
			}
			return false;
		}

		public static ItemData GetEquippedKnife(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			return IsUsableKnife(currentWeapon) ? currentWeapon : null;
		}

		public static ItemData FindUsableKnife(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			ItemData val = ToolSlots.PeekKnife();
			if (IsUsableKnife(val))
			{
				return val;
			}
			return FindUsableKnifeFallback(player);
		}

		private static ItemData FindUsableKnifeFallback(Player player)
		{
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			ItemData equippedKnife = GetEquippedKnife(player);
			if (equippedKnife != null)
			{
				return equippedKnife;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return null;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null || allItems.Count == 0)
			{
				return null;
			}
			ItemData val = null;
			float num = float.MinValue;
			foreach (ItemData item in allItems)
			{
				if (IsUsableKnife(item))
				{
					DamageTypes damages = item.m_shared.m_damages;
					float num2 = damages.m_slash + damages.m_pierce + damages.m_blunt;
					if (val == null || num2 > num)
					{
						val = item;
						num = num2;
					}
				}
			}
			return val;
		}

		private static bool IsUsableKnife(ItemData item)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Invalid comparison between Unknown and I4
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			return (int)item.m_shared.m_skillType == 2 && item.HavePrimaryAttack() && (!item.m_shared.m_useDurability || item.m_durability > 0f);
		}

		private static void CaptureOriginalEquipment(Player player)
		{
			_originalWeapon = null;
			_gatherKnife = null;
			_knifeWasAutoEquipped = false;
			_equipmentCaptured = false;
			_knifeCheckedOutFromToolSlot = false;
			if (!((Object)(object)player == (Object)null))
			{
				_originalWeapon = ((Humanoid)player).GetCurrentWeapon();
				_equipmentCaptured = true;
			}
		}

		private static ItemData GetOrEquipKnife(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			_knifeCheckedOutFromToolSlot = false;
			ItemData val = null;
			ItemData item = ToolSlots.PeekKnife();
			if (IsUsableKnife(item))
			{
				val = ToolSlots.CheckoutKnife(player);
				if (val != null && IsUsableKnife(val))
				{
					_knifeCheckedOutFromToolSlot = true;
					if (((Humanoid)player).GetCurrentWeapon() == val || ((Humanoid)player).IsItemEquiped(val) || ((Humanoid)player).EquipItem(val, true))
					{
						_gatherKnife = val;
						_knifeWasAutoEquipped = true;
						return val;
					}
					ToolSlots.ReturnKnifeToSlot(player, val);
					_knifeCheckedOutFromToolSlot = false;
					val = null;
				}
			}
			val = FindUsableKnifeFallback(player);
			if (val == null)
			{
				return null;
			}
			_gatherKnife = val;
			if (((Humanoid)player).GetCurrentWeapon() == val || ((Humanoid)player).IsItemEquiped(val))
			{
				return val;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || !inventory.ContainsItem(val))
			{
				BeastRunesPlugin.BeastRunesLogger.LogWarning((object)"[GatherForaging] Inventory fallback knife was found but is no longer in the player's inventory.");
				_gatherKnife = null;
				return null;
			}
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			try
			{
				if (currentWeapon != null && currentWeapon != val && ((Humanoid)player).IsItemEquiped(currentWeapon))
				{
					((Humanoid)player).UnequipItem(currentWeapon, true);
				}
				if (!((Humanoid)player).EquipItem(val, true) && !((Humanoid)player).IsItemEquiped(val) && ((Humanoid)player).GetCurrentWeapon() != val)
				{
					if (currentWeapon != null && inventory.ContainsItem(currentWeapon))
					{
						((Humanoid)player).EquipItem(currentWeapon, true);
					}
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[GatherForaging] Failed equipping inventory fallback knife '" + val.m_shared.m_name + "'."));
					_gatherKnife = null;
					return null;
				}
			}
			catch (Exception ex)
			{
				if (currentWeapon != null && inventory.ContainsItem(currentWeapon))
				{
					try
					{
						((Humanoid)player).EquipItem(currentWeapon, true);
					}
					catch
					{
					}
				}
				BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[GatherForaging] Inventory fallback knife equip failed: " + ex.Message));
				_gatherKnife = null;
				return null;
			}
			_knifeWasAutoEquipped = true;
			BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("[GatherForaging] Equipped inventory fallback knife '" + val.m_shared.m_name + "'."));
			return val;
		}

		private static void ConsumeKnifeDurability(Player player, ItemData knife)
		{
			if ((Object)(object)player == (Object)null || knife == null || knife.m_shared == null || !knife.m_shared.m_useDurability)
			{
				return;
			}
			try
			{
				float num = knife.m_shared.m_useDurabilityDrain;
				if (num <= 0f)
				{
					num = 1f;
				}
				knife.m_durability = Mathf.Max(0f, knife.m_durability - num);
				if (knife.m_durability <= 0f && ((Humanoid)player).IsItemEquiped(knife))
				{
					((Humanoid)player).UnequipItem(knife, true);
				}
			}
			catch (Exception ex)
			{
				BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("Could not consume knife durability after gathering: " + ex.Message));
			}
		}

		private static void RestoreOriginalEquipment(Player player)
		{
			if (!_equipmentCaptured || !_knifeWasAutoEquipped || (Object)(object)player == (Object)null)
			{
				ClearEquipmentState();
				return;
			}
			try
			{
				if (_gatherKnife != null && ((Humanoid)player).IsItemEquiped(_gatherKnife))
				{
					((Humanoid)player).UnequipItem(_gatherKnife, true);
				}
				if (_originalWeapon != null)
				{
					((Humanoid)player).EquipItem(_originalWeapon, true);
				}
			}
			catch (Exception ex)
			{
				BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("Could not restore previous weapon after gathering: " + ex.Message));
			}
			if (_knifeCheckedOutFromToolSlot && _gatherKnife != null)
			{
				ToolSlots.ReturnKnifeToSlot(player, _gatherKnife);
			}
			ClearEquipmentState();
		}

		private static void ClearEquipmentState()
		{
			_gatherKnife = null;
			_originalWeapon = null;
			_knifeWasAutoEquipped = false;
			_equipmentCaptured = false;
			_knifeCheckedOutFromToolSlot = false;
		}

		public static bool TryBeginFromIntercept(Pickable target, MethodBase originalMethod, object[] originalArguments, out bool result)
		{
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			result = false;
			if (AllowOriginalInteract || (Object)(object)target == (Object)null || originalMethod == null || !IsSupportedForage(target))
			{
				return false;
			}
			Player val = FindPlayerArgument(originalArguments);
			if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (HasHeldInteraction(originalArguments))
			{
				result = false;
				return true;
			}
			if (IsTarget(target))
			{
				Cancel(notify: false);
				result = true;
				return true;
			}
			if (!CanStart(val, target))
			{
				result = true;
				return true;
			}
			if (Active)
			{
				Cancel(notify: false);
			}
			CaptureOriginalEquipment(val);
			ItemData orEquipKnife = GetOrEquipKnife(val);
			if (orEquipKnife == null)
			{
				ClearEquipmentState();
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, "You need a usable knife to gather this", 0, (Sprite)null, false, true);
				}
				result = true;
				return true;
			}
			float gatherDurationForKnife = GetGatherDurationForKnife(orEquipKnife);
			if (gatherDurationForKnife <= 0f)
			{
				try
				{
					AllowOriginalInteract = true;
					MethodInfo methodInfo = originalMethod as MethodInfo;
					if (methodInfo != null)
					{
						if (methodInfo.Invoke(target, CloneArguments(originalArguments)) is bool flag)
						{
							result = flag;
						}
						else
						{
							result = true;
						}
						ConsumeKnifeDurability(val, orEquipKnife);
					}
					else
					{
						result = true;
					}
				}
				catch (TargetInvocationException ex)
				{
					Exception arg = ex.InnerException ?? ex;
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)$"Instant gather failed: {arg}");
					result = true;
				}
				catch (Exception arg2)
				{
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)$"Instant gather failed: {arg2}");
					result = true;
				}
				finally
				{
					AllowOriginalInteract = false;
					GatherForagingHud.Hide();
					RestoreOriginalEquipment(val);
				}
				return true;
			}
			_session = new ForageWorkSession(target, val, originalMethod, CloneArguments(originalArguments), gatherDurationForKnife, 2.4f, 0.3f, 0.01f);
			_session.Start();
			ForagePlayerWorkPose.Begin(val, _session.LockPosition);
			if (IsSkollAndHatiKnife(orEquipKnife))
			{
				GatherForagingHud.Hide();
			}
			else
			{
				GatherForagingHud.Show();
			}
			result = true;
			return true;
		}

		private static Player FindPlayerArgument(object[] args)
		{
			if (args == null)
			{
				return null;
			}
			for (int i = 0; i < args.Length; i++)
			{
				object obj = args[i];
				Player val = (Player)((obj is Player) ? obj : null);
				if (val != null)
				{
					return val;
				}
				object obj2 = args[i];
				Humanoid val2 = (Humanoid)((obj2 is Humanoid) ? obj2 : null);
				if (val2 != null)
				{
					Player val3 = (Player)(object)((val2 is Player) ? val2 : null);
					if ((Object)(object)val3 != (Object)null)
					{
						return val3;
					}
				}
			}
			return null;
		}

		private static bool HasHeldInteraction(object[] args)
		{
			if (args == null)
			{
				return false;
			}
			for (int i = 0; i < args.Length; i++)
			{
				if (args[i] is bool result)
				{
					return result;
				}
			}
			return false;
		}

		private static object[] CloneArguments(object[] args)
		{
			if (args == null)
			{
				return Array.Empty<object>();
			}
			object[] array = new object[args.Length];
			Array.Copy(args, array, args.Length);
			return array;
		}

		private static bool CanStart(Player player, Pickable target)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)target == (Object)null)
			{
				return false;
			}
			if (((Character)player).IsDead() || ((Character)player).InAttack() || ((Character)player).InDodge() || ((Character)player).IsStaggering() || ((Character)player).IsKnockedBack())
			{
				return false;
			}
			float num = Vector3.Distance(((Character)player).GetCenterPoint(), ((Component)target).transform.position);
			if (num > 2.4f)
			{
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, "Move closer to gather", 0, (Sprite)null, false, true);
				}
				return false;
			}
			return true;
		}

		public static void Tick()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (Active && _session != null)
			{
				ForagePlayerWorkPose.Tick(_session.Player, _session.LockPosition);
				ForageWorkSession.WorkState workState = _session.Tick();
				GatherForagingHud.SetProgress(_session.Progress);
				switch (workState)
				{
				case ForageWorkSession.WorkState.Running:
					break;
				case ForageWorkSession.WorkState.Completed:
					CompleteCurrentSession();
					break;
				case ForageWorkSession.WorkState.Cancelled:
				case ForageWorkSession.WorkState.Invalid:
					Cancel(notify: false);
					break;
				}
			}
		}

		private static void CompleteCurrentSession()
		{
			if (_session == null)
			{
				return;
			}
			ForageWorkSession session = _session;
			_session = null;
			GatherForagingHud.Hide();
			ForagePlayerWorkPose.End(session.Player);
			ConsumeKnifeDurability(session.Player, _gatherKnife);
			RestoreOriginalEquipment(session.Player);
			string arg = (((Object)(object)session.Target != (Object)null && (Object)(object)((Component)session.Target).gameObject != (Object)null) ? GetCleanName(((Object)((Component)session.Target).gameObject).name) : "Unknown");
			try
			{
				AllowOriginalInteract = true;
				session.InvokeOriginalInteraction();
			}
			catch (TargetInvocationException ex)
			{
				Exception arg2 = ex.InnerException ?? ex;
				BeastRunesPlugin.BeastRunesLogger.LogWarning((object)$"Gather completion failed for '{arg}': {arg2}");
			}
			catch (Exception arg3)
			{
				BeastRunesPlugin.BeastRunesLogger.LogWarning((object)$"Gather completion failed for '{arg}': {arg3}");
			}
			finally
			{
				AllowOriginalInteract = false;
			}
		}

		public static void Cancel(bool notify)
		{
			if (_session == null)
			{
				GatherForagingHud.Hide();
				return;
			}
			if (notify && (Object)(object)_session.Player != (Object)null && (Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, "Stopped gathering", 0, (Sprite)null, false, true);
			}
			string text = (((Object)(object)_session.Target != (Object)null && (Object)(object)((Component)_session.Target).gameObject != (Object)null) ? GetCleanName(((Object)((Component)_session.Target).gameObject).name) : "Unknown");
			Player player = _session.Player;
			_session.Stop();
			_session = null;
			GatherForagingHud.Hide();
			ForagePlayerWorkPose.End(player);
			RestoreOriginalEquipment(player);
		}
	}
	internal sealed class ForageWorkSession
	{
		internal enum WorkState
		{
			Running,
			Completed,
			Cancelled,
			Invalid
		}

		private readonly MethodBase _originalMethod;

		private readonly object[] _originalArguments;

		private readonly float _duration;

		private readonly float _maxDistance;

		private readonly float _moveCancelDistance;

		private readonly float _moveCancelThresholdSqr;

		private float _startedAt;

		private Vector3 _startPosition;

		private bool _armedForUseCancel;

		private bool _stopped;

		public Pickable Target { get; }

		public Player Player { get; }

		public Vector3 LockPosition => _startPosition;

		public bool Active => !_stopped && (Object)(object)Target != (Object)null && (Object)(object)Player != (Object)null;

		public float Progress
		{
			get
			{
				if (!Active || _duration <= 0f)
				{
					return 0f;
				}
				return Mathf.Clamp01((Time.time - _startedAt) / _duration);
			}
		}

		public ForageWorkSession(Pickable target, Player player, MethodBase originalMethod, object[] originalArguments, float duration, float maxDistance, float moveCancelDistance, float moveCancelThresholdSqr)
		{
			Target = target;
			Player = player;
			_originalMethod = originalMethod;
			_originalArguments = originalArguments ?? Array.Empty<object>();
			_duration = Mathf.Max(0f, duration);
			_maxDistance = Mathf.Max(0.5f, maxDistance);
			_moveCancelDistance = Mathf.Max(0.05f, moveCancelDistance);
			_moveCancelThresholdSqr = Mathf.Max(0f, moveCancelThresholdSqr);
		}

		public void Start()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			_startedAt = Time.time;
			_startPosition = (((Object)(object)Player != (Object)null) ? ((Component)Player).transform.position : Vector3.zero);
			_armedForUseCancel = false;
			_stopped = false;
		}

		public void Stop()
		{
			_stopped = true;
		}

		public WorkState Tick()
		{
			if (_stopped)
			{
				return WorkState.Cancelled;
			}
			if (!StillValid())
			{
				return WorkState.Invalid;
			}
			if (!_armedForUseCancel)
			{
				if (!ZInput.GetButton("Use"))
				{
					_armedForUseCancel = true;
				}
			}
			else if (ZInput.GetButtonDown("Use"))
			{
				return WorkState.Cancelled;
			}
			if (ZInput.GetButtonDown("Attack") || ZInput.GetButtonDown("SecondaryAttack"))
			{
				return WorkState.Cancelled;
			}
			if (Progress >= 1f)
			{
				return WorkState.Completed;
			}
			return WorkState.Running;
		}

		private bool StillValid()
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Invalid comparison between Unknown and I4
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Target == (Object)null || (Object)(object)Player == (Object)null)
			{
				return false;
			}
			if ((Object)(object)Player != (Object)(object)Player.m_localPlayer || ((Character)Player).IsDead())
			{
				return false;
			}
			ItemData currentWeapon = ((Humanoid)Player).GetCurrentWeapon();
			if (currentWeapon == null || currentWeapon.m_shared == null || (int)currentWeapon.m_shared.m_skillType != 2 || (currentWeapon.m_shared.m_useDurability && currentWeapon.m_durability <= 0f))
			{
				return false;
			}
			if (((Character)Player).InAttack() || ((Character)Player).InDodge() || ((Character)Player).IsStaggering() || ((Character)Player).IsKnockedBack())
			{
				return false;
			}
			float num = Vector3.Distance(((Character)Player).GetCenterPoint(), ((Component)Target).transform.position);
			if (num > _maxDistance)
			{
				return false;
			}
			return true;
		}

		public object InvokeOriginalInteraction()
		{
			if (_originalMethod == null || (Object)(object)Target == (Object)null)
			{
				return null;
			}
			MethodInfo methodInfo = _originalMethod as MethodInfo;
			if (methodInfo == null)
			{
				return null;
			}
			return methodInfo.Invoke(Target, _originalArguments);
		}
	}
	internal static class ForagePlayerWorkPose
	{
		private const string StartSfxPrefab = "sfx_build_cultivator";

		private static readonly MethodInfo SetCrouchMethod = AccessTools.Method(typeof(Player), "SetCrouch", new Type[1] { typeof(bool) }, (Type[])null);

		private static readonly FieldInfo PlayerCrouchField = AccessTools.Field(typeof(Player), "m_crouch");

		private static readonly FieldInfo CharacterMoveDirField = AccessTools.Field(typeof(Character), "m_moveDir");

		private static Player _activePlayer;

		public static void Begin(Player player, Vector3 lockPosition)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				_activePlayer = player;
				PlayStartSfx(player);
				ForceCrouch(player, crouch: true);
				LockMovement(player, lockPosition);
			}
		}

		public static void Tick(Player player, Vector3 lockPosition)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && !((Object)(object)player != (Object)(object)_activePlayer))
			{
				ForceCrouch(player, crouch: true);
				LockMovement(player, lockPosition);
			}
		}

		public static void End(Player player)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				_activePlayer = null;
				return;
			}
			ForceCrouch(player, crouch: false);
			Rigidbody component = ((Component)player).GetComponent<Rigidbody>();
			if ((Object)(object)component != (Object)null)
			{
				Vector3 velocity = component.velocity;
				velocity.x = 0f;
				velocity.z = 0f;
				component.velocity = velocity;
			}
			_activePlayer = null;
		}

		private static void PlayStartSfx(Player player)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!((Object)(object)ZNetScene.instance == (Object)null))
				{
					GameObject prefab = ZNetScene.instance.GetPrefab("sfx_build_cultivator");
					if ((Object)(object)prefab == (Object)null)
					{
						BeastRunesPlugin.BeastRunesLogger.LogWarning((object)"Could not find forage start SFX prefab 'sfx_build_cultivator'.");
					}
					else
					{
						Object.Instantiate<GameObject>(prefab, ((Component)player).transform.position, Quaternion.identity);
					}
				}
			}
			catch (Exception ex)
			{
				BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("Forage start SFX could not play: " + ex.Message));
			}
		}

		private static void ForceCrouch(Player player, bool crouch)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			try
			{
				if (SetCrouchMethod != null)
				{
					SetCrouchMethod.Invoke(player, new object[1] { crouch });
				}
				else if (PlayerCrouchField != null)
				{
					PlayerCrouchField.SetValue(player, crouch);
				}
			}
			catch (Exception ex)
			{
				BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("Could not force harvesting crouch state: " + ex.Message));
			}
		}

		private static void LockMovement(Player player, Vector3 lockPosition)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			try
			{
				if (CharacterMoveDirField != null)
				{
					CharacterMoveDirField.SetValue(player, Vector3.zero);
				}
				Rigidbody component = ((Component)player).GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					Vector3 velocity = component.velocity;
					velocity.x = 0f;
					velocity.z = 0f;
					component.velocity = velocity;
				}
				Vector3 position = ((Component)player).transform.position;
				position.x = lockPosition.x;
				position.z = lockPosition.z;
				((Component)player).transform.position = position;
			}
			catch (Exception ex)
			{
				BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("Could not lock player during harvesting: " + ex.Message));
			}
		}
	}
	internal static class GatherForagingHud
	{
		private static GatherForagingHudBehaviour _behaviour;

		public static void Show()
		{
			EnsureCreated();
			if ((Object)(object)_behaviour != (Object)null)
			{
				_behaviour.Show();
			}
		}

		public static void Hide()
		{
			if ((Object)(object)_behaviour != (Object)null)
			{
				_behaviour.Hide();
			}
		}

		public static void SetProgress(float progress)
		{
			EnsureCreated();
			if ((Object)(object)_behaviour != (Object)null)
			{
				_behaviour.SetProgress(progress);
			}
		}

		public static void Preload()
		{
			EnsureCreated();
		}

		private static void EnsureCreated()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			if (!((Object)(object)_behaviour != (Object)null))
			{
				GameObject val = new GameObject("BeastRunes_GatherForagingHud");
				Object.DontDestroyOnLoad((Object)(object)val);
				_behaviour = val.AddComponent<GatherForagingHudBehaviour>();
			}
		}
	}
	internal sealed class GatherForagingHudBehaviour : MonoBehaviour
	{
		private const int TextureSize = 160;

		private const float RingSize = 58f;

		private bool _visible;

		private float _progress;

		private Texture2D _backgroundRing;

		private Texture2D _progressRing;

		private float _lastRenderedProgress = -1f;

		private Color _lastRenderedColor = Color.clear;

		private readonly Color _backgroundColor = new Color(1f, 1f, 1f, 0.16f);

		private readonly Color _startColor = new Color(0.24f, 0.95f, 0.34f, 1f);

		private readonly Color _endColor = new Color(0.24f, 0.95f, 0.34f, 0.08f);

		private void Awake()
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			_backgroundRing = CreateRingTexture(1f, Color.white);
			_progressRing = CreateEmptyTexture("BeastRunes_GatherProgressRing");
			RebuildProgressRing(0f, _startColor);
		}

		private Texture2D CreateEmptyTexture(string name)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			Texture2D val = new Texture2D(160, 160, (TextureFormat)4, false);
			((Object)val).name = name;
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)1;
			return val;
		}

		private void OnDestroy()
		{
			DestroyTexture(_backgroundRing);
			DestroyTexture(_progressRing);
		}

		private static void DestroyTexture(Texture2D texture)
		{
			if ((Object)(object)texture != (Object)null)
			{
				Object.Destroy((Object)(object)texture);
			}
		}

		public void Show()
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			_progress = 0f;
			_lastRenderedProgress = -1f;
			_lastRenderedColor = Color.clear;
			_visible = true;
			RebuildProgressRing(0f, _startColor);
		}

		public void Hide()
		{
			_visible = false;
			_progress = 0f;
		}

		public void SetProgress(float progress)
		{
			_progress = Mathf.Clamp01(progress);
		}

		private void OnGUI()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: 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_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (_visible)
			{
				Color progressColor = GetProgressColor(_progress);
				if (Mathf.Abs(_progress - _lastRenderedProgress) >= 0.01f || progressColor != _lastRenderedColor || _progress >= 0.999f)
				{
					RebuildProgressRing(_progress, progressColor);
				}
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor((float)Screen.width * 0.5f, (float)Screen.height * 0.5f);
				Rect val2 = default(Rect);
				((Rect)(ref val2))..ctor(val.x - 29f, val.y - 29f, 58f, 58f);
				Color color = GUI.color;
				Matrix4x4 matrix = GUI.matrix;
				GUI.color = _backgroundColor;
				GUI.DrawTexture(val2, (Texture)(object)_backgroundRing, (ScaleMode)2, true);
				GUI.color = Color.white;
				GUI.DrawTexture(val2, (Texture)(object)_progressRing, (ScaleMode)2, true);
				GUI.matrix = matrix;
				GUI.color = color;
			}
		}

		private Texture2D CreateRingTexture(float fill, Color color)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = CreateEmptyTexture("BeastRunes_GatherRing");
			PaintArc(val, fill, color);
			return val;
		}

		private Color GetProgressColor(float progress)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Clamp01(progress);
			float num2 = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(0.45f, 1f, num));
			return Color.Lerp(_startColor, _endColor, num2);
		}

		private void RebuildProgressRing(float progress, Color color)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_progressRing == (Object)null))
			{
				PaintRemainingArc(_progressRing, Mathf.Clamp01(progress), color);
				_lastRenderedProgress = progress;
				_lastRenderedColor = color;
			}
		}

		private static void PaintArc(Texture2D texture, float fillAmount, Color color)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			PaintRingInternal(texture, Mathf.Clamp01(fillAmount), color, paintRemaining: false);
		}

		private static void PaintRemainingArc(Texture2D texture, float progress, Color color)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			PaintRingInternal(texture, Mathf.Clamp01(progress), color, paintRemaining: true);
		}

		private static void PaintRingInternal(Texture2D texture, float amount, Color color, bool paintRemaining)
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			int width = ((Texture)texture).width;
			float num = (float)(width - 1) * 0.5f;
			float num2 = (float)width * 0.47f;
			float num3 = (float)width * 0.365f;
			float num4 = 2.4f;
			Color32[] array = (Color32[])(object)new Color32[width * width];
			float num5 = Mathf.Clamp01(amount);
			for (int i = 0; i < width; i++)
			{
				for (int j = 0; j < width; j++)
				{
					float num6 = (float)j - num;
					float num7 = (float)i - num;
					float num8 = Mathf.Sqrt(num6 * num6 + num7 * num7);
					float num9 = Mathf.Clamp01((num2 - num8) / num4);
					float num10 = Mathf.Clamp01((num8 - num3) / num4);
					float num11 = Mathf.Min(num9, num10);
					if (num11 <= 0f)
					{
						array[i * width + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)0);
						continue;
					}
					float num12 = Mathf.Atan2(num7, num6) * 57.29578f;
					for (num12 = 0f - num12 + 90f; num12 < 0f; num12 += 360f)
					{
					}
					while (num12 >= 360f)
					{
						num12 -= 360f;
					}
					float num13 = num12 / 360f;
					bool flag;
					if (paintRemaining)
					{
						flag = num13 > num5;
						if (num5 <= 0.0001f)
						{
							flag = true;
						}
						if (num5 >= 0.9999f)
						{
							flag = false;
						}
					}
					else
					{
						flag = num5 >= 0.9999f || num13 <= num5;
					}
					if (!flag)
					{
						array[i * width + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)0);
						continue;
					}
					Color val = color;
					val.a *= num11;
					array[i * width + j] = Color32.op_Implicit(val);
				}
			}
			texture.SetPixels32(array);
			texture.Apply(false, false);
		}
	}
	[HarmonyPatch(typeof(Pickable), "Interact", new Type[]
	{
		typeof(Humanoid),
		typeof(bool),
		typeof(bool)
	})]
	internal static class BeastRunes_Pickable_Interact_GatherForagingPatch
	{
		private static bool Prefix(Pickable __instance, Humanoid __0, bool __1, bool __2, ref bool __result, MethodBase __originalMethod)
		{
			if (GatherForagingSystem.AllowOriginalInteract)
			{
				return true;
			}
			bool flag = __1;
			bool flag2 = __2;
			if ((Object)(object)__instance == (Object)null || !GatherForagingSystem.IsSupportedForage(__instance))
			{
				return true;
			}
			Player val = (Player)(object)((__0 is Player) ? __0 : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			if (flag || flag2)
			{
				__result = false;
				return false;
			}
			object[] originalArguments = new object[3] { __0, flag, flag2 };
			if (!GatherForagingSystem.TryBeginFromIntercept(__instance, __originalMethod, originalArguments, out var result))
			{
				return true;
			}
			__result = result;
			return false;
		}
	}
	[HarmonyPatch(typeof(Pickable), "GetHoverText")]
	internal static class BeastRunes_Pickable_GetHoverText_GatherForagingPatch
	{
		private static void Postfix(Pickable __instance, ref string __result)
		{
			if (!((Object)(object)__instance == (Object)null) && GatherForagingSystem.IsSupportedForage(__instance))
			{
				string hoverName;
				try
				{
					hoverName = __instance.GetHoverName();
				}
				catch
				{
					return;
				}
				string text = (GatherForagingSystem.IsTarget(__instance) ? "Stop gathering" : ((GatherForagingSystem.FindUsableKnife(Player.m_localPlayer) != null) ? "Gather" : "Need a knife"));
				__result = Localization.instance.Localize(hoverName + "\n[<color=yellow><b>$KEY_Use</b></color>] " + text);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class BeastRunes_Player_Update_GatherForagingPatch
	{
		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				GatherForagingSystem.Tick();
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	[HarmonyPriority(0)]
	internal static class BeastRunes_ZNetScene_Awake_GatherForagingPatch
	{
		private static void Postfix()
		{
			GatherForagingHud.Preload();
			BeastRunesPlugin.BeastRunesLogger.LogInfo((object)"GatherForagingSystem work-session patch ready.");
		}
	}
	[HarmonyPatch(typeof(Player), "OnDestroy")]
	internal static class BeastRunes_Player_Destroy_GatherForagingPatch
	{
		private static void Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				GatherForagingSystem.Cancel(notify: false);
			}
		}
	}
	public static class GoldenHammerPatch
	{
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static class ObjectDB_Awake_Patch
		{
			[HarmonyPostfix]
			private static void Postfix(ObjectDB __instance)
			{
				TryConfigure(__instance, "ObjectDB.Awake");
			}
		}

		[HarmonyPatch(typeof(ZNetScene), "Awake")]
		private static class ZNetScene_Awake_Patch
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				TryConfigure(ObjectDB.instance, "ZNetScene.Awake");
			}
		}

		private const string GoldenHammerPrefabName = "Golden_Hammer_Ygg";

		private const string VanillaHammerPrefabName = "Hammer";

		private static bool _configured;

		private static void TryConfigure(ObjectDB objectDb, string source)
		{
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			if (_configured)
			{
				return;
			}
			try
			{
				if ((Object)(object)objectDb == (Object)null)
				{
					BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("[GoldenHammer] ObjectDB was null during " + source + "."));
					return;
				}
				GameObject itemPrefab = objectDb.GetItemPrefab("Golden_Hammer_Ygg");
				GameObject itemPrefab2 = objectDb.GetItemPrefab("Hammer");
				if ((Object)(object)itemPrefab == (Object)null)
				{
					BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("[GoldenHammer] Golden_Hammer_Ygg not found yet during " + source + "; will retry later."));
					return;
				}
				if ((Object)(object)itemPrefab2 == (Object)null)
				{
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[GoldenHammer] Could not find vanilla Hammer during " + source + "."));
					return;
				}
				ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
				ItemDrop component2 = itemPrefab2.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null)
				{
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)"[GoldenHammer] Golden_Hammer_Ygg has no ItemDrop component.");
					return;
				}
				if ((Object)(object)component2 == (Object)null)
				{
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)"[GoldenHammer] Vanilla Hammer has no ItemDrop component.");
					return;
				}
				if (component.m_itemData == null || component2.m_itemData == null)
				{
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)"[GoldenHammer] One of the hammer ItemData objects was null.");
					return;
				}
				SharedData shared = component.m_itemData.m_shared;
				SharedData shared2 = component2.m_itemData.m_shared;
				if (shared == null)
				{
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)"[GoldenHammer] Golden Hammer SharedData was null.");
					return;
				}
				if (shared2 == null)
				{
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)"[GoldenHammer] Vanilla Hammer SharedData was null.");
					return;
				}
				shared.m_itemType = (ItemType)19;
				shared.m_buildPieces = shared2.m_buildPieces;
				if ((Object)(object)shared.m_buildPieces == (Object)null)
				{
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)"[GoldenHammer] Vanilla Hammer PieceTable was null.");
					return;
				}
				if (shared.m_attack != null && shared2.m_attack != null)
				{
					shared.m_attack.m_attackAnimation = shared2.m_attack.m_attackAnimation;
				}
				if (shared.m_secondaryAttack != null && shared2.m_secondaryAttack != null)
				{
					shared.m_secondaryAttack.m_attackAnimation = shared2.m_secondaryAttack.m_attackAnimation;
				}
				if (shared.m_attack != null)
				{
					shared.m_attack.m_attackStamina = 0f;
				}
				if (shared.m_secondaryAttack != null)
				{
					shared.m_secondaryAttack.m_attackStamina = 0f;
				}
				shared.m_useDurability = false;
				_configured = true;
				BeastRunesPlugin.BeastRunesLogger.LogInfo((object)("[GoldenHammer] Golden_Hammer_Ygg configured successfully during " + source + "."));
				BeastRunesPlugin.BeastRunesLogger.LogInfo((object)"[GoldenHammer] Vanilla Hammer PieceTable applied.");
				BeastRunesPlugin.BeastRunesLogger.LogInfo((object)"[GoldenHammer] Stamina cost: 0.");
				BeastRunesPlugin.BeastRunesLogger.LogInfo((object)"[GoldenHammer] Durability usage disabled.");
			}
			catch (Exception arg)
			{
				BeastRunesPlugin.BeastRunesLogger.LogError((object)$"[GoldenHammer] Failed to configure during {source}: {arg}");
			}
		}
	}
	internal static class MinePickingSystem
	{
		private static ConfigEntry<bool> _enableAutoMining;

		private static ConfigEntry<float> _minePickingMaximumDistance;

		private static readonly Collider[] NearbyColliders = (Collider[])(object)new Collider[64];

		private static MinePickingInteraction _target;

		private static Collider _collider;

		private static Vector3 _localSurfacePoint;

		private static Player _player;

		private static ItemData _pickaxe;

		private static ItemData _originalWeapon;

		private static bool _pickaxeWasAutoEquipped;

		private static bool _equipmentCaptured;

		private static bool _pickaxeCheckedOutFromToolSlot;

		private static bool _armedForCancel;

		private static float _nextAttackAttempt;

		private static float _firstFailedAttempt = -1f;

		private static float _retargetDeadline = -1f;

		private const float RetargetGraceSeconds = 1.5f;

		internal static bool AutoMiningEnabled
		{
			get
			{
				EnsureConfig();
				return _enableAutoMining == null || _enableAutoMining.Value;
			}
		}

		private static float ConfiguredInteractionReach
		{
			get
			{
				EnsureConfig();
				return (_minePickingMaximumDistance != null) ? _minePickingMaximumDistance.Value : 1.1f;
			}
		}

		public static bool Active => (Object)(object)_player != (Object)null && _pickaxe != null;

		private static void EnsureConfig()
		{
			if (!((Object)(object)BeastRunesPlugin.context == (Object)null))
			{
				if (_enableAutoMining == null)
				{
					_enableAutoMining = BeastRunesPlugin.context.config("Auto Mining", "Enable Auto Mining", value: true, "Enable Press-E automatic pickaxe mining.");
				}
				if (_minePickingMaximumDistance == null)
				{
					_minePickingMaximumDistance = BeastRunesPlugin.context.config("Auto Mining", "Maximum Interaction Distance", 1.1f, "Maximum distance from a mineable node to start automatic mining.");
				}
			}
		}

		public static bool IsTarget(MinePickingInteraction target)
		{
			return Active && (Object)(object)target != (Object)null && (Object)(object)_target != (Object)null && (Object)(object)_target == (Object)(object)target;
		}

		public static ItemData GetEquippedPickaxe(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			return IsUsablePickaxe(currentWeapon) ? currentWeapon : null;
		}

		public static ItemData FindUsablePickaxe(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			ItemData val = ToolSlots.PeekPickaxe();
			if (IsUsablePickaxe(val))
			{
				return val;
			}
			return FindUsablePickaxeFallback(player);
		}

		private static ItemData FindUsablePickaxeFallback(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			ItemData equippedPickaxe = GetEquippedPickaxe(player);
			if (equippedPickaxe != null)
			{
				return equippedPickaxe;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return null;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null || allItems.Count == 0)
			{
				return null;
			}
			ItemData val = null;
			float num = float.MinValue;
			foreach (ItemData item in allItems)
			{
				if (IsUsablePickaxe(item))
				{
					float pickaxe = item.m_shared.m_damages.m_pickaxe;
					if (val == null || pickaxe > num)
					{
						val = item;
						num = pickaxe;
					}
				}
			}
			return val;
		}

		public static ItemData GetOrEquipPickaxe(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			_pickaxeCheckedOutFromToolSlot = false;
			ItemData val = null;
			ItemData item = ToolSlots.PeekPickaxe();
			if (IsUsablePickaxe(item))
			{
				val = ToolSlots.CheckoutPickaxe(player);
				if (val != null && IsUsablePickaxe(val))
				{
					_pickaxeCheckedOutFromToolSlot = true;
					if (((Humanoid)player).GetCurrentWeapon() == val || ((Humanoid)player).IsItemEquiped(val) || ((Humanoid)player).EquipItem(val, true))
					{
						_pickaxeWasAutoEquipped = true;
						return val;
					}
					ToolSlots.ReturnPickaxeToSlot(player, val);
					_pickaxeCheckedOutFromToolSlot = false;
					val = null;
				}
			}
			val = FindUsablePickaxeFallback(player);
			if (val == null)
			{
				return null;
			}
			if (((Humanoid)player).GetCurrentWeapon() == val || ((Humanoid)player).IsItemEquiped(val))
			{
				return val;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || !inventory.ContainsItem(val))
			{
				BeastRunesPlugin.BeastRunesLogger.LogWarning((object)"[MinePicking] Inventory fallback pickaxe was found but is no longer in the player's inventory.");
				return null;
			}
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			try
			{
				if (currentWeapon != null && currentWeapon != val && ((Humanoid)player).IsItemEquiped(currentWeapon))
				{
					((Humanoid)player).UnequipItem(currentWeapon, true);
				}
				if (!((Humanoid)player).EquipItem(val, true) && !((Humanoid)player).IsItemEquiped(val) && ((Humanoid)player).GetCurrentWeapon() != val)
				{
					if (currentWeapon != null && inventory.ContainsItem(currentWeapon))
					{
						((Humanoid)player).EquipItem(currentWeapon, true);
					}
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[MinePicking] Failed equipping inventory fallback pickaxe '" + val.m_shared.m_name + "'."));
					return null;
				}
			}
			catch (Exception ex)
			{
				if (currentWeapon != null && inventory.ContainsItem(currentWeapon))
				{
					try
					{
						((Humanoid)player).EquipItem(currentWeapon, true);
					}
					catch
					{
					}
				}
				BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[MinePicking] Inventory fallback pickaxe equip failed: " + ex.Message));
				return null;
			}
			_pickaxeWasAutoEquipped = true;
			BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("[MinePicking] Equipped inventory fallback pickaxe '" + val.m_shared.m_name + "'."));
			return val;
		}

		public static void CaptureOriginalEquipment(Player player)
		{
			_originalWeapon = null;
			_pickaxeWasAutoEquipped = false;
			_equipmentCaptured = false;
			_pickaxeCheckedOutFromToolSlot = false;
			if (!((Object)(object)player == (Object)null))
			{
				_originalWeapon = ((Humanoid)player).GetCurrentWeapon();
				_equipmentCaptured = true;
			}
		}

		private static void RestoreOriginalEquipment(Player player)
		{
			if (!_equipmentCaptured || !_pickaxeWasAutoEquipped || (Object)(object)player == (Object)null)
			{
				ClearEquipmentState();
				return;
			}
			try
			{
				if (_pickaxe != null && ((Humanoid)player).IsItemEquiped(_pickaxe))
				{
					((Humanoid)player).UnequipItem(_pickaxe, true);
				}
				if (_originalWeapon != null)
				{
					((Humanoid)player).EquipItem(_originalWeapon, true);
				}
			}
			catch (Exception ex)
			{
				BeastRunesPlugin.BeastRunesLogger.LogDebug((object)("Could not restore previous weapon after mining: " + ex.Message));
			}
			if (_pickaxeCheckedOutFromToolSlot && _pickaxe != null)
			{
				ToolSlots.ReturnPickaxeToSlot(player, _pickaxe);
			}
			ClearEquipmentState();
		}

		private static void ClearEquipmentState()
		{
			_originalWeapon = null;
			_pickaxeWasAutoEquipped = false;
			_equipmentCaptured = false;
			_pickaxeCheckedOutFromToolSlot = false;
		}

		private static bool IsUsablePickaxe(ItemData item)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			return (int)item.m_shared.m_skillType == 12 && item.m_shared.m_damages.m_pickaxe > 0f && item.HavePrimaryAttack() && (!item.m_shared.m_useDurability || item.m_durability > 0f);
		}

		private static float GetInteractionReach()
		{
			float configuredInteractionReach = ConfiguredInteractionReach;
			return Mathf.Clamp(configuredInteractionReach, 0.5f, 4f);
		}

		public static bool IsColliderNearby(Player player, Collider collider)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)collider == (Object)null || !collider.enabled)
			{
				return false;
			}
			int num = Physics.OverlapSphereNonAlloc(((Character)player).GetCenterPoint(), GetInteractionReach(), NearbyColliders, -1, (QueryTriggerInteraction)1);
			for (int i = 0; i < num; i++)
			{
				if ((Object)(object)NearbyColliders[i] == (Object)(object)collider)
				{
					return true;
				}
			}
			return false;
		}

		public static bool TryGetCrosshairPoint(Player player, Collider collider, out Vector3 point)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: 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)
			Vector3 val;
			if (!((Object)(object)collider != (Object)null))
			{
				val = Vector3.zero;
			}
			else
			{
				Bounds bounds = collider.bounds;
				val = ((Bounds)(ref bounds)).center;
			}
			point = val;
			if ((Object)(object)player == (Object)null || (Object)(object)collider == (Object)null || !collider.enabled || (Object)(object)GameCamera.instance == (Object)null)
			{
				return false;
			}
			Transform transform = ((Component)GameCamera.instance).transform;
			Ray val2 = default(Ray);
			((Ray)(ref val2))..ctor(transform.position, transform.forward);
			RaycastHit val3 = default(RaycastHit);
			if (!collider.Raycast(val2, ref val3, 50f))
			{
				return false;
			}
			point = ((RaycastHit)(ref val3)).point;
			return true;
		}

		public static void Begin(MinePickingInteraction target, Collider collider, Vector3 surfacePoint, Player player, ItemData pickaxe)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)target == (Object)null) && !((Object)(object)collider == (Object)null) && !((Object)(object)player == (Object)null) && pickaxe != null)
			{
				if (Active)
				{
					Cancel(notify: false);
				}
				_target = target;
				_collider = collider;
				_localSurfacePoint = ((Component)collider).transform.InverseTransformPoint(surfacePoint);
				_player = player;
				_pickaxe = pickaxe;
				_armedForCancel = false;
				_nextAttackAttempt = 0f;
				_firstFailedAttempt = -1f;
				_retargetDeadline = -1f;
				BeastRunesPlugin.BeastRunesLogger.LogInfo((object)("Started mining '" + ((Object)((Component)target).gameObject).name + "' with '" + pickaxe.m_shared.m_name + "'."));
			}
		}

		public static void Tick()
		{
			if (!Active)
			{
				return;
			}
			if (!CanContinueMiningSession())
			{
				Cancel(notify: false);
				return;
			}
			if (!HasValidCurrentTarget())
			{
				if (_retargetDeadline < 0f)
				{
					_retargetDeadline = Time.time + 1.5f;
				}
				if (!TryRetargetMineable())
				{
					if (Time.time >= _retargetDeadline)
					{
						Cancel(notify: false);
					}
					return;
				}
				_retargetDeadline = -1f;
			}
			else
			{
				_retargetDeadline = -1f;
			}
			if (!StillValid())
			{
				Cancel(notify: false);
				return;
			}
			if (!_armedForCancel)
			{
				if (!ZInput.GetButton("Use"))
				{
					_armedForCancel = true;
				}
			}
			else if (ZInput.GetButtonDown("Use"))
			{
				Cancel(notify: false);
				return;
			}
			if (ZInput.GetButtonDown("Attack"))
			{
				Cancel(notify: false);
			}
			else
			{
				if (((Character)_player).InAttack() || Time.time < _nextAttackAttempt)
				{
					return;
				}
				FaceTarget();
				bool flag = ((Character)_player).StartAttack((Character)null, false);
				_nextAttackAttempt = Time.time + 0.12f;
				if (flag)
				{
					_firstFailedAttempt = -1f;
					return;
				}
				if (_firstFailedAttempt < 0f)
				{
					_firstFailedAttempt = Time.time;
				}
				if (Time.time - _firstFailedAttempt > 1f)
				{
					if ((Object)(object)MessageHud.instance != (Object)null)
					{
						MessageHud.instance.ShowMessage((MessageType)2, "Unable to continue mining", 0, (Sprite)null, false, true);
					}
					Cancel(notify: false);
				}
			}
		}

		private static bool CanContinueMiningSession()
		{
			//IL_00c4: 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)
			if ((Object)(object)_player == (Object)null || _pickaxe == null)
			{
				return false;
			}
			if (!AutoMiningEnabled)
			{
				return false;
			}
			if ((Object)(object)_player != (Object)(object)Player.m_localPlayer || ((Character)_player).IsDead())
			{
				return false;
			}
			if (((Humanoid)_player).GetCurrentWeapon() != _pickaxe || !IsUsablePickaxe(_pickaxe))
			{
				return false;
			}
			if (((Character)_player).InDodge() || ((Character)_player).IsStaggering() || ((Character)_player).IsKnockedBack())
			{
				return false;
			}
			Vector3 moveDir = ((Character)_player).GetMoveDir();
			if (((Vector3)(ref moveDir)).sqrMagnitude > 0.01f)
			{
				return false;
			}
			return true;
		}

		private static bool HasValidCurrentTarget()
		{
			return (Object)(object)_target != (Object)null && (Object)(object)_collider != (Object)null && _collider.enabled && _target.IsAvailable() && IsColliderNearby(_player, _collider);
		}

		private static bool TryRetargetMineable()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_player == (Object)null)
			{
				return false;
			}
			int num = Physics.OverlapSphereNonAlloc(((Character)_player).GetCenterPoint(), GetInteractionReach(), NearbyColliders, -1, (QueryTriggerInteraction)1);
			MinePickingInteraction minePickingInteraction = null;
			Collider val = null;
			float num2 = float.MaxValue;
			for (int i = 0; i < num; i++)
			{
				Collider val2 = NearbyColliders[i];
				if ((Object)(object)val2 == (Object)null || !val2.enabled)
				{
					continue;
				}
				MinePickingInteractionPoint component = ((Component)val2).GetComponent<MinePickingInteractionPoint>();
				if (!((Object)(object)component == (Object)null) && !((Object)(object)component.Target == (Object)null) && component.Target.IsAvailable())
				{
					Bounds bounds = val2.bounds;
					Vector3 val3 = ((Bounds)(ref bounds)).center - ((Character)_player).GetCenterPoint();
					float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
					if (!(sqrMagnitude >= num2))
					{
						num2 = sqrMagnitude;
						minePickingInteraction = component.Target;
						val = val2;
					}
				}
			}
			if ((Object)(object)minePickingInteraction == (Object)null || (Object)(object)val == (Object)null)
			{
				return false;
			}
			_target = minePickingInteraction;
			_collider = val;
			if (!TryGetCrosshairPoint(_player, _collider, out var point))
			{
				point = _collider.ClosestPoint(((Character)_player).GetCenterPoint());
			}
			_localSurfacePoint = ((Component)_collider).transform.InverseTransformPoint(point);
			BeastRunesPlugin.BeastRunesLogger.LogInfo((object)("Mining target transitioned to '" + ((Object)((Component)_target).gameObject).name + "'."));
			return true;
		}

		private static bool StillValid()
		{
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_target == (Object)null || (Object)(object)_collider == (Object)null || (Object)(object)_player == (Object)null || _pickaxe == null)
			{
				return false;
			}
			if (!AutoMiningEnabled)
			{
				return false;
			}
			if ((Object)(object)_player != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (((Character)_player).IsDead())
			{
				return false;
			}
			if (!_target.IsAvailable())
			{
				return false;
			}
			if (((Humanoid)_player).GetCurrentWeapon() != _pickaxe)
			{
				return false;
			}
			if (!IsUsablePickaxe(_pickaxe))
			{
				return false;
			}
			if (((Character)_player).InDodge())
			{
				return false;
			}
			if (((Character)_player).IsStaggering())
			{
				return false;
			}
			if (((Character)_player).IsKnockedBack())
			{
				return false;
			}
			Vector3 moveDir = ((Character)_player).GetMoveDir();
			if (((Vector3)(ref moveDir)).sqrMagnitude > 0.01f)
			{
				return false;
			}
			if (!IsColliderNearby(_player, _collider))
			{
				return false;
			}
			return true;
		}

		private static Vector3 GetSurfacePoint()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_collider == (Object)null)
			{
				return Vector3.zero;
			}
			return ((Component)_collider).transform.TransformPoint(_localSurfacePoint);
		}

		private static void FaceTarget()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: 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 (!((Object)(object)_player == (Object)null))
			{
				Vector3 val = GetSurfacePoint() - ((Component)_player).transform.position;
				val.y = 0f;
				if (!(((Vector3)(ref val)).sqrMagnitude <= 0.001f))
				{
					((Character)_player).SetLookDir(((Vector3)(ref val)).normalized, 0.08f);
				}
			}
		}

		public static void Cancel(bool notify)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			if (Active)
			{
				if (notify && (Object)(object)_player != (Object)null && (Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, "Stopped mining", 0, (Sprite)null, false, true);
				}
				string text = (((Object)(object)_target != (Object)null) ? ((Object)((Component)_target).gameObject).name : "Unknown");
				BeastRunesPlugin.BeastRunesLogger.LogInfo((object)("Stopped mining '" + text + "'."));
				Player player = _player;
				RestoreOriginalEquipment(player);
				_target = null;
				_collider = null;
				_localSurfacePoint = Vector3.zero;
				_player = null;
				_pickaxe = null;
				_armedForCancel = false;
				_nextAttackAttempt = 0f;
				_firstFailedAttempt = -1f;
				_retargetDeadline = -1f;
			}
		}
	}
	public sealed class MinePickingInteraction : MonoBehaviour
	{
		private MineRock _mineRock;

		private MineRock5 _mineRock5;

		private Destructible _destructible;

		private ZNetView _nview;

		private void Awake()
		{
			_mineRock = ((Component)this).GetComponent<MineRock>();
			_mineRock5 = ((Component)this).GetComponent<MineRock5>();
			_destructible = ((Component)this).GetComponent<Destructible>();
			_nview = ((Component)this).GetComponent<ZNetView>();
		}

		public bool IsAvailable()
		{
			if (!MinePickingSystem.AutoMiningEnabled)
			{
				return false;
			}
			if ((Object)(object)_mineRock == (Object)null && (Object)(object)_mineRock5 == (Object)null && (Object)(object)_destructible == (Object)null)
			{
				return false;
			}
			if ((Object)(object)_nview != (Object)null && !_nview.IsValid())
			{
				return false;
			}
			return true;
		}

		public string GetHoverName(Collider collider)
		{
			if (!IsAvailable())
			{
				return string.Empty;
			}
			if (!MinePickingSystem.IsColliderNearby(Player.m_localPlayer, collider))
			{
				return string.Empty;
			}
			return "Mineable";
		}

		public string GetHoverText(Collider collider)
		{
			if (!IsAvailable())
			{
				return string.Empty;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return string.Empty;
			}
			if (!MinePickingSystem.IsColliderNearby(localPlayer, collider))
			{
				return string.Empty;
			}
			string text = (MinePickingSystem.IsTarget(this) ? "Stop mining" : ((MinePickingSystem.FindUsablePickaxe(localPlayer) != null) ? "Start mining" : "Need a pickaxe"));
			return Localization.instance.Localize("[<color=yellow><b>$KEY_Use</b></color>] " + text);
		}

		public bool Interact(Humanoid user, bool hold, bool alt, Collider collider)
		{
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			if (hold || alt || !IsAvailable())
			{
				return false;
			}
			Player val = (Player)(object)((user is Player) ? user : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (MinePickingSystem.IsTarget(this))
			{
				MinePickingSystem.Cancel(notify: false);
				return false;
			}
			MinePickingSystem.CaptureOriginalEquipment(val);
			ItemData orEquipPickaxe = MinePickingSystem.GetOrEquipPickaxe(val);
			if (orEquipPickaxe == null)
			{
				MinePickingSystem.CaptureOriginalEquipment(null);
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, "You need a usable pickaxe to start mining", 0, (Sprite)null, false, true);
				}
				return false;
			}
			if (!MinePickingSystem.IsColliderNearby(val, collider))
			{
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage((MessageType)2, "Move closer to start mining", 0, (Sprite)null, false, true);
				}
				return false;
			}
			MinePickingSystem.TryGetCrosshairPoint(val, collider, out var point);
			MinePickingSystem.Begin(this, collider, point, val, orEquipPickaxe);
			return false;
		}
	}
	public sealed class MinePickingInteractionPoint : MonoBehaviour, Hoverable, Interactable
	{
		public MinePickingInteraction Target;

		private Collider _collider;

		private Collider PointCollider
		{
			get
			{
				if ((Object)(object)_collider == (Object)null)
				{
					_collider = ((Component)this).GetComponent<Collider>();
				}
				return _collider;
			}
		}

		private void Awake()
		{
			_collider = ((Component)this).GetComponent<Collider>();
		}

		public string GetHoverName()
		{
			if ((Object)(object)Target == (Object)null)
			{
				return string.Empty;
			}
			return Target.GetHoverName(PointCollider);
		}

		public string GetHoverText()
		{
			if ((Object)(object)Target == (Object)null)
			{
				return string.Empty;
			}
			return Target.GetHoverText(PointCollider);
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			return (Object)(object)Target != (Object)null && Target.Interact(user, hold, alt, PointCollider);
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}
	}
	internal static class MinePickingInstaller
	{
		public static void Install(GameObject root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			MinePickingInteraction minePickingInteraction = null;
			try
			{
				minePickingInteraction = root.GetComponent<MinePickingInteraction>();
				if ((Object)(object)minePickingInteraction == (Object)null)
				{
					minePickingInteraction = root.AddComponent<MinePickingInteraction>();
				}
			}
			catch (Exception ex)
			{
				BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[MinePicking] Could not attach MinePickingInteraction to '" + ((Object)root).name + "': " + ex.Message));
				return;
			}
			if ((Object)(object)minePickingInteraction == (Object)null)
			{
				BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[MinePicking] Unity refused to create MinePickingInteraction on '" + ((Object)root).name + "'."));
				return;
			}
			Collider[] componentsInChildren;
			try
			{
				componentsInChildren = root.GetComponentsInChildren<Collider>(true);
			}
			catch (Exception ex2)
			{
				BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[MinePicking] Could not enumerate colliders for '" + ((Object)root).name + "': " + ex2.Message));
				return;
			}
			if (componentsInChildren == null || componentsInChildren.Length == 0)
			{
				return;
			}
			Collider[] array = componentsInChildren;
			foreach (Collider val in array)
			{
				if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null)
				{
					continue;
				}
				try
				{
					MinePickingInteractionPoint minePickingInteractionPoint = ((Component)val).GetComponent<MinePickingInteractionPoint>();
					if ((Object)(object)minePickingInteractionPoint == (Object)null)
					{
						minePickingInteractionPoint = ((Component)val).gameObject.AddComponent<MinePickingInteractionPoint>();
					}
					if ((Object)(object)minePickingInteractionPoint == (Object)null)
					{
						BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[MinePicking] Unity refused to create MinePickingInteractionPoint on '" + ((Object)((Component)val).gameObject).name + "'."));
					}
					else
					{
						minePickingInteractionPoint.Target = minePickingInteraction;
					}
				}
				catch (Exception ex3)
				{
					BeastRunesPlugin.BeastRunesLogger.LogWarning((object)("[MinePicking] Could not attach interaction point to '" + ((Object)((Component)val).gameObject).name + "' on '" + ((Object)root).name + "': " + ex3.Message));
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	[HarmonyPriority(0)]
	internal static class BeastRunes_ZNetScene_Awake_MinePickingPatch
	{
		private static void Postfix(ZNetScene __instance)
		{
			if ((Object)(object)__instance == (Object)null || __instance.m_prefabs == null)
			{
				return;
			}
			int num = 0;
			foreach (GameObject prefab in __instance.m_prefabs)
			{
				if (!((Object)(object)prefab == (Object)null) && IsMineablePrefab(prefab))
				{
					MinePickingInstaller.Install(prefab);
					num++;
				}
			}
			BeastRunesPlugin.BeastRunesLogger.LogInfo((object)$"MinePicking installed on {num} mineable prefab(s).");
		}

		private static bool IsMineablePrefab(GameObject prefab)
		{
			if ((Object)(object)prefab == (Object)null)
			{
				return false;
			}
			if ((Object)(object)prefab.GetComponent<MineRock>() != (Object)null || (Object)(object)prefab.GetComponent<MineRock5>() != (Object)null)
			{
				return true;
			}
			Destructible component = prefab.GetComponent<Destructible>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			string text = ((((Object)prefab).name != null) ? ((Object)prefab).name.ToLowerInvariant() : string.Empty);
			if (text.StartsWith("rock") || text.StartsWith("minerock"))
			{
				return true;
			}
			if (text.Contains("copper") || text.Contains("tin") || text.Contains("obsidian") || text.Contains("silvervein") || text.Contains("silver_vein") || text.Contains("flametal") || text.Contains("marble"))
			{
				return true;
			}
			if (text.StartsWith("mudpile") || text.Contains("scrap_pile") || text.Contains("scrappile"))
			{
				return true;
			}
			if (text.Contains("oredeposit") || text.Contains("ore_deposit") || text.Contains("mineraldeposit") || text.Contains("mineral_deposit"))
			{
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class BeastRunes_Player_Update_MinePickingPatch
	{
		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				MinePickingSystem.Tick();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDestroy")]
	internal static class BeastRunes_Player_Destroy_MinePickingPatch
	{
		private static void Prefix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				MinePickingSystem.Cancel(notify: false);
			}
		}
	}
	public static class ToolSlots
	{
		private sealed class PendingToolReturn
		{
			public Player Player;

			public ItemData Item;

			public ToolSlotType SlotType;

			public float NotBefore;
		}

		public enum ToolSlotType
		{
			Axe,
			Pickaxe,
			Dagger
		}

		public class ToolSlotsCtrlDrag : MonoBehaviour, IBeginDragHandler, IEventSystemHandler, IDragHandler, IEndDragHandler
		{
			public RectTransform Target;

			public Canvas Canvas;

			private bool _dragging;

			private bool _moved;

			private bool _suppressNextClick;

			private Vector2 _dragStartPosition;

			private static bool CtrlHeld()
			{
				return Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305);
			}

			public void OnBeginDrag(PointerEventData eventData)
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_004a: Unknown result type (might be due to invalid IL or missing references)
				_dragging = false;
				_moved = false;
				if (eventData != null && (int)eventData.button == 0 && CtrlHeld() && !((Object)(object)Target == (Object)null))
				{
					_dragging = true;
					_dragStartPosition = Target.anchoredPosition;
				}
			}

			public void OnDrag(PointerEventData eventData)
			{
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0065: Unknown result type (might be due to invalid IL or missing references)
				//IL_006b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0070: Unknown result type (might be due to invalid IL or missing references)
				//IL_0089: Unknown result type (might be due to invalid IL or missing references)
				//IL_008f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0094: Unknown result type (might be due to invalid IL or missing references)
				//IL_0099: Unknown result type (might be due to invalid IL or missing references)
				if (!_dragging || eventData == null || (Object)(object)Target == (Object)null)
				{
					return;
				}
				float num = (((Object)(object)Canvas != (Object)null && Canvas.scaleFactor > 0f) ? Canvas.scaleFactor : 1f);
				RectTransform target = Target;
				target.anchoredPosition += eventData.delta / num;
				if (!_moved)
				{
					Vector2 val = Target.anchoredPosition - _dragStartPosition;
					if (((Vector2)(ref val)).sqrMagnitude > 1f)
					{
						_moved = true;
					}
				}
			}

			public void OnEndDrag(PointerEventData eventData)
			{
				//IL_0048: Unknown result type (might be due to invalid IL or missing references)
				//IL_0063: Unknown result type (might be due to invalid IL or missing references)
				if (_dragging)
				{
					_dragging = false;
					if (_moved && !((Object)(object)Target == (Object)null))
					{
						_suppressNextClick = true;
						PlayerPrefs.SetFloat("BeastRunes.ToolSlots.PosX", Target.anchoredPosition.x);
						PlayerPrefs.SetFloat("BeastRunes.ToolSlots.PosY", Target.anchoredPosition.y);
						PlayerPrefs.Save();
					}
				}
			}

			public bool ConsumeSuppressedClick()
			{
				if (!_suppressNextClick)
				{
					return false;
				}
				_suppressNextClick = false;
				return true;
			}
		}

		public class ToolSlotDeferredDropDriver : MonoBehaviour
		{
			private InventoryGui _gui;

			private Player _player;

			private ItemData _item;

			private ToolSlotType _slotType;

			private int _waitFrames;

			private bool _pending;

			public void Queue(InventoryGui gui, Player player, ToolSlotType slotType, ItemData item)
			{
				_gui = gui;
				_player = player;
				_slotType = slotType;
				_item = item;
				_waitFrames = 1;
				_pending = true;
			}

			private void LateUpdate()
			{
				if (!_pending)
				{
					return;
				}
				if (_waitFrames > 0)
				{
					_waitFrames--;
					return;
				}
				InventoryGui gui = _gui;
				Player player = _player;
				ItemData item = _item;
				ToolSlotType slotType = _slotType;
				_pending = false;
				_gui = null;
				_player = null;
				_item = null;
				if (!((Object)(object)gui == (Object)null) && !((Object)(object)player == (Object)null) && item != null && gui.m_dragItem == item)
				{
					StoreDraggedTool(gui, player, slotType, item);
				}
			}
		}

		public class ToolSlotDragReleaseDriver : MonoBehaviour
		{
			private InventoryGui _gui;

			private int _lateFramesRemaining;

			public void Begin(InventoryGui gui)
			{
				_gui = gui;
				_lateFramesRemaining = 1;
			}

			private void LateUpdate()
			{
				if (!((Object)(object)_gui == (Object)null) && _lateFramesRemaining > 0)
				{
					ClearInventoryDrag(_gui);
					_lateFramesRemaining--;
					if (_lateFramesRemaining <= 0)
					{
						_gui = null;
					}
				}
			}
		}

		public class ToolSlotVisual : MonoBehaviour
		{
			public Image Placeholder;

			public Image ItemIcon;
		}

		public class ToolSlotPointerHandler : MonoBehaviour, IPointerClickHandler, IEventSystemHandler
		{
			public int SlotIndex;

			public void OnPointerClick(PointerEventData eventData)
			{
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Invalid comparison between Unknown and I4
				if (eventData != null && (int)eventData.button == 1)
				{
					ReturnToolToInventory((ToolSlotType)SlotIndex);
				}
			}
		}

		[HarmonyPatch]
		private static class InventoryGui_UpdateItemDrag_NoItemSafety_Patch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(InventoryGui), "UpdateItemDrag", (Type[])null, (Type[])null);
			}

			private static bool Prefix(InventoryGui __instance)
			{
				if ((Object)(object)__instance == (Object)null)
				{
					return false;
				}
				try
				{
					ItemData value = Traverse.Create((object)__instance).Field("m_dragItem").GetValue<ItemData>();
					if (value == null)
					{
						return false;
					}
				}
				catch
				{
					return true;
				}
				return true;
			}
		}

		[HarmonyPatch]
		private static class InventoryGui_OnDropOutside_ToolSlotsSafety_Patch
		{
			private static MethodBase TargetMethod()
			{
				return AccessTools.Method(typeof(InventoryGui), "OnDropOutside", (Type[])null, (Type[])null);
			}

			private static bool Prefix(InventoryGui __instance)
			{
				if ((Object)(object)__instance == (Object)null)
				{
					return false;
				}
				ItemData val = null;
				try
				{
					val = Traverse.Create((object)__instance).Field("m_dragItem").GetValue<ItemData>();
				}
				catch
				{
					return true;
				}
				if (val == null)
				{
					return false;
				}
				return tru