Decompiled source of ItemRetriever v1.0.1

ItemRetriever.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using HowToFish.ItemRetriever.Config;
using HowToFish.ItemRetriever.Models;
using HowToFish.ItemRetriever.Systems;
using HowToFish.ItemRetriever.Utils;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("ItemRetriever")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ItemRetriever")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("D181CDA7-EF07-4BBC-B975-2B80FC6BBFAE")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace HowToFish.ItemRetriever
{
	[BepInPlugin("odinplus.itemretriever", "Item Retriever", "1.0.1")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "odinplus.itemretriever";

		public const string PluginName = "Item Retriever";

		public const string PluginVersion = "1.0.1";

		private Harmony _harmony;

		private GameBridge _bridge;

		private ItemFinder _finder;

		private RetrieverLogic _logic;

		private NetworkSync _network;

		private IDisposable _consoleRegistration;

		private float _nextConsoleAttempt;

		private float _cooldownUntil;

		private bool _loggedReady;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			RetrieverConfig.Bind(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Config.Save();
			_bridge = new GameBridge(((BaseUnityPlugin)this).Logger);
			_network = new NetworkSync(((BaseUnityPlugin)this).Logger, _bridge);
			_finder = new ItemFinder(((BaseUnityPlugin)this).Logger, _bridge);
			_logic = new RetrieverLogic(((BaseUnityPlugin)this).Logger, _bridge, _finder, _network);
			_harmony = new Harmony("odinplus.itemretriever");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			TryRegisterConsoleCommand();
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Hotkey: {2}.", "Item Retriever", "1.0.1", RetrieverConfig.RetrieveKey.Value));
		}

		private void Update()
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			if (_consoleRegistration == null && Time.unscaledTime >= _nextConsoleAttempt)
			{
				_nextConsoleAttempt = Time.unscaledTime + 2f;
				TryRegisterConsoleCommand();
			}
			if (RetrieverConfig.Enabled.Value)
			{
				if (!_loggedReady && _bridge.GetLocalPlayer() != null)
				{
					_loggedReady = true;
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Local player found. Item Retriever is ready.");
				}
				if (!ShouldIgnoreHotkey() && Input.GetKeyDown(RetrieverConfig.RetrieveKey.Value))
				{
					RunRetrieve("hotkey");
				}
			}
		}

		private void OnDestroy()
		{
			_consoleRegistration?.Dispose();
			_consoleRegistration = null;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			if (Instance == this)
			{
				Instance = null;
			}
		}

		internal RetrieveResult RunRetrieve(string source)
		{
			if (!RetrieverConfig.Enabled.Value)
			{
				return Fail("Item Retriever is disabled in config.");
			}
			if (Time.unscaledTime < _cooldownUntil)
			{
				float num = _cooldownUntil - Time.unscaledTime;
				return Fail($"On cooldown ({num:0.0}s left).");
			}
			object localPlayer = _bridge.GetLocalPlayer();
			if (localPlayer == null)
			{
				return Fail("No local player yet. Load into an island first.");
			}
			if (RetrieverConfig.HostOnly.Value && !_network.IsServer)
			{
				return Fail("Only the lobby host can retrieve items.");
			}
			if (!_network.CanRetrieve)
			{
				return Fail("This client does not have authority to move world items. Ask the host, or disable HostOnly if you own the items.");
			}
			RetrieveResult retrieveResult = _logic.TryRetrieve(localPlayer, source);
			if (retrieveResult.Success)
			{
				float num2 = Mathf.Max(0f, RetrieverConfig.CooldownSeconds.Value);
				if (num2 > 0f)
				{
					_cooldownUntil = Time.unscaledTime + num2;
				}
			}
			Feedback(retrieveResult);
			return retrieveResult;
		}

		private bool ShouldIgnoreHotkey()
		{
			if (RetrieverConfig.IgnoreWhenChatOpen.Value && _bridge.IsChatOrConsoleBlocking())
			{
				return true;
			}
			try
			{
				object localPlayer = _bridge.GetLocalPlayer();
				if (localPlayer != null && _bridge.IsPlayerBlockingInputs(localPlayer))
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		private void Feedback(RetrieveResult result)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			string text = result.Message ?? (result.Success ? "Items retrieved." : "Nothing to retrieve.");
			if (RetrieverConfig.LogToConsole.Value)
			{
				if (result.Success)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)text);
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)text);
				}
			}
			if (RetrieverConfig.ShowChatMessage.Value)
			{
				_bridge.TrySendLocalChat(text);
			}
			if (result.Success && RetrieverConfig.ScreenShake.Value)
			{
				_bridge.TryShake(0.25f, 8, new Vector2(0.12f, 0.12f));
			}
			WriteModConsole(text, result.Success ? "info" : "warn");
		}

		private RetrieveResult Fail(string message)
		{
			RetrieveResult result = RetrieveResult.Failed(message);
			Feedback(result);
			return result;
		}

		private void TryRegisterConsoleCommand()
		{
			if (_consoleRegistration != null)
			{
				return;
			}
			try
			{
				MethodInfo methodInfo = Type.GetType("HowToFish.ModConsole.ModConsoleApi, HowToFish.ModConsole", throwOnError: false)?.GetMethod("RegisterCommand", BindingFlags.Static | BindingFlags.Public, null, new Type[6]
				{
					typeof(string),
					typeof(string),
					typeof(string),
					typeof(string),
					typeof(Func<string[], string>),
					typeof(string[])
				}, null);
				if (!(methodInfo == null))
				{
					Func<string[], string> func = ExecuteConsoleCommand;
					_consoleRegistration = methodInfo.Invoke(null, new object[6]
					{
						"odinplus.itemretriever",
						"retrieve",
						"Pull lost weapons and items back to you.",
						"retrieve [status|now]",
						func,
						new string[3] { "retriever", "getitem", "bringitems" }
					}) as IDisposable;
					if (_consoleRegistration != null)
					{
						((BaseUnityPlugin)this).Logger.LogInfo((object)"Registered retrieve command with Mod Console.");
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Mod Console not available yet: " + ex.Message));
			}
		}

		private string ExecuteConsoleCommand(string[] args)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			string text = ((args != null && args.Length != 0) ? args[0].Trim() : "now");
			if (text.Equals("status", StringComparison.OrdinalIgnoreCase) || text.Equals("help", StringComparison.OrdinalIgnoreCase))
			{
				object localPlayer = _bridge.GetLocalPlayer();
				int num = ((localPlayer != null) ? _finder.CountCandidates(_bridge.GetPlayerPosition(localPlayer)) : 0);
				float num2 = Mathf.Max(0f, _cooldownUntil - Time.unscaledTime);
				return string.Format("Item Retriever {0} | enabled={1} | ", "1.0.1", RetrieverConfig.Enabled.Value) + $"host={_network.IsServer} | canRetrieve={_network.CanRetrieve} | " + $"candidates={num} | cooldown={num2:0.0}s | key={RetrieverConfig.RetrieveKey.Value}";
			}
			RetrieveResult retrieveResult = RunRetrieve("console");
			return retrieveResult.Message;
		}

		private static void WriteModConsole(string text, string level)
		{
			try
			{
				(Type.GetType("HowToFish.ModConsole.ModConsoleApi, HowToFish.ModConsole", throwOnError: false)?.GetMethod("WriteLine", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
				{
					typeof(string),
					typeof(string)
				}, null))?.Invoke(null, new object[2] { text, level });
			}
			catch
			{
			}
		}
	}
}
namespace HowToFish.ItemRetriever.Utils
{
	internal static class ReflectionUtil
	{
		public const BindingFlags All = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		public static Type FindType(params string[] names)
		{
			foreach (string typeName in names)
			{
				Type type = Type.GetType(typeName, throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
			return null;
		}

		public static FieldInfo FindField(Type type, params string[] names)
		{
			if (type == null)
			{
				return null;
			}
			foreach (string name in names)
			{
				FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field;
				}
			}
			FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				foreach (string value in names)
				{
					if (fieldInfo.Name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return fieldInfo;
					}
				}
			}
			return (type.BaseType != null && type.BaseType != typeof(object)) ? FindField(type.BaseType, names) : null;
		}

		public static PropertyInfo FindProperty(Type type, params string[] names)
		{
			if (type == null)
			{
				return null;
			}
			foreach (string name in names)
			{
				PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					return property;
				}
			}
			PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (PropertyInfo propertyInfo in properties)
			{
				foreach (string value in names)
				{
					if (propertyInfo.Name.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return propertyInfo;
					}
				}
			}
			return (type.BaseType != null && type.BaseType != typeof(object)) ? FindProperty(type.BaseType, names) : null;
		}

