Decompiled source of SwappableStaysail v1.2.1

BepInEx/plugins/SwappableStaysail/SwappableStaysail.dll

Decompiled 2 weeks 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.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1")]
[assembly: AssemblyProduct("SwappableStaysail")]
[assembly: AssemblyTitle("SwappableStaysail")]
[assembly: AssemblyVersion("1.2.1.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
{
	[BepInPlugin("DogEggz", "Swappable Staysail", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class SwappableStaysailPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "DogEggz";

		public const string PluginName = "Swappable Staysail";

		public const string PluginVersion = "1.2.1";

		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");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Swappable Staysail 1.2.1 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;
		}
	}
	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(GoPointer), "DoRaycast")]
	internal static class StayInstallRaycastPatch
	{
		[HarmonyPrefix]
		private static void Prefix(GoPointer __instance)
		{
			StayInstallTargeting.PrepareRaycast(__instance);
		}
	}
	[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(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);
		}
	}
	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 (StayInstallTargetButton availableButton in availableButtons)
			{
				if ((Object)(object)availableButton != (Object)null)
				{
					availableButton.Configure(val, sailpackData, text);
					availableButton.SetAvailable(available: true);
				}
			}
		}

		private static Mast ResolveTarget(SailpackData data)
		{
			if ((Object)(object)data == (Object)null || data.Record == null)
			{
				cachedData = null;
				cachedTarget = null;
				return null;
			}
			if ((Object)(object)cachedData != (Object)(object)data || (Object)(object)cachedTarget == (Object)null || !((Component)cachedTarget).gameObject.activeInHierarchy)
			{
				bool num = (Object)(object)cachedData != (Object)(object)data;
				cachedData = data;
				cachedTarget = StaysailTargets.Resolve(data);
				if (num && (Object)(object)cachedTarget == (Object)null)
				{
					ManualLogSource log = SwappableStaysailPlugin.Log;
					if (log != null)
					{
						log.LogWarning((object)("Could not resolve target stay for " + data.Record.GetDisplayName() + " (boat " + $"{data.Record.targetBoatSceneIndex}, mast " + $"{data.Record.targetMastOrderIndex})."));
					}
				}
			}
			return cachedTarget;
		}

		internal static List<StayInstallTargetButton> GetOrCreateButtons(Mast mast)
		{
			PruneButtons();
			if ((Object)(object)mast == (Object)null)
			{
				return null;
			}
			if (Buttons.TryGetValue(mast, out var value) && value != null && value.Count > 0)
			{
				return value;
			}
			List<Collider> list = CreateInteractionColliders(mast);
			if (list.Count == 0)
			{
				return null;
			}
			List<StayInstallTargetButton> list2 = new List<StayInstallTargetButton>(list.Count);
			foreach (Collider item in list)
			{
				if (!((Object)(object)item == (Object)null))
				{
					((Renderer)((Component)item).gameObject.AddComponent<MeshRenderer>()).enabled = false;
					StayInstallTargetButton stayInstallTargetButton = ((Component)item).gameObject.AddComponent<StayInstallTargetButton>();
					stayInstallTargetButton.Initialize(mast, item);
					stayInstallTargetButton.Configure(mast, null, StaysailInput.DefaultActivateLabel);
					stayInstallTargetButton.SetAvailable(available: false);
					list2.Add(stayInstallTargetButton);
				}
			}
			Buttons[mast] = list2;
			return list2;
		}

		private static List<Collider> CreateInteractionColliders(Mast mast)
		{
			List<Collider> list = new List<Collider>();
			HashSet<GameObject> hashSet = new HashSet<GameObject>();
			Collider[] componentsInChildren = ((Component)mast).GetComponentsInChildren<Collider>(true);
			foreach (Collider val in componentsInChildren)
			{
				if (IsInteractionSource((Component)(object)val))
				{
					Collider val2 = CopyCollider(val, mast);
					if ((Object)(object)val2 != (Object)null)
					{
						list.Add(val2);
						hashSet.Add(((Component)val).gameObject);
					}
				}
			}
			Renderer[] componentsInChildren2 = ((Component)mast).GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val3 in componentsInChildren2)
			{
				if (IsInteractionSource((Component)(object)val3) && !hashSet.Contains(((Component)val3).gameObject))
				{
					Collider val4 = CreateRendererCollider(val3, mast);
					if ((Object)(object)val4 != (Object)null)
					{
						list.Add(val4);
					}
				}
			}
			return list;
		}

		private static bool IsInteractionSource(Component component)
		{
			if ((Object)(object)component != (Object)null && (Object)(object)component.GetComponentInParent<Sail>() == (Object)null && (Object)(object)component.GetComponentInParent<StayInstallTargetProxy>() == (Object)null)
			{
				return (Object)(object)component.GetComponentInParent<PickupableItem>() == (Object)null;
			}
			return false;
		}

		private static Collider CopyCollider(Collider source, Mast mast)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateVolume(((Component)source).transform);
			Collider val2 = null;
			CapsuleCollider val3 = (CapsuleCollider)(object)((source is CapsuleCollider) ? source : null);
			if (val3 != null)
			{
				CapsuleCollider obj = val.AddComponent<CapsuleCollider>();
				obj.center = val3.center;
				obj.direction = val3.direction;
				obj.height = val3.height + 0.16f;
				obj.radius = val3.radius + 0.08f;
				val2 = (Collider)(object)obj;
			}
			else
			{
				BoxCollider val4 = (BoxCollider)(object)((source is BoxCollider) ? source : null);
				if (val4 != null)
				{
					BoxCollider obj2 = val.AddComponent<BoxCollider>();
					obj2.center = val4.center;
					obj2.size = val4.size + Vector3.one * 0.16f;
					val2 = (Collider)(object)obj2;
				}
				else
				{
					SphereCollider val5 = (SphereCollider)(object)((source is SphereCollider) ? source : null);
					if (val5 != null)
					{
						SphereCollider obj3 = val.AddComponent<SphereCollider>();
						obj3.center = val5.center;
						obj3.radius = val5.radius + 0.08f;
						val2 = (Collider)(object)obj3;
					}
					else
					{
						MeshCollider val6 = (MeshCollider)(object)((source is MeshCollider) ? source : null);
						if (val6 == null || !((Object)(object)val6.sharedMesh != (Object)null))
						{
							Object.Destroy((Object)(object)val);
							return CreateWorldBoundsCollider(source.bounds, mast);
						}
						BoxCollider obj4 = val.AddComponent<BoxCollider>();
						Bounds bounds = val6.sharedMesh.bounds;
						obj4.center = ((Bounds)(ref bounds)).center;
						bounds = val6.sharedMesh.bounds;
						obj4.size = ((Bounds)(ref bounds)).size + Vector3.one * 0.16f;
						val2 = (Collider)(object)obj4;
					}
				}
			}
			PrepareInteractionCollider(val2);
			return val2;
		}

		private static Collider CreateRendererCollider(Renderer source, Mast mast)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			MeshFilter component = ((Component)source).GetComponent<MeshFilter>();
			Bounds val;
			if ((Object)(object)component != (Object)null && (Object)(object)component.sharedMesh != (Object)null)
			{
				val = component.sharedMesh.bounds;
			}
			else
			{
				SkinnedMeshRenderer val2 = (SkinnedMeshRenderer)(object)((source is SkinnedMeshRenderer) ? source : null);
				if (val2 == null)
				{
					return CreateWorldBoundsCollider(source.bounds, mast);
				}
				val = val2.localBounds;
			}
			BoxCollider obj = CreateVolume(((Component)source).transform).AddComponent<BoxCollider>();
			obj.center = ((Bounds)(ref val)).center;
			obj.size = ((Bounds)(ref val)).size + Vector3.one * 0.16f;
			PrepareInteractionCollider((Collider)(object)obj);
			return (Collider)(object)obj;
		}

		private static Collider CreateWorldBoundsCollider(Bounds bounds, Mast mast)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			Vector3 size = ((Bounds)(ref bounds)).size;
			if (((Vector3)(ref size)).sqrMagnitude <= 0.0001f)
			{
				return null;
			}
			GameObject obj = CreateVolume(((Component)mast).transform);
			obj.transform.position = ((Bounds)(ref bounds)).center;
			obj.transform.rotation = Quaternion.identity;
			Vector3 lossyScale = obj.transform.lossyScale;
			BoxCollider obj2 = obj.AddComponent<BoxCollider>();
			obj2.center = Vector3.zero;
			obj2.size = new Vector3(((Bounds)(ref bounds)).size.x / Mathf.Max(Mathf.Abs(lossyScale.x), 0.0001f), ((Bounds)(ref bounds)).size.y / Mathf.Max(Mathf.Abs(lossyScale.y), 0.0001f), ((Bounds)(ref bounds)).size.z / Mathf.Max(Mathf.Abs(lossyScale.z), 0.0001f)) + Vector3.one * 0.16f;
			PrepareInteractionCollider((Collider)(object)obj2);
			return (Collider)(object)obj2;
		}

		private static GameObject CreateVolume(Transform sourceTransform)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			GameObject val = new GameObject("Swappable Staysail install target volume")
			{
				layer = 0
			};
			val.transform.SetParent(sourceTransform, false);
			val.transform.localPosition = Vector3.zero;
			val.transform.localRotation = Quaternion.identity;
			val.transform.localScale = Vector3.one;
			val.AddComponent<StayInstallTargetProxy>();
			return val;
		}

		private static void PrepareInteractionCollider(Collider collider)
		{
			collider.isTrigger = true;
			collider.enabled = false;
		}

		internal static void Deactivate()
		{
			DisableAvailableButtons();
			cachedData = null;
			cachedTarget = null;
			activePointer = null;
			activeData = null;
			activeTarget = null;
			activeLabel = null;
		}

		private static void Deactivate(GoPointer pointer)
		{
			if ((Object)(object)pointer == (Object)null || (Object)(object)pointer == (Object)(object)activePointer)
			{
				Deactivate();
			}
		}

		private static void DisableAvailableButtons()
		{
			if (availableButtons == null)
			{
				return;
			}
			foreach (StayInstallTargetButton availableButton in availableButtons)
			{
				availableButton?.ResetInteractionVisuals();
			}
			availableButtons = null;
		}

		private static void PruneButtons()
		{
			List<Mast> list = new List<Mast>();
			foreach (KeyValuePair<Mast, List<StayInstallTargetButton>> button in Buttons)
			{
				if ((Object)(object)button.Key == (Object)null || button.Value == null)
				{
					list.Add(button.Key);
					continue;
				}
				button.Value.RemoveAll((StayInstallTargetButton button) => (Object)(object)button == (Object)null);
				if (button.Value.Count == 0)
				{
					list.Add(button.Key);
				}
			}
			foreach (Mast item in list)
			{
				Buttons.Remove(item);
			}
		}

		internal static void Dispose()
		{
			Deactivate();
			foreach (List<StayInstallTargetButton> value in Buttons.Values)
			{
				if (value == null)
				{
					continue;
				}
				foreach (StayInstallTargetButton item in value)
				{
					item?.DisposeInteractionVolume();
				}
			}
			Buttons.Clear();
		}
	}
	internal static class StaysailInput
	{
		internal static string DefaultActivateLabel => ((object)GameInput.GetKeyCode((InputName)9, false, false)/*cast due to .constrained prefix*/).ToString();

		internal static bool ActivateDown(GoPointer pointer)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_0017: Invalid comparison between Unknown and I4
			if ((Object)(object)pointer == (Object)null)
			{
				return false;
			}
			PointerType type = pointer.type;
			if ((int)type != 0)
			{
				if ((int)type == 1)
				{
					return OVRInput.GetDown((Button)1, (Controller)int.MinValue);
				}
				if (!GameState.inCursorMenu)
				{
					return GameInput.GetKeyDown((InputName)9);
				}
				return false;
			}
			return OVRInput.GetDown((Button)4, (Controller)int.MinValue);
		}

		internal static string ActivateLabel(GoPointer pointer)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Invalid comparison between Unknown and I4
			if (pointer != null && (int)pointer.type == 0)
			{
				return "X";
			}
			if (pointer != null && (int)pointer.type == 1)
			{
				return "A";
			}
			return DefaultActivateLabel;
		}
	}
	internal static class StaysailDetacher
	{
		internal static bool TryDetach(Mast mast, Sail sail, out string error)
		{
			error = null;
			if ((Object)(object)mast == (Object)null || (Object)(object)sail == (Object)null || mast.sails == null || !mast.sails.Contains(((Component)sail).gameObject))
			{
				error = "That staysail is no longer attached to this stay.";
				return false;
			}
			GameObject gameObject = ((Component)sail).gameObject;
			HashSet<GameObject> hashSet = CollectConnectionObjects(gameObject.GetComponent<SailConnections>());
			gameObject.SetActive(false);
			Exception ex = null;
			try
			{
				mast.DetachSailFromMast(gameObject);
			}
			catch (Exception ex2)
			{
				ex = ex2;
			}
			mast.sails.Remove(gameObject);
			foreach (GameObject item in hashSet)
			{
				if ((Object)(object)item != (Object)null && (Object)(object)item != (Object)(object)gameObject)
				{
					Object.Destroy((Object)(object)item);
				}
			}
			Object.Destroy((Object)(object)gameObject);
			try
			{
				mast.UpdateSailOrder();
				mast.UpdateControllerAttachments();
			}
			catch (Exception ex3)
			{
				ManualLogSource log = SwappableStaysailPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Staysail was detached, but rig refresh failed: " + ex3));
				}
			}
			if (ex != null)
			{
				ManualLogSource log2 = SwappableStaysailPlugin.Log;
				if (log2 != null)
				{
					log2.LogWarning((object)("Vanilla staysail detachment stopped early; completed " + $"cleanup with the safe fallback: {ex}"));
				}
			}
			return !mast.sails.Contains(gameObject);
		}

		private static HashSet<GameObject> CollectConnectionObjects(SailConnections connections)
		{
			HashSet<GameObject> hashSet = new HashSet<GameObject>();
			if ((Object)(object)connections == (Object)null)
			{
				return hashSet;
			}
			Add(hashSet, (Component)(object)connections.colChecker);
			Add(hashSet, (Component)(object)connections.reefController);
			Add(hashSet, (Component)(object)connections.angleControllerMid);
			Add(hashSet, (Component)(object)connections.angleControllerLeft);
			Add(hashSet, (Component)(object)connections.angleControllerRight);
			Add(hashSet, (Component)(object)connections.midRopeAttachment);
			Add(hashSet, (Component)(object)connections.mastReefAttachment);
			Add(hashSet, (Component)(object)connections.mastReefAttExtension);
			return hashSet;
		}

		private static void Add(HashSet<GameObject> objects, Component component)
		{
			if ((Object)(object)component != (Object)null)
			{
				objects.Add(component.gameObject);
			}
		}
	}
	internal sealed class FurledStaysailButton : GoPointerButton
	{
		private static readonly HashSet<FurledStaysailButton> Instances = new HashSet<FurledStaysailButton>();

		private Mast mast;

		private Sail sail;

		private Collider targetCollider;

		private bool removalInProgress;

		internal void Initialize(Mast newMast, Sail newSail)
		{
			Instances.Add(this);
			mast = newMast;
			sail = newSail;
			if ((Object)(object)targetCollider == (Object)null)
			{
				FurledStaysailHitbox component = ((Component)this).GetComponent<FurledStaysailHitbox>();
				targetCollider = (((Object)(object)component != (Object)null) ? component.Collider : null);
			}
		}

		internal void Initialize(Mast newMast, Sail newSail, Collider newTargetCollider)
		{
			targetCollider = newTargetCollider;
			Initialize(newMast, newSail);
		}

		public override void ExtraLateUpdate()
		{
			bool flag = !removalInProgress && CanRemove();
			if ((Object)(object)targetCollider != (Object)null)
			{
				targetCollider.enabled = flag;
			}
			base.lookText = (flag ? (StaysailInput.ActivateLabel(base.pointedAtBy) + ": remove staysail to sail package") : "");
		}

		internal static void ResetAllInteractionVisuals()
		{
			Instances.RemoveWhere((FurledStaysailButton button) => (Object)(object)button == (Object)null);
			foreach (FurledStaysailButton instance in Instances)
			{
				instance.ResetInteractionVisuals();
			}
		}

		internal static void DisposeAll()
		{
			ResetAllInteractionVisuals();
			Instances.Clear();
		}

		internal void ResetInteractionVisuals()
		{
			((GoPointerButton)this).ForceUnlook();
			((GoPointerButton)this).Unclick();
			base.overrideEnableOutline = false;
			if ((Object)(object)targetCollider != (Object)null)
			{
				targetCollider.enabled = false;
			}
			Outline[] components = ((Component)this).GetComponents<Outline>();
			for (int i = 0; i < components.Length; i++)
			{
				((Behaviour)components[i]).enabled = false;
			}
		}

		public override void OnAltActivate(GoPointer activatingPointer)
		{
			//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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (removalInProgress || !CanRemove() || (Object)(object)activatingPointer == (Object)null || (Object)(object)activatingPointer.GetHeldItem() != (Object)null)
			{
				return;
			}
			removalInProgress = true;
			if ((Object)(object)targetCollider != (Object)null)
			{
				targetCollider.enabled = false;
			}
			SailpackRecord sailpackRecord = SailpackRecord.FromSail(sail, mast);
			Vector3 heldPosition = SailpackPickupHandoff.GetHeldPosition(activatingPointer, 1.25f, 0f);
			Quaternion heldRotation = SailpackPickupHandoff.GetHeldRotation(activatingPointer, 180f);
			SailpackData sailpackData = SailpackFactory.CreateStaged(sailpackRecord, heldPosition, heldRotation, registerToSave: true, mast);
			if ((Object)(object)sailpackData == (Object)null)
			{
				removalInProgress = false;
				NotificationUi instance = NotificationUi.instance;
				if (instance != null)
				{
					instance.ShowNotification("Could not create a sail package; the sail was not removed.");
				}
				return;
			}
			ShipItem component = ((Component)sailpackData).GetComponent<ShipItem>();
			if ((Object)(object)component == (Object)null)
			{
				SailpackFactory.DiscardStaged(sailpackData, unregisterFromSave: true);
				removalInProgress = false;
				NotificationUi instance2 = NotificationUi.instance;
				if (instance2 != null)
				{
					instance2.ShowNotification("Could not create a usable sail package; the sail was not removed.");
				}
				return;
			}
			if (!StaysailDetacher.TryDetach(mast, sail, out var error))
			{
				SailpackFactory.DiscardStaged(sailpackData, unregisterFromSave: true);
				removalInProgress = false;
				NotificationUi instance3 = NotificationUi.instance;
				if (instance3 != null)
				{
					instance3.ShowNotification(error);
				}
				return;
			}
			SailpackFactory.ActivateStaged(sailpackData);
			StaysailInteractionVisuals.ResetAfterInteraction();
			SailpackPickupHandoff.Schedule(activatingPointer, component, sailpackRecord);
			ManualLogSource log = SwappableStaysailPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)("Removed " + sailpackRecord.GetDisplayName() + " into a saved sail package; player handoff scheduled."));
			}
		}

		private bool CanRemove()
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Invalid comparison between Unknown and I4
			if ((Object)(object)mast != (Object)null && (Object)(object)sail != (Object)null && sail.IsInstalled() && (Object)(object)GameState.currentShipyard == (Object)null && !GameState.sleeping && !Object.op_Implicit((Object)(object)GameState.inBed) && !BoatCamera.on && StaysailTargets.IsEligible(mast) && (int)sail.category == 5)
			{
				return sail.currentUnroll <= 0.001f;
			}
			return false;
		}

		private void OnDisable()
		{
			ResetInteractionVisuals();
		}

		private void OnDestroy()
		{
			Instances.Remove(this);
		}
	}
	internal sealed class FurledStaysailHitbox : MonoBehaviour
	{
		internal Collider Collider { get; private set; }

		internal void Set(Collider collider)
		{
			Collider = collider;
		}
	}
	internal static class StaysailTargets
	{
		internal static bool IsEligible(Mast mast)
		{
			if ((Object)(object)mast != (Object)null && mast.onlyStaysails)
			{
				return !IsBermudaMast(mast);
			}
			return false;
		}

		internal static bool IsBermudaMast(Mast mast)
		{
			if ((Object)(object)mast == (Object)null)
			{
				return false;
			}
			BoatPartOption component = ((Component)mast).GetComponent<BoatPartOption>();
			string text = (((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.optionName)) ? component.optionName : ((Object)((Component)mast).gameObject).name);
			text = text.ToLowerInvariant().Replace('_', ' ');
			if (text.Contains("bermuda") && text.Contains("mast"))
			{
				return !text.Contains("stay");
			}
			return false;
		}

		internal static int GetBoatSceneIndex(Mast mast)
		{
			if ((Object)(object)mast == (Object)null)
			{
				return -1;
			}
			SaveableObject val = null;
			if ((Object)(object)mast.shipRigidbody != (Object)null)
			{
				val = ((Component)mast.shipRigidbody).GetComponent<SaveableObject>() ?? ((Component)mast.shipRigidbody).GetComponentInParent<SaveableObject>();
			}
			val = val ?? ((Component)mast).GetComponentInParent<SaveableObject>();
			if (!((Object)(object)val != (Object)null))
			{
				return -1;
			}
			return val.sceneIndex;
		}

		internal static string GetStayName(Mast mast)
		{
			if ((Object)(object)mast == (Object)null)
			{
				return "Staysail stay";
			}
			BoatPartOption component = ((Component)mast).GetComponent<BoatPartOption>();
			string text = (((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.optionName)) ? component.optionName : ((Object)((Component)mast).gameObject).name);
			text = text.Replace('_', ' ').Trim();
			if (!string.IsNullOrEmpty(text))
			{
				return text;
			}
			return "Staysail stay";
		}

		internal static Mast Resolve(SailpackData data)
		{
			SailpackRecord record = data?.Record;
			if (record == null)
			{
				return null;
			}
			if (Matches(data.RuntimeTarget, record))
			{
				return data.RuntimeTarget;
			}
			Mast[] array = Array.Empty<Mast>();
			SaveLoadManager instance = SaveLoadManager.instance;
			if ((Object)(object)instance != (Object)null)
			{
				SaveableObject[] currentObjects = instance.GetCurrentObjects();
				int targetBoatSceneIndex = record.targetBoatSceneIndex;
				if (currentObjects != null && targetBoatSceneIndex >= 0 && targetBoatSceneIndex < currentObjects.Length && (Object)(object)currentObjects[targetBoatSceneIndex] != (Object)null)
				{
					array = (from mast in ((Component)currentObjects[targetBoatSceneIndex]).GetComponentsInChildren<Mast>(false)
						where Matches(mast, record)
						select mast).ToArray();
				}
			}
			if (array.Length == 0)
			{
				array = (from mast in Object.FindObjectsOfType<Mast>()
					where Matches(mast, record)
					select mast).ToArray();
			}
			Mast val = ((IEnumerable<Mast>)array).FirstOrDefault((Func<Mast, bool>)((Mast mast) => string.Equals(GetStayName(mast), record.targetStayName, StringComparison.OrdinalIgnoreCase)));
			if ((Object)(object)val == (Object)null && array.Length == 1)
			{
				val = array[0];
			}
			if ((Object)(object)val != (Object)null)
			{
				data.BindTarget(val);
			}
			return val;
		}

		private static bool Matches(Mast mast, SailpackRecord record)
		{
			if ((Object)(object)mast != (Object)null && ((Component)mast).gameObject.activeInHierarchy && IsEligible(mast) && GetBoatSceneIndex(mast) == record.targetBoatSceneIndex)
			{
				return mast.orderIndex == record.targetMastOrderIndex;
			}
			return false;
		}

		internal static void EnsureRuntimeButtons(Mast mast)
		{
			if (!IsEligible(mast))
			{
				return;
			}
			foreach (GameObject item in mast.sails ?? new List<GameObject>())
			{
				EnsureRemovalButton(mast, item);
			}
		}

		internal static void EnsureRemovalButton(Mast mast, GameObject sailObject)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Invalid comparison between Unknown and I4
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			Sail val = (((Object)(object)sailObject != (Object)null) ? sailObject.GetComponent<Sail>() : null);
			if (!IsEligible(mast) || (Object)(object)val == (Object)null || (int)val.category != 5)
			{
				return;
			}
			ReefEffectAnimUniversal componentInChildren = ((Component)val).GetComponentInChildren<ReefEffectAnimUniversal>(true);
			Renderer val2 = (((Object)(object)componentInChildren != (Object)null) ? componentInChildren.furledSail : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return;
			}
			FurledStaysailButton component = ((Component)val2).GetComponent<FurledStaysailButton>();
			if ((Object)(object)component == (Object)null)
			{
				BoxCollider val3 = ((Component)val2).gameObject.AddComponent<BoxCollider>();
				MeshFilter component2 = ((Component)val2).GetComponent<MeshFilter>();
				if ((Object)(object)component2 != (Object)null && (Object)(object)component2.sharedMesh != (Object)null)
				{
					Bounds bounds = component2.sharedMesh.bounds;
					val3.center = ((Bounds)(ref bounds)).center;
					bounds = component2.sharedMesh.bounds;
					val3.size = ((Bounds)(ref bounds)).size;
				}
				((Collider)val3).isTrigger = true;
				((Component)val2).gameObject.AddComponent<FurledStaysailHitbox>().Set((Collider)(object)val3);
				component = ((Component)val2).gameObject.AddComponent<FurledStaysailButton>();
				component.Initialize(mast, val, (Collider)(object)val3);
			}
			else
			{
				component.Initialize(mast, val);
			}
		}
	}
	internal static class StaysailWindShadow
	{
		private static readonly FieldInfo CheckingField = AccessTools.Field(typeof(SailShadowCol), "checking") ?? throw new MissingFieldException(typeof(SailShadowCol).FullName, "checking");

		internal static bool AppliesTo(Sail sail)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Invalid comparison between Unknown and I4
			if ((Object)(object)sail != (Object)null && sail.IsInstalled() && (int)sail.category == 5)
			{
				return StaysailTargets.IsEligible(((Component)sail).GetComponentInParent<Mast>());
			}
			return false;
		}

		internal static void RecoverAfterHingeReset(Sail sail)
		{
			if (AppliesTo(sail))
			{
				SailShadowCol componentInChildren = ((Component)sail).GetComponentInChildren<SailShadowCol>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					CheckingField.SetValue(componentInChildren, false);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Sail), "GetCurrentShadowMult")]
	internal static class StaysailWindReceptionPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Sail __instance, ref float __result)
		{
			if (StaysailWindShadow.AppliesTo(__instance))
			{
				__result = 1f;
			}
		}
	}
	[HarmonyPatch(typeof(JibAngleMaster), "ResetHingeRestingRot")]
	internal static class StaysailShadowHingeResetPatch
	{
		[HarmonyPostfix]
		private static void Postfix(JibAngleMaster __instance)
		{
			StaysailWindShadow.RecoverAfterHingeReset(((Component)__instance).GetComponent<Sail>());
		}
	}
	[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(ShipyardDocuments), "Update")]
	internal static class SailmakerDeliveryPointPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ShipyardDocuments __instance)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_005d: 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_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: 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_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance.shipyard == (Object)null || (Object)(object)((Component)__instance).GetComponent<SailmakerDeliveryInstalled>() != (Object)null)
			{
				return;
			}
			((Component)__instance).gameObject.AddComponent<SailmakerDeliveryInstalled>();
			try
			{
				Transform transform = ((Component)__instance).transform;
				GameObject val = new GameObject("sail maker delivery anchor");
				val.transform.SetParent(transform.parent, false);
				val.transform.localScale = transform.localScale;
				val.transform.rotation = transform.rotation;
				Scene scene = ((Component)__instance).gameObject.scene;
				if (TryGetAuthoredPose(((Scene)(ref scene)).name, out var localPosition, out var localEulerAngles))
				{
					val.transform.position = transform.TransformPoint(localPosition);
					val.transform.rotation = transform.rotation * Quaternion.Euler(localEulerAngles);
				}
				else
				{
					val.transform.position = transform.position + GetFallbackOffset(((Component)__instance).GetComponent<Collider>(), transform);
				}
				GameObject val2 = new GameObject("sail package delivery point");
				val2.transform.SetParent(val.transform, false);
				val2.transform.localPosition = new Vector3(0f, 0.55f, 0.8f);
				SailmakerSession.RegisterDeliveryPoint(__instance.shipyard, val2.transform);
			}
			catch (Exception arg)
			{
				ManualLogSource log = SwappableStaysailPlugin.Log;
				if (log != null)
				{
					log.LogError((object)$"Could not create the Sail Maker delivery point: {arg}");
				}
			}
		}

		private static Vector3 GetFallbackOffset(Collider collider, Transform source)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: 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)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			Vector3 surfaceLateral = GetSurfaceLateral(collider, source);
			Vector3 val = surfaceLateral * (GetProjectedHalfExtent(collider, surfaceLateral) * 2f + 0.45f);
			if ((Object)(object)collider == (Object)null)
			{
				return val;
			}
			Bounds bounds = collider.bounds;
			Bounds val2 = bounds;
			for (int i = 0; i < 8; i++)
			{
				((Bounds)(ref val2)).center = ((Bounds)(ref bounds)).center + val;
				if (!((Bounds)(ref bounds)).Intersects(val2))
				{
					break;
				}
				val += surfaceLateral * 0.25f;
			}
			return val;
		}

		private static bool TryGetAuthoredPose(string sceneName, out Vector3 localPosition, out Vector3 localEulerAngles)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			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;
			default:
				localPosition = Vector3.zero;
				localEulerAngles = Vector3.zero;
				return false;
			case "island 27 Lagoon SwampShipyard":
				localPosition = new Vector3(-0.699f, -0.891f, 0f);
				localEulerAngles = new Vector3(0f, 0f, -157.151f);
				return true;
			}
		}

		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 SailmakerDeliveryInstalled : MonoBehaviour
	{
	}
	[HarmonyPatch(typeof(ShipyardUI), "Awake")]
	internal static class SailmakerModeButtonInstallPatch
	{
		private const float GapAboveCleanShip = 0.3f;

		[HarmonyPostfix]
		private static void Postfix(ShipyardUI __instance)
		{
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				ShipyardButton val = ((IEnumerable<ShipyardButton>)((Component)__instance).GetComponentsInChildren<ShipyardButton>(true)).FirstOrDefault((Func<ShipyardButton, bool>)((ShipyardButton button) => (int)button.function == 12));
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource log = SwappableStaysailPlugin.Log;
					if (log != null)
					{
						log.LogError((object)"Could not find the shipyard's Clean Ship button; the Sail Maker button was not added.");
					}
					return;
				}
				Transform transform = ((Component)val).transform;
				GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, transform.parent);
				((Object)val2).name = "shipyard ui button sail maker";
				ShipyardButton[] componentsInChildren = val2.GetComponentsInChildren<ShipyardButton>(true);
				for (int num = 0; num < componentsInChildren.Length; num++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren[num]);
				}
				Outline[] componentsInChildren2 = val2.GetComponentsInChildren<Outline>(true);
				for (int num2 = 0; num2 < componentsInChildren2.Length; num2++)
				{
					Object.DestroyImmediate((Object)(object)componentsInChildren2[num2]);
				}
				val2.transform.localRotation = transform.localRotation;
				val2.transform.localScale = transform.localScale;
				val2.transform.localPosition = transform.localPosition + Vector3.up * (transform.localScale.y + 0.3f);
				TextMesh text = ((val2.transform.childCount > 0) ? ((Component)val2.transform.GetChild(0)).GetComponent<TextMesh>() : null);
				val2.AddComponent<SailmakerModeButton>().Initialize(text);
			}
			catch (Exception arg)
			{
				ManualLogSource log2 = SwappableStaysailPlugin.Log;
				if (log2 != null)
				{
					log2.LogError((object)$"Could not add the Sail Maker button: {arg}");
				}
			}
		}
	}
	[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(Shipyard __instance)
		{
			SailmakerCollisionChecks.Cancel(__instance.sailInstaller);
			SailmakerSession.End();
		}

		[HarmonyPostfix]
		private static void Postfix(Shipyard __instance)
		{
			SailmakerCollisionChecks.Cancel(__instance.sailInstaller);
		}
	}
	[HarmonyPatch(typeof(ShipyardSailInstaller), "RecheckAllSailsCols")]
	internal static class ShipyardCollisionRecheckPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(ShipyardSailInstaller __instance, Shipyard ___shipyard)
		{
			SailmakerCollisionChecks.Request(__instance, ___shipyard);
			return false;
		}
	}
	internal sealed class SailmakerCollisionChecks : MonoBehaviour
	{
		private Shipyard shipyard;

		private GameObject boat;

		private Coroutine worker;

		private int revision;

		private bool ready;

		internal static void Request(ShipyardSailInstaller installer, Shipyard yard)
		{
			if ((Object)(object)installer == (Object)null)
			{
				return;
			}
			SailmakerCollisionChecks sailmakerCollisionChecks = ((Component)installer).GetComponent<SailmakerCollisionChecks>();
			if ((Object)(object)yard == (Object)null || (Object)(object)GameState.currentShipyard != (Object)(object)yard || (Object)(object)yard.GetCurrentBoat() == (Object)null)
			{
				if ((Object)(object)sailmakerCollisionChecks != (Object)null)
				{
					sailmakerCollisionChecks.Stop();
				}
				return;
			}
			if ((Object)(object)sailmakerCollisionChecks == (Object)null)
			{
				sailmakerCollisionChecks = ((Component)installer).gameObject.AddComponent<SailmakerCollisionChecks>();
			}
			sailmakerCollisionChecks.Stop();
			sailmakerCollisionChecks.shipyard = yard;
			sailmakerCollisionChecks.boat = yard.GetCurrentBoat();
			sailmakerCollisionChecks.worker = ((MonoBehaviour)sailmakerCollisionChecks).StartCoroutine(sailmakerCollisionChecks.Check(sailmakerCollisionChecks.revision));
		}

		internal static bool IsReady(Shipyard yard)
		{
			SailmakerCollisionChecks sailmakerCollisionChecks = (((Object)(object)yard != (Object)null && (Object)(object)yard.sailInstaller != (Object)null) ? ((Component)yard.sailInstaller).GetComponent<SailmakerCollisionChecks>() : null);
			if ((Object)(object)sailmakerCollisionChecks != (Object)null && sailmakerCollisionChecks.ready)
			{
				return sailmakerCollisionChecks.IsCurrent(sailmakerCollisionChecks.revision);
			}
			return false;
		}

		internal static void Cancel(ShipyardSailInstaller installer)
		{
			SailmakerCollisionChecks sailmakerCollisionChecks = (((Object)(object)installer != (Object)null) ? ((Component)installer).GetComponent<SailmakerCollisionChecks>() : null);
			if ((Object)(object)sailmakerCollisionChecks != (Object)null)
			{
				sailmakerCollisionChecks.Stop();
			}
		}

		private void Stop()
		{
			revision++;
			ready = false;
			if (worker != null)
			{
				((MonoBehaviour)this).StopCoroutine(worker);
			}
			worker = null;
			shipyard = null;
			boat = null;
		}

		private bool IsCurrent(int request)
		{
			if (request == revision && (Object)(object)shipyard != (Object)null && (Object)(object)boat != (Object)null && (Object)(object)GameState.currentShipyard == (Object)(object)shipyard)
			{
				return (Object)(object)shipyard.GetCurrentBoat() == (Object)(object)boat;
			}
			return false;
		}

		private IEnumerator Check(int request)
		{
			yield return (object)new WaitForEndOfFrame();
			yield return (object)new WaitForEndOfFrame();
			yield return (object)new WaitForFixedUpdate();
			yield return (object)new WaitForFixedUpdate();
			yield return (object)new WaitForFixedUpdate();
			if (!IsCurrent(request))
			{
				yield break;
			}
			BoatRefs component = boat.GetComponent<BoatRefs>();
			if ((Object)(object)component == (Object)null || component.masts == null)
			{
				yield break;
			}
			Sail[] array = (from s in component.masts.Where((Mast m) => (Object)(object)m != (Object)null && ((Component)m).gameObject.activeInHierarchy && m.sails != null).SelectMany((Mast m) => m.sails.ToArray())
				where (Object)(object)s != (Object)null && s.activeInHierarchy
				select s.GetComponent<Sail>() into s
				where (Object)(object)s != (Object)null
				select s).ToArray();
			List<ShipyardSailColChecker> checkers = new List<ShipyardSailColChecker>();
			Sail[] array2 = array;
			foreach (Sail val in array2)
			{
				if (!IsCurrent(request) || (Object)(object)val == (Object)null)
				{
					yield break;
				}
				SailConnections component2 = ((Component)val).GetComponent<SailConnections>();
				ShipyardSailColChecker val2 = (((Object)(object)component2 != (Object)null) ? component2.colChecker : null);
				if ((Object)(object)val2 == (Object)null)
				{
					yield break;
				}
				checkers.Add(val2);
				val2.RunColCheck();
				yield return (object)new WaitForFixedUpdate();
				yield return (object)new WaitForEndOfFrame();
			}
			while (IsCurrent(request) && checkers.Any((ShipyardSailColChecker c) => (Object)(object)c != (Object)null && c.checking))
			{
				yield return (object)new WaitForFixedUpdate();
			}
			yield return (object)new WaitForFixedUpdate();
			yield return (object)new WaitForEndOfFrame();
			if (IsCurrent(request) && !checkers.Any((ShipyardSailColChecker c) => (Object)(object)c == (Object)null || c.checking || !c.readyToInstall))
			{
				ready = true;
				worker = null;
				shipyard.UpdateOrder();
				if ((Object)(object)ShipyardUI.instance != (Object)null)
				{
					ShipyardUI.instance.RefreshButtons();
				}
			}
		}

		private void OnDisable()
		{
			Stop();
		}
	}
	[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();
		}
	}
	[HarmonyPatch(typeof(WindCloth), "Update")]
	internal static class SailmakerHiddenClothPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Sail ___sail, Renderer ___renderer)
		{
			if ((Object)(object)___renderer != (Object)null && SailmakerSession.IsHiddenSail(___sail))
			{
				___renderer.enabled = false;
			}
		}
	}
	[HarmonyPatch(typeof(ShipyardSailColChecker), "OnTriggerEnter")]
	internal static class SailmakerPreviewCollisionPatch
	{
		private static readonly FieldInfo SailField = AccessTools.Field(typeof(ShipyardSailColChecker), "sail");

		[HarmonyPostfix]
		private static void Postfix(ShipyardSailColChecker __instance, Collider other, Sail ___sail)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Invalid comparison between Unknown and I4
			if (!SailmakerSession.Active || (Object)(object)other == (Object)null || __instance.currentAngle != 0 || ((Component)__instance).transform.position.y >= 900f || !SailmakerSession.IsPreview(___sail))
			{
				return;
			}
			GameObject gameObject = ((Component)other).gameObject;
			if (gameObject.CompareTag("SailColCheckerSub") && (Object)(object)gameObject.transform.parent != (Object)null)
			{
				gameObject = ((Component)gameObject.transform.parent).gameObject;
			}
			if (!((Object)(object)gameObject == (Object)(object)((Component)__instance).gameObject) && gameObject.CompareTag("SailColChecker"))
			{
				ShipyardSailColChecker component = gameObject.GetComponent<ShipyardSailColChecker>();
				Sail val;
				if (!((Object)(object)component != (Object)null))
				{
					val = null;
				}
				else
				{
					FieldInfo sailField = SailField;
					object? obj = ((sailField != null) ? sailField.GetValue(component) : null);
					val = (Sail)((obj is Sail) ? obj : null);
				}
				Sail val2 = val;
				if (!((Object)(object)component == (Object)null) && component.currentAngle == 0 && !((Object)(object)val2 == (Object)null) && (int)val2.category != 5)
				{
					__instance.collisionsWithSails++;
					SailmakerSession.RefreshOrder(GameState.currentShipyard);
				}
			}
		}
	}
	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>();

		private static readonly HashSet<Sail> HiddenSails = new HashSet<Sail>();

		private static readonly Dictionary<Renderer, bool> HiddenClothRenderers = new Dictionary<Renderer, bool>();

		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);
				HideInstalledSails(shipyard);
				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
			{
				ShowInstalledSails();
				ActiveShipyard = null;
				SailmakerUiPatch.RestoreUi();
			}
		}

		internal static bool IsHiddenSail(Sail sail)
		{
			if (HiddenSails.Count > 0 && (Object)(object)sail != (Object)null)
			{
				return HiddenSails.Contains(sail);
			}
			return false;
		}

		private static void HideInstalledSails(Shipyard shipyard)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Invalid comparison between Unknown and I4
			ShowInstalledSails();
			GameObject currentBoat = shipyard.GetCurrentBoat();
			BoatRefs val = (((Object)(object)currentBoat != (Object)null) ? currentBoat.GetComponent<BoatRefs>() : null);
			if ((((Object)(object)val != (Object)null) ? val.masts : null) == null)
			{
				return;
			}
			Mast[] masts = val.masts;
			foreach (Mast val2 in masts)
			{
				if ((Object)(object)val2 == (Object)null || val2.sails == null)
				{
					continue;
				}
				foreach (GameObject sail in val2.sails)
				{
					Sail val3 = (((Object)(object)sail != (Object)null) ? sail.GetComponent<Sail>() : null);
					if ((Object)(object)val3 == (Object)null || (int)val3.category != 5 || IsPreview(val3))
					{
						continue;
					}
					HiddenSails.Add(val3);
					WindCloth[] componentsInChildren = sail.GetComponentsInChildren<WindCloth>(true);
					for (int j = 0; j < componentsInChildren.Length; j++)
					{
						Renderer component = ((Component)componentsInChildren[j]).GetComponent<Renderer>();
						if ((Object)(object)component != (Object)null && !HiddenClothRenderers.ContainsKey(component))
						{
							HiddenClothRenderers[component] = component.enabled;
							component.enabled = false;
						}
					}
				}
			}
		}

		private static void ShowInstalledSails()
		{
			foreach (KeyValuePair<Renderer, bool> hiddenClothRenderer in HiddenClothRenderers)
			{
				if ((Object)(object)hiddenClothRenderer.Key != (Object)null)
				{
					hiddenClothRenderer.Key.enabled = hiddenClothRenderer.Value;
				}
			}
			HiddenClothRenderers.Clear();
			HiddenSails.Clear();
		}

		internal static void ToggleFromShipyardMenu()
		{
			Shipyard currentShipyard = GameState.currentShipyard;
			if ((Object)(object)currentShipyard == (Object)null || (Object)(object)currentShipyard.GetCurrentBoat() == (Object)null)
			{
				return;
			}
			if (Active)
			{
				if (GetPreviews().Count > 0)
				{
					Notify("Buy or cancel the\nstaysails first.");
					return;
				}
				End();
				ShowSailPage(currentShipyard);
				currentShipyard.UpdateOrder();
				ManualLogSource log = SwappableStaysailPlugin.Log;
				if (log != null)
				{
					log.LogInfo((object)"Switched from Sail Maker back to the shipyard.");
				}
			}
			else if (HasUnconfirmedShipyardOrder(currentShipyard))
			{
				Notify("Confirm or cancel the\ncurrent order first.");
			}
			else if (!HasSupportedStay(currentShipyard.GetCurrentBoat()))
			{
				Notify("This ship has no\nsupported staysail stay.");
			}
			else
			{
				currentShipyard.sailInstaller.SelectMast((Mast)null);
				Request(currentShipyard);
				TryBegin(currentShipyard);
			}
		}

		private static void ShowSailPage(Shipyard shipyard)
		{
			shipyard.sailInstaller.SelectMast((Mast)null);
			ShipyardUI instance = ShipyardUI.instance;
			if ((Object)(object)instance != (Object)null)
			{
				instance.ChangeMenuCategory(-1);
				instance.CloseSailCategoryMenu();
				instance.HideNewSailButtons();
			}
		}

		private static bool HasUnconfirmedShipyardOrder(Shipyard shipyard)
		{
			if (shipyard.IncludeHullCleaning() || shipyard.IncludeRepair())
			{
				return true;
			}
			List<ShipyardRefund> currentRefunds = shipyard.GetCurrentRefunds();
			if (currentRefunds != null && currentRefunds.Count > 0)
			{
				return true;
			}
			GameObject currentBoat = shipyard.GetCurrentBoat();
			BoatRefs component = currentBoat.GetComponent<BoatRefs>();
			if ((((Object)(object)component != (Object)null) ? component.masts : null) != null)
			{
				Mast[] masts = component.masts;
				foreach (Mast val in masts)
				{
					if ((Object)(object)val == (Object)null || val.sails == null)
					{
						continue;
					}
					foreach (GameObject sail in val.sails)
					{
						Sail val2 = (((Object)(object)sail != (Object)null) ? sail.GetComponent<Sail>() : null);
						if ((Object)(object)val2 != (Object)null && !val2.IsInstalled())
						{
							return true;
						}
					}
				}
			}
			BoatCustomParts component2 = currentBoat.GetComponent<BoatCustomParts>();
			BoatPartsOrder val3 = (((Object)(object)shipyard.partsInstaller != (Object)null) ? shipyard.partsInstaller.GetCurrentOrder() : null);
			if ((((Object)(object)component2 != (Object)null) ? component2.availableParts : null) != null && val3?.orderedOptions != null)
			{
				int num = Math.Min(component2.availableParts.Count, val3.orderedOptions.Length);
				for (int j = 0; j < num; j++)
				{
					if (component2.availableParts[j].activeOption != val3.orderedOptions[j])
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool HasSupportedStay(GameObject boat)
		{
			BoatRefs component = boat.GetComponent<BoatRefs>();
			if ((((Object)(object)component != (Object)null) ? component.masts : null) != null)
			{
				return component.masts.Any((Mast mast) => (Object)(object)mast != (Object)null && ((Component)mast).gameObject.activeInHierarchy && StaysailTargets.IsEligible(mast));
			}
			return false;
		}

		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[] obj = (((Object)(object)ShipyardUI.instance != (Object)null) ? (SelectSailButtonsField?.GetValue(ShipyardUI.instance) as GameObject[]) : null);
			int num = ((obj != null) ? obj.Length : 0);
			if (GetPreviews(currentMast).Count >= num)
			{
				Notify((num > 0) ? $"Order at most {num} staysails per stay.\nBuy or remove one first." : "The staysail selection menu is unavailable.");
				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));
				ActiveShipyard.sailInstaller.RecheckAllSailsCols();
				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