Decompiled source of Behold Pirates v0.14.1
BepInEx/plugins/Behold.Pirates/Behold.Pirates.dll
Decompiled a month 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.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; 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 FishNet.Transporting; using HarmonyLib; 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.14.1.0")] [assembly: AssemblyInformationalVersion("0.14.1+6b3c3d8ae249a35490bff0a914e30f9f2d1cff04")] [assembly: AssemblyProduct("Behold: Pirates")] [assembly: AssemblyTitle("Behold: Pirates")] [assembly: AssemblyVersion("0.14.1.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 Behold.Pirates { 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_0091: 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; Physics.SyncTransforms(); Vector3 outward = default(Vector3); for (int i = 0; i < 120; i++) { float num2 = MathF.PI * 2f * (float)i / 120f; ((Vector3)(ref outward))..ctor(Mathf.Cos(num2), 0f, Mathf.Sin(num2)); float num3 = FindOutermostShoreRadius(outward, scanRadius, waterHeight); if (num3 >= 1f) { array[i] = true; num++; _shoreRadii[i] = num3; } 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); } _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) { float num6 = (num5 - num4) / array2[k]; Vector3 position = Vector3.Lerp(array[k], array[(k + 1) % sampleCount], num6); CreateMarker(l, position); continue; } break; } } 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 bool StreamModeEnabled => false; public int StreamBoatsPerTeam => 3; 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."); } } [HarmonyPatch(typeof(GameInfo), "Awake")] internal static class ItemFishGameCatalogPatch { private static void Postfix() { try { PirateCollectibleSupport.RegisterPirateItem(); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not register the pirate item: {arg}"); } ItemFishRegistry.ApplyGameCatalog(); } } [HarmonyPatch(typeof(NetworkManager), "Awake")] internal static class ItemFishNetworkCatalogPatch { private static void Postfix(NetworkManager __instance) { ItemFishRegistry.ApplyNetworkManager(__instance); } } [HarmonyPatch(typeof(Item), "GetName")] internal static class CustomItemDisplayNamePatch { private static bool Prefix(Item __instance, ref string __result) { if (ItemFishRegistry.TryGetDisplayName(__instance, out string displayName)) { __result = displayName; return false; } return true; } } [HarmonyPatch] internal static class PirateBoatDrivingInitializationPatch { private static readonly FieldInfo DrivingField = AccessTools.Field(typeof(Boat), "<IsDrivingLocally>k__BackingField"); private static readonly FieldInfo WantToDriveField = AccessTools.Field(typeof(Boat), "<WantToDrive>k__BackingField"); private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(Boat), "Awake", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Boat), "OnStartClient", (Type[])null, (Type[])null); } private static void Prefix(Boat __instance, out (bool Preserve, bool Driving, bool Want) __state) { __state = (Preserve: Object.op_Implicit((Object)(object)BoatManager.Boat) && (Object)(object)BoatManager.Boat != (Object)(object)__instance, Driving: Boat.IsDrivingLocally, Want: Boat.WantToDrive); } private static void Finalizer((bool Preserve, bool Driving, bool Want) __state) { if (__state.Preserve) { DrivingField.SetValue(null, __state.Driving); WantToDriveField.SetValue(null, __state.Want); } } } [HarmonyPatch(typeof(Boat), "OnDriverChange")] internal static class PirateBoatDriverChangePatch { private static bool Prefix(Boat __instance) { return !PirateBoatSupport.IsPirateBoat(__instance); } } [HarmonyPatch(typeof(Boat), "OnStartClient")] internal static class PirateBoatClientRegistrationPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.Method(typeof(BoatManager), "SetBoat", (Type[])null, (Type[])null); MethodInfo replacement = AccessTools.Method(typeof(PirateBoatClientRegistrationPatch), "Register", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { instruction.operand = replacement; } yield return instruction; } } private static void Register(Collider[] cols, Boat boat) { if (!PirateBoatSupport.IsPirateBoat(boat)) { BoatManager.SetBoat(cols, boat); return; } foreach (Collider key in cols) { BoatManager.ColToBoat[key] = boat; } } } [HarmonyPatch(typeof(Boat), "OnStopClient")] internal static class PirateBoatClientRemovalPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.Method(typeof(BoatManager), "RemoveBoat", (Type[])null, (Type[])null); MethodInfo replacement = AccessTools.Method(typeof(PirateBoatClientRemovalPatch), "Remove", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { yield return CodeInstructionExtensions.MoveLabelsFrom(new CodeInstruction(OpCodes.Ldarg_0, (object)null), instruction); instruction.operand = replacement; } yield return instruction; } } private static void Remove(Boat boat) { if ((Object)(object)boat == (Object)(object)BoatManager.Boat && !PirateBoatSupport.IsPirateBoat(boat)) { BoatManager.RemoveBoat(); return; } List<Collider> list = new List<Collider>(); foreach (KeyValuePair<Collider, Boat> item in BoatManager.ColToBoat) { if ((Object)(object)item.Value == (Object)(object)boat) { list.Add(item.Key); } } foreach (Collider item2 in list) { BoatManager.ColToBoat.Remove(item2); } } } [HarmonyPatch(typeof(BoatTrigger), "FixedUpdate")] internal static class PirateBoatCargoTriggerPatch { private static void Postfix(BoatTrigger __instance) { Boat boat; bool flag = PirateBoatSupport.TryGetPirateBoat(__instance, out boat); foreach (RigidbodySync item in __instance.ItemsOnBoat) { if (Object.op_Implicit((Object)(object)item)) { PirateBoatCargo pirateBoatCargo = ((Component)item).GetComponent<PirateBoatCargo>(); if (flag && !Object.op_Implicit((Object)(object)pirateBoatCargo)) { pirateBoatCargo = ((Component)item).gameObject.AddComponent<PirateBoatCargo>(); } if (Object.op_Implicit((Object)(object)pirateBoatCargo)) { pirateBoatCargo.Boat = (flag ? boat : null); item.SetBoat(true); } } } } } internal sealed class PirateBoatCargo : MonoBehaviour { public Boat? Boat; public static bool UsePlayerBoatCoordinates(RigidbodySync sync) { PirateBoatCargo component = ((Component)sync).GetComponent<PirateBoatCargo>(); PirateCreatureLink component2 = ((Component)sync).GetComponent<PirateCreatureLink>(); if (sync.OnBoat && (!Object.op_Implicit((Object)(object)component) || !Object.op_Implicit((Object)(object)component.Boat))) { if (Object.op_Implicit((Object)(object)component2)) { return !component2.IsAttached; } return true; } return false; } } [HarmonyPatch(typeof(RigidbodySync), "SetBoat")] internal static class PirateBoatCargoLeavePatch { private static void Postfix(RigidbodySync __instance, bool onBoat) { if (!onBoat) { PirateBoatCargo component = ((Component)__instance).GetComponent<PirateBoatCargo>(); if (Object.op_Implicit((Object)(object)component)) { component.Boat = null; } } } } [HarmonyPatch(typeof(RigidbodySync), "GetSendBoatVisual")] internal static class PirateBoatCargoSendFramePatch { private static bool Prefix(RigidbodySync __instance, ref Transform? __result) { if (PirateBoatCargo.UsePlayerBoatCoordinates(__instance)) { return true; } __result = null; return false; } } [HarmonyPatch(typeof(RigidbodySync), "SendPosRotToObservers")] internal static class PirateBoatCargoPacketPatch { private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { MethodInfo original = AccessTools.PropertyGetter(typeof(RigidbodySync), "OnBoat"); MethodInfo replacement = AccessTools.Method(typeof(PirateBoatCargo), "UsePlayerBoatCoordinates", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { if (CodeInstructionExtensions.Calls(instruction, original)) { instruction.opcode = OpCodes.Call; instruction.operand = replacement; } yield return instruction; } } } [HarmonyPatch(typeof(Boat), "OnStartClient")] internal static class PirateBoatStartClientPatch { private static void Prefix(Boat __instance, out Boat? __state) { Boat boat = BoatManager.Boat; __state = ((Object.op_Implicit((Object)(object)boat) && (Object)(object)boat != (Object)(object)__instance) ? boat : null); if (Object.op_Implicit((Object)(object)__state)) { PirateBoatSupport.IdentifyClientPirate(__instance); } } private static void Postfix(Boat __instance, Boat? __state) { if (Object.op_Implicit((Object)(object)__state)) { PirateBoatSupport.ConfigureClientPirate(__instance, __state); } } } [HarmonyPatch(typeof(Boat), "OnStartServer")] internal static class PirateBoatStartServerPatch { private static void Postfix(Boat __instance) { PirateBoatSupport.ApplyServerPirateDefaults(__instance); } } [HarmonyPatch(typeof(Boat), "OnSkinChange")] internal static class PirateBoatSkinPatch { private static bool Prefix(Boat __instance, byte next) { return PirateBoatSupport.ShouldApplyBoatSkin(__instance, next); } } [HarmonyPatch(typeof(Boat), "OnMotorChange")] internal static class PirateBoatMotorAchievementScopePatch { private static void Prefix(Boat __instance, out bool __state) { __state = PirateBoatSupport.BeginPirateMotorChange(__instance); } private static void Postfix(bool __state) { if (__state) { PirateBoatSupport.EndPirateMotorChange(); } } } [HarmonyPatch(typeof(AchievementManager), "CheckBoatUpgradeAchievement")] internal static class PirateBoatMotorAchievementPatch { private static bool Prefix() { return !PirateBoatSupport.IsApplyingPirateMotor; } } [HarmonyPatch(typeof(Boat), "OnStopClient")] internal static class PirateBoatStopClientPatch { private static void Prefix(Boat __instance, out Boat? __state) { Boat boat = BoatManager.Boat; __state = ((Object.op_Implicit((Object)(object)boat) && (Object)(object)boat != (Object)(object)__instance) ? boat : null); } private static void Postfix(Boat __instance, Boat? __state) { PirateBoatSupport.UnregisterServerPirate(__instance); if (Object.op_Implicit((Object)(object)__state)) { PirateBoatSupport.RestoreMainBoat(__state); } } } [HarmonyPatch(typeof(Boat), "ApplyReturnForce")] internal static class PirateBoatReturnForcePatch { private static bool Prefix(Boat __instance) { return PirateBoatSupport.ShouldApplyAutomaticReturnForce(__instance); } } [HarmonyPatch(typeof(Boat), "ApplyInputForce")] internal static class PirateBoatInputForcePatch { private static bool Prefix(Boat __instance) { if (!PirateBoatSupport.IsServerPirate(__instance)) { return true; } PirateBoatController component = ((Component)__instance).GetComponent<PirateBoatController>(); if (Object.op_Implicit((Object)(object)component)) { component.ApplyNativeInputForce(); } return false; } } [HarmonyPatch(typeof(Boat), "ApplyWaterForce")] internal static class PirateBoatWaterForcePatch { private static bool Prefix(Boat __instance) { PirateBoatController component = ((Component)__instance).GetComponent<PirateBoatController>(); if (Object.op_Implicit((Object)(object)component) && component.IsSinking) { component.ReduceNativeBuoyancy(); } return true; } } [HarmonyPatch(typeof(Boat), "UpdateCollider")] internal static class PirateBoatColliderSyncPatch { private static readonly FieldInfo DynamicCollidersHolderField = AccessTools.Field(typeof(Boat), "_dynamicObjectColsHolder"); private static readonly FieldInfo ItemCollidersHolderField = AccessTools.Field(typeof(Boat), "_itemColsHolder"); private static readonly FieldInfo LerpPositionField = AccessTools.Field(typeof(Boat), "_curLerpedPos"); private static readonly FieldInfo LerpRotationField = AccessTools.Field(typeof(Boat), "_curLerpedRot"); private static bool Prefix(Boat __instance) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_003b: 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_0057: 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) //IL_007b: 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) if (!PirateBoatSupport.IsPirateBoat(__instance)) { return true; } Transform val = (Transform)DynamicCollidersHolderField.GetValue(__instance); if (((NetworkBehaviour)__instance).IsServerInitialized) { Rigidbody val2 = (Rigidbody)ItemCollidersHolderField.GetValue(__instance); val.SetPositionAndRotation(__instance.HiddenPhysicsRig.position, __instance.HiddenPhysicsRig.rotation); val2.MovePosition(__instance.HiddenPhysicsRig.position); val2.MoveRotation(__instance.HiddenPhysicsRig.rotation); if (!val2.isKinematic) { val2.linearVelocity = Vector3.zero; val2.angularVelocity = Vector3.zero; } } else { val.position = (Vector3)LerpPositionField.GetValue(__instance); val.rotation = (Quaternion)LerpRotationField.GetValue(__instance); } return false; } } [HarmonyPatch(typeof(BoatTrigger), "TellPlayerTheyAreOnBoat")] internal static class PirateBoatTriggerPlayerPatch { private struct RideTransition { public Boat? PreviousBoat; public Boat? PirateBoat; public bool Switched; public bool Leaving; } private static readonly FieldInfo LocalPlayerOnBoatField = AccessTools.Field(typeof(BoatTrigger), "_localPlayerOnBoat"); private static readonly FieldInfo OldLocalPlayerOnBoatField = AccessTools.Field(typeof(BoatTrigger), "_oldLocalPlayerOnBoat"); private static void Prefix(BoatTrigger __instance, out RideTransition __state) { __state = default(RideTransition); if (!PirateBoatSupport.TryGetPirateBoat(__instance, out Boat boat)) { return; } bool flag = (bool)LocalPlayerOnBoatField.GetValue(__instance); bool flag2 = (bool)OldLocalPlayerOnBoatField.GetValue(__instance); if (flag != flag2) { if (flag) { PirateBoatSupport.BeginLocalPirateRide(boat); } __state.PreviousBoat = BoatManager.Boat; __state.PirateBoat = boat; __state.Switched = true; __state.Leaving = !flag; PirateBoatSupport.SelectPirateBoatForLocalPlayer(boat); } } private static void Postfix(RideTransition __state) { if (__state.Switched) { if (__state.Leaving && Object.op_Implicit((Object)(object)__state.PirateBoat)) { PirateBoatSupport.EndLocalPirateRide(__state.PirateBoat); } PirateBoatSupport.RestoreBoatSelection(__state.PreviousBoat); } } } [HarmonyPatch(typeof(PlayerMovement), "FixedUpdate")] internal static class PirateBoatPlayerMovementPatch { private static void Prefix(PlayerMovement __instance, out Boat? __state) { __state = null; if (Object.op_Implicit((Object)(object)Player.LocalPlayer) && (Object)(object)__instance == (Object)(object)Player.LocalPlayer.Movement) { PirateBoatSupport.SelectLocalRiddenPirate(out __state); } } private static void Postfix(Boat? __state) { if (Object.op_Implicit((Object)(object)__state)) { PirateBoatSupport.RestoreBoatSelection(__state); } } } [HarmonyPatch(typeof(PlayerMovement), "AddBoatPos")] internal static class PirateBoatPlayerCarryPatch { private static void Prefix(PlayerMovement __instance, out Boat? __state) { __state = null; if (Object.op_Implicit((Object)(object)Player.LocalPlayer) && (Object)(object)__instance == (Object)(object)Player.LocalPlayer.Movement) { PirateBoatSupport.SelectLocalRiddenPirate(out __state); } } private static void Postfix(Boat? __state) { if (Object.op_Implicit((Object)(object)__state)) { PirateBoatSupport.RestoreBoatSelection(__state); } } } [HarmonyPatch(typeof(Boat), "GiveBoatDeltaToPlayerAndItems")] internal static class PirateBoatItemCarryPatch { private static void Prefix(Boat __instance, out Boat? __state) { __state = null; if (PirateBoatSupport.IsPirateBoat(__instance)) { __state = BoatManager.Boat; PirateBoatSupport.SelectPirateBoatForLocalPlayer(__instance); } } private static void Postfix(Boat __instance, Boat? __state) { if (PirateBoatSupport.IsPirateBoat(__instance)) { PirateBoatSupport.RestoreBoatSelection(__state); } } } [HarmonyPatch(typeof(Player), "SendPosRot")] internal static class PirateBoatPlayerNetworkPositionPatch { private static readonly FieldInfo SendTeleportField = AccessTools.Field(typeof(Player), "_sendTeleport"); private static bool Prefix(Player __instance) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)Player.LocalPlayer) || (Object)(object)__instance != (Object)(object)Player.LocalPlayer || !PirateBoatSupport.IsLocalRidingPirate) { return true; } bool flag = (bool)SendTeleportField.GetValue(__instance); if (flag) { SendTeleportField.SetValue(__instance, false); } Vector3 localEulerAngles = __instance.CamObject.localEulerAngles; Server.Instance.UpdatePlayerPosRot(__instance, __instance.Transform.position, new Vector2(localEulerAngles.x, localEulerAngles.y), false, flag, (Channel)1); return false; } } [HarmonyPatch(typeof(Boat), "SetLocalPlayerOnBoat")] internal static class PirateBoatInteractionStatePatch { private static void Postfix(Boat __instance) { if (PirateBoatSupport.IsPirateBoat(__instance)) { PirateBoatSupport.DisableDrivingInteraction(__instance); } } } [HarmonyPatch(typeof(BoatInteractable), "Interact")] internal static class PirateBoatDriveInteractionPatch { private static bool Prefix(BoatInteractable __instance) { return !PirateBoatSupport.IsPirateBoat(((Component)__instance).GetComponentInParent<Boat>()); } } [HarmonyPatch] internal static class PirateBoatMeleeTargetPatch { private static readonly FieldInfo PunchPlayerField = AccessTools.Field(typeof(PlayerPunching), "_player"); private static readonly FieldInfo PunchRangeField = AccessTools.Field(typeof(PlayerPunching), "_range"); private static readonly FieldInfo MeleeHolderField = AccessTools.Field(typeof(Item), "_holder"); private static readonly FieldInfo MeleeRangeField = AccessTools.Field(typeof(Melee), "_range"); private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(PlayerPunching), "CheckForPlayersAndLevel", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Melee), "CheckForPlayersAndLevel", (Type[])null, (Type[])null); } private static void Postfix(object __instance, ref Transform finalTarget, ref Vector3 finalHitPoint) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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_0058: 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_005e: Expected O, but got Unknown //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: 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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: 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_00dc: 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) if (!Object.op_Implicit((Object)(object)finalTarget) || !((Component)finalTarget).CompareTag("Boat")) { return; } Vector3 val = finalTarget.TransformPoint(finalHitPoint); if (PirateBoatSupport.TryGetPirateBoatAtPoint(val, out Boat boat)) { Player val2 = ((__instance is PlayerPunching) ? ((Player)PunchPlayerField.GetValue(__instance)) : ((Player)MeleeHolderField.GetValue(__instance))); float maxDistance = ((__instance is PlayerPunching) ? ((float)PunchRangeField.GetValue(__instance)) : ((float)MeleeRangeField.GetValue(__instance))); if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.CamObject) && PirateBoatSupport.TryGetLivingPirateHit(boat, new Ray(val2.CamObject.position, val2.CamObject.forward), maxDistance, 0f, out var pirateHit)) { finalTarget = ((RaycastHit)(ref pirateHit)).transform; finalHitPoint = finalTarget.InverseTransformPoint(((RaycastHit)(ref pirateHit)).point); } else { finalTarget = boat.VisualBoat; finalHitPoint = finalTarget.InverseTransformPoint(val); } } } } [HarmonyPatch] internal static class PirateBoatMeleeForcePatch { private static readonly FieldInfo PunchTargetsField = AccessTools.Field(typeof(PlayerPunching), "_curTarget"); private static readonly FieldInfo MeleeTargetsField = AccessTools.Field(typeof(Melee), "_curTarget"); private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(PlayerPunching), "HitTarget", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(Melee), "HitTarget", (Type[])null, (Type[])null); } private static void Prefix(object __instance, int side, out Boat? __state) { __state = null; Transform[] array = (Transform[])((__instance is PlayerPunching) ? PunchTargetsField : MeleeTargetsField).GetValue(__instance); if (side >= 0 && side < array.Length && PirateBoatSupport.TryGetPirateBoat(array[side], out Boat boat)) { __state = BoatManager.Boat; PirateBoatSupport.SelectPirateBoatForLocalPlayer(boat); } } private static void Postfix(Boat? __state) { if (Object.op_Implicit((Object)(object)__state)) { PirateBoatSupport.RestoreBoatSelection(__state); } } } [HarmonyPatch(typeof(ProjectileManager), "Hit")] internal static class PirateBoatProjectileForcePatch { private static void Prefix(RaycastHit hit, out Boat? __state) { __state = null; if (PirateBoatSupport.TryGetPirateBoat(((RaycastHit)(ref hit)).transform, out Boat boat)) { __state = BoatManager.Boat; PirateBoatSupport.SelectPirateBoatForLocalPlayer(boat); } } private static void Postfix(Boat? __state) { if (Object.op_Implicit((Object)(object)__state)) { PirateBoatSupport.RestoreBoatSelection(__state); } } } [HarmonyPatch(typeof(PlayerInventory), "LocalTrySelectSlot")] internal static class PirateCorpseInventorySlotPatch { private static void Prefix(PlayerInventory __instance) { //IL_002a: 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_0033: 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) Player componentInParent = ((Component)__instance).GetComponentInParent<Player>(); Item val = (Object.op_Implicit((Object)(object)componentInParent) ? componentInParent.Holding.HeldItem : null); if (PirateCollectibleSupport.IsPirateCorpse(val)) { val.Drop(true, default(Vector3), default(Vector3)); } } } [HarmonyPatch(typeof(PlayerInventory), "ResolveHeldItemReplacement")] internal static class PirateCorpseHeldReplacementPatch { private static bool Prefix(Item item) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!PirateCollectibleSupport.IsPirateCorpse(item)) { return true; } item.Drop(false, default(Vector3), default(Vector3)); return false; } } [HarmonyPatch(typeof(PlayerInventory), "ServerTryStoreHeldItem")] internal static class PirateCorpseServerStorePatch { private static bool Prefix(Item item, ref bool __result) { if (!PirateCollectibleSupport.IsPirateCorpse(item)) { return true; } __result = false; return false; } } [HarmonyPatch(typeof(PlayerInventory), "AddItem")] internal static class PirateCorpseInventoryAddPatch { private static bool Prefix(Item item) { return !PirateCollectibleSupport.IsPirateCorpse(item); } } [HarmonyPatch(typeof(Item), "PutInInventory")] internal static class PirateCorpsePutInInventoryPatch { private static bool Prefix(Item __instance) { return !PirateCollectibleSupport.IsPirateCorpse(__instance); } } [HarmonyPatch(typeof(Item), "UpdateLayer")] internal static class PirateCorpsePlayerCollisionPatch { private static readonly FieldInfo DisablePlayerCollisionField = AccessTools.Field(typeof(Item), "_disablePlayerColOnGround"); private static void Prefix(Item __instance) { if (PirateCollectibleSupport.IsPirateCorpse(__instance)) { DisablePlayerCollisionField.SetValue(__instance, true); } } } [HarmonyPatch(typeof(PlayerHolding), "ScanForInteractable")] internal static class PirateCorpseSteeringInteractionPatch { private static void Prefix(Player ____player, ref Item ____grabbableItem, ref Interactable ____interactable, out Item? __state) { __state = null; if (PirateCollectibleSupport.IsPirateCorpse(____grabbableItem) && !____player.BlockInputs && !Boat.IsDrivingLocally && !Boat.WantToDrive) { __state = ____grabbableItem; ____grabbableItem.UnHover(); ____grabbableItem = null; if (Object.op_Implicit((Object)(object)____interactable)) { ____interactable.UnHover(); ____interactable = null; } } } private static void Postfix(ref Item ____grabbableItem, ref Interactable ____interactable, Item? __state) { if (!Object.op_Implicit((Object)(object)__state)) { return; } Interactable obj = ____interactable; BoatInteractable val = (BoatInteractable)(object)((obj is BoatInteractable) ? obj : null); if (val != null) { Boat componentInParent = ((Component)val).GetComponentInParent<Boat>(); if (Object.op_Implicit((Object)(object)componentInParent) && (Object)(object)componentInParent == (Object)(object)BoatManager.Boat && !PirateBoatSupport.IsPirateBoat(componentInParent)) { return; } } if (Object.op_Implicit((Object)(object)____interactable)) { ____interactable.UnHover(); ____interactable = null; } ____grabbableItem = __state; __state.Hover(); } } [HarmonyPatch(typeof(ItemManager), "FloatItems")] internal static class PirateCorpseWaterClassificationPatch { private static readonly FieldInfo CreatureReferenceField = AccessTools.Field(typeof(Item), "_creature"); private static readonly Dictionary<Item, Creature> MaskedCreatures = new Dictionary<Item, Creature>(); private static void Prefix() { MaskedCreatures.Clear(); foreach (Item value in ItemManager.Items.Values) { if (PirateCollectibleSupport.IsPirateCorpse(value) && !MaskedCreatures.ContainsKey(value)) { Creature creature = value.Creature; if (Object.op_Implicit((Object)(object)creature)) { MaskedCreatures.Add(value, creature); CreatureReferenceField.SetValue(value, null); } } } } private static Exception? Finalizer(Exception? __exception) { foreach (KeyValuePair<Item, Creature> maskedCreature in MaskedCreatures) { if (Object.op_Implicit((Object)(object)maskedCreature.Key)) { CreatureReferenceField.SetValue(maskedCreature.Key, maskedCreature.Value); } } MaskedCreatures.Clear(); return __exception; } } [HarmonyPatch(typeof(ProjectileManager), "Hit")] [HarmonyPriority(801)] internal static class StreamProjectileFriendlyFirePatch { private const float FriendlyPassThroughDistance = 0.75f; private static bool Prefix(Projectile projectile, ref RaycastHit hit) { //IL_0042: 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) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_0095: 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) PirateTeam streamProjectileTeam = PirateProjectileSupport.GetStreamProjectileTeam(projectile.Id); if (streamProjectileTeam == PirateTeam.None || !Object.op_Implicit((Object)(object)((RaycastHit)(ref hit)).collider)) { return true; } Boat boat; bool flag = PirateBoatSupport.TryGetPirateBoat(((RaycastHit)(ref hit)).transform, out boat); if (!flag && ((Component)((RaycastHit)(ref hit)).transform).CompareTag("Boat")) { flag = PirateBoatSupport.TryGetPirateBoatAtPoint(((RaycastHit)(ref hit)).point, out boat); } if (!flag || PirateBoatSupport.GetTeam(boat) != streamProjectileTeam) { return true; } Vector3 normalized = ((Vector3)(ref projectile.Velocity)).normalized; if (((Vector3)(ref normalized)).sqrMagnitude > 0.001f) { projectile.Position = ((RaycastHit)(ref hit)).point + normalized * 0.75f; projectile.PreviousPosition = projectile.Position; } return false; } } [HarmonyPatch(typeof(ProjectileManager), "Hit")] [HarmonyPriority(800)] internal static class PirateCrewProjectileOcclusionPatch { private const float MaximumHullTraversalDistance = 12f; private static void Prefix(Projectile projectile, ProjectileType type, ref RaycastHit hit) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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) //IL_007d: 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_0092: Unknown result type (might be due to invalid IL or missing references) if (projectile.FromNpc || !Object.op_Implicit((Object)(object)((RaycastHit)(ref hit)).collider)) { return; } Boat boat; Boat val = (PirateBoatSupport.TryGetPirateBoat(((RaycastHit)(ref hit)).transform, out boat) ? boat : null); if (!Object.op_Implicit((Object)(object)val) && !((Component)((RaycastHit)(ref hit)).transform).CompareTag("Boat")) { return; } Vector3 normalized = ((Vector3)(ref projectile.Velocity)).normalized; if (!(((Vector3)(ref normalized)).sqrMagnitude <= 0.001f)) { Ray ray = default(Ray); ((Ray)(ref ray))..ctor(projectile.Position, normalized); float maxDistance = ((RaycastHit)(ref hit)).distance + 12f; if (PirateBoatSupport.TryGetLivingPirateHit(val, ray, maxDistance, type.WidthRadius, out var pirateHit)) { hit = pirateHit; } } } } [HarmonyPatch(typeof(CreatureManager), "AddAliveCreature")] internal static class PirateCreatureManagerRegistrationPatch { private static bool Prefix(Creature creature) { return !Object.op_Implicit((Object)(object)((Component)creature).GetComponent<PirateCreatureProxy>()); } } [HarmonyPatch(typeof(ItemManager), "OnIslandUnloaded")] internal static class PirateCreatureIslandCleanupPatch { private static void Prefix(out List<KeyValuePair<Transform, Item>> __state) { __state = new List<KeyValuePair<Transform, Item>>(); foreach (KeyValuePair<Transform, Item> item in ItemManager.Items) { PirateCreatureLink pirateCreatureLink = (Object.op_Implicit((Object)(object)item.Value) ? ((Component)item.Value).GetComponent<PirateCreatureLink>() : null); if (Object.op_Implicit((Object)(object)pirateCreatureLink) && pirateCreatureLink.IsAttached) { __state.Add(item); } } foreach (KeyValuePair<Transform, Item> item2 in __state) { ItemManager.Items.Remove(item2.Key); } } private static void Postfix(List<KeyValuePair<Transform, Item>> __state) { foreach (KeyValuePair<Transform, Item> item in __state) { if (Object.op_Implicit((Object)(object)item.Key) && Object.op_Implicit((Object)(object)item.Value)) { ItemManager.Items.TryAdd(item.Key, item.Value); } } } } [HarmonyPatch(typeof(Creature), "OnStartClient")] internal static class PirateCollectibleStartClientPatch { private static void Prefix(Creature __instance, out PirateCrewHealth? __state) { if (!PirateCollectibleSupport.IsPirateProxy((Item)(object)__instance)) { __state = null; return; } if (__instance._hp.Value <= 0) { __state = null; PirateCollectibleSupport.PrepareRestoredCorpse((Item)(object)__instance, ((NetworkBehaviour)__instance).ObjectId); return; } __state = PirateCollectibleSupport.FindMatchingPirate(__instance); if (Object.op_Implicit((Object)(object)__state)) { PirateCollectibleSupport.Prepare(__instance, __state); } else if (!((NetworkBehaviour)__instance).IsServerInitialized) { PirateCollectibleSupport.QueuePendingClientBinding(__instance); } } private static void Postfix(Creature __instance, PirateCrewHealth? __state) { if (Object.op_Implicit((Object)(object)__state)) { PirateCollectibleSupport.BindLivingCreature(__instance, __state); } else if (PirateCollectibleSupport.IsPirateProxy((Item)(object)__instance) && __instance._hp.Value <= 0) { PirateCollectibleSupport.FinalizeRestoredCorpse((Item)(object)__instance); } } } [HarmonyPatch(typeof(Creature), "ServerChangeHp")] internal static class PirateCreatureServerDeathPatch { private static void Postfix(Creature __instance) { PirateCreatureLink component = ((Component)__instance).GetComponent<PirateCreatureLink>(); if (Object.op_Implicit((Object)(object)component)) { component.TryAdoptIfDead(); } } } [HarmonyPatch(typeof(Creature), "OnHealthChange")] internal static class PirateCreatureObservedDeathPatch { private static void Postfix(Creature __instance) { PirateCreatureLink component = ((Component)__instance).GetComponent<PirateCreatureLink>(); if (Object.op_Implicit((Object)(object)component)) { component.TryAdoptIfDead(); } } } [HarmonyPatch(typeof(Item), "OnCooknessChange")] internal static class PirateCollectibleCookVisualPatch { private static void Postfix(Item __instance, float next) { if (PirateCollectibleSupport.IsPirateProxy(__instance)) { PirateCollectibleVisual component = ((Component)__instance).GetComponent<PirateCollectibleVisual>(); if (Object.op_Implicit((Object)(object)component)) { component.ApplyCookness(next); } } } } [HarmonyPatch(typeof(Item), "OnSyncedHolderChange")] internal static class PirateCollectibleHolderWaterTimerPatch { private static void Postfix(Item __instance, Player next) { if (Object.op_Implicit((Object)(object)next) && PirateCollectibleSupport.IsPirateProxy(__instance)) { PirateCollectibleVisual component = ((Component)__instance).GetComponent<PirateCollectibleVisual>(); if (Object.op_Implicit((Object)(object)component)) { component.ResetWaterTimeout(); } } } } [HarmonyPatch(typeof(RigidbodySync), "SetBoat")] internal static class PirateCollectibleBoatWaterTimerPatch { private static void Postfix(RigidbodySync __instance, bool onBoat) { if (!onBoat) { return; } Item component = ((Component)__instance).GetComponent<Item>(); if (PirateCollectibleSupport.IsPirateProxy(component)) { PirateCollectibleVisual component2 = ((Component)component).GetComponent<PirateCollectibleVisual>(); if (Object.op_Implicit((Object)(object)component2)) { component2.ResetWaterTimeout(); } } } } [HarmonyPatch(typeof(ItemUI), "ShowSpecificItemInfo", new Type[] { typeof(Item) })] internal static class PirateCorpseHeldInfoPatch { private static bool Prefix(ItemUI __instance, Item __0) { if (!PirateCollectibleSupport.IsPirateProxy(__0)) { return true; } __instance.HideSpecificItemInfo(); return false; } } [HarmonyPatch(typeof(ItemManager), "Get", new Type[] { typeof(Collider) })] internal static class PirateCrewColliderItemLookupPatch { private static void Postfix(Collider col, ref Item? __result) { if (!Object.op_Implicit((Object)(object)__result) && Object.op_Implicit((Object)(object)col)) { PirateCrewHealth componentInParent = ((Component)col).GetComponentInParent<PirateCrewHealth>(); if (Object.op_Implicit((Object)(object)componentInParent) && Object.op_Implicit((Object)(object)componentInParent.BoundCreature)) { __result = (Item?)(object)componentInParent.BoundCreature; } } } } [HarmonyPatch(typeof(ItemManager), "Get", new Type[] { typeof(Transform) })] internal static class PirateCrewTransformItemLookupPatch { private static void Postfix(Transform tran, ref Item? __result) { if (!Object.op_Implicit((Object)(object)__result) && Object.op_Implicit((Object)(object)tran)) { PirateCrewHealth componentInParent = ((Component)tran).GetComponentInParent<PirateCrewHealth>(); if (Object.op_Implicit((Object)(object)componentInParent) && Object.op_Implicit((Object)(object)componentInParent.BoundCreature)) { __result = (Item?)(object)componentInParent.BoundCreature; } } } } [HarmonyPatch(typeof(ProjectileManager), "Hit")] internal static class PirateCrewProjectileHitPatch { private static void Prefix(Projectile projectile, RaycastHit hit) { //IL_005a: 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_0076: 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) if (projectile.FromNpc && Object.op_Implicit((Object)(object)((RaycastHit)(ref hit)).collider) && !projectile.IsLocal && Object.op_Implicit((Object)(object)Server.Instance) && ((NetworkBehaviour)Server.Instance).IsServerInitialized) { Player playerFromBodyPart = PlayerManager.GetPlayerFromBodyPart(((RaycastHit)(ref hit)).transform); if (Object.op_Implicit((Object)(object)playerFromBodyPart) && !((NetworkBehaviour)playerFromBodyPart).IsDeinitializing) { Vector3 val = ((Vector3)(ref projectile.Velocity)).normalized * GameInfo.PlayerKillForce; Server.Instance.RpcLogic___HitPlayer___2449261505(playerFromBodyPart, projectile.Damage, val, ((RaycastHit)(ref hit)).point, (byte)2, (Player)null); } } } } [HarmonyPatch(typeof(Creature), "LocalHit")] internal static class PirateCreatureDamageScalingPatch { private static void Prefix(Creature __instance, ref int damage) { PirateCreatureLink component = ((Component)__instance).GetComponent<PirateCreatureLink>(); if (Object.op_Implicit((Object)(object)component) && Object.op_Implicit((Object)(object)component.Pirate)) { damage = component.Pirate.ScalePlayerDamage(damage); } } } [HarmonyPatch(typeof(Item), "LocalHit")] internal static class PirateCreaturePhysicsHitPatch { private static bool Prefix(Item __instance) { PirateCreatureLink component = ((Component)__instance).GetComponent<PirateCreatureLink>(); if (Object.op_Implicit((Object)(object)component)) { return !component.IsAttached; } return true; } } [HarmonyPatch(typeof(ProjectileManager), "ReceiveAddProjectiles")] internal static class PirateProjectileReceivePatch { private static bool Prefix(ProjectileManager __instance, Player owner, WeaponInfo weaponInfo, uint tick, uint id, Vector3 pos, Vector3[] velocities) { //IL_0020: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (!PirateProjectileSupport.IsPirateProjectile(weaponInfo, isLocal: false, id)) { return true; } if (((NetworkBehaviour)__instance).IsServerInitialized || velocities == null || velocities.Length == 0) { return false; } PirateWeaponVisual.FireClosestTo(pos); uint num = (uint)((float)(uint)((float)(InstanceFinder.TimeManager.Tick - tick) * GameInfo.TickMulti) * GameInfo.TickMulti); if (velocities.Length == 1) { bool flag = PirateProjectileSupport.IsStreamProjectile(weaponInfo, isLocal: false, id); __instance.AddProjectile(owner, weaponInfo, false, pos, velocities[0], num, id, !flag); } else { bool flag2 = PirateProjectileSupport.IsStreamProjectile(weaponInfo, isLocal: false, id); __instance.AddProjectiles(owner, weaponInfo, false, pos, velocities, num, id, !flag2); } return false; } } [HarmonyPatch(typeof(ProjectileManager), "HitWater")] internal static class PirateProjectileWaterImpactPatch { private const int SurfaceSearchIterations = 10; private const float MinimumSearchDistance = 1f; private const float MaximumSearchDistance = 500f; private static bool Prefix(Projectile projectile) { //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_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_002d: Unknown result type (might be due to invalid IL or missing references) if (!PirateProjectileSupport.IsPirateProjectile(projectile.Id)) { return true; } Vector3 val = FindWaterCrossing(projectile); VFXManager.Play("WaterSplash", val, Vector3.zero); AudioManager.PlayRandomClipAt("ItemHitWaterMedium_V", 1, 3, val, true, (AudioDistance)1, 2f, 0.1f); return false; } private static Vector3 FindWaterCrossing(Projectile projectile) { //IL_0001: 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_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_000e: 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_0034: 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_0025: 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_0028: 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_0049: 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_0073: 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_007e: 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_0081: 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_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_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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) Vector3 previousPosition = projectile.PreviousPosition; Vector3 position = projectile.Position; float num = HeightAboveWater(previousPosition); float num2 = HeightAboveWater(position); if (HasCrossedSurface(num, num2)) { return BisectSurface(previousPosition, position, num); } Vector3 normalized = ((Vector3)(ref projectile.Velocity)).normalized; if (((Vector3)(ref normalized)).sqrMagnitude <= 0.001f || Mathf.Abs(normalized.y) <= 0.001f) { position.y = WaterManager.GetWaterHeight(position); return position; } Vector3 val = ((num2 >= 0f) ? normalized : (-normalized)); Vector3 val2 = position; float num3 = num2; for (float num4 = Mathf.Max(1f, ((Vector3)(ref projectile.Velocity)).magnitude * Time.fixedDeltaTime); num4 <= 500f; num4 *= 2f) { Vector3 val3 = val2 + val * num4; float secondHeight = HeightAboveWater(val3); if (HasCrossedSurface(num3, secondHeight)) { return BisectSurface(val2, val3, num3); } } position.y = WaterManager.GetWaterHeight(position); return position; } private static Vector3 BisectSurface(Vector3 start, Vector3 end, float startHeight) { //IL_0010: 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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: 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_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //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) bool flag = startHeight >= 0f; for (int i = 0; i < 10; i++) { Vector3 val = Vector3.Lerp(start, end, 0.5f); if (HeightAboveWater(val) >= 0f == flag) { start = val; } else { end = val; } } Vector3 val2 = Vector3.Lerp(start, end, 0.5f); val2.y = WaterManager.GetWaterHeight(val2); return val2; } private static float HeightAboveWater(Vector3 position) { //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) return position.y - WaterManager.GetWaterHeight(position); } private static bool HasCrossedSurface(float firstHeight, float secondHeight) { if (!(firstHeight >= 0f) || !(secondHeight <= 0f)) { if (firstHeight <= 0f) { return secondHeight >= 0f; } return false; } return true; } } internal static class PirateProjectileHitmarkerScope { [ThreadStatic] private static int _depth; public static bool IsActive => _depth > 0; public static bool Begin(Projectile projectile) { if (!PirateProjectileSupport.IsPirateProjectile(projectile.Id)) { return false; } _depth++; return true; } public static void End(bool began) { if (began && _depth > 0) { _depth--; } } } [HarmonyPatch(typeof(ProjectileManager), "Hit")] [HarmonyPriority(802)] internal static class PirateProjectileHitAudioScopePatch { private static void Prefix(Projectile projectile, out bool __state) { __state = PirateProjectileHitmarkerScope.Begin(projectile); } private static Exception? Finalizer(Exception? __exception, bool __state) { PirateProjectileHitmarkerScope.End(__state); return __exception; } } [HarmonyPatch(typeof(AudioManager), "PlayGlobalClip", new Type[] { typeof(string), typeof(bool), typeof(float), typeof(float), typeof(bool) })] internal static class PirateProjectileHitGlobalClipPatch { private static bool Prefix(string __0) { if (PirateProjectileHitmarkerScope.IsActive) { return __0 != "Hitmarker"; } return true; } } [HarmonyPatch(typeof(ProjectileManager), "AddProjectile")] internal static class PirateSingleProjectileDamagePatch { private static void Prefix(WeaponInfo weaponInfo, bool isLocal, uint id, ref bool fromNpc, out int __state) { __state = int.MinValue; if (PirateProjectileSupport.IsPirateProjectile(weaponInfo, isLocal, id)) { bool flag = PirateProjectileSupport.IsStreamProjectile(weaponInfo, isLocal, id); fromNpc = !flag; __state = weaponInfo.ProjectileDamage; if (!flag) { int num = Math.Max(0, PlayerManager.Players.Count - 1) * 2; weaponInfo.ProjectileDamage -= num; } } } private static void Postfix(WeaponInfo weaponInfo, int __state) { if (__state != int.MinValue) { weaponInfo.ProjectileDamage = __state; } } } [HarmonyPatch(typeof(ProjectileManager), "AddProjectiles")] internal static class PirateMultipleProjectileNpcPatch { private static void Prefix(WeaponInfo weaponInfo, bool isLocal, uint id, ref bool canHitOwner) { if (PirateProjectileSupport.IsPirateProjectile(weaponInfo, isLocal, id)) { canHitOwner = !PirateProjectileSupport.IsStreamProjectile(weaponInfo, isLocal, id); } } } [HarmonyPatch(typeof(SaveManager), "SaveServer")] internal static class CustomItemSavePatch { private static void Postfix() { CustomItemSaveSupport.SaveCurrentServer(); } } [HarmonyPatch(typeof(SaveManager), "SavePlayer")] internal static class CustomItemPlayerSavePatch { private static void Prefix(Player player, ref Item heldItem, ref Dictionary<byte, Item> slotToItem) { Item filteredHeldItem = heldItem; Dictionary<byte, Item> filteredSlotToItem = slotToItem; CustomItemSaveSupport.CapturePlayer(player, heldItem, slotToItem, ref filteredHeldItem, ref filteredSlotToItem); heldItem = filteredHeldItem; slotToItem = filteredSlotToItem; } } [HarmonyPatch(typeof(SaveManager), "CanSaveWorldItem")] internal static class CustomItemVanillaWorldSavePatch { private static void Postfix(Item item, ref bool __result) { if (__result && CustomItemSaveSupport.TryGetSaveKey(item, out string _)) { __result = false; } } } [HarmonyPatch(typeof(SaveManager), "SelectServer")] internal static class CustomItemServerSelectionPatch { private static void Postfix() { CustomItemSaveSupport.SelectServer(); } } [HarmonyPatch(typeof(SaveManager), "CreateServer")] internal static class CustomItemServerCreationPatch { private static void Postfix() { CustomItemSaveSupport.CreateServer(); } } [HarmonyPatch(typeof(SaveManager), "DeleteServer")] internal static class CustomItemServerDeletionPatch { private static void Prefix() { CustomItemSaveSupport.DeleteCurrentServer(); } } [HarmonyPatch(typeof(PlayerInventory), "LoadFromSave")] internal static class CustomItemPlayerLoadPatch { private static void Postfix(PlayerInventory __instance) { CustomItemSaveSupport.RestorePlayer(__instance); } } [HarmonyPatch(typeof(SaveManager), "LoadWorldItemsRoutine")] internal static class CustomItemWorldLoadPatch { private static void Postfix(ServerSaveObject saveToLoad, ref IEnumerator __result) { __result = CustomItemSaveSupport.AppendWorldRestore(__result, saveToLoad); } } [HarmonyPatch(typeof(RigidbodySync), "InheritFakeVel")] internal static class KinematicVelocityNoisePatch { private static bool Prefix(RigidbodySync __instance) { //IL_0002: 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) RigidbodySyncVelocitySupport.Apply(__instance, __instance.FakeVelocity, __instance.FakeAngularVelocity); return false; } } [HarmonyPatch(typeof(RigidbodySync), "ZeroVelocity")] internal static class KinematicZeroVelocityNoisePatch { private static bool Prefix(RigidbodySync __instance) { //IL_0001: 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) RigidbodySyncVelocitySupport.Apply(__instance, Vector3.zero, Vector3.zero); return false; } } internal static class RigidbodySyncVelocitySupport { public static void Apply(RigidbodySync sync, Vector3 linear, Vector3 angular) { //IL_0006: 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_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) Apply(((Component)sync).GetComponent<Rigidbody>(), linear, angular); Rigidbody[] extraHinges = sync.ExtraHinges; for (int i = 0; i < extraHinges.Length; i++) { Apply(extraHinges[i], linear, angular); } } private static void Apply(Rigidbody body, Vector3 linear, Vector3 angular) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)body) && !body.isKinematic) { body.linearVelocity = linear; body.angularVelocity = angular; } } } 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 ReinforcementCooldown = 30f; 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 StreamBattleDistanceBeyondBoundary = 350f; private const float StreamFleetSeparation = 82f; private const float StreamReinforcementSpawnDistance = 115f; private const float StreamBoatSpacing = 14f; private const float StreamReinforcementCooldown = 6f; private const float UpgradeMotorWeightMultiplier = 0.35f; private const float FleetWorthLowerBound = 0.75f; private const int WorthPercentPerDistanceBand = 10; private const int WorthNormalizationReference = 100; private const int NormalizedWorthPerDoubling = 100; private const int FallbackMotorWorthPerTier = 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 float _nextReinforcementTime; private int _preferredReinforcementBoatCount; private bool _loggedNoWeapons; private bool _motorWorthsLoaded; private bool _streamBattleSpawned; private bool _streamSuspendedForLoading; private bool _wasStreamMode; private Vector3 _streamBattleCenter; private Vector3 _streamBattleAxis = Vector3.forward; private float _nextCrimsonReinforcementTime; private float _nextAzureReinforcementTime; public void Initialize(OpenSeaConfig config, OpenSeaBoundary openSeaBoundary) { _config = config; _openSeaBoundary = openSeaBoundary; } private void Update() { if (Time.time < _nextCheckTime) { return; } _nextCheckTime = Time.time + 1f; if (IslandManager.IsLoading || !Object.op_Implicit((Object)(object)Island.CurIsland)) { _motorWorthsLoaded = false; if (_config.StreamModeEnabled) { _streamBattleSpawned = false; ResetStreamReinforcementTimers(); _streamSuspendedForLoading = true; } } else { if (!Object.op_Implicit((Object)(object)Server.Instance) || !((NetworkBehaviour)Server.Instance).IsServerInitialized) { return; } if (_streamSuspendedForLoading) { DespawnPirates(); _streamBattleSpawned = false; ResetStreamReinforcementTimers(); _streamSuspendedForLoading = false; } if (!_config.PiratesEnabled.Value) { DespawnPirates(); _streamBattleSpawned = false; ResetStreamReinforcementTimers(); return; } if (!_motorWorthsLoaded) { RefreshMotorWorths(); _motorWorthsLoaded = true; } if (_config.StreamModeEnabled) { if (!_wasStreamMode) { DespawnPirates(); _streamBattleSpawned = false; ResetStreamReinforcementTimers(); } _wasStreamMode = true; UpdateStreamMode(); return; } if (_wasStreamMode) { DespawnPirates(); _streamBattleSpawned = false; ResetStreamReinforcementTimers(); _wasStreamMode = false; } 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); 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 && Time.time >= _nextReinforcementTime) { if (TrySpawnPirates(val, num - activePirateWorth, openOceanPlayerWorth, num2)) { _nextReinforcementTime = Time.time + 30f; } } 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_00e5: 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_00ef: 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_011e: 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, _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); } _preferredReinforcementBoatCount = 0; return true; } private void SpawnPirateBoat(Player target, Boat boatPrefab, PirateBoatPlan plan, Vector3 seawardDirection, float formationAngle, int index, int boatCount) { //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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: 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 approachDirection = target.Transform.position - val2; approachDirection.y = 0f; Quaternion spawnRotation = Quaternion.LookRotation(((Vector3)(ref approachDirection)).normalized, Vector3.up); SpawnPirateBoatAt(boatPrefab, plan, val2, spawnRotation, approachDirection, PirateTeam.None, $"Pirate Boat {index + 1}"); } private Boat SpawnPirateBoatAt(Boat boatPrefab, PirateBoatPlan plan, Vector3 spawnPosition, Quaternion spawnRotation, Vector3 approachDirection, PirateTeam team, string name) { //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_0013: 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_00bf: Unknown result type (might be due to invalid IL or missing references) Boat val = Object.Instantiate<Boat>(boatPrefab, spawnPosition, spawnRotation); ((Object)val).name = name; AlignBoatForward(val, approachDirection); IReadOnlyList<PirateWeaponDefinition> readOnlyList = plan.Weapons.OrderBy((PirateWeaponDefinition weapon) => weapon.AttackRange).ToArray(); int count = readOnlyList.Count; float orbitRadius = readOnlyList.Max((PirateWeaponDefinition weapon) => weapon.AttackRange) * 0.25f; ConfigureLongRangeNetworkVisibility(val); PirateBoatSupport.RegisterServerPirate(val, readOnlyList, team, plan.Worth); ((Component)val).gameObject.AddComponent<PirateBoatController>().Initialize(val, readOnlyList, orbitRadius, _openSeaBoundary); ((NetworkBehaviour)Server.Instance).Spawn(((Component)val).gameObject, (NetworkConnection)null, default(Scene)); val._curSkin.Value = PirateBoatSupport.EncodeIdentity(count, team); val._boatUnlocked.Value = true; val._boatRadarUnlocked.Value = true; val._motorIndex.Value = (byte)plan.MotorIndex; _activePirateBoats[val] = plan.Worth; return val; } private void UpdateStreamMode() { RemoveDestroyedPirateBoats(); if (!_streamBattleSpawned) { TryStartStreamBattle(); return; } UpdateStreamBattleCenter(); GetStreamFleetState(PirateTeam.Crimson, out var boatCount, out var worth); GetStreamFleetState(PirateTeam.Azure, out var boatCount2, out var worth2); if (boatCount == 0 && boatCount2 == 0) { SpawnMatchedStreamReinforcements(); return; } TryCallStreamReinforcements(PirateTeam.Crimson, boatCount, worth, worth2); TryCallStreamReinforcements(PirateTeam.Azure, boatCount2, worth2, worth); } private void GetStreamFleetState(PirateTeam team, out int boatCount, out int worth) { boatCount = 0; worth = 0; foreach (KeyValuePair<Boat, int> activePirateBoat in _activePirateBoats) { if (PirateBoatSupport.IsActiveStreamCombatant(activePirateBoat.Key) && PirateBoatSupport.GetTeam(activePirateBoat.Key) == team) { boatCount++; worth = SaturatingAdd(worth, activePirateBoat.Value); } } } private void TryCallStreamReinforcements(PirateTeam team, int ownBoatCount, int ownWorth, int opposingWorth) { if (ownWorth >= opposingWorth || opposingWorth <= 0) { return; } float num = ((team == PirateTeam.Crimson) ? _nextCrimsonReinforcementTime : _nextAzureReinforcementTime); if (Time.time < num) { return; } int num2 = Mathf.Clamp(_config.StreamBoatsPerTeam, 1, 4) - ownBoatCount; if (num2 <= 0) { return; } Boat boatPrefab = GetBoatPrefab(); IReadOnlyList<Weapon> readOnlyList = FindPirateWeapons(); if (!Object.op_Implicit((Object)(object)boatPrefab) || readOnlyList.Count == 0) { return; } int worthRequirement = opposingWorth - ownWorth; int preferredBoatCount = Mathf.Min(SelectPreferredReinforcementBoatCount(), num2); PirateFleetPlan pirateFleetPlan = SelectFleetForWorth(boatPrefab, readOnlyList, worthRequirement, preferredBoatCount, num2); if (pirateFleetPlan != null) { SpawnStreamReinforcementFleet(boatPrefab, pirateFleetPlan, team, ownBoatCount); float num3 = Time.time + 6f; if (team == PirateTeam.Crimson) { _nextCrimsonReinforcementTime = num3; } else { _nextAzureReinforcementTime = num3; } Plugin.Log.LogInfo((object)($"{team} fleet called {pirateFleetPlan.Boats.Count} reinforcement boat(s) " + $"after falling behind {ownWorth} to {opposingWorth} worth.")); } } private void TryStartStreamBattle() { //IL_002c: 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_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0063: 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_0056: 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_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_00fe: 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_0109: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0179: 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) Boat boatPrefab = GetBoatPrefab(); IReadOnlyList<Weapon> readOnlyList = FindPirateWeapons(); if (Object.op_Implicit((Object)(object)boatPrefab) && readOnlyList.Count != 0 && _openSeaBoundary.SampleCount != 0) { Vector3 val = SpawnManager.BoatSpawnPos - Island.IslandPos; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude <= 0.01f) { val = Vector3.forward; } ((Vector3)(ref val)).Normalize(); float num = Mathf.Atan2(val.z, val.x); if (num < 0f) { num += MathF.PI * 2f; } int sampleIndex = Mathf.RoundToInt(num * (float)_openSeaBoundary.SampleCount / (MathF.PI * 2f)); _streamBattleCenter = _openSeaBoundary.GetBoundaryPoint(sampleIndex, _openSeaBoundary.DistanceBeyondIsland + 350f); _streamBattleCenter.y = SpawnManager.BoatSpawnPos.y; _streamBattleAxis = Quaternion.AngleAxis(Random.Range(-28f, 28f), Vector3.up) * val; Vector3 val2 = Vector3.Cross(Vector3.up, _streamBattleAxis); Vector3 normalized = ((Vector3)(ref val2)).normalized; int num2 = Mathf.Clamp(_config.StreamBoatsPerTeam, 1, 4); for (int i = 0; i < num2; i++) { PirateBoatPlan plan = CreateRandomStreamBoatPlan(boatPrefab, readOnlyList); float formationOffset = ((float)i - (float)(num2 - 1) * 0.5f) * 14f; SpawnStreamTeamBoat(boatPrefab, plan, _streamBattleCenter, _streamBattleAxis, normalized, formationOffset, PirateTeam.Crimson, i); SpawnStreamTeamBoat(boatPrefab, plan, _streamBattleCenter, _streamBattleAxis, normalized, formationOffset, PirateTeam.Azure, i); } _streamBattleSpawned = true; Plugin.Log.LogInfo((object)$"Started continuous stream combat with {num2} boats per fleet."); } } private void SpawnStreamTeamBoat(Boat boatPrefab, PirateBoatPlan plan, Vector3 battleCenter, Vector3 battleAxis, Vector3 formationAxis, float formationOffset, PirateTeam team, int index) { //IL_0012: 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) //IL_0022: 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_002c: 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_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_003b: 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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: U