		public static MethodInfo FindMethod(Type type, string name, int parameterCount)
		{
			if (type == null)
			{
				return null;
			}
			MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (methodInfo.Name == name && methodInfo.GetParameters().Length == parameterCount)
				{
					return methodInfo;
				}
			}
			return (type.BaseType != null && type.BaseType != typeof(object)) ? FindMethod(type.BaseType, name, parameterCount) : null;
		}

		public static object GetMember(object instance, MemberInfo member)
		{
			try
			{
				if (member is FieldInfo fieldInfo)
				{
					return fieldInfo.GetValue(fieldInfo.IsStatic ? null : instance);
				}
				if (member is PropertyInfo { CanRead: not false } propertyInfo)
				{
					MethodInfo getMethod = propertyInfo.GetGetMethod(nonPublic: true);
					bool flag = getMethod != null && getMethod.IsStatic;
					return propertyInfo.GetValue(flag ? null : instance, null);
				}
			}
			catch
			{
				return null;
			}
			return null;
		}

		public static bool SetMember(object instance, MemberInfo member, object value)
		{
			try
			{
				if (member is FieldInfo fieldInfo)
				{
					fieldInfo.SetValue(fieldInfo.IsStatic ? null : instance, ConvertTo(value, fieldInfo.FieldType));
					return true;
				}
				if (member is PropertyInfo { CanWrite: not false } propertyInfo)
				{
					MethodInfo setMethod = propertyInfo.GetSetMethod(nonPublic: true);
					bool flag = setMethod != null && setMethod.IsStatic;
					propertyInfo.SetValue(flag ? null : instance, ConvertTo(value, propertyInfo.PropertyType), null);
					return true;
				}
			}
			catch
			{
				return false;
			}
			return false;
		}

		public static bool TryReadInt(object instance, MemberInfo member, out int value)
		{
			value = 0;
			object member2 = GetMember(instance, member);
			if (member2 == null)
			{
				return false;
			}
			try
			{
				value = Convert.ToInt32(member2, CultureInfo.InvariantCulture);
				return true;
			}
			catch
			{
				return false;
			}
		}

		public static bool TryReadFloat(object instance, MemberInfo member, out float value)
		{
			value = 0f;
			object member2 = GetMember(instance, member);
			if (member2 == null)
			{
				return false;
			}
			try
			{
				value = Convert.ToSingle(member2, CultureInfo.InvariantCulture);
				return true;
			}
			catch
			{
				return false;
			}
		}

		public static bool IsAlive(object value)
		{
			if (value == null)
			{
				return false;
			}
			Object val = (Object)((value is Object) ? value : null);
			if (val != null)
			{
				return Object.op_Implicit(val);
			}
			return true;
		}

		public static GameObject AsGameObject(object value)
		{
			GameObject val = (GameObject)((value is GameObject) ? value : null);
			if (val != null)
			{
				return val;
			}
			Component val2 = (Component)((value is Component) ? value : null);
			if (val2 != null)
			{
				return val2.gameObject;
			}
			return null;
		}

		public static IEnumerable<string> SplitList(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				yield break;
			}
			string[] parts = raw.Split(new char[3] { ',', ';', '|' }, StringSplitOptions.RemoveEmptyEntries);
			string[] array = parts;
			foreach (string part in array)
			{
				string trimmed = part.Trim();
				if (trimmed.Length > 0)
				{
					yield return trimmed;
				}
			}
		}

		public static bool NameMatches(string haystack, IEnumerable<string> needles)
		{
			if (string.IsNullOrEmpty(haystack))
			{
				return false;
			}
			foreach (string needle in needles)
			{
				if (haystack.IndexOf(needle, StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			return false;
		}

		private static object ConvertTo(object value, Type target)
		{
			if (value == null)
			{
				return null;
			}
			if (target.IsInstanceOfType(value))
			{
				return value;
			}
			if (target.IsEnum)
			{
				return (value is string value2) ? Enum.Parse(target, value2, ignoreCase: true) : Enum.ToObject(target, Convert.ToInt64(value, CultureInfo.InvariantCulture));
			}
			if (target == typeof(int))
			{
				return Convert.ToInt32(value, CultureInfo.InvariantCulture);
			}
			if (target == typeof(float))
			{
				return Convert.ToSingle(value, CultureInfo.InvariantCulture);
			}
			if (target == typeof(double))
			{
				return Convert.ToDouble(value, CultureInfo.InvariantCulture);
			}
			return Convert.ChangeType(value, target, CultureInfo.InvariantCulture);
		}
	}
}
namespace HowToFish.ItemRetriever.Systems
{
	public sealed class GameBridge
	{
		private readonly ManualLogSource _log;

		private float _nextRefresh;

		private bool _isCacheInitialized;

		private Type _playerType;

		private FieldInfo _localPlayerField;

		private PropertyInfo _blockInputsProperty;

		private PropertyInfo _holdingProperty;

		private PropertyInfo _screenShakeProperty;

		private PropertyInfo _heldItemProperty;

		private MethodInfo _shakeMethod;

		private Type _gameInfoType;

		private Type _waterManagerType;

		private MethodInfo _isUnderWaterMethod;

		private Type _instanceFinderType;

		private PropertyInfo _networkManagerProperty;

		private PropertyInfo _isServerStartedProperty;

		private PropertyInfo _isClientStartedProperty;

		private Type _chatManagerType;

		private MethodInfo _chatSendMethod;

		private FieldInfo _chatInstanceField;

		private MemberInfo _moneyMember;

		private object _moneyHost;

		private bool _moneyResolved;

		private Type _networkObjectType;

		private PropertyInfo _isOwnerProperty;

		private PropertyInfo _isServerInitializedProperty;

		public bool IsServer
		{
			get
			{
				EnsureCache();
				object networkManager = GetNetworkManager();
				if (networkManager == null)
				{
					return true;
				}
				CacheNetwork(networkManager.GetType());
				return ReadBool(_isServerStartedProperty, networkManager);
			}
		}

		public bool IsClient
		{
			get
			{
				EnsureCache();
				object networkManager = GetNetworkManager();
				if (networkManager == null)
				{
					return false;
				}
				CacheNetwork(networkManager.GetType());
				return ReadBool(_isClientStartedProperty, networkManager);
			}
		}

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

		public object GetLocalPlayer()
		{
			EnsureCache();
			try
			{
				object obj = _localPlayerField?.GetValue(null);
				return ReflectionUtil.IsAlive(obj) ? obj : null;
			}
			catch
			{
				return null;
			}
		}

		public Transform GetPlayerTransform(object player)
		{
			GameObject val = ReflectionUtil.AsGameObject(player);
			return Object.op_Implicit((Object)(object)val) ? val.transform : null;
		}

		public Transform GetPlayerCamTransform(object player)
		{
			if (player == null)
			{
				return null;
			}
			try
			{
				PropertyInfo property = player.GetType().GetProperty("CamObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					object value = property.GetValue(player, null);
					Transform val = (Transform)((value is Transform) ? value : null);
					if (val != null && Object.op_Implicit((Object)(object)val))
					{
						return val;
					}
					GameObject val2 = (GameObject)((value is GameObject) ? value : null);
					if (val2 != null && Object.op_Implicit((Object)(object)val2))
					{
						return val2.transform;
					}
					Component val3 = (Component)((value is Component) ? value : null);
					if (val3 != null && Object.op_Implicit((Object)(object)val3))
					{
						return val3.transform;
					}
				}
				FieldInfo field = player.GetType().GetField("CamObject", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object value2 = field.GetValue(player);
					Transform val4 = (Transform)((value2 is Transform) ? value2 : null);
					if (val4 != null && Object.op_Implicit((Object)(object)val4))
					{
						return val4;
					}
					GameObject val5 = (GameObject)((value2 is GameObject) ? value2 : null);
					if (val5 != null && Object.op_Implicit((Object)(object)val5))
					{
						return val5.transform;
					}
					Component val6 = (Component)((value2 is Component) ? value2 : null);
					if (val6 != null && Object.op_Implicit((Object)(object)val6))
					{
						return val6.transform;
					}
				}
			}
			catch
			{
			}
			return GetPlayerTransform(player);
		}

		public Vector3 GetPlayerPosition(object player)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Transform playerCamTransform = GetPlayerCamTransform(player);
			if (Object.op_Implicit((Object)(object)playerCamTransform))
			{
				return playerCamTransform.position;
			}
			Transform playerTransform = GetPlayerTransform(player);
			return Object.op_Implicit((Object)(object)playerTransform) ? playerTransform.position : Vector3.zero;
		}

		public Vector3 GetPlayerForward(object player)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			Transform playerCamTransform = GetPlayerCamTransform(player);
			if (Object.op_Implicit((Object)(object)playerCamTransform))
			{
				Vector3 forward = playerCamTransform.forward;
				forward.y = 0f;
				if (((Vector3)(ref forward)).sqrMagnitude > 0.001f)
				{
					return ((Vector3)(ref forward)).normalized;
				}
			}
			Transform playerTransform = GetPlayerTransform(player);
			if (!Object.op_Implicit((Object)(object)playerTransform))
			{
				return Vector3.forward;
			}
			Vector3 forward2 = playerTransform.forward;
			forward2.y = 0f;
			return (((Vector3)(ref forward2)).sqrMagnitude > 0.001f) ? ((Vector3)(ref forward2)).normalized : Vector3.forward;
		}

		public object GetHeldItem(object player)
		{
			EnsureCache();
			try
			{
				object obj = _holdingProperty?.GetValue(player, null);
				return (obj == null) ? null : _heldItemProperty?.GetValue(obj, null);
			}
			catch
			{
				return null;
			}
		}

		public bool IsPlayerBlockingInputs(object player)
		{
			EnsureCache();
			try
			{
				bool flag = default(bool);
				int num;
				if (_blockInputsProperty != null)
				{
					object value = _blockInputsProperty.GetValue(player, null);
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		public bool IsUnderWater(Vector3 position)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			EnsureCache();
			try
			{
				object obj = _isUnderWaterMethod?.Invoke(null, new object[1] { position });
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return position.y < -1f;
			}
		}

		public bool IsLikelyLava(Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (position.y > 2f)
			{
				return false;
			}
			Collider[] array = Physics.OverlapSphere(position, 1.2f, -1, (QueryTriggerInteraction)1);
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					string name = ((Object)((Component)val).gameObject).name;
					if (name.IndexOf("lava", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("magma", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("volcano", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
				}
			}
			return false;
		}

		public bool HasLineOfSight(Vector3 from, Vector3 to)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = to - from;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < 0.25f)
			{
				return true;
			}
			return !Physics.Raycast(from + Vector3.up * 0.4f, ((Vector3)(ref val)).normalized, magnitude - 0.2f, -5, (QueryTriggerInteraction)1);
		}

		public bool IsNetworkObjectOwner(GameObject gameObject)
		{
			EnsureCache();
			if (!Object.op_Implicit((Object)(object)gameObject) || _networkObjectType == null)
			{
				return false;
			}
			Component component = gameObject.GetComponent(_networkObjectType);
			if (!Object.op_Implicit((Object)(object)component))
			{
				return false;
			}
			try
			{
				object obj = _isOwnerProperty?.GetValue(component, null);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		public bool IsNetworkObjectServerInit(GameObject gameObject)
		{
			EnsureCache();
			if (!Object.op_Implicit((Object)(object)gameObject) || _networkObjectType == null || _isServerInitializedProperty == null)
			{
				return IsServer;
			}
			Component component = gameObject.GetComponent(_networkObjectType);
			if (!Object.op_Implicit((Object)(object)component))
			{
				return IsServer;
			}
			try
			{
				object value = _isServerInitializedProperty.GetValue(component, null);
				bool flag = default(bool);
				int num;
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return IsServer;
			}
		}

		public bool TryGetMoney(object player, out int money)
		{
			money = 0;
			EnsureMoney(player);
			return ReflectionUtil.TryReadInt(_moneyHost ?? player, _moneyMember, out money);
		}

		public bool TrySetMoney(object player, int money)
		{
			EnsureMoney(player);
			return _moneyMember != null && ReflectionUtil.SetMember(_moneyHost ?? player, _moneyMember, money);
		}

		public bool TryCharge(object player, int amount, out int charged, out string error)
		{
			charged = 0;
			error = null;
			if (amount <= 0)
			{
				return true;
			}
			if (!TryGetMoney(player, out var money))
			{
				error = "Could not resolve wallet field.";
				_log.LogWarning((object)"Money member not resolved yet. Cost skipped.");
				return true;
			}
			if (money < amount)
			{
				error = $"Need ${amount}, you have ${money}.";
				return false;
			}
			if (!TrySetMoney(player, money - amount))
			{
				error = "Failed to update balance.";
				return false;
			}
			charged = amount;
			return true;
		}

		public void TryShake(float intensity, int count, Vector2 range)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			EnsureCache();
			object localPlayer = GetLocalPlayer();
			if (localPlayer == null || _shakeMethod == null)
			{
				return;
			}
			try
			{
				object obj = _screenShakeProperty?.GetValue(localPlayer, null);
				if (obj != null)
				{
					_shakeMethod.Invoke(obj, new object[3] { intensity, count, range });
				}
			}
			catch
			{
			}
		}

		public void TrySendLocalChat(string message)
		{
			EnsureCache();
			if (string.IsNullOrEmpty(message))
			{
				return;
			}
			try
			{
				object obj = _chatInstanceField?.GetValue(null);
				if (obj != null && _chatSendMethod != null)
				{
					ParameterInfo[] parameters = _chatSendMethod.GetParameters();
					if (parameters.Length == 1 && parameters[0].ParameterType == typeof(string))
					{
						_chatSendMethod.Invoke(obj, new object[1] { message });
						return;
					}
				}
			}
			catch
			{
			}
			_log.LogInfo((object)message);
		}

		public bool IsChatOrConsoleBlocking()
		{
			try
			{
				object obj = (Type.GetType("HowToFish.ModConsole.ModConsoleApi, HowToFish.ModConsole", throwOnError: false)?.GetProperty("IsOpen", BindingFlags.Static | BindingFlags.Public))?.GetValue(null, null);
				bool flag = default(bool);
				int num;
				if (obj is bool)
				{
					flag = (bool)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					return true;
				}
			}
			catch
			{
			}
			return false;
		}

		public IEnumerable<GameObject> FindSceneObjects()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			List<GameObject> list = new List<GameObject>();
			for (int i = 0; i < SceneManager.sceneCount; i++)
			{
				Scene sceneAt = SceneManager.GetSceneAt(i);
				if (((Scene)(ref sceneAt)).isLoaded)
				{
					list.AddRange(((Scene)(ref sceneAt)).GetRootGameObjects());
				}
			}
			List<GameObject> list2 = new List<GameObject>();
			foreach (GameObject item in list)
			{
				Transform[] componentsInChildren = item.GetComponentsInChildren<Transform>(true);
				foreach (Transform val in componentsInChildren)
				{
					list2.Add(((Component)val).gameObject);
				}
			}
			return list2;
		}

		private object GetNetworkManager()
		{
			try
			{
				return _networkManagerProperty?.GetValue(null, null);
			}
			catch
			{
				return null;
			}
		}

		private void EnsureMoney(object player)
		{
			if (_moneyResolved)
			{
				return;
			}
			EnsureCache();
			string[] names = new string[11]
			{
				"Money", "money", "_money", "Cash", "cash", "_cash", "Gold", "Coins", "Wallet", "Balance",
				"PlayerMoney"
			};
			List<object> list = new List<object>();
			if (player != null)
			{
				list.Add(player);
			}
			if (_gameInfoType != null)
			{
				list.Add(_gameInfoType);
			}
			foreach (object item in list)
			{
				Type type = ((item is Type type2) ? type2 : item.GetType());
				object obj = ((item is Type) ? null : item);
				MemberInfo memberInfo = (MemberInfo)(((object)ReflectionUtil.FindProperty(type, names)) ?? ((object)ReflectionUtil.FindField(type, names)));
				if (memberInfo == null || !ReflectionUtil.TryReadInt(obj, memberInfo, out var _))
				{
					continue;
				}
				_moneyMember = memberInfo;
				_moneyHost = obj;
				_moneyResolved = true;
				_log.LogInfo((object)("Money bound to " + type.Name + "." + memberInfo.Name));
				break;
			}
		}

		private void EnsureCache()
		{
			if (Time.unscaledTime < _nextRefresh && _isCacheInitialized)
			{
				return;
			}
			_nextRefresh = Time.unscaledTime + 2f;
			_playerType = _playerType ?? ReflectionUtil.FindType("Player, Assembly-CSharp");
			_localPlayerField = _localPlayerField ?? ReflectionUtil.FindField(_playerType, "LocalPlayer");
			_blockInputsProperty = _blockInputsProperty ?? ReflectionUtil.FindProperty(_playerType, "BlockInputs");
			_holdingProperty = _holdingProperty ?? ReflectionUtil.FindProperty(_playerType, "Holding");
			_screenShakeProperty = _screenShakeProperty ?? ReflectionUtil.FindProperty(_playerType, "ScreenShake");
			Type type = ReflectionUtil.FindType("PlayerHolding, Assembly-CSharp");
			_heldItemProperty = _heldItemProperty ?? ReflectionUtil.FindProperty(type, "HeldItem");
			Type type2 = ReflectionUtil.FindType("PlayerScreenShake, Assembly-CSharp");
			_shakeMethod = _shakeMethod ?? type2?.GetMethod("Shake", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_gameInfoType = _gameInfoType ?? ReflectionUtil.FindType("GameInfo, Assembly-CSharp");
			_waterManagerType = _waterManagerType ?? ReflectionUtil.FindType("WaterManager, Assembly-CSharp");
			_isUnderWaterMethod = _isUnderWaterMethod ?? _waterManagerType?.GetMethod("IsUnderWater", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Vector3) }, null);
			_instanceFinderType = _instanceFinderType ?? ReflectionUtil.FindType("FishNet.InstanceFinder, FishNet.Runtime");
			_networkManagerProperty = _networkManagerProperty ?? _instanceFinderType?.GetProperty("NetworkManager", BindingFlags.Static | BindingFlags.Public);
			_networkObjectType = _networkObjectType ?? ReflectionUtil.FindType("FishNet.Object.NetworkObject, FishNet.Runtime");
			_isOwnerProperty = _isOwnerProperty ?? _networkObjectType?.GetProperty("IsOwner", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_isServerInitializedProperty = _isServerInitializedProperty ?? _networkObjectType?.GetProperty("IsServerInitialized", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_chatManagerType = _chatManagerType ?? ReflectionUtil.FindType("ChatManager, Assembly-CSharp");
			_chatInstanceField = _chatInstanceField ?? ReflectionUtil.FindField(_chatManagerType, "_instance", "Instance", "instance");
			if (_chatManagerType != null && _chatSendMethod == null)
			{
				string[] array = new string[6] { "SendMessage", "AddMessage", "PostMessage", "Write", "Say", "LocalMessage" };
				foreach (string name in array)
				{
					MethodInfo methodInfo = ReflectionUtil.FindMethod(_chatManagerType, name, 1);
					if (methodInfo != null)
					{
						_chatSendMethod = methodInfo;
						break;
					}
				}
			}
			_isCacheInitialized = _playerType != null && _chatManagerType != null;
		}

		private void CacheNetwork(Type type)
		{
			_isServerStartedProperty = _isServerStartedProperty ?? type.GetProperty("IsServerStarted", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_isClientStartedProperty = _isClientStartedProperty ?? type.GetProperty("IsClientStarted", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static bool ReadBool(PropertyInfo property, object instance)
		{
			try
			{
				bool flag = default(bool);
				int num;
				if (property != null)
				{
					object value = property.GetValue(instance, null);
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}
	}
	public sealed class ItemFinder
	{
		private static readonly string[] ItemHints = new string[5] { "Pickup", "InteractableItem", "DroppedItem", "InventoryItem", "HoldableItem" };

		private static readonly string[] FishHints = new string[6] { "Fish", "Shark", "Crab", "Whale", "Leech", "Piranha" };

		private static readonly string[] BaitHints = new string[3] { "Bait", "Lure", "Hook" };

		private static readonly string[] BoatHints = new string[4] { "Boat", "Propeller", "Hull", "Rudder" };

		private static readonly string[] WeaponHints = new string[8] { "Weapon", "Gun", "Rifle", "Pistol", "Shotgun", "Sniper", "Knife", "Melee" };

		private static readonly string[] ToolHints = new string[9] { "Tool", "Hammer", "Axe", "Rod", "FishingRod", "FishingRodCrab", "Rod", "Pole", "FishingPole" };

		private static readonly string[] RodHints = new string[9] { "Rod", "FishingRod", "Fishing Rod", "Fishing Rod(Clone)", "Crab Fishing Rod", "Crab Fishing Rod(Clone)", "FishRod", "Pole", "FishingPole" };

		private static readonly string[] DurabilityNames = new string[4] { "Durability", "Condition", "CurrentDurability", "_durability" };

		private static readonly string[] MaxDurabilityNames = new string[3] { "MaxDurability", "MaxHealth", "_maxDurability" };

		private readonly ManualLogSource _log;

		private readonly GameBridge _bridge;

		public ItemFinder(ManualLogSource log, GameBridge bridge)
		{
			_log = log;
			_bridge = bridge;
		}

		public int CountCandidates(Vector3 origin)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			return Collect(origin, _bridge.GetLocalPlayer()).Count;
		}

		public List<LostItem> Find(Vector3 origin, object player)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			return Collect(origin, player);
		}

		private List<LostItem> Collect(Vector3 origin, object player)
		{
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			List<LostItem> list = new List<LostItem>();
			HashSet<int> seenRootIds = new HashSet<int>();
			object obj = (RetrieverConfig.IgnoreHeldItem.Value ? _bridge.GetHeldItem(player) : null);
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				obj2 = ((obj is Item) ? obj : null);
				if (obj2 == null)
				{
					object obj3 = ((obj is Component) ? obj : null);
					obj2 = ((obj3 != null) ? ((Component)obj3).GetComponentInParent<Item>() : null);
				}
			}
			Item val = (Item)obj2;
			GameObject val2 = (((Object)(object)val != (Object)null) ? ((Component)val).gameObject : null);
			int heldId = (((Object)(object)val2 != (Object)null) ? GetObjectId(val2) : 0);
			float num = Mathf.Max(0f, RetrieverConfig.MinRange.Value);
			float value = RetrieverConfig.MaxRange.Value;
			int num2 = Mathf.Max(1, RetrieverConfig.MaxItemsPerUse.Value);
			int num3 = 0;
			int num4 = 0;
			if ((Object)(object)ItemManager.Instance != (Object)null && ItemManager.Items != null)
			{
				foreach (KeyValuePair<Transform, Item> item in ItemManager.Items)
				{
					Item value2 = item.Value;
					if ((Object)(object)value2 == (Object)null)
					{
						continue;
					}
					num3++;
					if ((Object)(object)value2.Holder != (Object)null || (Object)(object)value2.SyncedHolder != (Object)null || (Object)(object)value2.BirdHolder != (Object)null)
					{
						continue;
					}
					GameObject gameObjectFromItem = GetGameObjectFromItem(value2);
					if (!((Object)(object)gameObjectFromItem == (Object)null))
					{
						GameObject itemRoot = GetItemRoot(gameObjectFromItem);
						if (!((Object)(object)itemRoot == (Object)null))
						{
							ProcessCandidate(itemRoot, origin, heldId, val2, num, value, seenRootIds, list);
						}
					}
				}
			}
			if (ItemManager._itemsUnderwater != null)
			{
				try
				{
					foreach (Item item2 in ItemManager._itemsUnderwater)
					{
						if ((Object)(object)item2 == (Object)null)
						{
							continue;
						}
						num4++;
						if ((Object)(object)item2.Holder != (Object)null || (Object)(object)item2.SyncedHolder != (Object)null || (Object)(object)item2.BirdHolder != (Object)null)
						{
							continue;
						}
						GameObject gameObjectFromItem2 = GetGameObjectFromItem(item2);
						if (!((Object)(object)gameObjectFromItem2 == (Object)null))
						{
							GameObject itemRoot2 = GetItemRoot(gameObjectFromItem2);
							if (!((Object)(object)itemRoot2 == (Object)null))
							{
								ProcessCandidate(itemRoot2, origin, heldId, val2, num, value, seenRootIds, list);
							}
						}
					}
				}
				catch (Exception ex)
				{
					_log.LogDebug((object)("Underwater scan failed: " + ex.Message));
				}
			}
			if (list.Count == 0)
			{
				_log.LogInfo((object)($"No candidates. Scanned Items={num3}, Underwater={num4}, " + $"minRange={num:F1}, maxRange={value:F1}, origin={origin}"));
			}
			list.Sort((LostItem a, LostItem b) => a.Distance.CompareTo(b.Distance));
			if (list.Count > num2)
			{
				list.RemoveRange(num2, list.Count - num2);
			}
			return list;
		}

		private void ProcessCandidate(GameObject rootObject, Vector3 origin, int heldId, GameObject heldRoot, float minRange, float maxRange, HashSet<int> seenRootIds, List<LostItem> results)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			int objectId = GetObjectId(rootObject);
			if (!seenRootIds.Add(objectId) || (heldId != 0 && (objectId == heldId || IsChildOf(rootObject, heldRoot))) || !LooksLikeRetrievable(rootObject, out var category))
			{
				return;
			}
			Transform transform = rootObject.transform;
			Vector3 position = transform.position;
			if (position.y < RetrieverConfig.MinWorldY.Value || position.y > RetrieverConfig.MaxWorldY.Value)
			{
				return;
			}
			float num = Vector3.Distance(origin, position);
			if (num < minRange || (maxRange > 0.01f && num > maxRange))
			{
				return;
			}
			bool underWater = _bridge.IsUnderWater(position);
			bool flag = _bridge.IsLikelyLava(position);
			if (!PassesLocation(underWater, flag) || (RetrieverConfig.RequireLineOfSight.Value && !_bridge.HasLineOfSight(origin + Vector3.up, position)))
			{
				return;
			}
			bool flag2 = _bridge.IsNetworkObjectOwner(rootObject);
			if (!RetrieverConfig.RetrieveOnlyOwnedItems.Value || flag2)
			{
				LostItem lostItem = new LostItem
				{
					Source = rootObject,
					GameObject = rootObject,
					Transform = transform,
					Rigidbody = (rootObject.GetComponent<Rigidbody>() ?? rootObject.GetComponentInParent<Rigidbody>()),
					Name = ((Object)rootObject).name,
					Category = category,
					Distance = num,
					UnderWater = underWater,
					LikelyLava = flag,
					Owned = flag2
				};
				FillDurability(lostItem);
				if (!lostItem.HasDurability || !(lostItem.Durability <= 0.01f) || !RetrieverConfig.SkipBrokenItems.Value)
				{
					results.Add(lostItem);
				}
			}
		}

		private static int GetObjectId(GameObject obj)
		{
			if ((Object)(object)obj == (Object)null)
			{
				return 0;
			}
			Component component = obj.GetComponent<Component>();
			if ((Object)(object)component != (Object)null)
			{
				return ((Object)component).GetInstanceID();
			}
			return ((Object)obj).GetInstanceID();
		}

		private static GameObject GetGameObjectFromItem(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return null;
			}
			return ((Object)(object)item != (Object)null) ? ((Component)item).gameObject : null;
		}

		private static GameObject GetItemRoot(GameObject gameObject)
		{
			if ((Object)(object)gameObject == (Object)null)
			{
				return null;
			}
			Item componentInParent = gameObject.GetComponentInParent<Item>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				return ((Component)componentInParent).gameObject;
			}
			return null;
		}

		private static bool PassesLocation(bool underWater, bool lava)
		{
			if (RetrieverConfig.AllowFromAnywhere.Value)
			{
				if (underWater && !RetrieverConfig.AllowFromOcean.Value)
				{
					return false;
				}
				if (lava && !RetrieverConfig.AllowFromLava.Value)
				{
					return false;
				}
				return true;
			}
			return (underWater && RetrieverConfig.AllowFromOcean.Value) || (lava && RetrieverConfig.AllowFromLava.Value);
		}

		private static bool LooksLikeRetrievable(GameObject gameObject, out string category)
		{
			category = "Other";
			if ((Object)(object)gameObject == (Object)null)
			{
				return false;
			}
			string name = ((Object)gameObject).name;
			string haystack = BuildTypeBlob(gameObject);
			List<string> needles = ReflectionUtil.SplitList(RetrieverConfig.BlacklistNames.Value).ToList();
			if (ReflectionUtil.NameMatches(name, needles) || ReflectionUtil.NameMatches(haystack, needles))
			{
				return false;
			}
			if (RetrieverConfig.IgnoreBoatParts.Value && (ReflectionUtil.NameMatches(name, BoatHints) || ReflectionUtil.NameMatches(haystack, BoatHints)))
			{
				return false;
			}
			if (RetrieverConfig.IgnoreBait.Value && (ReflectionUtil.NameMatches(name, BaitHints) || ReflectionUtil.NameMatches(haystack, BaitHints)))
			{
				return false;
			}
			Item val = gameObject.GetComponent<Item>() ?? gameObject.GetComponentInParent<Item>();
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			if ((Object)(object)val.FishingRod != (Object)null)
			{
				category = "Rods";
				return CategoryAllowed(category);
			}
			bool flag = ReflectionUtil.NameMatches(name, FishHints) || ReflectionUtil.NameMatches(haystack, FishHints);
			bool flag2 = ReflectionUtil.NameMatches(name, RodHints) || ReflectionUtil.NameMatches(haystack, RodHints);
			bool flag3 = ReflectionUtil.NameMatches(name, WeaponHints) || ReflectionUtil.NameMatches(haystack, WeaponHints);
			bool flag4 = ReflectionUtil.NameMatches(name, ToolHints) || ReflectionUtil.NameMatches(haystack, ToolHints);
			if (flag)
			{
				if (RetrieverConfig.IgnoreFish.Value)
				{
					return false;
				}
				category = "Fish";
			}
			else if (flag3)
			{
				category = "Weapons";
			}
			else if (flag2)
			{
				category = "Rods";
			}
			else if (flag4)
			{
				category = "Tools";
			}
			else
			{
				category = "Other";
			}
			if ((Object)(object)gameObject.GetComponentInChildren<Camera>() != (Object)null || (Object)(object)gameObject.GetComponentInChildren<Light>() != (Object)null || (Object)(object)gameObject.GetComponentInChildren<AudioListener>() != (Object)null)
			{
				return false;
			}
			return CategoryAllowed(category);
		}

		private static bool CategoryAllowed(string category)
		{
			bool flag = false;
			List<string> list = ReflectionUtil.SplitList(RetrieverConfig.AllowedItemTypes.Value).ToList();
			for (int i = 0; i < list.Count; i++)
			{
				string text = list[i];
				flag = true;
				if (text.Equals("All", StringComparison.OrdinalIgnoreCase) || text.Equals(category, StringComparison.OrdinalIgnoreCase))
				{
					return true;
				}
			}
			return !flag;
		}

		private static string BuildTypeBlob(GameObject gameObject)
		{
			Component[] componentsInChildren = gameObject.GetComponentsInChildren<Component>(true);
			StringBuilder stringBuilder = new StringBuilder(((Object)gameObject).name);
			foreach (Component val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					stringBuilder.Append(' ').Append(((object)val).GetType().Name);
				}
			}
			return stringBuilder.ToString();
		}

		private static bool IsChildOf(GameObject candidate, GameObject parent)
		{
			if ((Object)(object)candidate == (Object)null || (Object)(object)parent == (Object)null)
			{
				return false;
			}
			return candidate.transform.IsChildOf(parent.transform);
		}

		private static void FillDurability(LostItem item)
		{
			Item val = item.GameObject.GetComponent<Item>() ?? item.GameObject.GetComponentInParent<Item>();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Type type = ((object)val).GetType();
			MemberInfo memberInfo = (MemberInfo)(((object)ReflectionUtil.FindProperty(type, DurabilityNames)) ?? ((object)ReflectionUtil.FindField(type, DurabilityNames)));
			if (!(memberInfo == null) && ReflectionUtil.TryReadFloat(val, memberInfo, out var value))
			{
				item.HasDurability = true;
				item.Durability = value;
				MemberInfo memberInfo2 = (MemberInfo)(((object)ReflectionUtil.FindProperty(type, MaxDurabilityNames)) ?? ((object)ReflectionUtil.FindField(type, MaxDurabilityNames)));
				if (memberInfo2 != null && ReflectionUtil.TryReadFloat(val, memberInfo2, out var value2) && value2 > 0f)
				{
					item.MaxDurability = value2;
				}
				else
				{
					item.MaxDurability = Mathf.Max(value, 100f);
				}
			}
		}
	}
	public sealed class NetworkSync
	{
		private readonly ManualLogSource _log;

		private readonly GameBridge _bridge;

		private Type _networkTransformType;

		private MethodInfo _teleportMethod;

		public bool IsServer => _bridge.IsServer;

		public bool CanRetrieve
		{
			get
			{
				if (RetrieverConfig.HostOnly.Value)
				{
					return _bridge.IsServer;
				}
				return true;
			}
		}

		public NetworkSync(ManualLogSource log, GameBridge bridge)
		{
			_log = log;
			_bridge = bridge;
		}

		public bool CanMove(GameObject gameObject)
		{
			if (!Object.op_Implicit((Object)(object)gameObject))
			{
				return false;
			}
			if (_bridge.IsServer)
			{
				return true;
			}
			Item val = gameObject.GetComponent<Item>() ?? gameObject.GetComponentInParent<Item>();
			if ((Object)(object)val != (Object)null)
			{
				return true;
			}
			return _bridge.IsNetworkObjectOwner(gameObject);
		}

		public void TeleportItem(GameObject gameObject, Vector3 targetPosition, Quaternion targetRotation)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)gameObject))
			{
				return;
			}
			Rigidbody val = gameObject.GetComponent<Rigidbody>() ?? gameObject.GetComponentInParent<Rigidbody>();
			if ((Object)(object)val != (Object)null)
			{
				if (!val.isKinematic)
				{
					try
					{
						val.linearVelocity = Vector3.zero;
						val.angularVelocity = Vector3.zero;
					}
					catch
					{
						val.velocity = Vector3.zero;
						val.angularVelocity = Vector3.zero;
					}
				}
				val.position = targetPosition;
				val.rotation = targetRotation;
				val.WakeUp();
			}
			Transform transform = gameObject.transform;
			transform.position = targetPosition;
			transform.rotation = targetRotation;
			SyncNetworkTransform(gameObject, targetPosition, targetRotation);
			TryStartSimulateLocal(gameObject, targetPosition, targetRotation);
		}

		private void TryStartSimulateLocal(GameObject gameObject, Vector3 pos, Quaternion rot)
		{
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Item val = gameObject.GetComponent<Item>() ?? gameObject.GetComponentInParent<Item>();
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				MemberInfo memberInfo = (MemberInfo)(((object)((object)val).GetType().GetProperty("RigidbodySync", BindingFlags.Instance | BindingFlags.Public)) ?? ((object)((object)val).GetType().GetField("RigidbodySync", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)));
				if (memberInfo == null)
				{
					return;
				}
				object obj = ((memberInfo is PropertyInfo propertyInfo) ? propertyInfo.GetValue(val) : ((FieldInfo)memberInfo).GetValue(val));
				if (obj != null)
				{
					MethodInfo method = obj.GetType().GetMethod("StartSimulateLocal", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
					{
						typeof(Vector3),
						typeof(Quaternion)
					}, null);
					if (method != null)
					{
						method.Invoke(obj, new object[2] { pos, rot });
						_log.LogInfo((object)("-> RigidbodySync.StartSimulateLocal called on '" + ((Object)gameObject).name + "'"));
					}
				}
			}
			catch (Exception ex)
			{
				_log.LogDebug((object)("RigidbodySync.StartSimulateLocal failed: " + ex.Message));
			}
		}

		private void SyncNetworkTransform(GameObject gameObject, Vector3 position, Quaternion rotation)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			EnsureCache();
			if (_networkTransformType == null)
			{
				return;
			}
			Component val = gameObject.GetComponent(_networkTransformType) ?? gameObject.GetComponentInParent(_networkTransformType);
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			try
			{
				if (_teleportMethod != null)
				{
					_teleportMethod.Invoke(val, new object[2] { position, rotation });
				}
			}
			catch (Exception ex)
			{
				_log.LogDebug((object)("NetworkTransform teleport failed: " + ex.Message));
			}
		}

		private void EnsureCache()
		{
			if (!(_networkTransformType != null))
			{
				_networkTransformType = ReflectionUtil.FindType("FishNet.Component.Transforming.NetworkTransform, FishNet.Runtime");
				if (_networkTransformType != null)
				{
					_teleportMethod = _networkTransformType.GetMethod("Teleport", new Type[2]
					{
						typeof(Vector3),
						typeof(Quaternion)
					});
				}
			}
		}
	}
	public sealed class RetrieverLogic
	{
		private readonly ManualLogSource _log;

		private readonly GameBridge _bridge;

		private readonly ItemFinder _finder;

		private readonly NetworkSync _network;

		public RetrieverLogic(ManualLogSource log, GameBridge bridge, ItemFinder finder, NetworkSync network)
		{
			_log = log;
			_bridge = bridge;
			_finder = finder;
			_network = network;
		}

		public RetrieveResult TryRetrieve(object player, string source)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0558: Unknown result type (might be due to invalid IL or missing references)
			//IL_043d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0447: Unknown result type (might be due to invalid IL or missing references)
			//IL_044c: Unknown result type (might be due to invalid IL or missing references)
			//IL_044e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0450: Unknown result type (might be due to invalid IL or missing references)
			//IL_045c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0463: Unknown result type (might be due to invalid IL or missing references)
			//IL_0468: Unknown result type (might be due to invalid IL or missing references)
			//IL_046d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0472: Unknown result type (might be due to invalid IL or missing references)
			Transform playerTransform = _bridge.GetPlayerTransform(player);
			if ((Object)(object)playerTransform == (Object)null)
			{
				return RetrieveResult.Failed("Player transform missing.");
			}
			Vector3 playerPosition = _bridge.GetPlayerPosition(player);
			_log.LogInfo((object)$"Retrieve origin (cam): {playerPosition}");
			List<LostItem> list = _finder.Find(playerPosition, player);
			if (list.Count == 0)
			{
				if (RetrieverConfig.ChargeEvenIfNothingFound.Value && RetrieverConfig.MoneyCost.Value > 0)
				{
					if (!TryPay(player, RetrieverConfig.MoneyCost.Value, 0f, 0, out var charged, out var error))
					{
						return RetrieveResult.Failed(error);
					}
					return RetrieveResult.Ok("No items found. Charged the flat fee anyway.", 0, 0, 0, charged);
				}
				return RetrieveResult.Failed("No lost items found. They may have despawned, or filters excluded them.");
			}
			list = DeduplicateRodsKeepFarthest(list);
			float num = 0f;
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].Distance > num)
				{
					num = list[i].Distance;
				}
			}
			int num2 = ComputeCost(num, list.Count);
			if (RetrieverConfig.FailIfCannotAfford.Value && num2 > 0 && _bridge.TryGetMoney(player, out var money) && money < num2)
			{
				return RetrieveResult.Failed($"Need ${num2} to retrieve {list.Count} item(s). You have ${money}.");
			}
			Vector3 val = playerPosition + _bridge.GetPlayerForward(player) * RetrieverConfig.DropInFrontDistance.Value + Vector3.up * RetrieverConfig.DropHeight.Value;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			HashSet<GameObject> hashSet = new HashSet<GameObject>();
			for (int j = 0; j < list.Count; j++)
			{
				LostItem lostItem = list[j];
				GameObject targetRootObject = GetTargetRootObject(lostItem.GameObject);
				if ((Object)(object)targetRootObject == (Object)null || hashSet.Contains(targetRootObject))
				{
					num4++;
					continue;
				}
				_log.LogInfo((object)$"Evaluating candidate item: '{lostItem.Name}' -> Root: '{((Object)targetRootObject).name}' (Category: {lostItem.Category}, Distance: {lostItem.Distance:F1}m)");
				if (IsAttachedToPlayerHierarchy(targetRootObject, playerTransform))
				{
					_log.LogInfo((object)("-> Skipped '" + ((Object)targetRootObject).name + "' because it is attached to the local player."));
					num4++;
					continue;
				}
				Item val2 = targetRootObject.GetComponent<Item>() ?? targetRootObject.GetComponentInParent<Item>();
				if ((Object)(object)val2 != (Object)null && ((Object)(object)val2.Holder != (Object)null || (Object)(object)val2.SyncedHolder != (Object)null))
				{
					_log.LogInfo((object)("-> Skipped '" + ((Object)targetRootObject).name + "' because it is actively held."));
					num4++;
					continue;
				}
				if (!_network.CanMove(targetRootObject))
				{
					_log.LogInfo((object)("-> Skipped '" + ((Object)targetRootObject).name + "' due to network authority/CanMove."));
					num4++;
					continue;
				}
				if (!ApplyDurability(lostItem, out var broke))
				{
					num4++;
					continue;
				}
				if (broke && RetrieverConfig.DestroyIfBroken.Value)
				{
					if ((Object)(object)val2 != (Object)null && (Object)(object)ItemManager.Instance != (Object)null)
					{
						ItemManager._itemsUnderwater.Remove(val2);
					}
					Object.Destroy((Object)(object)targetRootObject);
					num5++;
					hashSet.Add(targetRootObject);
					continue;
				}
				if ((Object)(object)val2 != (Object)null && (Object)(object)ItemManager.Instance != (Object)null)
				{
					ItemManager._itemsUnderwater.Remove(val2);
				}
				Vector2 val3 = Random.insideUnitCircle * 0.2f;
				Vector3 dropTarget = val + new Vector3(val3.x, 0f, val3.y);
				MoveOrFlyRootItem(targetRootObject, dropTarget);
				hashSet.Add(targetRootObject);
				_log.LogInfo((object)("-> Successfully initiated retrieval for root item '" + ((Object)targetRootObject).name + "'"));
				num3++;
			}
			if (num3 == 0 && num5 == 0)
			{
				return RetrieveResult.Failed($"Found {list.Count} candidate(s) but none could be moved.");
			}
			if (!TryPay(player, RetrieverConfig.MoneyCost.Value, num, num3, out var charged2, out var error2) && RetrieverConfig.FailIfCannotAfford.Value)
			{
				_log.LogWarning((object)("Items already moved but payment failed: " + error2));
			}
			if (RetrieverConfig.PlaySound.Value)
			{
				PlayWhoosh(val);
			}
			string message = $"Retrieved {num3} item(s) via {source}" + ((num5 > 0) ? $", destroyed {num5}" : string.Empty) + ((num4 > 0) ? $", skipped {num4}" : string.Empty) + ((charged2 > 0) ? $", charged ${charged2}" : string.Empty) + ".";
			return RetrieveResult.Ok(message, num3, num4, num5, charged2);
		}

		private List<LostItem> DeduplicateRodsKeepFarthest(List<LostItem> items)
		{
			if (items == null || items.Count <= 1)
			{
				return items;
			}
			Dictionary<string, LostItem> dictionary = new Dictionary<string, LostItem>(StringComparer.OrdinalIgnoreCase);
			List<LostItem> list = new List<LostItem>();
			for (int i = 0; i < items.Count; i++)
			{
				LostItem lostItem = items[i];
				if (!string.Equals(lostItem.Category, "Rods", StringComparison.OrdinalIgnoreCase) && (lostItem.Name == null || lostItem.Name.IndexOf("Rod", StringComparison.OrdinalIgnoreCase) < 0))
				{
					list.Add(lostItem);
					continue;
				}
				string key = NormalizeItemName(lostItem.Name);
				if (!dictionary.TryGetValue(key, out var value) || lostItem.Distance > value.Distance)
				{
					if (value != null)
					{
						_log.LogInfo((object)$"-> Skipping nearer duplicate rod '{value.Name}' at {value.Distance:F1}m (keeping {lostItem.Distance:F1}m)");
					}
					dictionary[key] = lostItem;
				}
				else
				{
					_log.LogInfo((object)$"-> Skipping nearer duplicate rod '{lostItem.Name}' at {lostItem.Distance:F1}m (keeping {value.Distance:F1}m)");
				}
			}
			List<LostItem> list2 = new List<LostItem>(list.Count + dictionary.Count);
			list2.AddRange(list);
			foreach (KeyValuePair<string, LostItem> item in dictionary)
			{
				list2.Add(item.Value);
			}
			list2.Sort((LostItem a, LostItem b) => a.Distance.CompareTo(b.Distance));
			return list2;
		}

		private static string NormalizeItemName(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return string.Empty;
			}
			string text = name.Trim();
			if (text.EndsWith("(Clone)", StringComparison.OrdinalIgnoreCase))
			{
				text = text.Substring(0, text.Length - 7).Trim();
			}
			return text;
		}

		private static GameObject GetTargetRootObject(GameObject candidate)
		{
			if ((Object)(object)candidate == (Object)null)
			{
				return null;
			}
			Item componentInParent = candidate.GetComponentInParent<Item>();
			if ((Object)(object)componentInParent != (Object)null)
			{
				return ((Component)((Component)componentInParent).transform).gameObject;
			}
			Rigidbody componentInParent2 = candidate.GetComponentInParent<Rigidbody>();
			if ((Object)(object)componentInParent2 != (Object)null)
			{
				return ((Component)componentInParent2).gameObject;
			}
			return ((Component)candidate.transform.root).gameObject;
		}

		private static bool IsAttachedToPlayerHierarchy(GameObject obj, Transform playerTransform)
		{
			if ((Object)(object)obj == (Object)null || (Object)(object)playerTransform == (Object)null)
			{
				return false;
			}
			return obj.transform.IsChildOf(playerTransform);
		}

		private void MoveOrFlyRootItem(GameObject rootObj, Vector3 dropTarget)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Plugin.Instance != (Object)null)
			{
				((MonoBehaviour)Plugin.Instance).StartCoroutine(FlyToTargetRoutine(rootObj, dropTarget, 0.55f));
				return;
			}
			_network.TeleportItem(rootObj, dropTarget, rootObj.transform.rotation);
			ForceVisible(rootObj);
			LogGroundDrop(rootObj, dropTarget);
		}

		private IEnumerator FlyToTargetRoutine(GameObject itemRoot, Vector3 dropPoint, float duration)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)itemRoot == (Object)null)
			{
				yield break;
			}
			Rigidbody rb = itemRoot.GetComponent<Rigidbody>() ?? itemRoot.GetComponentInParent<Rigidbody>();
			bool wasKinematic = false;
			if ((Object)(object)rb != (Object)null)
			{
				wasKinematic = rb.isKinematic;
				rb.isKinematic = true;
			}
			ForceVisible(itemRoot);
			Vector3 startPos = itemRoot.transform.position;
			float elapsed = 0f;
			while (elapsed < duration)
			{
				if ((Object)(object)itemRoot == (Object)null)
				{
					yield break;
				}
				elapsed += Time.deltaTime;
				float t = Mathf.Clamp01(elapsed / duration);
				float easeT = 1f - Mathf.Pow(1f - t, 3f);
				Vector3 arcOffset = Vector3.up * (Mathf.Sin(t * MathF.PI) * 1.2f);
				itemRoot.transform.position = Vector3.Lerp(startPos, dropPoint, easeT) + arcOffset;
				yield return null;
			}
			if ((Object)(object)itemRoot == (Object)null)
			{
				yield break;
			}
			if ((Object)(object)rb != (Object)null)
			{
				rb.isKinematic = wasKinematic;
			}
			Quaternion finalRot = itemRoot.transform.rotation;
			_network.TeleportItem(itemRoot, dropPoint, finalRot);
			ForceVisible(itemRoot);
			_log.LogInfo((object)$"-> Teleported '{((Object)itemRoot).name}' to {dropPoint}");
			yield return null;
			if ((Object)(object)itemRoot == (Object)null)
			{
				_log.LogWarning((object)"-> Item destroyed after teleport (game cleanup?).");
				yield break;
			}
			float drift = Vector3.Distance(itemRoot.transform.position, dropPoint);
			_log.LogInfo((object)$"-> After settle: pos={itemRoot.transform.position}, drift={drift:F1}m");
			if (drift > 3f)
			{
				_log.LogWarning((object)$"-> Drift {drift:F1}m — re-applying teleport + SimulateLocal.");
				_network.TeleportItem(itemRoot, dropPoint, finalRot);
				ForceVisible(itemRoot);
			}
			LogGroundDrop(itemRoot, dropPoint);
		}

		private void LogGroundDrop(GameObject itemRoot, Vector3 dropPoint)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)itemRoot == (Object)null))
			{
				_log.LogInfo((object)$"-> '{((Object)itemRoot).name}' is on the ground in front of you at {dropPoint}. Press E to pick it up.");
			}
		}

		private static void ForceVisible(GameObject root)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			if (!root.activeSelf)
			{
				root.SetActive(true);
			}
			MeshRenderer[] componentsInChildren = root.GetComponentsInChildren<MeshRenderer>(true);
			foreach (MeshRenderer val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					((Renderer)val).enabled = true;
				}
			}
		}

		private static int ComputeCost(float farthest, int count)
		{
			float num = (float)RetrieverConfig.MoneyCost.Value + RetrieverConfig.CostPerDistance.Value * farthest + (float)(RetrieverConfig.CostPerItem.Value * count);
			return Mathf.Max(0, Mathf.RoundToInt(num));
		}

		private bool TryPay(object player, int flat, float farthest, int count, out int charged, out string error)
		{
			int amount = ComputeCost(farthest, count);
			if (flat <= 0 && RetrieverConfig.CostPerDistance.Value <= 0f && RetrieverConfig.CostPerItem.Value <= 0)
			{
				amount = 0;
			}
			return _bridge.TryCharge(player, amount, out charged, out error);
		}

		private static bool ApplyDurability(LostItem item, out bool broke)
		{
			broke = false;
			if (!item.HasDurability)
			{
				return true;
			}
			float num = Mathf.Clamp(RetrieverConfig.DurabilityLossPercent.Value, 0f, 100f);
			float num2 = item.Durability;
			if (num > 0f)
			{
				num2 -= item.Durability * (num / 100f);
			}
			num2 -= Mathf.Max(0f, RetrieverConfig.DurabilityLossFlat.Value);
			num2 = Mathf.Max(0f, num2);
			broke = num2 <= 0.01f;
			item.Durability = num2;
			WriteDurability(item, num2);
			return true;
		}

		private static void WriteDurability(LostItem item, float value)
		{
			string[] names = new string[5] { "Durability", "Condition", "Health", "CurrentDurability", "_durability" };
			Component[] components = item.GameObject.GetComponents<Component>();
			foreach (Component val in components)
			{
				if (!((Object)(object)val == (Object)null))
				{
					MemberInfo memberInfo = (MemberInfo)(((object)ReflectionUtil.FindProperty(((object)val).GetType(), names)) ?? ((object)ReflectionUtil.FindField(((object)val).GetType(), names)));
					if (memberInfo != null && ReflectionUtil.SetMember(val, memberInfo, value))
					{
						break;
					}
				}
			}
		}

		private static void PlayWhoosh(Vector3 position)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				AudioClip val = AudioClip.Create("retriever-whoosh", 8000, 1, 8000, false);
				float[] array = new float[8000];
				for (int i = 0; i < array.Length; i++)
				{
					float num = (float)i / 8000f;
					array[i] = Mathf.Sin(MathF.PI * 2f * Mathf.Lerp(900f, 180f, num) * num) * (1f - num) * 0.2f;
				}
				val.SetData(array, 0);
				AudioSource.PlayClipAtPoint(val, position, 0.6f);
			}
			catch
			{
			}
		}
	}
}
namespace HowToFish.ItemRetriever.Models
{
	public sealed class LostItem
	{
		public object Source { get; set; }

