Decompiled source of Trolling Fishing v1.0.5

TrollingFishing.dll

Decompiled 21 hours ago
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.BufferedDeserialization;
using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators;
using YamlDotNet.Serialization.Callbacks;
using YamlDotNet.Serialization.Converters;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.NodeDeserializers;
using YamlDotNet.Serialization.NodeTypeResolvers;
using YamlDotNet.Serialization.ObjectFactories;
using YamlDotNet.Serialization.ObjectGraphTraversalStrategies;
using YamlDotNet.Serialization.ObjectGraphVisitors;
using YamlDotNet.Serialization.Schemas;
using YamlDotNet.Serialization.TypeInspectors;
using YamlDotNet.Serialization.TypeResolvers;
using YamlDotNet.Serialization.Utilities;
using YamlDotNet.Serialization.ValueDeserializers;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TrollingFishing")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("TrollingFishing")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("D50DC2B3-A9F3-4E91-83F5-B36EA9ECA08C")]
[assembly: AssemblyFileVersion("1.0.5")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.5.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class ExtensionMarkerAttribute : Attribute
	{
		private readonly string <Name>k__BackingField;

		public string Name => <Name>k__BackingField;

		public ExtensionMarkerAttribute(string name)
		{
			<Name>k__BackingField = name;
		}
	}
}
namespace TrollingFishing
{
	internal static class AzuCraftyBoxesCompat
	{
		private const string AzuCraftyBoxesAssemblyName = "AzuCraftyBoxes";

		private static bool _initialized;

		private static bool _loaded;

		private static bool _loggedAddFailure;

		private static bool _loggedRemoveFailure;

		private static bool _loggedAggressiveUnavailable;

		private static bool _loggedAggressiveFailure;

		private static bool _aggressiveRefreshFailed;

		private static MethodInfo? _addContainerMethod;

		private static MethodInfo? _removeContainerMethod;

		private static MethodInfo? _updateContainersMethod;

		private static FieldInfo? _queryFrameIdField;

		private static FieldInfo? _lastQueryTimeField;

		internal static bool IsLoaded()
		{
			return TryEnsureLoaded();
		}

		internal static void AddContainer(Container container)
		{
			if ((Object)(object)container == (Object)null || TrollingFishingPlugin.FishingRodBagAzuCraftyBoxesCompatibility.Value.IsOff() || !TryEnsureLoaded())
			{
				return;
			}
			try
			{
				_addContainerMethod?.Invoke(null, new object[1] { container });
			}
			catch (Exception ex)
			{
				if (!_loggedAddFailure)
				{
					_loggedAddFailure = true;
					TrollingFishingPlugin.ModLogger.LogWarning((object)("Could not register fishing rod bag with AzuCraftyBoxes: " + ex.GetBaseException().Message));
				}
				return;
			}
			TryAggressiveRefresh();
		}

		internal static void RemoveContainer(Container container)
		{
			if ((Object)(object)container == (Object)null || !TryEnsureLoaded())
			{
				return;
			}
			try
			{
				_removeContainerMethod?.Invoke(null, new object[1] { container });
			}
			catch (Exception ex)
			{
				if (!_loggedRemoveFailure)
				{
					_loggedRemoveFailure = true;
					TrollingFishingPlugin.ModLogger.LogWarning((object)("Could not unregister fishing rod bag from AzuCraftyBoxes: " + ex.GetBaseException().Message));
				}
				return;
			}
			TryAggressiveRefresh();
		}

