Decompiled source of SwappableStaysail v1.1.4
BepInEx/plugins/SwappableStaysail/SwappableStaysail.dll
Decompiled 9 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; using cakeslice; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")] [assembly: AssemblyCompany("SwappableStaysail")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.4.0")] [assembly: AssemblyInformationalVersion("1.1.4")] [assembly: AssemblyProduct("SwappableStaysail")] [assembly: AssemblyTitle("SwappableStaysail")] [assembly: AssemblyVersion("1.1.4.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace SwappableStaysail { internal static class StaysailInteractionVisuals { private static readonly FieldInfo PointedAtButtonField = AccessTools.Field(typeof(GoPointer), "pointedAtButton"); internal static void ResetAfterInteraction() { Reset(); } internal static void ResetForShipyard() { Reset(); } private static void Reset() { StayInstallTargeting.Deactivate(); FurledStaysailButton.ResetAllInteractionVisuals(); ClearPointerTargets(); } private static void ClearPointerTargets() { if (PointedAtButtonField == null) { return; } GoPointer[] array = Object.FindObjectsOfType<GoPointer>(); foreach (GoPointer obj in array) { object? value = PointedAtButtonField.GetValue(obj); GoPointerButton val = (GoPointerButton)((value is GoPointerButton) ? value : null); if (!((Object)(object)val == (Object)null) && (val is StayInstallTargetButton || val is FurledStaysailButton)) { val.ForceUnlook(); val.Unclick(); PointedAtButtonField.SetValue(obj, null); } } } } [HarmonyPatch(typeof(PrefabsDirectory), "Start")] internal static class PrefabsDirectoryPatch { [HarmonyPrefix] [HarmonyPriority(0)] private static void Prefix(PrefabsDirectory __instance) { SailpackFactory.EnsureRegistered(__instance); } [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(PrefabsDirectory __instance) { SailpackFactory.PopulateItemCache(__instance); } } [HarmonyPatch(typeof(SaveablePrefab), "Load")] internal static class ActivateLoadedSailPackagePatch { [HarmonyPostfix] private static void Postfix(SaveablePrefab __instance) { if (!((Object)(object)__instance == (Object)null) && !((Component)__instance).gameObject.activeSelf && !((Object)(object)((Component)__instance).GetComponent<SailpackData>() == (Object)null)) { ((Component)__instance).gameObject.SetActive(true); ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogInfo((object)($"Activated loaded sail package {__instance.instanceId} " + $"from prefab {__instance.prefabIndex}.")); } } } } [HarmonyPatch(typeof(SaveLoadManager), "SaveModData")] internal static class SaveModDataPatch { [HarmonyPrefix] private static void Prefix() { SailpackPersistence.Save(); } } [HarmonyPatch(typeof(SaveLoadManager), "LoadModData")] internal static class LoadModDataPatch { [HarmonyPostfix] private static void Postfix() { SailpackPersistence.Load(); } } [HarmonyPatch(typeof(SaveableBoatCustomization), "GetData")] internal static class ExcludeSailmakerPreviewsFromSavePatch { [HarmonyPostfix] private static void Postfix(SaveableBoatCustomization __instance, SaveBoatCustomizationData __result) { if (__result?.sails == null || SailmakerSession.GetPreviews().Count == 0) { return; } BoatRefs component = ((Component)__instance).GetComponent<BoatRefs>(); if (component?.masts == null) { return; } List<int> list = new List<int>(); int num = 0; Mast[] masts = component.masts; foreach (Mast val in masts) { if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeSelf || val.sails == null) { continue; } foreach (GameObject sail in val.sails) { if (SailmakerSession.IsPreview(((Object)(object)sail != (Object)null) ? sail.GetComponent<Sail>() : null)) { list.Add(num); } num++; } } for (int num2 = list.Count - 1; num2 >= 0; num2--) { int num3 = list[num2]; if (num3 >= 0 && num3 < __result.sails.Count) { __result.sails.RemoveAt(num3); } } } } [HarmonyPatch(typeof(ShipItem), "UpdateLookText")] internal static class SailpackLookTextPatch { [HarmonyPostfix] private static void Postfix(ShipItem __instance) { SailpackData component = ((Component)__instance).GetComponent<SailpackData>(); if ((Object)(object)component != (Object)null && component.HasRecord) { ((GoPointerButton)__instance).lookText = component.Record.GetDisplayName(); } } } [HarmonyPatch(typeof(GoPointer), "DoRaycast")] internal static class StayInstallRaycastPatch { [HarmonyPrefix] private static void Prefix(GoPointer __instance) { StayInstallTargeting.PrepareRaycast(__instance); } } [HarmonyPatch(typeof(PickupableItemCollisionChecker), "GetDecollision")] internal static class SailPackageBigItemDecollisionPatch { [HarmonyPrefix] private static bool Prefix(ShipItem ___item, ref Vector3 __result) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) SailpackData sailpackData = (((Object)(object)___item != (Object)null) ? ((Component)___item).GetComponent<SailpackData>() : null); if ((Object)(object)sailpackData == (Object)null || !sailpackData.SuppressBigItemDecollision) { return true; } __result = Vector3.zero; return false; } } [HarmonyPatch(typeof(GoPointer), "LateUpdate")] internal static class StaysailInstallActivatePatch { [HarmonyPrefix] private static void Prefix(GoPointer __instance, GoPointerButton ___pointedAtButton) { if (StaysailInput.ActivateDown(__instance) && ___pointedAtButton is StayInstallTargetButton { IsAvailable: not false } stayInstallTargetButton) { stayInstallTargetButton.TryInstall(__instance.GetHeldItem()); } } } [HarmonyPatch(typeof(GoPointer), "PickUpItem")] internal static class SailPackageHeldLayerPatch { [HarmonyPostfix] private static void Postfix(PickupableItem item) { SailpackData sailpackData = (((Object)(object)item != (Object)null) ? ((Component)item).GetComponent<SailpackData>() : null); if (!((Object)(object)sailpackData == (Object)null)) { Transform[] componentsInChildren = ((Component)sailpackData).GetComponentsInChildren<Transform>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((Component)componentsInChildren[i]).gameObject.layer = 2; } ShipItem val = (ShipItem)(object)((item is ShipItem) ? item : null); if ((Object)(object)val?.itemRigidbodyC != (Object)null) { ((Component)val.itemRigidbodyC).gameObject.layer = 2; } } } } [HarmonyPatch(typeof(GoPointer), "DropItem")] internal static class SailPackageDroppedLayerPatch { [HarmonyPrefix] private static void Prefix(GoPointer __instance, out SailpackData __state) { PickupableItem heldItem = __instance.GetHeldItem(); __state = (((Object)(object)heldItem != (Object)null) ? ((Component)heldItem).GetComponent<SailpackData>() : null); } [HarmonyPostfix] private static void Postfix(SailpackData __state) { if (!((Object)(object)__state == (Object)null)) { Transform[] componentsInChildren = ((Component)__state).GetComponentsInChildren<Transform>(true); for (int i = 0; i < componentsInChildren.Length; i++) { ((Component)componentsInChildren[i]).gameObject.layer = 0; } ShipItem component = ((Component)__state).GetComponent<ShipItem>(); if ((Object)(object)component?.itemRigidbodyC != (Object)null) { ((Component)component.itemRigidbodyC).gameObject.layer = 2; } } } } [HarmonyPatch(typeof(Mast), "Start")] internal static class MastStartPatch { [HarmonyPostfix] private static void Postfix(Mast __instance) { StaysailTargets.EnsureRuntimeButtons(__instance); } } [HarmonyPatch(typeof(Mast), "AttachSailToMast")] internal static class MastAttachPatch { [HarmonyPostfix] private static void Postfix(Mast __instance, GameObject sailObject) { StaysailTargets.EnsureRuntimeButtons(__instance); StaysailTargets.EnsureRemovalButton(__instance, sailObject); } } [BepInPlugin("DogEggz.SwappableStaysail", "Swappable Staysail", "1.1.4")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class SwappableStaysailPlugin : BaseUnityPlugin { public const string PluginGuid = "DogEggz.SwappableStaysail"; public const string PluginName = "Swappable Staysail"; public const string PluginVersion = "1.1.4"; public const string ShipyardExpansionGuid = "com.nandbrew.shipyardexpansion"; internal const string SaveDataKey = "DogEggz"; private Harmony harmony; internal static SwappableStaysailPlugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; harmony = new Harmony("DogEggz.SwappableStaysail"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)("Swappable Staysail 1.1.4 loaded. Sail package prefab indexes: legacy=" + $"{390}, standard=" + $"{600}, small=" + $"{601}.")); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } SailmakerSession.Dispose(); StayInstallTargeting.Dispose(); FurledStaysailButton.DisposeAll(); SailpackFactory.Dispose(); SailpackPersistence.Dispose(); Instance = null; Log = null; } } [HarmonyPatch(typeof(ShipyardDocuments), "Update")] internal static class SailmakerEntryPatch { private static bool cloning; [HarmonyPostfix] private static void Postfix(ShipyardDocuments __instance) { //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Expected O, but got Unknown //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_0272: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027e: Unknown result type (might be due to invalid IL or missing references) if (cloning || (Object)(object)__instance == (Object)null || (Object)(object)__instance.shipyard == (Object)null || (Object)(object)((Component)__instance).GetComponent<SailmakerEntryInstalled>() != (Object)null) { return; } cloning = true; try { GameObject val = Object.Instantiate<GameObject>(((Component)__instance).gameObject, ((Component)__instance).transform.parent); val.SetActive(false); ((Object)val).name = "Sail Maker"; ShipyardDocuments component = val.GetComponent<ShipyardDocuments>(); GameObject copy = (((Object)(object)component != (Object)null) ? component.keeper : null); GoPointerButton[] componentsInChildren = val.GetComponentsInChildren<GoPointerButton>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i]); } Outline[] componentsInChildren2 = val.GetComponentsInChildren<Outline>(true); for (int i = 0; i < componentsInChildren2.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren2[i]); } SailmakerEntryInstalled[] componentsInChildren3 = val.GetComponentsInChildren<SailmakerEntryInstalled>(true); for (int i = 0; i < componentsInChildren3.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren3[i]); } Collider component2 = ((Component)__instance).GetComponent<Collider>(); Collider component3 = val.GetComponent<Collider>(); Collider[] componentsInChildren4 = val.GetComponentsInChildren<Collider>(true); foreach (Collider val2 in componentsInChildren4) { if ((Object)(object)val2 != (Object)(object)component3) { Object.DestroyImmediate((Object)(object)val2); } } Vector3 val3 = Vector3.zero; Scene scene = ((Component)__instance).gameObject.scene; Vector3 localPosition; Vector3 localEulerAngles; bool flag = TryGetAuthoredPose(((Scene)(ref scene)).name, out localPosition, out localEulerAngles); if (flag) { val.transform.position = ((Component)__instance).transform.TransformPoint(localPosition); val.transform.rotation = ((Component)__instance).transform.rotation * Quaternion.Euler(localEulerAngles); } else { val3 = GetSurfaceLateral(component2, ((Component)__instance).transform); float num = GetProjectedHalfExtent(component2, val3) * 2f + 0.45f; val.transform.position = ((Component)__instance).transform.position + val3 * num; } val.AddComponent<SailmakerOrderButton>().Initialize(__instance.shipyard); val.AddComponent<SailmakerEntryStateMirror>().Initialize(__instance.keeper, copy); GameObject val4 = new GameObject("sail package delivery point"); val4.transform.SetParent(val.transform, false); val4.transform.localPosition = new Vector3(0f, 0.55f, 0.8f); SailmakerSession.RegisterDeliveryPoint(__instance.shipyard, val4.transform); ((Component)__instance).gameObject.AddComponent<SailmakerEntryInstalled>(); val.SetActive(true); int num2 = 0; while (!flag && (Object)(object)component2 != (Object)null && (Object)(object)component3 != (Object)null) { Bounds bounds = component2.bounds; if (((Bounds)(ref bounds)).Intersects(component3.bounds) && num2 < 8) { Transform transform = val.transform; transform.position += val3 * 0.25f; num2++; continue; } break; } } catch (Exception arg) { ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogError((object)$"Could not create sailmaker entry: {arg}"); } } finally { cloning = false; } } private static bool TryGetAuthoredPose(string sceneName, out Vector3 localPosition, out Vector3 localEulerAngles) { //IL_004f: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) switch (sceneName) { case "island 1 A Gold Rock": localPosition = new Vector3(-0.646f, -0.66f, 0f); localEulerAngles = new Vector3(-1.716f, 0f, -27.634f); return true; case "island 9 E Dragon Cliffs": localPosition = new Vector3(-0.267f, -0.758f, 0f); localEulerAngles = new Vector3(0f, 0f, 20.456f); return true; case "island 15 M (Fort)": localPosition = new Vector3(-0.993f, -1.933f, 0f); localEulerAngles = new Vector3(0f, 0f, -15.067f); return true; case "island 27 Lagoon SwampShipyard": localPosition = new Vector3(-0.699f, -0.891f, 0f); localEulerAngles = new Vector3(0f, 0f, -157.151f); return true; default: localPosition = Vector3.zero; localEulerAngles = Vector3.zero; return false; } } private static float GetProjectedHalfExtent(Collider collider, Vector3 direction) { //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_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_0043: 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) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)collider == (Object)null) { return 0.6f; } Bounds bounds = collider.bounds; Vector3 extents = ((Bounds)(ref bounds)).extents; Vector3 normalized = ((Vector3)(ref direction)).normalized; return Mathf.Abs(normalized.x) * extents.x + Mathf.Abs(normalized.y) * extents.y + Mathf.Abs(normalized.z) * extents.z; } private static Vector3 GetSurfaceLateral(Collider collider, Transform source) { //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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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_0043: 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_002c: 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_00ad: 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_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: 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_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) Vector3 val = source.forward; BoxCollider val2 = (BoxCollider)(object)((collider is BoxCollider) ? collider : null); if ((Object)(object)val2 != (Object)null) { Vector3 size = val2.size; if (size.x <= size.y && size.x <= size.z) { val = source.right; } else if (size.y <= size.x && size.y <= size.z) { val = source.up; } } Vector3 val3 = Vector3.Cross(Vector3.up, val); if (((Vector3)(ref val3)).sqrMagnitude < 0.01f) { val3 = Vector3.ProjectOnPlane(source.right, Vector3.up); } if (((Vector3)(ref val3)).sqrMagnitude < 0.01f) { val3 = source.right; } ((Vector3)(ref val3)).Normalize(); if (Vector3.Dot(val3, source.right) < 0f) { val3 = -val3; } return val3; } } internal sealed class SailmakerEntryInstalled : MonoBehaviour { } internal sealed class SailmakerEntryStateMirror : MonoBehaviour { private GameObject sourceKeeper; private GameObject sailmakerKeeper; internal void Initialize(GameObject source, GameObject copy) { sourceKeeper = source; sailmakerKeeper = copy; Synchronize(); } private void LateUpdate() { Synchronize(); } private void Synchronize() { if (!((Object)(object)sourceKeeper == (Object)null) && !((Object)(object)sailmakerKeeper == (Object)null)) { bool activeSelf = sourceKeeper.activeSelf; if (sailmakerKeeper.activeSelf != activeSelf) { sailmakerKeeper.SetActive(activeSelf); } } } } [HarmonyPatch(typeof(ShipyardDocuments), "OnActivate")] internal static class NormalShipyardEntryPatch { [HarmonyPrefix] private static void Prefix() { SailmakerSession.End(); } } [HarmonyPatch(typeof(Shipyard), "ActivateDocuments")] internal static class ShipyardActivatePatch { [HarmonyPrefix] private static void Prefix() { StaysailInteractionVisuals.ResetForShipyard(); } [HarmonyPostfix] private static void Postfix(Shipyard __instance) { SailmakerSession.TryBegin(__instance); } } [HarmonyPatch(typeof(Shipyard), "DischargeShip")] internal static class ShipyardDischargePatch { [HarmonyPrefix] private static void Prefix() { SailmakerSession.End(); } } [HarmonyPatch(typeof(Shipyard), "CancelOrder")] internal static class ShipyardCancelPatch { [HarmonyPrefix] private static bool Prefix() { if (SailmakerSession.Active) { SailmakerSession.Cancel(); return false; } return true; } } [HarmonyPatch(typeof(Shipyard), "SelectMast")] internal static class ShipyardSelectMastPatch { [HarmonyPrefix] private static bool Prefix(Shipyard __instance, int index) { if (!SailmakerSession.Active) { return true; } GameObject currentBoat = __instance.GetCurrentBoat(); BoatRefs val = ((currentBoat != null) ? currentBoat.GetComponent<BoatRefs>() : null); if (!SailmakerSession.IsAllowedMast((val?.masts != null && index >= 0 && index < val.masts.Length) ? val.masts[index] : null)) { NotificationUi instance = NotificationUi.instance; if (instance != null) { instance.ShowNotification("Select a supported staysail stay."); } return false; } return true; } } [HarmonyPatch(typeof(Shipyard), "SelectSail")] internal static class ShipyardSelectSailPatch { [HarmonyPrefix] private static bool Prefix(Shipyard __instance, int index) { if (!SailmakerSession.Active) { return true; } SailmakerSession.SelectPreview(index); return false; } } [HarmonyPatch(typeof(Shipyard), "AddNewSail")] internal static class ShipyardAddSailPatch { [HarmonyPrefix] private static bool Prefix(Shipyard __instance, GameObject prefab) { if (!SailmakerSession.Active) { return true; } if (!SailmakerSession.IsAllowedMast(__instance.sailInstaller.GetCurrentMast()) || !SailmakerSession.IsAllowedSailPrefab(prefab)) { NotificationUi instance = NotificationUi.instance; if (instance != null) { instance.ShowNotification("Swappable Staysail only accepts staysails on supported stays."); } return false; } SailmakerSession.AddPreview(prefab); return false; } } [HarmonyPatch(typeof(Shipyard), "RemoveSelectedSail")] internal static class ShipyardRemoveSailPatch { [HarmonyPrefix] private static bool Prefix() { if (!SailmakerSession.Active) { return true; } SailmakerSession.RemoveSelectedPreview(); return false; } } [HarmonyPatch(typeof(ShipyardSailInstaller), "ApplySailPosition")] internal static class SailmakerApplyPositionSafetyPatch { [HarmonyPrefix] private static bool Prefix(ShipyardSailInstaller __instance) { if ((Object)(object)GameState.currentShipyard != (Object)null && (Object)(object)__instance.GetCurrentMast() != (Object)null) { return (Object)(object)__instance.GetCurrentSail() != (Object)null; } return false; } } [HarmonyPatch(typeof(Shipyard), "UpdateOrder", new Type[] { typeof(bool) })] internal static class ShipyardOrderPatch { [HarmonyPrefix] private static bool Prefix(Shipyard __instance) { return !SailmakerSession.RefreshOrder(__instance); } } [HarmonyPatch(typeof(Shipyard), "ConfirmOrder")] internal static class ShipyardConfirmPatch { [HarmonyPrefix] private static bool Prefix() { if (!SailmakerSession.Active) { return true; } SailmakerSession.Buy(); return false; } } [HarmonyPatch(typeof(ShipyardUI), "SailMastCompatible")] internal static class SailCompatibilityPatch { [HarmonyPostfix] private static void Postfix(ref bool __result, GameObject sailPrefab) { if (SailmakerSession.Active) { Shipyard currentShipyard = GameState.currentShipyard; Mast mast = ((currentShipyard != null) ? currentShipyard.sailInstaller.GetCurrentMast() : null); __result = SailmakerSession.IsAllowedMast(mast) && SailmakerSession.IsAllowedSailPrefab(sailPrefab); } } } [HarmonyPatch(typeof(ShipyardButton), "OnActivate")] internal static class SailmakerButtonGuardPatch { [HarmonyPrefix] private static bool Prefix(ShipyardButton __instance) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: 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_0012: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Invalid comparison between Unknown and I4 //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 if (!SailmakerSession.Active) { return true; } ButtonFunction function = __instance.function; if ((int)function != 2) { if (function - 12 <= 3 || (int)function == 18) { return false; } return true; } return __instance.index == 5; } } [HarmonyPatch(typeof(ShipyardUI), "UpdateMastSailsList")] internal static class SailmakerSailListPatch { [HarmonyPrefix] private static bool Prefix(ShipyardUI __instance) { if (!SailmakerSession.Active) { return true; } SailmakerSession.RenderPreviewList(__instance); return false; } } [HarmonyPatch(typeof(ShipyardUI), "UpdateButtonSelections")] internal static class SailmakerSelectionPatch { [HarmonyPostfix] private static void Postfix(ShipyardUI __instance) { if (SailmakerSession.Active) { SailmakerSession.RenderPreviewSelections(__instance); } } } [HarmonyPatch(typeof(ShipyardUI), "UpdateSailsCountText")] internal static class SailmakerSailCountPatch { [HarmonyPrefix] private static bool Prefix(ShipyardUI __instance) { if (!SailmakerSession.Active) { return true; } SailmakerSession.HideSailCount(__instance); return false; } } [HarmonyPatch(typeof(ShipyardUI), "RefreshButtons")] internal static class SailmakerUiPatch { private sealed class ButtonLayoutState { private const float ConfirmWidthMultiplier = 1.45f; private const float CancelLeftOffset = 0.55f; private readonly Transform button; private readonly Transform label; private readonly Vector3 buttonLocalPosition; private readonly Vector3 buttonLocalScale; private readonly Vector3 labelLocalScale; internal ButtonLayoutState(ShipyardButton source) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) button = (((Object)(object)source != (Object)null) ? ((Component)source).transform : null); label = (((Object)(object)button != (Object)null && button.childCount > 0) ? button.GetChild(0) : null); buttonLocalPosition = (((Object)(object)button != (Object)null) ? button.localPosition : Vector3.zero); buttonLocalScale = (((Object)(object)button != (Object)null) ? button.localScale : Vector3.one); labelLocalScale = (((Object)(object)label != (Object)null) ? label.localScale : Vector3.one); } internal void ApplyConfirm() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)button == (Object)null)) { button.localScale = new Vector3(buttonLocalScale.x * 1.45f, buttonLocalScale.y, buttonLocalScale.z); if ((Object)(object)label != (Object)null) { label.localScale = new Vector3(labelLocalScale.x / 1.45f, labelLocalScale.y, labelLocalScale.z); } } } internal void ApplyCancel() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)button != (Object)null) { button.localPosition = buttonLocalPosition + Vector3.left * 0.55f; } } internal void Restore() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)button != (Object)null) { button.localPosition = buttonLocalPosition; button.localScale = buttonLocalScale; } if ((Object)(object)label != (Object)null) { label.localScale = labelLocalScale; } } } private static readonly Dictionary<Collider, bool> ColliderStates = new Dictionary<Collider, bool>(); private static readonly Dictionary<Renderer, Material> RendererStates = new Dictionary<Renderer, Material>(); private static readonly Dictionary<ShipyardButton, string> ButtonTexts = new Dictionary<ShipyardButton, string>(); private static readonly Dictionary<ShipyardButton, ButtonLayoutState> ButtonLayouts = new Dictionary<ShipyardButton, ButtonLayoutState>(); [HarmonyPostfix] private static void Postfix(ShipyardUI __instance) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Invalid comparison between Unknown and I4 //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Invalid comparison between Unknown and I4 //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Invalid comparison between Unknown and I4 //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Invalid comparison between Unknown and I4 //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Invalid comparison between Unknown and I4 //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Invalid comparison between Unknown and I4 //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Invalid comparison between Unknown and I4 if (!SailmakerSession.Active) { RestoreUi(); return; } int count = SailmakerSession.GetPreviews().Count; ShipyardButton[] componentsInChildren = ((Component)__instance).GetComponentsInChildren<ShipyardButton>(true); foreach (ShipyardButton val in componentsInChildren) { bool num = (int)val.function == 12 || (int)val.function == 18 || (int)val.function == 13 || (int)val.function == 14 || (int)val.function == 15 || ((int)val.function == 2 && val.index != 5); Collider component = ((Component)val).GetComponent<Collider>(); Renderer component2 = ((Component)val).GetComponent<Renderer>(); if (num) { if ((Object)(object)component != (Object)null) { if (!ColliderStates.ContainsKey(component)) { ColliderStates[component] = component.enabled; } component.enabled = false; } if ((Object)(object)component2 != (Object)null) { if (!RendererStates.ContainsKey(component2)) { RendererStates[component2] = component2.sharedMaterial; } component2.sharedMaterial = __instance.darkParchmentMaterial; } } if ((int)val.function == 8) { if (!ButtonTexts.ContainsKey(val) && ((Component)val).transform.childCount > 0) { TextMesh component3 = ((Component)((Component)val).transform.GetChild(0)).GetComponent<TextMesh>(); if ((Object)(object)component3 != (Object)null) { ButtonTexts[val] = component3.text; } } val.SetText($"Buy Staysails ({count})"); GetLayout(val).ApplyConfirm(); } else if ((int)val.function == 9) { GetLayout(val).ApplyCancel(); } } } private static ButtonLayoutState GetLayout(ShipyardButton button) { if (!ButtonLayouts.TryGetValue(button, out var value)) { value = new ButtonLayoutState(button); ButtonLayouts[button] = value; } return value; } internal static void RestoreUi() { foreach (KeyValuePair<Collider, bool> colliderState in ColliderStates) { if ((Object)(object)colliderState.Key != (Object)null) { colliderState.Key.enabled = colliderState.Value; } } foreach (KeyValuePair<Renderer, Material> rendererState in RendererStates) { if ((Object)(object)rendererState.Key != (Object)null) { rendererState.Key.sharedMaterial = rendererState.Value; } } foreach (KeyValuePair<ShipyardButton, string> buttonText in ButtonTexts) { if ((Object)(object)buttonText.Key != (Object)null) { buttonText.Key.SetText(buttonText.Value); } } foreach (ButtonLayoutState value in ButtonLayouts.Values) { value.Restore(); } ColliderStates.Clear(); RendererStates.Clear(); ButtonTexts.Clear(); ButtonLayouts.Clear(); } } internal static class SailmakerSession { private static readonly FieldInfo CurrentOrderTotalField = AccessTools.Field(typeof(Shipyard), "currentOrderTotal"); private static readonly FieldInfo CurrentOrderTextField = AccessTools.Field(typeof(Shipyard), "currentOrderText"); private static readonly FieldInfo InstallErrorField = AccessTools.Field(typeof(Shipyard), "installError"); private static readonly FieldInfo SelectSailButtonsField = AccessTools.Field(typeof(ShipyardUI), "selectSailButtons"); private static readonly FieldInfo SailCountTextField = AccessTools.Field(typeof(ShipyardUI), "sailCountText"); private static readonly Dictionary<Shipyard, Transform> DeliveryPoints = new Dictionary<Shipyard, Transform>(); private static readonly List<SailmakerPreview> Previews = new List<SailmakerPreview>(); private static readonly Dictionary<Mast, SailmakerMastRigState> RigStates = new Dictionary<Mast, SailmakerMastRigState>(); internal static bool Pending { get; private set; } internal static Shipyard ActiveShipyard { get; private set; } internal static bool Active { get { if ((Object)(object)ActiveShipyard != (Object)null) { return (Object)(object)GameState.currentShipyard == (Object)(object)ActiveShipyard; } return false; } } internal static void RegisterDeliveryPoint(Shipyard shipyard, Transform point) { PruneDeliveryPoints(); if ((Object)(object)shipyard != (Object)null && (Object)(object)point != (Object)null) { DeliveryPoints[shipyard] = point; } } internal static void Request(Shipyard shipyard) { Pending = (Object)(object)shipyard != (Object)null; } internal static void TryBegin(Shipyard shipyard) { if (Pending && !((Object)(object)shipyard == (Object)null) && !((Object)(object)GameState.currentShipyard != (Object)(object)shipyard)) { StayInstallTargeting.Deactivate(); Pending = false; ActiveShipyard = shipyard; ClearPreviews(refresh: false); ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogInfo((object)$"Opened Sail Maker at shipyard region {shipyard.region}."); } RefreshOrder(shipyard); ShipyardUI instance = ShipyardUI.instance; if ((Object)(object)instance != (Object)null) { instance.ChangeMenuCategory(-1); instance.CloseSailCategoryMenu(); instance.HideNewSailButtons(); instance.RefreshButtons(); } } } internal static void End() { Pending = false; StayInstallTargeting.Deactivate(); try { ClearPreviews(refresh: false); } catch (Exception arg) { ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogError((object)$"Sail Maker cleanup failed during exit: {arg}"); } } finally { ActiveShipyard = null; SailmakerUiPatch.RestoreUi(); } } internal static bool IsAllowedMast(Mast mast) { if (Active) { return StaysailTargets.IsEligible(mast); } return false; } internal static bool IsAllowedSailPrefab(GameObject prefab) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 Sail val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Sail>() : null); if ((Object)(object)val != (Object)null) { return (int)val.category == 5; } return false; } internal static IReadOnlyList<SailmakerPreview> GetPreviews() { Previews.RemoveAll((SailmakerPreview preview) => preview == null || (Object)(object)preview.Mast == (Object)null || (Object)(object)preview.Sail == (Object)null); return Previews; } internal static List<SailmakerPreview> GetPreviews(Mast mast) { return (from preview in GetPreviews() where (Object)(object)preview.Mast == (Object)(object)mast select preview).ToList(); } internal static bool IsPreview(Sail sail) { if ((Object)(object)sail != (Object)null) { return GetPreviews().Any((SailmakerPreview preview) => (Object)(object)preview.Sail == (Object)(object)sail); } return false; } internal static bool AddPreview(GameObject prefab) { if (!Active || !IsAllowedSailPrefab(prefab)) { return false; } Mast currentMast = ActiveShipyard.sailInstaller.GetCurrentMast(); if (!IsAllowedMast(currentMast)) { Notify("Select a supported staysail stay."); return false; } GameObject val = null; try { val = Object.Instantiate<GameObject>(prefab); Sail component = val.GetComponent<Sail>(); SailConnections component2 = val.GetComponent<SailConnections>(); if (!CanSupplyPreviewControllers(currentMast, component2)) { Object.Destroy((Object)(object)val); Notify("That stay does not have the required sail controllers."); return false; } EnsurePreviewRigCapacity(currentMast, (currentMast.sails?.Count ?? 0) + 1); ActiveShipyard.sailInstaller.AddNewSail(val); Previews.Add(new SailmakerPreview(currentMast, component)); RefreshPreviewUi(); ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogInfo((object)("Added registered Sail Maker preview '" + component.sailName + "' for " + StaysailTargets.GetStayName(currentMast) + ".")); } return true; } catch (Exception arg) { if ((Object)(object)val != (Object)null) { if (currentMast.sails != null && currentMast.sails.Contains(val)) { currentMast.DetachSailFromMast(val); } else { Object.Destroy((Object)(object)val); } } RestorePreviewRigIfUnused(currentMast); ManualLogSource log2 = SwappableStaysailPlugin.Log; if (log2 != null) { log2.LogError((object)$"Could not add Sail Maker preview: {arg}"); } Notify("Could not add that staysail."); return false; } } internal static bool SelectPreview(int index) { if (!Active) { return false; } List<SailmakerPreview> previews = GetPreviews(ActiveShipyard.sailInstaller.GetCurrentMast()); if (index < 0 || index >= previews.Count) { return false; } ActiveShipyard.sailInstaller.SelectSail(((Component)previews[index].Sail).gameObject); RefreshPreviewUi(); return true; } internal static bool RemoveSelectedPreview() { if (!Active) { return false; } Sail selected = ActiveShipyard.sailInstaller.GetCurrentSail(); SailmakerPreview sailmakerPreview = Previews.FirstOrDefault((SailmakerPreview candidate) => (Object)(object)candidate.Sail == (Object)(object)selected); if (sailmakerPreview == null) { return false; } Previews.Remove(sailmakerPreview); ActiveShipyard.sailInstaller.SelectSail((GameObject)null); DestroyPreview(sailmakerPreview); RestorePreviewRigIfUnused(sailmakerPreview.Mast); ActiveShipyard.sailInstaller.RecheckAllSailsCols(); RefreshPreviewUi(); return true; } internal static bool Cancel() { if (!Active) { return false; } ClearPreviews(refresh: true); return true; } internal static bool RefreshOrder(Shipyard shipyard) { if (!Active || (Object)(object)shipyard != (Object)(object)ActiveShipyard || (Object)(object)ShipyardUI.instance == (Object)null) { return false; } IReadOnlyList<SailmakerPreview> previews = GetPreviews(); ShipyardUIOrderText shipyardOrderText = ShipyardUI.instance.shipyardOrderText; shipyardOrderText.ResetOrderText(); int num = 0; bool flag = false; foreach (SailmakerPreview item in previews) { bool error = false; string arg = ValidatePreview(item.Mast, item.Sail, ref error); int num2 = Mathf.RoundToInt(item.Sail.GetSailPrice() * shipyard.GetCurrencyRate()); num += num2; flag = flag || error; shipyardOrderText.AddLine($"{num2}: {item.Sail.sailName}{arg}"); } if (previews.Count > 0) { shipyardOrderText.AddLine(" "); } shipyardOrderText.SetTotalLine($"TOTAL: {num} {PlayerGold.GetCurrencyName(shipyard.region)}"); shipyardOrderText.DisplayCurrentText(); CurrentOrderTotalField?.SetValue(shipyard, num); CurrentOrderTextField?.SetValue(shipyard, $"{previews.Count} spare staysail(s), total {num}"); InstallErrorField?.SetValue(shipyard, flag); return true; } internal static bool Buy() { //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) if (!Active) { return false; } List<SailmakerPreview> list = GetPreviews().ToList(); if (list.Count == 0) { Notify("Add at least one staysail first."); return false; } int num = 0; List<SailpackRecord> list2 = new List<SailpackRecord>(); List<Mast> list3 = new List<Mast>(); foreach (SailmakerPreview item in list) { bool error = false; string text = ValidatePreview(item.Mast, item.Sail, ref error); if (error) { Notify("A staysail cannot be packed: " + text.Trim()); return false; } list2.Add(SailpackRecord.FromSail(item.Sail, item.Mast)); list3.Add(item.Mast); num += Mathf.RoundToInt(item.Sail.GetSailPrice() * ActiveShipyard.GetCurrencyRate()); } if (PlayerGold.currency[ActiveShipyard.region] < num) { Notify("Not enough money."); return false; } Transform val = GetDeliveryPoint(ActiveShipyard) ?? ActiveShipyard.editedShipPosition; if ((Object)(object)val == (Object)null) { Notify("Could not find the Sail Maker delivery point."); return false; } List<SailpackData> list4 = new List<SailpackData>(); bool flag = false; try { for (int i = 0; i < list2.Count; i++) { Vector3 position = val.position + val.right * (0.45f * (float)(i % 3)) + val.forward * (0.4f * (float)(i / 3)); SailpackData sailpackData = SailpackFactory.CreateStaged(list2[i], position, val.rotation, registerToSave: false, list3[i]); if ((Object)(object)sailpackData == (Object)null) { throw new InvalidOperationException("A staged sail package could not be created."); } list4.Add(sailpackData); } foreach (SailpackData item2 in list4) { SaveablePrefab component = ((Component)item2).GetComponent<SaveablePrefab>(); if ((Object)(object)component == (Object)null) { throw new InvalidOperationException("A staged sail package has no save component."); } component.RegisterToSave(); SailpackPersistence.Register(component.instanceId, item2.Record); } ClearPreviews(refresh: true); foreach (SailpackData item3 in list4) { SailpackFactory.ActivateStaged(item3); } PlayerGold.currency[ActiveShipyard.region] -= num; flag = true; } catch (Exception arg) { if (!flag) { foreach (SailpackData item4 in list4) { if (!((Object)(object)item4 == (Object)null)) { SailpackFactory.DiscardStaged(item4, unregisterFromSave: true); } } } ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogError((object)$"Sail package purchase failed before commit: {arg}"); } Notify("Could not create the sail packages; nothing was charged."); return false; } UISoundPlayer instance = UISoundPlayer.instance; if (instance != null) { instance.PlayGoldSound(); } try { DayLogs instance2 = DayLogs.instance; if (instance2 != null) { instance2.dayLogs[ActiveShipyard.region].LogTransaction(-num, (TransactionCategory)11); } } catch (Exception ex) { ManualLogSource log2 = SwappableStaysailPlugin.Log; if (log2 != null) { log2.LogWarning((object)("Sail package purchase succeeded but day-log entry failed: " + ex)); } } Notify($"Created {list4.Count} sail package(s)."); ManualLogSource log3 = SwappableStaysailPlugin.Log; if (log3 != null) { log3.LogInfo((object)($"Purchased {list4.Count} spare sail package(s) for {num}; " + "installed staysails were unchanged.")); } return true; } internal static void RenderPreviewList(ShipyardUI ui) { if (SelectSailButtonsField?.GetValue(ui) is GameObject[] array) { Shipyard activeShipyard = ActiveShipyard; List<SailmakerPreview> previews = GetPreviews((activeShipyard != null) ? activeShipyard.sailInstaller.GetCurrentMast() : null); for (int i = 0; i < array.Length; i++) { bool flag = i < previews.Count && (Object)(object)previews[i].Sail != (Object)null; array[i].SetActive(flag); array[i].GetComponent<ShipyardButton>().SetText(flag ? previews[i].Sail.sailName : "-"); } } } internal static void RenderPreviewSelections(ShipyardUI ui) { if (SelectSailButtonsField?.GetValue(ui) is GameObject[] array) { Shipyard activeShipyard = ActiveShipyard; List<SailmakerPreview> previews = GetPreviews((activeShipyard != null) ? activeShipyard.sailInstaller.GetCurrentMast() : null); Shipyard activeShipyard2 = ActiveShipyard; Sail val = ((activeShipyard2 != null) ? activeShipyard2.sailInstaller.GetCurrentSail() : null); for (int i = 0; i < array.Length; i++) { bool flag = i < previews.Count && (Object)(object)previews[i].Sail == (Object)(object)val; array[i].GetComponent<Renderer>().sharedMaterial = (flag ? ui.selectedButtonMaterial : ui.defaultButtonMaterial); } } } internal static void HideSailCount(ShipyardUI ui) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) object? obj = SailCountTextField?.GetValue(ui); TextMesh val = (TextMesh)((obj is TextMesh) ? obj : null); if ((Object)(object)val != (Object)null) { val.text = ""; val.color = Color.black; } } internal static void Dispose() { try { End(); } finally { DeliveryPoints.Clear(); Previews.Clear(); RigStates.Clear(); } } private static Transform GetDeliveryPoint(Shipyard shipyard) { PruneDeliveryPoints(); if (!((Object)(object)shipyard != (Object)null) || !DeliveryPoints.TryGetValue(shipyard, out var value) || !((Object)(object)value != (Object)null)) { return null; } return value; } private static void PruneDeliveryPoints() { Shipyard[] array = (from pair in DeliveryPoints where (Object)(object)pair.Key == (Object)null || (Object)(object)pair.Value == (Object)null select pair.Key).ToArray(); foreach (Shipyard key in array) { DeliveryPoints.Remove(key); } } private static string ValidatePreview(Mast mast, Sail sail, ref bool error) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 if (!StaysailTargets.IsEligible(mast)) { error = true; return " (NOT A SUPPORTED STAY)"; } if ((Object)(object)sail == (Object)null || (int)sail.category != 5) { error = true; return " (NOT A STAYSAIL)"; } float num = sail.GetCurrentInstallHeight() - sail.GetScaledHeight(); if (sail.GetScaledHeight() > mast.mastHeight || num < -0.1f) { error = true; return " (DOES NOT FIT STAY)"; } return ""; } private static void ClearPreviews(bool refresh) { Shipyard activeShipyard = ActiveShipyard; Sail selected = (((Object)(object)activeShipyard?.sailInstaller != (Object)null) ? activeShipyard.sailInstaller.GetCurrentSail() : null); bool flag = Previews.Any((SailmakerPreview preview) => (Object)(object)preview.Sail == (Object)(object)selected); SailmakerPreview[] array = Previews.ToArray(); Previews.Clear(); SailmakerPreview[] array2 = array; for (int num = 0; num < array2.Length; num++) { DestroyPreview(array2[num]); } Mast[] array3 = RigStates.Keys.ToArray(); for (int num = 0; num < array3.Length; num++) { RestorePreviewRig(array3[num]); } if ((Object)(object)activeShipyard?.sailInstaller != (Object)null) { if ((Object)(object)selected == (Object)null || flag) { activeShipyard.sailInstaller.SelectSail((GameObject)null); } activeShipyard.sailInstaller.RecheckAllSailsCols(); } if (refresh && Active) { RefreshPreviewUi(); } } private static void DestroyPreview(SailmakerPreview preview) { if ((Object)(object)preview?.Sail == (Object)null) { return; } GameObject gameObject = ((Component)preview.Sail).gameObject; Mast mast = preview.Mast; if ((Object)(object)mast != (Object)null && mast.sails != null && mast.sails.Contains(gameObject)) { if (!StaysailDetacher.TryDetach(mast, preview.Sail, out var error)) { ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogWarning((object)("Could not fully detach Sail Maker preview: " + error)); } mast.sails.Remove(gameObject); Object.Destroy((Object)(object)gameObject); } } else { Object.Destroy((Object)(object)gameObject); } } private static bool CanSupplyPreviewControllers(Mast mast, SailConnections connections) { if ((Object)(object)mast == (Object)null || (Object)(object)connections == (Object)null) { return false; } if ((Object)(object)connections.reefController != (Object)null && (mast.reefWinch == null || mast.reefWinch.Length == 0)) { return false; } if ((Object)(object)connections.angleControllerMid != (Object)null && (mast.midAngleWinch == null || mast.midAngleWinch.Length == 0)) { return false; } if (((Object)(object)connections.angleControllerLeft != (Object)null || (Object)(object)connections.angleControllerRight != (Object)null) && (mast.leftAngleWinch == null || mast.leftAngleWinch.Length == 0 || mast.rightAngleWinch == null || mast.rightAngleWinch.Length == 0)) { return false; } if ((Object)(object)connections.mastReefAttachment != (Object)null && (mast.mastReefAtt == null || mast.mastReefAtt.Length == 0)) { return false; } if ((Object)(object)connections.midRopeAttachment != (Object)null && (mast.midRopeAtt == null || mast.midRopeAtt.Length == 0) && (mast.midAngleWinch == null || mast.midAngleWinch.Length == 0)) { return false; } return true; } private static void EnsurePreviewRigCapacity(Mast mast, int count) { if (!RigStates.TryGetValue(mast, out var value)) { value = new SailmakerMastRigState(mast); RigStates[mast] = value; } value.EnsureCapacity(mast, count); } private static void RestorePreviewRigIfUnused(Mast mast) { if ((Object)(object)mast != (Object)null && !Previews.Any((SailmakerPreview preview) => (Object)(object)preview.Mast == (Object)(object)mast)) { RestorePreviewRig(mast); } } private static void RestorePreviewRig(Mast mast) { if (!((Object)(object)mast == (Object)null) && RigStates.TryGetValue(mast, out var value)) { value.Restore(mast); RigStates.Remove(mast); mast.UpdateSailOrder(); mast.UpdateControllerAttachments(); } } private static void RefreshPreviewUi() { if (Active) { RefreshOrder(ActiveShipyard); ShipyardUI instance = ShipyardUI.instance; if (instance != null) { instance.RefreshButtons(); } } } private static void Notify(string message) { NotificationUi instance = NotificationUi.instance; if (instance != null) { instance.ShowNotification(message); } } } internal sealed class SailmakerPreview { internal Mast Mast { get; } internal Sail Sail { get; } internal SailmakerPreview(Mast mast, Sail sail) { Mast = mast; Sail = sail; } } internal sealed class SailmakerMastRigState { private readonly GPButtonRopeWinch[] leftAngleWinch; private readonly GPButtonRopeWinch[] rightAngleWinch; private readonly GPButtonRopeWinch[] midAngleWinch; private readonly GPButtonRopeWinch[] reefWinch; private readonly Transform[] midRopeAtt; private readonly Transform[] mastReefAtt; private readonly Transform[] mastReefAttExtension; internal SailmakerMastRigState(Mast mast) { leftAngleWinch = mast.leftAngleWinch; rightAngleWinch = mast.rightAngleWinch; midAngleWinch = mast.midAngleWinch; reefWinch = mast.reefWinch; midRopeAtt = mast.midRopeAtt; mastReefAtt = mast.mastReefAtt; mastReefAttExtension = mast.mastReefAttExtension; } internal void EnsureCapacity(Mast mast, int count) { mast.leftAngleWinch = Expand(leftAngleWinch, count); mast.rightAngleWinch = Expand(rightAngleWinch, count); mast.midAngleWinch = Expand(midAngleWinch, count); mast.reefWinch = Expand(reefWinch, count); mast.midRopeAtt = Expand(midRopeAtt, count); mast.mastReefAtt = Expand(mastReefAtt, count); mast.mastReefAttExtension = Expand(mastReefAttExtension, count); } internal void Restore(Mast mast) { mast.leftAngleWinch = leftAngleWinch; mast.rightAngleWinch = rightAngleWinch; mast.midAngleWinch = midAngleWinch; mast.reefWinch = reefWinch; mast.midRopeAtt = midRopeAtt; mast.mastReefAtt = mastReefAtt; mast.mastReefAttExtension = mastReefAttExtension; } private static T[] Expand<T>(T[] source, int count) { if (source == null || source.Length == 0 || source.Length >= count) { return source; } T[] array = new T[count]; Array.Copy(source, array, source.Length); T val = source[^1]; for (int i = source.Length; i < array.Length; i++) { array[i] = val; } return array; } } internal sealed class SailmakerOrderButton : GoPointerButton { private Shipyard shipyard; internal void Initialize(Shipyard target) { shipyard = target; base.lookText = "Sail Maker"; } public override void ExtraLateUpdate() { if ((Object)(object)shipyard == (Object)null) { base.lookText = ""; } else if (shipyard.GetBoatsInTriggerCount() < 1) { base.lookText = "(no boat in the Sail Maker area)"; } else if (shipyard.GetBoatsInTriggerCount() > 1) { base.lookText = "(too many boats in the Sail Maker area)"; } else { base.lookText = "Sail Maker"; } } public override void OnActivate() { if (!((Object)(object)shipyard == (Object)null) && !((Object)(object)GameState.currentShipyard != (Object)null) && shipyard.GetBoatsInTriggerCount() == 1) { SailmakerSession.Request(shipyard); shipyard.ActivateDocuments(); } } } internal static class SailpackFactory { internal const int StandardDonorPrefabIndex = 219; internal const int SmallDonorPrefabIndex = 221; internal const int LegacyPrefabIndex = 390; internal const int StandardPrefabIndex = 600; internal const int SmallPrefabIndex = 601; internal const float SmallPackageMaximumMass = 25f; private static GameObject legacyPrefab; private static GameObject standardPrefab; private static GameObject smallPrefab; private static bool registrationLogged; internal static bool EnsureRegistered(PrefabsDirectory directory) { if ((Object)(object)directory == (Object)null || directory.directory == null) { ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogError((object)"Cannot register sail packages: the prefab directory is unavailable."); } return false; } if (!HasDonor(directory, 219, "standard") || !HasDonor(directory, 221, "small")) { return false; } if (directory.directory.Length <= 601) { Array.Resize(ref directory.directory, 602); } if (!CanClaimSlot(directory, 390, legacyPrefab) || !CanClaimSlot(directory, 600, standardPrefab) || !CanClaimSlot(directory, 601, smallPrefab)) { return false; } if ((Object)(object)legacyPrefab == (Object)null) { legacyPrefab = BuildPrefab(directory.directory[219], 390); } if ((Object)(object)standardPrefab == (Object)null) { standardPrefab = BuildPrefab(directory.directory[219], 600); } if ((Object)(object)smallPrefab == (Object)null) { smallPrefab = BuildPrefab(directory.directory[221], 601); } directory.directory[390] = legacyPrefab; directory.directory[600] = standardPrefab; directory.directory[601] = smallPrefab; if (!registrationLogged) { registrationLogged = true; ManualLogSource log2 = SwappableStaysailPlugin.Log; if (log2 != null) { log2.LogInfo((object)("Registered sail package prefabs: " + $"legacy loader={390}, " + $"standard={600} (donor {219}), " + $"small={601} (donor {221}).")); } } return true; } internal static void PopulateItemCache(PrefabsDirectory directory) { if (EnsureRegistered(directory)) { if (directory.shipItems == null) { directory.shipItems = (ShipItem[])(object)new ShipItem[directory.directory.Length]; } else if (directory.shipItems.Length < directory.directory.Length) { Array.Resize(ref directory.shipItems, directory.directory.Length); } directory.shipItems[390] = legacyPrefab.GetComponent<ShipItem>(); directory.shipItems[600] = standardPrefab.GetComponent<ShipItem>(); directory.shipItems[601] = smallPrefab.GetComponent<ShipItem>(); } } internal static SailpackData CreateStaged(SailpackRecord record, Vector3 position, Quaternion rotation, bool registerToSave, Mast runtimeTarget = null) { //IL_003b: 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) if (record == null || (Object)(object)PrefabsDirectory.instance == (Object)null || !EnsureRegistered(PrefabsDirectory.instance)) { return null; } GameObject val = null; SaveablePrefab val2 = null; try { val = Object.Instantiate<GameObject>(UsesSmallPackage(record.packageMass) ? smallPrefab : standardPrefab, position, rotation); val.SetActive(false); SailpackData component = val.GetComponent<SailpackData>(); component.Initialize(record, runtimeTarget); if (registerToSave) { val2 = val.GetComponent<SaveablePrefab>(); val2.RegisterToSave(); SailpackPersistence.Register(val2.instanceId, record); } return component; } catch (Exception arg) { if ((Object)(object)val2 != (Object)null && val2.instanceId > 0) { SailpackPersistence.Remove(val2.instanceId); TryUnregister(val2, "staging failure"); } if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogError((object)$"Could not stage a sail package: {arg}"); } return null; } } internal static void ActivateStaged(SailpackData data) { if ((Object)(object)data != (Object)null) { ((Component)data).gameObject.SetActive(true); } } internal static void DiscardStaged(SailpackData data, bool unregisterFromSave) { if ((Object)(object)data == (Object)null) { return; } if (unregisterFromSave) { SaveablePrefab component = ((Component)data).GetComponent<SaveablePrefab>(); if ((Object)(object)component != (Object)null && component.instanceId > 0) { SailpackPersistence.Remove(component.instanceId); TryUnregister(component, "staged-package rollback"); } } Object.Destroy((Object)(object)((Component)data).gameObject); } private static void TryUnregister(SaveablePrefab saveable, string context) { try { saveable.Unregister(); } catch (Exception ex) { ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogWarning((object)("Could not unregister a sail package during " + context + ": " + ex)); } } } internal static void Dispose() { PrefabsDirectory instance = PrefabsDirectory.instance; if ((Object)(object)instance != (Object)null) { UnregisterPrefab(instance, 390, legacyPrefab); UnregisterPrefab(instance, 600, standardPrefab); UnregisterPrefab(instance, 601, smallPrefab); } DestroyPrefab(legacyPrefab); DestroyPrefab(standardPrefab); DestroyPrefab(smallPrefab); legacyPrefab = null; standardPrefab = null; smallPrefab = null; registrationLogged = false; } private static bool HasDonor(PrefabsDirectory directory, int donorIndex, string packageKind) { if (directory.directory.Length > donorIndex && (Object)(object)directory.directory[donorIndex] != (Object)null) { return true; } ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogError((object)("Cannot register sail packages: " + packageKind + " package donor " + $"{donorIndex} is unavailable.")); } return false; } private static bool CanClaimSlot(PrefabsDirectory directory, int prefabIndex, GameObject ownedPrefab) { GameObject val = directory.directory[prefabIndex]; if ((Object)(object)val == (Object)null || (Object)(object)val == (Object)(object)ownedPrefab || (Object)(object)val.GetComponent<SailpackData>() != (Object)null) { return true; } ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogError((object)($"Cannot register sail package: prefab index {prefabIndex} is " + "occupied by '" + ((Object)val).name + "'.")); } return false; } private static bool UsesSmallPackage(float packageMass) { if (!float.IsNaN(packageMass) && !float.IsInfinity(packageMass)) { return packageMass <= 25f; } return false; } private static void UnregisterPrefab(PrefabsDirectory directory, int prefabIndex, GameObject registeredPrefab) { if (!((Object)(object)registeredPrefab == (Object)null)) { if (directory.directory != null && directory.directory.Length > prefabIndex && (Object)(object)directory.directory[prefabIndex] == (Object)(object)registeredPrefab) { directory.directory[prefabIndex] = null; } if (directory.shipItems != null && directory.shipItems.Length > prefabIndex && (Object)(object)directory.shipItems[prefabIndex] == (Object)(object)registeredPrefab.GetComponent<ShipItem>()) { directory.shipItems[prefabIndex] = null; } } } private static void DestroyPrefab(GameObject registeredPrefab) { if ((Object)(object)registeredPrefab != (Object)null) { Object.Destroy((Object)(object)registeredPrefab); } } private static GameObject BuildPrefab(GameObject donor, int prefabIndex) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(donor); val.SetActive(false); ((Object)val).name = "sail package"; val.transform.localScale = Vector3.one; if (prefabIndex == 601) { Transform val2 = val.transform.Find("label"); if ((Object)(object)val2 != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)val2).gameObject); } } SaveablePrefab component = val.GetComponent<SaveablePrefab>(); ShipItem component2 = val.GetComponent<ShipItem>(); component.prefabIndex = prefabIndex; component2.name = "sail package"; ((GoPointerButton)component2).lookText = "sail package"; component2.mass = 0.1f; ((PickupableItem)component2).big = true; ((PickupableItem)component2).holdDistance = 1.25f; ((PickupableItem)component2).holdHeight = 0f; ((PickupableItem)component2).heldRotationOffset = 180f; component2.wallAttachment = false; component2.sold = true; component2.value = 0; val.tag = "Untagged"; Good component3 = val.GetComponent<Good>(); if ((Object)(object)component3 != (Object)null) { Object.DestroyImmediate((Object)(object)component3); } if ((Object)(object)component2.itemRigidbodyC != (Object)null) { Object.DestroyImmediate((Object)(object)((Component)component2.itemRigidbodyC).gameObject); component2.itemRigidbodyC = null; ((PickupableItem)component2).colChecker = null; } LODGroup[] components = val.GetComponents<LODGroup>(); for (int i = 0; i < components.Length; i++) { Object.DestroyImmediate((Object)(object)components[i]); } val.AddComponent<SailpackData>(); Object.DontDestroyOnLoad((Object)(object)val); return val; } } internal sealed class SailpackData : MonoBehaviour { private SailpackRecord record; private Mast runtimeTarget; internal bool HasRecord => record != null; internal SailpackRecord Record => record; internal Mast RuntimeTarget => runtimeTarget; internal bool SuppressBigItemDecollision { get; set; } internal void Initialize(SailpackRecord newRecord, Mast newRuntimeTarget = null) { record = newRecord?.Copy(); record?.Normalize(); runtimeTarget = newRuntimeTarget; ApplyRecord(); } internal void BindTarget(Mast target) { runtimeTarget = target; } private IEnumerator Start() { if (record == null) { while ((Object)(object)SaveLoadManager.instance != (Object)null && !SaveLoadManager.instance.loaded) { yield return (object)new WaitForEndOfFrame(); } TryHydrate(); } } private void TryHydrate() { SaveablePrefab component = ((Component)this).GetComponent<SaveablePrefab>(); if ((Object)(object)component == (Object)null || component.instanceId <= 0) { return; } if (SailpackPersistence.TryGet(component.instanceId, out var sailpackRecord)) { record = sailpackRecord.Copy(); record.Normalize(); ApplyRecord(); return; } SaveLoadManager instance = SaveLoadManager.instance; if (instance != null && instance.loaded) { ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogWarning((object)$"Sail package {component.instanceId} has no mod metadata."); } } } private void ApplyRecord() { if (record == null) { return; } MigrateLegacyPrefabIndex(); string displayName = record.GetDisplayName(); ((Object)((Component)this).gameObject).name = displayName; ShipItem component = ((Component)this).GetComponent<ShipItem>(); if ((Object)(object)component != (Object)null) { component.name = displayName; ((GoPointerButton)component).lookText = displayName; component.mass = record.packageMass; if ((Object)(object)component.itemRigidbodyC != (Object)null && (Object)(object)component.itemRigidbodyC.GetBody() != (Object)null) { component.itemRigidbodyC.UpdateMass(); component.itemRigidbodyC.GetBody().angularDrag = component.mass * 0.1f; } } } private void MigrateLegacyPrefabIndex() { SaveablePrefab component = ((Component)this).GetComponent<SaveablePrefab>(); if (!((Object)(object)component == (Object)null) && component.prefabIndex == 390) { component.prefabIndex = 600; ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogInfo((object)($"Migrated sail package {component.instanceId} from prefab index " + $"{390} to " + $"{600}.")); } } } internal GameObject GetSailPrefab() { if (record == null || (Object)(object)PrefabsDirectory.instance == (Object)null || PrefabsDirectory.instance.sails == null || record.sailPrefabIndex < 0 || record.sailPrefabIndex >= PrefabsDirectory.instance.sails.Length) { return null; } return PrefabsDirectory.instance.sails[record.sailPrefabIndex]; } } internal static class SailpackPersistence { private static readonly Dictionary<int, SailpackRecord> Records = new Dictionary<int, SailpackRecord>(); internal static bool TryGet(int instanceId, out SailpackRecord record) { return Records.TryGetValue(instanceId, out record); } internal static void Register(int instanceId, SailpackRecord record) { if (instanceId > 0 && record != null) { record.Normalize(); Records[instanceId] = record.Copy(); } } internal static void Remove(int instanceId) { if (instanceId > 0) { Records.Remove(instanceId); } } internal static void Load() { Records.Clear(); if (GameState.modData == null || !GameState.modData.TryGetValue("DogEggz", out var value) || string.IsNullOrEmpty(value)) { return; } try { foreach (KeyValuePair<int, SailpackRecord> item in SailpackPersistenceCodec.Decode(value)) { Records[item.Key] = item.Value.Copy(); } ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogInfo((object)$"Loaded {Records.Count} sail package record(s)."); } } catch (Exception arg) { ManualLogSource log2 = SwappableStaysailPlugin.Log; if (log2 != null) { log2.LogError((object)$"Could not load sail package data: {arg}"); } } } internal static void Dispose() { Records.Clear(); } internal static void Save() { try { ReconcileLiveSailpacks(); Dictionary<int, SailpackRecord> dictionary = Records.ToDictionary((KeyValuePair<int, SailpackRecord> pair) => pair.Key, (KeyValuePair<int, SailpackRecord> pair) => pair.Value.Copy()); string text = SailpackPersistenceCodec.Encode(dictionary); Dictionary<int, SailpackRecord> dictionary2 = SailpackPersistenceCodec.Decode(text); if (dictionary2.Count != dictionary.Count || !dictionary2.Keys.OrderBy((int id) => id).SequenceEqual(dictionary.Keys.OrderBy((int id) => id))) { throw new InvalidDataException("Sail package metadata failed its save-time verification."); } if (GameState.modData == null) { GameState.modData = new Dictionary<string, string>(); } GameState.modData["DogEggz"] = text; ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogInfo((object)$"Saved {dictionary.Count} sail package record(s)."); } } catch (Exception arg) { ManualLogSource log2 = SwappableStaysailPlugin.Log; if (log2 != null) { log2.LogError((object)$"Could not save sail package data: {arg}"); } } } private static void ReconcileLiveSailpacks() { SailpackData[] array = Resources.FindObjectsOfTypeAll<SailpackData>(); HashSet<int> liveIds = new HashSet<int>(); SailpackData[] array2 = array; foreach (SailpackData sailpackData in array2) { SaveablePrefab component = ((Component)sailpackData).GetComponent<SaveablePrefab>(); if (!((Object)(object)component == (Object)null) && component.instanceId > 0 && sailpackData.HasRecord) { liveIds.Add(component.instanceId); Records[component.instanceId] = sailpackData.Record.Copy(); } } SaveLoadManager instance = SaveLoadManager.instance; if ((Object)(object)instance == (Object)null) { return; } List<SaveablePrefab> currentPrefabs = instance.GetCurrentPrefabs(); if (currentPrefabs != null) { foreach (SaveablePrefab item in currentPrefabs) { if ((Object)(object)item != (Object)null && item.instanceId > 0) { liveIds.Add(item.instanceId); } } } SaveableObject[] array3 = Resources.FindObjectsOfTypeAll<SaveableObject>(); foreach (SaveableObject val in array3) { if ((Object)(object)val?.localItems == (Object)null) { continue; } List<SavePrefabData> cachedItems = val.localItems.GetCachedItems(); if (cachedItems == null) { continue; } foreach (SavePrefabData item2 in cachedItems) { if (item2 != null && item2.instanceId > 0) { liveIds.Add(item2.instanceId); } } } foreach (int item3 in Records.Keys.Where((int id) => !liveIds.Contains(id)).ToList()) { Records.Remove(item3); } } } internal static class SailpackPersistenceCodec { private const string Prefix = "SSP3:"; private const int FormatVersion = 3; private const int MaximumRecordCount = 10000; private const int MaximumEncodedLength = 16777216; private static bool IsCurrentFormat(string payload) { return payload?.StartsWith("SSP3:", StringComparison.Ordinal) ?? false; } internal static string Encode(IEnumerable<KeyValuePair<int, SailpackRecord>> records) { List<KeyValuePair<int, SailpackRecord>> list = (from pair in records ?? Enumerable.Empty<KeyValuePair<int, SailpackRecord>>() where pair.Key > 0 && pair.Value != null orderby pair.Key select pair).ToList(); if (list.Count > 10000) { throw new InvalidDataException($"Sail package record count {list.Count} exceeds " + $"the supported maximum of {10000}."); } using MemoryStream memoryStream = new MemoryStream(); using (BinaryWriter binaryWriter = new BinaryWriter(memoryStream, Encoding.UTF8, leaveOpen: true)) { binaryWriter.Write(3); binaryWriter.Write(list.Count); foreach (KeyValuePair<int, SailpackRecord> item in list) { WriteRecord(binaryWriter, item.Key, item.Value); } } return "SSP3:" + Convert.ToBase64String(memoryStream.ToArray()); } internal static Dictionary<int, SailpackRecord> Decode(string payload) { if (!IsCurrentFormat(payload)) { throw new InvalidDataException("Sail package metadata does not use the current format."); } if (payload.Length > 16777216) { throw new InvalidDataException("Sail package metadata exceeds the supported size."); } byte[] buffer; try { buffer = Convert.FromBase64String(payload.Substring("SSP3:".Length)); } catch (FormatException innerException) { throw new InvalidDataException("Sail package metadata contains invalid base64 data.", innerException); } using MemoryStream memoryStream = new MemoryStream(buffer, writable: false); using BinaryReader binaryReader = new BinaryReader(memoryStream, Encoding.UTF8, leaveOpen: true); int num = binaryReader.ReadInt32(); if (num != 3) { throw new InvalidDataException($"Unsupported sail package metadata version {num}."); } int num2 = binaryReader.ReadInt32(); if (num2 < 0 || num2 > 10000) { throw new InvalidDataException($"Invalid sail package record count {num2}."); } Dictionary<int, SailpackRecord> dictionary = new Dictionary<int, SailpackRecord>(num2); for (int i = 0; i < num2; i++) { int num3 = binaryReader.ReadInt32(); if (num3 <= 0 || dictionary.ContainsKey(num3)) { throw new InvalidDataException($"Invalid or duplicate sail package ID {num3}."); } SailpackRecord sailpackRecord = ReadRecord(binaryReader); sailpackRecord.Normalize(); dictionary.Add(num3, sailpackRecord); } if (memoryStream.Position != memoryStream.Length) { throw new InvalidDataException("Sail package metadata contains unexpected trailing data."); } return dictionary; } private static void WriteRecord(BinaryWriter writer, int instanceId, SailpackRecord record) { record.Normalize(); writer.Write(instanceId); writer.Write(record.sailPrefabIndex); writer.Write(record.sailColor); writer.Write(record.scaleY); writer.Write(record.scaleZ); writer.Write(record.packageMass); writer.Write(record.installHeight); writer.Write(record.minAngle); writer.Write(record.maxAngle); writer.Write(record.targetBoatSceneIndex); writer.Write(record.targetMastOrderIndex); writer.Write(record.targetStayName ?? string.Empty); writer.Write(record.sailName ?? string.Empty); } private static SailpackRecord ReadRecord(BinaryReader reader) { return new SailpackRecord { sailPrefabIndex = reader.ReadInt32(), sailColor = reader.ReadInt32(), scaleY = reader.ReadSingle(), scaleZ = reader.ReadSingle(), packageMass = reader.ReadSingle(), installHeight = reader.ReadSingle(), minAngle = reader.ReadSingle(), maxAngle = reader.ReadSingle(), targetBoatSceneIndex = reader.ReadInt32(), targetMastOrderIndex = reader.ReadInt32(), targetStayName = reader.ReadString(), sailName = reader.ReadString() }; } } internal sealed class SailpackRecord { public int sailPrefabIndex; public int sailColor; public float scaleY = 1f; public float scaleZ = 1f; public float packageMass = 0.1f; public float installHeight; public float minAngle; public float maxAngle; public int targetBoatSceneIndex; public int targetMastOrderIndex; public string targetStayName; public string sailName; internal static SailpackRecord FromSail(Sail sail, Mast mast) { return new SailpackRecord { sailPrefabIndex = sail.prefabIndex, sailColor = sail.activeColor, scaleY = sail.GetScaleY(), scaleZ = sail.GetScaleZ(), packageMass = SailpackWeight.FromSail(sail), installHeight = sail.GetCurrentInstallHeight(), minAngle = sail.minAngle, maxAngle = sail.maxAngle, targetBoatSceneIndex = StaysailTargets.GetBoatSceneIndex(mast), targetMastOrderIndex = mast.orderIndex, targetStayName = StaysailTargets.GetStayName(mast), sailName = (string.IsNullOrEmpty(sail.sailName) ? ((Object)((Component)sail).gameObject).name : sail.sailName) }; } internal string GetDisplayName() { string text = (string.IsNullOrEmpty(targetStayName) ? "Staysail stay" : targetStayName); string text2 = (string.IsNullOrEmpty(sailName) ? "Staysail" : sailName); if (HasScalePercentageSuffix(text2)) { return text + " " + text2; } int num = Mathf.RoundToInt(scaleY * 100f); int num2 = Mathf.RoundToInt(scaleZ * 100f); return text + " " + text2 + " " + $"({num}%x{num2}%)"; } private static bool HasScalePercentageSuffix(string value) { if (string.IsNullOrEmpty(value) || value[value.Length - 1] != ')') { return false; } int num = value.LastIndexOf(" (", StringComparison.Ordinal); if (num < 0) { return false; } string text = value.Substring(num + 2, value.Length - num - 3); int num2 = text.IndexOf('x'); if (num2 < 0) { return IsWholePercentage(text); } if (text.IndexOf('x', num2 + 1) >= 0) { return false; } if (IsWholePercentage(text.Substring(0, num2))) { return IsWholePercentage(text.Substring(num2 + 1)); } return false; } private static bool IsWholePercentage(string value) { if (string.IsNullOrEmpty(value) || value[value.Length - 1] != '%') { return false; } if (int.TryParse(value.Substring(0, value.Length - 1), out var result)) { return result >= 0; } return false; } internal SailpackRecord Copy() { return (SailpackRecord)MemberwiseClone(); } internal void Normalize() { if (float.IsNaN(packageMass) || float.IsInfinity(packageMass) || packageMass <= 0f) { packageMass = 0.1f; } } } internal static class SailpackWeight { internal const float MinimumPackageMass = 0.1f; internal static float FromSail(Sail sail) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 if ((Object)(object)sail == (Object)null) { return 0.1f; } float num = Mathf.Max(0f, sail.GetRealSailPower() * 20f); float num2 = (((int)sail.category == 2 || (int)sail.category == 3) ? num : (((int)sail.category != 5) ? (num * 0.5f) : 0f)); return Mathf.Max(0.1f, num + num2); } } internal static class StaysailInstaller { internal static bool TryInstall(SailpackData data, Mast mast, out string error) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 //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) error = null; SailpackRecord sailpackRecord = data?.Record; GameObject val = data?.GetSailPrefab(); Sail val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Sail>() : null); if (!StaysailTargets.IsEligible(mast)) { error = "That stay cannot accept swappable staysails."; return false; } if (sailpackRecord == null || (Object)(object)val2 == (Object)null || (int)val2.category != 5) { error = "The sail model in this sail package is unavailable."; return false; } if (mast.sails == null || mast.sails.Count >= mast.maxSails) { error = "That stay has no free sail slot."; return false; } GameObject val3 = null; bool flag = false; try { val3 = Object.Instantiate<GameObject>(val, ((Component)mast).transform.position, ((Component)mast).transform.rotation); Sail component = val3.GetComponent<Sail>(); ((Behaviour)component).enabled = false; Rigidbody component2 = val3.GetComponent<Rigidbody>(); if ((Object)(object)component2 != (Object)null) { component2.isKinematic = true; } val3.transform.SetParent(((Component)mast).transform, true); component.ChangeInstallHeight(sailpackRecord.installHeight); component.LoadScale(sailpackRecord.scaleY, sailpackRecord.scaleZ); component.ChangeSailColor(sailpackRecord.sailColor); SailConnections component3 = val3.GetComponent<SailConnections>(); if ((Object)(object)component3 == (Object)null || (Object)(object)component3.colChecker == (Object)null) { throw new InvalidOperationException("Sail connection data is missing."); } if (!HasControllerCapacity(mast, mast.sails.Count, component3)) { error = "That stay has no free winch connection."; Object.Destroy((Object)(object)val3); return false; } component3.colChecker.colAngleMin = sailpackRecord.minAngle; component3.colChecker.colAngleMax = sailpackRecord.maxAngle; component.UpdateInstallPosition(); float num = component.GetCurrentInstallHeight() - component.GetScaledHeight(); if (component.GetScaledHeight() > mast.mastHeight || num < -0.1f) { error = "The packed sail does not fit this stay."; Object.Destroy((Object)(object)val3); return false; } foreach (GameObject sail in mast.sails) { Sail val4 = (((Object)(object)sail != (Object)null) ? sail.GetComponent<Sail>() : null); if (!((Object)(object)val4 == (Object)null)) { float num2 = val4.GetCurrentInstallHeight() - val4.GetScaledHeight(); if (Mathf.Max(num, num2) <= Mathf.Min(component.GetCurrentInstallHeight(), val4.GetCurrentInstallHeight())) { error = "The packed sail overlaps another sail."; Object.Destroy((Object)(object)val3); return false; } } } mast.AttachSailToMast(val3); if (!component.IsInstalled() || mast.sails == null || !mast.sails.Contains(val3)) { throw new InvalidOperationException("The stay did not retain the installed sail."); } flag = true; try { StaysailTargets.EnsureRemovalButton(mast, val3); } catch (Exception arg) { ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogWarning((object)("Staysail installed, but its removal target could not be " + $"created immediately: {arg}")); } } ConsumePackage(data); try { StaysailInteractionVisuals.ResetAfterInteraction(); } catch (Exception ex) { ManualLogSource log2 = SwappableStaysailPlugin.Log; if (log2 != null) { log2.LogWarning((object)("Installed staysail interaction cleanup failed: " + ex)); } } ManualLogSource log3 = SwappableStaysailPlugin.Log; if (log3 != null) { log3.LogInfo((object)("Installed " + sailpackRecord.GetDisplayName() + " on " + $"boat {sailpackRecord.targetBoatSceneIndex}, stay " + $"{sailpackRecord.targetMastOrderIndex}.")); } return true; } catch (Exception arg2) { if (!flag && (Object)(object)val3 != (Object)null) { Sail component4 = val3.GetComponent<Sail>(); if (mast?.sails != null && mast.sails.Contains(val3) && (Object)(object)component4 != (Object)null) { StaysailDetacher.TryDetach(mast, component4, out var error2); if (!string.IsNullOrEmpty(error2)) { ManualLogSource log4 = SwappableStaysailPlugin.Log; if (log4 != null) { log4.LogWarning((object)("Partial install rollback: " + error2)); } } } else { Object.Destroy((Object)(object)val3); } } error = "Could not install the packed sail."; ManualLogSource log5 = SwappableStaysailPlugin.Log; if (log5 != null) { log5.LogError((object)$"Sail package installation failed: {arg2}"); } return false; } } private static void ConsumePackage(SailpackData data) { if ((Object)(object)data == (Object)null) { return; } try { ConsumePackageCore(data); } catch (Exception arg) { ShipItem component = ((Component)data).GetComponent<ShipItem>(); GoPointer val = (((Object)(object)component != (Object)null) ? ((PickupableItem)component).held : null); try { if (val != null) { val.DropItem(); } } catch (Exception ex) { if ((Object)(object)component != (Object)null) { ((PickupableItem)component).held = null; } ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogWarning((object)("Fallback pointer cleanup failed after install: " + ex)); } } SailpackFactory.DiscardStaged(data, unregisterFromSave: true); ManualLogSource log2 = SwappableStaysailPlugin.Log; if (log2 != null) { log2.LogWarning((object)("Used fallback sail-package cleanup after a committed " + $"install: {arg}")); } } } private static void ConsumePackageCore(SailpackData data) { ShipItem component = ((Component)data).GetComponent<ShipItem>(); SaveablePrefab component2 = ((Component)data).GetComponent<SaveablePrefab>(); GoPointer val = (((Object)(object)component != (Object)null) ? ((PickupableItem)component).held : null); if ((Object)(object)component2 != (Object)null && component2.instanceId > 0) { SailpackPersistence.Remove(component2.instanceId); } if ((Object)(object)val != (Object)null) { try { ((PickupableItem)component).OnDrop(); } catch (Exception ex) { ManualLogSource log = SwappableStaysailPlugin.Log; if (log != null) { log.LogWarning((object)("Sail package drop callback failed during install: " + ex)); } } try { val.DropItem(); } catch (Exception ex2) { ((PickupableItem)component).held = null; ManualLogSource log2 = SwappableStaysailPlugin.Log; if (log2 != null) { log2.LogWarning((object)("Sail package pointer release failed during install: " + ex2)); } } } ((Component)data).gameObject.SetActive(false); try { if ((Object)(object)component != (Object)null) { component.DestroyItem(); return; } if (component2 != null) { component2.Unregister(); } Object.Destroy((Object)(object)((Component)data).gameObject); } catch (Exception ex3) { try { if (component2 != null) { component2.Unregister(); } } catch (Exception ex4) { ManualLogSource log3 = SwappableStaysailPlugin.Log; if (log3 != null) { log3.LogWarning((object)("Sail package save cleanup failed after install: " + ex4)); } } Object.Destroy((Object)(object)((Component)data).gameObject); ManualLogSource log4 = SwappableStaysailPlugin.Log; if (log4 != null) { log4.LogWarning((object)("Sail package used fallback destruction after install: " + ex3)); } } } private static bool HasControllerCapacity(Mast mast, int index, SailConnections connections) { if ((Object)(object)connections.reefController != (Object)null && (mast.reefWinch == null || index >= mast.reefWinch.Length)) { return false; } if ((Object)(object)connections.angleControllerMid != (Object)null && (mast.midAngleWinch == null || index >= mast.midAngleWinch.Length)) { return false; } if (((Object)(object)connections.angleControllerLeft != (Object)null || (Object)(object)connections.angleControllerRight != (Object)null) && (mast.leftAngleWinch == null || index >= mast.leftAngleWinch.Length || mast.rightAngleWinch == null || index >= mast.rightAngleWinch.Length)) { return false; } if ((Object)(object)connections.mastReefAttachment != (Object)null && (mast.mastReefAtt == null || index >= mast.mastReefAtt.Length)) { return false; } return true; } } internal sealed class StayInstallTargetButton : GoPointerButton { private Mast target; private SailpackData data; private Collider interactionCollider; private string interactionLabel; internal bool IsAvailable { get { if (!base.unclickable && (Object)(object)data != (Object)null && data.HasRecord && (Object)(object)interactionCollider != (Object)null) { return interactionCollider.enabled; } return false; } } internal void Initialize(Mast newTarget, Collider collider) { target = newTarget; interactionCollider = collider; } internal void Configure(Mast newTarget, SailpackData newData, string activateLabel) { target = newTarget; data = newData; interactionLabel = activateLabel; base.lookText = (((Object)(object)data != (Object)null && data.HasRecord) ? (interactionLabel + ": install " + data.Record.GetDisplayName()) : (interactionLabel + ": install staysail")); } internal void SetAvailable(bool available) { base.unclickable = !available; if (available && !((Component)this).gameObject.activeSelf) { ((Component)this).gameObject.SetActive(true); } if ((Object)(object)interactionCollider != (Object)null) { interactionCollider.enabled = available; } if (!available && ((Component)this).gameObject.activeSelf) { ((Component)this).gameObject.SetActive(false); } } internal void ResetInteractionVisuals() { SetAvailable(available: false); ((GoPointerButton)this).ForceUnlook(); ((GoPointerButton)this).Unclick(); base.overrideEnableOutline = false; Outline[] components = ((Component)this).GetComponents<Outline>(); for (int i = 0; i < components.Length; i++) { ((Behaviour)components[i]).enabled = false; } } internal void DisposeInteractionVolume() { if ((Object)(object)interactionCollider != (Object)null) { Object.Destroy((Object)(object)((Component)interactionCollider).gameObject); interactionCollider = null; } } internal void TryInstall(PickupableItem heldItem) { SailpackData sailpackData = (((Object)(object)heldItem != (Object)null) ? ((Component)heldItem).GetComponent<SailpackData>() : null); if (!((Object)(object)sailpackData == (Object)null) && !((Object)(object)sailpackData != (Object)(object)data) && !((Object)(object)target == (Object)null) && !StaysailInstaller.TryInstall(sailpackData, target, out var error)) { NotificationUi instance = NotificationUi.instance; if (instance != null) { instance.ShowNotification(error); } } } public override bool OnItemClick(PickupableItem heldItem) { return false; } public override void OnAltActivate(GoPointer activatingPointer) { if ((Object)(object)activatingPointer != (Object)null && IsAvailable) { TryInstall(activatingPointer.GetHeldItem()); } } } internal sealed class StayInstallTargetProxy : MonoBehaviour { } internal static class StayInstallTargeting { private static readonly Dictionary<Mast, List<StayInstallTargetButton>> Buttons = new Dictionary<Mast, List<StayInstallTargetButton>>(); private static SailpackData cachedData; private static Mast cachedTarget; private static List<StayInstallTargetButton> availableButtons; private static GoPointer activePointer; private static SailpackData activeData; private static Mast activeTarget; private static string activeLabel; internal static void PrepareRaycast(GoPointer pointer) { if ((Object)(object)pointer == (Object)null || GameState.sleeping || Object.op_Implicit((Object)(object)GameState.inBed) || BoatCamera.on || (Object)(object)GameState.currentShipyard != (Object)null) { Deactivate(pointer); return; } PickupableItem heldItem = pointer.GetHeldItem(); SailpackData sailpackData = (((Object)(object)heldItem != (Object)null) ? ((Component)heldItem).GetComponent<SailpackData>() : null); if ((Object)(object)sailpackData == (Object)null) { Deactivate(pointer); return; } Mast val = ResolveTarget(sailpackData); if ((Object)(object)val == (Object)null) { Deactivate(pointer); return; } List<StayInstallTargetButton> orCreateButtons = GetOrCreateButtons(val); if (orCreateButtons == null || orCreateButtons.Count == 0) { Deactivate(pointer); return; } string text = StaysailInput.ActivateLabel(pointer); if (!((Object)(object)activePointer != (Object)(object)pointer) && availableButtons == orCreateButtons && !((Object)(object)activeData != (Object)(object)sailpackData) && !((Object)(object)activeTarget != (Object)(object)val) && !(activeLabel != text)) { return; } if (availableButtons != orCreateButtons || (Object)(object)activePointer != (Object)(object)pointer) { DisableAvailableButtons(); availableButtons = orCreateButtons; } activePointer = pointer; activeData = sailpackData; activeTarget = val; activeLabel = text; foreach (StayInstallTargetBu