		public GameObject GameObject { get; set; }

		public Transform Transform { get; set; }

		public Rigidbody Rigidbody { get; set; }

		public string Name { get; set; }

		public string Category { get; set; }

		public float Distance { get; set; }

		public bool UnderWater { get; set; }

		public bool LikelyLava { get; set; }

		public bool Owned { get; set; }

		public float Durability { get; set; }

		public float MaxDurability { get; set; }

		public bool HasDurability { get; set; }

		public GameObject RootGameObject => ((Object)(object)Rigidbody != (Object)null) ? ((Component)Rigidbody).gameObject : (Object.op_Implicit((Object)(object)GameObject) ? ((Component)GameObject.transform.root).gameObject : null);
	}
	public sealed class RetrieveResult
	{
		public bool Success { get; private set; }

		public string Message { get; private set; }

		public int Moved { get; private set; }

		public int Skipped { get; private set; }

		public int Destroyed { get; private set; }

		public int Charged { get; private set; }

		public int TotalProcessed => Moved + Skipped + Destroyed;

		public static RetrieveResult Failed(string message)
		{
			return new RetrieveResult
			{
				Success = false,
				Message = (message ?? string.Empty),
				Moved = 0,
				Skipped = 0,
				Destroyed = 0,
				Charged = 0
			};
		}