		private static bool TryEnsureLoaded()
		{
			if (_initialized)
			{
				return _loaded;
			}
			_initialized = true;
			Assembly assembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault((Assembly candidate) => string.Equals(candidate.GetName().Name, "AzuCraftyBoxes", StringComparison.OrdinalIgnoreCase));
			Type type = assembly?.GetType("AzuCraftyBoxes.API");
			_addContainerMethod = type?.GetMethod("AddContainer", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Container) }, null);
			_removeContainerMethod = type?.GetMethod("RemoveContainer", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(Container) }, null);
			Type type2 = assembly?.GetType("AzuCraftyBoxes.Util.Functions.Boxes");
			_updateContainersMethod = type2?.GetMethod("UpdateContainers", BindingFlags.Static | BindingFlags.NonPublic);
			_queryFrameIdField = (assembly?.GetType("AzuCraftyBoxes.Util.Functions.Boxes+QueryFrame"))?.GetField("FrameId", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			_lastQueryTimeField = type2?.GetField("_lastQueryTime", BindingFlags.Static | BindingFlags.NonPublic);
			_loaded = _addContainerMethod != null && _removeContainerMethod != null;
			if (_loaded)
			{
				TrollingFishingPlugin.ModLogger.LogInfo((object)"AzuCraftyBoxes compatibility enabled for fishing rod bags.");
			}
			return _loaded;
		}

		private static void TryAggressiveRefresh()
		{
			if (TrollingFishingPlugin.FishingRodBagAzuCraftyBoxesAggressiveRefresh.Value.IsOff() || _aggressiveRefreshFailed)
			{
				return;
			}
			if (_updateContainersMethod == null && _queryFrameIdField == null && _lastQueryTimeField == null)
			{
				if (!_loggedAggressiveUnavailable)
				{
					_loggedAggressiveUnavailable = true;
					TrollingFishingPlugin.ModLogger.LogWarning((object)"AzuCraftyBoxes aggressive refresh is enabled, but private refresh hooks were not found. Falling back to public API only.");
				}
				return;
			}
			try
			{
				_updateContainersMethod?.Invoke(null, Array.Empty<object>());
				_queryFrameIdField?.SetValue(null, -1);
				_lastQueryTimeField?.SetValue(null, -999f);
			}
			catch (Exception ex)
			{
				_aggressiveRefreshFailed = true;
				if (!_loggedAggressiveFailure)
				{
					_loggedAggressiveFailure = true;
					TrollingFishingPlugin.ModLogger.LogWarning((object)("AzuCraftyBoxes aggressive refresh failed and was disabled for this session; public API compatibility remains active: " + ex.GetBaseException().Message));
				}
			}
		}
	}
	internal static class FishingOverrideSystem
	{
		internal sealed class FishingRodBagProxyContainer : MonoBehaviour
		{
			private Player? _player;

			private ItemData? _rod;

			private Inventory? _inventory;

			private Container? _container;

			private bool _closed;

			internal void Initialize(Player player, ItemData rod, Container container, int slots, int width, int height)
			{
				_player = player;
				_rod = rod;
				_container = container;
				container.m_name = "$item_fishingrod";
				container.m_width = width;
				container.m_height = height;
				container.m_inUse = false;
				rod.m_customData["TrollingFishing.FishingRodBag.Slots"] = slots.ToString(CultureInfo.InvariantCulture);
				LoadInventory(width, height);
				AzuCraftyBoxesCompat.AddContainer(container);
			}

			internal void SetPlayer(Player player)
			{
				_player = player;
			}

			internal void ReloadFromRod()
			{
				if (!_closed && !((Object)(object)_player == (Object)null) && _rod != null && !((Object)(object)_container == (Object)null))
				{
					ResolveGridSize(ResolveTargetSlotCount(_player, _rod), out var width, out var height);
					_container.m_width = width;
					_container.m_height = height;
					LoadInventory(width, height);
				}
			}

			private void LoadInventory(int width, int height)
			{
				if (_rod != null && !((Object)(object)_container == (Object)null))
				{
					if (_inventory != null)
					{
						Inventory? inventory = _inventory;
						inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, new Action(Save));
						UnregisterFishingRodBagInventory(_inventory);
					}
					_inventory = (((Object)(object)_player != (Object)null) ? LoadFishingRodBagInventory(_player, _rod, out width, out height) : CreateFishingRodBagInventory(_rod, width, height));
					Inventory? inventory2 = _inventory;
					inventory2.m_onChanged = (Action)Delegate.Combine(inventory2.m_onChanged, new Action(Save));
					RegisterFishingRodBagInventory(_inventory, _rod);
					_container.m_width = width;
					_container.m_height = height;
					_container.m_inventory = _inventory;
				}
			}

			private void Update()
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)_player != (Object)null)
				{
					((Component)this).transform.position = ((Component)_player).transform.position;
				}
			}

			internal void Save()
			{
				if (_rod != null && _inventory != null)
				{
					SaveFishingRodBagInventory(_rod, _inventory, refreshProxy: false);
				}
			}

			internal void CloseAndDestroy()
			{
				if (!_closed)
				{
					Cleanup();
					Object.Destroy((Object)(object)((Component)this).gameObject);
				}
			}

			private void OnDestroy()
			{
				Cleanup();
			}

			private void Cleanup()
			{
				if (!_closed)
				{
					_closed = true;
					Save();
					if (_inventory != null)
					{
						Inventory? inventory = _inventory;
						inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, new Action(Save));
						UnregisterFishingRodBagInventory(_inventory);
					}
					if ((Object)(object)_container != (Object)null)
					{
						AzuCraftyBoxesCompat.RemoveContainer(_container);
					}
				}
			}
		}

		internal sealed class FishingRodBagContainer : MonoBehaviour
		{
			private Player? _player;

			private ItemData? _rod;

			private Inventory? _inventory;

			private Container? _container;

			private bool _closed;

			internal void Initialize(Player player, ItemData rod, Container container, int slots, int width, int height)
			{
				_player = player;
				_rod = rod;
				_container = container;
				_inventory = LoadFishingRodBagInventory(player, rod, out width, out height);
				Inventory? inventory = _inventory;
				inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, new Action(Save));
				RegisterFishingRodBagInventory(_inventory, rod);
				container.m_name = "$item_fishingrod";
				container.m_width = width;
				container.m_height = height;
				container.m_inventory = _inventory;
				container.m_inUse = true;
				AzuCraftyBoxesCompat.AddContainer(container);
				rod.m_customData["TrollingFishing.FishingRodBag.Slots"] = slots.ToString(CultureInfo.InvariantCulture);
				Save();
			}

			private void Update()
			{
				//IL_0060: Unknown result type (might be due to invalid IL or missing references)
				if (!_closed && (Object)(object)_container != (Object)null && (Object)(object)InventoryGui.instance != (Object)null && (Object)(object)InventoryGui.instance.m_currentContainer != (Object)(object)_container)
				{
					CloseAndDestroy();
				}
				else if ((Object)(object)_player != (Object)null)
				{
					((Component)this).transform.position = ((Component)_player).transform.position;
				}
			}

			internal void Save()
			{
				if (_rod != null && _inventory != null)
				{
					SaveFishingRodBagInventory(_rod, _inventory);
				}
			}

			internal void CloseAndDestroy()
			{
				if (!_closed)
				{
					_closed = true;
					Save();
					if (_rod != null)
					{
						FishingRodBagUiState.OpenRods.Remove(_rod);
					}
					if (_inventory != null)
					{
						Inventory? inventory = _inventory;
						inventory.m_onChanged = (Action)Delegate.Remove(inventory.m_onChanged, new Action(Save));
						UnregisterFishingRodBagInventory(_inventory);
					}
					if ((Object)(object)_container != (Object)null)
					{
						AzuCraftyBoxesCompat.RemoveContainer(_container);
					}
					Object.Destroy((Object)(object)((Component)this).gameObject);
				}
			}
		}

		internal readonly struct ExtraDropChanceState
		{
			internal readonly DropTable? DropTable;

			internal readonly float OriginalDropChance;

			internal ExtraDropChanceState(DropTable dropTable, float originalDropChance)
			{
				DropTable = dropTable;
				OriginalDropChance = originalDropChance;
			}
		}

		internal enum FishingRodAmmoSource
		{
			Inventory,
			FishingRodBag
		}

		internal readonly struct FishingRodAmmoSelection
		{
			internal readonly ItemData AmmoItem;

			internal readonly FishingRodAmmoSource Source;

			internal bool IsValid => AmmoItem != null;

			internal FishingRodAmmoSelection(ItemData ammoItem, FishingRodAmmoSource source)
			{
				AmmoItem = ammoItem;
				Source = source;
			}
		}

		internal readonly struct MultiLineBaitReservation
		{
			internal readonly string PrefabName;

			internal readonly bool FromRodBag;

			internal readonly ItemData? Rod;

			internal bool IsValid => !string.IsNullOrWhiteSpace(PrefabName);

			internal MultiLineBaitReservation(string prefabName, bool fromRodBag, ItemData? rod = null)
			{
				PrefabName = prefabName ?? "";
				FromRodBag = fromRodBag;
				Rod = rod;
			}
		}

		internal readonly struct MultiLineFishingSetupContext
		{
			internal static readonly MultiLineFishingSetupContext Empty = new MultiLineFishingSetupContext(null, null, 0, 0, default(MultiLineBaitReservation), default(MultiLineBaitReservation));

			internal readonly Character? Owner;

			internal readonly ItemData? Rod;

			internal readonly int LineIndex;

			internal readonly int PrimaryEquivalentLineIndex;

			internal readonly MultiLineBaitReservation ReservedBait;

			internal readonly MultiLineBaitReservation BaitReturnSource;

			internal readonly bool ConsumeTrackedBaitOnSetup;

			internal MultiLineFishingSetupContext(Character? owner, ItemData? rod, int lineIndex, int primaryEquivalentLineIndex, MultiLineBaitReservation reservedBait, MultiLineBaitReservation baitReturnSource, bool consumeTrackedBaitOnSetup = false)
			{
				Owner = owner;
				Rod = rod;
				LineIndex = Mathf.Max(0, lineIndex);
				PrimaryEquivalentLineIndex = Mathf.Max(0, primaryEquivalentLineIndex);
				ReservedBait = reservedBait;
				BaitReturnSource = baitReturnSource;
				ConsumeTrackedBaitOnSetup = consumeTrackedBaitOnSetup;
			}
		}

		internal readonly struct MultiLineFishingUpdateContext
		{
			internal readonly Character Owner;

			internal readonly float StaminaFactor;

			internal readonly float SkillRaiseFactor;

			internal MultiLineFishingUpdateContext(Character owner, float staminaFactor, float skillRaiseFactor)
			{
				Owner = owner;
				StaminaFactor = staminaFactor;
				SkillRaiseFactor = skillRaiseFactor;
			}
		}

		private sealed class MultiLineFishingSetupScope : IDisposable
		{
			public void Dispose()
			{
				MultiLineFishingCastState.SetupDepth = Mathf.Max(0, MultiLineFishingCastState.SetupDepth - 1);
				if (MultiLineFishingCastState.SetupContexts.Count > 0)
				{
					MultiLineFishingCastState.SetupContexts.RemoveAt(MultiLineFishingCastState.SetupContexts.Count - 1);
				}
			}
		}

		private sealed class BaitReturnSetupScope : IDisposable
		{
			private readonly Attack? _attack;

			internal BaitReturnSetupScope(Attack? attack)
			{
				_attack = attack;
			}

			public void Dispose()
			{
				if (BaitSourceTrackerState.SetupContexts.Count > 0)
				{
					BaitSourceTrackerState.SetupContexts.RemoveAt(BaitSourceTrackerState.SetupContexts.Count - 1);
				}
				if (_attack != null)
				{
					BaitSourceTrackerState.AttackSources.Remove(_attack);
				}
			}
		}

		private sealed class MultiLineFishingUpdateScope : IDisposable
		{
			public void Dispose()
			{
				if (MultiLineFishingCastState.UpdateContexts.Count > 0)
				{
					MultiLineFishingCastState.UpdateContexts.RemoveAt(MultiLineFishingCastState.UpdateContexts.Count - 1);
				}
			}
		}

		private sealed class MultiLineFishingFloatMarker : MonoBehaviour
		{
			private Character? _owner;

			private ItemData? _rod;

			private float _ignoreAttackUntil;

			private int _lineIndex;

			private int _primaryEquivalentLineIndex;

			private MultiLineBaitReservation _reservedBait;

			private MultiLineBaitReservation _baitReturnSource;

			private bool _initialAttackEnded;

			private bool _loggedInitialAttackSkip;

			internal Character? Owner => _owner;

			internal ItemData? Rod => _rod;

			internal int LineIndex => _lineIndex;

			internal int PrimaryEquivalentLineIndex => _primaryEquivalentLineIndex;

			internal bool IsAdditionalLine => _lineIndex != _primaryEquivalentLineIndex;

			internal MultiLineBaitReservation ReservedBait => _reservedBait;

			internal MultiLineBaitReservation BaitReturnSource => _baitReturnSource;

			internal void Initialize(Character owner, ItemData? rod, float ignoreAttackUntil, int lineIndex, int primaryEquivalentLineIndex, MultiLineBaitReservation reservedBait, MultiLineBaitReservation baitReturnSource)
			{
				_owner = owner;
				_rod = rod;
				_ignoreAttackUntil = ignoreAttackUntil;
				_lineIndex = Mathf.Max(0, lineIndex);
				_primaryEquivalentLineIndex = Mathf.Max(0, primaryEquivalentLineIndex);
				_reservedBait = reservedBait;
				_baitReturnSource = baitReturnSource;
				_initialAttackEnded = false;
				_loggedInitialAttackSkip = false;
			}

			internal bool ShouldSkipAttackCancellation()
			{
				if ((Object)(object)_owner == (Object)null)
				{
					return false;
				}
				if (!_owner.InAttack() && !_owner.IsDrawingBow())
				{
					_initialAttackEnded = true;
					return false;
				}
				if (_initialAttackEnded || Time.time > _ignoreAttackUntil)
				{
					return false;
				}
				if (!_loggedInitialAttackSkip)
				{
					_loggedInitialAttackSkip = true;
					TrollingFishingPlugin.LogDebug("[Fishing multiLine] suppressing initial cast cancellation object=" + ((Object)((Component)this).gameObject).name + ".");
				}
				return true;
			}
		}

		private sealed class FishingBaitReturnTracker : MonoBehaviour
		{
			private MultiLineBaitReservation _source;

			private bool _settled;

			internal MultiLineBaitReservation Source => _source;

			internal bool IsSettled => _settled;

			internal void Initialize(MultiLineBaitReservation source, bool settled = false)
			{
				_source = source;
				_settled = !source.IsValid || settled;
			}

			internal bool TrySettle()
			{
				if (_settled)
				{
					return false;
				}
				_settled = true;
				return true;
			}
		}

		private static readonly Dictionary<FishingFloat, float> FishingFloatOwnerSkillFactorCache = new Dictionary<FishingFloat, float>();

		private static int _fishingFloatOwnerSkillFactorCacheFrame = -1;

		private static readonly FieldInfo? FishingFloatBaitConsumedField = AccessTools.Field(typeof(FishingFloat), "m_baitConsumed");

		private static readonly FieldInfo? InventoryGridElementsField = AccessTools.Field(typeof(InventoryGrid), "m_elements");

		private static readonly MethodInfo? InventoryGuiCloseContainerMethod = AccessTools.Method(typeof(InventoryGui), "CloseContainer", (Type[])null, (Type[])null);

		internal static int CountAvailableAmmo(Humanoid humanoid, ItemData weapon, string ammoType)
		{
			int num = CountAmmo(humanoid.GetInventory(), ammoType);
			if (TrollingFishingPlugin.FishingRodBag.Value.IsOn())
			{
				Player val = (Player)(object)((humanoid is Player) ? humanoid : null);
				if (val != null && IsFishingRod(weapon))
				{
					num += CountFishingRodBagAmmo(val, weapon, ammoType);
				}
			}
			return num;
		}

		internal static int CountAvailableAmmo(Humanoid humanoid, ItemData weapon, string ammoType, ItemData ammoItem)
		{
			int num = CountAmmo(humanoid.GetInventory(), ammoType, ammoItem);
			if (TrollingFishingPlugin.FishingRodBag.Value.IsOn())
			{
				Player val = (Player)(object)((humanoid is Player) ? humanoid : null);
				if (val != null && IsFishingRod(weapon))
				{
					num += CountFishingRodBagAmmo(val, weapon, ammoType, ammoItem);
				}
			}
			return num;
		}

		internal static int CountAvailableAmmoFromSource(Humanoid humanoid, ItemData weapon, string ammoType, ItemData ammoItem, FishingRodAmmoSource source)
		{
			if ((Object)(object)humanoid == (Object)null || weapon == null || string.IsNullOrWhiteSpace(ammoType) || ammoItem == null)
			{
				return 0;
			}
			switch (source)
			{
			case FishingRodAmmoSource.Inventory:
				return CountAmmo(humanoid.GetInventory(), ammoType, ammoItem);
			case FishingRodAmmoSource.FishingRodBag:
				if (TrollingFishingPlugin.FishingRodBag.Value.IsOn())
				{
					Player val = (Player)(object)((humanoid is Player) ? humanoid : null);
					if (val != null && IsFishingRod(weapon))
					{
						return CountFishingRodBagAmmo(val, weapon, ammoType, ammoItem);
					}
				}
				break;
			}
			return 0;
		}

		internal static bool TryResolveFishingRodAmmo(Humanoid humanoid, ItemData weapon, out ItemData ammoItem)
		{
			ammoItem = null;
			if (!TryResolveFishingRodAmmoSelection(humanoid, weapon, out var selection))
			{
				return false;
			}
			ammoItem = selection.AmmoItem;
			return true;
		}

		internal static bool TryResolveFishingRodAmmoSelection(Humanoid humanoid, ItemData weapon, out FishingRodAmmoSelection selection)
		{
			selection = default(FishingRodAmmoSelection);
			if ((Object)(object)humanoid == (Object)null || weapon == null || string.IsNullOrWhiteSpace(weapon.m_shared.m_ammoType))
			{
				return false;
			}
			string ammoType = weapon.m_shared.m_ammoType;
			Player val = (Player)(object)((humanoid is Player) ? humanoid : null);
			if (val != null && TryFindEquippedInventoryAmmo(val, ammoType, out ItemData ammoItem))
			{
				selection = new FishingRodAmmoSelection(ammoItem, FishingRodAmmoSource.Inventory);
				return true;
			}
			if (TryFindFishingRodAmmo(humanoid, weapon, out ItemData ammoItem2))
			{
				selection = new FishingRodAmmoSelection(ammoItem2, FishingRodAmmoSource.FishingRodBag);
				return true;
			}
			Inventory inventory = humanoid.GetInventory();
			ItemData ammoItem3 = humanoid.GetAmmoItem();
			if (ammoItem3 != null && inventory.ContainsItem(ammoItem3) && IsMatchingAmmo(ammoItem3, ammoType))
			{
				selection = new FishingRodAmmoSelection(ammoItem3, FishingRodAmmoSource.Inventory);
				return true;
			}
			ammoItem3 = inventory.GetAmmoItem(ammoType, (string)null);
			if (ammoItem3 == null)
			{
				return false;
			}
			selection = new FishingRodAmmoSelection(ammoItem3, FishingRodAmmoSource.Inventory);
			return true;
		}

		internal static bool TryFindFishingRodAmmo(Humanoid humanoid, ItemData weapon, out ItemData ammoItem)
		{
			ammoItem = null;
			if (!TrollingFishingPlugin.FishingRodBag.Value.IsOff())
			{
				Player val = (Player)(object)((humanoid is Player) ? humanoid : null);
				if (val != null && IsFishingRod(weapon) && !string.IsNullOrWhiteSpace(weapon.m_shared.m_ammoType) && !TryFindEquippedInventoryAmmo(val, weapon.m_shared.m_ammoType, out ItemData _))
				{
					return TryFindFishingRodBagAmmo(val, weapon, weapon.m_shared.m_ammoType, out ammoItem);
				}
			}
			return false;
		}

		internal static bool HasFishingRodBagAmmo(Humanoid humanoid, ItemData weapon)
		{
			if (TrollingFishingPlugin.FishingRodBag.Value.IsOn())
			{
				Player val = (Player)(object)((humanoid is Player) ? humanoid : null);
				if (val != null && IsFishingRod(weapon) && !string.IsNullOrWhiteSpace(weapon.m_shared.m_ammoType) && !TryFindEquippedInventoryAmmo(val, weapon.m_shared.m_ammoType, out ItemData _))
				{
					return CountFishingRodBagAmmo(val, weapon, weapon.m_shared.m_ammoType) > 0;
				}
			}
			return false;
		}

		internal static bool TryUseFishingRodBagAmmoForVanillaAttack(Attack attack, ref ItemData ammoItem, out bool result)
		{
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Invalid comparison between Unknown and I4
			Attack attack2 = attack;
			result = true;
			ammoItem = null;
			if (!TrollingFishingPlugin.FishingRodBag.Value.IsOff())
			{
				Humanoid obj = attack2?.m_character;
				Player val = (Player)(object)((obj is Player) ? obj : null);
				if (val != null && IsFishingRod(attack2.m_weapon) && !string.IsNullOrWhiteSpace(attack2.m_weapon.m_shared.m_ammoType) && !TryFindEquippedInventoryAmmo(val, attack2.m_weapon.m_shared.m_ammoType, out ItemData _) && TryFindFishingRodBagAmmo(val, attack2.m_weapon, attack2.m_weapon.m_shared.m_ammoType, out ItemData bagAmmo))
				{
					int width;
					int height;
					Inventory val2 = LoadFishingRodBagInventory(val, attack2.m_weapon, out width, out height);
					ItemData val3 = ((IEnumerable<ItemData>)val2.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData item) => IsMatchingAmmo(item, attack2.m_weapon.m_shared.m_ammoType) && (Object)(object)item.m_dropPrefab == (Object)(object)bagAmmo.m_dropPrefab)) ?? val2.GetAmmoItem(attack2.m_weapon.m_shared.m_ammoType, (string)null);
					if (val3 == null)
					{
						return false;
					}
					if ((int)val3.m_shared.m_itemType == 2)
					{
						if (!((Humanoid)val).ConsumeItem(val2, val3, false))
						{
							result = false;
							return true;
						}
					}
					else
					{
						val2.RemoveItem(val3, 1);
					}
					SaveFishingRodBagInventory(attack2.m_weapon, val2);
					RegisterAttackBaitReturnSource(attack2, val, attack2.m_weapon, bagAmmo);
					ammoItem = bagAmmo;
					attack2.m_ammoItem = bagAmmo;
					return true;
				}
			}
			return false;
		}

		internal static bool TryUseFishingRodBagItem(Humanoid humanoid, Inventory inventory, ItemData item)
		{
			if (!TrollingFishingPlugin.FishingRodBag.Value.IsOff())
			{
				Player val = (Player)(object)((humanoid is Player) ? humanoid : null);
				if (val != null && inventory != null && item != null && FishingRodBagStoreState.InventoryOwners.TryGetValue(inventory, out ItemData value) && IsFishingRod(value))
				{
					TryResolveMissingDropPrefab(item);
					if (!string.IsNullOrWhiteSpace(value.m_shared.m_ammoType) && IsMatchingAmmo(item, value.m_shared.m_ammoType))
					{
						if ((Object)(object)item.m_dropPrefab == (Object)null)
						{
							return false;
						}
						if (IsSelectedFishingRodBagBait(value, item))
						{
							value.m_customData.Remove("TrollingFishing.FishingRodBag.SelectedBait");
							NotifyFishingRodBagBaitSelectionChanged(val, inventory, value, saveBagInventory: true);
							((Character)val).Message((MessageType)2, "$msg_removed " + item.m_shared.m_name, 0, (Sprite)null);
							TrollingFishingPlugin.LogDebug("[Fishing bag] unselected bait " + ((Object)item.m_dropPrefab).name + " for rod bag.");
							return true;
						}
						UnequipMatchingInventoryAmmo(val, value.m_shared.m_ammoType);
						value.m_customData["TrollingFishing.FishingRodBag.SelectedBait"] = ((Object)item.m_dropPrefab).name;
						NotifyFishingRodBagBaitSelectionChanged(val, inventory, value, saveBagInventory: true);
						((Character)val).Message((MessageType)2, "$msg_added " + item.m_shared.m_name, 0, (Sprite)null);
						TrollingFishingPlugin.LogDebug("[Fishing bag] selected bait " + ((Object)item.m_dropPrefab).name + " for rod bag.");
						return true;
					}
					if (IsFishChumItem(item))
					{
						return TryMoveFishChumToPlayerInventoryAndUse(val, inventory, item);
					}
					return false;
				}
			}
			return false;
		}

		internal static void SyncFishingRodBagBaitAfterInventoryUse(Humanoid humanoid, Inventory inventory, ItemData item)
		{
			if (!TrollingFishingPlugin.FishingRodBag.Value.IsOff())
			{
				Player val = (Player)(object)((humanoid is Player) ? humanoid : null);
				if (val != null && inventory != null && item != null && inventory == ((Humanoid)val).GetInventory())
				{
					TryResolveMissingDropPrefab(item);
					ClearMatchingFishingRodBagBaitSelections(val, item);
				}
			}
		}

		private static bool IsSelectedFishingRodBagBait(ItemData rod, ItemData item)
		{
			if (rod != null && (Object)(object)item?.m_dropPrefab != (Object)null && rod.m_customData.TryGetValue("TrollingFishing.FishingRodBag.SelectedBait", out var value))
			{
				return string.Equals(value, ((Object)item.m_dropPrefab).name, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool TryFindEquippedInventoryAmmo(Player player, string ammoType, out ItemData ammoItem)
		{
			ammoItem = null;
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || string.IsNullOrWhiteSpace(ammoType))
			{
				return false;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_equipped && IsMatchingAmmo(allItem, ammoType))
				{
					ammoItem = allItem;
					return true;
				}
			}
			ItemData ammoItem2 = ((Humanoid)player).GetAmmoItem();
			if (ammoItem2 != null && inventory.ContainsItem(ammoItem2) && IsMatchingAmmo(ammoItem2, ammoType))
			{
				ammoItem = ammoItem2;
				return true;
			}
			return false;
		}

		private static bool TryFindEquippedInventoryAmmo(Player player, ItemData sourceAmmo, out ItemData ammoItem)
		{
			ammoItem = null;
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || sourceAmmo == null)
			{
				return false;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem.m_equipped && IsSameAmmoPrefabOrName(allItem, sourceAmmo))
				{
					ammoItem = allItem;
					return true;
				}
			}
			ItemData ammoItem2 = ((Humanoid)player).GetAmmoItem();
			if (ammoItem2 != null && inventory.ContainsItem(ammoItem2) && IsSameAmmoPrefabOrName(ammoItem2, sourceAmmo))
			{
				ammoItem = ammoItem2;
				return true;
			}
			return false;
		}

		private static void UnequipMatchingInventoryAmmo(Player player, string ammoType)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || string.IsNullOrWhiteSpace(ammoType))
			{
				return;
			}
			bool flag = false;
			foreach (ItemData item in inventory.GetAllItems().ToList())
			{
				if (item.m_equipped && IsMatchingAmmo(item, ammoType))
				{
					((Humanoid)player).UnequipItem(item, true);
					flag = true;
				}
			}
			ItemData ammoItem = ((Humanoid)player).GetAmmoItem();
			if (ammoItem != null && inventory.ContainsItem(ammoItem) && IsMatchingAmmo(ammoItem, ammoType))
			{
				((Humanoid)player).UnequipItem(ammoItem, true);
				flag = true;
			}
			if (flag)
			{
				inventory.Changed();
			}
		}

		private static void ClearMatchingFishingRodBagBaitSelections(Player player, ItemData equippedAmmo)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null || equippedAmmo == null)
			{
				return;
			}
			bool flag = false;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (IsFishingRod(allItem) && !string.IsNullOrWhiteSpace(allItem.m_shared.m_ammoType) && IsMatchingAmmo(equippedAmmo, allItem.m_shared.m_ammoType) && allItem.m_customData.ContainsKey("TrollingFishing.FishingRodBag.SelectedBait"))
				{
					allItem.m_customData.Remove("TrollingFishing.FishingRodBag.SelectedBait");
					NotifyOpenFishingRodBagInventoriesChanged(allItem, saveBagInventories: false);
					flag = true;
				}
			}
			if (flag)
			{
				inventory.Changed();
				TrollingFishingPlugin.LogDebug("[Fishing bag] cleared selected bag bait because an inventory bait was equipped.");
			}
		}

		private static void NotifyFishingRodBagBaitSelectionChanged(Player player, Inventory bagInventory, ItemData rod, bool saveBagInventory)
		{
			NotifyOpenFishingRodBagInventoriesChanged(rod, saveBagInventory);
			if (saveBagInventory && bagInventory != null)
			{
				bagInventory.Changed();
			}
			if (player != null)
			{
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (inventory != null)
				{
					inventory.Changed();
				}
			}
		}

		private static void NotifyOpenFishingRodBagInventoriesChanged(ItemData rod, bool saveBagInventories)
		{
			if (rod == null)
			{
				return;
			}
			foreach (KeyValuePair<Inventory, ItemData> item in FishingRodBagStoreState.InventoryOwners.ToList())
			{
				if (item.Value != rod)
				{
					continue;
				}
				Inventory key = item.Key;
				if (key != null)
				{
					if (saveBagInventories)
					{
						key.Changed();
					}
					else
					{
						MarkFishingRodBagInventoryVisualDirty(key);
					}
				}
			}
		}

		private static bool IsSameAmmoPrefabOrName(ItemData item, ItemData sourceAmmo)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (item == null || sourceAmmo == null)
			{
				return false;
			}
			if (!IsAmmoItemType(item.m_shared.m_itemType) || !IsAmmoItemType(sourceAmmo.m_shared.m_itemType))
			{
				return false;
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null && (Object)(object)sourceAmmo.m_dropPrefab != (Object)null)
			{
				return string.Equals(((Object)item.m_dropPrefab).name, ((Object)sourceAmmo.m_dropPrefab).name, StringComparison.OrdinalIgnoreCase);
			}
			return string.Equals(item.m_shared.m_name, sourceAmmo.m_shared.m_name, StringComparison.OrdinalIgnoreCase);
		}

		private static bool TryMoveFishChumToPlayerInventoryAndUse(Player player, Inventory bagInventory, ItemData item)
		{
			if ((Object)(object)player == (Object)null || bagInventory == null || item == null || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return true;
			}
			ItemData val = FindMatchingInventoryItem(inventory, item, requireEquipped: true);
			if (!inventory.CanAddItem(item, item.m_stack))
			{
				((Character)player).Message((MessageType)1, Localization.instance.Localize("$inventory_full"), 0, (Sprite)null);
				return true;
			}
			ItemData val2 = item.Clone();
			val2.m_equipped = false;
			if (!inventory.AddItem(val2))
			{
				((Character)player).Message((MessageType)1, Localization.instance.Localize("$inventory_full"), 0, (Sprite)null);
				return true;
			}
			bagInventory.RemoveItem(item);
			bagInventory.Changed();
			if (val != null && inventory.ContainsItem(val))
			{
				((Character)player).Message((MessageType)2, "$msg_added " + val2.m_shared.m_name, 0, (Sprite)null);
				return true;
			}
			ItemData val3 = (ItemData)(inventory.ContainsItem(val2) ? ((object)val2) : ((object)FindMatchingInventoryItem(inventory, val2, requireEquipped: false)));
			if (val3 == null)
			{
				return true;
			}
			((Humanoid)player).UseItem(inventory, val3, true);
			return true;
		}

		private static ItemData? FindMatchingInventoryItem(Inventory inventory, ItemData source, bool requireEquipped)
		{
			if (inventory == null || source == null)
			{
				return null;
			}
			string text = (((Object)(object)source.m_dropPrefab != (Object)null) ? StripCloneSuffix(((Object)source.m_dropPrefab).name) : null);
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if ((!requireEquipped || allItem.m_equipped) && (requireEquipped || !allItem.m_equipped) && allItem.m_quality == source.m_quality && allItem.m_worldLevel == source.m_worldLevel)
				{
					if (!string.IsNullOrWhiteSpace(text) && (Object)(object)allItem.m_dropPrefab != (Object)null && string.Equals(StripCloneSuffix(((Object)allItem.m_dropPrefab).name), text, StringComparison.OrdinalIgnoreCase))
					{
						return allItem;
					}
					if (string.Equals(allItem.m_shared.m_name, source.m_shared.m_name, StringComparison.OrdinalIgnoreCase))
					{
						return allItem;
					}
				}
			}
			return null;
		}

		private static int CountAmmo(Inventory inventory, string ammoType)
		{
			if (inventory == null || string.IsNullOrWhiteSpace(ammoType))
			{
				return 0;
			}
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (IsMatchingAmmo(allItem, ammoType))
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		private static int CountAmmo(Inventory inventory, string ammoType, ItemData targetAmmo)
		{
			if (inventory == null || string.IsNullOrWhiteSpace(ammoType) || targetAmmo == null)
			{
				return 0;
			}
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (IsMatchingAmmo(allItem, ammoType, targetAmmo))
				{
					num += allItem.m_stack;
				}
			}
			return num;
		}

		private static bool IsMatchingAmmo(ItemData item, string ammoType)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (item == null || string.IsNullOrWhiteSpace(ammoType))
			{
				return false;
			}
			if (!IsAmmoItemType(item.m_shared.m_itemType))
			{
				return false;
			}
			if (!string.Equals(item.m_shared.m_ammoType, ammoType, StringComparison.OrdinalIgnoreCase))
			{
				if ((Object)(object)item.m_dropPrefab != (Object)null)
				{
					return string.Equals(((Object)item.m_dropPrefab).name, ammoType, StringComparison.OrdinalIgnoreCase);
				}
				return false;
			}
			return true;
		}

		private static bool IsMatchingAmmo(ItemData item, string ammoType, ItemData targetAmmo)
		{
			if (!IsMatchingAmmo(item, ammoType) || targetAmmo == null)
			{
				return false;
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null && (Object)(object)targetAmmo.m_dropPrefab != (Object)null)
			{
				return string.Equals(((Object)item.m_dropPrefab).name, ((Object)targetAmmo.m_dropPrefab).name, StringComparison.OrdinalIgnoreCase);
			}
			return string.Equals(item.m_shared.m_name, targetAmmo.m_shared.m_name, StringComparison.OrdinalIgnoreCase);
		}

		private static bool IsAmmoItemType(ItemType itemType)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			if ((int)itemType != 9 && (int)itemType != 23)
			{
				return (int)itemType == 2;
			}
			return true;
		}

		private static List<ItemData> ResolveInventoryAmmoRemovalOrder(Inventory inventory, string ammoType, ItemData targetAmmo)
		{
			string ammoType2 = ammoType;
			ItemData targetAmmo2 = targetAmmo;
			if (inventory == null || string.IsNullOrWhiteSpace(ammoType2) || targetAmmo2 == null)
			{
				return new List<ItemData>();
			}
			return (from item in inventory.GetAllItems()
				where IsMatchingAmmo(item, ammoType2, targetAmmo2)
				select item).ToList();
		}

		private static int CountFishingRodBagAmmo(Player player, ItemData rod, string ammoType)
		{
			string ammoType2 = ammoType;
			int width;
			int height;
			return (from item in LoadFishingRodBagInventory(player, rod, out width, out height).GetAllItems()
				where IsMatchingAmmo(item, ammoType2)
				select item).Sum((ItemData item) => item.m_stack);
		}

		private static int CountFishingRodBagAmmo(Player player, ItemData rod, string ammoType, ItemData targetAmmo)
		{
			string ammoType2 = ammoType;
			ItemData targetAmmo2 = targetAmmo;
			int width;
			int height;
			return (from item in LoadFishingRodBagInventory(player, rod, out width, out height).GetAllItems()
				where IsMatchingAmmo(item, ammoType2, targetAmmo2)
				select item).Sum((ItemData item) => item.m_stack);
		}

		private static bool TryFindFishingRodBagAmmo(Player player, ItemData rod, string ammoType, out ItemData ammoItem)
		{
			int width;
			int height;
			Inventory val = LoadFishingRodBagInventory(player, rod, out width, out height);
			ItemData val2 = ResolveSelectedFishingRodBagAmmo(val, rod, ammoType) ?? val.GetAmmoItem(ammoType, (string)null);
			if (val2 == null)
			{
				ammoItem = null;
				return false;
			}
			ammoItem = val2.Clone();
			ammoItem.m_stack = 1;
			return true;
		}

		private static ItemData? ResolveSelectedFishingRodBagAmmo(Inventory inventory, ItemData rod, string ammoType)
		{
			string ammoType2 = ammoType;
			if (inventory == null || rod == null || !rod.m_customData.TryGetValue("TrollingFishing.FishingRodBag.SelectedBait", out var selectedPrefabName) || string.IsNullOrWhiteSpace(selectedPrefabName))
			{
				return null;
			}
			ItemData? obj = ((IEnumerable<ItemData>)inventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData item) => (Object)(object)item?.m_dropPrefab != (Object)null && IsMatchingAmmo(item, ammoType2) && string.Equals(((Object)item.m_dropPrefab).name, selectedPrefabName, StringComparison.OrdinalIgnoreCase)));
			if (obj == null)
			{
				rod.m_customData.Remove("TrollingFishing.FishingRodBag.SelectedBait");
			}
			return obj;
		}

		private static List<ItemData> ResolveFishingRodBagAmmoRemovalOrder(Inventory inventory, ItemData rod, string ammoType, ItemData targetAmmo)
		{
			string ammoType2 = ammoType;
			ItemData targetAmmo2 = targetAmmo;
			List<ItemData> list = (from item in inventory.GetAllItems()
				where IsMatchingAmmo(item, ammoType2, targetAmmo2)
				select item).ToList();
			ItemData selected = ResolveSelectedFishingRodBagAmmo(inventory, rod, ammoType2);
			if (selected == null)
			{
				return list;
			}
			return list.OrderByDescending((ItemData item) => item == selected).ToList();
		}

		internal static void UpdateFishingRodBagAzuProxyLifetime(Player player)
		{
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && FishingRodBagProxyState.Proxies.Count != 0 && (!ShouldUseFishingRodBagProxies() || Time.time > FishingRodBagProxyState.KeepAliveUntil))
			{
				DestroyFishingRodBagProxies();
			}
		}

		internal static void RefreshFishingRodBagProxiesForAzuContext(Humanoid user)
		{
			Player val = (Player)(object)((user is Player) ? user : null);
			if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && ShouldUseFishingRodBagProxies())
			{
				FishingRodBagProxyState.KeepAliveUntil = Time.time + 2f;
				SyncFishingRodBagProxies(val);
			}
		}

		internal static void ClearFishingRodBagProxiesForAzuContext()
		{
			FishingRodBagProxyState.KeepAliveUntil = -1f;
			DestroyFishingRodBagProxies();
		}

		private static bool ShouldUseFishingRodBagProxies()
		{
			if (TrollingFishingPlugin.FishingRodBag.Value.IsOn() && TrollingFishingPlugin.FishingRodBagAzuCraftyBoxesCompatibility.Value.IsOn())
			{
				return AzuCraftyBoxesCompat.IsLoaded();
			}
			return false;
		}

		private static void SyncFishingRodBagProxies(Player player)
		{
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				DestroyFishingRodBagProxies();
				return;
			}
			ItemData val = ResolveAzuCraftyBoxesFishingRod(player, inventory);
			foreach (ItemData item in FishingRodBagProxyState.Proxies.Keys.ToList())
			{
				if (item != val || FishingRodBagUiState.OpenRods.Contains(item))
				{
					RemoveFishingRodBagProxy(item);
				}
			}
			if (val != null && !FishingRodBagUiState.OpenRods.Contains(val))
			{
				if (FishingRodBagProxyState.Proxies.TryGetValue(val, out FishingRodBagProxyContainer value))
				{
					value.SetPlayer(player);
				}
				else
				{
					CreateFishingRodBagProxy(player, val);
				}
			}
		}

		private static ItemData? ResolveAzuCraftyBoxesFishingRod(Player player, Inventory playerInventory)
		{
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			if (IsFishingRod(currentWeapon) && playerInventory.ContainsItem(currentWeapon))
			{
				return currentWeapon;
			}
			return ((IEnumerable<ItemData>)playerInventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)IsFishingRod);
		}

		private static void CreateFishingRodBagProxy(Player player, ItemData rod)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			int slots = ResolveTargetSlotCount(player, rod);
			ResolveGridSize(slots, out var width, out var height);
			GameObject val = new GameObject("TrollingFishing_FishingRodBagProxy");
			val.transform.position = ((Component)player).transform.position;
			FishingRodBagProxyContainer fishingRodBagProxyContainer = val.AddComponent<FishingRodBagProxyContainer>();
			Container container = val.AddComponent<Container>();
			fishingRodBagProxyContainer.Initialize(player, rod, container, slots, width, height);
			FishingRodBagProxyState.Proxies[rod] = fishingRodBagProxyContainer;
		}

		private static void RemoveFishingRodBagProxy(ItemData rod)
		{
			if (rod != null && FishingRodBagProxyState.Proxies.TryGetValue(rod, out FishingRodBagProxyContainer value))
			{
				FishingRodBagProxyState.Proxies.Remove(rod);
				value.CloseAndDestroy();
			}
		}

		private static void RefreshFishingRodBagProxy(ItemData rod)
		{
			if (rod != null && FishingRodBagProxyState.Proxies.TryGetValue(rod, out FishingRodBagProxyContainer value))
			{
				value.ReloadFromRod();
			}
		}

		private static void DestroyFishingRodBagProxies()
		{
			foreach (ItemData item in FishingRodBagProxyState.Proxies.Keys.ToList())
			{
				RemoveFishingRodBagProxy(item);
			}
		}

		internal static bool IsFishingRodBagInventory(Inventory inventory)
		{
			if (inventory != null)
			{
				return FishingRodBagStoreState.Inventories.Contains(inventory);
			}
			return false;
		}

		internal static bool CanAddToFishingRodBag(Inventory inventory, ItemData item)
		{
			if (IsFishingRodBagInventory(inventory))
			{
				return IsAllowedFishingRodBagItem(item);
			}
			return true;
		}

		internal static bool CanAddToFishingRodBag(Inventory inventory, GameObject itemPrefab)
		{
			if (IsFishingRodBagInventory(inventory))
			{
				return IsAllowedFishingRodBagPrefab(itemPrefab);
			}
			return true;
		}

		private static bool IsAllowedFishingRodBagItem(ItemData item)
		{
			if (item == null)
			{
				return false;
			}
			TryResolveMissingDropPrefab(item);
			if ((Object)(object)item.m_dropPrefab != (Object)null)
			{
				return IsAllowedFishingRodBagPrefab(item.m_dropPrefab);
			}
			return false;
		}

		private static bool IsAllowedFishingRodBagPrefab(GameObject itemPrefab)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Invalid comparison between Unknown and I4
			if ((Object)(object)itemPrefab == (Object)null)
			{
				return false;
			}
			ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
			if (component != null && (int)(component.m_itemData?.m_shared?.m_itemType).GetValueOrDefault() == 21)
			{
				return true;
			}
			if (IsCompatibleFishingItemDropPrefab(itemPrefab, component))
			{
				return true;
			}
			if (IsFishChumPrefab(itemPrefab))
			{
				return true;
			}
			EnsureAllowedFishingItemCache();
			return FishingRodBagRulesState.AllowedPrefabNames.Contains(((Object)itemPrefab).name);
		}

		private static bool IsFishChumItem(ItemData item)
		{
			if (item == null)
			{
				return false;
			}
			TryResolveMissingDropPrefab(item);
			if ((Object)(object)item.m_dropPrefab != (Object)null && IsFishChumPrefab(item.m_dropPrefab))
			{
				return true;
			}
			return false;
		}

		private static bool IsFishChumPrefab(GameObject itemPrefab)
		{
			if ((Object)(object)itemPrefab == (Object)null)
			{
				return false;
			}
			return FishingRodBagRulesState.FishChumPrefabNames.Contains(StripCloneSuffix(((Object)itemPrefab).name));
		}

		private static bool IsCompatibleFishingItemDropPrefab(GameObject itemPrefab, ItemDrop? itemDrop)
		{
			if ((Object)(object)itemPrefab == (Object)null || (Object)(object)itemDrop == (Object)null)
			{
				return false;
			}
			string text = StripCloneSuffix(((Object)itemPrefab).name);
			if (!((Object)(object)itemPrefab.GetComponent<Fish>() != (Object)null))
			{
				return text.IndexOf("Starfish", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			return true;
		}

		private static string StripCloneSuffix(string name)
		{
			if (name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return name.Substring(0, name.Length - "(Clone)".Length);
			}
			return name;
		}

		private static bool TryResolveMissingDropPrefab(ItemData item)
		{
			if (item == null || (Object)(object)item.m_dropPrefab != (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return (Object)(object)item?.m_dropPrefab != (Object)null;
			}
			GameObject dropPrefab = default(GameObject);
			if (ObjectDB.instance.TryGetItemPrefab(item.m_shared, ref dropPrefab))
			{
				item.m_dropPrefab = dropPrefab;
				return true;
			}
			return false;
		}

		private static void EnsureAllowedFishingItemCache()
		{
			ZNetScene instance = ZNetScene.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance == (Object)(object)FishingRodBagRulesState.CachedScene)
			{
				return;
			}
			FishingRodBagRulesState.CachedScene = instance;
			FishingRodBagRulesState.AllowedPrefabNames.Clear();
			AddAllowedFishingItemsFromPrefabs(instance.m_prefabs);
			AddAllowedFishingItemsFromPrefabs(instance.m_nonNetViewPrefabs);
			if (!((Object)(object)ObjectDB.instance != (Object)null))
			{
				return;
			}
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				if ((Object)(object)item != (Object)null && ((Object)item).name.IndexOf("FishingBait", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					FishingRodBagRulesState.AllowedPrefabNames.Add(((Object)item).name);
				}
			}
		}

		private static void AddAllowedFishingItemsFromPrefabs(IEnumerable<GameObject> prefabs)
		{
			foreach (GameObject prefab in prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Fish component = prefab.GetComponent<Fish>();
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				if ((Object)(object)component.m_pickupItem != (Object)null)
				{
					FishingRodBagRulesState.AllowedPrefabNames.Add(((Object)component.m_pickupItem).name);
				}
				foreach (BaitSetting bait in component.m_baits)
				{
					if ((Object)(object)bait?.m_bait != (Object)null)
					{
						FishingRodBagRulesState.AllowedPrefabNames.Add(((Object)bait.m_bait).name);
					}
				}
			}
		}

		internal static bool TryCatchFishToFishingRodBag(Fish fish, Character owner, out string message)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: 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)
			message = "";
			if (!TrollingFishingPlugin.FishingRodBag.Value.IsOff() && !((Object)(object)fish == (Object)null))
			{
				Player val = (Player)(object)((owner is Player) ? owner : null);
				if (val != null)
				{
					ItemData currentWeapon = ((Humanoid)val).GetCurrentWeapon();
					if (!IsFishingRod(currentWeapon) || !TryCreateFishPickupItem(fish, out ItemData fishItem))
					{
						return false;
					}
					int width;
					int height;
					Inventory inventory = LoadFishingRodBagInventory(val, currentWeapon, out width, out height);
					if (!TryAddItemToFishingRodBagInventory(inventory, fishItem))
					{
						return false;
					}
					Vector3 position = ((Component)fish).transform.position;
					message = "$msg_fishing_catched " + fish.GetHoverName();
					if (!fish.m_extraDrops.IsEmpty())
					{
						foreach (ItemData dropListItem in fish.m_extraDrops.GetDropListItems())
						{
							message = message + " & " + dropListItem.m_shared.m_name;
							ItemData val2 = dropListItem.Clone();
							val2.m_stack = dropListItem.m_stack;
							if (!TryAddItemToFishingRodBagInventory(inventory, val2))
							{
								Inventory inventory2 = ((Humanoid)val).GetInventory();
								if (inventory2 != null && inventory2.CanAddItem(dropListItem.m_dropPrefab, dropListItem.m_stack))
								{
									inventory2.AddItem(dropListItem.m_dropPrefab, dropListItem.m_stack);
									continue;
								}
								Object.Instantiate<GameObject>(dropListItem.m_dropPrefab, position, Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f)).GetComponent<ItemDrop>().SetStack(dropListItem.m_stack);
								((Character)val).Message((MessageType)1, Localization.instance.Localize("$inventory_full"), 0, (Sprite)null);
							}
						}
					}
					SaveFishingRodBagInventory(currentWeapon, inventory);
					DestroyCaughtFish(fish);
					return true;
				}
			}
			return false;
		}

		internal static void RegisterFishingRodBagInventory(Inventory inventory, ItemData rod)
		{
			if (inventory != null)
			{
				FishingRodBagStoreState.Inventories.Add(inventory);
				if (rod != null)
				{
					FishingRodBagStoreState.InventoryOwners[inventory] = rod;
				}
			}
		}

		internal static void UnregisterFishingRodBagInventory(Inventory inventory)
		{
			if (inventory != null)
			{
				FishingRodBagStoreState.Inventories.Remove(inventory);
				FishingRodBagStoreState.InventoryOwners.Remove(inventory);
				FishingRodBagStoreState.InventoryStates.Remove(inventory);
			}
		}

		internal static bool IsFishingRodBagContainer(Container container)
		{
			if ((Object)(object)container != (Object)null)
			{
				if (!((Object)(object)((Component)container).GetComponent<FishingRodBagContainer>() != (Object)null))
				{
					return (Object)(object)((Component)container).GetComponent<FishingRodBagProxyContainer>() != (Object)null;
				}
				return true;
			}
			return false;
		}

		internal static bool TrySaveFishingRodBagContainer(Container container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return false;
			}
			FishingRodBagContainer component = ((Component)container).GetComponent<FishingRodBagContainer>();
			if ((Object)(object)component != (Object)null)
			{
				component.Save();
				return true;
			}
			FishingRodBagProxyContainer component2 = ((Component)container).GetComponent<FishingRodBagProxyContainer>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.Save();
				return true;
			}
			return false;
		}

		internal static float GetFishingRodBagExtraWeight(Inventory inventory)
		{
			if (TrollingFishingPlugin.FishingRodBag.Value.IsOff() || TrollingFishingPlugin.FishingRodBagCountsWeight.Value.IsOff() || inventory == null || (Object)(object)Player.m_localPlayer == (Object)null || inventory != ((Humanoid)Player.m_localPlayer).GetInventory())
			{
				return 0f;
			}
			float num = 0f;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (IsFishingRod(allItem))
				{
					num += GetFishingRodBagWeight(allItem);
				}
			}
			return num * ResolveFishingRodBagWeightMultiplier(Player.m_localPlayer);
		}

		internal static bool TryGetFishingRodBagContainerDisplayWeight(Container container, Player player, out float weight)
		{
			weight = 0f;
			if (!((Object)(object)container == (Object)null) && !((Object)(object)player == (Object)null) && IsFishingRodBagContainer(container))
			{
				Inventory inventory = container.GetInventory();
				if (inventory != null && FishingRodBagStoreState.InventoryOwners.TryGetValue(inventory, out ItemData value))
				{
					weight = GetFishingRodBagWeight(value) * ResolveFishingRodBagWeightMultiplier(player);
					return true;
				}
			}
			return false;
		}

		private static float ResolveFishingRodBagWeightMultiplier(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 1f;
			}
			float num = (float)Mathf.Clamp(TrollingFishingPlugin.FishingRodBagWeightAtMaxSkillPercent.Value, 0, 100) / 100f;
			float num2 = Mathf.Clamp01(((Character)player).GetSkillFactor((SkillType)104));
			return Mathf.Lerp(1f, num, num2);
		}

		private static int ResolveTargetSlotCount(Player player, ItemData rod)
		{
			int result = NormalizeFishingRodBagSlotCount(ResolveConfiguredSlotCount(player));
			rod.m_customData["TrollingFishing.FishingRodBag.Slots"] = result.ToString(CultureInfo.InvariantCulture);
			return result;
		}

		private static int ResolveConfiguredSlotCount(Player player)
		{
			if (!TrollingFishingPlugin.FishingRodBagScalesWithFishingSkill.Value.IsOn())
			{
				return 32;
			}
			return ResolveSkillSlotCount(((Character)player).GetSkillLevel((SkillType)104));
		}

		private static int ResolveSkillSlotCount(float fishingLevel)
		{
			return (Mathf.Clamp(Mathf.FloorToInt(Mathf.Max(0f, fishingLevel) / 10f), 0, 9) + 1) * 8;
		}

		private static Inventory CreateFishingRodBagInventory(ItemData rod, int width, int height)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			return new Inventory("$item_fishingrod", rod.GetIcon(), width, height);
		}

		private static void ResolveGridSize(int slots, out int width, out int height)
		{
			int num = NormalizeFishingRodBagSlotCount(slots);
			if (num <= 16)
			{
				if (num <= 8)
				{
					width = 4;
					height = 2;
				}
				else
				{
					width = 4;
					height = 4;
				}
			}
			else if (num <= 24)
			{
				width = 6;
				height = 4;
			}
			else
			{
				width = 8;
				height = num / 8;
			}
		}

		private static int NormalizeFishingRodBagSlotCount(int slots)
		{
			return Mathf.CeilToInt((float)Mathf.Clamp(slots, 8, 80) / 8f) * 8;
		}

		private static bool TryAddItemToFishingRodBag(Player player, ItemData rod, ItemData item)
		{
			int width;
			int height;
			Inventory inventory = LoadFishingRodBagInventory(player, rod, out width, out height);
			if (!TryAddItemToFishingRodBagInventory(inventory, item))
			{
				return false;
			}
			SaveFishingRodBagInventory(rod, inventory);
			return true;
		}

		private static bool TryAddItemToFishingRodBagInventory(Inventory inventory, ItemData item)
		{
			if (inventory == null || !IsAllowedFishingRodBagItem(item))
			{
				return false;
			}
			ItemData val = item.Clone();
			if (inventory.CanAddItem(val, -1))
			{
				return inventory.AddItem(val);
			}
			return false;
		}

		private static Inventory LoadFishingRodBagInventory(Player player, ItemData rod, out int width, out int height)
		{
			int num = ResolveTargetSlotCount(player, rod);
			ResolveGridSize(num, out width, out height);
			Inventory val = CreateFishingRodBagInventory(rod, width, height);
			if (rod.m_customData.TryGetValue("TrollingFishing.FishingRodBag.Data", out var value) && !string.IsNullOrWhiteSpace(value))
			{
				LoadFishingRodBagVisibleInventory(rod, val, value, num);
			}
			else
			{
				SetFishingRodBagInventoryState(val, new FishingRodBagInventoryState(new List<ItemData>(), num));
			}
			return val;
		}

		private static void SaveFishingRodBagInventory(ItemData rod, Inventory inventory, bool refreshProxy = true)
		{
			List<ItemData> allItems = CollectFishingRodBagStoredItems(inventory);
			SaveFishingRodBagStoredItems(rod, allItems);
			InvalidateFishingRodBagWeight(rod);
			if (refreshProxy)
			{
				RefreshFishingRodBagProxy(rod);
			}
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				Inventory inventory2 = ((Humanoid)localPlayer).GetInventory();
				if (inventory2 != null)
				{
					inventory2.Changed();
				}
			}
		}

		private static float GetFishingRodBagWeight(ItemData rod)
		{
			if (rod == null)
			{
				return 0f;
			}
			if (!rod.m_customData.TryGetValue("TrollingFishing.FishingRodBag.Data", out var value) || string.IsNullOrWhiteSpace(value))
			{
				FishingRodBagStoreState.WeightCache.Remove(rod);
				return 0f;
			}
			if (FishingRodBagStoreState.WeightCache.TryGetValue(rod, out BagWeightCacheEntry value2) && string.Equals(value2.RawData, value, StringComparison.Ordinal))
			{
				return value2.Weight;
			}
			if (!TryLoadFishingRodBagInventoryForReadOnly(rod, value, out Inventory inventory))
			{
				FishingRodBagStoreState.WeightCache.Remove(rod);
				return 0f;
			}
			float num = inventory.GetAllItems().Sum((ItemData item) => item.GetWeight(-1));
			FishingRodBagStoreState.WeightCache[rod] = new BagWeightCacheEntry(value, num);
			return num;
		}

		private static bool TryLoadFishingRodBagInventoryForReadOnly(ItemData rod, string rawData, out Inventory inventory)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected O, but got Unknown
			ResolveGridSize(80, out var width, out var height);
			inventory = CreateFishingRodBagInventory(rod, width, height);
			try
			{
				inventory.Load(new ZPackage(rawData));
				return true;
			}
			catch (Exception ex)
			{
				TrollingFishingPlugin.ModLogger.LogWarning((object)("Could not read FishingRod bag weight: " + ex.GetBaseException().Message));
				return false;
			}
		}

		private static void InvalidateFishingRodBagWeight(ItemData rod)
		{
			if (rod != null)
			{
				FishingRodBagStoreState.WeightCache.Remove(rod);
			}
		}

		private static void LoadFishingRodBagVisibleInventory(ItemData rod, Inventory visibleInventory, string rawData, int visibleSlots)
		{
			if (!TryLoadFishingRodBagInventoryForReadOnly(rod, rawData, out Inventory inventory))
			{
				SetFishingRodBagInventoryState(visibleInventory, new FishingRodBagInventoryState(new List<ItemData>(), visibleSlots));
				return;
			}
			List<ItemData> overflowItems = MaterializeFishingRodBagVisibleItems(inventory.GetAllItems(), visibleInventory, visibleSlots);
			SetFishingRodBagInventoryState(visibleInventory, new FishingRodBagInventoryState(overflowItems, visibleSlots));
			visibleInventory.Changed();
		}

		private static List<ItemData> MaterializeFishingRodBagVisibleItems(IEnumerable<ItemData> sourceItems, Inventory visibleInventory, int visibleSlots)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			List<ItemData> list = new List<ItemData>();
			HashSet<Vector2i> hashSet = new HashSet<Vector2i>();
			List<ItemData> list2 = new List<ItemData>();
			foreach (ItemData item in from item in sourceItems
				orderby item.m_gridPos.y, item.m_gridPos.x
				select item)
			{
				ItemData val = CloneFishingRodBagItem(item);
				if (IsGridPositionInInventory(val.m_gridPos, visibleInventory) && hashSet.Add(val.m_gridPos) && visibleInventory.m_inventory.Count < visibleSlots)
				{
					visibleInventory.m_inventory.Add(val);
				}
				else
				{
					list2.Add(val);
				}
			}
			foreach (ItemData item2 in list2)
			{
				if (visibleInventory.m_inventory.Count < visibleSlots && TryFindNextFreeFishingRodBagSlot(visibleInventory, hashSet, out var slot))
				{
					item2.m_gridPos = slot;
					hashSet.Add(slot);
					visibleInventory.m_inventory.Add(item2);
				}
				else
				{
					list.Add(item2);
				}
			}
			return list;
		}

		private static List<ItemData> CollectFishingRodBagStoredItems(Inventory inventory)
		{
			List<ItemData> list = (from item in inventory.GetAllItems()
				orderby item.m_gridPos.y, item.m_gridPos.x
				select item).Select(CloneFishingRodBagItem).ToList();
			if (FishingRodBagStoreState.InventoryStates.TryGetValue(inventory, out FishingRodBagInventoryState value))
			{
				list.AddRange(value.OverflowItems.Select(CloneFishingRodBagItem));
			}
			return list;
		}

		private static void SaveFishingRodBagStoredItems(ItemData rod, List<ItemData> allItems)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_009c: Expected O, but got Unknown
			//IL_005f: 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_0067: Unknown result type (might be due to invalid IL or missing references)
			ResolveGridSize(80, out var width, out var height);
			Inventory val = CreateFishingRodBagInventory(rod, width, height);
			HashSet<Vector2i> hashSet = new HashSet<Vector2i>();
			foreach (ItemData allItem in allItems)
			{
				ItemData val2 = CloneFishingRodBagItem(allItem);
				if (!IsGridPositionInInventory(val2.m_gridPos, val) || !hashSet.Add(val2.m_gridPos))
				{
					if (!TryFindNextFreeFishingRodBagSlot(val, hashSet, out var slot))
					{
						break;
					}
					val2.m_gridPos = slot;
					hashSet.Add(slot);
				}
				val.m_inventory.Add(val2);
			}
			ZPackage val3 = new ZPackage();
			val.Save(val3);
			rod.m_customData["TrollingFishing.FishingRodBag.Data"] = val3.GetBase64();
		}

		private static void SetFishingRodBagInventoryState(Inventory inventory, FishingRodBagInventoryState state)
		{
			FishingRodBagStoreState.InventoryStates.Remove(inventory);
			FishingRodBagStoreState.InventoryStates.Add(inventory, state);
		}

		private static ItemData CloneFishingRodBagItem(ItemData item)
		{
			ItemData obj = item.Clone();
			TryResolveMissingDropPrefab(obj);
			return obj;
		}

		private static bool IsGridPositionInInventory(Vector2i position, Inventory inventory)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: 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)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if (position.x >= 0 && position.y >= 0 && position.x < inventory.GetWidth())
			{
				return position.y < inventory.GetHeight();
			}
			return false;
		}

		private static bool TryFindNextFreeFishingRodBagSlot(Inventory inventory, HashSet<Vector2i> occupied, out Vector2i slot)
		{
			//IL_0040: 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_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			Vector2i val = default(Vector2i);
			for (int i = 0; i < inventory.GetHeight(); i++)
			{
				for (int j = 0; j < inventory.GetWidth(); j++)
				{
					((Vector2i)(ref val))..ctor(j, i);
					if (!occupied.Contains(val))
					{
						slot = val;
						return true;
					}
				}
			}
			slot = new Vector2i(-1, -1);
			return false;
		}

		private static bool TryCreateFishPickupItem(Fish fish, out ItemData fishItem)
		{
			ItemDrop component = ((Component)fish).GetComponent<ItemDrop>();
			if ((Object)(object)component != (Object)null)
			{
				fishItem = component.m_itemData.Clone();
				if ((Object)(object)fishItem.m_dropPrefab == (Object)null)
				{
					fishItem.m_dropPrefab = ((Component)component).gameObject;
				}
				return true;
			}
			fishItem = null;
			if ((Object)(object)fish.m_pickupItem == (Object)null)
			{
				return false;
			}
			ItemDrop component2 = fish.m_pickupItem.GetComponent<ItemDrop>();
			if ((Object)(object)component2 == (Object)null)
			{
				return false;
			}
			fishItem = component2.m_itemData.Clone();
			fishItem.m_dropPrefab = fish.m_pickupItem;
			fishItem.m_stack = Mathf.Clamp(fish.m_pickupItemStackSize, 1, fishItem.m_shared.m_maxStackSize);
			fishItem.m_worldLevel = (byte)Game.m_worldLevel;
			return true;
		}

		private static void DestroyCaughtFish(Fish fish)
		{
			ZNetView component = ((Component)fish).GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				component.Destroy();
			}
			else
			{
				Object.Destroy((Object)(object)((Component)fish).gameObject);
			}
		}

		internal static void UpdateFishingRodBagSelectedBaitVisual(InventoryGrid grid, Inventory inventory)
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)grid == (Object)null || inventory == null || !FishingRodBagStoreState.InventoryOwners.TryGetValue(inventory, out ItemData value) || !(InventoryGridElementsField?.GetValue(grid) is IEnumerable enumerable))
			{
				return;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (value.m_customData.TryGetValue("TrollingFishing.FishingRodBag.SelectedBait", out var value2) && !string.IsNullOrWhiteSpace(value2))
			{
				hashSet.Add(value2);
			}
			foreach (object item in enumerable)
			{
				Type type = item.GetType();
				FieldInfo fieldInfo = AccessTools.Field(type, "m_pos");
				FieldInfo fieldInfo2 = AccessTools.Field(type, "m_equiped");
				if (!(fieldInfo?.GetValue(item) is Vector2i val))
				{
					continue;
				}
				object? obj = fieldInfo2?.GetValue(item);
				Image val2 = (Image)((obj is Image) ? obj : null);
				if (val2 != null)
				{
					((Behaviour)val2).enabled = false;
					ItemData itemAt = inventory.GetItemAt(val.x, val.y);
					if (itemAt != null)
					{
						TryResolveMissingDropPrefab(itemAt);
					}
					object obj2;
					if (itemAt == null)
					{
						obj2 = null;
					}
					else
					{
						GameObject dropPrefab = itemAt.m_dropPrefab;
						obj2 = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
					}
					string text = (string)obj2;
					if (!string.IsNullOrWhiteSpace(text))
					{
						((Behaviour)val2).enabled = hashSet.Contains(text);
					}
				}
			}
		}

		private static void MarkFishingRodBagInventoryVisualDirty(Inventory inventory)
		{
			if (inventory != null && !((Object)(object)InventoryGui.instance == (Object)null) && !((Object)(object)InventoryGui.instance.m_containerGrid == (Object)null))
			{
				InventoryGui.instance.m_containerGrid.UpdateInventory(inventory, Player.m_localPlayer, (ItemData)null);
			}
		}

		internal static void DestroyExistingFishingFloats(Character owner, FishingFloat? except = null)
		{
			if ((Object)(object)owner == (Object)null)
			{
				return;
			}
			MultiLineFishingCastState.FloatBuffer.Clear();
			foreach (FishingFloat allInstance in FishingFloat.GetAllInstances())
			{
				if ((Object)(object)allInstance != (Object)null && (Object)(object)allInstance != (Object)(object)except && (Object)(object)allInstance.GetOwner() == (Object)(object)owner)
				{
					MultiLineFishingCastState.FloatBuffer.Add(allInstance);
				}
			}
			foreach (FishingFloat item in MultiLineFishingCastState.FloatBuffer)
			{
				if (!((Object)(object)item == (Object)null))
				{
					ReturnFishingFloatBaitBeforeDestroy(item);
					GameObject gameObject = ((Component)item).gameObject;
					if ((Object)(object)ZNetScene.instance != (Object)null)
					{
						ZNetScene.instance.Destroy(gameObject);
					}
					else
					{
						Object.Destroy((Object)(object)gameObject);
					}
				}
			}
			MultiLineFishingCastState.FloatBuffer.Clear();
		}

		internal static void TryOpenFishingRodBagFromUseInput(Player player)
		{
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && !TrollingFishingPlugin.FishingRodBag.Value.IsOff() && !((Object)(object)InventoryGui.instance == (Object)null))
			{
				TryCloseFishingRodBagFromInput();
			}
		}

		internal static bool TryHandleInventoryGuiUseInput(InventoryGui inventoryGui)
		{
			//IL_0050: 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_005a: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)inventoryGui == (Object)null || TrollingFishingPlugin.FishingRodBag.Value.IsOff())
			{
				return false;
			}
			if (TryCloseFishingRodBagFromInput())
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)inventoryGui.m_currentContainer != (Object)null || !ZInput.GetButtonDown("Use"))
			{
				return false;
			}
			Vector2 val = Vector2.op_Implicit(Input.mousePosition);
			ItemData item = inventoryGui.m_playerGrid.GetItem(new Vector2i(Mathf.RoundToInt(val.x), Mathf.RoundToInt(val.y)));
			if (!IsFishingRod(item))
			{
				return false;
			}
			OpenFishingRodBag(localPlayer, item);
			return true;
		}

		internal static void UpdateInventoryWeightDisplay(InventoryGui inventoryGui, Player player)
		{
			if ((Object)(object)inventoryGui == (Object)null || (Object)(object)player == (Object)null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory != null)
			{
				int num = Mathf.CeilToInt(inventory.m_totalWeight + GetFishingRodBagExtraWeight(inventory));
				int num2 = Mathf.CeilToInt(player.GetMaxCarryWeight());
				if (num > num2 && Mathf.Sin(Time.time * 10f) > 0f)
				{
					inventoryGui.m_weight.text = $"<color=red>{num}</color>/{num2}";
				}
				else
				{
					inventoryGui.m_weight.text = $"{num}/{num2}";
				}
			}
		}

		internal static void UpdateFishingRodBagContainerWeightDisplay(InventoryGui inventoryGui, Player player)
		{
			if (!((Object)(object)inventoryGui == (Object)null) && !((Object)(object)player == (Object)null) && !((Object)(object)inventoryGui.m_containerWeight == (Object)null) && TryGetFishingRodBagContainerDisplayWeight(inventoryGui.m_currentContainer, player, out var weight))
			{
				inventoryGui.m_containerWeight.text = Mathf.CeilToInt(weight).ToString(CultureInfo.InvariantCulture);
			}
		}

		internal static void AppendFishingRodTooltipHints(ItemData item, ref string tooltip)
		{
			if (item != null && IsFishingRod(item))
			{
				List<string> list = new List<string>();
				if (TrollingFishingPlugin.FishingRodBag != null && TrollingFishingPlugin.FishingRodBag.Value.IsOn())
				{
					list.Add(FishingLocalization.Localize("$tf_fishingrod_bag_open_hint"));
				}
				if (TrollingFishingPlugin.FishingRodMultiLine != null && TrollingFishingPlugin.FishingRodMultiLine.Value.IsOn())
				{
					list.Add(FishingLocalization.Localize("$tf_fishingrod_multi_line_hint"));
				}
				if (list.Count > 0)
				{
					tooltip = tooltip + "\n\n<color=orange>" + string.Join("\n", list) + "</color>";
				}
			}
		}

		private static void OpenFishingRodBag(Player player, ItemData rod)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			FishingRodBagUiState.OpenRods.Add(rod);
			RemoveFishingRodBagProxy(rod);
			int slots = ResolveTargetSlotCount(player, rod);
			ResolveGridSize(slots, out var width, out var height);
			GameObject val = new GameObject("TrollingFishing_FishingRodBag");
			val.transform.position = ((Component)player).transform.position;
			FishingRodBagContainer fishingRodBagContainer = val.AddComponent<FishingRodBagContainer>();
			Container val2 = val.AddComponent<Container>();
			fishingRodBagContainer.Initialize(player, rod, val2, slots, width, height);
			InventoryGui.instance.Show(val2, 1);
			ZInput.ResetButtonStatus("Use");
		}

		private static bool TryCloseFishingRodBagFromInput()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || !IsFishingRodBagContainer(instance.m_currentContainer))
			{
				return false;
			}
			if (ZInput.GetButtonDown("Use"))
			{
				ZInput.ResetButtonStatus("Use");
				CloseFishingRodBagContainerOnly(instance);
				return true;
			}
			if (!IsFishingRodBagFullCloseInput())
			{
				return false;
			}
			ResetFishingRodBagFullCloseInput();
			instance.Hide();
			return true;
		}

		private static void CloseFishingRodBagContainerOnly(InventoryGui inventoryGui)
		{
			if (InventoryGuiCloseContainerMethod == null)
			{
				TrollingFishingPlugin.ModLogger.LogWarning((object)"Could not close the fishing bag without closing the inventory because InventoryGui.CloseContainer was not found.");
			}
			else
			{
				InventoryGuiCloseContainerMethod.Invoke(inventoryGui, Array.Empty<object>());
			}
		}

		private static bool IsFishingRodBagFullCloseInput()
		{
			if (!ZInput.GetButtonDown("Inventory") && !ZInput.GetButtonDown("JoyButtonB") && !ZInput.GetButtonDown("JoyButtonY"))
			{
				return ZInput.GetKeyDown((KeyCode)27, true);
			}
			return true;
		}

		private static void ResetFishingRodBagFullCloseInput()
		{
			ZInput.ResetButtonStatus("Inventory");
			ZInput.ResetButtonStatus("JoyButtonB");
			ZInput.ResetButtonStatus("JoyButtonY");
		}

		internal static FishingFloat? FindFloatWithSkillChance(Fish fish)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)fish == (Object)null)
			{
				return null;
			}
			Vector3 position = ((Component)fish).transform.position;
			float num = Mathf.Clamp01(fish.m_baseHookChance);
			float targetChance = Mathf.Max(num, Mathf.Clamp(TrollingFishingPlugin.FishingOverrideBiteChanceBonusFactor.Value, 0.1f, 1f));
			foreach (FishingFloat allInstance in FishingFloat.GetAllInstances())
			{
				if ((Object)(object)allInstance == (Object)null || !allInstance.IsInWater())
				{
					continue;
				}
				float num2 = Mathf.Max(0f, allInstance.m_range);
				Vector3 val = position - ((Component)allInstance).transform.position;
				if (!(((Vector3)(ref val)).sqrMagnitude > num2 * num2) && !((Object)(object)allInstance.GetCatch() != (Object)null))
				{
					float num3 = ResolveBiteChance(num, targetChance, GetFishingFloatOwnerSkillFactor(allInstance));
					if (Random.value < num3)
					{
						return allInstance;
					}
				}
			}
			return null;
		}

		internal static ExtraDropChanceState ApplyExtraDropChance(Fish fish, Character character)
		{
			if (!((Object)(object)fish == (Object)null))
			{
				Player val = (Player)(object)((character is Player) ? character : null);
				if (val != null && fish.m_extraDrops != null && !fish.m_extraDrops.IsEmpty())
				{
					DropTable extraDrops = fish.m_extraDrops;
					float dropChance = extraDrops.m_dropChance;
					extraDrops.m_dropChance = ResolveExtraDropChance(dropChance, val);
					return new ExtraDropChanceState(extraDrops, dropChance);
				}
			}
			return default(ExtraDropChanceState);
		}

		internal static void RestoreExtraDropChance(ExtraDropChanceState state)
		{
			if (state.DropTable != null)
			{
				state.DropTable.m_dropChance = state.OriginalDropChance;
			}
		}

		private static float ResolveBiteChance(float baseChance, float targetChance, float skillFactor)
		{
			if (skillFactor <= 0f)
			{
				return baseChance;
			}
			return Mathf.Clamp01(Mathf.Lerp(baseChance, targetChance, Mathf.Clamp01(skillFactor)));
		}

		private static float GetFishingFloatOwnerSkillFactor(FishingFloat fishingFloat)
		{
			int frameCount = Time.frameCount;
			if (_fishingFloatOwnerSkillFactorCacheFrame != frameCount)
			{
				_fishingFloatOwnerSkillFactorCacheFrame = frameCount;
				FishingFloatOwnerSkillFactorCache.Clear();
			}
			if (FishingFloatOwnerSkillFactorCache.TryGetValue(fishingFloat, out var value))
			{
				return value;
			}
			Character owner = fishingFloat.GetOwner();
			Player val = (Player)(object)((owner is Player) ? owner : null);
			value = ((val != null) ? Mathf.Clamp01(((Character)val).GetSkillFactor((SkillType)104)) : 0f);
			FishingFloatOwnerSkillFactorCache[fishingFloat] = value;
			return value;
		}

		private static float ResolveExtraDropChance(float baseChance, Player? player)
		{
			float num = Mathf.Clamp01(baseChance);
			if ((Object)(object)player == (Object)null)
			{
				return num;
			}
			float num2 = Mathf.Clamp01(((Character)player).GetSkillFactor((SkillType)104));
			float num3 = Mathf.Clamp(TrollingFishingPlugin.FishingOverrideExtraDropChanceBonusFactor.Value, 0f, 2f);
			return Mathf.Clamp01(num * (1f + num3 * num2));
		}

		internal static bool IsFishingRod(ItemData? item)
		{
			if ((Object)(object)item?.m_dropPrefab != (Object)null)
			{
				return string.Equals(((Object)item.m_dropPrefab).name, "FishingRod", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static bool TryCreateItemFromPrefabName(string prefabName, out ItemData item)
		{
			item = null;
			if (string.IsNullOrWhiteSpace(prefabName) || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(prefabName);
			ItemDrop val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			item = val.m_itemData.Clone();
			item.m_stack = 1;
			item.m_dropPrefab = prefab;
			return true;
		}

		internal static IDisposable BeginMultiLineFishingSetup()
		{
			MultiLineFishingCastState.SetupDepth++;
			MultiLineFishingCastState.SetupContexts.Add(MultiLineFishingSetupContext.Empty);
			return new MultiLineFishingSetupScope();
		}

		private static IDisposable BeginMultiLineFishingSetup(MultiLineFishingFloatMarker sourceMarker)
		{
			return BeginMultiLineFishingSetup(sourceMarker, consumeTrackedBaitOnSetup: false);
		}

		private static IDisposable BeginMultiLineFishingSetup(MultiLineFishingFloatMarker sourceMarker, bool consumeTrackedBaitOnSetup)
		{
			MultiLineFishingCastState.SetupDepth++;
			MultiLineFishingCastState.SetupContexts.Add(new MultiLineFishingSetupContext(sourceMarker.Owner, sourceMarker.Rod, sourceMarker.LineIndex, sourceMarker.PrimaryEquivalentLineIndex, sourceMarker.ReservedBait, sourceMarker.BaitReturnSource, consumeTrackedBaitOnSetup));
			return new MultiLineFishingSetupScope();
		}

		internal static bool IsMultiLineFishingSetupActive()
		{
			return MultiLineFishingCastState.SetupDepth > 0;
		}

		internal static void MarkMultiLineFishingFloat(FishingFloat fishingFloat, Character owner)
		{
			if (!((Object)(object)fishingFloat == (Object)null) && !((Object)(object)owner == (Object)null))
			{
				MultiLineFishingSetupContext multiLineFishingSetupContext = CurrentMultiLineSetupContext();
				Character owner2 = multiLineFishingSetupContext.Owner ?? owner;
				MarkMultiLineFishingObject(((Component)fishingFloat).gameObject, owner2, multiLineFishingSetupContext.LineIndex, multiLineFishingSetupContext.PrimaryEquivalentLineIndex, multiLineFishingSetupContext.ReservedBait, multiLineFishingSetupContext.Rod, multiLineFishingSetupContext.BaitReturnSource);
			}
		}

		internal static void MarkMultiLineFishingObject(GameObject projectileObject, Character owner, int lineIndex = 0, int primaryEquivalentLineIndex = 0, MultiLineBaitReservation reservedBait = default(MultiLineBaitReservation), ItemData? rod = null, MultiLineBaitReservation baitReturnSource = default(MultiLineBaitReservation))
		{
			if (!((Object)(object)projectileObject == (Object)null) && !((Object)(object)owner == (Object)null))
			{
				MultiLineFishingFloatMarker multiLineFishingFloatMarker = projectileObject.GetComponent<MultiLineFishingFloatMarker>() ?? projectileObject.AddComponent<MultiLineFishingFloatMarker>();
				multiLineFishingFloatMarker.Initialize(owner, rod, Time.time + 8f, lineIndex, primaryEquivalentLineIndex, reservedBait, baitReturnSource);
				MultiLineBaitReservation baitReturnSource2 = (reservedBait.IsValid ? reservedBait : baitReturnSource);
				if (baitReturnSource2.IsValid)
				{
					MarkFishingBaitReturnSource(projectileObject, baitReturnSource2, CurrentMultiLineSetupContext().ConsumeTrackedBaitOnSetup);
				}
				TrollingFishingPlugin.LogDebug($"[Fishing multiLine] marked float object={((Object)projectileObject).name} line={multiLineFishingFloatMarker.LineIndex} primaryLine={multiLineFishingFloatMarker.PrimaryEquivalentLineIndex} extra={multiLineFishingFloatMarker.IsAdditionalLine} reservedBait={multiLineFishingFloatMarker.ReservedBait.PrefabName} hasFishingFloat={(Object)(object)projectileObject.GetComponent<FishingFloat>() != (Object)null} hasProjectile={(Object)(object)projectileObject.GetComponent<Projectile>() != (Object)null}.");
			}
		}

		private static MultiLineFishingSetupContext CurrentMultiLineSetupContext()
		{
			if (MultiLineFishingCastState.SetupContexts.Count <= 0)
			{
				return MultiLineFishingSetupContext.Empty;
			}
			return MultiLineFishingCastState.SetupContexts[MultiLineFishingCastState.SetupContexts.Count - 1];
		}

		internal static bool TrySuppressMultiLineFishingFloatInitialUpdate(FishingFloat fishingFloat)
		{
			if ((Object)(object)fishingFloat == (Object)null)
			{
				return false;
			}
			MultiLineFishingFloatMarker component = ((Component)fishingFloat).GetComponent<MultiLineFishingFloatMarker>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			if (component.ShouldSkipAttackCancellation())
			{
				return true;
			}
			return false;
		}

		internal static bool TrySuppressMultiLineFishingFindFloat(out FishingFloat result)
		{
			result = null;
			return IsMultiLineFishingSetupActive();
		}

		internal static IDisposable? BeginAdditionalMultiLineFishingFloatUpdate(FishingFloat fishingFloat)
		{
			if ((Object)(object)fishingFloat == (Object)null)
			{
				return null;
			}
			MultiLineFishingFloatMarker component = ((Component)fishingFloat).GetComponent<MultiLineFishingFloatMarker>();
			if ((Object)(object)component == (Object)null || !component.IsAdditionalLine)
			{
				return null;
			}
			Character val = component.Owner ?? fishingFloat.GetOwner();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			MultiLineFishingCastState.UpdateContexts.Add(new MultiLineFishingUpdateContext(val, Mathf.Max(0f, TrollingFishingPlugin.FishingRodMultiLineExtraPullStaminaFactor.Value), Mathf.Max(0f, TrollingFishingPlugin.FishingRodMultiLineSkillRaiseFactor.Value)));
			return new MultiLineFishingUpdateScope();
		}

		internal static void AdjustMultiLineFishingStaminaUse(Character character, ref float stamina)
		{
			if (!(stamina <= 0f) && TryGetCurrentMultiLineFishingUpdateContext(character, out var context))
			{
				stamina *= context.StaminaFactor;
			}
		}

		internal static void AdjustMultiLineFishingSkillRaise(Character character, SkillType skillType, ref float factor)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			if (!(factor <= 0f) && (int)skillType == 104 && TryGetCurrentMultiLineFishingUpdateContext(character, out var context))
			{
				factor *= context.SkillRaiseFactor;
			}
		}

		private static bool TryGetCurrentMultiLineFishingUpdateContext(Character character, out MultiLineFishingUpdateContext context)
		{
			if ((Object)(object)character != (Object)null)
			{
				for (int num = MultiLineFishingCastState.UpdateContexts.Count - 1; num >= 0; num--)
				{
					MultiLineFishingUpdateContext multiLineFishingUpdateContext = MultiLineFishingCastState.UpdateContexts[num];
					if (multiLineFishingUpdateContext.Owner == character)
					{
						context = multiLineFishingUpdateContext;
						return true;
					}
				}
			}
			context = default(MultiLineFishingUpdateContext);
			return false;
		}

		internal static bool TrySettleMultiLineFishingProjectileOnWater(Projectile projectile, Vector3 hitPoint, bool water)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: 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_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			if (!water || (Object)(object)projectile == (Object)null)
			{
				return false;
			}
			MultiLineFishingFloatMarker component = ((Component)projectile).GetComponent<MultiLineFishingFloatMarker>();
			FishingFloat component2 = ((Component)projectile).GetComponent<FishingFloat>();
			if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null)
			{
				if ((Object)(object)component2 != (Object)null)
				{
					TrollingFishingPlugin.LogDebug($"[Fishing multiLine] water hit not intercepted object={((Object)((Component)projectile).gameObject).name} hasMarker={(Object)(object)component != (Object)null}.");
				}
				return false;
			}
			TrollingFishingPlugin.LogDebug($"[Fishing multiLine] water hit intercepted object={((Object)((Component)projectile).gameObject).name} point={hitPoint}.");
			((Component)projectile).transform.position = hitPoint;
			ProjectileAccess.SetVelocity(projectile, Vector3.zero);
			ProjectileAccess.SetDidHit(projectile, didHit: true);
			projectile.m_ttl = 0f;
			projectile.m_hitWaterEffects.Create(hitPoint, Quaternion.identity, (Transform)null, 1f, -1);
			Rigidbody component3 = ((Component)projectile).GetComponent<Rigidbody>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.linearVelocity = Vector3.zero;
				component3.angularVelocity = Vector3.zero;
			}
			((Behaviour)projectile).enabled = false;
			return true;
		}

		internal static bool TryBeginFishingProjectileHit(Projectile projectile, Collider collider, bool water, out IDisposable? scope)
		{
			scope = null;
			if ((Object)(object)projectile == (Object)null || (Object)(object)((Component)projectile).GetComponent<FishingFloat>() != (Object)null)
			{
				return false;
			}
			MultiLineFishingFloatMarker component = ((Component)projectile).GetComponent<MultiLineFishingFloatMarker>();
			if ((Object)(object)component != (Object)null)
			{
				bool flag = component.IsAdditionalLine && !water && ProjectileAccess.WillDestroyAfterHit(projectile, collider);
				scope = BeginMultiLineFishingSetup(component, flag);
				TrollingFishingPlugin.LogDebug($"[Fishing multiLine] hit spawn scope opened projectile={((Object)((Component)projectile).gameObject).name} water={water} consumeTrackedBaitOnSetup={flag}.");
				return true;
			}
			FishingBaitReturnTracker component2 = ((Component)projectile).GetComponent<FishingBaitReturnTracker>();
			if ((Object)(object)component2 == (Object)null || !component2.Source.IsValid || component2.IsSettled)
			{
				return false;
			}
			scope = BeginBaitReturnSourceSetup(component2.Source);
			TrollingFishingPlugin.LogDebug($"[Fishing bait] hit spawn source scope opened projectile={((Object)((Component)projectile).gameObject).name} prefab={component2.Source.PrefabName} fromRodBag={component2.Source.FromRodBag}.");
			return true;
		}

		internal static void ReturnFishingFloatBaitBeforeDestroy(FishingFloat fishingFloat)
		{
			if (!((Object)(object)fishingFloat == (Object)null) && !IsFishingFloatBaitConsumed(fishingFloat) && !TryReturnBaitToOriginalSource(fishingFloat))
			{
				fishingFloat.ReturnBait();
			}
		}

		internal static void LogMultiLineFishingFloatSetup(FishingFloat fishingFloat, Character owner, string phase)
		{
			if (!((Object)(object)fishingFloat == (Object)null))
			{
				TrollingFishingPlugin.LogDebug(string.Format("[Fishing multiLine] setup {0} object={1} setupActive={2} owner={3} hasMarker={4}.", phase, ((Object)((Component)fishingFloat).gameObject).name, IsMultiLineFishingSetupActive(), ((Object)(object)owner != (Object)null) ? ((Object)owner).name : "null", (Object)(object)((Component)fishingFloat).GetComponent<MultiLineFishingFloatMarker>() != (Object)null));
			}
		}

		internal static void LogMultiLineFishingFloatDestroyed(FishingFloat fishingFloat)
		{
			if (!((Object)(object)fishingFloat == (Object)null))
			{
				MultiLineFishingFloatMarker component = ((Component)fishingFloat).GetComponent<MultiLineFishingFloatMarker>();
				if (!((Object)(object)component == (Object)null))
				{
					Character owner = component.Owner;
					TrollingFishingPlugin.LogDebug(string.Format("[Fishing multiLine] float destroyed object={0} owner={1} ownerInAttack={2} ownerDrawing={3} inWater={4}.", ((Object)((Component)fishingFloat).gameObject).name, ((Object)(object)owner != (Object)null) ? ((Object)owner).name : "null", (Object)(object)owner != (Object)null && owner.InAttack(), (Object)(object)owner != (Object)null && owner.IsDrawingBow(), fishingFloat.IsInWater()));
				}
			}
		}

		internal static void RegisterAttackBaitReturnSource(Attack attack, Player player, ItemData rod, ItemData bait)
		{
			if (attack != null && !((Object)(object)player == (Object)null) && !((Object)(object)bait?.m_dropPrefab == (Object)null))
			{
				MultiLineBaitReservation source = new MultiLineBaitReservation(((Object)bait.m_dropPrefab).name, fromRodBag: true, rod);
				BaitSourceTrackerState.AttackSources.Remove(attack);
				BaitSourceTrackerState.AttackSources.Add(attack, new AttackBaitReturnSourceState(source));
				TrollingFishingPlugin.LogDebug("[Fishing bait] registered attack bait return source prefab=" + ((Object)bait.m_dropPrefab).name + " fromRodBag=true.");
			}
		}

		internal static MultiLineBaitReservation ResolveAttackBaitReturnSource(Attack attack, ItemData? ammo)
		{
			if (!TryGetAttackBaitReturnSource(attack, ammo, out var source))
			{
				return default(MultiLineBaitReservation);
			}
			BaitSourceTrackerState.AttackSources.Remove(attack);
			return source;
		}

		internal static IDisposable? BeginAttackBaitReturnSourceSetup(Attack attack)
		{
			if (attack == null || !TryGetAttackBaitReturnSource(attack, attack.m_lastUsedAmmo ?? attack.m_ammoItem, out var source))
			{
				return null;
			}
			return BeginBaitReturnSourceSetup(source, attack);
		}

		private static IDisposable BeginBaitReturnSourceSetup(MultiLineBaitReservation source, Attack? attack = null)
		{
			BaitSourceTrackerState.SetupContexts.Add(source);
			return new BaitReturnSetupScope(attack);
		}

		internal static void MarkFishingFloatBaitReturnSource(FishingFloat fishingFloat, Character owner, ItemData? ammo)
		{
			MultiLineBaitReservation multiLineBaitReservation = CurrentBaitReturnSetupContext();
			if (!((Object)(object)fishingFloat == (Object)null) && multiLineBaitReservation.IsValid && IsMatchingBaitReturnAmmo(multiLineBaitReservation, ammo))
			{
				MarkFishingBaitReturnSource(((Component)fishingFloat).gameObject, multiLineBaitReservation);
				TrollingFishingPlugin.LogDebug($"[Fishing bait] marked bait return source prefab={multiLineBaitReservation.PrefabName} fromRodBag={multiLineBaitReservation.FromRodBag}.");
			}
		}

		internal static void MarkProjectileBaitReturnSource(Projectile projectile, ItemData? ammo)
		{
			MultiLineBaitReservation multiLineBaitReservation = CurrentBaitReturnSetupContext();
			if (!((Object)(object)projectile == (Object)null) && multiLineBaitReservation.IsValid && IsMatchingBaitReturnAmmo(multiLineBaitReservation, ammo))
			{
				MarkFishingBaitReturnSource(((Component)projectile).gameObject, multiLineBaitReservation);
				TrollingFishingPlugin.LogDebug($"[Fishing bait] marked projectile bait return source prefab={multiLineBaitReservation.PrefabName} fromRodBag={multiLineBaitReservation.FromRodBag}.");
			}
		}

		private static void MarkFishingBaitReturnSource(GameObject sourceObject, MultiLineBaitReservation baitReturnSource, bool settled = false)
		{
			if (!((Object)(object)sourceObject == (Object)null) && baitReturnSource.IsValid)
			{
				(sourceObject.GetComponent<FishingBaitReturnTracker>() ?? sourceObject.AddComponent<FishingBaitReturnTracker>()).Initialize(baitReturnSource, settled);
			}
		}

		private static bool TryGetAttackBaitReturnSource(Attack attack, ItemData? ammo, out MultiLineBaitReservation source)
		{
			source = default(MultiLineBaitReservation);
			if (attack == null || !BaitSourceTrackerState.AttackSources.TryGetValue(attack, out AttackBaitReturnSourceState value))
			{
				return false;
			}
			if (!IsMatchingBaitReturnAmmo(value.Source, ammo))
			{
				return false;
			}
			source = value.Source;
			return true;
		}

		private static bool IsMatchingBaitReturnAmmo(MultiLineBaitReservation source, ItemData? ammo)
		{
			if (source.IsValid && (Object)(object)ammo?.m_dropPrefab != (Object)null)
			{
				return string.Equals(source.PrefabName, ((Object)ammo.m_dropPrefab).name, StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}

		private static MultiLineBaitReservation CurrentBaitReturnSetupContext()
		{
			if (BaitSourceTrackerState.SetupContexts.Count <= 0)
			{
				return default(MultiLineBaitReservation);
			}
			return BaitSourceTrackerState.SetupContexts[BaitSourceTrackerState.SetupContexts.Count - 1];
		}

		internal static bool TryReturnBaitToOriginalSource(FishingFloat fishingFloat)
		{
			if ((Object)(object)fishingFloat == (Object)null)
			{
				return false;
			}
			if (TryReturnTrackedBaitToOriginalSource(((Component)fishingFloat).gameObject, fishingFloat))
			{
				return true;
			}
			MultiLineFishingFloatMarker component = ((Component)fishingFloat).GetComponent<MultiLineFishingFloatMarker>();
			if ((Object)(object)component == (Object)null)
			{
				return false;
			}
			MultiLineBaitReservation baitReturnSource = (component.ReservedBait.IsValid ? component.ReservedBait : component.BaitReturnSource);
			if (baitReturnSource.IsValid)
			{
				MarkFishingBaitReturnSource(((Component)fishingFloat).gameObject, baitReturnSource);
				return TryReturnTrackedBaitToOriginalSource(((Component)fishingFloat).gameObject, fishingFloat);
			}
			return component.IsAdditionalLine;
		}

		internal static void TrySettleBaitAfterProjectileGroundHit(Projectile projectile, Collider collider, bool water)
		{
			if (water || (Object)(object)projectile == (Object)null || !ProjectileAccess.GetDidHit(projectile) || !ProjectileAccess.WillDestroyAfterHit(projectile, collider))
			{
				return;
			}
			MultiLineFishingFloatMarker component = ((Component)projectile).GetComponent<MultiLineFishingFloatMarker>();
			if (!((Object)(object)component == (Object)null) && component.IsAdditionalLine)
			{
				FishingBaitReturnTracker component2 = ((Component)projectile).GetComponent<FishingBaitReturnTracker>();
				if ((Object)(object)component2 != (Object)null && component2.TrySettle())
				{