Decompiled source of Behold Pirates v0.10.2

BepInEx/plugins/Behold.Pirates/Behold.Pirates.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Connection;
using FishNet.Managing;
using FishNet.Managing.Object;
using FishNet.Object;
using FishNet.Observing;
using HarmonyLib;
using HowToFish.OpenSea.Pirates;
using Microsoft.CodeAnalysis;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Behold")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Adds hostile pirate fleets to the open ocean in How to Fish.")]
[assembly: AssemblyFileVersion("0.10.2.0")]
[assembly: AssemblyInformationalVersion("0.10.2+8a654bc991c15eb866a0717277930c97c5c304ad")]
[assembly: AssemblyProduct("Behold: Pirates")]
[assembly: AssemblyTitle("Behold: Pirates")]
[assembly: AssemblyVersion("0.10.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace HowToFish.OpenSea
{
	internal sealed class OpenSeaBoundary : MonoBehaviour
	{
		private const int ShorelineSampleCount = 120;

		private const float MinimumScanRadius = 500f;

		private const float ScanRadiusMultiplier = 4f;

		private const float MinimumValidShoreRadius = 1f;

		private const float MinimumValidSampleRatio = 0.5f;

		private static readonly float[] ProbeHeightsAboveWater = new float[3] { 0.25f, 1f, 2f };

		private ConfigEntry<float> _distanceBeyondIsland;

		private Island? _sampledIsland;

		private Vector3 _islandPosition;

		private float _authoredIslandRadius;

		private float[] _shoreRadii = Array.Empty<float>();

		private int _revision;

		public int Revision
		{
			get
			{
				EnsureCurrentIsland();
				return _revision;
			}
		}

		public int SampleCount
		{
			get
			{
				EnsureCurrentIsland();
				return _shoreRadii.Length;
			}
		}

		public float DistanceBeyondIsland => Mathf.Max(0f, _distanceBeyondIsland.Value);

		public void Initialize(ConfigEntry<float> distanceBeyondIsland)
		{
			_distanceBeyondIsland = distanceBeyondIsland;
		}

		private void Update()
		{
			EnsureCurrentIsland();
		}

		public float GetDistanceFromShoreline(Vector3 position)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			EnsureCurrentIsland();
			Vector3 offset = position - _islandPosition;
			offset.y = 0f;
			return ((Vector3)(ref offset)).magnitude - GetShoreRadius(offset);
		}

		public bool IsBeyondBoundary(Vector3 position, float distanceBeyondShoreline)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return GetDistanceFromShoreline(position) > Mathf.Max(0f, distanceBeyondShoreline);
		}

		public Vector3 GetBoundaryPoint(int sampleIndex, float distanceBeyondShoreline)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			EnsureCurrentIsland();
			if (_shoreRadii.Length == 0)
			{
				return _islandPosition;
			}
			int num = (sampleIndex % _shoreRadii.Length + _shoreRadii.Length) % _shoreRadii.Length;
			float num2 = MathF.PI * 2f * (float)num / (float)_shoreRadii.Length;
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(Mathf.Cos(num2), 0f, Mathf.Sin(num2));
			float num3 = _shoreRadii[num] + Mathf.Max(0f, distanceBeyondShoreline);
			return _islandPosition + val * num3;
		}

		private void EnsureCurrentIsland()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			if (IslandManager.IsLoading || !Object.op_Implicit((Object)(object)Island.CurIsland))
			{
				Clear();
				return;
			}
			Island curIsland = Island.CurIsland;
			if (!((Object)(object)_sampledIsland == (Object)(object)curIsland) || !(HorizontalSqrDistance(_islandPosition, Island.IslandPos) <= 0.01f) || !Mathf.Approximately(_authoredIslandRadius, Island.IslandSize))
			{
				SampleShoreline(curIsland);
			}
		}

		private void SampleShoreline(Island island)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			_sampledIsland = island;
			_islandPosition = Island.IslandPos;
			_authoredIslandRadius = Mathf.Max(1f, Island.IslandSize);
			_shoreRadii = new float[120];
			bool[] array = new bool[120];
			float scanRadius = Mathf.Max(500f, _authoredIslandRadius * 4f);
			float waterHeight = WaterManager.WaterHeight;
			int num = 0;
			float num2 = float.PositiveInfinity;
			float num3 = 0f;
			Physics.SyncTransforms();
			Vector3 outward = default(Vector3);
			for (int i = 0; i < 120; i++)
			{
				float num4 = MathF.PI * 2f * (float)i / 120f;
				((Vector3)(ref outward))..ctor(Mathf.Cos(num4), 0f, Mathf.Sin(num4));
				float num5 = FindOutermostShoreRadius(outward, scanRadius, waterHeight);
				if (num5 >= 1f)
				{
					array[i] = true;
					num++;
					num2 = Mathf.Min(num2, num5);
					num3 = Mathf.Max(num3, num5);
					_shoreRadii[i] = num5;
				}
				else
				{
					_shoreRadii[i] = _authoredIslandRadius;
				}
			}
			if (num < Mathf.CeilToInt(60f))
			{
				for (int j = 0; j < _shoreRadii.Length; j++)
				{
					_shoreRadii[j] = _authoredIslandRadius;
				}
				Plugin.Log.LogWarning((object)($"Only {num}/{120} shoreline directions hit level geometry; " + $"using the authored {_authoredIslandRadius:0.#}m island radius as the open-sea boundary fallback."));
			}
			else
			{
				ReplaceMissingSamples(array);
				Plugin.Log.LogInfo((object)($"Sampled the island shoreline in {num}/{120} directions " + $"with radii from {num2:0.#}m to {num3:0.#}m."));
			}
			_revision++;
		}

		private float FindOutermostShoreRadius(Vector3 outward, float scanRadius, float waterHeight)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			float num = -1f;
			float[] probeHeightsAboveWater = ProbeHeightsAboveWater;
			RaycastHit val2 = default(RaycastHit);
			foreach (float num2 in probeHeightsAboveWater)
			{
				Vector3 val = _islandPosition + outward * scanRadius;
				val.y = waterHeight + num2;
				if (Physics.Raycast(val, -outward, ref val2, scanRadius, LayerMask.op_Implicit(GameInfo.LevelLayer), (QueryTriggerInteraction)1))
				{
					Vector3 val3 = ((RaycastHit)(ref val2)).point - _islandPosition;
					val3.y = 0f;
					float num3 = Vector3.Dot(val3, outward);
					num = Mathf.Max(num, num3);
				}
			}
			return num;
		}

		private void ReplaceMissingSamples(bool[] validDirections)
		{
			for (int i = 0; i < _shoreRadii.Length; i++)
			{
				if (!validDirections[i])
				{
					int num = FindValidNeighbor(i, -1, validDirections);
					int num2 = FindValidNeighbor(i, 1, validDirections);
					if (num != i && num2 != i)
					{
						int num3 = (num2 - num + _shoreRadii.Length) % _shoreRadii.Length;
						int num4 = (i - num + _shoreRadii.Length) % _shoreRadii.Length;
						_shoreRadii[i] = Mathf.Lerp(_shoreRadii[num], _shoreRadii[num2], (float)num4 / (float)num3);
					}
				}
			}
		}

		private int FindValidNeighbor(int startIndex, int step, bool[] validDirections)
		{
			int num = startIndex;
			for (int i = 0; i < _shoreRadii.Length; i++)
			{
				num = (num + step + _shoreRadii.Length) % _shoreRadii.Length;
				if (validDirections[num])
				{
					return num;
				}
			}
			return startIndex;
		}

		private float GetShoreRadius(Vector3 offset)
		{
			//IL_001e: 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)
			if (_shoreRadii.Length == 0 || ((Vector3)(ref offset)).sqrMagnitude <= 0.0001f)
			{
				return _authoredIslandRadius;
			}
			float num = Mathf.Atan2(offset.z, offset.x);
			if (num < 0f)
			{
				num += MathF.PI * 2f;
			}
			float num2 = num * (float)_shoreRadii.Length / (MathF.PI * 2f);
			int num3 = Mathf.FloorToInt(num2) % _shoreRadii.Length;
			int num4 = (num3 + 1) % _shoreRadii.Length;
			return Mathf.Lerp(_shoreRadii[num3], _shoreRadii[num4], num2 - (float)num3);
		}

		private void Clear()
		{
			if (Object.op_Implicit((Object)(object)_sampledIsland) || _shoreRadii.Length != 0)
			{
				_sampledIsland = null;
				_shoreRadii = Array.Empty<float>();
				_authoredIslandRadius = 0f;
				_revision++;
			}
		}

		private static float HorizontalSqrDistance(Vector3 first, Vector3 second)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			float num = first.x - second.x;
			float num2 = first.z - second.z;
			return num * num + num2 * num2;
		}
	}
	internal sealed class OpenSeaBoundaryMarkers : MonoBehaviour
	{
		private const float MarkerSpacing = 10f;

		private const float MarkerDiameter = 0.6f;

		private const string MarkerShaderName = "Universal Render Pipeline/Lit";

		private static readonly int BaseColorId = Shader.PropertyToID("_BaseColor");

		private static readonly Color MarkerColor = Color32.op_Implicit(new Color32(byte.MaxValue, (byte)60, (byte)12, byte.MaxValue));

		private readonly List<Transform> _markers = new List<Transform>();

		private OpenSeaBoundary _openSeaBoundary;

		private GameObject? _markerRoot;

		private Material? _markerMaterial;

		private Mesh? _markerMesh;

		private int _boundaryRevision = -1;

		private float _boundaryOffset = -1f;

		public void Initialize(OpenSeaBoundary openSeaBoundary)
		{
			_openSeaBoundary = openSeaBoundary;
		}

		private void Update()
		{
			if (IslandManager.IsLoading || !Object.op_Implicit((Object)(object)Island.CurIsland))
			{
				ClearMarkers();
				return;
			}
			int revision = _openSeaBoundary.Revision;
			float distanceBeyondIsland = _openSeaBoundary.DistanceBeyondIsland;
			if (!Object.op_Implicit((Object)(object)_markerRoot) || revision != _boundaryRevision || !Mathf.Approximately(distanceBeyondIsland, _boundaryOffset))
			{
				CreateMarkers(revision, distanceBeyondIsland);
			}
			UpdateWaterHeights();
		}

		private void CreateMarkers(int boundaryRevision, float boundaryOffset)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: 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_016b: Unknown result type (might be due to invalid IL or missing references)
			ClearMarkers();
			_boundaryRevision = boundaryRevision;
			_boundaryOffset = boundaryOffset;
			_markerRoot = new GameObject("Open Sea Boundary Markers");
			_markerRoot.transform.SetParent(((Component)this).transform, false);
			int sampleCount = _openSeaBoundary.SampleCount;
			if (sampleCount == 0)
			{
				return;
			}
			Vector3[] array = (Vector3[])(object)new Vector3[sampleCount];
			float[] array2 = new float[sampleCount];
			float num = 0f;
			for (int i = 0; i < _openSeaBoundary.SampleCount; i++)
			{
				array[i] = _openSeaBoundary.GetBoundaryPoint(i, boundaryOffset);
			}
			for (int j = 0; j < sampleCount; j++)
			{
				num += (array2[j] = Vector3.Distance(array[j], array[(j + 1) % sampleCount]));
			}
			if (num <= Mathf.Epsilon)
			{
				CreateMarker(0, array[0]);
				return;
			}
			int num2 = Mathf.Max(1, Mathf.RoundToInt(num / 10f));
			float num3 = num / (float)num2;
			int k = 0;
			float num4 = 0f;
			for (int l = 0; l < num2; l++)
			{
				float num5;
				for (num5 = (float)l * num3; k < sampleCount && (array2[k] <= Mathf.Epsilon || num4 + array2[k] < num5); k++)
				{
					num4 += array2[k];
				}
				if (k >= sampleCount)
				{
					break;
				}
				float num6 = (num5 - num4) / array2[k];
				Vector3 position = Vector3.Lerp(array[k], array[(k + 1) % sampleCount], num6);
				CreateMarker(l, position);
			}
			Plugin.Log.LogInfo((object)($"Created {num2} orange-red shoreline-shaped open-sea boundary markers " + $"approximately {num3:0.#}m apart."));
		}

		private void CreateMarker(int index, Vector3 position)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject($"Open Sea Boundary Point {index + 1}");
			val.layer = LayerMask.NameToLayer("Ignore Raycast");
			val.transform.SetParent(_markerRoot.transform, true);
			val.transform.position = position;
			val.transform.localScale = Vector3.one * 0.6f;
			MeshFilter obj = val.AddComponent<MeshFilter>();
			if (!Object.op_Implicit((Object)(object)_markerMesh))
			{
				_markerMesh = CreateMarkerMesh();
			}
			obj.sharedMesh = _markerMesh;
			MeshRenderer obj2 = val.AddComponent<MeshRenderer>();
			if (!Object.op_Implicit((Object)(object)_markerMaterial))
			{
				Shader val2 = Shader.Find("Universal Render Pipeline/Lit");
				if (!Object.op_Implicit((Object)(object)val2))
				{
					Plugin.Log.LogError((object)"Could not find the Universal Render Pipeline/Lit shader for boundary markers.");
				}
				else
				{
					_markerMaterial = new Material(val2)
					{
						name = "Open Sea Boundary Orange Red",
						color = MarkerColor
					};
					_markerMaterial.SetColor(BaseColorId, MarkerColor);
				}
			}
			((Renderer)obj2).sharedMaterial = _markerMaterial;
			((Renderer)obj2).shadowCastingMode = (ShadowCastingMode)1;
			((Renderer)obj2).receiveShadows = true;
			_markers.Add(val.transform);
		}

		private static Mesh CreateMarkerMesh()
		{
			//IL_002b: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: 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_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cf: Expected O, but got Unknown
			float num = (1f + Mathf.Sqrt(5f)) * 0.5f;
			Vector3[] array = (Vector3[])(object)new Vector3[12]
			{
				new Vector3(-1f, num, 0f),
				new Vector3(1f, num, 0f),
				new Vector3(-1f, 0f - num, 0f),
				new Vector3(1f, 0f - num, 0f),
				new Vector3(0f, -1f, num),
				new Vector3(0f, 1f, num),
				new Vector3(0f, -1f, 0f - num),
				new Vector3(0f, 1f, 0f - num),
				new Vector3(num, 0f, -1f),
				new Vector3(num, 0f, 1f),
				new Vector3(0f - num, 0f, -1f),
				new Vector3(0f - num, 0f, 1f)
			};
			int[] array2 = new int[60]
			{
				0, 11, 5, 0, 5, 1, 0, 1, 7, 0,
				7, 10, 0, 10, 11, 1, 5, 9, 5, 11,
				4, 11, 10, 2, 10, 7, 6, 7, 1, 8,
				3, 9, 4, 3, 4, 2, 3, 2, 6, 3,
				6, 8, 3, 8, 9, 4, 9, 5, 2, 4,
				11, 6, 2, 10, 8, 6, 7, 9, 8, 1
			};
			Vector3[] array3 = (Vector3[])(object)new Vector3[array2.Length];
			int[] array4 = new int[array2.Length];
			for (int i = 0; i < array2.Length; i++)
			{
				array3[i] = ((Vector3)(ref array[array2[i]])).normalized * 0.5f;
				array4[i] = i;
			}
			Mesh val = new Mesh
			{
				name = "Open Sea Boundary Low-Poly Icosahedron",
				vertices = array3,
				triangles = array4
			};
			val.RecalculateNormals();
			val.RecalculateBounds();
			val.UploadMeshData(true);
			return val;
		}

		private void UpdateWaterHeights()
		{
			//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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			foreach (Transform marker in _markers)
			{
				if (Object.op_Implicit((Object)(object)marker))
				{
					Vector3 position = marker.position;
					position.y = WaterManager.GetWaterHeight(position);
					marker.position = position;
				}
			}
		}

		private void ClearMarkers()
		{
			if (Object.op_Implicit((Object)(object)_markerRoot))
			{
				Object.Destroy((Object)(object)_markerRoot);
			}
			_markerRoot = null;
			_markers.Clear();
			_boundaryRevision = -1;
			_boundaryOffset = -1f;
		}

		private void OnDestroy()
		{
			ClearMarkers();
			if (Object.op_Implicit((Object)(object)_markerMaterial))
			{
				Object.Destroy((Object)(object)_markerMaterial);
			}
			if (Object.op_Implicit((Object)(object)_markerMesh))
			{
				Object.Destroy((Object)(object)_markerMesh);
			}
		}
	}
	internal sealed class OpenSeaConfig
	{
		public ConfigEntry<bool> PiratesEnabled { get; }

		public ConfigEntry<float> DistanceBeyondIsland { get; }

		public ConfigEntry<float> DifficultyMultiplier { get; }

		public ConfigEntry<int> MaximumActiveBoats { get; }

		public ConfigEntry<bool> BoundaryMarkersEnabled { get; }

		public OpenSeaConfig(ConfigFile config)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Expected O, but got Unknown
			PiratesEnabled = config.Bind<bool>("Pirates", "Enabled", true, "Spawn pirate encounters in the open ocean. The host's setting controls multiplayer encounters.");
			DistanceBeyondIsland = config.Bind<float>("Pirates", "DistanceBeyondIsland", 60f, new ConfigDescription("Horizontal distance in metres beyond the sampled island shoreline before pirates spawn.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
			DifficultyMultiplier = config.Bind<float>("Pirates", "DifficultyMultiplier", 1f, new ConfigDescription("Multiplier applied to the value budget used to equip and reinforce pirate fleets. Lower values make encounters easier; higher values make them harder.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 3f), Array.Empty<object>()));
			MaximumActiveBoats = config.Bind<int>("Pirates", "MaximumActiveBoats", 8, new ConfigDescription("Maximum number of pirate boats that may be active at once.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>()));
			BoundaryMarkersEnabled = config.Bind<bool>("Visuals", "BoundaryMarkersEnabled", true, "Show the orange-red markers around the open-sea encounter boundary.");
		}
	}
	internal sealed class PirateEncounterController : MonoBehaviour
	{
		private sealed class PirateBoatPlan
		{
			public int MotorIndex;

			public IReadOnlyList<PirateWeaponDefinition> Weapons;

			public int Worth;
		}

		private sealed class PirateFleetPlan
		{
			public readonly List<PirateBoatPlan> Boats = new List<PirateBoatPlan>();

			public int Worth;
		}

		private const float CheckInterval = 1f;

		private const float PreferredSingleBoatChance = 0.7f;

		private const float PreferredTwoBoatChance = 0.25f;

		private const float AdditionalPirateChance = 0.5f;

		private const float AdditionalPirateChanceMultiplier = 0.5f;

		private const float HorizonSpawnDistance = 250f;

		private const float MaximumFleetBearingOffsetDegrees = 90f;

		private const float FleetArcSpacingDegrees = 12f;

		private const float FleetArcJitterDegrees = 4f;

		private const float WeaponRangeOrbitMultiplier = 0.25f;

		private const float UpgradeMotorWeightMultiplier = 0.35f;

		private const float FleetWorthLowerBound = 0.75f;

		private const int WorthPercentPerDistanceBand = 10;

		private const int PirateHullWorth = 100;

		private const int MaximumFleetRerolls = 256;

		private static readonly string[] PirateWeaponSpawnNames = new string[5] { "pistol", "shotgun", "smg", "assaultrifle", "sniperrifle" };

		private static readonly FieldInfo MotorsField = AccessTools.Field(typeof(Boat), "_motors");

		private static readonly FieldInfo MotorPurchasableIndexField = AccessTools.Field(typeof(MotorPurchasable), "_motorIndex");

		private static readonly FieldInfo PurchasableCostField = AccessTools.Field(typeof(Purchasable), "_customCost");

		private static readonly Dictionary<int, int> MotorWorthByIndex = new Dictionary<int, int>();

		private OpenSeaConfig _config;

		private OpenSeaBoundary _openSeaBoundary;

		private Boat? _boatPrefab;

		private readonly List<Weapon> _weapons = new List<Weapon>();

		private readonly Dictionary<Boat, int> _activePirateBoats = new Dictionary<Boat, int>();

		private float _nextCheckTime;

		private int _preferredReinforcementBoatCount;

		private bool _loggedNoWeapons;

		private bool _motorWorthsLoaded;

		public void Initialize(OpenSeaConfig config, OpenSeaBoundary openSeaBoundary)
		{
			_config = config;
			_openSeaBoundary = openSeaBoundary;
		}

		private void Update()
		{
			if (Time.time < _nextCheckTime)
			{
				return;
			}
			_nextCheckTime = Time.time + 1f;
			if (!Object.op_Implicit((Object)(object)Server.Instance) || !((NetworkBehaviour)Server.Instance).IsServerInitialized)
			{
				return;
			}
			if (!_config.PiratesEnabled.Value)
			{
				DespawnPirates();
				return;
			}
			if (IslandManager.IsLoading || !Object.op_Implicit((Object)(object)Island.CurIsland))
			{
				_motorWorthsLoaded = false;
				DespawnPirates();
				return;
			}
			if (!_motorWorthsLoaded)
			{
				RefreshMotorWorths();
				_motorWorthsLoaded = true;
			}
			float distanceBeyondIsland = _openSeaBoundary.DistanceBeyondIsland;
			float farthestDistanceFromShoreline;
			Player val = FindFarthestPlayerFromIsland(out farthestDistanceFromShoreline);
			RemoveDestroyedPirateBoats();
			if (!Object.op_Implicit((Object)(object)val) || farthestDistanceFromShoreline <= distanceBeyondIsland)
			{
				_preferredReinforcementBoatCount = 0;
				return;
			}
			int openOceanPlayerWorth = GetOpenOceanPlayerWorth(distanceBeyondIsland, farthestDistanceFromShoreline);
			openOceanPlayerWorth = ApplyDifficultyMultiplier(openOceanPlayerWorth);
			int activePirateWorth = GetActivePirateWorth();
			int num = Mathf.Max(1, openOceanPlayerWorth);
			int num2 = Mathf.Max(0, _config.MaximumActiveBoats.Value - _activePirateBoats.Count);
			if (activePirateWorth < num && num2 > 0)
			{
				TrySpawnPirates(val, num - activePirateWorth, openOceanPlayerWorth, num2);
			}
			else
			{
				_preferredReinforcementBoatCount = 0;
			}
		}

		private bool TrySpawnPirates(Player target, int reinforcementWorth, int playerWorth, int maximumBoatCount)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: 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)
			Boat boatPrefab = GetBoatPrefab();
			if (!Object.op_Implicit((Object)(object)boatPrefab))
			{
				Plugin.Log.LogError((object)"Could not find the game's registered boat network prefab for the pirate encounter.");
				return false;
			}
			IReadOnlyList<Weapon> readOnlyList = FindPirateWeapons();
			if (readOnlyList.Count == 0)
			{
				if (!_loggedNoWeapons)
				{
					_loggedNoWeapons = true;
					Plugin.Log.LogError((object)"No pirate weapon resources were available; pirate encounter could not spawn.");
				}
				return false;
			}
			_loggedNoWeapons = false;
			if (_preferredReinforcementBoatCount <= 0)
			{
				_preferredReinforcementBoatCount = Mathf.Min(SelectPreferredReinforcementBoatCount(), maximumBoatCount);
			}
			Vector3 val = target.Transform.position - Island.IslandPos;
			val.y = 0f;
			Vector3 val2 = ((((Vector3)(ref val)).sqrMagnitude > 0.01f) ? ((Vector3)(ref val)).normalized : Vector3.forward);
			PirateFleetPlan pirateFleetPlan = SelectFleetForWorth(boatPrefab, readOnlyList, reinforcementWorth, playerWorth, _preferredReinforcementBoatCount, maximumBoatCount);
			if (pirateFleetPlan == null)
			{
				return false;
			}
			int count = pirateFleetPlan.Boats.Count;
			Vector3 seawardDirection = Quaternion.AngleAxis(Random.Range(-90f, 90f), Vector3.up) * val2;
			float formationAngle = Random.Range(-4f, 4f);
			for (int i = 0; i < count; i++)
			{
				SpawnPirateBoat(target, boatPrefab, pirateFleetPlan.Boats[i], seawardDirection, formationAngle, i, count, playerWorth, pirateFleetPlan.Worth);
			}
			_preferredReinforcementBoatCount = 0;
			return true;
		}

		private void SpawnPirateBoat(Player target, Boat boatPrefab, PirateBoatPlan plan, Vector3 seawardDirection, float formationAngle, int index, int boatCount, int playerWorth, int fleetWorth)
		{
			//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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: 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_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			float num = (float)index - (float)(boatCount - 1) * 0.5f;
			Vector3 val = Quaternion.AngleAxis(formationAngle + num * 12f, Vector3.up) * seawardDirection;
			float num2 = 250f + Mathf.Abs(num) * 5f;
			Vector3 val2 = target.Transform.position + val * num2;
			val2.y = SpawnManager.BoatSpawnPos.y;
			Vector3 desiredForward = target.Transform.position - val2;
			desiredForward.y = 0f;
			Quaternion val3 = Quaternion.LookRotation(((Vector3)(ref desiredForward)).normalized, Vector3.up);
			Boat val4 = Object.Instantiate<Boat>(boatPrefab, val2, val3);
			((Object)val4).name = $"Pirate Boat {index + 1}";
			int motorIndex = plan.MotorIndex;
			AlignBoatForward(val4, desiredForward);
			IReadOnlyList<PirateWeaponDefinition> weapons = plan.Weapons;
			int count = weapons.Count;
			float num3 = weapons.Max((PirateWeaponDefinition weapon) => weapon.AttackRange) * 0.25f;
			string motorName = GetMotorName(val4, motorIndex);
			ConfigureLongRangeNetworkVisibility(val4);
			PirateBoatSupport.RegisterServerPirate(val4, weapons);
			((Component)val4).gameObject.AddComponent<PirateBoatController>().Initialize(val4, weapons, num3, _openSeaBoundary);
			((NetworkBehaviour)Server.Instance).Spawn(((Component)val4).gameObject, (NetworkConnection)null, default(Scene));
			val4._curSkin.Value = (byte)(count - 1);
			val4._boatUnlocked.Value = true;
			val4._boatRadarUnlocked.Value = true;
			val4._motorIndex.Value = (byte)motorIndex;
			_activePirateBoats[val4] = plan.Worth;
			Plugin.Log.LogInfo((object)($"Spawned pirate boat {index + 1}/{boatCount}, " + string.Format("with {0}, {1} shooter{2}, ", motorName, count, (count == 1) ? "" : "s") + "one helmsman, and " + string.Join(", ", weapons.Select((PirateWeaponDefinition weapon) => weapon.Name)) + " " + $"on the seaward horizon with a {num3:0.#}m attack orbit " + $"near island {OnlineIslandManager.CurIsland + 1}. " + $"Reinforcement worth {fleetWorth:N0} counters open-ocean player worth {playerWorth:N0}."));
		}

		private static PirateFleetPlan? SelectFleetForWorth(Boat boatPrefab, IReadOnlyList<Weapon> weapons, int worthRequirement, int totalWorthBudget, int preferredBoatCount, int maximumBoatCount)
		{
			int num = SaturatingMultiply(GetBoatWorth(boatPrefab, 0, (IEnumerable<Weapon>)(object)new Weapon[1] { weapons.OrderBy(GetWeaponWorth).First() }), preferredBoatCount);
			if (worthRequirement < num)
			{
				return null;
			}
			int num2 = Mathf.CeilToInt((float)worthRequirement * 0.75f);
			PirateFleetPlan pirateFleetPlan = ((preferredBoatCount == 1) ? CreatePreferredSingleBoat(boatPrefab, weapons, totalWorthBudget) : null);
			if (pirateFleetPlan != null && pirateFleetPlan.Worth > worthRequirement)
			{
				return null;
			}
			PirateFleetPlan pirateFleetPlan2 = null;
			PirateFleetPlan pirateFleetPlan3 = pirateFleetPlan;
			if (pirateFleetPlan != null && pirateFleetPlan.Worth >= num2)
			{
				pirateFleetPlan2 = pirateFleetPlan;
			}
			for (int i = 1; i <= 256; i++)
			{
				PirateFleetPlan pirateFleetPlan4 = GenerateRandomFleet(boatPrefab, weapons, maximumBoatCount);
				if (pirateFleetPlan4.Worth <= worthRequirement)
				{
					if (IsPreferredFleet(pirateFleetPlan4, pirateFleetPlan3, preferredBoatCount))
					{
						pirateFleetPlan3 = pirateFleetPlan4;
					}
					if (pirateFleetPlan4.Worth >= num2 && IsPreferredFleet(pirateFleetPlan4, pirateFleetPlan2, preferredBoatCount))
					{
						pirateFleetPlan2 = pirateFleetPlan4;
					}
				}
			}
			if (pirateFleetPlan2 != null)
			{
				Plugin.Log.LogInfo((object)($"Selected {pirateFleetPlan2.Boats.Count} pirate reinforcement " + string.Format("boat{0} worth {1:N0} ", (pirateFleetPlan2.Boats.Count == 1) ? "" : "s", pirateFleetPlan2.Worth) + $"for shortfall {worthRequirement:N0} after comparing {256} rolls."));
				return pirateFleetPlan2;
			}
			PirateFleetPlan pirateFleetPlan5 = pirateFleetPlan3 ?? CreateMinimumFleet(weapons);
			Plugin.Log.LogWarning((object)($"No pirate reinforcement landed within 75%-100% of shortfall {worthRequirement:N0} " + $"after {256} rolls; using the preferred affordable " + $"{pirateFleetPlan5.Boats.Count}-boat fleet worth {pirateFleetPlan5.Worth:N0}."));
			return pirateFleetPlan5;
		}

		private static PirateFleetPlan? CreatePreferredSingleBoat(Boat boatPrefab, IReadOnlyList<Weapon> weapons, int worthBudget)
		{
			List<BoatMotor> list = (List<BoatMotor>)MotorsField.GetValue(boatPrefab);
			int num = ((list == null || list.Count == 0) ? 1 : list.Count);
			int num2 = Mathf.Min(new int[3]
			{
				num,
				PirateBoatSupport.MaximumShooterCount,
				weapons.Count
			});
			PirateBoatPlan preferredBoat = null;
			List<Weapon> selectedWeapons = new List<Weapon>(num2);
			for (int i = 0; i < num; i++)
			{
				for (int j = 1; j <= num2; j++)
				{
					FindPreferredSingleBoatLoadout(boatPrefab, weapons, worthBudget, i, j, 0, selectedWeapons, ref preferredBoat);
				}
			}
			if (preferredBoat == null)
			{
				return null;
			}
			return new PirateFleetPlan
			{
				Worth = preferredBoat.Worth,
				Boats = { preferredBoat }
			};
		}

		private static void FindPreferredSingleBoatLoadout(Boat boatPrefab, IReadOnlyList<Weapon> weapons, int worthBudget, int motorIndex, int weaponsRemaining, int startWeaponIndex, List<Weapon> selectedWeapons, ref PirateBoatPlan? preferredBoat)
		{
			if (weaponsRemaining == 0)
			{
				int boatWorth = GetBoatWorth(boatPrefab, motorIndex, selectedWeapons);
				if (boatWorth <= worthBudget && (preferredBoat == null || boatWorth > preferredBoat.Worth))
				{
					preferredBoat = new PirateBoatPlan
					{
						MotorIndex = motorIndex,
						Weapons = selectedWeapons.Select(PirateWeaponDefinition.FromWeapon).ToArray(),
						Worth = boatWorth
					};
				}
			}
			else
			{
				int num = weapons.Count - weaponsRemaining;
				for (int i = startWeaponIndex; i <= num; i++)
				{
					selectedWeapons.Add(weapons[i]);
					FindPreferredSingleBoatLoadout(boatPrefab, weapons, worthBudget, motorIndex, weaponsRemaining - 1, i + 1, selectedWeapons, ref preferredBoat);
					selectedWeapons.RemoveAt(selectedWeapons.Count - 1);
				}
			}
		}

		private static bool IsPreferredFleet(PirateFleetPlan candidate, PirateFleetPlan? current, int preferredBoatCount)
		{
			if (current == null)
			{
				return true;
			}
			int num = Mathf.Abs(candidate.Boats.Count - preferredBoatCount);
			int num2 = Mathf.Abs(current.Boats.Count - preferredBoatCount);
			if (num != num2)
			{
				return num < num2;
			}
			return candidate.Worth > current.Worth;
		}

		private static int SelectPreferredReinforcementBoatCount()
		{
			float value = Random.value;
			if (value < 0.7f)
			{
				return 1;
			}
			if (!(value < 0.95f))
			{
				return 3;
			}
			return 2;
		}

		private static PirateFleetPlan GenerateRandomFleet(Boat boatPrefab, IReadOnlyList<Weapon> weapons, int maximumBoatCount)
		{
			PirateFleetPlan pirateFleetPlan = new PirateFleetPlan();
			int num = 1;
			float num2 = 0.5f;
			while (num < maximumBoatCount && Random.value < num2)
			{
				num++;
				num2 *= 0.5f;
			}
			for (int i = 0; i < num; i++)
			{
				int motorIndex = SelectMotorIndex(boatPrefab);
				int shooterCount = SelectShooterCount(boatPrefab);
				IReadOnlyList<PirateWeaponDefinition> readOnlyList = SelectShooterWeapons(weapons, shooterCount);
				PirateBoatPlan pirateBoatPlan = new PirateBoatPlan
				{
					MotorIndex = motorIndex,
					Weapons = readOnlyList,
					Worth = GetBoatWorth(boatPrefab, motorIndex, readOnlyList.Select((PirateWeaponDefinition weapon) => weapon.VisualSource))
				};
				pirateFleetPlan.Boats.Add(pirateBoatPlan);
				pirateFleetPlan.Worth = SaturatingAdd(pirateFleetPlan.Worth, pirateBoatPlan.Worth);
			}
			return pirateFleetPlan;
		}

		private static PirateFleetPlan CreateMinimumFleet(IReadOnlyList<Weapon> weapons)
		{
			Boat boat = FindLoadedPlayerBoat();
			Weapon val = weapons.OrderBy(GetWeaponWorth).First();
			IReadOnlyList<PirateWeaponDefinition> weapons2 = new PirateWeaponDefinition[1] { PirateWeaponDefinition.FromWeapon(val) };
			PirateBoatPlan pirateBoatPlan = new PirateBoatPlan();
			pirateBoatPlan.MotorIndex = 0;
			pirateBoatPlan.Weapons = weapons2;
			pirateBoatPlan.Worth = GetBoatWorth(boat, 0, (IEnumerable<Weapon>)(object)new Weapon[1] { val });
			PirateBoatPlan pirateBoatPlan2 = pirateBoatPlan;
			return new PirateFleetPlan
			{
				Worth = pirateBoatPlan2.Worth,
				Boats = { pirateBoatPlan2 }
			};
		}

		private static int GetBoatWorth(Boat? boat, int motorIndex, IEnumerable<Weapon> weapons)
		{
			int num = SaturatingAdd(100, GetMotorWorth(boat, motorIndex));
			foreach (Weapon weapon in weapons)
			{
				num = SaturatingAdd(num, GetWeaponWorth(weapon));
			}
			return num;
		}

		private static int GetWeaponWorth(Weapon weapon)
		{
			return Mathf.Max(1, (((Item)weapon).Cost > 0) ? ((Item)weapon).Cost : ((Item)weapon).DefaultWorth);
		}

		private int GetOpenOceanPlayerWorth(float triggerDistance, float farthestDistanceFromShoreline)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Item> countedItems = new HashSet<Item>();
			int num = 0;
			bool flag = false;
			foreach (Player alivePlayer in PlayerManager.AlivePlayers)
			{
				if (!Object.op_Implicit((Object)(object)alivePlayer) || !Object.op_Implicit((Object)(object)alivePlayer.Transform) || !_openSeaBoundary.IsBeyondBoundary(alivePlayer.Transform.position, triggerDistance))
				{
					continue;
				}
				flag = true;
				foreach (KeyValuePair<byte, Item> item in alivePlayer.Inventory._items)
				{
					num = AddItemWorth(item.Value, countedItems, num);
				}
				Item heldItem = alivePlayer.Holding.HeldItem;
				if (Object.op_Implicit((Object)(object)heldItem) && (Object)(object)heldItem.SyncedHolder == (Object)(object)alivePlayer)
				{
					num = AddItemWorth(heldItem, countedItems, num);
				}
			}
			if (flag)
			{
				Boat val = FindLoadedPlayerBoat();
				if (Object.op_Implicit((Object)(object)val))
				{
					num = SaturatingAdd(num, GetMotorWorth(val, val.MotorIndex));
				}
			}
			return ScaleWorthForDistance(num, farthestDistanceFromShoreline, triggerDistance);
		}

		private static int ScaleWorthForDistance(int worth, float distanceFromShoreline, float distanceBandSize)
		{
			int num = ((!(distanceBandSize > 0f)) ? 1 : Mathf.Max(1, Mathf.FloorToInt(distanceFromShoreline / distanceBandSize)));
			long num2 = (long)worth * (long)num / 10;
			if (num2 < int.MaxValue)
			{
				return (int)num2;
			}
			return int.MaxValue;
		}

		private int ApplyDifficultyMultiplier(int worth)
		{
			double num = (double)worth * (double)_config.DifficultyMultiplier.Value;
			if (!(num >= 2147483647.0))
			{
				return Mathf.Max(0, Mathf.RoundToInt((float)num));
			}
			return int.MaxValue;
		}

		private static int GetMotorWorth(Boat? boat, int motorIndex)
		{
			if (motorIndex <= 0)
			{
				return 0;
			}
			if (MotorWorthByIndex.TryGetValue(motorIndex, out var value))
			{
				return value;
			}
			List<BoatMotor> list = (Object.op_Implicit((Object)(object)boat) ? ((List<BoatMotor>)MotorsField.GetValue(boat)) : null);
			if (list == null || motorIndex >= list.Count)
			{
				return 0;
			}
			return SaturatingMultiply(100, motorIndex);
		}

		private static void RefreshMotorWorths()
		{
			MotorPurchasable[] array = Resources.FindObjectsOfTypeAll<MotorPurchasable>();
			foreach (MotorPurchasable val in array)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					int num = (byte)MotorPurchasableIndexField.GetValue(val);
					int num2 = Mathf.Max(0, (int)PurchasableCostField.GetValue(val));
					if (num > 0 && (!MotorWorthByIndex.TryGetValue(num, out var value) || num2 > value))
					{
						MotorWorthByIndex[num] = num2;
					}
				}
			}
		}

		private static Boat? FindLoadedPlayerBoat()
		{
			Boat boat = BoatManager.Boat;
			if (!Object.op_Implicit((Object)(object)boat) || PirateBoatSupport.IsPirateBoat(boat))
			{
				return null;
			}
			return boat;
		}

		private static int AddItemWorth(Item item, ISet<Item> countedItems, int totalWorth)
		{
			if (!Object.op_Implicit((Object)(object)item) || !countedItems.Add(item))
			{
				return totalWorth;
			}
			return SaturatingAdd(totalWorth, Mathf.Max(0, item.TotalWorth));
		}

		private static int SaturatingAdd(int first, int second)
		{
			long num = (long)first + (long)second;
			if (num < int.MaxValue)
			{
				return (int)num;
			}
			return int.MaxValue;
		}

		private static int SaturatingMultiply(int value, int multiplier)
		{
			long num = (long)value * (long)multiplier;
			if (num < int.MaxValue)
			{
				return (int)num;
			}
			return int.MaxValue;
		}

		private static IReadOnlyList<PirateWeaponDefinition> SelectShooterWeapons(IReadOnlyList<Weapon> weapons, int shooterCount)
		{
			List<Weapon> list = new List<Weapon>(weapons);
			List<PirateWeaponDefinition> list2 = new List<PirateWeaponDefinition>(shooterCount);
			for (int i = 0; i < shooterCount; i++)
			{
				if (list.Count == 0)
				{
					list.AddRange(weapons);
				}
				int index = Random.Range(0, list.Count);
				list2.Add(PirateWeaponDefinition.FromWeapon(list[index]));
				list.RemoveAt(index);
			}
			return list2;
		}

		private static int SelectMotorIndex(Boat boat)
		{
			List<BoatMotor> list = (List<BoatMotor>)MotorsField.GetValue(boat);
			if (list == null || list.Count <= 1)
			{
				return 0;
			}
			return SelectWeightedTier(list.Count);
		}

		private static void AlignBoatForward(Boat boat, Vector3 desiredForward)
		{
			//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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.ProjectOnPlane(-boat.VisualBoat.right, Vector3.up);
			desiredForward = Vector3.ProjectOnPlane(desiredForward, Vector3.up);
			if (!(((Vector3)(ref val)).sqrMagnitude <= 0.01f) && !(((Vector3)(ref desiredForward)).sqrMagnitude <= 0.01f))
			{
				float num = Vector3.SignedAngle(val, desiredForward, Vector3.up);
				((Component)boat).transform.rotation = Quaternion.AngleAxis(num, Vector3.up) * ((Component)boat).transform.rotation;
			}
		}

		private static int SelectShooterCount(Boat boat)
		{
			List<BoatMotor> list = (List<BoatMotor>)MotorsField.GetValue(boat);
			return SelectWeightedTier((list == null || list.Count == 0) ? 1 : Mathf.Min(list.Count, PirateBoatSupport.MaximumShooterCount)) + 1;
		}

		private static int SelectWeightedTier(int tierCount)
		{
			float num = 0f;
			float num2 = 1f;
			for (int i = 0; i < tierCount; i++)
			{
				num += num2;
				num2 *= 0.35f;
			}
			float num3 = Random.value * num;
			num2 = 1f;
			for (int j = 0; j < tierCount; j++)
			{
				if (num3 < num2)
				{
					return j;
				}
				num3 -= num2;
				num2 *= 0.35f;
			}
			return tierCount - 1;
		}

		private static string GetMotorName(Boat boat, int motorIndex)
		{
			List<BoatMotor> list = (List<BoatMotor>)MotorsField.GetValue(boat);
			if (list == null || list.Count == 0)
			{
				return "default motor";
			}
			BoatMotor val = list[Mathf.Clamp(motorIndex, 0, list.Count - 1)];
			if (!Object.op_Implicit((Object)(object)val))
			{
				return $"motor {motorIndex + 1}";
			}
			return $"motor {motorIndex + 1} ({((Object)val).name})";
		}

		private static void ConfigureLongRangeNetworkVisibility(Boat pirateBoat)
		{
			if (!Object.op_Implicit((Object)(object)((Component)pirateBoat).GetComponent<NetworkObserver>()))
			{
				((Component)pirateBoat).gameObject.AddComponent<NetworkObserver>();
			}
		}

		private IReadOnlyList<Weapon> FindPirateWeapons()
		{
			_weapons.RemoveAll((Weapon weapon) => !Object.op_Implicit((Object)(object)weapon));
			if (_weapons.Count > 0)
			{
				return _weapons;
			}
			string[] pirateWeaponSpawnNames = PirateWeaponSpawnNames;
			foreach (string text in pirateWeaponSpawnNames)
			{
				Item spawnable = GameInfo.GetSpawnable(text);
				Weapon val = (Weapon)(object)((spawnable is Weapon) ? spawnable : null);
				if (Object.op_Implicit((Object)(object)val))
				{
					_weapons.Add(val);
				}
				else
				{
					Plugin.Log.LogWarning((object)("Pirate weapon resource " + text + " was unavailable."));
				}
			}
			return _weapons;
		}

		private Boat? GetBoatPrefab()
		{
			if (Object.op_Implicit((Object)(object)_boatPrefab))
			{
				return _boatPrefab;
			}
			NetworkManager networkManager = InstanceFinder.NetworkManager;
			PrefabObjects val = ((networkManager != null) ? networkManager.SpawnablePrefabs : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			for (int i = 0; i < val.GetObjectCount(); i++)
			{
				NetworkObject val2 = val.GetObject(true, i);
				if (Object.op_Implicit((Object)(object)val2))
				{
					Boat component = ((Component)val2).GetComponent<Boat>();
					if (Object.op_Implicit((Object)(object)component))
					{
						_boatPrefab = component;
						break;
					}
				}
			}
			return _boatPrefab;
		}

		private Player? FindFarthestPlayerFromIsland(out float farthestDistanceFromShoreline)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			Player result = null;
			farthestDistanceFromShoreline = float.NegativeInfinity;
			foreach (Player alivePlayer in PlayerManager.AlivePlayers)
			{
				if (Object.op_Implicit((Object)(object)alivePlayer) && Object.op_Implicit((Object)(object)alivePlayer.Transform))
				{
					float distanceFromShoreline = _openSeaBoundary.GetDistanceFromShoreline(alivePlayer.Transform.position);
					if (distanceFromShoreline > farthestDistanceFromShoreline)
					{
						result = alivePlayer;
						farthestDistanceFromShoreline = distanceFromShoreline;
					}
				}
			}
			return result;
		}

		private void DespawnPirates()
		{
			Boat[] array = _activePirateBoats.Keys.ToArray();
			foreach (Boat val in array)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					PirateBoatSupport.UnregisterServerPirate(val);
					if (Object.op_Implicit((Object)(object)Server.Instance) && ((NetworkBehaviour)Server.Instance).IsServerInitialized)
					{
						((NetworkBehaviour)Server.Instance).Despawn(((Component)val).gameObject, (DespawnType?)null);
					}
				}
			}
			_activePirateBoats.Clear();
			_preferredReinforcementBoatCount = 0;
		}

		private void RemoveDestroyedPirateBoats()
		{
			Boat[] array = _activePirateBoats.Keys.Where((Boat boat) => !Object.op_Implicit((Object)(object)boat)).ToArray();
			foreach (Boat key in array)
			{
				_activePirateBoats.Remove(key);
			}
		}

		private int GetActivePirateWorth()
		{
			int num = 0;
			foreach (KeyValuePair<Boat, int> activePirateBoat in _activePirateBoats)
			{
				if (Object.op_Implicit((Object)(object)activePirateBoat.Key))
				{
					num = SaturatingAdd(num, activePirateBoat.Value);
				}
			}
			return num;
		}

		private void OnDestroy()
		{
			DespawnPirates();
		}
	}
	[BepInPlugin("behold.howtofish.pirates", "Behold: Pirates", "0.10.2")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "behold.howtofish.pirates";

		public const string PluginName = "Behold: Pirates";

		public const string PluginVersion = "0.10.2";

		private Harmony? _harmony;

		private OpenSeaBoundary? _openSeaBoundary;

		private PirateEncounterController? _pirateEncounterController;

		private OpenSeaBoundaryMarkers? _openSeaBoundaryMarkers;

		private int _boatLayer = -1;

		private bool _boatCollisionsWereIgnored;

		private bool _boatCollisionStateCaptured;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("behold.howtofish.pirates");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			EnableBoatToBoatCollisions();
			OpenSeaConfig openSeaConfig = new OpenSeaConfig(((BaseUnityPlugin)this).Config);
			_openSeaBoundary = ((Component)this).gameObject.AddComponent<OpenSeaBoundary>();
			_openSeaBoundary.Initialize(openSeaConfig.DistanceBeyondIsland);
			_pirateEncounterController = ((Component)this).gameObject.AddComponent<PirateEncounterController>();
			_pirateEncounterController.Initialize(openSeaConfig, _openSeaBoundary);
			if (openSeaConfig.BoundaryMarkersEnabled.Value)
			{
				_openSeaBoundaryMarkers = ((Component)this).gameObject.AddComponent<OpenSeaBoundaryMarkers>();
				_openSeaBoundaryMarkers.Initialize(_openSeaBoundary);
			}
			Log.LogInfo((object)("Behold: Pirates 0.10.2 loaded; Harmony patches applied. Configuration: " + ((BaseUnityPlugin)this).Config.ConfigFilePath));
		}

		private void OnDestroy()
		{
			RestoreBoatToBoatCollisionSetting();
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_openSeaBoundary = null;
			_pirateEncounterController = null;
			_openSeaBoundaryMarkers = null;
		}

		private void EnableBoatToBoatCollisions()
		{
			_boatLayer = LayerMask.NameToLayer("Boat");
			if (_boatLayer < 0)
			{
				Log.LogWarning((object)"Could not find the Boat physics layer; boat-to-boat collisions remain unchanged.");
				return;
			}
			_boatCollisionsWereIgnored = Physics.GetIgnoreLayerCollision(_boatLayer, _boatLayer);
			_boatCollisionStateCaptured = true;
			Physics.IgnoreLayerCollision(_boatLayer, _boatLayer, false);
			Log.LogInfo((object)"Enabled boat-to-boat collisions using the boats' authored physics hulls.");
		}

		private void RestoreBoatToBoatCollisionSetting()
		{
			if (_boatCollisionStateCaptured)
			{
				Physics.IgnoreLayerCollision(_boatLayer, _boatLayer, _boatCollisionsWereIgnored);
				_boatCollisionStateCaptured = false;
			}
		}
	}
}
namespace HowToFish.OpenSea.Pirates
{
	internal sealed class PirateBoatAudio : MonoBehaviour
	{
		private const float PirateBoatAudibleDistance = 80f;