		public static RetrieveResult Ok(string message, int moved, int skipped, int destroyed, int charged)
		{
			return new RetrieveResult
			{
				Success = true,
				Message = (message ?? string.Empty),
				Moved = moved,
				Skipped = skipped,
				Destroyed = destroyed,
				Charged = charged
			};
		}
	}
}
namespace HowToFish.ItemRetriever.Config
{
	public static class RetrieverConfig
	{
		public static ConfigEntry<bool> Enabled { get; private set; }

		public static ConfigEntry<KeyCode> RetrieveKey { get; private set; }

		public static ConfigEntry<bool> IgnoreWhenChatOpen { get; private set; }

		public static ConfigEntry<float> MaxRange { get; private set; }

		public static ConfigEntry<float> MinRange { get; private set; }

		public static ConfigEntry<int> MaxItemsPerUse { get; private set; }

		public static ConfigEntry<float> DropInFrontDistance { get; private set; }

		public static ConfigEntry<float> DropHeight { get; private set; }

		public static ConfigEntry<float> CooldownSeconds { get; private set; }

		public static ConfigEntry<int> MoneyCost { get; private set; }

		public static ConfigEntry<float> CostPerDistance { get; private set; }

		public static ConfigEntry<int> CostPerItem { get; private set; }

		public static ConfigEntry<bool> FailIfCannotAfford { get; private set; }

