Decompiled source of FishingEnhanced Spears Bows Harpoons v1.0.2

FishingEnhanced.dll

Decompiled 6 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("FishingEnhanced")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("FishingEnhanced")]
[assembly: AssemblyTitle("FishingEnhanced")]
[assembly: AssemblyVersion("1.0.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 FishingEnhanced
{
	internal static class FishingTweaks
	{
		internal static ConfigEntry<float> ReelSpeedMultiplier;

		internal static ConfigEntry<float> StaminaMultiplier;

		internal static ConfigEntry<float> LureRangeMultiplier;

		internal static ConfigEntry<float> HookChanceMultiplier;

		internal static ConfigEntry<bool> LureAssist;

		internal static ConfigEntry<float> LureAssistRange;

		internal static readonly ManualLogSource Log = Logger.CreateLogSource("FishingEnhanced.Tweaks");

		internal static readonly string[] FloatReelFields = new string[2] { "m_pullLineSpeed", "m_moveForce" };

		internal static readonly string[] FloatRangeFields = new string[1] { "m_range" };

		internal static readonly string[] FishHookFields = new string[1] { "m_baseHookChance" };

		private static readonly HashSet<string> Logged = new HashSet<string>();

		internal static void BindConfig(ConfigFile config)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Expected O, but got Unknown
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Expected O, but got Unknown
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Expected O, but got Unknown
			ReelSpeedMultiplier = config.Bind<float>("3 - Fishing Rod", "ReelSpeedMultiplier", 2f, new ConfigDescription("Multiplier for how fast the rod reels in the line (1 = vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
			StaminaMultiplier = config.Bind<float>("3 - Fishing Rod", "StaminaMultiplier", 0.5f, new ConfigDescription("Multiplier for stamina used while reeling / fighting a fish (0 = free, 1 = vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			LureRangeMultiplier = config.Bind<float>("4 - Lure", "LureRangeMultiplier", 4f, new ConfigDescription("Multiplier for how far away fish notice the bait (1 = vanilla).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			HookChanceMultiplier = config.Bind<float>("4 - Lure", "HookChanceMultiplier", 3f, new ConfigDescription("Multiplier for the chance a fish bites the bait (capped at 100%).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			LureAssist = config.Bind<bool>("4 - Lure", "LureAssist", true, "Actively steer nearby fish toward your bait while it's in the water.");
			LureAssistRange = config.Bind<float>("4 - Lure", "LureAssistRange", 25f, new ConfigDescription("Radius (m) around the bait that LureAssist pulls fish from.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), Array.Empty<object>()));
			GameObject val = new GameObject("FishingEnhanced_LureAssist");
			Object.DontDestroyOnLoad((Object)(object)val);
			((Object)val).hideFlags = (HideFlags)61;
			val.AddComponent<LureAssistRunner>();
		}

		internal static int Scale(object obj, string[] names, float mult, float? max = null)
		{
			int num = 0;
			foreach (string text in names)
			{
				FieldInfo fieldInfo = AccessTools.Field(obj.GetType(), text);
				if (!(fieldInfo == null) && !(fieldInfo.FieldType != typeof(float)))
				{
					float num2 = (float)fieldInfo.GetValue(obj) * mult;
					if (max.HasValue)
					{
						num2 = Mathf.Min(num2, max.Value);
					}
					fieldInfo.SetValue(obj, num2);
					num++;
				}
			}
			if (num == 0)
			{
				LogMissingOnce(obj.GetType(), names);
			}
			return num;
		}

		internal static void ScaleBaitChances(Fish fish, float mult)
		{
			if (!(AccessTools.Field(typeof(Fish), "m_baits")?.GetValue(fish) is IList list))
			{
				return;
			}
			foreach (object item in list)
			{
				if (item != null)
				{
					FieldInfo fieldInfo = AccessTools.Field(item.GetType(), "m_chance");
					if (!(fieldInfo == null) && !(fieldInfo.FieldType != typeof(float)))
					{
						fieldInfo.SetValue(item, Mathf.Min((float)fieldInfo.GetValue(item) * mult, 1f));
					}
				}
			}
		}

		private static void LogMissingOnce(Type type, string[] names)
		{
			string item = type.Name + ":" + string.Join(",", names);
			if (Logged.Add(item))
			{
				IEnumerable<string> values = from f in AccessTools.GetDeclaredFields(type)
					where f.FieldType == typeof(float)
					select f.Name;
				Log.LogWarning((object)("None of [" + string.Join(", ", names) + "] found on " + type.Name + ". Available float fields: " + string.Join(", ", values)));
			}
		}
	}
	[HarmonyPatch]
	internal static class FishingFloat_Awake_Patch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(FishingFloat), "Awake", (Type[])null, (Type[])null) ?? AccessTools.Method(typeof(FishingFloat), "Start", (Type[])null, (Type[])null);
		}

		private static void Postfix(FishingFloat __instance)
		{
			if (Plugin.Enabled.Value)
			{
				FishingTweaks.Scale(__instance, FishingTweaks.FloatReelFields, FishingTweaks.ReelSpeedMultiplier.Value);
				FishingTweaks.Scale(__instance, FishingTweaks.FloatRangeFields, FishingTweaks.LureRangeMultiplier.Value);
			}
		}
	}
	[HarmonyPatch(typeof(FishingFloat), "FixedUpdate")]
	internal static class FishingFloat_FixedUpdate_Patch
	{
		internal static bool InFloatUpdate;

		private static void Prefix()
		{
			InFloatUpdate = true;
		}

		private static void Finalizer()
		{
			InFloatUpdate = false;
		}
	}
	[HarmonyPatch]
	internal static class Player_UseStamina_Patch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(Player), "UseStamina", (Type[])null, (Type[])null) ?? AccessTools.Method(typeof(Character), "UseStamina", (Type[])null, (Type[])null);
		}

		private static void Prefix(Character __instance, ref float __0)
		{
			if (Plugin.Enabled.Value && FishingFloat_FixedUpdate_Patch.InFloatUpdate && (object)__instance == Player.m_localPlayer)
			{
				__0 *= FishingTweaks.StaminaMultiplier.Value;
			}
		}
	}
	[HarmonyPatch]
	internal static class Fish_Awake_Patch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(Fish), "Awake", (Type[])null, (Type[])null) ?? AccessTools.Method(typeof(Fish), "Start", (Type[])null, (Type[])null);
		}

		private static void Postfix(Fish __instance)
		{
			if (Plugin.Enabled.Value)
			{
				FishingTweaks.Scale(__instance, FishingTweaks.FishHookFields, FishingTweaks.HookChanceMultiplier.Value, 1f);
				FishingTweaks.ScaleBaitChances(__instance, FishingTweaks.HookChanceMultiplier.Value);
			}
		}
	}
	internal class LureAssistRunner : MonoBehaviour
	{
		private static readonly MethodInfo FloatGetCatch = AccessTools.Method(typeof(FishingFloat), "GetCatch", (Type[])null, (Type[])null);

		private static readonly FieldInfo FishWaypoint = AccessTools.Field(typeof(Fish), "m_waypoint");

		private static readonly FieldInfo FishHaveWaypoint = AccessTools.Field(typeof(Fish), "m_haveWaypoint");

		private static readonly MethodInfo FishIsHooked = AccessTools.Method(typeof(Fish), "IsHooked", (Type[])null, (Type[])null);

		private static readonly MethodInfo GetLiquidLevel = AccessTools.Method(typeof(Floating), "GetLiquidLevel", new Type[3]
		{
			typeof(Vector3),
			typeof(float),
			typeof(LiquidType)
		}, (Type[])null);

		private const float SettleTime = 1f;

		private readonly Dictionary<int, float> _settled = new Dictionary<int, float>();

		private float _timer;

		private bool _warned;

		private static float WaterLevelAt(Vector3 pos)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (GetLiquidLevel != null)
			{
				try
				{
					return (float)GetLiquidLevel.Invoke(null, new object[3]
					{
						pos,
						1f,
						(object)(LiquidType)0
					});
				}
				catch
				{
				}
			}
			return ((Object)(object)ZoneSystem.instance != (Object)null) ? ZoneSystem.instance.m_waterLevel : 30f;
		}

		private bool BaitInWater(FishingFloat ff)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)ff).transform.position;
			float num = WaterLevelAt(position);
			if (position.y > num + 0.4f || position.y < num - 1.5f)
			{
				return false;
			}
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(position) > num - 0.5f)
			{
				return false;
			}
			Rigidbody component = ((Component)ff).GetComponent<Rigidbody>();
			if ((Object)(object)component != (Object)null && Mathf.Abs(component.linearVelocity.y) > 1.5f)
			{
				return false;
			}
			int instanceID = ((Object)ff).GetInstanceID();
			if (!_settled.TryGetValue(instanceID, out var value))
			{
				_settled[instanceID] = Time.time;
				return false;
			}
			return Time.time - value >= 1f;
		}

		private void Update()
		{
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Enabled.Value || !FishingTweaks.LureAssist.Value || (_timer -= Time.deltaTime) > 0f)
			{
				return;
			}
			_timer = 0.5f;
			if (FishWaypoint == null || FishHaveWaypoint == null)
			{
				if (!_warned)
				{
					_warned = true;
					FishingTweaks.Log.LogWarning((object)"LureAssist disabled: Fish waypoint fields not found (game update?).");
				}
				return;
			}
			FishingFloat[] array = Object.FindObjectsByType<FishingFloat>((FindObjectsSortMode)0);
			if (array.Length == 0)
			{
				_settled.Clear();
				return;
			}
			Fish[] array2 = Object.FindObjectsByType<Fish>((FindObjectsSortMode)0);
			float value = FishingTweaks.LureAssistRange.Value;
			FishingFloat[] array3 = array;
			foreach (FishingFloat val in array3)
			{
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				ZNetView component = ((Component)val).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
				{
					continue;
				}
				if (!BaitInWater(val))
				{
					_settled.Remove(((Object)val).GetInstanceID());
				}
				else
				{
					if (FloatGetCatch != null && FloatGetCatch.Invoke(val, null) != null)
					{
						continue;
					}
					Vector3 val2 = ((Component)val).transform.position + Vector3.down * 0.5f;
					Fish[] array4 = array2;
					foreach (Fish val3 in array4)
					{
						if (!((Object)(object)val3 == (Object)null) && Plugin.IsInWater(val3) && (!(FishIsHooked != null) || !(bool)FishIsHooked.Invoke(val3, null)) && !(Vector3.Distance(((Component)val3).transform.position, val2) > value))
						{
							ZNetView component2 = ((Component)val3).GetComponent<ZNetView>();
							if (!((Object)(object)component2 == (Object)null) && component2.IsValid() && component2.IsOwner())
							{
								FishWaypoint.SetValue(val3, val2);
								FishHaveWaypoint.SetValue(val3, true);
							}
						}
					}
				}
			}
		}
	}
	[BepInPlugin("com.matthew.valheim.fishingenhanced", "Fishing Enhanced", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.matthew.valheim.fishingenhanced";

		public const string ModName = "Fishing Enhanced";

		public const string ModVersion = "1.0.1";

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<float> PickupRange;

		internal static ConfigEntry<float> AimTolerance;

		private static MethodInfo _pickupMethod;

		private static MethodInfo _isOutOfWaterMethod;

		internal static Plugin Instance;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Expected O, but got Unknown
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Expected O, but got Unknown
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Allow picking up fish with the Use key while they are swimming in water.");
			PickupRange = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "PickupRange", 2.5f, new ConfigDescription("Max distance (m) for the fallback in-water pickup.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 6f), Array.Empty<object>()));
			AimTolerance = ((BaseUnityPlugin)this).Config.Bind<float>("1 - General", "AimTolerance", 0.5f, new ConfigDescription("How directly you must face the fish for the fallback pickup (1 = dead center, 0 = 90° cone).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			_pickupMethod = AccessTools.Method(typeof(Fish), "Pickup", new Type[1] { typeof(Humanoid) }, (Type[])null);
			_isOutOfWaterMethod = AccessTools.Method(typeof(Fish), "IsOutOfWater", (Type[])null, (Type[])null);
			if (_pickupMethod == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Fish.Pickup(Humanoid) not found - game update may have changed it.");
			}
			if (_isOutOfWaterMethod == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Fish.IsOutOfWater() not found - game update may have changed it.");
			}
			Instance = this;
			WeaponCatch.BindConfig(((BaseUnityPlugin)this).Config);
			FishingTweaks.BindConfig(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("com.matthew.valheim.fishingenhanced");
			_harmony.PatchAll();
		}

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

		internal static bool IsInWater(Fish fish)
		{
			if ((Object)(object)fish == (Object)null || _isOutOfWaterMethod == null)
			{
				return false;
			}
			return !(bool)_isOutOfWaterMethod.Invoke(fish, null);
		}

		internal static bool TryPickup(Fish fish, Humanoid character)
		{
			if ((Object)(object)fish == (Object)null || (Object)(object)character == (Object)null || _pickupMethod == null)
			{
				return false;
			}
			object obj = _pickupMethod.Invoke(fish, new object[1] { character });
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = ((1 == 0) ? 1 : 0);
			}
			else
			{
				num = 1;
			}
			return (byte)((uint)num | (flag ? 1u : 0u)) != 0;
		}

		private void Update()
		{
			//IL_00f8: 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_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || (!ZInput.GetButtonDown("Use") && !ZInput.GetButtonDown("JoyUse")) || (Object)(object)((Humanoid)localPlayer).GetHoverObject() != (Object)null || InventoryGui.IsVisible() || Menu.IsVisible() || Minimap.IsOpen() || Console.IsVisible() || TextInput.IsVisible() || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()))
			{
				return;
			}
			Transform val = (((Object)(object)GameCamera.instance != (Object)null) ? ((Component)GameCamera.instance).transform : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Vector3 val2 = ((Component)localPlayer).transform.position + Vector3.up;
			Fish val3 = null;
			float num = float.MaxValue;
			Fish[] array = Object.FindObjectsByType<Fish>((FindObjectsSortMode)0);
			foreach (Fish val4 in array)
			{
				if (!IsInWater(val4))
				{
					continue;
				}
				Vector3 position = ((Component)val4).transform.position;
				float num2 = Vector3.Distance(val2, position);
				if (num2 > PickupRange.Value)
				{
					continue;
				}
				Vector3 forward = val.forward;
				Vector3 val5 = position - val.position;
				float num3 = Vector3.Dot(forward, ((Vector3)(ref val5)).normalized);
				if (!(num3 < AimTolerance.Value))
				{
					float num4 = num2 - num3;
					if (num4 < num)
					{
						num = num4;
						val3 = val4;
					}
				}
			}
			if ((Object)(object)val3 != (Object)null)
			{
				TryPickup(val3, (Humanoid)(object)localPlayer);
			}
		}
	}
	[HarmonyPatch(typeof(Fish), "Interact")]
	internal static class Fish_Interact_Patch
	{
		private static bool Prefix(Fish __instance, Humanoid __0, bool __1, ref bool __result)
		{
			if (!Plugin.Enabled.Value || __1 || !Plugin.IsInWater(__instance))
			{
				return true;
			}
			__result = Plugin.TryPickup(__instance, __0);
			return false;
		}
	}
	[HarmonyPatch(typeof(Fish), "GetHoverText")]
	internal static class Fish_GetHoverText_Patch
	{
		private static void Postfix(Fish __instance, ref string __result)
		{
			if (Plugin.Enabled.Value && Plugin.IsInWater(__instance))
			{
				__result = Localization.instance.Localize(__instance.GetHoverName() + "\n[<color=yellow><b>$KEY_Use</b></color>] $inventory_pickup");
			}
		}
	}
	internal enum CatchType
	{
		None,
		HarpoonPull,
		ToInventory
	}
	internal static class WeaponCatch
	{
		internal static ConfigEntry<bool> HarpoonPullEnabled;

		internal static ConfigEntry<bool> ProjectileCatchEnabled;

		internal static ConfigEntry<float> HitRadius;

		internal static ConfigEntry<float> PullDuration;

		internal static ConfigEntry<string> HarpoonPrefabs;

		private static readonly FieldInfo OwnerField = AccessTools.Field(typeof(Projectile), "m_owner");

		private static readonly FieldInfo WeaponField = AccessTools.Field(typeof(Projectile), "m_weapon");

		private static readonly FieldInfo SkillField = AccessTools.Field(typeof(Projectile), "m_skill");

		private static readonly Dictionary<Projectile, Vector3> LastPos = new Dictionary<Projectile, Vector3>();

		private static readonly HashSet<int> SpentProjectiles = new HashSet<int>();

		private static readonly HashSet<int> FishInFlight = new HashSet<int>();

		private static MonoBehaviour _runner;

		private static MonoBehaviour Runner
		{
			get
			{
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				//IL_001c: Expected O, but got Unknown
				if ((Object)(object)_runner == (Object)null)
				{
					GameObject val = new GameObject("FishingEnhanced_CoroutineRunner");
					Object.DontDestroyOnLoad((Object)(object)val);
					((Object)val).hideFlags = (HideFlags)61;
					_runner = (MonoBehaviour)(object)val.AddComponent<CoroutineRunner>();
				}
				return _runner;
			}
		}

		internal static void BindConfig(ConfigFile config)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			HarpoonPullEnabled = config.Bind<bool>("2 - Weapon Catching", "HarpoonPull", true, "Hitting a fish with a harpoon pulls it to your feet.");
			ProjectileCatchEnabled = config.Bind<bool>("2 - Weapon Catching", "SpearAndBowCatch", true, "Hitting a fish with a thrown spear or a bow arrow puts it straight into your inventory.");
			HitRadius = config.Bind<float>("2 - Weapon Catching", "HitRadius", 0.6f, new ConfigDescription("How close (m) a projectile must pass to a fish to count as a hit.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 2f), Array.Empty<object>()));
			PullDuration = config.Bind<float>("2 - Weapon Catching", "PullDuration", 0.35f, new ConfigDescription("Seconds the harpoon takes to drag the fish to you (0 = instant).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			HarpoonPrefabs = config.Bind<string>("2 - Weapon Catching", "HarpoonPrefabs", "SpearChitin", "Comma-separated item prefab names treated as harpoons (SpearChitin = Abyssal Harpoon).");
		}

		private static bool IsLocalShot(Projectile p)
		{
			return (Object)(object)p != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null && OwnerField != null && OwnerField.GetValue(p) == Player.m_localPlayer;
		}

		internal static CatchType Classify(Projectile p)
		{
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Invalid comparison between Unknown and I4
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Invalid comparison between Unknown and I4
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Invalid comparison between Unknown and I4
			object? obj = WeaponField?.GetValue(p);
			ItemData val = (ItemData)((obj is ItemData) ? obj : null);
			string value = (((Object)(object)val?.m_dropPrefab != (Object)null) ? ((Object)val.m_dropPrefab).name : "");
			IEnumerable<string> source = from s in HarpoonPrefabs.Value.Split(',')
				select s.Trim() into s
				where s.Length > 0
				select s;
			if (source.Contains<string>(value, StringComparer.OrdinalIgnoreCase) || ((Object)p).name.IndexOf("harpoon", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				return HarpoonPullEnabled.Value ? CatchType.HarpoonPull : CatchType.None;
			}
			SkillType val2 = (SkillType)((val == null) ? ((SkillField != null) ? ((int)(SkillType)SkillField.GetValue(p)) : 0) : ((int)val.m_shared.m_skillType));
			if ((int)val2 == 5 || (int)val2 == 8)
			{
				return ProjectileCatchEnabled.Value ? CatchType.ToInventory : CatchType.None;
			}
			return CatchType.None;
		}

		private static Fish FindFish(Collider c)
		{
			return ((Object)(object)c != (Object)null) ? ((Component)c).GetComponentInParent<Fish>() : null;
		}

		private static Fish NearestFish(Vector3 point, float radius)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: 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)
			Fish result = null;
			float num = radius;
			Collider[] array = Physics.OverlapSphere(point, radius, -1, (QueryTriggerInteraction)2);
			foreach (Collider c in array)
			{
				Fish val = FindFish(c);
				if (!((Object)(object)val == (Object)null))
				{
					float num2 = Vector3.Distance(point, ((Component)val).transform.position);
					if (num2 <= num)
					{
						num = num2;
						result = val;
					}
				}
			}
			return result;
		}

		internal static void TryCatch(Projectile p, Fish fish)
		{
			if ((Object)(object)fish == (Object)null || !Plugin.Enabled.Value || SpentProjectiles.Contains(((Object)p).GetInstanceID()) || FishInFlight.Contains(((Object)fish).GetInstanceID()))
			{
				return;
			}
			CatchType catchType = Classify(p);
			if (catchType != CatchType.None)
			{
				SpentProjectiles.Add(((Object)p).GetInstanceID());
				Player localPlayer = Player.m_localPlayer;
				if (catchType != CatchType.ToInventory || !Plugin.TryPickup(fish, (Humanoid)(object)localPlayer))
				{
					Runner.StartCoroutine(PullToPlayer(fish, localPlayer));
				}
			}
		}

		private static IEnumerator PullToPlayer(Fish fish, Player player)
		{
			int id = ((Object)fish).GetInstanceID();
			FishInFlight.Add(id);
			ZNetView nview = ((Component)fish).GetComponent<ZNetView>();
			if ((Object)(object)nview != (Object)null && nview.IsValid())
			{
				nview.ClaimOwnership();
			}
			Rigidbody body = ((Component)fish).GetComponent<Rigidbody>();
			bool wasKinematic = (Object)(object)body != (Object)null && body.isKinematic;
			if ((Object)(object)body != (Object)null)
			{
				body.isKinematic = true;
			}
			((Behaviour)fish).enabled = false;
			Vector3 start = ((Component)fish).transform.position;
			float duration = PullDuration.Value;
			float t = 0f;
			while (duration > 0f && t < 1f && (Object)(object)fish != (Object)null && (Object)(object)player != (Object)null)
			{
				t += Time.deltaTime / duration;
				Vector3 target = ((Component)player).transform.position + Vector3.up * 0.3f;
				((Component)fish).transform.position = Vector3.Lerp(start, target, t);
				yield return null;
			}
			if ((Object)(object)fish != (Object)null)
			{
				if ((Object)(object)player != (Object)null)
				{
					((Component)fish).transform.position = ((Component)player).transform.position + Vector3.up * 0.3f;
				}
				if ((Object)(object)body != (Object)null)
				{
					body.isKinematic = wasKinematic;
					body.linearVelocity = Vector3.zero;
					body.angularVelocity = Vector3.zero;
				}
				((Behaviour)fish).enabled = true;
			}
			FishInFlight.Remove(id);
		}

		internal static void SweepCheck(Projectile p)
		{
			//IL_0041: 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_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Enabled.Value || !IsLocalShot(p) || SpentProjectiles.Contains(((Object)p).GetInstanceID()))
			{
				return;
			}
			Vector3 position = ((Component)p).transform.position;
			if (LastPos.TryGetValue(p, out var value))
			{
				Vector3 val = position - value;
				float magnitude = ((Vector3)(ref val)).magnitude;
				if (magnitude > 0.001f)
				{
					foreach (RaycastHit item in from h in Physics.SphereCastAll(value, HitRadius.Value, val / magnitude, magnitude, -1, (QueryTriggerInteraction)2)
						orderby ((RaycastHit)(ref h)).distance
						select h)
					{
						RaycastHit current = item;
						Fish val2 = FindFish(((RaycastHit)(ref current)).collider);
						if ((Object)(object)val2 == (Object)null)
						{
							continue;
						}
						TryCatch(p, val2);
						break;
					}
				}
			}
			LastPos[p] = position;
			if (LastPos.Count > 256)
			{
				Prune();
			}
		}

		private static void Prune()
		{
			foreach (Projectile item in LastPos.Keys.Where((Projectile k) => (Object)(object)k == (Object)null).ToList())
			{
				LastPos.Remove(item);
			}
			if (SpentProjectiles.Count > 1024)
			{
				SpentProjectiles.Clear();
			}
		}

		internal static void OnProjectileHit(Projectile p, object[] args)
		{
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Enabled.Value || !IsLocalShot(p))
			{
				return;
			}
			Collider c = args.OfType<Collider>().FirstOrDefault();
			Fish val = FindFish(c);
			if ((Object)(object)val == (Object)null)
			{
				Vector3 point = ((args.FirstOrDefault((object a) => a is Vector3) is Vector3 val2) ? val2 : ((Component)p).transform.position);
				val = NearestFish(point, HitRadius.Value);
			}
			TryCatch(p, val);
			LastPos.Remove(p);
		}
	}
	internal class CoroutineRunner : MonoBehaviour
	{
	}
	[HarmonyPatch(typeof(Projectile), "FixedUpdate")]
	internal static class Projectile_FixedUpdate_Patch
	{
		private static void Postfix(Projectile __instance)
		{
			WeaponCatch.SweepCheck(__instance);
		}
	}
	[HarmonyPatch(typeof(Projectile), "OnHit")]
	internal static class Projectile_OnHit_Patch
	{
		private static void Prefix(Projectile __instance, object[] __args)
		{
			WeaponCatch.OnProjectileHit(__instance, __args);
		}
	}
}