		private static readonly FieldInfo CurrentMotorField = AccessTools.Field(typeof(Boat), "_curMotor");

		private static readonly FieldInfo StartStopVolumeField = AccessTools.Field(typeof(Boat), "_boatStartStopSoundVolume");

		private Boat _boat;

		private BoatMotor? _playingMotor;

		private Coroutine? _startCoroutine;

		private bool _stopped;

		private void Awake()
		{
			_boat = ((Component)this).GetComponent<Boat>();
			AudioSource[] componentsInChildren = ((Component)_boat).GetComponentsInChildren<AudioSource>(true);
			foreach (AudioSource val in componentsInChildren)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					val.maxDistance = Mathf.Max(val.maxDistance, 80f);
				}
			}
		}

		private void OnEnable()
		{
			StartMotor();
		}

		private void Update()
		{
			if (!Object.op_Implicit((Object)(object)_boat))
			{
				return;
			}
			if (!_boat.BoatUnlocked && !_boat.BoatRadarUnlocked)
			{
				StopMotor(playStopSound: true);
				return;
			}
			BoatMotor currentMotor = GetCurrentMotor();
			if ((Object)(object)currentMotor != (Object)(object)_playingMotor && _startCoroutine == null)
			{
				StopMotor(playStopSound: false);
				StartMotor();
			}
			EnsureSourcesPlaying(currentMotor);
		}

		private void StartMotor()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_boat) && !_stopped)
			{
				BoatMotor currentMotor = GetCurrentMotor();
				if (Object.op_Implicit((Object)(object)currentMotor))
				{
					_playingMotor = currentMotor;
					AudioManager.PlayClipAt(currentMotor.MotorStartSoundName, ((Component)currentMotor).transform.position, false, (AudioDistance)2, GetStartStopVolume(), 0.1f);
					_startCoroutine = ((MonoBehaviour)this).StartCoroutine(StartLoopsAfterDelay(currentMotor));
				}
			}
		}

		private IEnumerator StartLoopsAfterDelay(BoatMotor motor)
		{
			if (motor.MotorStartDelay > 0f)
			{
				yield return (object)new WaitForSeconds(motor.MotorStartDelay);
			}
			_startCoroutine = null;
			if (Object.op_Implicit((Object)(object)_boat) && !_stopped && (Object)(object)motor == (Object)(object)GetCurrentMotor())
			{
				PlaySources(motor.MotorSounds);
				PlaySources(motor.MotorIdleSounds);
				Plugin.Log.LogInfo((object)"Started the pirate boat's default motor audio.");
			}
		}

		private static void PlaySources(AudioSource[] sources)
		{
			foreach (AudioSource val in sources)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					((Behaviour)val).enabled = true;
					val.maxDistance = Mathf.Max(val.maxDistance, 80f);
					if (!val.isPlaying)
					{
						val.Play();
					}
				}
			}
		}

		private void EnsureSourcesPlaying(BoatMotor? motor)
		{
			if (Object.op_Implicit((Object)(object)motor) && !_stopped && _startCoroutine == null)
			{
				PlaySources(motor.MotorSounds);
				PlaySources(motor.MotorIdleSounds);
			}
		}

		private static void StopSources(AudioSource[] sources)
		{
			foreach (AudioSource val in sources)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					val.Stop();
					((Behaviour)val).enabled = false;
				}
			}
		}

		private void StopMotor(bool playStopSound)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			if (_stopped && playStopSound)
			{
				return;
			}
			if (_startCoroutine != null)
			{
				((MonoBehaviour)this).StopCoroutine(_startCoroutine);
				_startCoroutine = null;
			}
			BoatMotor val = _playingMotor ?? GetCurrentMotor();
			if (Object.op_Implicit((Object)(object)val))
			{
				StopSources(val.MotorSounds);
				StopSources(val.MotorIdleSounds);
				if (playStopSound)
				{
					AudioManager.PlayClipAt(val.MotorStopSoundName, ((Component)val).transform.position, false, (AudioDistance)2, GetStartStopVolume(), 0.1f);
				}
			}
			_playingMotor = null;
			_stopped = playStopSound;
		}

		private BoatMotor? GetCurrentMotor()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			if (!Object.op_Implicit((Object)(object)_boat))
			{
				return null;
			}
			return (BoatMotor)CurrentMotorField.GetValue(_boat);
		}

		private float GetStartStopVolume()
		{
			if (!Object.op_Implicit((Object)(object)_boat))
			{
				return 1f;
			}
			return (float)StartStopVolumeField.GetValue(_boat);
		}

		private void OnDisable()
		{
			StopMotor(playStopSound: false);
		}
	}
	internal sealed class PirateBoatController : MonoBehaviour
	{
		private sealed class ShooterState
		{
			public int ShooterIndex;

			public PirateCrewHealth? Health;

			public bool WasBoundToCrew;

			public PirateWeaponDefinition Weapon;

			public float NextShotTime;

			public int Ammo;

			public bool IsReloading;

			public float ReloadCompleteTime;
		}

		private static readonly FieldInfo CurrentMotorField = AccessTools.Field(typeof(Boat), "_curMotor");

		private static readonly FieldInfo ForcePointsField = AccessTools.Field(typeof(Boat), "_forcePoints");

		private static readonly FieldInfo ForceModeField = AccessTools.Field(typeof(Boat), "_forceMode");

		private static readonly FieldInfo BoatBouncinessField = AccessTools.Field(typeof(Boat), "_boatBounciness");

		private const float FullSteerAngle = 22f;

		private const float InputChangeSpeed = 3f;

		private const float MinimumOrbitLookAheadAngle = 25f;

		internal const float BuoyancyFadeDuration = 30f;

		private const float SinkRollAcceleration = 0.35f;

		private const float SinkDespawnDepth = 8f;

		private const float MaxSinkDuration = 45f;

		private const float RetreatDespawnDistance = 250f;

		private const float RetreatPlayerDespawnDistance = 200f;

		private const float ShooterSpreadDegrees = 4f;

		private const float NavigationCheckInterval = 5f;

		private Boat _boat;

		private OpenSeaBoundary _openSeaBoundary;

		private float _orbitRadius;

		private readonly List<ShooterState> _shooters = new List<ShooterState>();

		private bool _clockwise;

		private float _sinkStartedAt = -1f;

		private float _sinkRollDirection;

		private float _steeringInput;

		private float _throttleInput;

		private bool _retreatComplete;

		private bool _isRetreatingFromIsland;

		private Vector3 _retreatStartPosition;

		private float _nextNavigationCheckTime;

		public bool IsSinking => _sinkStartedAt >= 0f;

		public bool IsRetreating => _isRetreatingFromIsland;

		public void Initialize(Boat boat, IReadOnlyList<PirateWeaponDefinition> shooterWeapons, float orbitRadius, OpenSeaBoundary openSeaBoundary)
		{
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			_boat = boat;
			_openSeaBoundary = openSeaBoundary;
			_orbitRadius = orbitRadius;
			for (int i = 0; i < shooterWeapons.Count; i++)
			{
				PirateWeaponDefinition pirateWeaponDefinition = shooterWeapons[i];
				ShooterState item = new ShooterState
				{
					ShooterIndex = i,
					Weapon = pirateWeaponDefinition,
					Ammo = pirateWeaponDefinition.MagazineSize,
					NextShotTime = Time.time + Random.Range(1.5f, 3f)
				};
				_shooters.Add(item);
			}
			_clockwise = Random.value >= 0.5f;
			_sinkRollDirection = ((Random.value >= 0.5f) ? 1f : (-1f));
			Rigidbody hiddenPhysicsRig = boat.HiddenPhysicsRig;
			hiddenPhysicsRig.interpolation = (RigidbodyInterpolation)1;
			hiddenPhysicsRig.isKinematic = false;
			hiddenPhysicsRig.linearVelocity = Vector3.zero;
			hiddenPhysicsRig.angularVelocity = Vector3.zero;
			_nextNavigationCheckTime = Time.time + 5f;
		}

		private void LateUpdate()
		{
			//IL_0057: 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)
			if (Object.op_Implicit((Object)(object)_boat) && ((NetworkBehaviour)_boat).IsServerInitialized && !IslandManager.IsLoading)
			{
				Rigidbody hiddenPhysicsRig = _boat.HiddenPhysicsRig;
				Rigidbody visualPhysicsRig = _boat.VisualPhysicsRig;
				if (Object.op_Implicit((Object)(object)hiddenPhysicsRig) && Object.op_Implicit((Object)(object)visualPhysicsRig))
				{
					((Component)visualPhysicsRig).transform.SetPositionAndRotation(((Component)hiddenPhysicsRig).transform.position, ((Component)hiddenPhysicsRig).transform.rotation);
				}
			}
		}

		private void FixedUpdate()
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_0057: 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_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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_01d9: 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_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_0214: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: 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_0235: 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)
			//IL_023c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_0255: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)_boat) || !((NetworkBehaviour)_boat).IsServerInitialized || IslandManager.IsLoading)
			{
				return;
			}
			if (IsSinking)
			{
				Rigidbody hiddenPhysicsRig = _boat.HiddenPhysicsRig;
				float sinkProgress = GetSinkProgress();
				hiddenPhysicsRig.AddTorque(((Component)hiddenPhysicsRig).transform.forward * (_sinkRollDirection * 0.35f * sinkProgress), (ForceMode)5);
				return;
			}
			Rigidbody hiddenPhysicsRig2 = _boat.HiddenPhysicsRig;
			Player val = FindClosestOceanPlayer(hiddenPhysicsRig2.position);
			UpdateRetreatState(val);
			float distance = 0f;
			Vector3 directionToTarget = Vector3.zero;
			Vector3 val6;
			if (_isRetreatingFromIsland)
			{
				Vector3 val2 = hiddenPhysicsRig2.position - _retreatStartPosition;
				val2.y = 0f;
				Player val3 = FindClosestLivingPlayer(hiddenPhysicsRig2.position);
				Vector3 val4 = (Object.op_Implicit((Object)(object)val3) ? (val3.Transform.position - hiddenPhysicsRig2.position) : Vector3.zero);
				val4.y = 0f;
				bool flag = !Object.op_Implicit((Object)(object)val3) || ((Vector3)(ref val4)).sqrMagnitude >= 40000f;
				if (((Vector3)(ref val2)).sqrMagnitude >= 62500f && flag)
				{
					_retreatComplete = true;
					SetBoatInputs(0f, 0f);
					Plugin.Log.LogInfo((object)"A retreating pirate boat is beyond both its travel and player-distance despawn limits.");
					return;
				}
				Vector3 val5 = (Object.op_Implicit((Object)(object)val3) ? (hiddenPhysicsRig2.position - val3.Transform.position) : (hiddenPhysicsRig2.position - Island.IslandPos));
				val5.y = 0f;
				val6 = ((((Vector3)(ref val5)).sqrMagnitude > 0.01f) ? ((Vector3)(ref val5)).normalized : GetHullForward());
			}
			else
			{
				if (!Object.op_Implicit((Object)(object)val))
				{
					SetBoatInputs(0f, 0f);
					return;
				}
				Vector3 val7 = val.Transform.position - hiddenPhysicsRig2.position;
				val7.y = 0f;
				float magnitude = ((Vector3)(ref val7)).magnitude;
				Vector3 val8 = ((magnitude > 0.01f) ? (val7 / magnitude) : Vector3.forward);
				distance = magnitude;
				directionToTarget = val8;
				val6 = GetOrbitPointDirection(hiddenPhysicsRig2.position, val.Transform.position);
			}
			Vector3 hullForward = GetHullForward();
			float steering = ((((Vector3)(ref hullForward)).sqrMagnitude > 0.01f) ? Mathf.Clamp(Vector3.SignedAngle(hullForward, val6, Vector3.up) / 22f, -1f, 1f) : 0f);
			int num;
			if (!_boat.BoatUnlocked)
			{
				if (_boat.BoatRadarUnlocked)
				{
					num = (IsOutsideCirclingRange() ? 1 : 0);
					if (num != 0)
					{
						goto IL_02a7;
					}
				}
				else
				{
					num = 0;
				}
				steering = 0f;
			}
			else
			{
				num = 1;
			}
			goto IL_02a7;
			IL_02a7:
			float throttle = ((num != 0) ? 1f : 0f);
			SetBoatInputs(steering, throttle);
			if (!_isRetreatingFromIsland)
			{
				MonitorApproach(hiddenPhysicsRig2, distance, directionToTarget);
			}
		}

		private Vector3 GetHullForward()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Vector3.ProjectOnPlane(-_boat.VisualBoat.right, Vector3.up);
			return ((Vector3)(ref val)).normalized;
		}

		private Vector3 GetOrbitPointDirection(Vector3 boatPosition, Vector3 targetPosition)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = boatPosition - targetPosition;
			val.y = 0f;
			float magnitude = ((Vector3)(ref val)).magnitude;
			Vector3 val2 = ((magnitude > 0.01f) ? (val / magnitude) : (-GetHullForward()));
			float num = Mathf.Max(0.1f, _orbitRadius);
			float num2 = ((magnitude > num) ? (Mathf.Acos(Mathf.Clamp01(num / magnitude)) * 57.29578f) : 0f);
			float num3 = Mathf.Max(25f, num2);
			Vector3 val3 = Quaternion.AngleAxis(_clockwise ? (0f - num3) : num3, Vector3.up) * val2;
			Vector3 val4 = targetPosition + val3 * num;
			val4.y = boatPosition.y;
			Vector3 val5 = val4 - boatPosition;
			if (!(((Vector3)(ref val5)).sqrMagnitude > 0.01f))
			{
				return Vector3.Cross(Vector3.up, val2) * (_clockwise ? (-1f) : 1f);
			}
			return ((Vector3)(ref val5)).normalized;
		}

		private void MonitorApproach(Rigidbody rig, float distance, Vector3 directionToTarget)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_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_0031: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			if (!(Time.time < _nextNavigationCheckTime))
			{
				_nextNavigationCheckTime = Time.time + 5f;
				Vector3 val = Vector3.ProjectOnPlane(rig.linearVelocity, Vector3.up);
				float num = Vector3.Dot(val, directionToTarget);
				float num2 = Vector3.Dot(GetHullForward(), directionToTarget);
				BoatMotor val2 = (BoatMotor)CurrentMotorField.GetValue(_boat);
				bool flag = Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.Propeller) && WaterManager.IsUnderWater(val2.Propeller.position);
				Plugin.Log.LogInfo((object)($"Pirate navigation: {distance:0.#}m away, {((Vector3)(ref val)).magnitude:0.#}m/s speed, " + $"{num:0.#}m/s closing, bow alignment {num2:0.00}, " + $"{_throttleInput:0.00} throttle, motor force {(Object.op_Implicit((Object)(object)val2) ? val2.Force : 0f):0.#}, " + $"propeller underwater={flag}, " + $"kinematic={rig.isKinematic}."));
			}
		}

		private void Update()
		{
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)_boat) || !((NetworkBehaviour)_boat).IsServerInitialized || IslandManager.IsLoading)
			{
				return;
			}
			if (!_boat.BoatUnlocked && !_boat.BoatRadarUnlocked)
			{
				if (!IsSinking)
				{
					BeginSinking();
				}
				if ((_boat.HiddenPhysicsRig.position.y <= WaterManager.WaterHeight - 8f || Time.time - _sinkStartedAt >= 45f) && Object.op_Implicit((Object)(object)Server.Instance) && ((NetworkBehaviour)Server.Instance).IsServerInitialized)
				{
					PirateBoatSupport.UnregisterServerPirate(_boat);
					((NetworkBehaviour)Server.Instance).Despawn(((Component)_boat).gameObject, (DespawnType?)null);
				}
				return;
			}
			Player val = FindClosestOceanPlayer(_boat.VisualBoat.position);
			UpdateRetreatState(val);
			if (_retreatComplete)
			{
				PirateBoatSupport.UnregisterServerPirate(_boat);
				((NetworkBehaviour)Server.Instance).Despawn(((Component)_boat).gameObject, (DespawnType?)null);
			}
			else
			{
				if (_isRetreatingFromIsland || !_boat.BoatRadarUnlocked || !Object.op_Implicit((Object)(object)val))
				{
					return;
				}
				foreach (ShooterState shooter in _shooters)
				{
					UpdateShooter(shooter, val);
				}
			}
		}

		private void UpdateShooter(ShooterState shooter, Player target)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			PirateWeaponDefinition weapon = shooter.Weapon;
			PirateCrewHealth pirateCrewHealth = ResolveShooterHealth(shooter);
			if (!IsShooterAlive(shooter, pirateCrewHealth) || IsShooterAtHelm(shooter, pirateCrewHealth))
			{
				return;
			}
			if (shooter.IsReloading)
			{
				if (Time.time < shooter.ReloadCompleteTime)
				{
					return;
				}
				shooter.IsReloading = false;
				shooter.Ammo = weapon.MagazineSize;
			}
			if (Time.time < shooter.NextShotTime)
			{
				return;
			}
			Vector3 val = target.Transform.position + Vector3.up * 0.8f;
			PirateWeaponVisual pirateWeaponVisual = (Object.op_Implicit((Object)(object)pirateCrewHealth) ? ((Component)pirateCrewHealth).GetComponentInChildren<PirateWeaponVisual>() : null);
			if (Object.op_Implicit((Object)(object)pirateWeaponVisual) && !pirateWeaponVisual.CanFire)
			{
				return;
			}
			Vector3 val2 = (Object.op_Implicit((Object)(object)pirateWeaponVisual) ? pirateWeaponVisual.GetMuzzlePosition(val) : PirateBoatSupport.GetShooterMuzzleFallback(_boat, shooter.ShooterIndex, val));
			float num = Vector3.Distance(val2, val);
			float num2 = Mathf.Min(1.5f, num / weapon.ProjectileSpeed);
			val += target.Other.Velocity * num2;
			val.y += 0.5f * weapon.Projectile.ProjectileGravity * num2 * num2;
			Vector3 val3 = val - val2;
			float magnitude = ((Vector3)(ref val3)).magnitude;
			if (!(magnitude > weapon.AttackRange) && !Physics.Raycast(val2, val3, magnitude, LayerMask.op_Implicit(GameInfo.LevelLayer)))
			{
				FireAt(shooter, target, pirateWeaponVisual, val2, ((Vector3)(ref val3)).normalized);
				shooter.Ammo--;
				if (shooter.Ammo <= 0)
				{
					StartReload(shooter, pirateWeaponVisual, empty: true);
				}
				else
				{
					shooter.NextShotTime = Time.time + weapon.FireInterval;
				}
			}
		}

		private void FireAt(ShooterState shooter, Player target, PirateWeaponVisual? visual, Vector3 muzzlePosition, Vector3 direction)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: 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_001e: 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_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			PirateWeaponDefinition weapon = shooter.Weapon;
			Vector2 val = Random.insideUnitCircle * 4f;
			Vector3 val2 = Quaternion.LookRotation(direction, Vector3.up) * Quaternion.Euler(0f - val.y, val.x, 0f) * Vector3.forward;
			Vector3[] array = (Vector3[])(object)new Vector3[weapon.ProjectileCount];
			for (int i = 0; i < array.Length; i++)
			{
				Vector3 val3 = Random.insideUnitSphere * weapon.Spread;
				array[i] = Quaternion.Euler(val3) * val2 * weapon.ProjectileSpeed;
			}
			Player val4 = (Player)(Object.op_Implicit((Object)(object)Player.LocalPlayer) ? ((object)Player.LocalPlayer) : ((object)target));
			uint num = PirateProjectileSupport.AllocateIds(array.Length);
			if (array.Length == 1)
			{
				ProjectileManager.Instance.ObserverAddProjectile(val4, weapon.Projectile, InstanceFinder.TimeManager.Tick, num, muzzlePosition, array[0]);
				ProjectileManager.Instance.AddProjectile(val4, weapon.Projectile, false, muzzlePosition, array[0], 0u, num, true);
			}
			else
			{
				ProjectileManager.Instance.ObserverAddProjectiles(val4, weapon.Projectile, InstanceFinder.TimeManager.Tick, num, muzzlePosition, array);
				ProjectileManager.Instance.AddProjectiles(val4, weapon.Projectile, false, muzzlePosition, array, 0u, num, true);
			}
			Attachments attachments = weapon.Attachments;
			AudioManager.PlayRandomClipAt(attachments.FireSound, 1, attachments.FireSoundCount, muzzlePosition, false, (AudioDistance)(attachments.UseMediumSoundDistance ? 2 : 3), attachments.FireSoundVolume, weapon.FireInterval * 0.8f);
			if (Object.op_Implicit((Object)(object)visual))
			{
				visual.Fire();
			}
			PirateCrewAnimator pirateCrewAnimator = (Object.op_Implicit((Object)(object)visual) ? ((Component)visual).GetComponentInParent<PirateCrewAnimator>() : null);
			if (Object.op_Implicit((Object)(object)pirateCrewAnimator))
			{
				pirateCrewAnimator.Fire();
			}
		}

		private void StartReload(ShooterState shooter, PirateWeaponVisual? visual, bool empty)
		{
			shooter.IsReloading = true;
			PirateWeaponDefinition weapon = shooter.Weapon;
			float num = (empty ? weapon.EmptyReloadDuration : weapon.ReloadDuration);
			shooter.ReloadCompleteTime = Time.time + num;
			shooter.NextShotTime = shooter.ReloadCompleteTime;
			if (Object.op_Implicit((Object)(object)visual))
			{
				visual.Reload(num);
			}
			PirateCrewAnimator pirateCrewAnimator = (Object.op_Implicit((Object)(object)visual) ? ((Component)visual).GetComponentInParent<PirateCrewAnimator>() : null);
			if (Object.op_Implicit((Object)(object)pirateCrewAnimator))
			{
				pirateCrewAnimator.Reload(num);
			}
			Plugin.Log.LogDebug((object)$"Pirate reloading {weapon.Name} ({num:0.00}s).");
		}

		private PirateCrewHealth? ResolveShooterHealth(ShooterState shooter)
		{
			if (Object.op_Implicit((Object)(object)shooter.Health))
			{
				return shooter.Health;
			}
			if (shooter.WasBoundToCrew)
			{
				return null;
			}
			int num = shooter.ShooterIndex + 1;
			PirateCrewHealth[] componentsInChildren = ((Component)_boat).GetComponentsInChildren<PirateCrewHealth>();
			foreach (PirateCrewHealth pirateCrewHealth in componentsInChildren)
			{
				if (pirateCrewHealth.IsShooter && pirateCrewHealth.CrewIndex == num)
				{
					shooter.Health = pirateCrewHealth;
					shooter.WasBoundToCrew = true;
					return shooter.Health;
				}
			}
			return null;
		}

		private bool IsShooterAlive(ShooterState shooter, PirateCrewHealth? health)
		{
			if (!Object.op_Implicit((Object)(object)health))
			{
				if (!shooter.WasBoundToCrew)
				{
					return _boat.BoatRadarUnlocked;
				}
				return false;
			}
			return health.Alive;
		}

		private bool IsShooterAtHelm(ShooterState shooter, PirateCrewHealth? health)
		{
			if (Object.op_Implicit((Object)(object)health))
			{
				return health.IsAtHelm;
			}
			if (!_boat.BoatUnlocked && shooter.ShooterIndex == 0)
			{
				return IsOutsideCirclingRange();
			}
			return false;
		}

		internal bool IsOutsideCirclingRange()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)_boat))
			{
				return false;
			}
			Vector3 val = (Object.op_Implicit((Object)(object)_boat.HiddenPhysicsRig) ? _boat.HiddenPhysicsRig.position : ((Component)_boat).transform.position);
			Player val2 = FindClosestLivingPlayer(val);
			if (!Object.op_Implicit((Object)(object)val2))
			{
				return true;
			}
			Vector3 val3 = val2.Transform.position - val;
			val3.y = 0f;
			return ((Vector3)(ref val3)).sqrMagnitude > _orbitRadius * _orbitRadius;
		}

		private Player? FindClosestOceanPlayer(Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return FindClosestLivingPlayer(position, requireOpenOcean: true);
		}

		private Player? FindClosestLivingPlayer(Vector3 position, bool requireOpenOcean = false)
		{
			//IL_005e: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Player result = null;
			float num = float.PositiveInfinity;
			foreach (Player alivePlayer in PlayerManager.AlivePlayers)
			{
				if (Object.op_Implicit((Object)(object)alivePlayer) && Object.op_Implicit((Object)(object)alivePlayer.Transform) && (!requireOpenOcean || _openSeaBoundary.IsBeyondBoundary(alivePlayer.Transform.position, _openSeaBoundary.DistanceBeyondIsland)))
				{
					Vector3 val = alivePlayer.Transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						result = alivePlayer;
						num = sqrMagnitude;
					}
				}
			}
			return result;
		}

		private void SetBoatInputs(float steering, float throttle)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			_steeringInput = Mathf.MoveTowards(_steeringInput, steering, 3f * Time.fixedDeltaTime);
			_throttleInput = Mathf.MoveTowards(_throttleInput, throttle, 3f * Time.fixedDeltaTime);
			_boat._driverXInput.Value = (half)_steeringInput;
			_boat._driverYInput.Value = (half)_throttleInput;
		}

		public void ApplyNativeInputForce()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: 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_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)
			if (!Object.op_Implicit((Object)(object)_boat) || (!_boat.BoatUnlocked && !_boat.BoatRadarUnlocked) || _throttleInput <= 0f)
			{
				return;
			}
			BoatMotor val = (BoatMotor)CurrentMotorField.GetValue(_boat);
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.Propeller) && WaterManager.IsUnderWater(val.Propeller.position))
			{
				Rigidbody hiddenPhysicsRig = _boat.HiddenPhysicsRig;
				Vector3 val2 = -val.Propeller.right;
				Vector3 hullForward = GetHullForward();
				if (Vector3.Dot(val2, hullForward) < 0f)
				{
					val2 = -val2;
				}
				hiddenPhysicsRig.AddForceAtPosition(val2 * (val.Force * _throttleInput), val.Propeller.position);
			}
		}

		private void UpdateRetreatState(Player? oceanTarget)
		{
			bool flag = HasLivingFreeShooter();
			if (Object.op_Implicit((Object)(object)oceanTarget) && flag)
			{
				ResumePursuit();
			}
			else
			{
				BeginRetreat(flag ? "No living player remains in the open ocean" : "No living pirate remains free to shoot");
			}
		}

		private bool HasLivingFreeShooter()
		{
			foreach (ShooterState shooter in _shooters)
			{
				PirateCrewHealth health = ResolveShooterHealth(shooter);
				if (IsShooterAlive(shooter, health) && !IsShooterAtHelm(shooter, health))
				{
					return true;
				}
			}
			return false;
		}

		private void BeginRetreat(string reason)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (IsSinking || _isRetreatingFromIsland)
			{
				return;
			}
			_isRetreatingFromIsland = true;
			_retreatStartPosition = _boat.HiddenPhysicsRig.position;
			foreach (ShooterState shooter in _shooters)
			{
				shooter.IsReloading = false;
				shooter.NextShotTime = float.PositiveInfinity;
			}
			Plugin.Log.LogInfo((object)(reason + "; the pirate boat is retreating."));
		}

		private void ResumePursuit()
		{
			if (!_isRetreatingFromIsland || IsSinking)
			{
				return;
			}
			_isRetreatingFromIsland = false;
			_retreatComplete = false;
			foreach (ShooterState shooter in _shooters)
			{
				shooter.NextShotTime = Time.time + Random.Range(0.5f, 1.5f);
			}
			Plugin.Log.LogInfo((object)"A living player is in the open ocean; the pirate boat is pursuing again.");
		}

		public void ReduceNativeBuoyancy()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			if (!IsSinking)
			{
				return;
			}
			float sinkProgress = GetSinkProgress();
			Rigidbody hiddenPhysicsRig = _boat.HiddenPhysicsRig;
			Transform[] obj = (Transform[])ForcePointsField.GetValue(_boat);
			ForceMode val = (ForceMode)ForceModeField.GetValue(_boat);
			float num = (float)BoatBouncinessField.GetValue(_boat);
			Transform[] array = obj;
			foreach (Transform val2 in array)
			{
				if (Object.op_Implicit((Object)(object)val2))
				{
					KeyValuePair<bool, float> waterInfo = WaterManager.GetWaterInfo(val2.position);
					if (waterInfo.Key)
					{
						float num2 = WaterManager.BoatWaterForce * Mathf.Abs(waterInfo.Value);
						float y = hiddenPhysicsRig.GetPointVelocity(val2.position).y;
						float num3 = WaterManager.BoatWaterForce * (1f - num);
						float num4 = Mathf.Clamp((0f - y) * num3, 0f - num2, WaterManager.BoatWaterForce);
						float num5 = num2 + num4;
						hiddenPhysicsRig.AddForceAtPosition(Vector3.down * (num5 * sinkProgress), val2.position, val);
					}
				}
			}
		}

		private float GetSinkProgress()
		{
			return Mathf.Clamp01((Time.time - _sinkStartedAt) / 30f);
		}

		private void BeginSinking()
		{
			_sinkStartedAt = Time.time;
			SetBoatInputs(0f, 0f);
			Rigidbody hiddenPhysicsRig = _boat.HiddenPhysicsRig;
			hiddenPhysicsRig.linearDamping = Mathf.Min(hiddenPhysicsRig.linearDamping, 0.35f);
			hiddenPhysicsRig.angularDamping = Mathf.Min(hiddenPhysicsRig.angularDamping, 0.5f);
			Plugin.Log.LogInfo((object)"Both pirates were killed; the pirate boat is losing buoyancy over 30 seconds.");
		}

		private void OnDestroy()
		{
			PirateBoatSupport.UnregisterServerPirate(_boat);
		}
	}
	internal static class PirateBoatSupport
	{
		private static readonly Vector3[] ShooterStations = (Vector3[])(object)new Vector3[5]
		{
			new Vector3(-1.75f, 0.28f, 0f),
			new Vector3(-2.45f, 0.28f, -0.8f),
			new Vector3(-2.45f, 0.28f, 0.8f),
			new Vector3(-3.1f, 0.28f, -0.75f),
			new Vector3(-3.1f, 0.28f, 0.75f)
		};

		private static readonly FieldInfo DynamicObjectCollidersField = AccessTools.Field(typeof(Boat), "_dynamicObjectCols");

		private static readonly FieldInfo BoatInteractableField = AccessTools.Field(typeof(Boat), "_boatInteractable");

		private static readonly FieldInfo BoatSkinRendererField = AccessTools.Field(typeof(Boat), "_skinRenderer");

		private static readonly FieldInfo PlayerBodyRendererField = AccessTools.Field(typeof(PlayerSkin), "_bodyRenderer");

		private static readonly FieldInfo PlayerOutfitRendererField = AccessTools.Field(typeof(PlayerSkin), "_outfitRenderer");

		private static readonly FieldInfo PlayerHatRendererField = AccessTools.Field(typeof(PlayerSkin), "_hatRenderer");

		private static readonly FieldInfo PlayerAccessoryRendererField = AccessTools.Field(typeof(PlayerSkin), "_accessoryRenderer");

		private static readonly FieldInfo PlayerLeftHandRendererField = AccessTools.Field(typeof(PlayerSkin), "_leftHand");

		private static readonly FieldInfo PlayerRightHandRendererField = AccessTools.Field(typeof(PlayerSkin), "_rightHand");

		private static readonly FieldInfo ItemInHandHolderField = AccessTools.Field(typeof(Item), "_inHandHolder");

		private static readonly FieldInfo PlayerRightArmIkField = AccessTools.Field(typeof(PlayerArms), "_ikRight");

		private static readonly FieldInfo PlayerLeftArmIkField = AccessTools.Field(typeof(PlayerArms), "_ikLeft");

		private static readonly FieldInfo IkPoleField = AccessTools.Field(typeof(IK), "_pole");

		private static readonly HashSet<Boat> ServerPirates = new HashSet<Boat>();

		private static readonly HashSet<Boat> IdentifiedClientPirates = new HashSet<Boat>();

		private static readonly HashSet<Boat> VisualizedPirates = new HashSet<Boat>();

		private static readonly Dictionary<Boat, IReadOnlyList<PirateWeaponDefinition>> PirateWeapons = new Dictionary<Boat, IReadOnlyList<PirateWeaponDefinition>>();

		private static readonly Dictionary<Boat, int> PirateShooterCounts = new Dictionary<Boat, int>();