		public static ConfigEntry<bool> ChargeEvenIfNothingFound { get; private set; }

		public static ConfigEntry<float> DurabilityLossPercent { get; private set; }

		public static ConfigEntry<float> DurabilityLossFlat { get; private set; }

		public static ConfigEntry<bool> SkipBrokenItems { get; private set; }

		public static ConfigEntry<bool> DestroyIfBroken { get; private set; }

		public static ConfigEntry<bool> HostOnly { get; private set; }

		public static ConfigEntry<bool> RetrieveOnlyOwnedItems { get; private set; }

		public static ConfigEntry<bool> RequireLineOfSight { get; private set; }

		public static ConfigEntry<bool> AllowFromOcean { get; private set; }

		public static ConfigEntry<bool> AllowFromLava { get; private set; }

		public static ConfigEntry<bool> AllowFromAnywhere { get; private set; }

		public static ConfigEntry<float> MinWorldY { get; private set; }

		public static ConfigEntry<float> MaxWorldY { get; private set; }

		public static ConfigEntry<string> AllowedItemTypes { get; private set; }

		public static ConfigEntry<string> BlacklistNames { get; private set; }

		public static ConfigEntry<bool> IgnoreFish { get; private set; }

		public static ConfigEntry<bool> IgnoreBait { get; private set; }

