Decompiled source of Behold Pirates v0.10.2
BepInEx/plugins/Behold.Pirates/Behold.Pirates.dll
Decompiled 7 hours ago
The result has been truncated due to the large size, download it to view full contents!
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>();