Decompiled source of SwappableStaysail v1.1.4

BepInEx/plugins/SwappableStaysail/SwappableStaysail.dll

Decompiled 9 hours ago
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