		public static ConfigEntry<bool> IgnoreBoatParts { get; private set; }

		public static ConfigEntry<bool> IgnoreHeldItem { get; private set; }

		public static ConfigEntry<bool> PlaySound { get; private set; }

		public static ConfigEntry<bool> ShowChatMessage { get; private set; }

		public static ConfigEntry<bool> ScreenShake { get; private set; }

		public static ConfigEntry<bool> LogToConsole { get; private set; }

		public static ConfigEntry<bool> ZeroVelocityOnRetrieve { get; private set; }

		public static void Bind(ConfigFile config)
		{
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Expected O, but got Unknown
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ee: Expected O, but got Unknown
			Enabled = config.Bind<bool>("General", "Enabled", true, "Master switch for Item Retriever.");
			RetrieveKey = config.Bind<KeyCode>("Controls", "RetrieveKey", (KeyCode)289, "Press to pull lost items back in front of you.");
			IgnoreWhenChatOpen = config.Bind<bool>("Controls", "IgnoreWhenChatOpen", true, "Ignore the hotkey while chat, console, or BlockInputs is active.");
			MaxRange = config.Bind<float>("Range", "MaxRange", 8f, "Maximum distance in meters. 0 = unlimited.");
			MinRange = config.Bind<float>("Range", "MinRange", 0f, "Ignore items already this close. Stops you scooping the pile at your feet.");
			MaxItemsPerUse = config.Bind<int>("Range", "MaxItemsPerUse", 25, new ConfigDescription("Hard cap per press.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 200), Array.Empty<object>()));
			DropInFrontDistance = config.Bind<float>("Range", "DropInFrontDistance", 1.6f, "How far in front of the player retrieved items appear.");
			DropHeight = config.Bind<float>("Range", "DropHeight", 0.85f, "Extra height so items do not spawn inside the ground.");
			CooldownSeconds = config.Bind<float>("Range", "CooldownSeconds", 8f, "Seconds before you can retrieve again. 0 = no cooldown.");
			MoneyCost = config.Bind<int>("Cost", "MoneyCost", 0, "Flat money charged per retrieve, before distance/item extras. 0 = free.");
			CostPerDistance = config.Bind<float>("Cost", "CostPerDistance", 0f, "Extra money per meter of the farthest retrieved item.");
			CostPerItem = config.Bind<int>("Cost", "CostPerItem", 0, "Extra money charged per item actually moved.");
			FailIfCannotAfford = config.Bind<bool>("Cost", "FailIfCannotAfford", true, "If true, do nothing when you cannot pay the full cost.");
			ChargeEvenIfNothingFound = config.Bind<bool>("Cost", "ChargeEvenIfNothingFound", false, "Charge the flat fee even when no items were found.");
			DurabilityLossPercent = config.Bind<float>("Durability", "DurabilityLossPercent", 0f, new ConfigDescription("Percent of current durability removed on retrieve (0-100).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			DurabilityLossFlat = config.Bind<float>("Durability", "DurabilityLossFlat", 0f, "Flat durability removed after the percent cut.");
			SkipBrokenItems = config.Bind<bool>("Durability", "SkipBrokenItems", true, "Do not retrieve items that already have 0 durability.");
			DestroyIfBroken = config.Bind<bool>("Durability", "DestroyIfBroken", false, "If durability hits 0 during retrieve, destroy the item instead of returning it.");
			HostOnly = config.Bind<bool>("Multiplayer", "HostOnly", true, "Only the lobby host may retrieve. Safest with FishNet authority.");
			RetrieveOnlyOwnedItems = config.Bind<bool>("Multiplayer", "RetrieveOnlyOwnedItems", false, "Only move items this player owns / originally dropped.");
			RequireLineOfSight = config.Bind<bool>("Restrictions", "RequireLineOfSight", false, "Skip items with terrain between you and them.");
			AllowFromOcean = config.Bind<bool>("Restrictions", "AllowFromOcean", true, "Retrieve items that are underwater.");
			AllowFromLava = config.Bind<bool>("Restrictions", "AllowFromLava", true, "Retrieve items that sit very low / in lava-like volumes.");
			AllowFromAnywhere = config.Bind<bool>("Restrictions", "AllowFromAnywhere", true, "If true, land items far away are also retrieved. If false, only ocean/lava.");
			MinWorldY = config.Bind<float>("Restrictions", "MinWorldY", -500f, "Ignore items below this world Y.");
			MaxWorldY = config.Bind<float>("Restrictions", "MaxWorldY", 500f, "Ignore items above this world Y.");
			AllowedItemTypes = config.Bind<string>("Filters", "AllowedItemTypes", "All", "Comma list: All, Weapons, Tools, Rods, Fish, Other. Case-insensitive.");
			BlacklistNames = config.Bind<string>("Filters", "BlacklistNames", "Bait,Boat,Player,Camera,Water,Seagull,Bird,Clam,Rock,Prop,Terrain", "Comma list of name fragments to skip.");
			IgnoreFish = config.Bind<bool>("Filters", "IgnoreFish", true, "Skip living / flopping fish so you do not vacuum the whole ocean.");
			IgnoreBait = config.Bind<bool>("Filters", "IgnoreBait", true, "Skip fishing bait objects.");
			IgnoreBoatParts = config.Bind<bool>("Filters", "IgnoreBoatParts", true, "Skip the boat and attached parts.");
			IgnoreHeldItem = config.Bind<bool>("Filters", "IgnoreHeldItem", true, "Never teleport the item currently in your hands.");
			PlaySound = config.Bind<bool>("Feedback", "PlaySound", true, "Play a short whoosh when something is retrieved.");
			ShowChatMessage = config.Bind<bool>("Feedback", "ShowChatMessage", true, "Print a local chat / hint line with the result.");
			ScreenShake = config.Bind<bool>("Feedback", "ScreenShake", false, "Tiny camera shake on a successful retrieve.");
			LogToConsole = config.Bind<bool>("Feedback", "LogToConsole", true, "Write results to the BepInEx log.");
			ZeroVelocityOnRetrieve = config.Bind<bool>("Feedback", "ZeroVelocityOnRetrieve", true, "Kill rigidbody velocity so items do not immediately yeet again.");
		}
	}
}