Decompiled source of BetterAngling v0.18.1

BepInEx/plugins/BetterAngling/BetterAngling.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Splatform;
using TMPro;
using UnityEngine;
using UnityEngine.Audio;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("BetterAngling")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.18.1.0")]
[assembly: AssemblyInformationalVersion("0.18.1")]
[assembly: AssemblyProduct("BetterAngling")]
[assembly: AssemblyTitle("BetterAngling")]
[assembly: AssemblyMetadata("BetterAngling.Build", "PublicRelease/0.18.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.18.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BetterAngling
{
	internal sealed class BaitRecipes : IDisposable
	{
		private sealed class Entry
		{
			internal BaitDefinition Definition;

			internal ConfigEntry<bool> Enabled;

			internal ConfigEntry<int> Amount;

			internal ConfigEntry<string> Costs;

			internal Recipe Recipe;
		}

		private readonly List<Entry> _entries = new List<Entry>();

		private readonly ManualLogSource _log;

		private readonly ConfigEntry<bool> _enabled;

		private bool _ready;

		private volatile bool _dirty;

		internal BaitRecipes(ConfigFile config, ManualLogSource log)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			_log = log;
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			_enabled = config.Bind<bool>("Bait", "Enabled", true, new ConfigDescription("Enable our additional workbench recipes. Vanilla recipes remain available.", (AcceptableValueBase)null, new object[1] { val }));
			_enabled.SettingChanged += Changed;
			BaitDefinition[] all = RecipeCatalog.All;
			foreach (BaitDefinition baitDefinition in all)
			{
				string text = "Bait - " + baitDefinition.Biome;
				Entry entry = new Entry
				{
					Definition = baitDefinition,
					Enabled = config.Bind<bool>(text, "Enabled", true, new ConfigDescription("Enable this additional bait recipe.", (AcceptableValueBase)null, new object[1] { val })),
					Amount = config.Bind<int>(text, "Amount", 20, new ConfigDescription("Bait produced per craft.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), new object[1] { val })),
					Costs = config.Bind<string>(text, "Ingredients", baitDefinition.Costs, new ConfigDescription("Comma-separated vanilla prefab names and quantities. Invalid costs disable the entire recipe.", (AcceptableValueBase)null, new object[1] { val }))
				};
				entry.Enabled.SettingChanged += Changed;
				entry.Amount.SettingChanged += Changed;
				entry.Costs.SettingChanged += Changed;
				_entries.Add(entry);
			}
			PrefabManager.OnVanillaPrefabsAvailable += Register;
			ItemManager.OnItemsRegisteredFejd += ValidateDefinitions;
			ItemManager.OnItemsRegistered += ValidateRegistered;
		}

		private void Changed(object sender, EventArgs args)
		{
			_dirty = true;
		}

		private static ItemDrop FindItem(string name)
		{
			GameObject prefab = Cache.GetPrefab<GameObject>(name);
			if (prefab == null)
			{
				return null;
			}
			return prefab.GetComponent<ItemDrop>();
		}

		private void Register()
		{
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			if (_ready)
			{
				return;
			}
			GameObject prefab = Cache.GetPrefab<GameObject>("piece_workbench");
			CraftingStation val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<CraftingStation>() : null);
			if ((Object)(object)val == (Object)null)
			{
				_log.LogError((object)"Workbench not found; bait recipes were not registered.");
				return;
			}
			foreach (Entry entry in _entries)
			{
				ItemDrop val2 = FindItem(entry.Definition.Output);
				if ((Object)(object)val2 == (Object)null)
				{
					_log.LogError((object)("Missing bait item: " + entry.Definition.Output));
					continue;
				}
				Recipe val3 = ScriptableObject.CreateInstance<Recipe>();
				((Object)val3).name = "Recipe_BetterAngling_" + entry.Definition.Output;
				val3.m_item = val2;
				val3.m_craftingStation = val;
				val3.m_minStationLevel = 1;
				val3.m_enabled = false;
				val3.m_resources = Array.Empty<Requirement>();
				entry.Recipe = val3;
				Apply(entry);
				if (!ItemManager.Instance.AddRecipe(new CustomRecipe(val3, false, false)))
				{
					_log.LogError((object)("Could not register " + ((Object)val3).name));
					entry.Recipe = null;
				}
			}
			_ready = true;
			_log.LogInfo((object)("Registered " + _entries.FindAll((Entry e) => (Object)(object)e.Recipe != (Object)null).Count + " additional bait recipes."));
		}

		private void Apply(Entry entry)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			Recipe recipe = entry.Recipe;
			if ((Object)(object)recipe == (Object)null)
			{
				return;
			}
			recipe.m_enabled = false;
			if (!IngredientParser.TryParse(entry.Costs.Value, (string name) => (Object)(object)FindItem(name) != (Object)null, out var ingredients, out var error))
			{
				_log.LogWarning((object)(entry.Definition.Biome + " bait disabled: " + error));
				return;
			}
			List<Requirement> list = new List<Requirement>();
			Ingredient[] array = ingredients;
			foreach (Ingredient ingredient in array)
			{
				list.Add(new Requirement
				{
					m_resItem = FindItem(ingredient.Item),
					m_amount = ingredient.Amount,
					m_amountPerLevel = 0,
					m_recover = false
				});
			}
			recipe.m_resources = list.ToArray();
			recipe.m_amount = entry.Amount.Value;
			recipe.m_enabled = _enabled.Value && entry.Enabled.Value;
		}

		internal void Update()
		{
			if (!_ready || !_dirty)
			{
				return;
			}
			_dirty = false;
			foreach (Entry entry in _entries)
			{
				Apply(entry);
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				Player.m_localPlayer.UpdateKnownRecipesList();
			}
		}

		private void ValidateDefinitions()
		{
			int count = _entries.FindAll((Entry entry) => (Object)(object)entry.Recipe != (Object)null && ItemManager.Instance.GetRecipe(((Object)entry.Recipe).name) != null).Count;
			_log.LogInfo((object)("Bait definitions ready: " + count + "/" + _entries.Count + ". Recipes enter ObjectDB when a world starts."));
		}

		private void ValidateRegistered()
		{
			int num = 0;
			bool flag = true;
			foreach (Entry entry in _entries)
			{
				if ((Object)(object)entry.Recipe == (Object)null || (Object)(object)ObjectDB.instance == (Object)null || !ObjectDB.instance.m_recipes.Contains(entry.Recipe))
				{
					_log.LogError((object)("Bait recipe missing from ObjectDB: " + entry.Definition.Output));
					flag = false;
					continue;
				}
				num++;
				Recipe recipe = entry.Recipe;
				bool flag2 = (Object)(object)recipe.m_item != (Object)null && ((Object)recipe.m_item).name == entry.Definition.Output && (Object)(object)recipe.m_craftingStation != (Object)null && recipe.m_minStationLevel == 1 && recipe.m_amount > 0 && recipe.m_resources.Length != 0;
				Requirement[] resources = recipe.m_resources;
				foreach (Requirement val in resources)
				{
					flag2 &= (Object)(object)val.m_resItem != (Object)null && val.m_amount > 0;
				}
				if (!flag2)
				{
					_log.LogError((object)("Unresolved or invalid bait recipe: " + entry.Definition.Output));
					flag = false;
				}
			}
			_log.LogInfo((object)("Bait recipe validation: " + num + "/" + _entries.Count + " present in ObjectDB."));
			flag &= (Object)(object)Plugin.Instance != (Object)null && Plugin.Instance.ValidateFoodRegistration();
		}

		public void Dispose()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= Register;
			ItemManager.OnItemsRegisteredFejd -= ValidateDefinitions;
			ItemManager.OnItemsRegistered -= ValidateRegistered;
			_enabled.SettingChanged -= Changed;
			foreach (Entry entry in _entries)
			{
				entry.Enabled.SettingChanged -= Changed;
				entry.Amount.SettingChanged -= Changed;
				entry.Costs.SettingChanged -= Changed;
			}
		}
	}
	[HarmonyPatch(typeof(Attack), "DoMeleeAttack")]
	internal static class BoardingPikeSweep
	{
		[ThreadStatic]
		internal static Player User;

		private static void Prefix(Attack __instance, out Player __state)
		{
			__state = User;
			ItemData weapon = __instance.m_weapon;
			object id;
			if (weapon == null)
			{
				id = null;
			}
			else
			{
				GameObject dropPrefab = weapon.m_dropPrefab;
				id = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
			}
			User = (Player)(MarineCrafting.IsPike((string)id) ? /*isinst with value type is only supported in some contexts*/: null);
		}

		private static Exception Finalizer(Exception __exception, Player __state)
		{
			User = __state;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Projectile), "FindHitObject")]
	internal static class BoardingPikeRailPatch
	{
		private static void Postfix(ref GameObject __result)
		{
			Player user = BoardingPikeSweep.User;
			if (!((Object)(object)user == (Object)null) && !((Object)(object)__result == (Object)null))
			{
				Ship component = __result.GetComponent<Ship>();
				if ((Object)(object)component != (Object)null && component.IsPlayerInBoat(user))
				{
					__result = ((Component)user).gameObject;
				}
			}
		}
	}
	public sealed class BoatFishHold : MonoBehaviour
	{
		internal const string ItemKey = "BA_hold_item";

		internal const string CargoKey = "BA_hold_cargo_v1";

		private const string UserKey = "BA_hold_user";

		private const string LeaseKey = "BA_hold_lease";

		internal Ship Ship;

		internal ZNetView View;

		private string _raw;

		private string _kitRecord;

		private FishHoldDefinition _definition;

		private int _readSlots;

		private int _visualSlots;

		private TackleStorage.State _state;

		private float _appliedWeight;

		private float _weightTick;

		private GameObject _visual;

		private GameObject _fill;

		private GameObject _gauge;

		private string _hull;

		private Material _catchMaterial;

		internal ZDO Data
		{
			get
			{
				if (!((Object)(object)View != (Object)null) || !View.IsValid())
				{
					return null;
				}
				return View.GetZDO();
			}
		}

		internal bool Installed
		{
			get
			{
				if (Data != null)
				{
					return !string.IsNullOrEmpty(Data.GetString("BA_hold_item", ""));
				}
				return false;
			}
		}

		internal FishHoldDefinition Definition
		{
			get
			{
				ZDO data = Data;
				string text = ((data != null) ? data.GetString("BA_hold_item", "") : null) ?? "";
				if (_definition == null || text != _kitRecord)
				{
					_kitRecord = text;
					_definition = (EquipmentRecord.TryDecode(text, FishHoldCatalog.IsKit, out var item, out var _) ? FishHoldCatalog.Find(((Object)item.m_dropPrefab).name) : FishHoldCatalog.All[0]);
				}
				return _definition;
			}
		}

		internal int Capacity => Definition.Slots;

		internal float Weight
		{
			get
			{
				if (Installed)
				{
					if (!Read().Readable)
					{
						float appliedWeight = _appliedWeight;
						ZDO data = Data;
						return Mathf.Max(appliedWeight, (data != null) ? data.GetFloat("BA_hold_weight", 0f) : 0f);
					}
					return Read().Inventory.GetTotalWeight();
				}
				return 0f;
			}
		}

		internal bool NeedsRecovery
		{
			get
			{
				if (Installed)
				{
					return !Fits(Definition);
				}
				return false;
			}
		}

		private bool Fits(FishHoldDefinition definition)
		{
			if (_hull != "Raft")
			{
				if (!(_hull != "Karve"))
				{
					return definition.Slots <= FishHoldCatalog.All[FishHoldCatalog.All.Length - 4].Slots;
				}
				return true;
			}
			return false;
		}

		internal void Awake()
		{
			Ship = ((Component)this).GetComponent<Ship>();
			View = ((Component)this).GetComponent<ZNetView>();
			_hull = Utils.GetPrefabName(((Component)this).gameObject);
		}

		internal static BoatFishHold Aboard(Player player)
		{
			Ship localShip = Ship.GetLocalShip();
			if (!((Object)(object)player != (Object)null) || !((Object)(object)localShip != (Object)null) || !localShip.IsPlayerInBoat(player))
			{
				return null;
			}
			return ((Component)localShip).GetComponent<BoatFishHold>();
		}

		internal TackleStorage.State Read()
		{
			ZDO data = Data;
			string text = ((data != null) ? data.GetString("BA_hold_cargo_v1", "") : null) ?? "";
			if (_state != null && _raw == text && _readSlots == Capacity)
			{
				return _state;
			}
			_raw = text;
			_readSlots = Capacity;
			_state = FishHoldStorage.Read(text, Capacity);
			if (_state.Readable && _state.Inventory.GetAllItems().Any((ItemData i) => !TackleStorage.IsFish(i)))
			{
				_state.Error = "Unexpected hold contents; original data preserved.";
			}
			return _state;
		}

		internal bool CanOperate(Player player)
		{
			if (Data != null)
			{
				BoatFishing instance = BoatFishing.Instance;
				if (instance != null && instance.Enabled.Value && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && !((Character)player).IsDead() && !((Character)player).IsTeleporting())
				{
					return Ship.IsPlayerInBoat(player);
				}
			}
			return false;
		}

		internal bool Own(Player player)
		{
			//IL_0016: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (!CanOperate(player))
			{
				return false;
			}
			ZDOID zDOID = Data.GetZDOID("BA_hold_user");
			long num = Data.GetLong("BA_hold_lease", 0L);
			if (zDOID != ZDOID.None && zDOID != ((Character)player).GetZDOID() && num > ZNet.instance.GetTime().Ticks)
			{
				return false;
			}
			return ExpeditionAuthority.For(((Component)this).gameObject).TryAcquire(player);
		}

		internal bool Acquire(Player player)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (!Installed || !Own(player))
			{
				return false;
			}
			Data.Set("BA_hold_user", ((Character)player).GetZDOID());
			Data.Set("BA_hold_lease", ZNet.instance.GetTime().AddSeconds(3.0).Ticks);
			return true;
		}

		internal void Release(Player player)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			if (Data != null && View.IsOwner() && (Object)(object)player != (Object)null && Data.GetZDOID("BA_hold_user") == ((Character)player).GetZDOID())
			{
				Data.Set("BA_hold_user", ZDOID.None);
				Data.Set("BA_hold_lease", 0L);
				ExpeditionAuthority.For(((Component)this).gameObject).Release(player);
			}
		}

		internal bool Install(Player player, ItemData kit)
		{
			if (!CanOperate(player) || !FishHoldCatalog.IsKit(kit) || !((Humanoid)player).GetInventory().ContainsItem(kit))
			{
				return false;
			}
			FishHoldDefinition fishHoldDefinition = FishHoldCatalog.Find(((Object)kit.m_dropPrefab).name);
			bool installed = Installed;
			if (!Fits(fishHoldDefinition))
			{
				LobsterFishing.Say(player, (_hull == "Raft") ? "A raft has no fish hold mount." : "The Karve fits wood, copper, bronze and iron fish holds.");
				return false;
			}
			if (NeedsRecovery)
			{
				LobsterFishing.Say(player, "Use the boat fitting install key to recover the old fish hold first.");
				return false;
			}
			if (!Own(player))
			{
				return false;
			}
			if (installed && fishHoldDefinition.Slots <= Capacity)
			{
				LobsterFishing.Say(player, "Use a larger metal hold kit to expand this hold.");
				return false;
			}
			if (installed && kit.m_customData.ContainsKey("BA_hold_recoveredCargo"))
			{
				LobsterFishing.Say(player, "Install the recovered loaded hold on a boat with no hold first.");
				return false;
			}
			string value;
			TackleStorage.State state = (installed ? Read() : FishHoldStorage.Read(kit.m_customData.TryGetValue("BA_hold_recoveredCargo", out value) ? value : "", fishHoldDefinition.Slots));
			if (!state.Readable)
			{
				LobsterFishing.Say(player, state.Error);
				return false;
			}
			string cargo = FishHoldStorage.Encode(FishHoldStorage.Resize(state.Inventory, fishHoldDefinition.Slots));
			ItemData item = null;
			if (installed && !EquipmentRecord.TryDecode(Data.GetString("BA_hold_item", ""), FishHoldCatalog.IsKit, out item, out var _))
			{
				return false;
			}
			ItemData obj = kit.Clone();
			obj.m_customData.Remove("BA_hold_recoveredCargo");
			string text = EquipmentRecord.Encode(obj);
			if (!((Humanoid)player).GetInventory().RemoveItem(kit, 1))
			{
				return false;
			}
			Data.Set("BA_hold_item", text);
			SetCargo(cargo);
			if (item != null)
			{
				item.m_customData.Remove("BA_hold_recoveredCargo");
				TackleStorage.PlanInsert(((Humanoid)player).GetInventory(), item, 1).Apply(((Humanoid)player).GetInventory());
				((Humanoid)player).GetInventory().Changed(false, false);
			}
			LobsterFishing.Say(player, fishHoldDefinition.Metal + " fish hold " + (installed ? "expanded" : "installed") + ": " + fishHoldDefinition.Slots + " slots. Catch preserved.");
			return true;
		}

		internal bool Prepare(Player player, ItemData fish, out string encoded)
		{
			encoded = null;
			if (!Installed || !Fits(Definition) || !TackleStorage.IsFish(fish) || !Own(player) || !Read().Readable)
			{
				return false;
			}
			if (Read().Inventory.NrOfItems() > Capacity)
			{
				return false;
			}
			Inventory val = TackleStorage.Copy(Read().Inventory);
			TackleStorage.InsertPlan insertPlan = TackleStorage.PlanInsert(val, fish, fish.m_stack);
			if (insertPlan == null)
			{
				return false;
			}
			insertPlan.Apply(val);
			encoded = FishHoldStorage.Encode(val);
			return true;
		}

		internal bool Store(Player player, ItemData fish)
		{
			if (!Prepare(player, fish, out var encoded))
			{
				return false;
			}
			SetCargo(encoded);
			return true;
		}

		internal void SetCargo(string encoded)
		{
			Data.Set("BA_hold_cargo_v1", encoded);
			Data.Set("BA_hold_weight", Read().Inventory.GetTotalWeight());
		}

		internal bool Deposit(Player player, ItemData fish)
		{
			if (!Acquire(player) || !((Humanoid)player).GetInventory().ContainsItem(fish) || !Prepare(player, fish, out var encoded))
			{
				return false;
			}
			SetCargo(encoded);
			((Humanoid)player).GetInventory().GetAllItems().Remove(fish);
			((Humanoid)player).GetInventory().Changed(false, false);
			return true;
		}

		internal bool Withdraw(Player player, ItemData fish)
		{
			if (!Acquire(player) || !Read().Readable || !Read().Inventory.ContainsItem(fish))
			{
				return false;
			}
			TackleStorage.InsertPlan insertPlan = TackleStorage.PlanInsert(((Humanoid)player).GetInventory(), fish, fish.m_stack);
			if (insertPlan == null)
			{
				return false;
			}
			Inventory obj = TackleStorage.Copy(Read().Inventory);
			obj.GetAllItems().RemoveAll((ItemData i) => i.m_gridPos == fish.m_gridPos);
			string cargo = FishHoldStorage.Encode(obj);
			List<ItemData> allItems = ((Humanoid)player).GetInventory().GetAllItems();
			if (allItems.Capacity < allItems.Count + insertPlan.Added.Count)
			{
				allItems.Capacity = allItems.Count + insertPlan.Added.Count;
			}
			SetCargo(cargo);
			insertPlan.Apply(((Humanoid)player).GetInventory());
			((Humanoid)player).GetInventory().Changed(false, false);
			return true;
		}

		internal bool Recover(Player player)
		{
			if (!NeedsRecovery || !Own(player) || !Read().Readable || !EquipmentRecord.TryDecode(Data.GetString("BA_hold_item", ""), FishHoldCatalog.IsKit, out var item, out var _))
			{
				return false;
			}
			item.m_customData["BA_hold_recoveredCargo"] = FishHoldStorage.Encode(Read().Inventory);
			TackleStorage.InsertPlan insertPlan = TackleStorage.PlanInsert(((Humanoid)player).GetInventory(), item, 1);
			if (insertPlan == null)
			{
				LobsterFishing.Say(player, "Free one inventory slot to recover the loaded fish hold.");
				return false;
			}
			Data.Set("BA_hold_item", "");
			Data.Set("BA_hold_cargo_v1", "");
			Data.Set("BA_hold_weight", 0f);
			insertPlan.Apply(((Humanoid)player).GetInventory());
			((Humanoid)player).GetInventory().Changed(false, false);
			LobsterFishing.Say(player, "Recovered the fish hold and its catch.");
			return true;
		}

		internal void ApplyWeight()
		{
			if (!((Object)(object)Ship?.m_body == (Object)null))
			{
				float weight = Weight;
				Ship.m_body.mass = Mathf.Max(1f, Ship.m_body.mass + weight - _appliedWeight);
				_appliedWeight = weight;
			}
		}

		private void FixedUpdate()
		{
			_weightTick += Time.fixedDeltaTime;
			if (!(_weightTick < 1f) && !((Object)(object)Ship?.m_body == (Object)null))
			{
				_weightTick = 0f;
				ApplyWeight();
			}
		}

		private void Update()
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			if (Application.isBatchMode)
			{
				return;
			}
			if (!Installed || !Fits(Definition))
			{
				if ((Object)(object)_visual != (Object)null)
				{
					ClearVisual();
				}
				return;
			}
			if ((Object)(object)_visual == (Object)null || _visualSlots != Capacity)
			{
				ClearVisual();
				BuildVisual();
			}
			if ((Object)(object)_fill != (Object)null && Read().Readable)
			{
				int count = Read().Inventory.GetAllItems().Count;
				float num = 0.3f * (float)Mathf.Min(count, Capacity) / (float)Capacity;
				_fill.SetActive(count > 0);
				_fill.transform.localScale = new Vector3(1.15f, 0.035f + num, 0.42f);
				_fill.transform.localPosition = new Vector3(0f, 0.133f + num / 2f, 0f);
				_gauge.transform.localScale = new Vector3(0.026f, 0.012f + num, 0.01f);
				_gauge.transform.localPosition = new Vector3(0.25f, 0.12f + num / 2f, 0.345f);
			}
		}

		private void BuildVisual()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//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_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: 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_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			_visualSlots = Capacity;
			_visual = new GameObject("BA_FishHold");
			_visual.layer = LayerMask.NameToLayer("piece");
			_visual.transform.SetParent(((Component)this).transform, false);
			Quaternion val = Quaternion.identity;
			float num = 1f;
			Vector3 localPosition = default(Vector3);
			switch (_hull)
			{
			case "Karve":
				((Vector3)(ref localPosition))..ctor(-0.69f, 0.081f, 0.1f);
				val = Quaternion.Euler(0f, 0f, 9.2f);
				num = 0.81f;
				break;
			case "VikingShip":
				((Vector3)(ref localPosition))..ctor(-1.03f, 0.639f, 0.85f);
				val = Quaternion.Euler(0f, 0f, 1.2f);
				break;
			case "VikingShip_Ashlands":
				((Vector3)(ref localPosition))..ctor(-1.55f, 1.947f, 1.25f);
				val = Quaternion.Euler(0.3f, 0f, 0f);
				num = 1.12f;
				break;
			case "BA_Pequod":
				((Vector3)(ref localPosition))..ctor(-1.25f, 0.74f, -1.72f);
				break;
			default:
				localPosition = ShipFishingRig.FloatBoxAnchor(Ship, RigStation.Net);
				localPosition.x *= -0.45f;
				localPosition.z = 0f;
				break;
			}
			_visual.transform.localPosition = localPosition;
			_visual.transform.localRotation = val * Quaternion.Euler(0f, 90f, 0f);
			_visual.transform.localScale = Vector3.one * num;
			BoxCollider obj = _visual.AddComponent<BoxCollider>();
			obj.center = new Vector3(0f, 0.285f, 0f);
			obj.size = new Vector3(1.5f, 0.57f, 0.7f);
			((Collider)obj).isTrigger = true;
			_visual.AddComponent<FishHoldControl>().Hold = this;
			HuntRelicArt.Build(_visual.transform, Definition.Model, Vector3.zero);
			_catchMaterial = RigVisual.CloneNativeMaterial(null, RigMaterialKind.Metal);
			_fill = Part("Visible catch", Vector3.zero, Vector3.zero, _catchMaterial);
			Part("Gauge channel", new Vector3(0.25f, 0.267f, 0.336f), new Vector3(0.042f, 0.32f, 0.011f), ((Renderer)_visual.GetComponentInChildren<MeshRenderer>()).sharedMaterial);
			_gauge = Part("Fill gauge", Vector3.zero, Vector3.zero, _catchMaterial);
		}

		private GameObject Part(string name, Vector3 pos, Vector3 scale, Material material)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3);
			((Object)obj).name = name;
			obj.transform.SetParent(_visual.transform, false);
			obj.transform.localPosition = pos;
			obj.transform.localScale = scale;
			obj.GetComponent<Collider>().enabled = false;
			Object.Destroy((Object)(object)obj.GetComponent<Collider>());
			obj.GetComponent<Renderer>().sharedMaterial = material;
			return obj;
		}

		internal void DropOnDestruction()
		{
			//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)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			if (Installed && ExpeditionAuthority.For(((Component)this).gameObject).ReadyToWrite && Read().Readable && EquipmentRecord.TryDecode(Data.GetString("BA_hold_item", ""), FishHoldCatalog.IsKit, out var item, out var _))
			{
				item.m_customData["BA_hold_recoveredCargo"] = FishHoldStorage.Encode(Read().Inventory);
				WaterVolume val = null;
				Vector3 val2 = ((Component)this).transform.position + Vector3.up;
				val2.y = Mathf.Max(val2.y, Floating.GetWaterLevel(val2, ref val) + 0.7f);
				ItemDrop.DropItem(item, 1, val2, Quaternion.identity);
				Data.Set("BA_hold_item", "");
				Data.Set("BA_hold_cargo_v1", "");
				Data.Set("BA_hold_weight", 0f);
			}
		}

		private void ClearVisual()
		{
			if ((Object)(object)_visual != (Object)null)
			{
				_visual.SetActive(false);
				Object.Destroy((Object)(object)_visual);
				_visual = null;
			}
			if ((Object)(object)_catchMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_catchMaterial);
				_catchMaterial = null;
			}
		}

		private void OnDestroy()
		{
			if ((Object)(object)Ship?.m_body != (Object)null)
			{
				Ship.m_body.mass = Mathf.Max(1f, Ship.m_body.mass - _appliedWeight);
			}
			ClearVisual();
		}
	}
	public sealed class FishHoldControl : MonoBehaviour, Hoverable, Interactable
	{
		internal BoatFishHold Hold;

		public string GetHoverName()
		{
			return Hold.Definition.Metal + " fish hold";
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public string GetHoverText()
		{
			return Localization.instance.Localize(GetHoverName() + "\n" + (Hold.Read().Readable ? (Hold.Read().Inventory.GetAllItems().Count + "/" + Hold.Capacity + " slots | " + Hold.Weight.ToString("F1") + " weight\n[<color=yellow><b>$KEY_Use</b></color>] Open") : Hold.Read().Error));
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			if (!hold && !alt)
			{
				Player val = (Player)(object)((user is Player) ? user : null);
				if (val != null && BoatFishing.CanInput())
				{
					return ExpeditionGear.Instance.OpenHold(val, Hold);
				}
			}
			return false;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			Player val = (Player)(object)((user is Player) ? user : null);
			if (val != null && FishHoldCatalog.IsKit(item))
			{
				return Hold.Install(val, item);
			}
			return false;
		}
	}
	internal sealed class BoatFishing : IDisposable
	{
		internal const string HolderKit = "BA_HarpoonHolderKit";

		internal const string NetKit = "BA_TrawlNetKit";

		internal static BoatFishing Instance;

		internal readonly ConfigEntry<KeyboardShortcut> InstallKey;

		internal readonly ConfigEntry<bool> Enabled;

		private readonly Harmony _harmony;

		private readonly ManualLogSource _log;

		private readonly List<CustomItem> _items = new List<CustomItem>();

		private readonly List<Recipe> _recipes = new List<Recipe>();

		internal BoatFishing(ConfigFile config, ManualLogSource log)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Expected O, but got Unknown
			Instance = this;
			_log = log;
			Enabled = config.Bind<bool>("Boat fishing", "Enabled", true, new ConfigDescription("Enable boat fittings and pot interactions. Disable only with all nets and lobster pots recovered and tethers released.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			InstallKey = config.Bind<KeyboardShortcut>("Boat fishing", "InstallKey", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Install a carried holder/net kit aboard a boat. One fitting of each type per boat.");
			PrefabManager.OnVanillaPrefabsAvailable += Register;
			_harmony = new Harmony("curti.valheim.betterangling.rigs");
			Type[] array = new Type[10]
			{
				typeof(RigShipPatch),
				typeof(HaulingPlayerPatch),
				typeof(HaulingCrouchPatch),
				typeof(RigAttackerPatch),
				typeof(RigHarpoonUpdatePatch),
				typeof(RigHarpoonDonePatch),
				typeof(RigLinePatch),
				typeof(RigDestroyPatch),
				typeof(NetUsePatch),
				typeof(NetTooltipPatch)
			};
			foreach (Type type in array)
			{
				_harmony.CreateClassProcessor(type).Patch();
			}
		}

		private void Register()
		{
			if (_items.Count != 0)
			{
				return;
			}
			Add("BA_HarpoonHolderKit", "Harpoon holder kit", "Install aboard a boat with F9. Secure a harpooned serpent at the bow holder (front); the boat takes the strain.", "Bronze", (Requirement[])(object)new Requirement[3]
			{
				Cost("FineWood", 10),
				Cost("Bronze", 3),
				Cost("LeatherScraps", 4)
			}, "piece_workbench", 1);
			NetDefinition[] all = NetCatalog.All;
			foreach (NetDefinition netDefinition in all)
			{
				if (!IngredientParser.TryParse(netDefinition.Costs, null, out var ingredients, out var error))
				{
					throw new InvalidOperationException(error);
				}
				string text = ((netDefinition.Prefab == "BA_SalvageTrawl") ? "Mount in the stern net slot. Lower over submerged loose items, then hold Use to reel them aboard. Recovers scales, trophies and equipment, preserving item details. Eight cargo slots, 200 weight, 120m cable. Tow slowly; full inventory leaves cargo secured. Pack only after unloading." : ("Forge-crafted net. Carry aboard and use near the stern winch to mount or swap; F9 mounts a first net. Capacity " + RigRules.Capacity(netDefinition.Tier) + ". Alternate Use at a stowed winch returns the net to your inventory."));
				if (netDefinition.Prefab != "BA_SalvageTrawl")
				{
					text += ((netDefinition.Tier == 1) ? " Deployed width: 4m." : ((netDefinition.Tier == 6) ? " Spreads two hull-widths to each side of the boat's centerline." : " Wider fishing spread than the preceding metal tier."));
				}
				Add(netDefinition.Prefab, netDefinition.Name, text, "LinenThread", Array.ConvertAll(ingredients, (Ingredient c) => Cost(c.Item, c.Amount)), "forge", netDefinition.ForgeLevel);
			}
			_log.LogInfo((object)"Registered harpoon holder, six forge-crafted fishing nets and salvage trawl.");
		}

		private void Add(string prefab, string name, string description, string template, Requirement[] costs, string station, int level)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Expected O, but got Unknown
			CustomItem val = new CustomItem(prefab, template);
			SharedData shared = val.ItemDrop.m_itemData.m_shared;
			shared.m_name = name;
			shared.m_description = description;
			shared.m_itemType = (ItemType)1;
			shared.m_maxStackSize = 1;
			shared.m_maxQuality = 1;
			shared.m_weight = 5f;
			shared.m_value = 0;
			shared.m_ammoType = "";
			shared.m_useDurability = false;
			shared.m_consumeStatusEffect = null;
			shared.m_equipStatusEffect = null;
			FittingItemArt.Apply(val.ItemPrefab);
			if (!ItemManager.Instance.AddItem(val))
			{
				throw new InvalidOperationException("Could not register " + prefab);
			}
			_items.Add(val);
			Recipe val2 = ScriptableObject.CreateInstance<Recipe>();
			((Object)val2).name = "Recipe_" + prefab;
			val2.m_item = val.ItemDrop;
			val2.m_amount = 1;
			val2.m_enabled = true;
			val2.m_minStationLevel = level;
			val2.m_resources = costs;
			val2.m_craftingStation = Cache.GetPrefab<GameObject>(station).GetComponent<CraftingStation>();
			if (!ItemManager.Instance.AddRecipe(new CustomRecipe(val2, false, false)))
			{
				throw new InvalidOperationException("Could not register recipe " + prefab);
			}
			_recipes.Add(val2);
		}

		private static Requirement Cost(string name, int amount)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			return new Requirement
			{
				m_resItem = Cache.GetPrefab<GameObject>(name).GetComponent<ItemDrop>(),
				m_amount = amount
			};
		}

		internal void Update()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (!Enabled.Value)
			{
				return;
			}
			KeyboardShortcut value = InstallKey.Value;
			if (!((KeyboardShortcut)(ref value)).IsDown() || !CanInput())
			{
				return;
			}
			Ship localShip = Ship.GetLocalShip();
			if ((Object)(object)localShip == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || !localShip.IsPlayerInBoat(Player.m_localPlayer))
			{
				return;
			}
			BoatFishHold component = ((Component)localShip).GetComponent<BoatFishHold>();
			if ((Object)(object)component != (Object)null && component.NeedsRecovery)
			{
				component.Recover(Player.m_localPlayer);
				return;
			}
			ShipFishingRig component2 = ((Component)localShip).GetComponent<ShipFishingRig>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.Install(Player.m_localPlayer);
			}
		}

		internal static bool CanInput()
		{
			if (!Application.isBatchMode && (Object)(object)Player.m_localPlayer != (Object)null && !((Character)Player.m_localPlayer).IsDead() && !((Character)Player.m_localPlayer).IsTeleporting() && !Menu.IsVisible() && !InventoryGui.IsVisible() && !Console.IsVisible())
			{
				if (!((Object)(object)Chat.instance == (Object)null))
				{
					return !Chat.instance.HasFocus();
				}
				return true;
			}
			return false;
		}

		internal bool ValidateRegistered()
		{
			bool flag = _items.Count == NetCatalog.All.Length + 1 && _recipes.Count == NetCatalog.All.Length + 1;
			foreach (CustomItem item in _items)
			{
				flag &= (Object)(object)ObjectDB.instance.GetItemPrefab(((Object)item.ItemPrefab).name) == (Object)(object)item.ItemPrefab;
			}
			foreach (Recipe recipe in _recipes)
			{
				flag &= ObjectDB.instance.m_recipes.Contains(recipe);
			}
			_log.LogInfo((object)("Boat fitting registration: " + (flag ? "valid." : "FAILED.")));
			return flag;
		}

		public void Dispose()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= Register;
			_harmony.UnpatchSelf();
			foreach (ShipFishingRig item in new List<ShipFishingRig>(ShipFishingRig.All))
			{
				if ((Object)(object)item != (Object)null)
				{
					Object.Destroy((Object)(object)item);
				}
			}
			if (Instance == this)
			{
				Instance = null;
			}
		}
	}
	[HarmonyPatch(typeof(Ship), "Awake")]
	internal static class RigShipPatch
	{
		private static void Postfix(Ship __instance)
		{
			if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid() && (Object)(object)((Component)__instance).GetComponent<ShipFishingRig>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<ShipFishingRig>();
			}
			if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid() && (Object)(object)((Component)__instance).GetComponent<BoatFishHold>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<BoatFishHold>();
			}
			if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid())
			{
				ExpeditionAuthority.For(((Component)__instance).gameObject);
			}
			if ((Object)(object)__instance.m_nview != (Object)null && __instance.m_nview.IsValid() && (Object)(object)((Component)__instance).GetComponent<BoatLamp>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<BoatLamp>();
			}
		}
	}
	public sealed class BoatLamp : MonoBehaviour
	{
		internal const string ItemKey = "BA_lamp_item";

		internal const string FuelKey = "BA_lamp_until";

		private ZNetView _view;

		private Ship _ship;

		private GameObject _model;

		private GameObject _flame;

		private Light _light;

		private Material _flameMaterial;

		private ZDO Data
		{
			get
			{
				if (!((Object)(object)_view != (Object)null) || !_view.IsValid())
				{
					return null;
				}
				return _view.GetZDO();
			}
		}

		internal bool Installed
		{
			get
			{
				ZDO data = Data;
				return !string.IsNullOrEmpty((data != null) ? data.GetString("BA_lamp_item", "") : null);
			}
		}

		internal double Seconds
		{
			get
			{
				if (Data != null)
				{
					return Math.Max(0.0, (double)(Data.GetLong("BA_lamp_until", 0L) - ZNet.instance.GetTime().Ticks) / 10000000.0);
				}
				return 0.0;
			}
		}

		private void Awake()
		{
			_view = ((Component)this).GetComponent<ZNetView>();
			_ship = ((Component)this).GetComponent<Ship>();
		}

		private bool Own(Player p)
		{
			if ((Object)(object)p != (Object)null && !((Character)p).IsDead() && _ship.IsPlayerInBoat(p))
			{
				return ((Component)this).GetComponent<BoatFishHold>().Own(p);
			}
			return false;
		}

		internal bool Install(Player p, ItemData kit)
		{
			if (!Installed && Own(p))
			{
				object obj;
				if (kit == null)
				{
					obj = null;
				}
				else
				{
					GameObject dropPrefab = kit.m_dropPrefab;
					obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
				}
				if (!((string?)obj != "BA_BlubberLamp") && ((Humanoid)p).GetInventory().ContainsItem(kit))
				{
					string text = EquipmentRecord.Encode(kit);
					if (!((Humanoid)p).GetInventory().RemoveItem(kit, 1))
					{
						return false;
					}
					Data.Set("BA_lamp_item", text);
					Data.Set("BA_lamp_until", 0L);
					LobsterFishing.Say(p, "Deck lamp installed beside the mast. Add whale blubber at the lamp.");
					return true;
				}
			}
			return false;
		}

		internal bool Fuel(Player p)
		{
			if (!Installed || !Own(p) || Seconds >= 24000.0)
			{
				return false;
			}
			ItemData item = ((Humanoid)p).GetInventory().GetItem(PrefabManager.Instance.GetPrefab("BA_WhaleBlubber").GetComponent<ItemDrop>().m_itemData.m_shared.m_name, -1, false);
			if (item == null)
			{
				LobsterFishing.Say(p, "Carry whale blubber to fuel this lamp.");
				return false;
			}
			if (!((Humanoid)p).GetInventory().RemoveItem(item, 1))
			{
				return false;
			}
			Data.Set("BA_lamp_until", ZNet.instance.GetTime().AddSeconds(Math.Min(24000.0, Seconds + 1200.0)).Ticks);
			return true;
		}

		internal bool Pack(Player p)
		{
			if (!Installed || !Own(p))
			{
				return false;
			}
			if (Seconds > 0.0)
			{
				LobsterFishing.Say(p, "Let the lamp burn empty before packing it.");
				return false;
			}
			if (!EquipmentRecord.TryDecode(Data.GetString("BA_lamp_item", ""), (ItemData i) => ((Object)i.m_dropPrefab).name == "BA_BlubberLamp", out var item, out var _))
			{
				return false;
			}
			TackleStorage.InsertPlan insertPlan = TackleStorage.PlanInsert(((Humanoid)p).GetInventory(), item, 1);
			if (insertPlan == null)
			{
				return false;
			}
			Data.Set("BA_lamp_item", "");
			insertPlan.Apply(((Humanoid)p).GetInventory());
			((Humanoid)p).GetInventory().Changed(false, false);
			return true;
		}

		private void Update()
		{
			//IL_03b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Expected O, but got Unknown
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Expected O, but got Unknown
			//IL_027d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0300: Unknown result type (might be due to invalid IL or missing references)
			if (Application.isBatchMode)
			{
				return;
			}
			if (!Installed)
			{
				if ((Object)(object)_model != (Object)null)
				{
					Object.Destroy((Object)(object)_model);
				}
				_model = null;
				return;
			}
			if ((Object)(object)_model == (Object)null)
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab("BA_BlubberLamp");
				Transform val = ((prefab != null) ? ((IEnumerable<Transform>)prefab.GetComponentsInChildren<Transform>(true)).FirstOrDefault((Func<Transform, bool>)((Transform t) => ((Object)t).name == "BA_ItemArt")) : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				_model = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)this).transform);
				((Object)_model).name = "BA_DeckLamp";
				_model.SetActive(true);
				_model.transform.localPosition = new Vector3(0.95f, ShipFishingRig.FloatBoxAnchor(_ship, RigStation.Net).y + 0.65f, 0f);
				_model.transform.localRotation = Quaternion.identity;
				_model.transform.localScale = Vector3.one * 1.5f;
				BoxCollider obj = _model.AddComponent<BoxCollider>();
				obj.center = new Vector3(0f, 0.2f, 0f);
				obj.size = new Vector3(0.6f, 0.7f, 0.6f);
				((Collider)obj).isTrigger = true;
				_model.layer = LayerMask.NameToLayer("piece");
				_model.AddComponent<BoatLampControl>().Lamp = this;
				GameObject val2 = new GameObject("Blubber flame light", new Type[1] { typeof(Light) });
				val2.transform.SetParent(_model.transform, false);
				val2.transform.localPosition = new Vector3(0f, 0.25f, 0f);
				_light = val2.GetComponent<Light>();
				_light.type = (LightType)2;
				_light.range = 9f;
				_light.color = new Color(1f, 0.63f, 0.24f);
				_light.shadows = (LightShadows)0;
				_flame = new GameObject("Blubber flame");
				_flame.transform.SetParent(_model.transform, false);
				FittingItemArt.Shape shape = new FittingItemArt.Shape();
				shape.Taper(new Vector3(0f, 0.2f, 0f), new Vector3(0.018f, 0.38f, 0f), 0.045f, 0.002f);
				_flameMaterial = MarineItemArt.Material(new Color(1f, 0.55f, 0.06f));
				if (_flameMaterial.HasProperty("_EmissionColor"))
				{
					_flameMaterial.EnableKeyword("_EMISSION");
					_flameMaterial.SetColor("_EmissionColor", new Color(1.7f, 0.48f, 0.025f));
				}
				shape.Finish(_flame.transform, "Flame", _flameMaterial);
			}
			((Behaviour)_light).enabled = Seconds > 0.0;
			_light.intensity = 1.2f + Mathf.Sin(Time.time * 9f) * 0.07f;
			_flame.SetActive(Seconds > 0.0);
			_flame.transform.localScale = new Vector3(1f, 1f + Mathf.Sin(Time.time * 11f) * 0.09f, 1f);
		}

		private void OnDestroy()
		{
			if ((Object)(object)_flameMaterial != (Object)null)
			{
				Object.Destroy((Object)(object)_flameMaterial);
			}
		}

		internal void DropOnDestruction()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (Installed && _view.IsOwner())
			{
				if (EquipmentRecord.TryDecode(Data.GetString("BA_lamp_item", ""), (ItemData i) => ((Object)i.m_dropPrefab).name == "BA_BlubberLamp", out var item, out var _))
				{
					ItemDrop.DropItem(item, 1, ((Component)this).transform.position + Vector3.up, Quaternion.identity);
				}
				int num = (int)(Seconds / 1200.0);
				if (num > 0)
				{
					ItemData obj = EquipmentRecord.Create("BA_WhaleBlubber");
					obj.m_stack = num;
					ItemDrop.DropItem(obj, num, ((Component)this).transform.position + Vector3.up, Quaternion.identity);
				}
				Data.Set("BA_lamp_item", "");
				Data.Set("BA_lamp_until", 0L);
			}
		}
	}
	public sealed class BoatLampControl : MonoBehaviour, Hoverable, Interactable
	{
		internal BoatLamp Lamp;

		public string GetHoverName()
		{
			return "Whaler's deck lamp";
		}

		public float GetHoverOffset()
		{
			return 0.4f;
		}

		public string GetHoverText()
		{
			return Localization.instance.Localize(GetHoverName() + " | " + Math.Ceiling(Lamp.Seconds / 60.0) + " minutes\n[<color=yellow><b>$KEY_Use</b></color>] Add whale blubber\n[<color=yellow><b>$KEY_AltPlace + $KEY_Use</b></color>] Pack empty lamp");
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			if (!hold && (Object)(object)user == (Object)(object)Player.m_localPlayer)
			{
				if (!alt)
				{
					return Lamp.Fuel((Player)user);
				}
				return Lamp.Pack((Player)user);
			}
			return false;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Expected O, but got Unknown
			object obj;
			if (item == null)
			{
				obj = null;
			}
			else
			{
				GameObject dropPrefab = item.m_dropPrefab;
				obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
			}
			if ((string?)obj == "BA_WhaleBlubber" && (Object)(object)user == (Object)(object)Player.m_localPlayer)
			{
				return Lamp.Fuel((Player)user);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UseItem")]
	internal static class BoatLampUsePatch
	{
		private static bool Prefix(Humanoid __instance, ItemData item)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				object obj;
				if (item == null)
				{
					obj = null;
				}
				else
				{
					GameObject dropPrefab = item.m_dropPrefab;
					obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
				}
				if (!((string?)obj != "BA_BlubberLamp"))
				{
					BoatFishHold boatFishHold = BoatFishHold.Aboard(Player.m_localPlayer);
					if (boatFishHold == null || ((Component)boatFishHold).GetComponent<BoatLamp>()?.Install(Player.m_localPlayer, item) != true)
					{
						LobsterFishing.Say(Player.m_localPlayer, "Use this kit aboard a boat without a deck lamp.");
					}
					return false;
				}
			}
			return true;
		}
	}
	internal sealed class HideBuoyDefinition
	{
		internal readonly string Id;

		internal readonly string Material;

		internal readonly int Days;

		internal readonly int Station;

		internal string Prefab => "BA_Buoy" + Id;

		internal HideBuoyDefinition(string id, string material, int days, int station)
		{
			Id = id;
			Material = material;
			Days = days;
			Station = station;
		}
	}
	internal static class BuoyCatalog
	{
		internal const string WhaleHide = "BA_WhaleHide";

		internal const string TypeKey = "BA_buoy_type";

		internal const string WearKey = "BA_buoy_wear";

		internal const string MarkerKey = "BA_buoy_marker";

		internal const string SunkenKey = "BA_buoy_sunken";

		internal const string StartKey = "BA_buoy_started";

		internal const string AgeKey = "BA_buoy_age";

		internal const string CatchKey = "BA_pot_savedCatch";

		internal const string LevelKey = "BA_pot_savedLevel";

		internal static readonly HideBuoyDefinition[] All = new HideBuoyDefinition[8]
		{
			new HideBuoyDefinition("Deer", "DeerHide", 5, 1),
			new HideBuoyDefinition("Troll", "TrollHide", 10, 2),
			new HideBuoyDefinition("Wolf", "WolfPelt", 15, 3),
			new HideBuoyDefinition("Lox", "LoxPelt", 30, 3),
			new HideBuoyDefinition("Gjall", "Bilebag", 60, 4),
			new HideBuoyDefinition("Asksvin", "AskHide", 90, 4),
			new HideBuoyDefinition("Seal", "SealHide", 0, 4),
			new HideBuoyDefinition("Whale", "BA_WhaleHide", 0, 3)
		};

		internal static HideBuoyDefinition Find(string name)
		{
			return All.FirstOrDefault((HideBuoyDefinition b) => b.Id == name || b.Prefab == name) ?? All[0];
		}

		internal static bool IsBuoy(ItemData item)
		{
			if ((Object)(object)item?.m_dropPrefab != (Object)null)
			{
				return All.Any((HideBuoyDefinition b) => b.Prefab == ((Object)item.m_dropPrefab).name);
			}
			return false;
		}

		internal static double Wear(ItemData item)
		{
			if (!item.m_customData.TryGetValue("BA_buoy_wear", out var value) || !double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || double.IsNaN(result) || double.IsInfinity(result))
			{
				return 0.0;
			}
			return Math.Max(0.0, result);
		}

		internal static string Type(ItemData item)
		{
			if (!IsBuoy(item))
			{
				if (!item.m_customData.TryGetValue("BA_buoy_type", out var value))
				{
					return "Deer";
				}
				return Find(value).Id;
			}
			return Find(((Object)item.m_dropPrefab).name).Id;
		}
	}
	internal sealed class MarineEncounters : IDisposable
	{
		internal const string Squid = "BetterAngling_GiantSquid";

		internal const string Whale = "BetterAngling_GiantWhale";

		internal const string Moby = "BetterAngling_MobyDick";

		internal const string Ink = "BA_SquidInk";

		internal const string Tooth = "BA_WhaleTooth";

		internal const string Clue = "BA_WhiteWhaleScrimshaw";

		internal const string Call = "BA_WhiteWhaleCall";

		internal const string Trophy = "BA_MobyTrophy";

		internal const string Blubber = "BA_WhaleBlubber";

		internal const string Beak = "BA_SquidBeak";

		private const string Ledger = "BetterAngling_OceanLedger";

		private const string RequestRpc = "BetterAngling_CallWhale";

		private const string ResponseRpc = "BetterAngling_CallReply";

		private const string WarnRpc = "BetterAngling_SeaWarning";

		private const string KillRpc = "BetterAngling_SeaKill";

		private const string PendingKey = "BetterAngling.PendingWhaleCall";

		private const string PendingId = "BetterAngling.PendingWhaleCallId";

		private const string RefundKey = "BetterAngling.WhaleCallRefund";

		internal static MarineEncounters Instance;

		internal readonly ConfigEntry<bool> WildlifeEnabled;

		private readonly ManualLogSource _log;

		private readonly Harmony _harmony;

		private readonly List<CustomItem> _items = new List<CustomItem>();

		private readonly List<Recipe> _recipes = new List<Recipe>();

		private ZRoutedRpc _rpc;

		private float _nextSpawn;

		private float _nextRetry;

		internal ConfigEntry<float> SquidSpawnBase;

		internal ConfigEntry<float> SquidSpawnMaximum;

		internal ConfigEntry<float> SquidNightMultiplier;

		internal ConfigEntry<float> SquidFarMultiplier;

		internal ConfigEntry<float> SquidOpenMultiplier;

		internal ConfigEntry<float> SquidFarDistance;

		internal ConfigEntry<float> SquidOneStar;

		internal ConfigEntry<float> SquidTwoStar;

		internal ConfigEntry<bool> WhaleDuels;

		internal ConfigEntry<float> WhaleDamage;

		internal ConfigEntry<float> WhaleSpawnBase;

		internal ConfigEntry<float> WhaleSpawnCap;

		internal ConfigEntry<float> WhaleOneStar;

		internal ConfigEntry<float> WhaleTwoStar;

		internal ConfigEntry<float> WhaleDuelChance;

		private void RegisterBuoys()
		{
			Item("BA_WhaleHide", "Whale hide", "Thick waterproof skin for a buoy that never wears out.", "SealHide", (ItemType)1, 50, 2f);
			HideBuoyDefinition[] all = BuoyCatalog.All;
			foreach (HideBuoyDefinition hideBuoyDefinition in all)
			{
				Item(hideBuoyDefinition.Prefab, hideBuoyDefinition.Id + " hide buoy", "A stitched hide float. " + ((hideBuoyDefinition.Days == 0) ? "Indefinite life." : (hideBuoyDefinition.Days + " in-game days of deployed life.")) + " Right-click to select this buoy and lower a carried lobster pot. A missing pot shows a warning and consumes nothing. Using a pot directly chooses your selected buoy, or the first usable buoy from the top-left if none is selected. Shift + right-click aboard a boat places this buoy as a sea marker. Expired floats sink and can be salvaged.", "DeerHide", (ItemType)1, 1, 2f);
				Recipe(hideBuoyDefinition.Prefab, "piece_workbench", hideBuoyDefinition.Station, hideBuoyDefinition.Material + ":2,LeatherScraps:4,Resin:2");
			}
		}

		private void RegisterCrafting()
		{
			Weapon("BA_WhaleHarpoon", "Whale-tooth harpoon", "A reinforced abyssal harpoon with an ivory point. Break a squid's crushing grip with 5/9/15 damaging hits at 0/1/2 stars using this or a boarding pike.", "SpearChitin", 30f);
			Weapon("BA_BoardingPike", "Squid-beak boarding pike", "A two-handed upward thrust for defending a deck. Break a squid's crushing grip with 5/9/15 damaging hits at 0/1/2 stars. Its attacks spare the boat you stand on.", "AtgeirBlackmetal", 85f);
			Weapon("BA_AbyssalBoardingPike", "Abyssal boarding pike", "A chitin-tipped upward thrust for early ocean expeditions. Less powerful than the colossal beak pike. Break a squid's crushing grip with 5/9/15 damaging hits at 0/1/2 stars; its thrust spares your own boat.", "AtgeirBronze", 55f);
			Item("BA_BlubberLamp", "Whaler's deck lamp", "Use aboard a boat to install. Add whale blubber at the lamp; one piece burns for 20 minutes. Alternate Use packs it when empty.", "Bronze", (ItemType)1, 1, 3f);
			Item("BA_SearedWhaleSteak", "Seared whale steak", "A rich sea hunter's feast with onions.", "SerpentStew", (ItemType)2, 20, 1f);
			Item("BA_StuffedSquid", "Barley-stuffed squid", "A sustaining meal for hauling nets and defending the deck.", "CarrotSoup", (ItemType)2, 20, 1f);
			string[] array = new string[3] { "BA_BlubberLamp", "BA_SearedWhaleSteak", "BA_StuffedSquid" };
			foreach (string text in array)
			{
				MarineCrafting.Art(PrefabManager.Instance.GetPrefab(text));
			}
			Food("BA_SearedWhaleSteak", 90f, 30f);
			Food("BA_StuffedSquid", 30f, 90f);
			Recipe("BA_WhaleHarpoon", "forge", 2, "BA_WhaleTooth:3,Chitin:20,FineWood:12,LeatherScraps:6");
			Recipe("BA_BoardingPike", "forge", 3, "BA_SquidBeak:1,BA_WhaleTooth:2,Silver:12,ElderBark:10");
			Recipe("BA_AbyssalBoardingPike", "piece_workbench", 2, "Chitin:30,FineWood:12,LeatherScraps:6");
			Recipe("BA_BlubberLamp", "forge", 2, "Bronze:4,FineWood:6,BA_WhaleTooth:1,BA_WhaleBlubber:2");
			Recipe("BA_SearedWhaleSteak", "piece_cauldron", 4, "BA_WhaleMeat:2,Onion:2,Cloudberry:2");
			Recipe("BA_StuffedSquid", "piece_cauldron", 4, "BA_SquidMeat:2,BarleyFlour:4,Thistle:2");
		}

		private void Weapon(string id, string name, string description, string template, float pierce)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Expected O, but got Unknown
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			CustomItem val = new CustomItem(id, template);
			SharedData shared = val.ItemDrop.m_itemData.m_shared;
			shared.m_name = name;
			shared.m_description = description;
			shared.m_maxQuality = 1;
			shared.m_maxDurability = 250f;
			shared.m_weight = 4f;
			shared.m_damages = new DamageTypes
			{
				m_pierce = pierce
			};
			shared.m_damagesPerLevel = default(DamageTypes);
			shared.m_attack = shared.m_attack.Clone();
			shared.m_secondaryAttack = shared.m_secondaryAttack.Clone();
			if (MarineCrafting.IsPike(id))
			{
				Attack attack = shared.m_attack;
				if (attack.m_attackChainLevels > 1)
				{
					attack.m_attackAnimation += "0";
				}
				attack.m_attackChainLevels = 1;
				attack.m_attackRange = 3.8f;
				attack.m_attackHeight = 1.25f;
				attack.m_attackAngle = 12f;
				attack.m_attackRayWidth = 0.3f;
				attack.m_maxYAngle = 70f;
				attack.m_attackStamina = ((id == "BA_AbyssalBoardingPike") ? 14 : 18);
				shared.m_secondaryAttack = attack.Clone();
			}
			MarineCrafting.Art(val.ItemPrefab);
			if (id == "BA_WhaleHarpoon")
			{
				Attack val2 = (((Object)(object)shared.m_secondaryAttack.m_attackProjectile != (Object)null) ? shared.m_secondaryAttack : shared.m_attack);
				if ((Object)(object)val2.m_attackProjectile != (Object)null)
				{
					GameObject val3 = PrefabManager.Instance.CreateClonedPrefab("BetterAngling_WhaleHarpoonProjectile", ((Object)val2.m_attackProjectile).name);
					Renderer[] componentsInChildren = val3.GetComponentsInChildren<Renderer>(true);
					for (int i = 0; i < componentsInChildren.Length; i++)
					{
						componentsInChildren[i].enabled = false;
					}
					LODGroup[] componentsInChildren2 = val3.GetComponentsInChildren<LODGroup>(true);
					for (int i = 0; i < componentsInChildren2.Length; i++)
					{
						componentsInChildren2[i].enabled = false;
					}
					Transform[] componentsInChildren3 = val.ItemPrefab.GetComponentsInChildren<Transform>(true);
					foreach (Transform val4 in componentsInChildren3)
					{
						if (((Object)val4).name == "BA_ItemArt")
						{
							GameObject obj = Object.Instantiate<GameObject>(((Component)val4).gameObject, val3.transform);
							obj.transform.localPosition = Vector3.zero;
							obj.transform.localRotation = Quaternion.identity;
							break;
						}
					}
					PrefabManager.Instance.AddPrefab(new CustomPrefab(val3, false));
					val2.m_attackProjectile = val3;
				}
			}
			if (!ItemManager.Instance.AddItem(val))
			{
				throw new Exception("Could not register " + id);
			}
			_items.Add(val);
		}

		internal MarineEncounters(ConfigFile config, ManualLogSource log)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			Instance = this;
			_log = log;
			WildlifeEnabled = config.Bind<bool>("Ocean wildlife", "Enabled", true, new ConfigDescription("Allow server-managed squid, giant whales and rare dolphins in deep Ocean water. Moby requires the rare call ritual.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			ConfigureSquid(config);
			ConfigureWhales(config);
			SquidAudio.Configure(config);
			WhaleAudio.Configure(config);
			PrefabManager.OnVanillaPrefabsAvailable += Register;
			_harmony = new Harmony("curti.valheim.betterangling.marine");
			_harmony.CreateClassProcessor(typeof(WhiteWhaleMotionPatch)).Patch();
			_harmony.CreateClassProcessor(typeof(WhiteWhaleStaggerPatch)).Patch();
			_harmony.CreateClassProcessor(typeof(MobyBoardingBalancePatch)).Patch();
			_harmony.CreateClassProcessor(typeof(WhiteWhaleDeathPatch)).Patch();
			_harmony.CreateClassProcessor(typeof(WhiteWhaleWeaknessPatch)).Patch();
			_harmony.CreateClassProcessor(typeof(WhiteWhalePhaseGatePatch)).Patch();
			_harmony.CreateClassProcessor(typeof(MobySimulationPatch)).Patch();
			_harmony.CreateClassProcessor(typeof(MobyActiveAreaPatch)).Patch();
			_harmony.CreateClassProcessor(typeof(MobySurfaceSpawnPatch)).Patch();
			Type[] array = new Type[20]
			{
				typeof(MarineAIPatch),
				typeof(MarineTurningPatch),
				typeof(MarineNativeTurnPatch),
				typeof(SwimmingKnifeEquipPatch),
				typeof(SwimmingKnifeKeepPatch),
				typeof(SwimmingKnifeAttackPatch),
				typeof(BoardingPikeAimPatch),
				typeof(BoardingPikeSweep),
				typeof(BoardingPikeRailPatch),
				typeof(BoardingPikeDeckPatch),
				typeof(BoatLampUsePatch),
				typeof(HideBuoyUsePatch),
				typeof(BuoyInventorySelectPatch),
				typeof(BuoySelectionBadgePatch),
				typeof(WhaleBreachMotionPatch),
				typeof(MobyDamagePatch),
				typeof(WhaleCallPatch),
				typeof(MobyProgressPatch),
				typeof(SquidDeathPatch),
				typeof(WhaleDeathPatch)
			};
			foreach (Type type in array)
			{
				_harmony.CreateClassProcessor(type).Patch();
			}
		}

		internal static string Prefab(MarineKind kind)
		{
			return kind switch
			{
				MarineKind.Whale => "BetterAngling_GiantWhale", 
				MarineKind.Squid => "BetterAngling_GiantSquid", 
				_ => "BetterAngling_MobyDick", 
			};
		}

		internal static string Name(MarineKind kind)
		{
			return kind switch
			{
				MarineKind.Whale => "Giant sperm whale", 
				MarineKind.Squid => "Colossal squid", 
				_ => "Moby Dick, the White Whale", 
			};
		}

		private void Register()
		{
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0293: Expected O, but got Unknown
			if (_items.Count != 0)
			{
				return;
			}
			Item("BA_SquidInk", "Abyssal squid ink", "Ink from a deep-water squid. An ingredient in the White Whale call.", "Coal", (ItemType)1, 20, 1f);
			Item("BA_WhaleTooth", "Giant whale tooth", "A great sperm whale's tooth, useful for the White Whale call.", "WolfFang", (ItemType)1, 20, 2f);
			Item("BA_WhaleBlubber", "Whale blubber", "Rich whale fat used to fuel a whaler's lamp.", "Resin", (ItemType)1, 50, 1f);
			Item("BA_SquidBeak", "Colossal squid beak", "A rare, curved abyssal beak. Its hard edge makes a formidable boarding pike.", "Chitin", (ItemType)1, 20, 2f);
			Item("BA_WhiteWhaleScrimshaw", "White Whale scrimshaw", "An exceedingly rare carving found with a giant whale. It describes a pale leviathan in Ocean water at least 40m deep. Craft a White Whale call at a level-3 forge. Prepare a strong ship and crew; he will answer deliberately.", "Chitin", (ItemType)1, 1, 1f);
			Item("BA_WhiteWhaleCall", "Call of the White Whale", "Use aboard a boat in Ocean water at least 40m deep. A maelstrom carries you, your ship and its cargo into the Drowned Court. One call opens one hunt. Single-player prototype; retreat through the golden current.", "DragonTear", (ItemType)1, 1, 1f);
			Item("BA_MobyTrophy", "Moby Dick's Tooth", "A weathered ivory tooth from the White Whale. A testament to a battle survived, but never won.\n\nThe sea remembers.", "TrophySerpent", (ItemType)13, 1, 4f);
			Item("BA_SquidMeat", "Giant squid meat", "Clean squid meat for sailor's calamari at a level-3 cauldron.", "FishRaw", (ItemType)21, 20, 1f);
			Item("BA_WhaleMeat", "Giant whale meat", "Rich whale meat for expedition stew at a level-3 cauldron.", "SerpentMeat", (ItemType)21, 20, 2f);
			Item("BA_SailorsCalamari", "Sailor's calamari", "A sustaining meal for hauling and long watches at sea.", "CarrotSoup", (ItemType)2, 20, 1f);
			Item("BA_WhaleStew", "Whaler's expedition stew", "A hearty stew made from a giant catch.", "SerpentStew", (ItemType)2, 20, 1f);
			Food("BA_SailorsCalamari", 25f, 75f);
			Food("BA_WhaleStew", 80f, 27f);
			Recipe("BA_WhiteWhaleCall", "forge", 3, "BA_WhiteWhaleScrimshaw:1,BA_WhaleTooth:4,BA_SquidInk:4,Silver:10");
			Recipe("BA_SailorsCalamari", "piece_cauldron", 3, "BA_SquidMeat:2,Onion:1,Thistle:1");
			Recipe("BA_WhaleStew", "piece_cauldron", 3, "BA_WhaleMeat:2,Onion:2,Thistle:1");
			RegisterCrafting();
			RegisterBuoys();
			foreach (MarineKind value in Enum.GetValues(typeof(MarineKind)))
			{
				Creature(value);
			}
			GameObject val = PrefabManager.Instance.CreateEmptyPrefab("BetterAngling_OceanLedger", true);
			val.GetComponent<Renderer>().enabled = false;
			val.GetComponent<Collider>().enabled = false;
			val.GetComponent<ZNetView>().m_persistent = true;
			val.GetComponent<ZNetView>().m_distant = true;
			PrefabManager.Instance.AddPrefab(new CustomPrefab(val, false));
			_log.LogInfo((object)"Registered original squid/whale models, dedicated Moby encounter and marine rewards.");
		}

		private void Item(string prefab, string name, string description, string template, ItemType type, int stack, float weight)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			CustomItem val = new CustomItem(prefab, template);
			SharedData shared = val.ItemDrop.m_itemData.m_shared;
			shared.m_name = name;
			shared.m_description = description;
			shared.m_itemType = type;
			shared.m_maxStackSize = stack;
			shared.m_weight = weight;
			shared.m_maxQuality = 1;
			shared.m_value = 0;
			shared.m_ammoType = "";
			shared.m_useDurability = false;
			shared.m_consumeStatusEffect = null;
			shared.m_equipStatusEffect = null;
			if ((Object)(object)val.ItemPrefab.GetComponent<Rigidbody>() != (Object)null && (Object)(object)val.ItemPrefab.GetComponent<Floating>() == (Object)null)
			{
				val.ItemPrefab.AddComponent<Floating>();
			}
			if (MarineItemArt.HasArt(prefab))
			{
				MarineItemArt.Apply(val.ItemPrefab);
			}
			if (UtilityItemArt.HasArt(prefab))
			{
				UtilityItemArt.Apply(val.ItemPrefab);
			}
			if (FoodItemArt.HasArt(prefab))
			{
				FoodItemArt.Apply(val.ItemPrefab);
			}
			if (!ItemManager.Instance.AddItem(val))
			{
				throw new Exception("Could not register " + prefab);
			}
			_items.Add(val);
		}

		private void Food(string prefab, float health, float stamina)
		{
			SharedData shared = _items.Single((CustomItem i) => ((Object)i.ItemPrefab).name == prefab).ItemDrop.m_itemData.m_shared;
			shared.m_food = health;
			shared.m_foodStamina = stamina;
			shared.m_foodEitr = 0f;
			shared.m_foodBurnTime = 1800f;
			shared.m_foodRegen = 3f;
		}

		private void Recipe(string prefab, string station, int level, string costs)
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Expected O, but got Unknown
			if (!IngredientParser.TryParse(costs, (string n) => (Object)(object)PrefabManager.Instance.GetPrefab(n) != (Object)null, out var ingredients, out var error))
			{
				throw new Exception(error);
			}
			Recipe val = ScriptableObject.CreateInstance<Recipe>();
			((Object)val).name = "Recipe_" + prefab;
			val.m_item = _items.Single((CustomItem i) => ((Object)i.ItemPrefab).name == prefab).ItemDrop;
			val.m_amount = 1;
			val.m_enabled = true;
			val.m_minStationLevel = level;
			val.m_craftingStation = PrefabManager.Instance.GetPrefab(station).GetComponent<CraftingStation>();
			val.m_resources = ((IEnumerable<Ingredient>)ingredients).Select((Func<Ingredient, Requirement>)((Ingredient c) => new Requirement
			{
				m_resItem = PrefabManager.Instance.GetPrefab(c.Item).GetComponent<ItemDrop>(),
				m_amount = c.Amount
			})).ToArray();
			if (!ItemManager.Instance.AddRecipe(new CustomRecipe(val, false, false)))
			{
				throw new Exception("Could not register " + ((Object)val).name);
			}
			_recipes.Add(val);
		}

		private void Creature(MarineKind kind)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Expected O, but got Unknown
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Expected O, but got Unknown
			GameObject val = PrefabManager.Instance.CreateClonedPrefab(Prefab(kind), "Serpent");
			val.transform.localScale = Vector3.one;
			Renderer[] componentsInChildren = val.GetComponentsInChildren<Renderer>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				componentsInChildren[i].enabled = false;
			}
			LODGroup[] componentsInChildren2 = val.GetComponentsInChildren<LODGroup>(true);
			for (int i = 0; i < componentsInChildren2.Length; i++)
			{
				componentsInChildren2[i].enabled = false;
			}
			Collider[] componentsInChildren3 = val.GetComponentsInChildren<Collider>(true);
			foreach (Collider val2 in componentsInChildren3)
			{
				if ((Object)(object)((Component)val2).gameObject != (Object)(object)val)
				{
					val2.enabled = false;
				}
			}
			Character component = val.GetComponent<Character>();
			component.m_name = Name(kind);
			component.m_health = MarineRules.Health(kind);
			component.m_group = "BetterAnglingOcean";
			component.m_boss = kind == MarineKind.Moby;
			component.m_bossOrder = ((kind == MarineKind.Moby) ? 99 : 0);
			component.m_bossEvent = "";
			component.m_defeatSetGlobalKey = ((kind == MarineKind.Moby) ? "defeated_ba_moby" : "");
			component.m_dontHideBossHud = kind == MarineKind.Moby;
			component.m_faction = (Faction)((kind == MarineKind.Whale) ? 1 : 6);
			if (kind == MarineKind.Whale)
			{
				component.m_damageModifiers.m_poison = (DamageModifier)3;
			}
			component.m_swimDepth = ((kind == MarineKind.Squid) ? 3f : 1.5f);
			component.m_swimSpeed = 3f;
			component.m_swimTurnSpeed = 30f;
			component.m_deathEffects = new EffectList();
			CapsuleCollider component2 = val.GetComponent<CapsuleCollider>();
			component2.direction = 2;
			component2.radius = kind switch
			{
				MarineKind.Moby => 3f, 
				MarineKind.Squid => 1.1f, 
				_ => 2.1f, 
			};
			component2.height = MarineRules.Length(kind);
			component2.center = Vector3.zero;
			if (kind == MarineKind.Squid)
			{
				component2.radius = 3.075f;
				component2.height = 14.1f;
				component2.center = new Vector3(0f, 0f, -4f);
			}
			if (kind == MarineKind.Whale)
			{
				component2.radius = 2.65f;
				component2.height = 19.8f;
				component2.center = new Vector3(0f, 0f, 0.3f);
			}
			val.GetComponent<Rigidbody>().mass = kind switch
			{
				MarineKind.Moby => 2200, 
				MarineKind.Squid => 250, 
				_ => 1500, 
			};
			val.GetComponent<MonsterAI>().m_attackPlayerObjects = false;
			ZNetView component3 = val.GetComponent<ZNetView>();
			component3.m_persistent = true;
			component3.m_distant = true;
			CharacterDrop component4 = val.GetComponent<CharacterDrop>();
			component4.m_drops = new List<Drop>();
			component4.m_spawnOffset = Vector3.up * 3.5f;
			switch (kind)
			{
			case MarineKind.Squid:
				Drop(component4, "BA_SquidMeat", 3, 5);
				Drop(component4, "BA_SquidInk", 1, 3);
				Drop(component4, "BA_SquidBeak", 1, 1, 0.4f);
				break;
			case MarineKind.Whale:
				Drop(component4, "BA_WhaleMeat", 6, 10);
				Drop(component4, "BA_WhaleTooth", 2, 4);
				Drop(component4, "BA_WhiteWhaleScrimshaw", 1, 1, 0.005f);
				Drop(component4, "BA_WhaleBlubber", 4, 8);
				Drop(component4, "BA_WhaleHide", 2, 4);
				break;
			}
			val.AddComponent<MarineCreature>().Species = (int)kind;
			val.AddComponent<MarineVisual>().Species = (int)kind;
			if (kind == MarineKind.Moby)
			{
				val.AddComponent<WhiteWhaleBoss>();
				component.m_health = 30000f;
				component.m_damageModifiers.m_poison = (DamageModifier)3;
				component2.radius = 5.875f;
				component2.height = 50f;
				val.GetComponent<Rigidbody>().mass = 5000f * Mathf.Pow(1.25f, 3f);
				component4.m_drops.Clear();
				WhaleAudio.PreparePrefab(val);
			}
			if (kind == MarineKind.Squid)
			{
				val.AddComponent<SquidHitboxes>();
			}
			if (kind == MarineKind.Squid)
			{
				SquidAudio.PreparePrefab(val);
			}
			if (kind == MarineKind.Whale)
			{
				WhaleAudio.PreparePrefab(val);
			}
			PrefabManager.Instance.AddPrefab(new CustomPrefab(val, false));
		}

		private static void Drop(CharacterDrop drop, string prefab, int min, int max, float chance = 1f)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			drop.m_drops.Add(new Drop
			{
				m_prefab = PrefabManager.Instance.GetPrefab(prefab),
				m_amountMin = min,
				m_amountMax = max,
				m_chance = chance,
				m_levelMultiplier = false,
				m_dontScale = true
			});
		}

		internal static bool ValidHabitat(MarineKind kind, Vector3 position)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			WorldGenerator instance = WorldGenerator.instance;
			if (instance == null)
			{
				return false;
			}
			WaterVolume val = null;
			float waterLevel = Floating.GetWaterLevel(position, ref val);
			return MarineRules.Habitat(kind, (int)instance.GetBiome(position) == 256, waterLevel - instance.GetHeight(position));
		}

		internal void Update()
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null)
			{
				return;
			}
			BindRpc();
			RetryPending();
			if (!ZNet.instance.IsServer() || !WildlifeEnabled.Value || Time.time < _nextSpawn || ZDOMan.instance == null)
			{
				return;
			}
			_nextSpawn = Time.time + 90f;
			if (ZDOMan.instance.m_objectsByID.Values.Count((ZDO z) => z.GetPrefab() == StringExtensionMethods.GetStableHashCode("BetterAngling_GiantSquid") || z.GetPrefab() == StringExtensionMethods.GetStableHashCode("BetterAngling_GiantWhale")) >= 4)
			{
				return;
			}
			Ship ship = (from s in MarineCreature.Ships()
				orderby Random.value
				select s).FirstOrDefault();
			if ((Object)(object)ship == (Object)null || TrySharedEncounter(((Component)ship).transform.position, Random.value))
			{
				return;
			}
			MarineKind marineKind = ((!(Random.value < 0.45f)) ? MarineKind.Whale : MarineKind.Squid);
			if (marineKind == MarineKind.Squid && ZDOMan.instance.m_objectsByID.Values.Count((ZDO z) => z.GetPrefab() == StringExtensionMethods.GetStableHashCode("BetterAngling_GiantSquid") && Vector3.Distance(z.GetPosition(), ((Component)ship).transform.position) < 250f) >= 2)
			{
				return;
			}
			for (int num = 0; num < 5; num++)
			{
				Vector2 insideUnitCircle = Random.insideUnitCircle;
				Vector2 val = ((Vector2)(ref insideUnitCircle)).normalized * 80f;
				Vector3 val2 = ((Component)ship).transform.position + new Vector3(val.x, 0f, val.y);
				if (ValidHabitat(marineKind, val2))
				{
					if (marineKind == MarineKind.Squid)
					{
						TrySpawnSquid(val2, EnvMan.IsNight(), Random.value, Random.value);
					}
					else
					{
						TrySpawnWhale(val2, EnvMan.IsNight(), Random.value, Random.value);
					}
					break;
				}
			}
		}

		private void BindRpc()
		{
			if (_rpc != ZRoutedRpc.instance)
			{
				_rpc = ZRoutedRpc.instance;
				_rpc.Register<ZDOID, ZDOID, string>("BetterAngling_CallWhale", (Action<long, ZDOID, ZDOID, string>)OnCall);
				_rpc.Register<string, bool, string>("BetterAngling_CallReply", (Action<long, string, bool, string>)OnReply);
				_rpc.Register<Vector3, string>("BetterAngling_SeaWarning", (Action<long, Vector3, string>)OnWarning);
				_rpc.Register<ZPackage>("BetterAngling_SeaKill", (Action<long, ZPackage>)OnKill);
			}
		}

		internal bool RequestCall(Player player, ItemData item)
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			if (MobyRealm.Instance != null)
			{
				return MobyRealm.Instance.Summon(player, item);
			}
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer) && !((Character)player).IsDead() && !((Character)player).IsTeleporting())
			{
				object obj;
				if (item == null)
				{
					obj = null;
				}
				else
				{
					GameObject dropPrefab = item.m_dropPrefab;
					obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
				}
				if (!((string?)obj != "BA_WhiteWhaleCall") && ((Humanoid)player).GetInventory().ContainsItem(item))
				{
					BoatFishHold boatFishHold = BoatFishHold.Aboard(player);
					if ((Object)(object)boatFishHold == (Object)null || !ValidHabitat(MarineKind.Moby, ((Component)boatFishHold).transform.position))
					{
						LobsterFishing.Say(player, "Call the White Whale from aboard a boat in Ocean water at least 40m deep.");
						return false;
					}
					if (player.m_customData.ContainsKey("BetterAngling.PendingWhaleCall"))
					{
						LobsterFishing.Say(player, "A whale call is awaiting a server response or item refund.");
						return false;
					}
					BindRpc();
					string value = EquipmentRecord.Encode(item);
					string value2 = Guid.NewGuid().ToString("N");
					if (!((Humanoid)player).GetInventory().RemoveItem(item, 1))
					{
						return false;
					}
					player.m_customData["BetterAngling.PendingWhaleCall"] = value;
					player.m_customData["BetterAngling.PendingWhaleCallId"] = value2;
					_nextRetry = 0f;
					RetryPending();
					return true;
				}
			}
			return false;
		}

		private void RetryPending()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || !localPlayer.m_customData.ContainsKey("BetterAngling.PendingWhaleCall") || Time.time < _nextRetry)
			{
				return;
			}
			_nextRetry = Time.time + 5f;
			if (localPlayer.m_customData.ContainsKey("BetterAngling.WhaleCallRefund"))
			{
				Refund(localPlayer);
				return;
			}
			BoatFishHold boatFishHold = BoatFishHold.Aboard(localPlayer);
			if (!((Object)(object)boatFishHold == (Object)null))
			{
				_rpc.InvokeRoutedRPC("BetterAngling_CallWhale", new object[3]
				{
					((Character)localPlayer).GetZDOID(),
					boatFishHold.View.GetZDO().m_uid,
					localPlayer.m_customData["BetterAngling.PendingWhaleCallId"]
				});
			}
		}

		private ZDO LedgerData()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = ((IEnumerable<ZDO>)ZDOMan.instance.m_objectsByID.Values).FirstOrDefault((Func<ZDO, bool>)((ZDO z) => z.GetPrefab() == StringExtensionMethods.GetStableHashCode("BetterAngling_OceanLedger")));
			if (val != null)
			{
				if (!val.IsOwner())
				{
					val.SetOwner(ZDOMan.GetSessionID());
				}
				return val;
			}
			return Object.Instantiate<GameObject>(PrefabManager.Instance.GetPrefab("BetterAngling_OceanLedger"), Vector3.zero, Quaternion.identity).GetComponent<ZNetView>().GetZDO();
		}

		private void OnCall(long sender, ZDOID playerId, ZDOID shipId, string request)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0237: Unknown result type (might be due to invalid IL or missing references)
			if (!ZNet.instance.IsServer() || request == null || request.Length != 32)
			{
				return;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(playerId);
			ZDO zDO2 = ZDOMan.instance.GetZDO(shipId);
			if (zDO == null || zDO.GetOwner() != sender || zDO2 == null)
			{
				return;
			}
			ZDO val = LedgerData();
			string text = val.GetString("BA_calls_accepted", "");
			if (text.Split(',').Contains(request))
			{
				Reply(sender, request, accepted: true, "The White Whale has answered this call.");
				return;
			}
			if (text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries).Length >= 8192)
			{
				Reply(sender, request, accepted: false, "This world's hunt ledger is full. Your call is returned.");
				return;
			}
			GameObject val2 = ZNetScene.instance.FindInstance(shipId);
			Ship val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<Ship>() : null);
			if ((Object)(object)val3 == (Object)null || Vector3.Distance(zDO.GetPosition(), ((Component)val3).transform.position) > 20f || !ValidHabitat(MarineKind.Moby, ((Component)val3).transform.position))
			{
				Reply(sender, request, accepted: false, "The boat must remain in deep Ocean water while the call is answered.");
				return;
			}
			if (ZDOMan.instance.m_objectsByID.Values.Any((ZDO z) => z.GetPrefab() == StringExtensionMethods.GetStableHashCode("BetterAngling_MobyDick")))
			{
				Reply(sender, request, accepted: false, "A White Whale hunt is already active. Your call is returned.");
				return;
			}
			if (val.GetLong("BA_nextWhaleCall", 0L) > ZNet.instance.GetTime().Ticks)
			{
				Reply(sender, request, accepted: false, "The White Whale will not answer again yet. The world cooldown is 30 minutes; your call is returned.");
				return;
			}
			Vector3 val4 = ((Component)val3).transform.position + ((Component)val3).transform.forward * 65f;
			if (!ValidHabitat(MarineKind.Moby, val4))
			{
				Reply(sender, request, accepted: false, "There is not enough deep water ahead of the boat. Your call is returned.");
				return;
			}
			WaterVolume val5 = null;
			val4.y = Floating.GetWaterLevel(val4, ref val5) - 1.5f;
			MarineCreature component = Object.Instantiate<GameObject>(PrefabManager.Instance.GetPrefab("BetterAngling_MobyDick"), val4, Quaternion.identity).GetComponent<MarineCreature>();
			if (component?.Data == null)
			{
				Reply(sender, request, accepted: false, "The hunt could not begin. Your call is returned.");
				return;
			}
			component.InitializeEncounter(shipId, ((Component)val3).transform.position);
			List<string> list = text.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
			list.Add(request);
			val.Set("BA_calls_accepted", string.Join(",", list));
			val.Set("BA_nextWhaleCall", ZNet.instance.GetTime().AddSeconds(1800.0).Ticks);
			Reply(sender, request, accepted: true, "Moby Dick has answered. Keep sea room; heed the charge warnings.");
		}

		private void Reply(long sender, string request, bool accepted, string text)
		{
			_rpc.InvokeRoutedRPC(sender, "BetterAngling_CallReply", new object[3] { request, accepted, text });
		}

		private void OnReply(long sender, string request, bool accepted, string text)
		{
			if (_rpc == null || sender != _rpc.GetServerPeerID())
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && localPlayer.m_customData.TryGetValue("BetterAngling.PendingWhaleCallId", out var value) && !(value != request))
			{
				if (accepted)
				{
					ClearPending(localPlayer);
				}
				else
				{
					localPlayer.m_customData["BetterAngling.WhaleCallRefund"] = "1";
					Refund(localPlayer);
				}
				LobsterFishing.Say(localPlayer, text);
			}
		}

		private static void ClearPending(Player player)
		{
			player.m_customData.Remove("BetterAngling.PendingWhaleCall");
			player.m_customData.Remove("BetterAngling.PendingWhaleCallId");
			player.m_customData.Remove("BetterAngling.WhaleCallRefund");
		}

		private static void Refund(Player player)
		{
			if (EquipmentRecord.TryDecode(player.m_customData["BetterAngling.PendingWhaleCall"], (ItemData i) => ((Object)i.m_dropPrefab).name == "BA_WhiteWhaleCall", out var item, out var _))
			{
				TackleStorage.InsertPlan insertPlan = TackleStorage.PlanInsert(((Humanoid)player).GetInventory(), item, 1);
				if (insertPlan == null)
				{
					LobsterFishing.Say(player, "Make one inventory slot for your refunded whale call.");
					return;
				}
				ClearPending(player);
				insertPlan.Apply(((Humanoid)player).GetInventory());
				((Humanoid)player).GetInventory().Changed(false, false);
			}
		}

		internal void WarnNearby(Vector3 position, string text)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (ZRoutedRpc.instance != null)
			{
				BindRpc();
				_rpc.InvokeRoutedRPC(0L, "BetterAngling_SeaWarning", new object[2] { position, text });
			}
		}

		private void OnWarning(long sender, Vector3 position, string text)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, position) < 250f)
			{
				LobsterFishing.Say(localPlayer, text);
			}
		}

		internal void NotifyKill(MarineKind kind, Vector3 position, ZDOID id, string attackers)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Expected O, but got Unknown
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (ZRoutedRpc.instance != null)
			{
				BindRpc();
				ZPackage val = new ZPackage();
				val.Write((int)kind);
				val.Write(position);
				val.Write(id);
				val.Write(attackers);
				_rpc.InvokeRoutedRPC(0L, "BetterAngling_SeaKill", new object[1] { val });
			}
		}

		private unsafe void OnKill(long sender, ZPackage package)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			MarineKind marineKind = (MarineKind)package.ReadInt();
			Vector3 val = package.ReadVector3();
			ZDOID val2 = package.ReadZDOID();
			string text = package.ReadString();
			if (Enum.IsDefined(typeof(MarineKind), marineKind) && !(Vector3.Distance(((Component)localPlayer).transform.position, val) > 200f) && text.Split(',').Contains(localPlayer.GetPlayerID().ToString()))
			{
				string species = Prefab(marineKind);
				string name = Name(marineKind);
				int level = marineKind switch
				{
					MarineKind.Whale => 3, 
					MarineKind.Moby => 5, 
					_ => 2, 
				};
				ZDOID val3 = val2;
				if (CatchJournal.Record(localPlayer, species, name, level, 0f, 1, "Hunt", "hunt:" + ((object)(*(ZDOID*)(&val3))/*cast due to .constrained prefix*/).ToString(), val))
				{
					((Character)localPlayer).RaiseSkill((SkillType)104, (float)((marineKind == MarineKind.Moby) ? 15 : 2));
				}
			}
		}

		internal bool ValidateRegistered()
		{
			bool flag = _items.Count == 26 && _recipes.Count == 17 && _items.All((CustomItem i) => (Object)(object)ObjectDB.instance.GetItemPrefab(((Object)i.ItemPrefab).name) == (Object)(object)i.ItemPrefab) && _recipes.All((Recipe r) => ObjectDB.instance.m_recipes.Contains(r));
			IEnumerator enumerator = Enum.GetValues(typeof(MarineKind)).GetEnumerator();
			try
			{
				bool num;
				int num2;
				for (; enumerator.MoveNext(); flag = (byte)((num ? 1u : 0u) & (uint)num2) != 0)
				{
					MarineKind marineKind = (MarineKind)enumerator.Current;
					GameObject prefab = PrefabManager.Instance.GetPrefab(Prefab(marineKind));
					num = flag;
					if ((Object)(object)prefab != (Object)null && prefab.activeSelf)
					{
						MarineCreature component = prefab.GetComponent<MarineCreature>();
						if (component != null && component.Kind == marineKind && (Object)(object)prefab.GetComponent<MarineVisual>() != (Object)null)
						{
							num2 = (prefab.GetComponent<CharacterDrop>().m_drops.All((Drop d) => (Object)(object)d.m_prefab != (Object)null) ? 1 : 0);
							continue;
						}
					}
					num2 = 0;
				}
			}
			finally
			{
				IDisposable disposable = enumerator as IDisposable;
				if (disposable != null)
				{
					disposable.Dispose();
				}
			}
			_log.LogInfo((object)("Marine encounter registration: " + flag));
			return flag;
		}

		public void Dispose()
		{
			SquidAudio.DisposeClips();
			WhaleAudio.DisposeClips();
			EncounterBells.DisposeClip();
			PrefabManager.OnVanillaPrefabsAvailable -= Register;
			_harmony.UnpatchSelf();
			if (_rpc != null)
			{
				string[] array = new string[4] { "BetterAngling_CallWhale", "BetterAngling_CallReply", "BetterAngling_SeaWarning", "BetterAngling_SeaKill" };
				foreach (string text in array)
				{
					_rpc.m_functions.Remove(StringExtensionMethods.GetStableHashCode(text));
				}
			}
			if (Instance == this)
			{
				Instance = null;
			}
		}

		private void ConfigureSquid(ConfigFile config)
		{
			SquidSpawnBase = SquidSetting(config, "Base spawn chance", 0.12f, 0f, 1f, "Chance per eligible squid check before ocean conditions. A server checks wildlife every 90 seconds and selects squid 45% of the time.");
			SquidSpawnMaximum = SquidSetting(config, "Maximum spawn chance", 0.5f, 0f, 1f, "Caps the adjusted chance per eligible squid check. Existing global population cap still applies.");
			SquidNightMultiplier = SquidSetting(config, "Night multiplier", 1.6f, 1f, 4f, "Encounter multiplier at night.");
			SquidFarMultiplier = SquidSetting(config, "Far ocean multiplier", 2f, 0.5f, 4f, "Encounter multiplier at the configured distance from the starting stones. Near the start it is 0.5.");
			SquidFarDistance = SquidSetting(config, "Far ocean distance", 6000f, 500f, 10000f, "Metres from the starting stones at which distance reaches maximum influence.");
			SquidOpenMultiplier = SquidSetting(config, "Open ocean multiplier", 1.5f, 0.5f, 4f, "Encounter multiplier with clear deep Ocean in all sampled directions. Enclosed water starts at 0.5.");
			SquidOneStar = SquidSetting(config, "Maximum one star chance", 0.28f, 0f, 0.6f, "One-star probability at highest risk; one quarter of this near the calmest waters. Separate from two-star chance.");
			SquidTwoStar = SquidSetting(config, "Maximum two star chance", 0.1f, 0f, 0.4f, "Two-star probability at highest risk; one quarter of this near the calmest waters. No natural levels above two stars.");
		}

		private static ConfigEntry<float> SquidSetting(ConfigFile config, string name, float value, float minimum, float maximum, string description)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			return config.Bind<float>("Colossal squid", name, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(minimum, maximum), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}

		internal static float OpenOceanScore(Vector3 point)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing referenc