Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimHelmsman v0.2.42
BepInEx/plugins/ValheimHelmsman/ValheimHelmsman.dll
Decompiled 4 days 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.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using HarmonyLib; using Helmsman; using Helmsman.Core; using Helmsman.Structures; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Jotunn.Utils; using MagicaCloth2; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Splatform; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.UI; using VikingBirds; using VikingGull; [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("ValheimHelmsman")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.42.0")] [assembly: AssemblyInformationalVersion("0.2.42+36c9918e3a0f86f4012d19cdddf84217608f7fa4")] [assembly: AssemblyProduct("ValheimHelmsman")] [assembly: AssemblyTitle("ValheimHelmsman")] [assembly: AssemblyVersion("0.2.42.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 VikingGull { public sealed class GullHelmet : MonoBehaviour { private Material template; private readonly List<Mesh> meshes = new List<Mesh>(); private readonly List<Material> materials = new List<Material>(); internal static readonly Vector3 Crown = new Vector3(0f, 1.216f, -0.345f); internal static GullHelmet Create(Transform parent, Vector3 position, Material? template = null) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: 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) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Viking gull helmet"); val.transform.SetParent(parent, false); val.transform.localPosition = position; GullHelmet gullHelmet = val.AddComponent<GullHelmet>(); Renderer component = ((Component)parent).GetComponent<Renderer>(); gullHelmet.template = (Material)(Object.op_Implicit((Object)(object)template) ? ((object)template) : ((object)(Object.op_Implicit((Object)(object)component) ? component.sharedMaterial : null))); Material material = gullHelmet.Material(new Color(0.27f, 0.3f, 0.32f), 0.65f); Material material2 = gullHelmet.Material(new Color(0.57f, 0.35f, 0.13f), 0.55f); Material material3 = gullHelmet.Material(new Color(0.82f, 0.75f, 0.54f), 0.05f); gullHelmet.Surface("Iron cap", material, 32, 12, delegate(float u, float v) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) float num4 = u * (float)Math.PI * 2f; float num5 = v * (float)Math.PI * 0.5f; return new Vector3(Mathf.Cos(num4) * 0.079f * Mathf.Cos(num5), 0.094f * Mathf.Sin(num5), Mathf.Sin(num4) * 0.103f * Mathf.Cos(num5)); }); gullHelmet.Surface("Bronze brow band", material2, 32, 2, delegate(float u, float v) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) float num4 = u * (float)Math.PI * 2f; return new Vector3(Mathf.Cos(num4) * 0.082f, -0.006f + v * 0.024f, Mathf.Sin(num4) * 0.106f); }); for (int num = -1; num <= 1; num += 2) { int s = num; gullHelmet.Surface("Curved horn", material3, 16, 14, delegate(float u, float v) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) float num4 = u * (float)Math.PI * 2f; float num5 = 0.031f * (1f - v); return new Vector3((float)s * (0.067f + 0.143f * v) + num5 * Mathf.Cos(num4) * 0.5f, 0.046f + 0.032f * v + 0.135f * v * v + num5 * Mathf.Sin(num4), 0.008f + 0.042f * v + num5 * Mathf.Cos(num4)); }); } gullHelmet.Surface("Rolled helmet rim", material2, 40, 8, delegate(float u, float v) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) float num4 = u * (float)Math.PI * 2f; float num5 = v * (float)Math.PI * 2f; return new Vector3(Mathf.Cos(num4) * (0.082f + 0.003f * Mathf.Cos(num5)), 0.006f + 0.003f * Mathf.Sin(num5), Mathf.Sin(num4) * (0.106f + 0.003f * Mathf.Cos(num5))); }); gullHelmet.Surface("Crown reinforcing strip", material2, 4, 24, delegate(float u, float v) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) float num4 = v * (float)Math.PI; return new Vector3((u - 0.5f) * 0.011f, 0.096f * Mathf.Sin(num4), 0.105f * Mathf.Cos(num4)); }); for (int num2 = 0; num2 < 10; num2++) { float num3 = (float)num2 * (float)Math.PI * 2f / 10f; Vector3 center = new Vector3(0.085f * Mathf.Cos(num3), 0.011f, 0.109f * Mathf.Sin(num3)); gullHelmet.Surface("Brow rivet", material2, 8, 6, delegate(float u, float v) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) float num4 = u * (float)Math.PI * 2f; float num5 = v * (float)Math.PI; return center + new Vector3(Mathf.Cos(num4) * Mathf.Sin(num5), Mathf.Cos(num5), Mathf.Sin(num4) * Mathf.Sin(num5)) * 0.0038f; }); } return gullHelmet; } private Material Material(Color color, float metallic) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) Material val = GullMaterials.Create(template, color, metallic); if (val.HasProperty("_MainTex")) { val.SetTexture("_MainTex", (Texture)(object)Texture2D.whiteTexture); } materials.Add(val); return val; } private void Surface(string label, Material material, int sides, int rings, Func<float, float, Vector3> point) { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Expected O, but got Unknown //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_016d: 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) List<Vector3> list = new List<Vector3>(); List<int> list2 = new List<int>(); int num = (sides + 1) * (rings + 1); for (int i = 0; i < 2; i++) { for (int j = 0; j <= rings; j++) { for (int k = 0; k <= sides; k++) { list.Add(point((float)k / (float)sides, (float)j / (float)rings)); } } } for (int l = 0; l < 2; l++) { for (int m = 0; m < rings; m++) { for (int n = 0; n < sides; n++) { int num2 = l * num + m * (sides + 1) + n; int num3 = num2 + 1; int num4 = num2 + sides + 1; int num5 = num4 + 1; if (l == 0) { list2.AddRange(new int[6] { num2, num4, num3, num3, num4, num5 }); } else { list2.AddRange(new int[6] { num2, num3, num4, num3, num5, num4 }); } } } } Mesh val = new Mesh { name = label }; val.SetVertices(list); val.SetTriangles(list2, 0); val.RecalculateNormals(); val.RecalculateBounds(); meshes.Add(val); GameObject val2 = new GameObject(label); val2.transform.SetParent(((Component)this).transform, false); val2.AddComponent<MeshFilter>().sharedMesh = val; ((Renderer)val2.AddComponent<MeshRenderer>()).sharedMaterial = material; } internal void FollowStanding(Quaternion head, Quaternion body, Vector3 neck, Vector3 hips, float crouch) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_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_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0022: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0051: Unknown result type (might be due to invalid IL or missing references) Vector3 val = neck + head * (Crown - neck); ((Component)this).transform.localPosition = hips + body * (val - hips) - Vector3.up * crouch; ((Component)this).transform.localRotation = body * head; } internal static void FitFlying(GameObject model, Transform actor) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_0099: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) SkinnedMeshRenderer componentInChildren = model.GetComponentInChildren<SkinnedMeshRenderer>(); if (!Object.op_Implicit((Object)(object)componentInChildren)) { return; } Transform val = ((Component)componentInChildren).transform; bool flag = false; Transform[] bones = componentInChildren.bones; foreach (Transform val2 in bones) { if (Object.op_Implicit((Object)(object)val2) && ((Object)val2).name == "Bone") { val = val2; flag = true; break; } } if (!flag) { return; } Mesh val3 = new Mesh(); try { componentInChildren.BakeMesh(val3); Vector3[] vertices = val3.vertices; float num = float.NegativeInfinity; float num2 = float.PositiveInfinity; for (int j = 0; j < vertices.Length; j++) { vertices[j] = actor.InverseTransformPoint(((Component)componentInChildren).transform.TransformPoint(vertices[j])); num = Mathf.Max(num, vertices[j].z); num2 = Mathf.Min(num2, vertices[j].z); } Vector3 val4 = default(Vector3); ((Vector3)(ref val4))..ctor(0f, float.NegativeInfinity, 0f); Vector3[] array = vertices; foreach (Vector3 val5 in array) { if (Mathf.Abs(val5.x) < 0.09f && val5.z > Mathf.Lerp(num2, num, 0.68f) && val5.y > val4.y) { val4 = val5; } } if (!float.IsInfinity(val4.y)) { GullHelmet gullHelmet = Create(actor, new Vector3(0f, val4.y - 0.02f * model.transform.localScale.y, val4.z), ((Renderer)componentInChildren).sharedMaterial); ((Component)gullHelmet).transform.localScale = model.transform.localScale; ((Component)gullHelmet).transform.localRotation = Quaternion.Euler(0f, 180f, 0f); ((Component)gullHelmet).transform.SetParent(val, true); if (Object.op_Implicit((Object)(object)componentInChildren.sharedMesh) && componentInChildren.sharedMesh.isReadable) { Mesh item = (componentInChildren.sharedMesh = GullSurfaceRefinement.Create(componentInChildren.sharedMesh, perched: false)); gullHelmet.meshes.Add(item); } } } finally { Object.Destroy((Object)(object)val3); } } private void OnDestroy() { foreach (Mesh mesh in meshes) { if (Object.op_Implicit((Object)(object)mesh)) { Object.Destroy((Object)(object)mesh); } } foreach (Material material in materials) { if (Object.op_Implicit((Object)(object)material)) { Object.Destroy((Object)(object)material); } } } } } namespace Helmsman { public sealed class BirdInteraction : MonoBehaviour, Interactable, Hoverable { internal Func<string> Label = () => "Bird"; internal Func<string> Hint = () => ""; internal Func<Humanoid, bool>? Use; public string GetHoverName() { return Label(); } public float GetHoverOffset() { return 0.15f; } public string GetHoverText() { return Hint(); } public bool Interact(Humanoid user, bool hold, bool alt) { if (!hold && (Object)(object)user == (Object)(object)Player.m_localPlayer) { return Use?.Invoke(user) ?? false; } return false; } public bool UseItem(Humanoid user, ItemData item) { return false; } } internal static class BoatyardModels { private static readonly List<Sprite> icons = new List<Sprite>(); internal static void RefreshIcon(GameObject prefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //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_0016: 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_002b: 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_003d: 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_0062: Expected O, but got Unknown if ((int)SystemInfo.graphicsDeviceType == 4) { return; } try { Sprite val = RenderManager.Instance.Render(new RenderRequest(prefab) { Rotation = RenderManager.IsometricRotation, Width = 128, Height = 128, UseCache = true, TargetPlugin = ((BaseUnityPlugin)Plugin.Instance).Info.Metadata, ParticleSimulationTime = -1f }); if (Object.op_Implicit((Object)(object)val)) { icons.Add(val); ItemDrop component = prefab.GetComponent<ItemDrop>(); if (Object.op_Implicit((Object)(object)component)) { component.m_itemData.m_shared.m_icons = (Sprite[])(object)new Sprite[1] { val }; } Piece component2 = prefab.GetComponent<Piece>(); if (Object.op_Implicit((Object)(object)component2)) { component2.m_icon = val; } } } catch (Exception ex) { Plugin.Instance.Record("Boatyard icon unavailable for " + ((Object)prefab).name + ": " + ex.Message); } } internal static void Release() { foreach (Sprite icon in icons) { if (Object.op_Implicit((Object)(object)icon)) { if (Object.op_Implicit((Object)(object)icon.texture)) { Object.Destroy((Object)(object)icon.texture); } Object.Destroy((Object)(object)icon); } } icons.Clear(); } } internal static class BuildMenuCategory { internal const string Token = "$helmsman_build_category"; private static readonly HashSet<string> Prefabs = new HashSet<string>(HarborCatalog.Pieces.Select((HarborEntry p) => p.Prefab).Concat(ShipConstruction.Blueprints.Select((ShipBlueprint b) => b.Prefab)).Concat(new string[7] { "helmsman_dock_ward", "CarpentersTable", "HelmsmanSlipway", "HelmsmanToolRack", "HelmsmanCaulkingStation", "HelmsmanRiggingRack", "HelmsmanPaintStand" }), StringComparer.Ordinal); internal static bool Contains(Piece piece) { if (Object.op_Implicit((Object)(object)piece)) { return Prefabs.Contains(Utils.GetPrefabName(((Component)piece).gameObject)); } return false; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class AddHelmsmanUsageCategory { [HarmonyPriority(0)] private static void Postfix(ref UsageTagFlags[] ___m_usageTags, ref string[] ___m_usageTagDisplayNames) { if (!___m_usageTagDisplayNames.Contains("$helmsman_build_category")) { ___m_usageTags = ___m_usageTags.Concat((IEnumerable<UsageTagFlags>)(object)new UsageTagFlags[1]).ToArray(); ___m_usageTagDisplayNames = ___m_usageTagDisplayNames.Concat(new string[1] { "$helmsman_build_category" }).ToArray(); } } } [HarmonyPatch(typeof(ByUsagePieceList), "GetTagDisplayName")] internal static class HelmsmanUsageLabel { private static void Postfix(ref string __result) { if (__result == "$helmsman_build_category") { __result = "Helmsman"; } } } [HarmonyPatch(typeof(ByUsagePieceList), "UpdateAvailableTags")] internal static class HideUnrelatedHelmsmanCategory { private static void Postfix(PieceTable pieceTable, string[] ___m_usageTagDisplayNames, List<int> ___m_availableTags) { int num = Array.IndexOf(___m_usageTagDisplayNames, "$helmsman_build_category"); if (num >= 0 && !pieceTable.m_availablePieces.Any(BuildMenuCategory.Contains)) { ___m_availableTags.Remove(num); } } } [HarmonyPatch(typeof(ByUsagePieceList), "GetAvailablePiecesWithTag")] internal static class FilterHelmsmanUsageCategory { [HarmonyPriority(800)] private static bool Prefix(int tagId, PieceTable pieceTable, IList<Piece> resultOut, string[] ___m_usageTagDisplayNames) { if (tagId < 0 || tagId >= ___m_usageTagDisplayNames.Length || ___m_usageTagDisplayNames[tagId] != "$helmsman_build_category") { return true; } foreach (Piece availablePiece in pieceTable.m_availablePieces) { if (BuildMenuCategory.Contains(availablePiece) || availablePiece.m_repairPiece || availablePiece.m_removePiece) { resultOut.Add(availablePiece); } } return false; } } public sealed class CargoOrder : MonoBehaviour { private Ship ship; private GullGuide gull; private Player requester; private object request; private bool ended; internal string Status { get; private set; } = ""; internal static bool Start(Ship ship, GullGuide gull, out string reason) { reason = "Speak to the landed gull aboard a stopped boat with Quartermaster in range."; if (!Plugin.LocalSession || !Object.op_Implicit((Object)(object)gull) || !gull.ReadyOn(ship) || !QuartermasterBridge.Available) { return false; } if (Object.op_Implicit((Object)(object)Plugin.Instance.Cargo)) { reason = "Finish or stop the current cargo request first."; return false; } try { reason = QuartermasterBridge.Check(ship); if (reason.Length > 0) { return false; } object obj = QuartermasterBridge.BeginUnload(ship); CargoOrder cargoOrder = ((Component)ship).gameObject.AddComponent<CargoOrder>(); cargoOrder.ship = ship; cargoOrder.gull = gull; cargoOrder.requester = Player.m_localPlayer; cargoOrder.request = obj; Plugin.Instance.Cargo = cargoOrder; cargoOrder.Status = "Unloading cargo into base storage…"; reason = cargoOrder.Status; return true; } catch (Exception ex) { reason = ex.GetBaseException().Message; return false; } } private void Update() { if (ended) { return; } if (!Plugin.LocalSession || !QuartermasterBridge.Available || !Object.op_Implicit((Object)(object)ship) || !Object.op_Implicit((Object)(object)gull) || !gull.ReadyOn(ship) || !Object.op_Implicit((Object)(object)requester) || (Object)(object)requester != (Object)(object)Player.m_localPlayer || ((Character)requester).IsDead() || !ship.IsPlayerInBoat(requester)) { Stop("Cargo request ended."); return; } try { string text = QuartermasterBridge.Check(ship); if (text.Length > 0) { Stop(text); } else { if (gull.SortingCargo) { return; } if (QuartermasterBridge.Finished(request)) { Stop(QuartermasterBridge.Status(request), completed: true); return; } ItemData val = QuartermasterBridge.UnloadNext(request); Status = QuartermasterBridge.Status(request); if (val != null) { gull.SortCargo(val); } else if (QuartermasterBridge.Finished(request)) { Stop(Status, completed: true); } } } catch (Exception ex) { Plugin.Instance.Error(ex); Stop("Cargo handling stopped: " + ex.GetBaseException().Message); } } internal void Stop(string reason, bool completed = false) { if (!ended) { ended = true; if (request != null && QuartermasterBridge.Available) { QuartermasterBridge.Cancel(request); } if (Object.op_Implicit((Object)(object)gull)) { gull.EndCargoSort(!completed); } if (Object.op_Implicit((Object)(object)Plugin.Instance) && (Object)(object)Plugin.Instance.Cargo == (Object)(object)this) { Plugin.Instance.Cargo = null; } Status = reason; if (completed && Object.op_Implicit((Object)(object)gull)) { gull.Speak(reason, important: true); } Plugin.Message(reason); Object.Destroy((Object)(object)this); } } private void OnDestroy() { if (!ended) { Stop("Cargo request ended."); } } } internal static class CarpenterRecipes { private static readonly HashSet<string> Items = new HashSet<string>(from item in HarborCatalog.Items where item.Recipe.Length > 0 select item.Prefab, StringComparer.Ordinal); internal static bool IsTable(CraftingStation station) { if (Object.op_Implicit((Object)(object)station)) { return Utils.GetPrefabName(((Component)station).gameObject) == "CarpentersTable"; } return false; } internal static bool Allows(Recipe recipe) { if (Object.op_Implicit((Object)(object)recipe) && Object.op_Implicit((Object)(object)recipe.m_item) && IsTable(recipe.m_craftingStation)) { return Items.Contains(Utils.GetPrefabName(((Component)recipe.m_item).gameObject)); } return false; } internal static void Configure(CraftingStation station) { station.m_name = "Carpenter's Table"; station.m_showBasicRecipies = false; station.m_upgrader = false; } } [HarmonyPatch(typeof(Player), "GetAvailableRecipes")] internal static class CarpenterRecipeList { [HarmonyPriority(0)] private static void Postfix(Player __instance, ref List<Recipe> available) { if (CarpenterRecipes.IsTable(__instance.GetCurrentCraftingStation())) { available.RemoveAll((Recipe recipe) => !CarpenterRecipes.Allows(recipe)); } } } [Serializable] public sealed class ConstructionOrder { public string blueprint = ""; public string recipe = ""; public string name = ""; public long started; public long creator; public float duration; public bool freeBuild; public Vector3 position; public float heading; internal bool Valid { get { if (ShipConstruction.Find(blueprint) != null && ShipConstruction.ValidDuration((double)duration) && started >= 0 && Finite(position.x) && Finite(position.y) && Finite(position.z) && Finite(heading) && name != null && name.Length <= 48) { return ShipConstruction.ValidRecipe(blueprint, recipe); } return false; } } private static bool Finite(float n) { if (!float.IsInfinity(n)) { return !float.IsNaN(n); } return false; } } internal static class ConstructionSite { internal static bool Ready(Player player, Vector3 bench, Vector3 site, out string reason) { //IL_0021: 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_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: 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) if (!Object.op_Implicit((Object)(object)player) || ((Character)player).IsDead() || ((Character)player).IsTeleporting() || !WorkshopRange.Connected(player, bench, site, ((Component)player).transform.position)) { reason = "Construction paused: return to the workshop area to resume."; return false; } if (!Object.op_Implicit((Object)(object)ZNetScene.instance) || !ZNetScene.instance.IsAreaReady(bench) || !ZNetScene.instance.IsAreaReady(site)) { reason = "Construction paused: waiting for the work area to finish loading."; return false; } reason = ""; return true; } } [Serializable] public sealed class Berth { public int version = 1; public string name = "Unnamed dock"; public Vector3 position; public float heading; public bool reverseDeparture; public float reverseDistance = 30f; public bool configured; public string shipType = "Karve"; public Vector3 Forward => Quaternion.Euler(0f, heading, 0f) * Vector3.forward; public Vector3 Approach => position - Forward * 35f; public Vector3 Exit => position + Forward * (reverseDeparture ? (0f - reverseDistance) : 35f); public bool ValidData { get { if (version == 1 && !string.IsNullOrWhiteSpace(shipType) && !string.IsNullOrWhiteSpace(name) && name.Length <= 48 && Finite(position.x) && Finite(position.y) && Finite(position.z) && Finite(heading) && Finite(reverseDistance) && reverseDistance >= 16f) { return reverseDistance <= 80f; } return false; } } public Berth Copy() { return JsonUtility.FromJson<Berth>(JsonUtility.ToJson((object)this)); } private static bool Finite(float v) { if (!float.IsNaN(v)) { return !float.IsInfinity(v); } return false; } } public sealed class DockRecord { internal bool Temporary; public ZDOID Id; public Berth Berth; public Vector3 MarkerPosition; } public sealed class DockDirectory : MonoBehaviour { internal static readonly int DataKey = StringExtensionMethods.GetStableHashCode("helmsman_berth_v1"); private long world; internal List<DockRecord> Records { get; private set; } = new List<DockRecord>(); private IEnumerator Start() { while (true) { if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZDOMan.instance == null || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { Records.Clear(); world = 0L; yield return (object)new WaitForSeconds(1f); continue; } long worldUID = ZNet.instance.GetWorldUID(); if (worldUID != world) { Records.Clear(); world = worldUID; } long scanWorld = world; List<ZDO> zdos = new List<ZDO>(); int cursor = 0; while (ZDOMan.instance != null && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.GetWorldUID() == world && !ZDOMan.instance.GetAllZDOsWithPrefabIterative("helmsman_dock_ward", zdos, ref cursor)) { yield return null; } if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.GetWorldUID() != scanWorld || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { Records.Clear(); continue; } List<DockRecord> list = new List<DockRecord>(); HashSet<ZDOID> hashSet = new HashSet<ZDOID>(); foreach (ZDO item in zdos) { if (hashSet.Add(item.m_uid)) { Berth berth = Read(item); if (berth != null && berth.configured) { list.Add(new DockRecord { Id = item.m_uid, Berth = berth, MarkerPosition = item.GetPosition() }); } } } Records = list; yield return (object)new WaitForSeconds(3f); } } internal static Berth? Read(ZDO? zdo) { if (zdo == null || !zdo.IsValid()) { return null; } string text = zdo.GetString(DataKey, ""); if (text.Length == 0 || text.Length > 4096) { return null; } try { Berth berth = JsonUtility.FromJson<Berth>(text); return (berth != null && berth.ValidData) ? berth : null; } catch (ArgumentException) { return null; } } internal static DockRecord? Resolve(ZDOID id) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0053: 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) ZDOMan instance = ZDOMan.instance; ZDO val = ((instance != null) ? instance.GetZDO(id) : null); if (val == null || val.GetPrefab() != StringExtensionMethods.GetStableHashCode("helmsman_dock_ward")) { return null; } Berth berth = Read(val); if (berth == null || !berth.configured) { return null; } return new DockRecord { Id = id, Berth = berth, MarkerPosition = val.GetPosition() }; } } public sealed class DockMarker : MonoBehaviour, Interactable, Hoverable { private ZNetView view; private GullGuide? gull; internal ZDOID Id => view.GetZDO().m_uid; internal Berth Settings => DockDirectory.Read(Object.op_Implicit((Object)(object)view) ? view.GetZDO() : null) ?? new Berth { position = ((Component)this).transform.position + ((Component)this).transform.forward * 12f, heading = ((Component)this).transform.eulerAngles.y }; internal bool Ready { get { if (Object.op_Implicit((Object)(object)view)) { return view.IsValid(); } return false; } } private void Awake() { view = ((Component)this).GetComponent<ZNetView>(); } private IEnumerator Start() { while (!Ready || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { yield return (object)new WaitForSeconds(0.5f); } if (!Object.op_Implicit((Object)(object)((Component)this).GetComponent<SummonBeacon>())) { ((Component)this).gameObject.AddComponent<SummonBeacon>(); } while (Ready) { if (ScoutGullPresentation.BusyDock(Id)) { yield return (object)new WaitForSeconds(1f); continue; } if (!Object.op_Implicit((Object)(object)gull)) { gull = GullGuide.Traveller(Id); } if (!Object.op_Implicit((Object)(object)gull)) { gull = GullGuide.Create(((Component)this).transform.position + Vector3.up * 2f, this, null); } yield return (object)new WaitForSeconds(1f); } } internal void SendScout() { if (Object.op_Implicit((Object)(object)gull) && !gull.IsTravelling) { gull.FlyAway(); } } internal GullGuide CallGuide(Ship ship) { //IL_0040: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)gull)) { gull = GullGuide.Create(((Component)this).transform.position + Vector3.up * 2f, this, null); } gull.ReserveDock(Id); gull.Visit(ship); return gull; } internal GullGuide FetchGuide(Vector3 target) { //IL_003f: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)gull)) { gull = GullGuide.Create(((Component)this).transform.position + Vector3.up * 2f, this, null); } gull.Fetch(target); return gull; } internal GullGuide BoardGuide(Voyage voyage) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: 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) if (!Object.op_Implicit((Object)(object)gull)) { gull = GullGuide.Create(((Component)this).transform.position + Vector3.up * 2f, this, null); } gull.Board(voyage); return gull; } private void OnDestroy() { if (Object.op_Implicit((Object)(object)gull) && !gull.IsTravelling) { Object.Destroy((Object)(object)((Component)gull).gameObject); } } public string GetHoverName() { return "Dock Ward — " + Settings.name; } public float GetHoverOffset() { return 0f; } public string GetHoverText() { return Localization.instance.Localize(GetHoverName() + "\n[<color=yellow><b>$KEY_Use</b></color>] Configure berth"); } public bool Interact(Humanoid user, bool hold, bool alt) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (hold || !Ready || (Object)(object)user != (Object)(object)Player.m_localPlayer || !PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, true)) { return false; } if (!Plugin.LocalSession) { Plugin.Message("Join a world before configuring a dock."); return false; } Plugin.Instance.UI.OpenDock(this); return true; } public bool UseItem(Humanoid user, ItemData item) { return false; } internal bool Save(Berth berth) { //IL_0030: 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) if (!Plugin.LocalSession || !Ready || !berth.ValidData) { return false; } if (Vector3.Distance(((Component)Player.m_localPlayer).transform.position, ((Component)this).transform.position) > 10f) { return false; } Berth copy = berth.Copy(); WorkstationLease lease = ((Component)this).GetComponent<WorkstationLease>(); Plugin.Message(lease.Run(delegate { //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (!lease.Held || !PrivateArea.CheckAccess(((Component)this).transform.position, 0f, false, true)) { return "Dock access changed; settings were not saved."; } view.GetZDO().Set(DockDirectory.DataKey, JsonUtility.ToJson((object)copy)); return "Berth saved."; })); return true; } } internal static class FinalFleetModels { private sealed class Part { internal string Group = ""; internal Mesh Mesh; internal Material Material; } private sealed class Model { internal Spec Spec; internal readonly List<Part> Parts = new List<Part>(); } [Serializable] internal sealed class Point { public string kind = ""; public float[] position = Array.Empty<float>(); public float[] exit = Array.Empty<float>(); public float[] facing = Array.Empty<float>(); public float[] size = Array.Empty<float>(); } [Serializable] internal sealed class Solid { public float[] position = Array.Empty<float>(); public float[] size = Array.Empty<float>(); } [Serializable] internal sealed class HullSolid { public float[] vertices = Array.Empty<float>(); public int[] triangles = Array.Empty<int>(); } [Serializable] internal sealed class Sheet { public float[] head = Array.Empty<float>(); public float[] foot = Array.Empty<float>(); } [Serializable] internal sealed class Spec { public string prefab = ""; public string name = ""; public float length; public float beam; public float walkHeight; public float waterline; public float airHeight; public float cargoHeight; public float[] sailPivot = Array.Empty<float>(); public float[] rudderPivot = Array.Empty<float>(); public HullSolid[] hullSolids = Array.Empty<HullSolid>(); public HullSolid[] cargoSolids = Array.Empty<HullSolid>(); public float[][] waterMask = Array.Empty<float[]>(); public Point[] points = Array.Empty<Point>(); public Solid[] colliders = Array.Empty<Solid>(); public Sheet[] sheets = Array.Empty<Sheet>(); } private static readonly Dictionary<string, Model> cache = new Dictionary<string, Model>(); private static readonly List<Object> owned = new List<Object>(); internal static bool Has(string name) { return new string[9] { "MercantShip", "BigCargoShip", "WarShip", "HerculeShip", "LittleBoat", "HelmsmanCurrach", "HelmsmanDugout", "HelmsmanFinewoodKayak", "HelmsmanTandemKayak" }.Contains(name); } internal static bool PaddleCraft(string name) { switch (name) { case "HelmsmanDugout": case "HelmsmanFinewoodKayak": case "HelmsmanTandemKayak": return true; default: return false; } } private static Vector3 V(float[] values) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (values.Length != 3 || values.Any((float v) => float.IsNaN(v) || float.IsInfinity(v))) { throw new InvalidDataException("Invalid final fleet vector"); } return new Vector3(values[0], values[1], values[2]); } private static int Count(BinaryReader r, int max) { int num = r.ReadInt32(); if (num < 0 || num > max) { throw new InvalidDataException("Invalid final fleet count"); } return num; } private static byte[] Bytes(BinaryReader r, int max) { int num = Count(r, max); byte[] array = r.ReadBytes(num); if (array.Length != num) { throw new EndOfStreamException(); } return array; } private static string Text(BinaryReader r, int max = 1024) { return Encoding.UTF8.GetString(Bytes(r, max)); } private static float Number(BinaryReader r) { float num = r.ReadSingle(); if (float.IsNaN(num) || float.IsInfinity(num) || Math.Abs(num) > 100000f) { throw new InvalidDataException("Invalid final fleet coordinate"); } return num; } private static Model Load(string name) { //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Expected O, but got Unknown if (cache.TryGetValue(name, out Model value)) { return value; } using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("Helmsman.Ships.final." + name + ".bin.gz") ?? throw new InvalidDataException("Missing final ship " + name); using GZipStream input = new GZipStream(stream, CompressionMode.Decompress); using BinaryReader binaryReader = new BinaryReader(input); if (Encoding.ASCII.GetString(binaryReader.ReadBytes(4)) != "HMF1") { throw new InvalidDataException("Unknown final fleet format"); } Model model = new Model { Spec = ReadSpecification(Text(binaryReader, 1000000), name) }; int num = Count(binaryReader, 128); Material[] array = (Material[])(object)new Material[num]; string[] array2 = new string[num]; bool flag = SmallCraftFinish.Applies(name); Color authored = default(Color); for (int i = 0; i < num; i++) { string text = Text(binaryReader); Bytes(binaryReader, 1048576); ((Color)(ref authored))..ctor(Number(binaryReader), Number(binaryReader), Number(binaryReader), Number(binaryReader)); Material val = ImportedShipMaterials.FleetMaterial(text, authored, flag); owned.Add((Object)(object)val); array[i] = val; array2[i] = text; } int num2 = Count(binaryReader, 256); for (int j = 0; j < num2; j++) { string text2 = Text(binaryReader); int num3 = Count(binaryReader, num - 1); int num4 = Count(binaryReader, 1000000); Vector3[] array3 = (Vector3[])(object)new Vector3[num4]; Vector3[] array4 = (Vector3[])(object)new Vector3[num4]; Vector2[] array5 = (Vector2[])(object)new Vector2[num4]; for (int k = 0; k < num4; k++) { array3[k] = new Vector3(Number(binaryReader), Number(binaryReader), Number(binaryReader)); array4[k] = new Vector3(Number(binaryReader), Number(binaryReader), Number(binaryReader)); array5[k] = new Vector2(Number(binaryReader), Number(binaryReader)); } int num5 = Count(binaryReader, 6000000); if (num5 % 3 != 0) { throw new InvalidDataException("Invalid final ship triangles"); } int[] array6 = new int[num5]; for (int l = 0; l < num5; l++) { array6[l] = binaryReader.ReadInt32(); if (array6[l] < 0 || array6[l] >= num4) { throw new InvalidDataException("Invalid final ship index"); } } Mesh val2 = new Mesh { name = "Helmsman " + name + " " + text2, indexFormat = (IndexFormat)(num4 > 65535) }; owned.Add((Object)(object)val2); if (flag) { SmallCraftFinish.Map(text2, array2[num3], array3, array5); } val2.vertices = array3; val2.normals = array4; val2.uv = array5; val2.triangles = array6; val2.RecalculateTangents(); val2.RecalculateBounds(); model.Parts.Add(new Part { Group = text2, Mesh = val2, Material = array[num3] }); } if (binaryReader.BaseStream.ReadByte() != -1) { throw new InvalidDataException("Trailing final ship data"); } cache.Add(name, model); return model; } private static Transform Node(string name, Transform parent, Vector3 position) { //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_0017: 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_002a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name) { layer = ((Component)parent).gameObject.layer }; val.transform.SetParent(parent, false); val.transform.localPosition = position; return val.transform; } private static void Rehome(Transform node, Transform root, Vector3 position) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) node.SetParent(root, false); node.localPosition = position; node.localRotation = Quaternion.identity; node.localScale = Vector3.one; } internal static void Apply(GameObject prefab, string name) { //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_03f7: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_0479: Unknown result type (might be due to invalid IL or missing references) //IL_05f0: Unknown result type (might be due to invalid IL or missing references) //IL_0620: Unknown result type (might be due to invalid IL or missing references) //IL_07a3: Unknown result type (might be due to invalid IL or missing references) //IL_07b3: Unknown result type (might be due to invalid IL or missing references) //IL_07d4: Unknown result type (might be due to invalid IL or missing references) //IL_084b: Unknown result type (might be due to invalid IL or missing references) //IL_0856: Unknown result type (might be due to invalid IL or missing references) //IL_0872: Unknown result type (might be due to invalid IL or missing references) //IL_08a0: Unknown result type (might be due to invalid IL or missing references) //IL_08d9: Unknown result type (might be due to invalid IL or missing references) //IL_06dc: Unknown result type (might be due to invalid IL or missing references) //IL_06e1: Unknown result type (might be due to invalid IL or missing references) //IL_06f4: Unknown result type (might be due to invalid IL or missing references) //IL_06f9: Unknown result type (might be due to invalid IL or missing references) //IL_0706: Expected O, but got Unknown //IL_0736: Unknown result type (might be due to invalid IL or missing references) //IL_0946: Unknown result type (might be due to invalid IL or missing references) //IL_094b: Unknown result type (might be due to invalid IL or missing references) //IL_0955: Unknown result type (might be due to invalid IL or missing references) //IL_097b: Unknown result type (might be due to invalid IL or missing references) //IL_0980: Unknown result type (might be due to invalid IL or missing references) //IL_0992: Unknown result type (might be due to invalid IL or missing references) //IL_0994: Unknown result type (might be due to invalid IL or missing references) //IL_09bc: Unknown result type (might be due to invalid IL or missing references) //IL_0b30: Unknown result type (might be due to invalid IL or missing references) //IL_0b46: Unknown result type (might be due to invalid IL or missing references) //IL_0b52: Unknown result type (might be due to invalid IL or missing references) //IL_0b6f: Unknown result type (might be due to invalid IL or missing references) //IL_0ed6: Unknown result type (might be due to invalid IL or missing references) //IL_0f1a: Unknown result type (might be due to invalid IL or missing references) //IL_0f46: Unknown result type (might be due to invalid IL or missing references) //IL_0a52: Unknown result type (might be due to invalid IL or missing references) //IL_0a57: Unknown result type (might be due to invalid IL or missing references) //IL_0a78: Unknown result type (might be due to invalid IL or missing references) //IL_0f84: Unknown result type (might be due to invalid IL or missing references) //IL_0fa2: Unknown result type (might be due to invalid IL or missing references) //IL_0aaa: Unknown result type (might be due to invalid IL or missing references) //IL_0abb: Unknown result type (might be due to invalid IL or missing references) //IL_0abd: Unknown result type (might be due to invalid IL or missing references) //IL_0ac2: Unknown result type (might be due to invalid IL or missing references) //IL_0bfd: Unknown result type (might be due to invalid IL or missing references) //IL_0c02: Unknown result type (might be due to invalid IL or missing references) //IL_0ff9: Unknown result type (might be due to invalid IL or missing references) //IL_1027: Unknown result type (might be due to invalid IL or missing references) //IL_1066: Unknown result type (might be due to invalid IL or missing references) //IL_10b0: Unknown result type (might be due to invalid IL or missing references) //IL_0c4c: Unknown result type (might be due to invalid IL or missing references) //IL_0c36: Unknown result type (might be due to invalid IL or missing references) //IL_0c7a: Unknown result type (might be due to invalid IL or missing references) //IL_0c84: Unknown result type (might be due to invalid IL or missing references) //IL_0c69: Unknown result type (might be due to invalid IL or missing references) //IL_0c73: Unknown result type (might be due to invalid IL or missing references) //IL_0e2a: Unknown result type (might be due to invalid IL or missing references) //IL_0e72: Unknown result type (might be due to invalid IL or missing references) Model model = Load(name); Spec spec = model.Spec; Ship component = prefab.GetComponent<Ship>(); Transform transform = prefab.transform; Material material = ImportedShipMaterials.WaterMask(); ShipControlls shipControlls = component.m_shipControlls; Container[] componentsInChildren = prefab.GetComponentsInChildren<Container>(true); Renderer[] componentsInChildren2 = prefab.GetComponentsInChildren<Renderer>(true); foreach (Renderer obj in componentsInChildren2) { obj.enabled = false; obj.forceRenderingOff = true; } LODGroup[] componentsInChildren3 = prefab.GetComponentsInChildren<LODGroup>(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[i]); } Animator[] componentsInChildren4 = prefab.GetComponentsInChildren<Animator>(true); for (int i = 0; i < componentsInChildren4.Length; i++) { ((Behaviour)componentsInChildren4[i]).enabled = false; } Cloth[] componentsInChildren5 = prefab.GetComponentsInChildren<Cloth>(true); foreach (Cloth obj2 in componentsInChildren5) { obj2.enabled = false; GlobalWind component2 = ((Component)obj2).GetComponent<GlobalWind>(); if (Object.op_Implicit((Object)(object)component2)) { ((MonoBehaviour)component2).CancelInvoke(); ((Behaviour)component2).enabled = false; } } Light[] componentsInChildren6 = prefab.GetComponentsInChildren<Light>(true); for (int i = 0; i < componentsInChildren6.Length; i++) { ((Behaviour)componentsInChildren6[i]).enabled = false; } EffectArea[] componentsInChildren7 = prefab.GetComponentsInChildren<EffectArea>(true); for (int i = 0; i < componentsInChildren7.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren7[i]); } Collider[] componentsInChildren8 = prefab.GetComponentsInChildren<Collider>(true); foreach (Collider val in componentsInChildren8) { if ((Object)(object)val != (Object)(object)component.m_floatCollider && ((Object)val).name != "OnboardTrigger") { Object.DestroyImmediate((Object)(object)val); } } Chair[] componentsInChildren9 = prefab.GetComponentsInChildren<Chair>(true); for (int i = 0; i < componentsInChildren9.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren9[i]); } Ladder[] componentsInChildren10 = prefab.GetComponentsInChildren<Ladder>(true); for (int i = 0; i < componentsInChildren10.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren10[i]); } Transform val2 = Node("Final fleet", transform, Vector3.zero); Transform val3 = Node("Rig", val2, Vector3.zero); Transform val4 = Node("Sail", val3, V(spec.sailPivot)); Transform val5 = Node("Steering rudder", val2, V(spec.rudderPivot)); Transform val6 = Node("Paddle template", val2, Vector3.zero); component.m_mastObject = ((Component)val3).gameObject; component.m_sailObject = ((Component)val4).gameObject; component.m_rudderObject = null; foreach (Part part in model.Parts) { Transform val7 = ((part.Group == "paddle") ? val6 : ((part.Group == "sail") ? val4 : ((part.Group == "rudder") ? val5 : val2))); Transform obj3 = Node(part.Group + " " + val7.childCount, val7, (part.Group == "sail") ? (-V(spec.sailPivot)) : ((part.Group == "rudder") ? (-V(spec.rudderPivot)) : Vector3.zero)); ((Component)obj3).gameObject.AddComponent<MeshFilter>().sharedMesh = part.Mesh; MeshRenderer obj4 = ((Component)obj3).gameObject.AddComponent<MeshRenderer>(); ((Renderer)obj4).sharedMaterial = part.Material; ((Renderer)obj4).shadowCastingMode = (ShadowCastingMode)1; ((Renderer)obj4).receiveShadows = true; } ((Component)val6).gameObject.SetActive(false); if (PaddleCraft(name)) { PaddleCraftRig paddleCraftRig = prefab.AddComponent<PaddleCraftRig>(); paddleCraftRig.PaddleTemplate = ((Component)val6).gameObject; paddleCraftRig.DoubleBlade = name != "HelmsmanDugout"; } FinalShipPresentation finalShipPresentation = prefab.AddComponent<FinalShipPresentation>(); finalShipPresentation.Sail = val4; finalShipPresentation.Rudder = val5; finalShipPresentation.Pivot = V(spec.sailPivot); finalShipPresentation.SheetHeads = spec.sheets.Select((Sheet s) => V(s.head)).ToArray(); finalShipPresentation.SheetFeet = spec.sheets.Select((Sheet s) => V(s.foot)).ToArray(); finalShipPresentation.RopeMaterial = ImportedShipMaterials.BoatyardMaterial(new Color(0.38f, 0.29f, 0.16f), timber: false); owned.Add((Object)(object)finalShipPresentation.RopeMaterial); finalShipPresentation.HullRenderers = (from r in ((Component)val2).GetComponentsInChildren<MeshRenderer>(true) where ((Object)r).name.StartsWith("hull ") select r).ToArray(); finalShipPresentation.Decorations = (from r in ((Component)val2).GetComponentsInChildren<MeshRenderer>(true) where ((Object)r).name.StartsWith("decoration ") select ((Component)r).gameObject).ToArray(); finalShipPresentation.SailRenderers = ((Component)val4).GetComponentsInChildren<MeshRenderer>(true); finalShipPresentation.AirHeight = spec.airHeight; FleetSailMotion fleetSailMotion = prefab.AddComponent<FleetSailMotion>(); fleetSailMotion.Kind = spec.name; fleetSailMotion.Sail = val4; fleetSailMotion.Top = spec.sailPivot[1]; fleetSailMotion.AuthoredMeshes = (from f in ((Component)val4).GetComponentsInChildren<MeshFilter>(true) select f.sharedMesh).ToArray(); finalShipPresentation.Motion = fleetSailMotion; finalShipPresentation.Sculling = spec.name == "freighter"; Solid[] colliders = spec.colliders; foreach (Solid solid in colliders) { Transform obj5 = Node("Hull solid", transform, V(solid.position)); ((Component)obj5).gameObject.layer = LayerMask.NameToLayer("vehicle"); ((Component)obj5).gameObject.AddComponent<BoxCollider>().size = V(solid.size); } foreach (HullSolid item in spec.hullSolids.Concat(spec.cargoSolids ?? Array.Empty<HullSolid>())) { if ((item.vertices.Length != 24 || item.triangles.Length != 36) && (item.vertices.Length != 18 || item.triangles.Length != 24)) { throw new InvalidDataException("Invalid convex ship section"); } Vector3[] array = (Vector3[])(object)new Vector3[item.vertices.Length / 3]; for (int num = 0; num < array.Length; num++) { array[num] = V(item.vertices.Skip(num * 3).Take(3).ToArray()); } Mesh val8 = new Mesh { name = "Fitted ship section" }; owned.Add((Object)(object)val8); val8.vertices = array; val8.triangles = item.triangles; val8.RecalculateBounds(); Transform obj6 = Node("Keel or cargo solid", transform, Vector3.zero); ((Component)obj6).gameObject.layer = LayerMask.NameToLayer("vehicle"); MeshCollider obj7 = ((Component)obj6).gameObject.AddComponent<MeshCollider>(); obj7.sharedMesh = val8; obj7.convex = true; } Rehome(((Component)component.m_floatCollider).transform, transform, new Vector3(0f, spec.waterline, 0f)); component.m_floatCollider.center = Vector3.zero; component.m_floatCollider.size = new Vector3(spec.beam, 0.5f, spec.length); ((Collider)component.m_floatCollider).isTrigger = true; BoxCollider? obj8 = ((IEnumerable<BoxCollider>)prefab.GetComponentsInChildren<BoxCollider>(true)).FirstOrDefault((Func<BoxCollider, bool>)((BoxCollider c) => ((Object)c).name == "OnboardTrigger")); if (!Object.op_Implicit((Object)(object)obj8)) { throw new InvalidDataException("Native onboard trigger missing for " + name); } Rehome(((Component)obj8).transform, transform, new Vector3(0f, spec.walkHeight + 0.8f, 0f)); obj8.center = Vector3.zero; obj8.size = new Vector3(spec.beam, 3.4f, spec.length); ((Collider)obj8).isTrigger = true; AddWaterMask(transform, spec, material); prefab.GetComponent<Rigidbody>().centerOfMass = new Vector3(0f, 0.25f, 0f); component.m_stearForceOffset = (0f - spec.length) * 0.35f; component.m_forceDistance = Mathf.Min(2f, spec.length * 0.16f); float num2 = Mathf.Abs(Physics.gravity.y) * component.m_forceDistance / (50f * Mathf.Max(0.01f, component.m_force)); component.m_waterLevelOffset = 0.25f - spec.waterline + num2; component.m_disableLevel = -0.02f; Point[] points = spec.points; foreach (Point point in points) { Vector3 val9 = V(point.position); Transform val10 = Node(point.kind, transform, val9); ((Component)val10).gameObject.layer = LayerMask.NameToLayer("piece_nonsolid"); Vector3 val11 = V(point.facing); if (((Vector3)(ref val11)).sqrMagnitude > 0.1f) { val10.localRotation = Quaternion.LookRotation(val11); } if (point.kind == "helm") { Rehome(((Component)shipControlls).transform, transform, val9); ((Component)shipControlls).gameObject.SetActive(true); ((Component)shipControlls).gameObject.layer = LayerMask.NameToLayer("piece_nonsolid"); ((Behaviour)shipControlls).enabled = true; shipControlls.m_attachPoint = val10; shipControlls.m_attachAnimation = ((spec.name == "freighter") ? "attach_mast" : ((spec.name == "snekkja") ? "sit" : "attach_sitship")); shipControlls.m_maxUseRange = 2.5f; shipControlls.m_detachOffset = new Vector3(0f, 0.1f, 0.7f); BoxCollider obj9 = ((Component)shipControlls).gameObject.AddComponent<BoxCollider>(); obj9.size = new Vector3(0.7f, 0.65f, 0.55f); obj9.center = new Vector3(0f, (spec.name == "freighter") ? 0.85f : 0.25f, 0f); component.m_controlGuiPos = Node("Helm UI", transform, val9 + Vector3.up); if (PaddleCraft(name)) { shipControlls.m_attachAnimation = "sit"; } } else if (point.kind == "ladder") { Ladder obj10 = ((Component)val10).gameObject.AddComponent<Ladder>(); obj10.m_name = "$piece_ship_ladder"; obj10.m_useDistance = 3f; obj10.m_targetPos = Node("Boarding exit", transform, V(point.exit)); obj10.m_targetPos.localRotation = val10.localRotation; val10.localRotation = Quaternion.identity; ((Component)val10).gameObject.AddComponent<BoxCollider>().size = V(point.size); } else { Chair obj11 = ((Component)val10).gameObject.AddComponent<Chair>(); obj11.m_name = ((point.kind == "mast") ? "Mast holdfast" : "Seat"); obj11.m_inShip = true; obj11.m_attachPoint = val10; obj11.m_attachAnimation = ((point.kind == "mast") ? "attach_mast" : "sit"); obj11.m_useDistance = 2.5f; obj11.m_detachOffset = new Vector3(0f, 0.12f, 0.6f); BoxCollider obj12 = ((Component)val10).gameObject.AddComponent<BoxCollider>(); obj12.size = ((point.kind == "mast") ? new Vector3(0.24f, 1.2f, 0.24f) : new Vector3(0.45f, 0.12f, 0.25f)); obj12.center = ((point.kind == "mast") ? (Vector3.up * 0.65f) : (Vector3.down * 0.08f)); } } Vector3 position = default(Vector3); for (int num3 = 0; num3 < componentsInChildren.Length; num3++) { Container val12 = componentsInChildren[num3]; ((Vector3)(ref position))..ctor((float)((num3 % 2 == 0) ? 1 : (-1)) * spec.beam * 0.17f, (spec.cargoHeight > 0f) ? spec.cargoHeight : (spec.walkHeight + 0.06f), (0f - spec.length) * 0.13f + (float)(num3 / 2) * 0.85f); if (PaddleCraft(name)) { ((Vector3)(ref position))..ctor(0f, (spec.name == "dugout") ? 0.415f : 0.68f, (spec.name == "tandem") ? 0f : (spec.length * 0.29f)); } if (spec.name == "ceol") { ((Vector3)(ref position))..ctor(-0.27f, 0.65f, -1.28f); } if (spec.name == "currach") { ((Vector3)(ref position))..ctor(0.32f, 0.5f, -0.8f); } if (spec.name == "snekkja") { ((Vector3)(ref position))..ctor((num3 % 2 == 0) ? (-0.3f) : 0.3f, 1.04f, (num3 < 2) ? (-6.3f) : 6.3f); } if (spec.name == "falkusa") { ((Vector3)(ref position))..ctor(0.35f, 0.67f, (num3 == 0) ? 0.55f : (-0.6f)); } Rehome(((Component)val12).transform, transform, position); ((Component)val12).gameObject.layer = LayerMask.NameToLayer("piece_nonsolid"); val12.m_open = null; val12.m_closed = null; ((Component)val12).gameObject.AddComponent<BoxCollider>().size = new Vector3(0.5f, 0.1f, 0.5f); if (name == "HelmsmanCurrach") { val12.m_width = 3; val12.m_height = 2; } } if (name == "HerculeShip") { Transform val13 = Node("Fishing net", transform, new Vector3(spec.beam * 0.57f, 0f, 0f)); ((Component)val13).gameObject.layer = LayerMask.NameToLayer("character_trigger"); BoxCollider obj13 = ((Component)val13).gameObject.AddComponent<BoxCollider>(); ((Collider)obj13).isTrigger = true; obj13.size = new Vector3(0.6f, 1.3f, 2.2f); ShipFishingNet shipFishingNet = ((Component)val13).gameObject.AddComponent<ShipFishingNet>(); shipFishingNet.Chest = componentsInChildren[0]; shipFishingNet.Visual = ((Component)Node("Deployed net", val13, Vector3.zero)).gameObject; for (int num4 = 0; num4 < 12; num4++) { AddNetLine(shipFishingNet.Visual.transform, new Vector3(0f, 0.55f, -1f + (float)num4 * 0.18f), new Vector3(0.25f, -0.6f, -1f + (float)num4 * 0.18f), finalShipPresentation.RopeMaterial); } for (int num5 = 0; num5 < 7; num5++) { AddNetLine(shipFishingNet.Visual.transform, new Vector3((float)num5 * 0.25f / 6f, 0.55f - (float)num5 * 1.15f / 6f, -1f), new Vector3((float)num5 * 0.25f / 6f, 0.55f - (float)num5 * 1.15f / 6f, 1f), finalShipPresentation.RopeMaterial); } Transform obj14 = Node("Net handle", transform, new Vector3(spec.beam * 0.36f, spec.walkHeight + 0.3f, 0f)); ((Component)obj14).gameObject.layer = LayerMask.NameToLayer("piece_nonsolid"); ((Component)obj14).gameObject.AddComponent<NetInteraction>().Net = shipFishingNet; ((Component)obj14).gameObject.AddComponent<BoxCollider>().size = new Vector3(0.22f, 0.3f, 0.3f); } } private static void AddWaterMask(Transform root, Spec spec, Material material) { //IL_009b: 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_00ac: Expected O, but got Unknown //IL_00d8: Unknown result type (might be due to invalid IL or missing references) if (spec.waterMask.Length < 4 || spec.waterMask.Length % 2 != 0) { throw new InvalidDataException("Missing fitted water mask"); } Vector3[] array = spec.waterMask.Select(V).ToArray(); List<int> list = new List<int>(); for (int i = 2; i < array.Length; i += 2) { int num = i - 2; list.AddRange(new int[6] { num, num + 2, num + 1, num + 1, num + 2, num + 3 }); } Mesh val = new Mesh { name = "Fitted interior water mask" }; owned.Add((Object)(object)val); val.vertices = array; val.SetTriangles(list, 0); val.RecalculateNormals(); val.RecalculateBounds(); Transform obj = Node("Interior water mask", root, Vector3.zero); ((Component)obj).gameObject.AddComponent<MeshFilter>().sharedMesh = val; MeshRenderer obj2 = ((Component)obj).gameObject.AddComponent<MeshRenderer>(); ((Renderer)obj2).sharedMaterial = material; ((Renderer)obj2).shadowCastingMode = (ShadowCastingMode)0; ((Renderer)obj2).receiveShadows = false; } private static void AddNetLine(Transform parent, Vector3 a, Vector3 b, Material material) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) LineRenderer obj = ((Component)Node("Net cord", parent, Vector3.zero)).gameObject.AddComponent<LineRenderer>(); obj.useWorldSpace = false; obj.positionCount = 2; obj.SetPosition(0, a); obj.SetPosition(1, b); float startWidth = (obj.endWidth = 0.012f); obj.startWidth = startWidth; ((Renderer)obj).sharedMaterial = material; } internal static void ConfigureStyles(GameObject prefab, ShipCosmetics cosmetics) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0054: 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_0096: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)prefab.GetComponent<FinalShipPresentation>())) { cosmetics.Binding = new ShipCosmeticBinding { prefab = ((Object)prefab).name }; cosmetics.HullStyles = (Material[])(object)new Material[5] { Choice("Weathered timber", Color.white), Choice("Red ochre", new Color(0.65f, 0.25f, 0.14f)), Choice("Deep blue", new Color(0.21f, 0.39f, 0.57f)), Choice("Ochre", new Color(0.82f, 0.62f, 0.27f)), Choice("Carved timber", Color.white) }; if (((Object)prefab).name != "MercantShip" && ((Object)prefab).name != "BigCargoShip") { cosmetics.HullStyles = cosmetics.HullStyles.Take(4).ToArray(); } cosmetics.SailStyles = (Material[])(PaddleCraft(((Object)prefab).name) ? ((Array)Array.Empty<Material>()) : ((Array)new Material[4] { Choice("Original cloth", Color.white), Choice("Unbleached linen", new Color(1f, 0.95f, 0.8f)), Choice("Red wool", new Color(0.65f, 0.23f, 0.16f)), Choice("Blue wool", new Color(0.37f, 0.54f, 0.7f)) })); } static Material Choice(string name, Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) Material val = ImportedShipMaterials.BoatyardMaterial(color, timber: false); ((Object)val).name = name; owned.Add((Object)(object)val); return val; } } internal static void Release() { foreach (Object item in owned) { if (Object.op_Implicit(item)) { Object.Destroy(item); } } owned.Clear(); cache.Clear(); } internal static Spec ReadSpecification(string json, string expected) { Spec spec = JsonConvert.DeserializeObject<Spec>(json) ?? throw new InvalidDataException("Missing final ship specification: " + expected); int num = spec.points?.Count((Point p) => p != null && p.kind == "helm") ?? 0; if (spec.prefab != expected || !Finite(spec.length) || spec.length < 4f || spec.length > 30f || !Finite(spec.beam) || spec.beam < 1f || spec.beam > 10f || num != 1) { throw new InvalidDataException($"Invalid final ship specification for {expected}: prefab={spec.prefab}, length={spec.length}, beam={spec.beam}, helms={num}"); } Spec spec2 = spec; if (spec2.cargoSolids == null) { spec2.cargoSolids = Array.Empty<HullSolid>(); } if (spec.hullSolids == null || spec.colliders == null || spec.sheets == null) { throw new InvalidDataException("Missing final ship geometry: " + expected); } return spec; } private static bool Finite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } } public sealed class FinalShipPresentation : MonoBehaviour { public Transform Sail; public Transform Rudder; public GameObject[] Decorations = Array.Empty<GameObject>(); public Vector3 Pivot; public Vector3[] SheetHeads = Array.Empty<Vector3>(); public Vector3[] SheetFeet = Array.Empty<Vector3>(); public Material RopeMaterial; public MeshRenderer[] HullRenderers = Array.Empty<MeshRenderer>(); public MeshRenderer[] SailRenderers = Array.Empty<MeshRenderer>(); public float AirHeight; public FleetSailMotion? Motion; public bool Sculling; private LineRenderer[] sheets = Array.Empty<LineRenderer>(); private readonly Dictionary<Renderer, Material> originals = new Dictionary<Renderer, Material>(); private readonly List<Material> painted = new List<Material>(); private void Start() { //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) if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsDedicated()) { sheets = (LineRenderer[])(object)new LineRenderer[SheetHeads.Length]; for (int i = 0; i < sheets.Length; i++) { GameObject val = new GameObject("Working sheet"); val.transform.SetParent(((Component)this).transform, false); LineRenderer val2 = val.AddComponent<LineRenderer>(); val2.useWorldSpace = false; val2.positionCount = 9; float startWidth = (val2.endWidth = 0.018f); val2.startWidth = startWidth; ((Renderer)val2).sharedMaterial = RopeMaterial; sheets[i] = val2; } } } private void LateUpdate() { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: 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_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_0100: 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_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_014f: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)Sail)) { return; } Ship component = ((Component)this).GetComponent<Ship>(); if (Object.op_Implicit((Object)(object)Rudder) && Object.op_Implicit((Object)(object)component)) { float num = ((Sculling && ((int)component.GetSpeedSetting() == 2 || (int)component.GetSpeedSetting() == 1)) ? (Mathf.Sin(Time.time * 2.4f) * 5f) : 0f); Rudder.localRotation = Quaternion.Euler(0f, Mathf.Clamp(-8f * component.GetRudderValue() + num, -8f, 8f), 0f); } for (int i = 0; i < sheets.Length; i++) { Vector3 val = (Object.op_Implicit((Object)(object)Motion) ? Motion.Furl(SheetHeads[i]) : (Pivot + Vector3.Scale(SheetHeads[i] - Pivot, Sail.localScale))); for (int j = 0; j < 9; j++) { float num2 = (float)j / 8f; Vector3 val2 = Vector3.Lerp(val, SheetFeet[i], num2); val2.y -= Mathf.Sin(num2 * (float)Math.PI) * 0.09f; sheets[i].SetPosition(j, val2); } } } internal void Paint(int hull, int sail, Material[] hullStyles, Material[] sailStyles) { foreach (Material item in painted) { if (Object.op_Implicit((Object)(object)item)) { Object.Destroy((Object)(object)item); } } painted.Clear(); GameObject[] decorations = Decorations; foreach (GameObject val in decorations) { if (Object.op_Implicit((Object)(object)val)) { val.SetActive(hull == 4); } } Apply(HullRenderers, hull, hullStyles); Apply(SailRenderers, sail, sailStyles); void Apply(MeshRenderer[] renderers, int choice, Material[] choices) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) foreach (MeshRenderer val2 in renderers) { if (!originals.TryGetValue((Renderer)(object)val2, out Material value)) { value = ((Renderer)val2).sharedMaterial; originals[(Renderer)(object)val2] = value; } if (choice <= 0 || choice >= choices.Length) { ((Renderer)val2).sharedMaterial = value; } else { Material val3 = new Material(value); val3.color = choices[choice].color; ((Renderer)val2).sharedMaterial = val3; painted.Add(val3); } } } } private void OnDestroy() { foreach (Material item in painted) { if (Object.op_Implicit((Object)(object)item)) { Object.Destroy((Object)(object)item); } } } } public sealed class FishCollisionPass : MonoBehaviour { private Ship ship; private float nextScan; private readonly HashSet<(Collider Boat, Collider Fish)> changed = new HashSet<(Collider, Collider)>(); internal static void UpdateFor(Ship ship, bool active) { if (!Object.op_Implicit((Object)(object)ship)) { return; } FishCollisionPass component = ((Component)ship).GetComponent<FishCollisionPass>(); if (!active) { if (Object.op_Implicit((Object)(object)component)) { component.Restore(); Object.Destroy((Object)(object)component); } } else if (!Object.op_Implicit((Object)(object)component)) { component = ((Component)ship).gameObject.AddComponent<FishCollisionPass>(); component.ship = ship; } } private void Update() { Voyage voyage = Plugin.Instance.Voyage; if (!Object.op_Implicit((Object)(object)ship) || !Plugin.Instance.FishPassThrough.Value || !Object.op_Implicit((Object)(object)voyage) || (Object)(object)voyage.Ship != (Object)(object)ship || !voyage.CanControl || ship.m_shipControlls.HaveValidUser()) { Restore(); Object.Destroy((Object)(object)this); } else if (!(Time.time < nextScan)) { nextScan = Time.time + 0.1f; Refresh(); } } internal void Refresh() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ship.m_floatCollider)) { return; } Bounds bounds = ((Collider)ship.m_floatCollider).bounds; ((Bounds)(ref bounds)).Expand(6f); HashSet<Fish> hashSet = new HashSet<Fish>(); Collider[] array = Physics.OverlapBox(((Bounds)(ref bounds)).center, ((Bounds)(ref bounds)).extents, Quaternion.identity, -1, (QueryTriggerInteraction)1); for (int i = 0; i < array.Length; i++) { Fish componentInParent = ((Component)array[i]).GetComponentInParent<Fish>(); if (Object.op_Implicit((Object)(object)componentInParent)) { hashSet.Add(componentInParent); } } HashSet<(Collider, Collider)> hashSet2 = new HashSet<(Collider, Collider)>(); array = ((Component)ship).GetComponentsInChildren<Collider>(); foreach (Collider val in array) { if (!val.enabled || val.isTrigger || (Object)(object)((Component)val).GetComponentInParent<Ship>() != (Object)(object)ship) { continue; } foreach (Fish item2 in hashSet) { Collider[] componentsInChildren = ((Component)item2).GetComponentsInChildren<Collider>(); foreach (Collider val2 in componentsInChildren) { if (val2.enabled && !val2.isTrigger) { (Collider, Collider) item = (val, val2); hashSet2.Add(item); if (!changed.Contains(item) && !Physics.GetIgnoreCollision(val, val2)) { Physics.IgnoreCollision(val, val2, true); changed.Add(item); } } } } } foreach (var item3 in new List<(Collider, Collider)>(changed)) { if (!hashSet2.Contains(item3)) { if (Object.op_Implicit((Object)(object)item3.Item1) && Object.op_Implicit((Object)(object)item3.Item2)) { Physics.IgnoreCollision(item3.Item1, item3.Item2, false); } changed.Remove(item3); } } } internal void Restore() { foreach (var item in changed) { if (Object.op_Implicit((Object)(object)item.Boat) && Object.op_Implicit((Object)(object)item.Fish)) { Physics.IgnoreCollision(item.Boat, item.Fish, false); } } changed.Clear(); } private void OnDisable() { Restore(); } private void OnDestroy() { Restore(); } } public sealed class FishingDock : MonoBehaviour { public Container Chest; public Transform WorkerAnchor; public string Catch = "Fish1"; private static ConfigEntry<float> seconds = null; private static ConfigEntry<int> limit = null; private static readonly int Progress = StringExtensionMethods.GetStableHashCode("helmsman_fishing_progress_v1"); private ZNetView view; private PerchedBird? bird; private double previous; private float next; private Vector3 perch; private float idleOffset; internal static void Configure(ConfigFile config) { //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_0039: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown //IL_006d: 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_007a: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown seconds = config.Bind<float>("Fishing Dock", "SecondsPerCatch", 20f, new ConfigDescription("Daylight work seconds per fish.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 3600f), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); limit = config.Bind<int>("Fishing Dock", "CatchLimit", 50, new ConfigDescription("Pause when the dock chest contains this many fish.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 10000), new object[1] { (object)new ConfigurationManagerAttributes { IsAdminOnly = true } })); } private void Awake() { view = ((Component)this).GetComponent<ZNetView>(); } private IEnumerator Start() { while (!Object.op_Implicit((Object)(object)view) || !view.IsValid() || !Object.op_Implicit((Object)(object)Player.m_localPlayer)) { yield return (object)new WaitForSeconds(1f); } Material val = ((Component)this).GetComponentsInChildren<MeshRenderer>(true).SelectMany((MeshRenderer r) => ((Renderer)r).sharedMaterials).FirstOrDefault((Func<Material, bool>)((Material m) => Object.op_Implicit((Object)(object)m) && Object.op_Implicit((Object)(object)m.shader) && ((Object)m.shader).name == "Custom/Piece")); if (!Object.op_Implicit((Object)(object)val)) { yield break; } bird = new PerchedBird(null, val, burrowingOwl: false, fishingPelican: true); bird.Root.transform.localScale = Vector3.one * 0.8f; bird.Probes(((Component)this).transform); idleOffset = Random.Range(0f, 23f); perch = (Object.op_Implicit((Object)(object)WorkerAnchor) ? ((Component)this).transform.InverseTransformPoint(WorkerAnchor.position) : Vector3.zero); Ray val2 = default(Ray); ((Ray)(ref val2))..ctor(((Component)this).transform.TransformPoint(perch) + ((Component)this).transform.up * 4f, -((Component)this).transform.up); float num = float.PositiveInfinity; Collider[] componentsInChildren = ((Component)this).GetComponentsInChildren<Collider>(true); RaycastHit val4 = default(RaycastHit); foreach (Collider val3 in componentsInChildren) { if (val3.enabled && !val3.isTrigger && ((Component)val3).gameObject.activeInHierarchy && val3.Raycast(val2, ref val4, 6f) && ((RaycastHit)(ref val4)).distance < num) { num = ((RaycastHit)(ref val4)).distance; perch = ((Component)this).transform.InverseTransformPoint(((RaycastHit)(ref val4)).point); } } SphereCollider obj = bird.Root.AddComponent<SphereCollider>(); obj.center = new Vector3(0f, 0.7f, 0f); obj.radius = 0.35f; BirdInteraction birdInteraction = bird.Root.AddComponent<BirdInteraction>(); birdInteraction.Label = () => "Pelican fisherman"; birdInteraction.Hint = () => Localization.instance.Localize("Pelican fisherman\n[<color=yellow><b>$KEY_Use</b></color>] Catch report"); birdInteraction.Use = delegate { Plugin.Message((!EnvMan.IsDaylight()) ? "Fishing resumes at dawn." : (CanFish(out ItemDrop _) ? ("Fishing. Next catch in " + Shipyard.FormatDuration(Math.Max(0f, seconds.Value - view.GetZDO().GetFloat(Progress, 0f))) + ".") : "Waiting for space in the station chest.")); return true; }; } private bool CanFish(out ItemDrop? item) { object obj; if (!Object.op_Implicit((Object)(object)ObjectDB.instance)) { obj = null; } else { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(Catch); obj = ((itemPrefab != null) ? itemPrefab.GetComponent<ItemDrop>() : null); } item = (ItemDrop?)obj; if (!Object.op_Implicit((Object)(object)item) || !Object.op_Implicit((Object)(object)Chest) || Chest.GetInventory() == null || Chest.IsInUse() || view.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 0 || !Object.op_Implicit((Object)(object)EnvMan.instance) || !EnvMan.IsDaylight()) { return false; } Inventory inventory = Chest.GetInventory(); if (inventory.CountItems(item.m_itemData.m_shared.m_name, -1, true) < limit.Value) { return inventory.CanAddItem(item.m_itemData, 1); } return false; } private void Update() { //IL_0052: 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_009d: 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_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)view) || !view.IsValid() || !Object.op_Implicit((Object)(object)ZNet.instance)) { return; } ItemDrop item; bool flag = CanFish(out item); if (bird != null) { bird.Root.transform.position = ((Component)this).transform.TransformPoint(perch); bird.Fishing(flag); bird.Root.transform.rotation = (Object.op_Implicit((Object)(object)WorkerAnchor) ? WorkerAnchor.rotation : ((Component)this).transform.rotation); float num = Mathf.Sin(Time.time * 0.75f); bird.Pose(flag ? (12f + 6f * num) : 0f, flag ? (10f * num) : 0f, 0f, flag ? 4 : 0, 0f, flag ? (8f + 4f * num) : 0f); int num2; if (Object.op_Implicit((Object)(object)Player.m_localPlayer)) { Vector3 val = ((Component)Player.m_localPlayer).transform.position - bird.Root.transform.position; num2 = ((((Vector3)(ref val)).sqrMagnitude < 9f) ? 1 : 0); } else { num2 = 0; } bool flag2 = (byte)num2 != 0; bool flag3 = !flag && Object.op_Implicit((Object)(object)EnvMan.instance) && !EnvMan.IsDaylight() && !flag2; if (!flag3) { bird.Idle(Time.time + idleOffset, flag); } bird.Rest(flag3, Time.time + idleOffset, Time.deltaTime); } if (Time.time < next) { return; } next = Time.time + 1f; double timeSeconds = ZNet.instance.GetTimeSeconds(); double num3 = ((previous > 0.0) ? Math.Max(0.0, Math.Min(2.0, timeSeconds - previous)) : 0.0); previous = timeSeconds; if (!view.IsOwner() || !flag) { return; } float num4 = Mathf.Clamp(view.GetZDO().GetFloat(Progress, 0f), 0f, seconds.Value) + (float)num3; if (num4 >= seconds.Value) { ItemData val2 = item.m_itemData.Clone(); val2.m_stack = 1; val2.m_dropPrefab = ObjectDB.instance.GetItemPrefab(Catch); if (Chest.GetInventory().AddItem(val2)) { num4 -= seconds.Value; } } view.GetZDO().Set(Progress, Mathf.Min(num4, seconds.Value)); } private void OnDestroy() { bird?.Dispose(); } } internal static class FleetHandling { internal static void Apply(Ship ship, string model) { (float, float, float, float, float) tuple = model switch { "HelmsmanDugout" => (1f, 1f, 1.15f, 1.1f, 1.15f), "HelmsmanFinewoodKayak" => (1f, 1.1f, 1.25f, 1.2f, 1.35f), "HelmsmanTandemKayak" => (1f, 1.1f, 1.2f, 1.15f, 1.3f), "LittleBoat" => (1.1f, 1.15f, 1.25f, 1.2f, 1.25f), "HelmsmanCurrach" => (1.08f, 1.15f, 1.25f, 1.2f, 1.25f), "HerculeShip" => (1.12f, 1.15f, 1.2f, 1.15f, 1.2f), "MercantShip" => (1.1f, 1.1f, 1.15f, 1.1f, 1.15f), "BigCargoShip" => (1.05f, 1.1f, 1.1f, 1.05f, 1.1f), "WarShip" => (1.18f, 1.2f, 1.3f, 1.25f, 1.3f), _ => (1f, 1f, 1f, 1f, 1f), }; ship.m_sailForceFactor *= tuple.Item1; ship.m_backwardForce *= tuple.Item2; ship.m_stearForce *= tuple.Item3; ship.m_stearVelForceFactor *= tuple.Item4; ship.m_rudderSpeed *= tuple.Item5; } } public sealed class FleetSailMotion : MonoBehaviour { private sealed class Cloth { internal Mesh Mesh; internal Vector3[] Rest; internal Vector3[] Live; } public string Kind = ""; public Transform Sail; public float Top; public Mesh[] AuthoredMeshes = Array.Empty<Mesh>(); private ImportedSailAnimation? animationState; private readonly List<Cloth> cloth = new List<Cloth>(); private float nextTick; private float halfWidth = 1f; private float bottom; private bool frozenPreview; internal void FreezePreview() { //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) if (frozenPreview) { return; } frozenPreview = true; ((Behaviour)this).enabled = false; FinalShipPresentation component = ((Component)this).GetComponent<FinalShipPresentation>(); if (Object.op_Implicit((Object)(object)component)) { ((Behaviour)component).enabled = false; } int num = 0; MeshFilter[] componentsInChildren = ((Component)Sail).GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val in componentsInChildren) { Mesh val2 = (Mesh)((num < AuthoredMeshes.Length) ? ((object)AuthoredMeshes[num++]) : ((object)val.sharedMesh)); if (Object.op_Implicit((Object)(object)val2)) { Mesh val3 = Object.Instantiate<Mesh>(val2); Vector3[] vertices = val2.vertices; for (int j = 0; j < vertices.Length; j++) { vertices[j] = Vector3.Lerp(FleetSailShape.Anchor(Kind, vertices[j], Top), vertices[j], 0.1f); } val3.vertices = vertices; val3.RecalculateNormals(); val3.RecalculateBounds(); val.sharedMesh = val3; cloth.Add(new Cloth { Mesh = val3 }); } } } private void Start() { //IL_00a6: 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_00b4: 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) if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsDedicated()) { return; } animationState = ((Component)this).GetComponent<ImportedSailAnimation>(); bottom = Top; int num = 0; MeshFilter[] componentsInChildren = ((Component)Sail).GetComponentsInChildren<MeshFilter>(true); foreach (MeshFilter val in componentsInChildren) { Mesh val2 = (Mesh)((num < AuthoredMeshes.Length) ? ((object)AuthoredMeshes[num++]) : ((object)val.sharedMesh)); if (Object.op_Implicit((Object)(object)val2)) { Mesh val3 = Object.Instantiate<Mesh>(val2); val3.MarkDynamic(); val.sharedMesh = val3; Vector3[] vertices = val3.vertices; Vector3[] array = vertices; foreach (Vector3 val4 in array) { halfWidth = Mathf.Max(halfWidth, Mathf.Abs(val4.x)); bottom = Mathf.Min(bottom, val4.y); } cloth.Add(new Cloth { Mesh = val3, Rest = vertices, Live = (Vector3[])(object)new Vector3[vertices.Length] }); } } } public Vector3 Furl(Vector3 p) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_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) float num = (Object.op_Implicit((Object)(object)animationState) ? animationState.FurlAmount : 1f); return Vector3.Lerp(FleetSailShape.Anchor(Kind, p, Top), p, num); } private void LateUpdate() { //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_0096: 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_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_010c: 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) if (Time.time < nextTick) { return; } nextTick = Time.time + 0.05f; float time = Time.time; float amount = (Object.op_Implicit((Object)(object)animationState) ? animationState.FurlAmount : 1f); float wind = (Object.op_Implicit((Object)(object)EnvMan.instance) ? EnvMan.instance.GetWindIntensity() : 0f); foreach (Cloth item in cloth) { for (int i = 0; i < item.Rest.Length; i++) { Vector3 p = item.Rest[i]; Vector3 val = Furl(p); float num = FleetSailShape.Flutter(Kind, p, Top, bottom, halfWidth, time, wind, amount); if (Kind == "falkusa" || Kind == "currach") { val.x += num; } else { val.z += num; } item.Live[i] = val; } item.Mesh.vertices = item.Live; item.Mesh.RecalculateNormals(); item.Mesh.RecalculateBounds(); } } private void OnDestroy() { foreach (Cloth item in cloth) { if (Object.op_Implicit((Object)(object)item.Mesh)) { Object.Destroy((Object)(object)item.Mesh); } } cloth.Clear(); } } internal static class FleetSailShape { internal static Vector3 Anchor(string kind, Vector3 p, float top) { //IL_000d: 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_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_0085: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_006d: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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) if (kind == "falkusa") { if (!(p.x > 0f)) { return Project(p, new Vector3(-0.25f, 1.48f, 5.45f), new Vector3(-0.22f, 6f, -0.8f)); } return Project(p, new Vector3(0.45f, 1.95f, 3.4f), new Vector3(0.45f, 8.65f, -3.25f)); } if (kind == "currach") { return Project(p, new Vector3(0.125f, 4.4f, 1.12f), new Vector3(0.135f, 0.95f, 1.12f)); } return new Vector3(p.x, top, p.z); } private static Vector3 Project(Vector3 p, Vector3 a, Vector3 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: 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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) Vector3 val = b - a; return a + val * Mathf.Clamp01(Vector3.Dot(p - a, val) / ((Vector3)(ref val)).sqrMagnitude); } internal static float Weight(string kind, Vector3 p, float top, float bottom, float halfWidth) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: 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_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_
BepInEx/plugins/ValheimHelmsman/Helmsman.Core.dll
Decompiled 4 days agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using Microsoft.CodeAnalysis; [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("Helmsman.Core")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.2.42.0")] [assembly: AssemblyInformationalVersion("0.2.42+36c9918e3a0f86f4012d19cdddf84217608f7fa4")] [assembly: AssemblyProduct("Helmsman.Core")] [assembly: AssemblyTitle("Helmsman.Core")] [assembly: AssemblyVersion("0.2.42.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 Helmsman.Core { public sealed class AdaptiveRouteSearch { private RouteSearch search; private readonly Point start; private readonly Point goal; private readonly Func<Point, Point, bool> clear; private int regionalExpanded; public bool Fine { get; private set; } public SearchState State => search.State; public List<Point> Route => search.Route; public int Expanded => regionalExpanded + search.Expanded; public AdaptiveRouteSearch(Point start, Point goal, Func<Point, Point, bool> clear, Func<Point, Point, double>? weight = null) { this.start = start; this.goal = goal; this.clear = clear; search = new RouteSearch(start, goal, clear, 24.0, 20000, weight); } public void Step(int budget = 8) { search.Step(budget); if (!Fine && search.State == SearchState.NoRoute) { regionalExpanded = search.Expanded; Fine = true; search = new RouteSearch(start, goal, clear, 6.0); } } } public enum BoardingState { GracePeriod, WaitingForPlayer, FinalCountdown, Ready } public sealed class BoardingGate { private readonly double graceEnd; private readonly double finalSeconds; private double? finalEnd; private bool waitedForPlayer; public BoardingState State { get; private set; } public double Remaining { get; private set; } public BoardingGate(double now, double graceSeconds = 10.0, double finalSeconds = 3.0) { graceEnd = now + Math.Max(0.0, graceSeconds); this.finalSeconds = Math.Max(0.0, finalSeconds); } public bool Update(double now, bool aboard) { Remaining = Math.Max(0.0, graceEnd - now); if (now < graceEnd) { State = BoardingState.GracePeriod; return false; } if (!aboard) { waitedForPlayer = true; finalEnd = null; State = BoardingState.WaitingForPlayer; return false; } if (waitedForPlayer) { if (!finalEnd.HasValue) { finalEnd = now + finalSeconds; } Remaining = Math.Max(0.0, finalEnd.Value - now); if (Remaining > 0.0) { State = BoardingState.FinalCountdown; return false; } } State = BoardingState.Ready; return true; } } public static class CommissionCosts { public static string Commit(bool freeBuild, Func<string> payMaterials, Action commit, Action<Exception> reportError) { if (!freeBuild) { return payMaterials(); } try { commit(); return ""; } catch (Exception obj) { reportError(obj); return "The free build order could not be started."; } } public static IReadOnlyDictionary<string, int> Refund(string recipe, bool freeBuild) { if (!freeBuild) { return ShipwrightRules.Costs(recipe); } return new Dictionary<string, int>(); } } public static class ConstructionClock { public static void Advance(ref long seen, ref long excluded, ref bool paused, long now, bool ready, bool continuing) { if (seen > 0) { long num = Math.Max(0L, now - seen); if ((!ready | paused) || !continuing || num > 50000000) { excluded += num; } } seen = Math.Max(seen, now); paused = !ready; } public static long Time(long now, long seen, long excluded, bool paused) { if (seen != 0L) { return Math.Max(0L, Math.Min(now, seen + (paused ? 0 : 10000000)) - excluded); } return now; } } public static class ConstructionFunding { public static Dictionary<string, int> Supplied(string supplied) { if (!string.IsNullOrEmpty(supplied)) { return ShipwrightRules.Costs(supplied); } return new Dictionary<string, int>(StringComparer.Ordinal); } public static bool Valid(string recipe, string supplied) { try { Dictionary<string, int> costs = ShipwrightRules.Costs(recipe); int value; return Supplied(supplied).All<KeyValuePair<string, int>>((KeyValuePair<string, int> p) => costs.TryGetValue(p.Key, out value) && p.Value <= value); } catch (ArgumentException) { return false; } catch (OverflowException) { return false; } } public static int Remaining(string recipe, string supplied, string item) { Dictionary<string, int> dictionary = ShipwrightRules.Costs(recipe); Dictionary<string, int> dictionary2 = Supplied(supplied); if (!dictionary.TryGetValue(item, out var value)) { return 0; } int value2; return Math.Max(0, value - (dictionary2.TryGetValue(item, out value2) ? value2 : 0)); } public static string Credit(string recipe, string supplied, string item, int actual) { if (!Valid(recipe, supplied) || actual < 0 || actual > Remaining(recipe, supplied, item)) { throw new ArgumentException("Invalid construction material receipt"); } Dictionary<string, int> dictionary = Supplied(supplied); if (actual > 0) { dictionary[item] = (dictionary.TryGetValue(item, out var value) ? value : 0) + actual; } return string.Join(",", from p in dictionary.OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> p) => p.Key, StringComparer.Ordinal) select p.Key + ":" + p.Value); } public static bool Complete(string recipe, string supplied) { if (Valid(recipe, supplied)) { return ShipwrightRules.Costs(recipe).All<KeyValuePair<string, int>>((KeyValuePair<string, int> p) => Remaining(recipe, supplied, p.Key) == 0); } return false; } public static IReadOnlyDictionary<string, int> Refund(string recipe, string supplied, bool waiting, bool free) { if (!free) { if (!waiting) { return ShipwrightRules.Costs(recipe); } return Supplied(supplied); } return new Dictionary<string, int>(); } } public static class DockApproach { public static bool TrySelect(Point berth, Point forward, double shipLength, Func<Point, Point, bool> corridorClear, Func<Point, Point, bool> leadClear, out Point approach, out Point lead) { approach = (lead = berth); double num = Math.Min(35.0, Math.Max(10.0, shipLength * 1.25)); double num2 = Math.Min(20.0, Math.Max(6.0, shipLength * 0.75)); double num3 = 35.0; while (true) { Point point = new Point(berth.X - forward.X * num3, berth.Y - forward.Y * num3); if (corridorClear(point, berth)) { double num4 = 20.0; while (true) { Point point2 = new Point(point.X - forward.X * num4, point.Y - forward.Y * num4); if (leadClear(point2, point)) { approach = point; lead = point2; return true; } if (num4 <= num2) { break; } num4 = Math.Max(num2, num4 - 6.0); } } if (num3 <= num) { break; } num3 = Math.Max(num, num3 - 6.0); } return false; } } public static class GuardedSteps { public static IEnumerator Run(IEnumerator steps, Action<Exception> failed) { try { while (true) { object current; try { if (!steps.MoveNext()) { break; } current = steps.Current; } catch (Exception obj) { failed(obj); break; } yield return current; } } finally { (steps as IDisposable)?.Dispose(); } } } public static class GullcallModel { public sealed class Part { public string Name = ""; public float[] Color = Array.Empty<float>(); public float[] Vertices = Array.Empty<float>(); public int[] Triangles = Array.Empty<int>(); } public static List<Part> Read(Stream stream) { using BinaryReader binaryReader = new BinaryReader(stream, Encoding.UTF8, leaveOpen: true); if (binaryReader.ReadUInt32() != 827086151) { throw new InvalidDataException("Invalid Gullcall model header."); } int num = binaryReader.ReadInt32(); if (num < 1 || num > 32) { throw new InvalidDataException("Invalid part count."); } List<Part> list = new List<Part>(); for (int i = 0; i < num; i++) { int num2 = binaryReader.ReadUInt16(); if (num2 == 0 || num2 > 256) { throw new InvalidDataException("Invalid part name."); } byte[] array = binaryReader.ReadBytes(num2); if (array.Length != num2) { throw new EndOfStreamException(); } Part part = new Part { Name = Encoding.UTF8.GetString(array), Color = new float[4] }; for (int j = 0; j < 4; j++) { part.Color[j] = Finite(binaryReader.ReadSingle()); } int num3 = binaryReader.ReadInt32(); if (num3 < 3 || num3 > 65535) { throw new InvalidDataException("Invalid vertex count."); } part.Vertices = new float[num3 * 3]; for (int k = 0; k < part.Vertices.Length; k++) { part.Vertices[k] = Finite(binaryReader.ReadSingle()); } int num4 = binaryReader.ReadInt32(); if (num4 < 3 || num4 > 200000 || num4 % 3 != 0) { throw new InvalidDataException("Invalid triangle count."); } part.Triangles = new int[num4]; for (int l = 0; l < num4; l++) { int num5 = binaryReader.ReadInt32(); if (num5 < 0 || num5 >= num3) { throw new InvalidDataException("Invalid triangle index."); } part.Triangles[l] = num5; } list.Add(part); } if (stream.Position != stream.Length) { throw new InvalidDataException("Unexpected trailing model data."); } return list; } private static float Finite(float value) { if (float.IsNaN(value) || float.IsInfinity(value)) { throw new InvalidDataException("Invalid model coordinate."); } return value; } } public enum GullMood { Calm, RoughSeas, Storm, Fog, Combat } public sealed class GullMoodState { private GullMood candidate; private double candidateSince; private double combatUntil = double.NegativeInfinity; public GullMood Current { get; private set; } public GullMood Update(double now, bool combat, bool storm, bool fog, bool rough) { if (combat) { combatUntil = now + 6.0; } if (now < combatUntil) { Current = GullMood.Combat; candidate = GullMood.Combat; candidateSince = now; return Current; } GullMood gullMood = (storm ? GullMood.Storm : (fog ? GullMood.Fog : (rough ? GullMood.RoughSeas : GullMood.Calm))); if (gullMood != candidate) { candidate = gullMood; candidateSince = now; } if (now - candidateSince >= 3.0) { Current = candidate; } return Current; } } public struct GullPose { public double HeadPitch; public double HeadYaw; public double HeadRoll; public double BodyPitch; public double BodyYaw; public double BodyRoll; public double Crouch; public double TailYaw; public double TailFan; public double Hop; } public static class GullPerformance { public static GullPose Blend(GullPose a, GullPose b, double amount) { double t = Clamp(amount, 0.0, 1.0); return new GullPose { HeadPitch = L(a.HeadPitch, b.HeadPitch), HeadYaw = L(a.HeadYaw, b.HeadYaw), HeadRoll = L(a.HeadRoll, b.HeadRoll), BodyPitch = L(a.BodyPitch, b.BodyPitch), BodyYaw = L(a.BodyYaw, b.BodyYaw), BodyRoll = L(a.BodyRoll, b.BodyRoll), Crouch = L(a.Crouch, b.Crouch), TailYaw = L(a.TailYaw, b.TailYaw), TailFan = L(a.TailFan, b.TailFan), Hop = b.Hop }; double L(double x, double y) { return x + (y - x) * t; } } public static GullPose Sample(GullMood mood, double elapsed, double threatYaw = 0.0, double windYaw = 0.0, double shipRoll = 0.0, double shipPitch = 0.0) { GullPose result = default(GullPose); double num = Math.Max(0.0, elapsed); switch (mood) { case GullMood.Calm: num %= 14.0; result.HeadYaw = 28.0 * Window(num, 0.7, 3.0) - 32.0 * Window(num, 5.5, 7.3) + 78.0 * Window(num, 9.0, 11.8); result.HeadPitch = 38.0 * (Pulse(num, 3.5, 0.65) + Pulse(num, 4.35, 0.55)) + 24.0 * Window(num, 9.5, 11.5); result.HeadRoll = 12.0 * Window(num, 1.5, 2.6) - 8.0 * Window(num, 6.0, 7.0); result.BodyPitch = 6.0 * (Pulse(num, 3.5, 0.65) + Pulse(num, 4.35, 0.55)); result.Crouch = 0.012 * (1.0 + Math.Sin(num * Math.PI * 2.0 / 3.5)); result.TailYaw = 13.0 * Math.Sin(num * 19.0) * Window(num, 8.0, 8.8); result.TailFan = 0.18 * Window(num, 10.0, 11.5); break; case GullMood.RoughSeas: num %= 7.0; result.BodyRoll = Clamp((0.0 - shipRoll) * 0.7, -14.0, 14.0) + 4.0 * Math.Sin(num * Math.PI * 2.0 / 7.0); result.BodyPitch = 5.0 + Clamp((0.0 - shipPitch) * 0.5, -8.0, 8.0); result.Crouch = 0.06 + 0.025 * (1.0 + Math.Sin(num * Math.PI * 4.0 / 7.0)); result.HeadRoll = (0.0 - result.BodyRoll) * 0.7; result.HeadPitch = (0.0 - result.BodyPitch) * 0.5; result.HeadYaw = 24.0 * Window(num, 1.2, 2.7) - 24.0 * Window(num, 4.0, 5.7); result.TailFan = 0.3; result.TailYaw = 0.0 - result.BodyRoll; result.Hop = 0.12 * Pulse(num, 5.8, 0.65); break; case GullMood.Storm: { num %= 10.0; result.BodyYaw = Clamp(windYaw, -65.0, 65.0); result.BodyPitch = 13.0 + 3.0 * Math.Sin(num * Math.PI * 4.0 / 10.0); result.Crouch = 0.14; result.HeadPitch = 24.0 - 32.0 * Window(num, 6.6, 8.1); result.HeadYaw = -20.0 * Window(num, 6.7, 7.5) + 20.0 * Window(num, 7.5, 8.3); double num2 = Window(num, 2.5, 3.7); result.BodyRoll = 7.0 * Math.Sin(num * 32.0) * num2; result.HeadRoll = -10.0 * Math.Sin(num * 32.0) * num2; result.TailYaw = 18.0 * Math.Sin(num * 26.0) * num2; result.TailFan = 0.25 * num2; break; } case GullMood.Fog: num %= 12.0; result.BodyPitch = -5.0; result.Crouch = -0.025; result.HeadPitch = -12.0 + 22.0 * Window(num, 8.5, 10.0); result.HeadYaw = -55.0 * Window(num, 0.7, 3.2) + 55.0 * Window(num, 4.2, 7.4); result.HeadRoll = 18.0 * Window(num, 2.0, 3.0) - 18.0 * Window(num, 5.8, 7.0); result.BodyYaw = result.HeadYaw * 0.2; result.TailYaw = 12.0 * Window(num, 9.0, 10.8); result.Hop = 0.16 * Pulse(num, 10.4, 0.8); break; case GullMood.Combat: num %= 6.0; result.BodyYaw = Clamp(threatYaw, -80.0, 80.0) * Window(num, 0.0, 5.8, 0.15); result.HeadYaw = Clamp(threatYaw - result.BodyYaw, -55.0, 55.0) + 9.0 * Math.Sin(num * 22.0) * Window(num, 1.5, 2.2); result.HeadPitch = -18.0 + 30.0 * Pulse(num, 2.5, 0.35) + 30.0 * Pulse(num, 2.95, 0.35); result.BodyPitch = -7.0 + 14.0 * Window(num, 2.4, 3.4); result.Crouch = 0.09 * Pulse(num, 0.0, 0.28) + 0.05 * Window(num, 2.3, 3.5); result.Hop = 0.4 * Pulse(num, 0.28, 1.05) + 0.25 * Pulse(num, 4.15, 0.8); result.TailFan = 0.6; result.TailYaw = 15.0 * Math.Sin(num * 16.0) * Window(num, 1.4, 2.2); break; } return result; } private static double Clamp(double x, double a, double b) { return Math.Max(a, Math.Min(b, x)); } private static double Smooth(double x) { x = Clamp(x, 0.0, 1.0); return x * x * (3.0 - 2.0 * x); } private static double Window(double t, double start, double end, double edge = 0.35) { return Smooth((t - start) / edge) * Smooth((end - t) / edge); } private static double Pulse(double t, double start, double duration) { if (t <= start || t >= start + duration) { return 0.0; } double num = Math.Sin((t - start) / duration * Math.PI); return num * num; } } public sealed class HarborEntry { public readonly string Prefab; public readonly string Name; public readonly string Recipe; public readonly int Amount; public HarborEntry(string prefab, string name, string recipe = "", int amount = 1) { Prefab = prefab; Name = name; Recipe = recipe; Amount = amount; } } public static class HarborCatalog { public static readonly IReadOnlyList<HarborEntry> Pieces = Array.AsReadOnly(new HarborEntry[9] { new HarborEntry("ShipConstruction", "Keel cradle", "FineWood:20,IronNails:10,ElderBark:10"), new HarborEntry("ShipConstruction1", "Launching rollers", "FineWood:20,IronNails:10,ElderBark:10"), new HarborEntry("ShipConstruction2", "Ship framing gantry", "FineWood:20,IronNails:10,ElderBark:10"), new HarborEntry("PierCrane1", "Timber pier crane", "Chain:4,Wood:40,RoundLog:20,IronNails:20"), new HarborEntry("PierCrane2", "Braced pier crane", "Iron:4,Wood:40,RoundLog:20,IronNails:20"), new HarborEntry("PulleyCobia", "Single block", "Wood:10,LeatherScraps:4"), new HarborEntry("PulleyElephantSeal", "Double purchase", "Wood:10,LeatherScraps:4"), new HarborEntry("PulleyMarlin", "Heavy purchase", "Wood:10,LeatherScraps:4"), new HarborEntry("FishingDock", "Pelican fishing station", "BronzeNails:20,Wood:20,RoundLog:4,Coins:250") }); public static readonly IReadOnlyList<HarborEntry> Items = Array.AsReadOnly(new HarborEntry[8] { new HarborEntry("ResinWood", "Resin wood", "", 10), new HarborEntry("CaulkedWood", "Caulked wood", "", 10), new HarborEntry("ClothShip", "Sail canvas"), new HarborEntry("ShipRope", "Marine rope"), new HarborEntry("WindBelt", "Wind belt", "Ruby:1,AmberPearl:4,DeerHide:5,Blueberries:2"), new HarborEntry("FishExtract", "Fish extract"), new HarborEntry("FishExtract2", "Fish extract"), new HarborEntry("DriedFishBasket", "Dried fish basket") }); public const string TableRecipe = "Bronze:1,Wood:40,BronzeNails:20"; } public static class HarborMotion { public const double Duration = 12.0; public static double Elapsed(long started, long now) { if (started > 0 && now > started) { return Math.Min(12.0, ((double)now - (double)started) / 10000000.0); } return 0.0; } public static float Travel(double seconds) { if (double.IsNaN(seconds) || seconds <= 0.0 || seconds >= 10.0) { return 0f; } double num = ((seconds < 4.0) ? (seconds / 4.0) : ((seconds <= 6.0) ? 1.0 : ((10.0 - seconds) / 4.0))); return (float)(num * num * (3.0 - 2.0 * num)); } public static bool Busy(long started, long now) { if (started > 0 && now >= started) { return ((double)now - (double)started) / 10000000.0 < 12.0; } return false; } } public static class HelmMath { public static bool NeedsSlowTurn(double headingError, bool alreadySlowing) { return Math.Abs(headingError) > (double)(alreadySlowing ? 55 : 80); } public static double StoppingDistance(double speed, double deceleration, double reactionSeconds, double margin) { if (deceleration <= 0.0) { throw new ArgumentOutOfRangeException("deceleration"); } speed = Math.Abs(speed); return speed * reactionSeconds + speed * speed / (2.0 * deceleration) + margin; } public static double Rudder(double headingError, bool reverse) { return Math.Max(-0.65, Math.Min(0.65, headingError / 55.0)) * (double)((!reverse) ? 1 : (-1)); } } public readonly struct IslandCell : IEquatable<IslandCell> { public readonly int X; public readonly int Z; public IslandCell(int x, int z) { X = x; Z = z; } public bool Equals(IslandCell other) { if (X == other.X) { return Z == other.Z; } return false; } public override bool Equals(object? obj) { if (obj is IslandCell other) { return Equals(other); } return false; } public override int GetHashCode() { return (X * 397) ^ Z; } } public enum SurveyState { SearchingShore, Surveying, Complete, Failed } public sealed class IslandSurvey { public const int CellSize = 8; public const int WorldRadius = 9800; public const int MaximumCells = 1000000; private readonly Func<float, float, float> height; private readonly float sea; private readonly int maximum; private readonly Queue<IslandCell> seeds; private readonly Queue<IslandCell> frontier = new Queue<IslandCell>(); private readonly HashSet<IslandCell> examined = new HashSet<IslandCell>(); private readonly HashSet<IslandCell> land = new HashSet<IslandCell>(); private IslandCell? expanding; private int side; private static readonly int[] Dx = new int[4] { -1, 1, 0, 0 }; private static readonly int[] Dz = new int[4] { 0, 0, -1, 1 }; public SurveyState State { get; private set; } public string Error { get; private set; } = ""; public IReadOnlyCollection<IslandCell> Cells => land; public IslandSurvey(float originX, float originZ, float sea, Func<float, float, float> height, int maximum = 1000000) { if (!Finite(originX) || !Finite(originZ) || !Finite(sea) || maximum < 1) { throw new ArgumentException("Invalid survey origin or budget."); } this.height = height; this.sea = sea; this.maximum = maximum; int num = (int)Math.Round(originX / 8f); int num2 = (int)Math.Round(originZ / 8f); List<IslandCell> list = new List<IslandCell>(); for (int i = -16; i <= 16; i++) { for (int j = -16; j <= 16; j++) { if (i * i + j * j <= 256) { list.Add(new IslandCell(num + i, num2 + j)); } } } seeds = new Queue<IslandCell>(list.OrderBy((IslandCell c) => Distance(c, originX, originZ))); } private static double Distance(IslandCell c, float x, float z) { return Math.Pow((float)(c.X * 8) - x, 2.0) + Math.Pow((float)(c.Z * 8) - z, 2.0); } private bool IsLand(float x, float z) { float num = height(x, z); if (!Finite(num)) { throw new InvalidOperationException("Terrain height is unavailable."); } return num > sea + 0.05f; } private static bool Finite(float n) { if (!float.IsNaN(n)) { return !float.IsInfinity(n); } return false; } private bool Inside(IslandCell c) { return (double)c.X * (double)c.X + (double)c.Z * (double)c.Z < 1500625.0; } private void Fail(string reason) { Error = reason; State = SurveyState.Failed; frontier.Clear(); } public void Step(int budget) { if (budget < 1) { return; } try { while (budget-- > 0 && State != SurveyState.Complete && State != SurveyState.Failed) { if (State == SurveyState.SearchingShore) { if (seeds.Count == 0) { Fail("No island shoreline within 128 m of this dock."); break; } IslandCell islandCell = seeds.Dequeue(); if (Inside(islandCell) && IsLand(islandCell.X * 8, islandCell.Z * 8)) { land.Add(islandCell); examined.Add(islandCell); frontier.Enqueue(islandCell); State = SurveyState.Surveying; } continue; } if (!expanding.HasValue) { if (frontier.Count == 0) { State = SurveyState.Complete; break; } expanding = frontier.Dequeue(); side = 0; } IslandCell value = expanding.Value; IslandCell islandCell2 = new IslandCell(value.X + Dx[side], value.Z + Dz[side]); if (++side == 4) { expanding = null; } if (land.Contains(islandCell2)) { continue; } if (!Inside(islandCell2)) { Fail("This landmass reaches the edge of the surveyable world."); break; } if (examined.Contains(islandCell2)) { continue; } if (!IsLand(islandCell2.X * 8, islandCell2.Z * 8)) { examined.Add(islandCell2); continue; } bool flag = true; for (int i = 1; i <= 3; i++) { if (!IsLand(((float)value.X + (float)((islandCell2.X - value.X) * i) * 0.25f) * 8f, ((float)value.Z + (float)((islandCell2.Z - value.Z) * i) * 0.25f) * 8f)) { flag = false; break; } } if (flag) { if (land.Count >= maximum) { Fail("This landmass is too large to survey in one report."); break; } land.Add(islandCell2); frontier.Enqueue(islandCell2); } } } catch (Exception ex) { Fail("Survey paused: " + ex.Message); } } public bool Contains(float x, float z) { return land.Contains(new IslandCell((int)Math.Round(x / 8f), (int)Math.Round(z / 8f))); } } public static class ScoutingPoi { public static string Label(string prefab) { string text = (prefab ?? "").ToLowerInvariant(); if (text.Contains("hildir") || text.Contains("boss") || text.Contains("altar") || text.Contains("starttemple")) { return ""; } if (text.Contains("sunkencrypt")) { return "Sunken crypt"; } if (text.Contains("crypt")) { return "Burial chamber"; } if (text.Contains("trollcave")) { return "Troll cave"; } if (text.Contains("mountaincave")) { return "Frost cave"; } if (text.Contains("dvergrtownentrance") || text.Contains("infestedmine")) { return "Infested mine"; } if (text.Contains("goblincamp")) { return "Fuling village"; } if (text.Contains("draugrvillage")) { return "Draugr village"; } if (text.Contains("charredfortress") || text.Contains("fortress")) { return "Fortress"; } if (text.Contains("dvergr") && (text.Contains("tower") || text.Contains("harbour") || text.Contains("town"))) { return "Dvergr outpost"; } if (text.Contains("ruin") || text.Contains("stonetower") || text.Contains("woodhouse")) { return "Ruins"; } return ""; } } public sealed class LocalDetour { public readonly int RejoinIndex; public readonly RouteSearch Search; private LocalDetour(int index, RouteSearch search) { RejoinIndex = index; Search = search; } public static LocalDetour? Create(Point start, IReadOnlyList<Point> route, int waypoint, Func<Point, Point, bool> clear) { if (waypoint < 0 || waypoint >= route.Count) { return null; } int i; for (i = waypoint; i < route.Count - 1 && start.Distance(route[i]) < 48.0; i++) { } if (start.Distance(route[i]) > 160.0) { return null; } return new LocalDetour(i, new RouteSearch(start, route[i], (Point a, Point b) => start.Distance(a) <= 192.0 && start.Distance(b) <= 192.0 && clear(a, b), 6.0, 4096)); } public List<Point> Splice(IReadOnlyList<Point> previous) { if (Search.State != SearchState.Found) { throw new InvalidOperationException("No clear local detour."); } List<Point> list = new List<Point>(Search.Route); for (int i = RejoinIndex + 1; i < previous.Count; i++) { list.Add(previous[i]); } return list; } } public readonly struct PaddleStroke { public readonly double X; public readonly double Y; public readonly double Z; public readonly double AxisX; public readonly double AxisY; public readonly double AxisZ; public readonly double Twist; public readonly double Lean; private PaddleStroke(double x, double y, double z, double ax, double ay, double az, double twist, double lean) { X = x; Y = y; Z = z; double num = Math.Sqrt(ax * ax + ay * ay + az * az); AxisX = ax / num; AxisY = ay / num; AxisZ = az / num; Twist = twist; Lean = lean; } public static PaddleStroke Sample(double seconds, bool doubleBlade, bool reverse) { if (double.IsNaN(seconds) || double.IsInfinity(seconds)) { seconds = 0.0; } double num = seconds % (doubleBlade ? 1.8 : 1.65) / (doubleBlade ? 1.8 : 1.65) * Math.PI * 2.0; if (reverse) { num = 0.0 - num; } double num2 = Math.Sin(num); double num3 = Math.Cos(num); if (doubleBlade) { return new PaddleStroke(0.06 * num2, 0.805 + 0.055 * Math.Cos(2.0 * num), 0.3 + 0.055 * Math.Cos(2.0 * num), 1.0, -0.76 * num2, 0.46 * num3, 12.0 * num3, 3.0 + 5.0 * Math.Cos(2.0 * num)); } double num4 = (1.0 - num3) * 0.5; return new PaddleStroke(-0.22, 0.67 + 0.35 * num4, 0.2 + 0.15 * num2, 0.64, 0.95 - 0.35 * num4, 0.08 * num2, 9.0 * num2, 6.0 * num3); } } public enum PuffinCrewJob { Hammer, Shave, Haul, Timber, Supplies } public static class PuffinCrewPlan { public const int Count = 6; public const int UnobservedArrivalSeconds = 20; public static bool WaitingForCrew(long called, long begun) { if (called > 0) { return begun == 0; } return false; } public static bool CanBeginConstruction(long called, long begun, long now, bool observed, bool assembled) { if (WaitingForCrew(called, begun) && now >= called) { if (!observed) { return (double)now - (double)called >= 200000000.0; } return assembled; } return false; } public static double ConstructionElapsed(long called, long begun, long commissioned, long now, double duration) { if (!WaitingForCrew(called, begun)) { return ShipConstruction.Elapsed((called > 0) ? begun : commissioned, now, duration); } return 0.0; } public static PuffinCrewJob Job(int index) { switch (index) { default: return PuffinCrewJob.Supplies; case 4: return PuffinCrewJob.Timber; case 2: return PuffinCrewJob.Haul; case 1: return PuffinCrewJob.Shave; case 0: case 3: return PuffinCrewJob.Hammer; } } public static float Variation(long seed, int index) { int num = (int)seed ^ (int)(seed >> 32) ^ ((index + 1) * -1640531535); int num2 = (num ^ (num >>> 16)) * -2048144777; return (float)(uint)((num2 ^ (num2 >>> 13)) & 0xFFFFFF) / 16777216f; } public static float ArrivalDelay(long seed, int index) { return (float)index * 0.65f + Variation(seed, index) * 0.8f; } public static PuffinPoint Departure(long seed, int index) { double num = ((double)index + ((double)Variation(seed, index) - 0.5) * 0.55) * Math.PI * 2.0 / 6.0 + (double)Variation(seed, 17) * Math.PI * 2.0; return new PuffinPoint((float)Math.Cos(num), 0.32f + Variation(seed, index + 31) * 0.2f, (float)Math.Sin(num)); } public static PuffinWorkPose Work(PuffinCrewJob job, double seconds) { if (job == PuffinCrewJob.Hammer) { PuffinWorkPose result = PuffinPerformance.Station(1, seconds); result.Pitch *= 0.65f; result.BodyPitch *= 0.25f; result.Crouch *= 0.2f; return result; } float num = (float)(Math.Sin(seconds * Math.PI * 2.0 / ((job == PuffinCrewJob.Shave) ? 2.2 : 2.7)) * 0.5 + 0.5); return job switch { PuffinCrewJob.Shave => new PuffinWorkPose { Tool = 5, Pitch = 42f - num * 18f, BodyPitch = 5f + num * 10f, Wing = 18f }, PuffinCrewJob.Haul => new PuffinWorkPose { Pitch = 10f - num * 17f, BodyPitch = (0f - num) * 17f, Wing = 25f, Crouch = num * 0.045f }, _ => new PuffinWorkPose { Tool = ((job == PuffinCrewJob.Timber) ? 6 : 7), Pitch = -8f, BodyPitch = 5f, Wing = 22f }, }; } } public static class PuffinCrewSpace { public static bool Clear(PuffinPoint a, PuffinPoint b, PuffinPoint min, PuffinPoint max, float padding = 0.2f) { float enter = 0f; float exit = 1f; if (Slab(a.X, b.X, min.X, max.X) && Slab(a.Y, b.Y, min.Y, max.Y)) { return !Slab(a.Z, b.Z, min.Z, max.Z); } return true; bool Slab(float p, float q, float lo, float hi) { lo -= padding; hi += padding; float num = q - p; if (Math.Abs(num) < 1E-06f) { if (p >= lo) { return p <= hi; } return false; } float num2 = (lo - p) / num; float num3 = (hi - p) / num; if (num2 > num3) { float num4 = num2; num2 = num3; num3 = num4; } enter = Math.Max(enter, num2); exit = Math.Min(exit, num3); return enter <= exit; } } } public struct PuffinPoint { public float X; public float Y; public float Z; public PuffinPoint(float x, float y, float z) { X = x; Y = y; Z = z; } public static float Distance(PuffinPoint a, PuffinPoint b) { float num = a.X - b.X; float num2 = a.Y - b.Y; float num3 = a.Z - b.Z; return (float)Math.Sqrt(num * num + num2 * num2 + num3 * num3); } } public sealed class PuffinFlightPath { public enum Result { Searching, Found, Unreachable } private sealed class Node { public int X; public int Y; public int Z; public float Cost = float.MaxValue; public Node Parent; public bool Closed; } public readonly List<PuffinPoint> Points = new List<PuffinPoint>(); private readonly float Cell; private const int Limit = 4096; private readonly PuffinPoint start; private readonly PuffinPoint goal; private readonly Func<PuffinPoint, PuffinPoint, bool> clear; private readonly Dictionary<(int, int, int), Node> nodes = new Dictionary<(int, int, int), Node>(); private readonly List<(Node node, float score)> heap = new List<(Node, float)>(); public Result State { get; private set; } public int Expanded { get; private set; } public PuffinFlightPath(PuffinPoint start, PuffinPoint goal, Func<PuffinPoint, PuffinPoint, bool> clear, float resolution = 0.75f) { this.start = start; this.goal = goal; this.clear = clear; Cell = Math.Max(0.35f, Math.Min(1.5f, resolution)); if (!Finite(start) || !Finite(goal) || PuffinPoint.Distance(start, goal) > 210f || !clear(start, start) || !clear(goal, goal)) { State = Result.Unreachable; } else if (clear(start, goal)) { Points.Add(start); Points.Add(goal); State = Result.Found; } else { Node node = new Node { Cost = 0f }; nodes.Add((0, 0, 0), node); Push(node, PuffinPoint.Distance(start, goal)); } } private static bool Finite(PuffinPoint p) { if (!float.IsNaN(p.X + p.Y + p.Z)) { return !float.IsInfinity(p.X + p.Y + p.Z); } return false; } private PuffinPoint Position(Node n) { return new PuffinPoint(start.X + (float)n.X * Cell, start.Y + (float)n.Y * Cell, start.Z + (float)n.Z * Cell); } public void Step(int budget) { if (State != Result.Searching) { return; } for (int i = 0; i < budget; i++) { if (heap.Count <= 0) { break; } Node node = Pop(); if (node.Closed) { continue; } node.Closed = true; Expanded++; PuffinPoint puffinPoint = Position(node); if (PuffinPoint.Distance(puffinPoint, goal) < Cell * 2.5f && clear(puffinPoint, goal)) { Finish(node); return; } if (Expanded >= 4096) { State = Result.Unreachable; return; } for (int j = 0; j < 3; j++) { for (int k = -1; k <= 1; k += 2) { int num = node.X + ((j == 0) ? k : 0); int num2 = node.Y + ((j == 1) ? k : 0); int num3 = node.Z + ((j == 2) ? k : 0); PuffinPoint puffinPoint2 = new PuffinPoint(start.X + (float)num * Cell, start.Y + (float)num2 * Cell, start.Z + (float)num3 * Cell); if (puffinPoint2.X < Math.Min(start.X, goal.X) - 12f || puffinPoint2.X > Math.Max(start.X, goal.X) + 12f || puffinPoint2.Z < Math.Min(start.Z, goal.Z) - 12f || puffinPoint2.Z > Math.Max(start.Z, goal.Z) + 12f || puffinPoint2.Y < Math.Min(start.Y, goal.Y) - 6f || puffinPoint2.Y > Math.Max(start.Y, goal.Y) + 9f) { continue; } (int, int, int) key = (num, num2, num3); if (nodes.TryGetValue(key, out var value) && value.Closed) { continue; } float num4 = node.Cost + Cell * ((j == 1) ? 1.2f : 1f); if ((value != null && num4 >= value.Cost) || !clear(puffinPoint, puffinPoint2)) { continue; } if (value == null) { if (nodes.Count >= 8192) { State = Result.Unreachable; return; } value = new Node { X = num, Y = num2, Z = num3 }; nodes.Add(key, value); } value.Cost = num4; value.Parent = node; Push(value, num4 + PuffinPoint.Distance(puffinPoint2, goal)); } } } if (heap.Count == 0) { State = Result.Unreachable; } } private void Finish(Node n) { List<PuffinPoint> list = new List<PuffinPoint> { goal }; while (n != null) { list.Add(Position(n)); n = n.Parent; } list.Reverse(); Points.Add(list[0]); int num = 0; while (num < list.Count - 1) { int num2 = Math.Min(num + 8, list.Count - 1); while (num2 > num + 1 && !clear(list[num], list[num2])) { num2--; } Points.Add(list[num2]); num = num2; } State = Result.Found; } private void Push(Node n, float score) { heap.Add((n, score)); int num = heap.Count - 1; while (num > 0) { int num2 = (num - 1) / 2; if (heap[num2].score <= score) { break; } heap[num] = heap[num2]; num = num2; } heap[num] = (n, score); } private Node Pop() { Node item = heap[0].node; (Node, float) value = heap[heap.Count - 1]; heap.RemoveAt(heap.Count - 1); if (heap.Count == 0) { return item; } int num = 0; while (num * 2 + 1 < heap.Count) { int num2 = num * 2 + 1; if (num2 + 1 < heap.Count && heap[num2 + 1].score < heap[num2].score) { num2++; } if (heap[num2].score >= value.Item2) { break; } heap[num] = heap[num2]; num = num2; } heap[num] = value; return item; } } public sealed class PuffinGroundPath { private sealed class Node { public PuffinPoint Point; public float Cost; public float Score; public Node Parent; public bool Closed; } public readonly List<PuffinPoint> Points = new List<PuffinPoint>(); private readonly PuffinPoint start; private readonly PuffinPoint goal; private readonly Func<PuffinPoint, PuffinPoint?> floor; private readonly Func<PuffinPoint, PuffinPoint, bool> clear; private readonly Dictionary<(int, int, int), Node> nodes = new Dictionary<(int, int, int), Node>(); private readonly List<Node> open = new List<Node>(); private const float Cell = 0.6f; public PuffinFlightPath.Result State { get; private set; } public int Expanded { get; private set; } public PuffinGroundPath(PuffinPoint start, PuffinPoint goal, Func<PuffinPoint, PuffinPoint?> floor, Func<PuffinPoint, PuffinPoint, bool> clear) { this.start = start; this.goal = goal; this.floor = floor; this.clear = clear; if (float.IsNaN(start.X + start.Y + start.Z + goal.X + goal.Y + goal.Z) || float.IsInfinity(start.X + start.Y + start.Z + goal.X + goal.Y + goal.Z) || PuffinPoint.Distance(start, goal) > 210f || !clear(start, start) || !clear(goal, goal)) { State = PuffinFlightPath.Result.Unreachable; } else if (clear(start, goal)) { Points.Add(start); Points.Add(goal); State = PuffinFlightPath.Result.Found; } else { Node node = new Node { Point = start, Score = PuffinPoint.Distance(start, goal) }; nodes[Key(start)] = node; open.Add(node); } } private (int, int, int) Key(PuffinPoint p) { return ((int)Math.Round((p.X - start.X) / 0.6f), (int)Math.Round(p.Y / 0.25f), (int)Math.Round((p.Z - start.Z) / 0.6f)); } public void Step(int budget) { if (State != PuffinFlightPath.Result.Searching) { return; } for (int i = 0; i < budget; i++) { if (open.Count <= 0) { break; } int index = 0; for (int j = 1; j < open.Count; j++) { if (open[j].Score < open[index].Score) { index = j; } } Node node = open[index]; open.RemoveAt(index); node.Closed = true; Expanded++; if (PuffinPoint.Distance(node.Point, goal) < 1.2f && clear(node.Point, goal)) { Points.Add(goal); for (Node node2 = node; node2 != null; node2 = node2.Parent) { Points.Add(node2.Point); } Points.Reverse(); State = PuffinFlightPath.Result.Found; return; } if (Expanded >= 1024) { State = PuffinFlightPath.Result.Unreachable; return; } for (int k = -1; k <= 1; k++) { for (int l = -1; l <= 1; l++) { if (k == 0 && l == 0) { continue; } PuffinPoint arg = new PuffinPoint(node.Point.X + (float)k * 0.6f, node.Point.Y, node.Point.Z + (float)l * 0.6f); if (arg.X < Math.Min(start.X, goal.X) - 9f || arg.X > Math.Max(start.X, goal.X) + 9f || arg.Z < Math.Min(start.Z, goal.Z) - 9f || arg.Z > Math.Max(start.Z, goal.Z) + 9f) { continue; } PuffinPoint? puffinPoint = floor(arg); if (!puffinPoint.HasValue) { continue; } PuffinPoint value = puffinPoint.Value; if (!clear(node.Point, value)) { continue; } (int, int, int) key = Key(value); nodes.TryGetValue(key, out var value2); float num = node.Cost + PuffinPoint.Distance(node.Point, value); if (value2 == null || (!value2.Closed && !(num >= value2.Cost))) { if (value2 == null) { value2 = new Node { Point = value }; nodes.Add(key, value2); open.Add(value2); } value2.Cost = num; value2.Score = num + PuffinPoint.Distance(value, goal); value2.Parent = node; } } } } if (open.Count == 0) { State = PuffinFlightPath.Result.Unreachable; } } } public struct PuffinWorkPose { public int Tool; public float X; public float Z; public float Hop; public float Yaw; public float Pitch; public float HeadYaw; public float Roll; public float BodyPitch; public float Crouch; public float Wing; } public static class PuffinPerformance { public static PuffinWorkPose Sample(bool hasSail, double elapsed) { double num = Math.Max(0.0, elapsed) % 36.0; int num2 = (int)(num / 12.0); num %= 12.0; int num3 = ((num2 == 0) ? 1 : ((num2 == 1 || !hasSail) ? 2 : 3)); double num4 = Smooth((num - 0.45) / 0.65) * (1.0 - Smooth((num - 9.85) / 0.65)); float num5 = (float)(Arc(num, 0.45, 0.65) + Arc(num, 9.85, 0.65)) * 0.22f; PuffinWorkPose result = new PuffinWorkPose { X = (float)num4 * num3 switch { 2 => -0.48f, 1 => -0.22f, _ => -0.12f, }, Z = (float)num4 * ((num3 == 2) ? 0.09f : (-0.03f)), Hop = num5, Yaw = (float)num4 * (float)(num3 switch { 2 => 18, 1 => -24, _ => -12, }), Crouch = (float)(Pulse(num, 0.05, 0.4) * 0.075 + Pulse(num, 1.1, 0.4) * 0.05 + Pulse(num, 9.45, 0.4) * 0.075 + Pulse(num, 10.5, 0.4) * 0.05), Wing = num5 * 125f, Pitch = (0f - num5) * 35f, Roll = (float)(Math.Sin(num * Math.PI / 6.0) * 3.0) }; if (num >= 1.5 && num < 9.4) { result.Tool = num3; double num6 = Smooth((num - 1.5) / 0.35) * Smooth((9.4 - num) / 0.35); switch (num3) { case 1: { double t2 = (num - 1.5) % 1.45; double num9 = Pulse(t2, 0.05, 0.52); double num10 = Pulse(t2, 0.62, 0.24); double num11 = Pulse(t2, 0.86, 0.35); result.Pitch += (float)((-24.0 * num9 + 43.0 * num10 - 9.0 * num11) * num6); result.BodyPitch = (float)((8.0 - 9.0 * num9 + 44.0 * num10) * num6); result.Crouch += (float)(0.065 * num10 * num6); result.Wing += (float)(12.0 * num6); break; } case 2: { double t = (num - 1.5) % 2.1; double num8 = Pulse(t, 0.25, 0.26) + Pulse(t, 0.72, 0.24) + Pulse(t, 1.1, 0.22); result.Pitch += (float)((24.0 + 16.0 * num8) * num6); result.BodyPitch = (float)((38.0 + 8.0 * num8) * num6); result.HeadYaw = (float)(8.0 * Math.Sin(num * 1.8) * num6); result.Crouch += (float)(0.025 * num8 * num6); result.Wing += (float)(10.0 * num6); break; } default: { double num7 = (num - 1.5) % 1.8 / 1.8; result.Pitch += (float)((23.0 + 12.0 * Math.Sin(num7 * Math.PI * 2.0)) * num6); result.HeadYaw = (float)(22.0 * Math.Sin(num7 * Math.PI * 2.0) * num6); result.BodyPitch = (float)(10.0 * num6); result.Wing += (float)((20.0 + 6.0 * Math.Cos(num7 * Math.PI * 2.0)) * num6); break; } } } else { result.HeadYaw = (float)(25.0 * Pulse(num, 11.0, 0.8)); } return result; } public static PuffinWorkPose Station(int tool, double seconds) { double num = Smooth(seconds / 0.35); double num2 = Math.Max(0.0, seconds) % tool switch { 3 => 1.8, 2 => 2.1, 1 => 1.45, _ => 2.6, }; PuffinWorkPose result = new PuffinWorkPose { Tool = tool }; switch (tool) { case 1: { double num4 = Pulse(num2, 0.05, 0.52); double num5 = Pulse(num2, 0.62, 0.24); double num6 = Pulse(num2, 0.86, 0.35); result.Pitch = (float)(-24.0 * num4 + 43.0 * num5 - 9.0 * num6); result.BodyPitch = (float)(8.0 - 9.0 * num4 + 44.0 * num5); result.Crouch = (float)(0.065 * num5); result.Wing = 12f; break; } case 2: { double num3 = Pulse(num2, 0.62, 0.24); result.Pitch = (float)(24.0 + 16.0 * num3); result.BodyPitch = (float)(38.0 + 8.0 * num3); result.HeadYaw = (float)Math.Sin(seconds * 1.8) * 8f; result.Wing = 10f; break; } case 3: result.Pitch = 23f + 12f * (float)Math.Sin(num2 * Math.PI * 2.0 / 1.8); result.HeadYaw = 22f * (float)Math.Sin(num2 * Math.PI * 2.0 / 1.8); result.BodyPitch = 10f; result.Wing = 20f + 6f * (float)Math.Cos(num2 * Math.PI * 2.0 / 1.8); break; default: result.Pitch = 26f + (float)Math.Sin(seconds * 2.4) * 9f; result.HeadYaw = (float)Math.Sin(seconds * 2.4) * 22f; result.BodyPitch = 16f; result.Wing = 12f; break; } result.Pitch *= (float)num; result.BodyPitch *= (float)num; result.Crouch *= (float)num; result.HeadYaw *= (float)num; result.Wing *= (float)num; return result; } public static bool Strike(int tool, double before, double after) { double num = tool switch { 3 => 1.8, 2 => 2.1, 1 => 1.45, _ => 2.6, }; if (Math.Floor((before - 0.74) / num) != Math.Floor((after - 0.74) / num)) { return after >= 0.74; } return false; } private static double Smooth(double t) { t = Math.Max(0.0, Math.Min(1.0, t)); return t * t * (3.0 - 2.0 * t); } private static double Pulse(double t, double start, double duration) { if (t <= start || t >= start + duration) { return 0.0; } double num = Math.Sin((t - start) / duration * Math.PI); return num * num; } private static double Arc(double t, double start, double duration) { if (t <= start || t >= start + duration) { return 0.0; } double num = (t - start) / duration; return 4.0 * num * (1.0 - num); } } public static class RoutePreference { public static bool Shortcut(IReadOnlyList<Point> route, int from, int to, Func<Point, Point, double> weight) { double num = 0.0; for (int i = from; i < to; i++) { num += route[i].Distance(route[i + 1]) * weight(route[i], route[i + 1]); } return route[from].Distance(route[to]) * weight(route[from], route[to]) <= num + 1E-06; } } public readonly struct Point { public readonly double X; public readonly double Y; public Point(double x, double y) { X = x; Y = y; } public double Distance(Point b) { return Math.Sqrt((X - b.X) * (X - b.X) + (Y - b.Y) * (Y - b.Y)); } } public enum SearchState { Searching, Found, NoRoute, BudgetExceeded } public sealed class RouteSearch { private sealed class Node { public int X; public int Y; public double Cost; public Node? Parent; public bool Closed; } private readonly struct Entry { public readonly Node Node; public readonly double Priority; public readonly double Cost; public Entry(Node node, double priority) { Node = node; Priority = priority; Cost = node.Cost; } } private readonly List<Entry> heap = new List<Entry>(); private readonly Dictionary<(int, int), Node> nodes = new Dictionary<(int, int), Node>(); private readonly Point start; private readonly Point goal; private readonly double spacing; private readonly int maxExpanded; private readonly Func<Point, Point, bool> clear; private readonly Func<Point, Point, double>? weight; public SearchState State { get; private set; } public List<Point> Route { get; } = new List<Point>(); public int Expanded { get; private set; } public RouteSearch(Point start, Point goal, Func<Point, Point, bool> clear, double spacing = 24.0, int maxExpanded = 20000, Func<Point, Point, double>? weight = null) { if (spacing <= 0.0 || maxExpanded <= 0) { throw new ArgumentOutOfRangeException(); } this.start = start; this.goal = goal; this.clear = clear; this.weight = weight; this.spacing = spacing; this.maxExpanded = maxExpanded; Node node = new Node(); nodes[(0, 0)] = node; Push(node, start.Distance(goal)); } public void Step(int budget = 8) { while (State == SearchState.Searching && budget-- > 0) { if (heap.Count == 0) { State = SearchState.NoRoute; break; } Entry entry = Pop(); Node node = entry.Node; if (node.Closed || entry.Cost != node.Cost) { continue; } if (++Expanded > maxExpanded) { State = SearchState.BudgetExceeded; break; } node.Closed = true; Point arg = Position(node); if (arg.Distance(goal) <= spacing * 1.5 && clear(arg, goal)) { Route.Add(goal); for (Node node2 = node; node2 != null; node2 = node2.Parent) { Route.Add(Position(node2)); } Route.Reverse(); State = SearchState.Found; break; } for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { if (i == 0 && j == 0) { continue; } (int, int) key = (node.X + i, node.Y + j); if (nodes.TryGetValue(key, out Node value) && value.Closed) { continue; } Point point = new Point(start.X + (double)key.Item1 * spacing, start.Y + (double)key.Item2 * spacing); if (!clear(arg, point)) { continue; } double num = weight?.Invoke(arg, point) ?? 1.0; if (double.IsNaN(num) || double.IsInfinity(num) || num < 1.0) { num = 1.0; } double num2 = node.Cost + arg.Distance(point) * num; if (value == null || !(num2 >= value.Cost)) { if (value == null) { value = new Node { X = key.Item1, Y = key.Item2 }; nodes[key] = value; } value.Cost = num2; value.Parent = node; Push(value, num2 + point.Distance(goal)); } } } } } private Point Position(Node n) { return new Point(start.X + (double)n.X * spacing, start.Y + (double)n.Y * spacing); } private void Push(Node node, double priority) { Entry entry = new Entry(node, priority); int num = heap.Count; heap.Add(entry); while (num > 0) { int num2 = (num - 1) / 2; if (heap[num2].Priority <= priority) { break; } heap[num] = heap[num2]; num = num2; } heap[num] = entry; } private Entry Pop() { Entry result = heap[0]; Entry value = heap[heap.Count - 1]; heap.RemoveAt(heap.Count - 1); if (heap.Count == 0) { return result; } int num = 0; while (num * 2 + 1 < heap.Count) { int num2 = num * 2 + 1; if (num2 + 1 < heap.Count && heap[num2 + 1].Priority < heap[num2].Priority) { num2++; } if (value.Priority <= heap[num2].Priority) { break; } heap[num] = heap[num2]; num = num2; } heap[num] = value; return result; } } public sealed class ScoutPoint { public readonly float X; public readonly float Z; public readonly string Name; public ScoutPoint(float x, float z, string name) { X = x; Z = z; Name = name; } } public sealed class ScoutReport { public readonly HashSet<IslandCell> Cells; public readonly List<ScoutPoint> Points; public ScoutReport(IEnumerable<IslandCell> cells, IEnumerable<ScoutPoint> points) { Cells = new HashSet<IslandCell>(cells); Points = new List<ScoutPoint>(points); } public bool Contains(float x, float z) { return Cells.Contains(new IslandCell((int)Math.Round(x / 8f), (int)Math.Round(z / 8f))); } public byte[] Encode() { using MemoryStream memoryStream = new MemoryStream(); using (GZipStream output = new GZipStream(memoryStream, CompressionLevel.Fastest, leaveOpen: true)) { using BinaryWriter binaryWriter = new BinaryWriter(output, Encoding.UTF8, leaveOpen: true); binaryWriter.Write(1); binaryWriter.Write(Cells.Count); foreach (IslandCell cell in Cells) { binaryWriter.Write(cell.X); binaryWriter.Write(cell.Z); } binaryWriter.Write(Points.Count); foreach (ScoutPoint point in Points) { binaryWriter.Write(point.X); binaryWriter.Write(point.Z); byte[] bytes = Encoding.UTF8.GetBytes(point.Name); if (bytes.Length > 128) { throw new InvalidDataException("POI label is too long."); } binaryWriter.Write(bytes.Length); binaryWriter.Write(bytes); } } return memoryStream.ToArray(); } public static ScoutReport Decode(byte[] data) { if (data.Length > 16777216) { throw new InvalidDataException("Report is too large."); } using MemoryStream stream = new MemoryStream(data); using GZipStream input = new GZipStream(stream, CompressionMode.Decompress); using BinaryReader binaryReader = new BinaryReader(input); if (binaryReader.ReadInt32() != 1) { throw new InvalidDataException("Unknown survey report version."); } int num = binaryReader.ReadInt32(); if (num < 1 || num > 1000000) { throw new InvalidDataException("Invalid survey size."); } HashSet<IslandCell> hashSet = new HashSet<IslandCell>(); for (int i = 0; i < num; i++) { int num2 = binaryReader.ReadInt32(); int num3 = binaryReader.ReadInt32(); if (Math.Abs((long)num2) > 1225 || Math.Abs((long)num3) > 1225 || !hashSet.Add(new IslandCell(num2, num3))) { throw new InvalidDataException("Invalid survey cell."); } } num = binaryReader.ReadInt32(); if (num < 0 || num > 10000) { throw new InvalidDataException("Invalid POI count."); } List<ScoutPoint> list = new List<ScoutPoint>(); for (int j = 0; j < num; j++) { float num4 = binaryReader.ReadSingle(); float num5 = binaryReader.ReadSingle(); int num6 = binaryReader.ReadInt32(); if (float.IsNaN(num4) || float.IsNaN(num5) || float.IsInfinity(num4) || float.IsInfinity(num5) || Math.Abs(num4) > 9800f || Math.Abs(num5) > 9800f || num6 < 1 || num6 > 128) { throw new InvalidDataException("Invalid POI."); } byte[] array = binaryReader.ReadBytes(num6); if (array.Length != num6) { throw new EndOfStreamException(); } list.Add(new ScoutPoint(num4, num5, Encoding.UTF8.GetString(array))); } if (binaryReader.BaseStream.ReadByte() != -1) { throw new InvalidDataException("Trailing survey data."); } return new ScoutReport(hashSet, list); } } public sealed class ShipBlueprint { public readonly string Id; public readonly string Name; public readonly string Source; public readonly string Recipe; public readonly int BuildSeconds; public readonly bool HasSail; public bool UsesSlipway { get { switch (Id) { case "hercule": case "merchant": case "longship": case "big_cargo": case "warship": return true; default: return false; } } } public string Prefab => Source; public ShipBlueprint(string id, string name, string source, string recipe, int seconds, bool sails = true) { Id = id; Name = name; Source = source; Recipe = recipe; BuildSeconds = seconds; HasSail = sails; } } public enum ShipwrightTask { Idle, Hammer, Chisel, Stitch, AwaitingLaunch } public static class ShipConstruction { public static readonly IReadOnlyList<ShipBlueprint> Blueprints = Array.AsReadOnly(new ShipBlueprint[10] { new ShipBlueprint("dugout", "Dugout", "HelmsmanDugout", "Wood:12", 120, sails: false), new ShipBlueprint("kayak", "Finewood Kayak", "HelmsmanFinewoodKayak", "FineWood:10,Wood:12,Resin:8", 150, sails: false), new ShipBlueprint("tandem_kayak", "Tandem Finewood Kayak", "HelmsmanTandemKayak", "FineWood:16,Wood:16,Resin:10", 180, sails: false), new ShipBlueprint("little_boat", "Ceol", "LittleBoat", "LeatherScraps:8,Wood:40", 240), new ShipBlueprint("currach", "Currach", "HelmsmanCurrach", "DeerHide:10,Wood:25,Resin:10", 180), new ShipBlueprint("hercule", "Falkuša fishing boat", "HerculeShip", "DeerHide:10,RoundLog:30,Resin:30,BronzeNails:100,LeatherScraps:10", 360), new ShipBlueprint("merchant", "Ottar", "MercantShip", "DeerHide:20,RoundLog:40,Resin:40,IronNails:100,LeatherScraps:10", 480), new ShipBlueprint("longship", "Longship", "VikingShip", ShipwrightRules.Hull.Recipe, 720), new ShipBlueprint("big_cargo", "Big cargo ship", "BigCargoShip", "DeerHide:20,FineWood:50,Resin:50,Coal:50,IronNails:150,LeatherScraps:20", 900), new ShipBlueprint("warship", "Snekkja", "WarShip", "DeerHide:20,FineWood:50,Resin:50,Coal:50,IronNails:120,LeatherScraps:10", 1080) }); public static ShipBlueprint? Find(string id) { return Blueprints.FirstOrDefault((ShipBlueprint b) => b.Id == id); } public static bool ValidRecipe(string id, string recipe) { ShipBlueprint shipBlueprint = Find(id); if (shipBlueprint == null) { return false; } if (recipe == shipBlueprint.Recipe) { return true; } return id switch { "hercule" => recipe == "ClothShip:2,ResinWood:30,BronzeNails:100,ShipRope:2", "merchant" => recipe == "ClothShip:4,ResinWood:40,IronNails:100,ShipRope:2", "big_cargo" => recipe == "ClothShip:4,CaulkedWood:50,IronNails:150,ShipRope:4", "warship" => recipe == "ClothShip:4,CaulkedWood:50,IronNails:120,ShipRope:2", _ => false, }; } public static bool ValidDuration(double seconds) { if (!double.IsNaN(seconds) && !double.IsInfinity(seconds) && seconds >= 30.0) { return seconds <= 86400.0; } return false; } public static double Elapsed(long started, long now, double duration) { if (started < 0 || now <= started || !ValidDuration(duration)) { return 0.0; } return Math.Min(duration, ((double)now - (double)started) / 10000000.0); } public static ShipwrightTask Task(bool hasSail, double elapsed, double duration) { if (!ValidDuration(duration) || double.IsNaN(elapsed) || elapsed < 0.0) { return ShipwrightTask.Idle; } if (elapsed >= duration) { return ShipwrightTask.AwaitingLaunch; } int num = (int)(elapsed % 36.0 / 12.0); if (num != 0) { if (num != 1 && hasSail) { return ShipwrightTask.Stitch; } return ShipwrightTask.Chisel; } return ShipwrightTask.Hammer; } } public static class ShipRules { public static bool CanSail(string prefabName, bool hasSail, float sailForce, bool hasClothSail = false) { string text = prefabName.Replace("_", "").Replace("-", "").Replace(" ", "") .ToLowerInvariant(); if ((hasSail || hasClothSail) && sailForce > 0.0001f && !text.Contains("rowboat") && !text.Contains("rowing")) { return !text.Contains("canoe"); } return false; } public static bool IsMast(string animation) { return string.Equals(animation, "attach_mast", StringComparison.OrdinalIgnoreCase); } public static bool IsSeat(string animation) { if (!string.IsNullOrEmpty(animation)) { if (animation.IndexOf("sit", StringComparison.OrdinalIgnoreCase) < 0 && animation.IndexOf("chair", StringComparison.OrdinalIgnoreCase) < 0) { return animation.Equals("attach_lox", StringComparison.OrdinalIgnoreCase); } return true; } return false; } public static bool Eligible(bool canSail, int seats) { if (!canSail) { return seats >= 1; } return true; } } public static class ShipText { public static string CleanName(string text) { StringBuilder stringBuilder = new StringBuilder(); bool flag = false; string text2 = text ?? ""; foreach (char c in text2) { switch (c) { case '<': flag = true; continue; case '>': flag = false; continue; } if (!flag && !char.IsControl(c) && stringBuilder.Length < 48) { stringBuilder.Append(c); } } return stringBuilder.ToString().Trim(); } public static string Bearing(double east, double north) { if (Math.Abs(east) + Math.Abs(north) < 0.01) { return "Here"; } string[] obj = new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" }; double num = (Math.Atan2(east, north) * 180.0 / Math.PI + 360.0) % 360.0; return obj[(int)Math.Floor((num + 22.5) / 45.0) % 8]; } } public sealed class ShipUpgrade { public readonly string Id; public readonly string Name; public readonly string Recipe; public readonly string Station; public readonly string Requires; public readonly int Level; public ShipUpgrade(string id, string name, string recipe, string station, int level, string requires = "") { Id = id; Name = name; Recipe = recipe; Station = station; Level = level; Requires = requires; } } public static class ShipwrightRules { public static readonly ShipUpgrade Hull = new ShipUpgrade("longship", "Commission longship", "IronNails:100,DeerHide:10,FineWood:40,ElderBark:40", "$piece_workbench", 1); public static readonly ShipUpgrade[] Upgrades = new ShipUpgrade[6] { new ShipUpgrade("lantern", "Deck lantern", "SurtlingCore:3,BronzeNails:10,FineWood:6,Chain:1", "$piece_forge", 4), new ShipUpgrade("canopy", "Sheltering canopy", "JuteRed:2,LoxPelt:2,LinenThread:6,RoundLog:2", "$piece_workbench", 4), new ShipUpgrade("cargo1", "Cargo hold I — 7 × 3", "ElderBark:20,Silver:5,Obsidian:10", "$piece_workbench", 4), new ShipUpgrade("cargo2", "Cargo hold II — 7 × 4", "BlackMetal:20,YggdrasilWood:20,FineWood:20", "$piece_artisanstation", 1, "cargo1"), new ShipUpgrade("treatment", "Fire / Ashlands treatment", "CeramicPlate:20,Tar:20,YggdrasilWood:20,IronNails:50", "$piece_blackforge", 3), new ShipUpgrade("trophy", "Decorative trophy mount", "FineWood:4,BronzeNails:10", "$piece_workbench", 1) }; public static Dictionary<string, int> Costs(string recipe) { Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.Ordinal); string[] array = recipe.Split(','); foreach (string text in array) { string[] array2 = text.Trim().Split(':'); if (array2.Length != 2 || string.IsNullOrWhiteSpace(array2[0]) || !int.TryParse(array2[1], out var result) || result <= 0 || result > 100000) { throw new ArgumentException("Invalid material cost: " + text); } dictionary[array2[0]] = checked((dictionary.TryGetValue(array2[0], out var value) ? value : 0) + result); } if (dictionary.Count == 0) { throw new ArgumentException("A paid build needs materials."); } return dictionary; } public static string Check(ShipUpgrade upgrade, Func<string, bool> built) { if (built(upgrade.Id)) { return "Already fitted."; } if (upgrade.Requires.Length > 0 && !built(upgrade.Requires)) { return "Fit Cargo hold I first."; } return ""; } public static (int Width, int Height) Cargo(bool first, bool second, int width, int height) { return (Width: Math.Max(width, (first || second) ? 7 : 6), Height: Math.Max(height, second ? 4 : 3)); } public static bool CanPay(IReadOnlyDictionary<string, int> costs, Func<string, int> available) { return costs.All<KeyValuePair<string, int>>((KeyValuePair<string, int> pair) => available(pair.Key) >= pair.Value); } } public enum YardSound { PuffinMurmur, PuffinGreeting, Hammer, Scrape, Rope, Creak, Splash, Footstep, WoodSlide } public static class ShipyardAudioSynth { public const int Rate = 22050; public static float[] Create(YardSound sound, int variant = 0) { double num = sound switch { YardSound.Scrape => 0.65, YardSound.Rope => 0.8, YardSound.PuffinGreeting => 0.52, YardSound.PuffinMurmur => 1.1, YardSound.Creak => 2.4, YardSound.Splash => 1.8, YardSound.WoodSlide => 4.2, _ => 0.19, }; float[] array = new float[(int)(22050.0 * num)]; Random random = new Random(2311 + (int)sound * 911 + variant * 1777); double num2 = 0.0; double num3 = 0.0; double num4 = 0.0; double num5 = 0.0; for (int i = 0; i < array.Length; i++) { double num6 = (double)i / 22050.0; double num7 = num6 / num; double num8 = random.NextDouble() * 2.0 - 1.0; num3 = 0.86 * num3 + 0.14 * num8; double num9 = Math.Pow(Math.Sin(Math.PI * num7), 0.8); double num10 = 0.0; switch (sound) { case YardSound.PuffinMurmur: case YardSound.PuffinGreeting: num2 += Math.PI * 2.0 * ((sound == YardSound.PuffinGreeting) ? (260.0 - 75.0 * num7) : (150.0 + 22.0 * Math.Sin(num6 * 11.0))) / 22050.0; num10 = (Math.Sin(num2) * 0.2 + Math.Sin(num2 * 2.0) * 0.11 + num3 * 0.3) * (0.6 + 0.4 * Math.Sin(num6 * 37.0)); break; case YardSound.Hammer: num10 = (Math.Sin(num6 * 2.0 * Math.PI * 190.0) * 0.5 + num8 * 0.17) * Math.Exp((0.0 - num6) * 25.0); num9 = Math.Min(1.0, num6 / 0.003); break; case YardSound.Footstep: num10 = num3 * 0.45 * Math.Exp((0.0 - num6) * 23.0); num9 = Math.Min(1.0, num6 / 0.004); break; case YardSound.Scrape: num10 = (num8 - num3) * 0.18 * (0.65 + 0.35 * Math.Sin(num6 * 83.0)); break; case YardSound.Rope: num10 = num3 * 0.38 + num8 * 0.06 * (0.5 + 0.5 * Math.Sin(num6 * 49.0)); break; case YardSound.Creak: { num4 = 0.992 * num4 + 0.008 * num8; double num13 = 0.35 + 0.65 * Math.Pow(Math.Sin(num6 * 5.2 + (double)variant), 2.0); double num14 = ((random.NextDouble() < 0.003 * num13) ? ((random.NextDouble() * 2.0 - 1.0) * 0.5) : 0.0); num5 = 0.91 * num5 + num14; num2 += Math.PI * 2.0 * (67.0 + num4 * 420.0 + 27.0 * Math.Sin(num6 * 3.8 + Math.Sin(num6 * 13.0))) / 22050.0; num10 = (num3 * 0.42 + num5 * 0.65 + Math.Sin(num2) * 0.075 + Math.Sin(num2 * 2.73) * 0.04) * num13; break; } case YardSound.WoodSlide: { num4 = 0.998 * num4 + 0.002 * num8; double num11 = 0.5 + 0.24 * Math.Sin(num6 * 19.0 + Math.Sin(num6 * 7.0)) + 0.16 * Math.Sin(num6 * 43.0); double num12 = Math.Pow(Math.Max(0.0, Math.Sin(num6 * 11.7 + 0.6 * Math.Sin(num6 * 3.0))), 9.0); num10 = num3 * (0.62 + 0.3 * num12) + (num8 - num3) * 0.08 * num11 + num4 * 0.28; num9 = Math.Min(1.0, num6 / 0.035) * Math.Min(1.0, (num - num6) / 0.035); break; } case YardSound.Splash: num10 = num3 * 0.65 + num8 * 0.16; num9 = Math.Pow(Math.Sin(Math.PI * num7), 0.55) * Math.Exp((0.0 - num7) * 1.8); break; } array[i] = (float)(num10 * num9 * Math.Min(1.0, (1.0 - num7) * 30.0)); } return array; } } public readonly struct ShoreCandidate { public readonly Point Center; public readonly double Heading; public ShoreCandidate(Point center, double heading) { Center = center; Heading = heading; } } public static class ShorelineSearch { public const double MaximumRadius = 48.0; public const double MaximumSwimGap = 12.0; public static IEnumerable<ShoreCandidate> Candidates(Point caller, double length) { if (!Finite(caller.X) || !Finite(caller.Y) || !Finite(length) || length <= 0.0) { yield break; } for (double radius = Math.Max(8.0, length * 0.5 + 3.0); radius <= 48.0; radius += 4.0) { for (int angle = 0; angle < 24; angle++) { double num = (double)angle * Math.PI / 12.0; yield return new ShoreCandidate(new Point(caller.X + Math.Sin(num) * radius, caller.Y + Math.Cos(num) * radius), angle * 15 + 180); } } } public static bool ShoreAccess(Point caller, Point bow, double sea, Func<Point, double?> height) { if (!Finite(caller.X) || !Finite(caller.Y) || !Finite(bow.X) || !Finite(bow.Y) || !Finite(sea)) { return false; } double num = caller.Distance(bow); if (num > 48.0 || num < 1.0) { return false; } int num2 = (int)Math.Ceiling(num); double num3 = 0.0; bool flag = false; for (int i = 0; i <= num2; i++) { double num4 = (double)i / (double)num2; double? num5 = height(new Point(caller.X + (bow.X - caller.X) * num4, caller.Y + (bow.Y - caller.Y) * num4)); if (!num5.HasValue || !Finite(num5.Value) || num5.Value > sea + 8.0) { return false; } if (num5.Value < sea - 0.25) { flag = true; continue; } if (flag) { return false; } num3 = num * num4; } if (flag) { return num - num3 <= 12.0; } return false; } private static bool Finite(double value) { if (!double.IsNaN(value)) { return !double.IsInfinity(value); } return false; } } public static class SlipwayMotion { public const float LaunchSeconds = 10f; public const float ResetSeconds = 6f; public static float Clamp(double t) { return (float)Math.Max(0.0, Math.Min(1.0, double.IsNaN(t) ? 0.0 : t)); } public static float Ease(double t) { float num = Clamp(t); return num * num * (3f - 2f * num); } public static float Travel(double seconds) { float num = Clamp((seconds - 1.4) / 8.6); return num * num * (2f - num); } public static float Level(double seconds) { return Ease(((double)Travel(seconds) - 0.48) / 0.52); } public static float Cradle(double seconds, float launchDistance = 23f) { return Math.Min(11f, Travel(seconds) * Math.Max(0f, launchDistance)); } public static float Reset(double seconds) { return 1f - Ease(seconds / 6.0); } public static double LaunchElapsed(long now, long started, long paused, long pauseTicks) { if (started > 0) { return Math.Max(0.0, ((double)((paused > 0) ? paused : now) - (double)started - (double)Math.Max(0L, pauseTicks)) / 10000000.0); } return 0.0; } public static float FloatOffset(float waterOffset, float massCenter, float forceDistance, float force, float gravity) { return waterOffset - massCenter - Math.Abs(gravity) * forceDistance / (50f * Math.Max(0.01f, force)); } public static float PartStart(string name, float height) { name = (name ?? "").ToLowerInvariant(); if (name.StartsWith("deck ") || name == "deck") { return 0.5f; } if (name.StartsWith("mast ") || name == "mast") { return 0.66f; } if (name.StartsWith("rigging ") || name == "rigging") { return 0.78f; } if (name.StartsWith("sail ") || name == "sail") { return 0.9f; } if (name.StartsWith("cargo ") || name == "cargo" || name.StartsWith("decoration ")) { return 0.96f; } if (name.StartsWith("fixed ") || name == "fixed") { return 0.4f; } if (name.Contains("sail")) { return 0.9f; } if (name.Contains("rope") || name.Contains("rigging") || name.Contains("shroud")) { return 0.78f; } if (name.Contains("cargo") || name.Contains("barrel") || name.Contains("sack") || name.Contains("shield") || name.Contains("decoration")) { return 0.96f; } if (name.Contains("mast") || name.Contains("yard")) { return 0.66f; } if (name.Contains("deck") || name.Contains("bench") || name.Contains("seat")) { return 0.5f; } return 0.05f + 0.48f * Clamp(height); } } public static class VoyageWords { public static string Obstruction(string reason) { string text = reason.ToLowerInvariant(); if (text.Contains("water depth")) { return "Too shallow ahead, Viking. " + reason; } if (text.Contains("not available") || text.Contains("not loaded")) { return "I can't check the waters ahead yet, Viking. " + reason; } if (text.Contains("blocked") || text.Contains("no clear")) { return "Our way is blocked, Viking. " + reason; } if (text.Contains("no progress")) { return "We're making no headway, Viking. I'm checking another course."; } return "Holding here, Viking. " + reason; } } public static class WorkshopCoverage { public static bool Contains(float radius, float dx, float dy, float dz) { if (radius > 0f && !float.IsNaN(radius) && !float.IsInfinity(radius)) { return (double)dx * (double)dx + (double)dy * (double)dy + (double)dz * (double)dz <= (double)radius * (double)radius; } return false; } } }